Wordfence Security – Firewall & Malware Scan - Version 5.3.9

Version Description

  • Premium Feature: Password Auditing. Audit the strength of your admin and user-level passwords against our GPU based auditing cluster. Easily alert users to weak passwords or force a password change.
  • Feature: Activity email summary. See options page to enable a weekly, bi-weekly or monthly activity summary.
  • Feature: Activity summary dashboard widget.
  • Fix: Fixed bug on plugin activation where the configuration table was being queried before it was created.
  • Improvement: Added .htaccess to wfcache directory.
  • Improvement: Switched to using wp_remote_post for Wordfence cloud API calls to improved SSL support and a more standards based approach.
Download this release

Release Info

Developer mmaunder
Plugin Icon 128x128 Wordfence Security – Firewall & Malware Scan
Version 5.3.9
Comparing to
See all releases

Code changes from version 5.3.8 to 5.3.9

css/activity-report-widget.css ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #wordfence_activity_report_widget .inside h1,
2
+ #wordfence_activity_report_widget .inside h2,
3
+ #wordfence_activity_report_widget .inside h3,
4
+ #wordfence_activity_report_widget .inside h4 {
5
+ margin: 20px 0 4px;
6
+ color: #222 !important;
7
+ }
8
+
9
+ #wordfence_activity_report_widget .inside h1 {
10
+ float: right;
11
+ text-align: right;
12
+ font-size: 30px;
13
+ color: #444444 !important;
14
+ line-height: 1.1;
15
+ }
16
+
17
+ #wordfence_activity_report_widget .inside h2 {
18
+ font-size: 20px;
19
+ }
20
+
21
+ #wordfence_activity_report_widget .inside h4 {
22
+ font-size: 16px;
23
+ color: #666666 !important;
24
+ }
25
+
26
+ #wordfence_activity_report_widget .inside code {
27
+ background-color: transparent;
28
+ }
29
+
30
+ #wordfence_activity_report_widget table.wf-table {
31
+ width: 100%;
32
+ max-width: 100%;
33
+ border-collapse: collapse;
34
+ }
35
+
36
+ #wordfence_activity_report_widget table.wf-table th,
37
+ #wordfence_activity_report_widget table.wf-table td {
38
+ text-align: left;
39
+ padding: 6px 4px;
40
+ border: 1px solid #cccccc;
41
+ }
42
+
43
+ #wordfence_activity_report_widget table.wf-table thead th,
44
+ #wordfence_activity_report_widget table.wf-table thead td {
45
+ background-color: #222;
46
+ color: #FFFFFF;
47
+ font-weight: bold;
48
+ border-color: #474747;
49
+ }
50
+
51
+ #wordfence_activity_report_widget table.wf-table tbody tr.even td {
52
+ background-color: #eeeeee;
53
+ }
54
+
55
+ #wordfence_activity_report_widget .loginFailValidUsername {
56
+ color: #00c000;
57
+ font-weight: bold;
58
+ }
59
+
60
+ #wordfence_activity_report_widget .loginFailInvalidUsername {
61
+ color: #e74a2a;
62
+ font-weight: bold;
63
+ }
64
+
65
+ #wordfence_activity_report_widget .display-file-table-cell {
66
+ overflow: hidden;
67
+ }
68
+
69
+ #wordfence_activity_report_widget .display-file {
70
+ margin: 0px;
71
+ display: block;
72
+ font-size: 12px;
73
+ width: 100%;
74
+ overflow: auto;
75
+ white-space: nowrap;
76
+ }
77
+
78
+ #wordfence_activity_report_widget .recently-modified-files {
79
+ table-layout: fixed;
80
+ }
81
+
82
+ #wordfence_activity_report_widget .recently-modified-files th:nth-child(1),
83
+ #wordfence_activity_report_widget .recently-modified-files td:nth-child(1) {
84
+ width: 30%;
85
+ }
86
+
87
+ #wordfence_activity_report_widget .recently-modified-files th:nth-child(2),
88
+ #wordfence_activity_report_widget .recently-modified-files td:nth-child(2) {
89
+ width: 70%;
90
+ }
css/main.css CHANGED
@@ -142,8 +142,8 @@ div.wordfenceScanButton input.button-wf-grey {
142
  }
143
  table.wfSummaryParent { font-family: sans-serif; font-size: 14px; color: #000; z-index: 9;}
144
  table.wfSummaryParent td { vertical-align: top; padding: 0; margin: 0; }
145
- table.wfSummaryParent table.wfSummaryChild th { font-weight: bold; text-align: right; font-family: Georgia, serif; color: #000; padding: 5px 10px 5px 0; border-top: 1px solid #CCC; }
146
- table.wfSummaryParent table.wfSummaryChild td { font-weight: normal; text-align: left; padding: 5px 0 5px 0; border-top: 1px solid #CCC; }
147
  table.wfSummaryParent table.wfSC1 td { width: 300px; padding: 0px 25px 10px 0; }
148
  table.wfSummaryParent table.wfSC2 th { width: 80px; }
149
  table.wfSummaryParent table.wfSC2 td { width: 100px; }
@@ -155,8 +155,8 @@ div.wfIssue table.wfIssue td { padding: 2px; margin: 0; border-width: 0px; text-
155
  div.wfIssue table.wfIssue th { padding: 2px; margin: 0; font-weight: bold; text-align: left; color: #777; }
156
  div.wfIssue h2 { margin: 0px 0 5px 0; padding: 0; }
157
  div.wfIssue table.wfIssueLinks td { border-width: 0px; text-align: left; padding-right: 10px; }
158
- .wfIssueOptions {
159
- border-top: 1px solid #CCC;
160
  padding: 10px;
161
  }
162
  .wfIssueOptions a {
@@ -241,11 +241,11 @@ h3.wfConfigHeading {
241
  width: 800px;
242
  }
243
  .consoleHeadText {
244
- font-size: 18px;
245
- font-family: Georgia, serif;
246
- font-style: italic;
247
- color: #555;
248
- font-weight: bold;
249
  -webkit-font-smoothing: antialiased;
250
 
251
  }
@@ -253,7 +253,7 @@ h3.wfConfigHeading {
253
  .consoleInner { height: 116px; overflow: auto; z-index: 1; }
254
  .bevelDiv1 { border: 1px solid #EFEFEF; }
255
  .bevelDiv2 { border: 1px solid #AAA; }
256
- .bevelDiv3 { border: 1px solid #555;
257
  background-color: #FFFFE0; /* #FFFFF0; /* #FFEBCD; #FFFACD; */
258
  color: #000; padding: 5px; font-family: Arial; -webkit-font-smoothing: none; }
259
 
@@ -276,9 +276,9 @@ h3.wfConfigHeading {
276
  clear: both;
277
  visibility: hidden;
278
  }
279
- .wfSummaryFinal {
280
  -webkit-font-smoothing: antialiased;
281
- font-weight: bold;
282
  color: #555;
283
  }
284
  input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
@@ -287,16 +287,16 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
287
  }
288
  #wordfenceWorking {
289
  padding: 2px 8px 2px 24px;
290
- z-index: 100000;
291
- position: fixed;
292
- right: 2px;
293
- bottom: 2px;
294
- border: 1px solid #000;
295
- background-color: #F00;
296
- color: #FFF;
297
- font-size: 12px;
298
- font-weight: bold;
299
- font-family: Arial;
300
  text-align: center;
301
  background-image: url('../images/icons/ajaxRed16.gif');
302
  background-position: 2px 2px;
@@ -322,10 +322,10 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
322
  width: 1px;
323
  }
324
  .wfPaidOnlyNotice {
325
- width: 500px;
326
- background-color: #FFFFE0;
327
- border: 1px solid #000;
328
- padding: 10px;
329
  margin: 20px;
330
  }
331
  .wfFalconNotice {
@@ -415,10 +415,10 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
415
  border: 2px solid #999999 !important ; border-radius: 19px !important ;
416
  position: absolute !important ; top: 0 !important ; bottom: 0 !important ; right: 46px !important ;
417
  -moz-transition: all 0.3s ease-in 0s !important ; -webkit-transition: all 0.3s ease-in 0s !important ;
418
- -o-transition: all 0.3s ease-in 0s !important ; transition: all 0.3s ease-in 0s !important ;
419
- background-image: -moz-linear-gradient(center top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0) 80%) !important ;
420
- background-image: -webkit-linear-gradient(center top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0) 80%) !important ;
421
- background-image: -o-linear-gradient(center top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0) 80%) !important ;
422
  background-image: linear-gradient(center top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0) 80%) !important ;
423
  box-shadow: 0 1px 1px white inset !important ;
424
  }
@@ -426,9 +426,38 @@ input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
426
  margin-left: 0 !important ;
427
  }
428
  .wfOnOffSwitch-checkbox:checked + .wfOnOffSwitch-label .wfOnOffSwitch-switch {
429
- right: 0px !important ;
430
  }
431
  #wordfenceConfigWarning {
432
  clear: left;
433
  margin-top: 5px;
434
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  }
143
  table.wfSummaryParent { font-family: sans-serif; font-size: 14px; color: #000; z-index: 9;}
144
  table.wfSummaryParent td { vertical-align: top; padding: 0; margin: 0; }
145
+ table.wfSummaryParent table.wfSummaryChild th { font-weight: bold; text-align: right; font-family: Georgia, serif; color: #000; padding: 5px 10px 5px 0; border-top: 1px solid #CCC; }
146
+ table.wfSummaryParent table.wfSummaryChild td { font-weight: normal; text-align: left; padding: 5px 0 5px 0; border-top: 1px solid #CCC; }
147
  table.wfSummaryParent table.wfSC1 td { width: 300px; padding: 0px 25px 10px 0; }
148
  table.wfSummaryParent table.wfSC2 th { width: 80px; }
149
  table.wfSummaryParent table.wfSC2 td { width: 100px; }
155
  div.wfIssue table.wfIssue th { padding: 2px; margin: 0; font-weight: bold; text-align: left; color: #777; }
156
  div.wfIssue h2 { margin: 0px 0 5px 0; padding: 0; }
157
  div.wfIssue table.wfIssueLinks td { border-width: 0px; text-align: left; padding-right: 10px; }
158
+ .wfIssueOptions {
159
+ border-top: 1px solid #CCC;
160
  padding: 10px;
161
  }
162
  .wfIssueOptions a {
241
  width: 800px;
242
  }
243
  .consoleHeadText {
244
+ font-size: 18px;
245
+ font-family: Georgia, serif;
246
+ font-style: italic;
247
+ color: #555;
248
+ font-weight: bold;
249
  -webkit-font-smoothing: antialiased;
250
 
251
  }
253
  .consoleInner { height: 116px; overflow: auto; z-index: 1; }
254
  .bevelDiv1 { border: 1px solid #EFEFEF; }
255
  .bevelDiv2 { border: 1px solid #AAA; }
256
+ .bevelDiv3 { border: 1px solid #555;
257
  background-color: #FFFFE0; /* #FFFFF0; /* #FFEBCD; #FFFACD; */
258
  color: #000; padding: 5px; font-family: Arial; -webkit-font-smoothing: none; }
259
 
276
  clear: both;
277
  visibility: hidden;
278
  }
279
+ .wfSummaryFinal {
280
  -webkit-font-smoothing: antialiased;
281
+ font-weight: bold;
282
  color: #555;
283
  }
284
  input.wfStartScanButton { width: 160px; text-align: left; padding-left: 20px; }
287
  }
288
  #wordfenceWorking {
289
  padding: 2px 8px 2px 24px;
290
+ z-index: 100000;
291
+ position: fixed;
292
+ right: 2px;
293
+ bottom: 2px;
294
+ border: 1px solid #000;
295
+ background-color: #F00;
296
+ color: #FFF;
297
+ font-size: 12px;
298
+ font-weight: bold;
299
+ font-family: Arial;
300
  text-align: center;
301
  background-image: url('../images/icons/ajaxRed16.gif');
302
  background-position: 2px 2px;
322
  width: 1px;
323
  }
324
  .wfPaidOnlyNotice {
325
+ width: 500px;
326
+ background-color: #FFFFE0;
327
+ border: 1px solid #000;
328
+ padding: 10px;
329
  margin: 20px;
330
  }
331
  .wfFalconNotice {
415
  border: 2px solid #999999 !important ; border-radius: 19px !important ;
416
  position: absolute !important ; top: 0 !important ; bottom: 0 !important ; right: 46px !important ;
417
  -moz-transition: all 0.3s ease-in 0s !important ; -webkit-transition: all 0.3s ease-in 0s !important ;
418
+ -o-transition: all 0.3s ease-in 0s !important ; transition: all 0.3s ease-in 0s !important ;
419
+ background-image: -moz-linear-gradient(center top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0) 80%) !important ;
420
+ background-image: -webkit-linear-gradient(center top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0) 80%) !important ;
421
+ background-image: -o-linear-gradient(center top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0) 80%) !important ;
422
  background-image: linear-gradient(center top, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0) 80%) !important ;
423
  box-shadow: 0 1px 1px white inset !important ;
424
  }
426
  margin-left: 0 !important ;
427
  }
428
  .wfOnOffSwitch-checkbox:checked + .wfOnOffSwitch-label .wfOnOffSwitch-switch {
429
+ right: 0px !important ;
430
  }
431
  #wordfenceConfigWarning {
432
  clear: left;
433
  margin-top: 5px;
434
  }
435
+
436
+ table.wf-table {
437
+ width: 100%;
438
+ max-width: 100%;
439
+ border-collapse: collapse;
440
+ }
441
+ table.wf-table th,
442
+ table.wf-table td {
443
+ padding: 6px 4px;
444
+ border: 1px solid #ccc;
445
+ }
446
+ table.wf-table thead th,
447
+ table.wf-table thead td {
448
+ background-color: #222;
449
+ color: #fff;
450
+ font-weight: bold;
451
+ border-color: #474747;
452
+ text-align: left;
453
+ }
454
+ table.wf-table tbody tr td {
455
+ background-color: #fff;
456
+ }
457
+ table.wf-table tbody tr.even td,
458
+ table.wf-table tbody tr:nth-child(2n) td {
459
+ background-color: #eee;
460
+ }
461
+ table.wf-table tbody tr:hover td {
462
+ background-color: #fffbd8;
463
+ }
js/admin.js CHANGED
@@ -1,1641 +1,1913 @@
1
- if(! window['wordfenceAdmin']){ //To compile for checking: java -jar /usr/local/bin/closure.jar --js=admin.js --js_output_file=test.js
2
- window['wordfenceAdmin'] = {
3
- loading16: '<div class="wfLoading16"></div>',
4
- loadingCount: 0,
5
- dbCheckTables: [],
6
- dbCheckCount_ok: 0,
7
- dbCheckCount_skipped: 0,
8
- dbCheckCount_errors: 0,
9
- issues: [],
10
- ignoreData: false,
11
- iconErrorMsgs: [],
12
- scanIDLoaded: 0,
13
- colorboxQueue: [],
14
- mode: '',
15
- visibleIssuesPanel: 'new',
16
- preFirstScanMsgsLoaded: false,
17
- newestActivityTime: 0, //must be 0 to force loading of all initially
18
- elementGeneratorIter: 1,
19
- reloadConfigPage: false,
20
- nonce: false,
21
- tickerUpdatePending: false,
22
- activityLogUpdatePending: false,
23
- lastALogCtime: 0,
24
- activityQueue: [],
25
- totalActAdded: 0,
26
- maxActivityLogItems: 1000,
27
- scanReqAnimation: false,
28
- debugOn: false,
29
- blockedCountriesPending: [],
30
- ownCountry: "",
31
- schedStartHour: false,
32
- currentPointer: false,
33
- countryMap: false,
34
- countryCodesToSave: "",
35
- performanceScale: 3,
36
- performanceMinWidth: 20,
37
- tourClosed: false,
38
- welcomeClosed: false,
39
- init: function(){
40
- this.nonce = WordfenceAdminVars.firstNonce;
41
- this.debugOn = WordfenceAdminVars.debugOn == '1' ? true : false;
42
- this.tourClosed = WordfenceAdminVars.tourClosed == '1' ? true : false;
43
- this.welcomeClosed = WordfenceAdminVars.welcomeClosed == '1' ? true : false;
44
- var startTicker = false;
45
- var self = this;
46
- if(jQuery('#wordfenceMode_scan').length > 0){
47
- this.mode = 'scan';
48
- jQuery('#wfALogViewLink').prop('href', WordfenceAdminVars.siteBaseURL + '?_wfsf=viewActivityLog&nonce=' + this.nonce);
49
- jQuery('#consoleActivity').scrollTop(jQuery('#consoleActivity').prop('scrollHeight'));
50
- jQuery('#consoleScan').scrollTop(jQuery('#consoleScan').prop('scrollHeight'));
51
- this.noScanHTML = jQuery('#wfNoScanYetTmpl').tmpl().html();
52
- this.loadIssues();
53
- this.startActivityLogUpdates();
54
- if(this.needTour()){
55
- this.scanTourStart();
56
- }
57
- } else if(jQuery('#wordfenceMode_activity').length > 0){
58
- this.mode = 'activity';
59
- this.setupSwitches('wfLiveTrafficOnOff', 'liveTrafficEnabled', function(){});
60
- jQuery('#wfLiveTrafficOnOff').change(function(){
61
- if(/^(?:falcon|php)$/.test(WordfenceAdminVars.cacheType) ){
62
- jQuery('#wfLiveTrafficOnOff').attr('checked', false);
63
- self.colorbox('400px', "Live Traffic not available in high performance mode", "Please note that you can't enable live traffic when Falcon Engine or basic caching is enabled. This is done for performance reasons. If you want live traffic, go to the 'Performance Setup' menu and disable caching.");
64
- } else {
65
- self.updateSwitch('wfLiveTrafficOnOff', 'liveTrafficEnabled', function(){ window.location.reload(true); });
66
- }
67
- });
 
 
 
 
 
68
 
69
- if(WordfenceAdminVars.liveTrafficEnabled){
70
- this.activityMode = 'hit';
71
- } else {
72
- this.activityMode = 'loginLogout';
73
- this.switchTab(jQuery('#wfLoginLogoutTab'), 'wfTab1', 'wfDataPanel', 'wfActivity_loginLogout', function(){ WFAD.activityTabChanged(); });
74
- }
75
- startTicker = true;
76
- if(this.needTour()){
77
- this.tour('wfWelcomeContent3', 'wfHeading', 'top', 'left', "Learn about Site Performance", function(){ self.tourRedir('WordfenceSitePerf'); });
78
- }
79
- } else if(jQuery('#wordfenceMode_options').length > 0){
80
- this.mode = 'options';
81
- jQuery('.wfConfigElem').change(function(){ jQuery('#securityLevel').val('CUSTOM'); });
82
- this.updateTicker(true);
83
- startTicker = true;
84
- if(this.needTour()){
85
- this.tour('wfContentBasicOptions', 'wfMarkerBasicOptions', 'top', 'left', "Learn about Live Traffic Options", function(){
86
- self.tour('wfContentLiveTrafficOptions', 'wfMarkerLiveTrafficOptions', 'bottom', 'left', "Learn about Scanning Options", function(){
87
- self.tour('wfContentScansToInclude', 'wfMarkerScansToInclude', 'bottom', 'left', "Learn about Firewall Rules", function(){
88
- self.tour('wfContentFirewallRules', 'wfMarkerFirewallRules', 'bottom', 'left', "Learn about Login Security", function(){
89
- self.tour('wfContentLoginSecurity', 'wfMarkerLoginSecurity', 'bottom', 'left', "Learn about Other Options", function(){
90
- self.tour('wfContentOtherOptions', 'wfMarkerOtherOptions', 'bottom', 'left', false, false);
 
 
 
 
 
 
 
91
  });
92
  });
93
  });
94
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  });
96
- }
97
- } else if(jQuery('#wordfenceMode_blockedIPs').length > 0){
98
- this.mode = 'blocked';
99
- this.staticTabChanged();
100
- this.updateTicker(true);
101
- startTicker = true;
102
- if(this.needTour()){
103
- this.tour('wfWelcomeContent4', 'wfHeading', 'top', 'left', "Learn about Cellphone Sign-in", function(){ self.tourRedir('WordfenceTwoFactor'); });
104
- }
105
- } else if(jQuery('#wordfenceMode_twoFactor').length > 0){
106
- this.mode = 'twoFactor';
107
- startTicker = false;
108
- if(this.needTour()){
109
- this.tour('wfWelcomeTwoFactor', 'wfHeading', 'top', 'left', "Learn how to Block Countries", function(){ self.tourRedir('WordfenceCountryBlocking'); });
110
- }
111
- this.loadTwoFactor();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- } else if(jQuery('#wordfenceMode_countryBlocking').length > 0){
114
- this.mode = 'countryBlocking';
115
- startTicker = false;
116
- if(this.needTour()){
117
- this.tour('wfWelcomeContentCntBlk', 'wfHeading', 'top', 'left', "Learn how to Schedule Scans", function(){ self.tourRedir('WordfenceScanSchedule'); });
118
- }
119
- } else if(jQuery('#wordfenceMode_rangeBlocking').length > 0){
120
- this.mode = 'rangeBlocking';
121
- startTicker = false;
122
- if(this.needTour()){
123
- this.tour('wfWelcomeContentRangeBlocking', 'wfHeading', 'top', 'left', "Learn how to Customize Wordfence", function(){ self.tourRedir('WordfenceSecOpt'); });
124
- }
125
- this.calcRangeTotal();
126
- this.loadBlockRanges();
127
- } else if(jQuery('#wordfenceMode_whois').length > 0){
128
- this.mode = 'whois';
129
- startTicker = false;
130
- if(this.needTour()){
131
- this.tour('wfWelcomeContentWhois', 'wfHeading', 'top', 'left', "Learn how to use Advanced Blocking", function(){ self.tourRedir('WordfenceRangeBlocking'); });
132
- }
133
- this.calcRangeTotal();
134
- this.loadBlockRanges();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
- } else if(jQuery('#wordfenceMode_scanScheduling').length > 0){
137
- this.mode = 'scanScheduling';
138
- startTicker = false;
139
- this.sched_modeChange();
140
- if(this.needTour()){
141
- this.tour('wfWelcomeContentScanSched', 'wfHeading', 'top', 'left', "Learn about WHOIS", function(){ self.tourRedir('WordfenceWhois'); });
142
- }
143
- } else if(jQuery('#wordfenceMode_caching').length > 0){
144
- this.mode = 'caching';
145
- startTicker = false;
146
- if(this.needTour()){
147
- this.tour('wfWelcomeContentCaching', 'wfHeading', 'top', 'left', "Learn about IP Blocking", function(){ self.tourRedir('WordfenceBlockedIPs'); });
148
- }
149
- this.loadCacheExclusions();
150
- } else {
151
- this.mode = false;
152
- }
153
- if(this.mode){ //We are in a Wordfence page
154
- if(startTicker){
155
- this.updateTicker();
156
- this.liveInt = setInterval(function(){ self.updateTicker(); }, WordfenceAdminVars.actUpdateInterval);
157
- }
158
- jQuery(document).bind('cbox_closed', function(){ self.colorboxIsOpen = false; self.colorboxServiceQueue(); });
159
- }
160
- },
161
- needTour: function(){
162
- if( (! this.tourClosed) && this.welcomeClosed) {
163
- return true;
164
- } else {
165
- return false;
166
- }
167
- },
168
- sendTestEmail: function(email){
169
- var self = this;
170
- this.ajax('wordfence_sendTestEmail', { email: email }, function(res){
171
- if(res.result){
172
- self.colorbox('400px', "Test Email Sent", "Your test email was sent to the requested email address. The result we received from the WordPress wp_mail() function was: " +
173
- res.result + "<br /><br />A 'True' result means WordPress thinks the mail was sent without errors. A 'False' result means that WordPress encountered an error sending your mail. Note that it's possible to get a 'True' response with an error elsewhere in your mail system that may cause emails to not be delivered.");
174
- }
175
- });
176
- },
177
- loadAvgSitePerf: function(){
178
- var self = this;
179
- this.ajax('wordfence_loadAvgSitePerf', { limit: jQuery('#wfAvgPerfNum').val() }, function(res){
180
- res['scale'] = self.performanceScale;
181
- res['min'] = self.performanceMinWidth;
182
- jQuery('#wfAvgSitePerfContent').empty();
183
- var newElem = jQuery('#wfAvgPerfTmpl').tmpl(res);
184
- newElem.prependTo('#wfAvgSitePerfContent').fadeIn();
185
- });
186
- },
187
- updateSwitch: function(elemID, configItem, cb){
188
- var setting = jQuery('#' + elemID).is(':checked');
189
- this.updateConfig(configItem, jQuery('#' + elemID).is(':checked') ? 1 : 0, cb);
190
- },
191
- setupSwitches: function(elemID, configItem, cb){
192
- jQuery('.wfOnOffSwitch-checkbox').change(function(){
193
- jQuery.data(this, 'lastSwitchChange', (new Date()).getTime());
194
- });
195
- var self = this;
196
- jQuery('div.wfOnOffSwitch').mouseup( function(){
197
- var elem = jQuery(this);
198
- setTimeout(function(){
199
- var checkedElem = elem.find('.wfOnOffSwitch-checkbox');
200
- if((new Date()).getTime() - jQuery.data(checkedElem[0], 'lastSwitchChange') > 300){
201
- checkedElem.prop('checked', ! checkedElem.is(':checked') );
202
- self.updateSwitch(elemID, configItem, cb);
203
- }
204
- }, 50);
205
- });
206
- },
207
- scanTourStart: function(){
208
- var self = this;
209
- this.tour('wfWelcomeContent1', 'wfHeading', 'top', 'left', "Continue the Tour", function(){
210
- self.tour('wfWelcomeContent2', 'wfHeading', 'top', 'left', "Learn how to use Wordfence", function(){
211
- self.tour('wfWelcomeContent3', 'wfHeading', 'top', 'left', "Learn about Live Traffic", function(){ self.tourRedir('WordfenceActivity'); });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  });
213
- });
214
- },
215
- tourRedir: function(menuItem){
216
- window.location.href = 'admin.php?page=' + menuItem;
217
- },
218
- updateConfig: function(key, val, cb){
219
- this.ajax('wordfence_updateConfig', { key: key, val: val }, function(){ cb(); });
220
- },
221
- tourFinish: function(){
222
- this.ajax('wordfence_tourClosed', {}, function(res){});
223
- },
224
- downgradeLicense: function(){
225
- this.colorbox('400px', "Confirm Downgrade", "Are you sure you want to downgrade your Wordfence Premium License? This will disable all Premium features and return you to the free version of Wordfence. <a href=\"https://www.wordfence.com/manage-wordfence-api-keys/\" target=\"_blank\">Click here to renew your paid membership</a> or click the button below to confirm you want to downgrade.<br /><br /><input type=\"button\" value=\"Downgrade and disable Premium features\" onclick=\"WFAD.downgradeLicenseConfirm();\" /><br />");
226
- },
227
- downgradeLicenseConfirm: function(){
228
- jQuery.colorbox.close();
229
- this.ajax('wordfence_downgradeLicense', {}, function(res){ location.reload(true); });
230
- },
231
- tour: function(contentID, elemID, edge, align, buttonLabel, buttonCallback){
232
- var self = this;
233
- if(this.currentPointer){
234
- this.currentPointer.pointer('destroy');
235
- this.currentPointer = false;
236
- }
237
- var options = {
238
- buttons: function(event, t){
239
- var buttonElem = jQuery('<div id="wfTourButCont"><a id="pointer-close" style="margin-left:5px" class="button-secondary">End the Tour</a></div><div><a id="wfRateLink" href="http://wordpress.org/extend/plugins/wordfence/" target="_blank" style="font-size: 10px; font-family: Verdana;">Help spread the word by rating us 5&#9733; on WordPress.org</a></div>');
240
- buttonElem.find('#pointer-close').bind('click.pointer', function (evtObj) {
241
- var evtSourceElem = evtObj.srcElement ? evtObj.srcElement : evtObj.target;
242
- if(evtSourceElem.id == 'wfRateLink'){
243
- return true;
244
- }
245
- self.tourFinish();
246
- t.element.pointer('close');
247
- return false;
248
- });
249
- return buttonElem;
250
- },
251
- close: function(){},
252
- content: jQuery('#' + contentID).tmpl().html(),
253
- pointerWidth: 400,
254
- position: {
255
- edge: edge,
256
- align: align
257
- }
258
- };
259
- this.currentPointer = jQuery('#' + elemID).pointer(options).pointer('open');
260
- if(buttonLabel && buttonCallback){
261
- jQuery('#pointer-close').after('<a id="pointer-primary" class="button-primary">' + buttonLabel + '</a>');
262
- jQuery('#pointer-primary').click(buttonCallback);
263
- }
264
- },
265
- startTourAgain: function(){
266
- var self = this;
267
- this.ajax('wordfence_startTourAgain', {}, function(res){
268
- self.tourClosed = false;
269
- self.scanTourStart();
270
- });
271
- },
272
- showLoading: function(){
273
- this.loadingCount++;
274
- if(this.loadingCount == 1){
275
- jQuery('<div id="wordfenceWorking">Wordfence is working...</div>').appendTo('body');
276
- }
277
- },
278
- removeLoading: function(){
279
- this.loadingCount--;
280
- if(this.loadingCount == 0){
281
- jQuery('#wordfenceWorking').remove();
282
- }
283
- },
284
- startActivityLogUpdates: function(){
285
- var self = this;
286
- setInterval(function(){
287
- self.updateActivityLog();
288
- }, parseInt(WordfenceAdminVars.actUpdateInterval));
289
- },
290
- updateActivityLog: function(){
291
- if(this.activityLogUpdatePending){
292
- return;
293
- }
294
- this.activityLogUpdatePending = true;
295
- var self = this;
296
- this.ajax('wordfence_activityLogUpdate', {
297
- lastctime: this.lastALogCtime
298
- }, function(res){ self.doneUpdateActivityLog(res); }, function(){ self.activityLogUpdatePending = false; }, true);
299
 
300
- },
301
- doneUpdateActivityLog: function(res){
302
- this.actNextUpdateAt = (new Date()).getTime() + parseInt(WordfenceAdminVars.actUpdateInterval);
303
- if(res.ok){
304
- if(res.items.length > 0){
305
- this.activityQueue.push.apply(this.activityQueue, res.items);
306
- this.lastALogCtime = res.items[res.items.length - 1].ctime;
307
- this.processActQueue(res.currentScanID);
308
- }
309
- }
310
- this.activityLogUpdatePending = false;
311
- },
312
- processActQueue: function(currentScanID){
313
- if(this.activityQueue.length > 0){
314
- this.addActItem(this.activityQueue.shift());
315
- this.totalActAdded++;
316
- if(this.totalActAdded > this.maxActivityLogItems){
317
- jQuery('#consoleActivity div:first').remove();
318
- this.totalActAdded--;
319
- }
320
- var timeTillNextUpdate = this.actNextUpdateAt - (new Date()).getTime();
321
- var maxRate = 50 / 1000; //Rate per millisecond
322
- var bulkTotal = 0;
323
- while(this.activityQueue.length > 0 && this.activityQueue.length / timeTillNextUpdate > maxRate ){
324
- var item = this.activityQueue.shift();
325
- if(item){
326
- bulkTotal++;
327
- this.addActItem(item);
328
  }
329
- }
330
- this.totalActAdded += bulkTotal;
331
- if(this.totalActAdded > this.maxActivityLogItems){
332
- jQuery('#consoleActivity div:lt(' + bulkTotal + ')').remove();
333
- this.totalActAdded -= bulkTotal;
334
- }
335
- var minDelay = 100;
336
- var delay = minDelay;
337
- if(timeTillNextUpdate < 1){
338
- delay = minDelay;
339
- } else {
340
- delay = Math.round(timeTillNextUpdate / this.activityQueue.length);
341
- if(delay < minDelay){ delay = minDelay; }
342
- }
343
- var self = this;
344
- setTimeout(function(){ self.processActQueue(); }, delay);
345
- }
346
- jQuery('#consoleActivity').scrollTop(jQuery('#consoleActivity').prop('scrollHeight'));
347
- },
348
- processActArray: function(arr){
349
- for(var i = 0; i < arr.length; i++){
350
- this.addActItem(arr[i]);
351
- }
352
- },
353
- addActItem: function(item){
354
- if(! item){ return; }
355
- if(! item.msg){ return; }
356
- if(item.msg.indexOf('SUM_') == 0){
357
- this.processSummaryLine(item);
358
- jQuery('#consoleSummary').scrollTop(jQuery('#consoleSummary').prop('scrollHeight'));
359
- jQuery('#wfStartingScan').addClass('wfSummaryOK').html('Done.');
360
- } else if(this.debugOn || item.level < 4){
361
-
362
- var html = '<div class="wfActivityLine';
363
- if(this.debugOn){
364
- html += ' wf' + item.type;
365
- }
366
- html += '">[' + item.date + ']&nbsp;' + item.msg + '</div>';
367
- jQuery('#consoleActivity').append(html);
368
- if(/Scan complete\./i.test(item.msg)){
369
- this.loadIssues();
370
- }
371
- }
372
- },
373
- processSummaryLine: function(item){
374
- var msg, summaryUpdated;
375
- if(item.msg.indexOf('SUM_START:') != -1){
376
- msg = item.msg.replace('SUM_START:', '');
377
- jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult"><div class="wfSummaryLoading"></div></div><div class="wfClear"></div>');
378
- summaryUpdated = true;
379
- } else if(item.msg.indexOf('SUM_ENDBAD') != -1){
380
- msg = item.msg.replace('SUM_ENDBAD:', '');
381
- jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Problems found.');
382
- summaryUpdated = true;
383
- } else if(item.msg.indexOf('SUM_ENDFAILED') != -1){
384
- msg = item.msg.replace('SUM_ENDFAILED:', '');
385
- jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Failed.');
386
- summaryUpdated = true;
387
- } else if(item.msg.indexOf('SUM_ENDOK') != -1){
388
- msg = item.msg.replace('SUM_ENDOK:', '');
389
- jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Secure.');
390
- summaryUpdated = true;
391
- } else if(item.msg.indexOf('SUM_ENDSUCCESS') != -1){
392
- msg = item.msg.replace('SUM_ENDSUCCESS:', '');
393
- jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Success.');
394
- summaryUpdated = true;
395
- } else if(item.msg.indexOf('SUM_ENDERR') != -1){
396
- msg = item.msg.replace('SUM_ENDERR:', '');
397
- jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryErr').html('An error occurred.');
398
- summaryUpdated = true;
399
- } else if(item.msg.indexOf('SUM_DISABLED:') != -1){
400
- msg = item.msg.replace('SUM_DISABLED:', '');
401
- jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult">Disabled [<a href="admin.php?page=WordfenceSecOpt">Visit Options to Enable</a>]</div><div class="wfClear"></div>');
402
- summaryUpdated = true;
403
- } else if(item.msg.indexOf('SUM_PAIDONLY:') != -1){
404
- msg = item.msg.replace('SUM_PAIDONLY:', '');
405
- jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult"><a href="https://www.wordfence.com/wordfence-signup/" target="_blank">Paid Members Only</a></div><div class="wfClear"></div>');
406
- summaryUpdated = true;
407
- } else if(item.msg.indexOf('SUM_FINAL:') != -1){
408
- msg = item.msg.replace('SUM_FINAL:', '');
409
- jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg wfSummaryFinal">' + msg + '</div><div class="wfSummaryResult wfSummaryOK">Scan Complete.</div><div class="wfClear"></div>');
410
- } else if(item.msg.indexOf('SUM_PREP:') != -1){
411
- msg = item.msg.replace('SUM_PREP:', '');
412
- jQuery('#consoleSummary').empty().html('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult" id="wfStartingScan"><div class="wfSummaryLoading"></div></div><div class="wfClear"></div>');
413
- } else if(item.msg.indexOf('SUM_KILLED:') != -1){
414
- msg = item.msg.replace('SUM_KILLED:', '');
415
- jQuery('#consoleSummary').empty().html('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult wfSummaryOK">Scan Complete.</div><div class="wfClear"></div>');
416
- }
417
- },
418
- processActQueueItem: function(){
419
- var item = this.activityQueue.shift();
420
- if(item){
421
- jQuery('#consoleActivity').append('<div class="wfActivityLine wf' + item.type + '">[' + item.date + ']&nbsp;' + item.msg + '</div>');
422
- this.totalActAdded++;
423
- if(this.totalActAdded > this.maxActivityLogItems){
424
- jQuery('#consoleActivity div:first').remove();
425
- this.totalActAdded--;
426
- }
427
- if(item.msg == 'Scan complete.'){
428
- this.loadIssues();
429
- }
430
- }
431
- },
432
- updateTicker: function(forceUpdate){
433
- if( (! forceUpdate) && this.tickerUpdatePending){
434
- return;
435
- }
436
- this.tickerUpdatePending = true;
437
- var self = this;
438
- var alsoGet = '';
439
- var otherParams = '';
440
- if(this.mode == 'activity' && /^(?:404|hit|human|ruser|gCrawler|crawler|loginLogout)$/.test(this.activityMode)){
441
- alsoGet = 'logList_' + this.activityMode;
442
- otherParams = this.newestActivityTime;
443
- } else if(this.mode == 'perfStats'){
444
- alsoGet = 'perfStats';
445
- otherParams = this.newestActivityTime;
446
- }
447
- this.ajax('wordfence_ticker', {
448
- alsoGet: alsoGet,
449
- otherParams: otherParams
450
- }, function(res){ self.handleTickerReturn(res); }, function(){ self.tickerUpdatePending = false; }, true);
451
- },
452
- handleTickerReturn: function(res){
453
- this.tickerUpdatePending = false;
454
- var newMsg = "";
455
- var oldMsg = jQuery('#wfLiveStatus').text();
456
- if( res.msg ){
457
- newMsg = res.msg;
458
- } else {
459
- newMsg = "Idle";
460
- }
461
- if(newMsg && newMsg != oldMsg){
462
- jQuery('#wfLiveStatus').hide().html(newMsg).fadeIn(200);
463
- }
464
- var haveEvents, newElem;
465
- if(this.mode == 'activity'){
466
- if(res.alsoGet != 'logList_' + this.activityMode){ return; } //user switched panels since ajax request started
467
- if(res.events.length > 0){
468
- this.newestActivityTime = res.events[0]['ctime'];
469
- }
470
- haveEvents = false;
471
- if(jQuery('#wfActivity_' + this.activityMode + ' .wfActEvent').length > 0){
472
- haveEvents = true;
473
- }
474
- if(res.events.length > 0){
475
- if(! haveEvents){
476
- jQuery('#wfActivity_' + this.activityMode).empty();
477
- }
478
- for(i = res.events.length - 1; i >= 0; i--){
479
- var elemID = '#wfActEvent_' + res.events[i].id;
480
- if(jQuery(elemID).length < 1){
481
- res.events[i]['activityMode'] = this.activityMode;
482
- if(this.activityMode == 'loginLogout'){
483
- newElem = jQuery('#wfLoginLogoutEventTmpl').tmpl(res.events[i]);
484
- } else {
485
- newElem = jQuery('#wfHitsEventTmpl').tmpl(res.events[i]);
486
  }
487
- jQuery(newElem).find('.wfTimeAgo').data('wfctime', res.events[i].ctime);
488
- newElem.prependTo('#wfActivity_' + this.activityMode).fadeIn();
 
 
 
 
 
 
 
 
 
 
489
  }
 
 
 
490
  }
491
- this.reverseLookupIPs();
492
- } else {
493
- if(! haveEvents){
494
- jQuery('#wfActivity_' + this.activityMode).html('<div>No events to report yet.</div>');
 
495
  }
496
- }
497
- var self = this;
498
- jQuery('.wfTimeAgo').each(function(idx, elem){
499
- jQuery(elem).html(self.makeTimeAgo(res.serverTime - jQuery(elem).data('wfctime')) + ' ago');
500
- });
501
- } else if(this.mode == 'perfStats'){
502
- haveEvents = false;
503
- if(jQuery('#wfPerfStats .wfPerfEvent').length > 0){
504
- haveEvents = true;
505
- }
506
- if(res.events.length > 0){
507
- if(! haveEvents){
508
- jQuery('#wfPerfStats').empty();
509
- }
510
- var curLength = parseInt(jQuery('#wfPerfStats').css('width'));
511
- if(res.longestLine > curLength){
512
- jQuery('#wfPerfStats').css('width', (res.longestLine + 200) + 'px');
513
- }
514
- this.newestActivityTime = res.events[0]['ctime'];
515
- for(var i = res.events.length - 1; i >= 0; i--){
516
- res.events[i]['scale'] = this.performanceScale;
517
- res.events[i]['min'] = this.performanceMinWidth;
518
- newElem = jQuery('#wfPerfStatTmpl').tmpl(res.events[i]);
519
- jQuery(newElem).find('.wfTimeAgo').data('wfctime', res.events[i].ctime);
520
- newElem.prependTo('#wfPerfStats').fadeIn();
521
- }
522
- } else {
523
- if(! haveEvents){
524
- jQuery('#wfPerfStats').html('<p>No events to report yet.</p>');
525
  }
526
- }
527
- jQuery('.wfTimeAgo').each(function(idx, elem){
528
- jQuery(elem).html(self.makeTimeAgo(res.serverTime - jQuery(elem).data('wfctime')) + ' ago');
 
 
 
 
 
 
 
 
 
 
 
 
529
  });
530
- }
531
- },
532
- reverseLookupIPs: function(){
533
- var ips = [];
534
- jQuery('.wfReverseLookup').each(function(idx, elem){
535
- var txt = jQuery(elem).text();
536
- if(/^\d+\.\d+\.\d+\.\d+$/.test(txt) && (! jQuery(elem).data('wfReverseDone'))){
537
- jQuery(elem).data('wfReverseDone', true);
538
- ips.push(jQuery(elem).text());
539
- }
540
- });
541
- if(ips.length < 1){ return; }
542
- var uni = {};
543
- var uniqueIPs = [];
544
- for(var i = 0; i < ips.length; i++){
545
- if(! uni[ips[i]]){
546
- uni[ips[i]] = true;
547
- uniqueIPs.push(ips[i]);
548
- }
549
- }
550
- this.ajax('wordfence_reverseLookup', {
551
- ips: uniqueIPs.join(',')
552
- },
553
- function(res){
554
- if(res.ok){
555
- jQuery('.wfReverseLookup').each(function(idx, elem){
556
- var txt = jQuery(elem).text();
557
- for(var ip in res.ips){
558
- if(txt == ip){
559
- if(res.ips[ip]){
560
- jQuery(elem).html('<strong>Hostname:</strong>&nbsp;' + res.ips[ip]);
561
- } else {
562
- jQuery(elem).html('');
563
- }
564
- }
565
- }
566
- });
567
  }
568
- }, false, false);
569
- },
570
- killScan: function(){
571
- var self = this;
572
- this.ajax('wordfence_killScan', {}, function(res){
573
- if(res.ok){
574
- self.colorbox('400px', "Kill requested", "A termination request has been sent to any running scans.");
575
- } else {
576
- self.colorbox('400px', "Kill failed", "We failed to send a termination request.");
577
- }
578
- });
579
- },
580
- startScan: function(){
581
- var scanReqAnimation = setInterval(function(){
582
- var str = jQuery('#wfStartScanButton1').prop('value');
583
- var ch = str.charAt(str.length - 1);
584
- if(ch == '/'){ ch = '-'; }
585
- else if(ch == '-'){ ch = '\\'; }
586
- else if(ch == '\\'){ ch = '|'; }
587
- else if(ch == '|'){ ch = '/'; }
588
- else {ch = '/'; }
589
- jQuery('#wfStartScanButton1,#wfStartScanButton2').prop('value', "Requesting a New Scan " + ch);
590
- }, 100);
591
- setTimeout(function(res){
592
- clearInterval(scanReqAnimation);
593
- jQuery('#wfStartScanButton1,#wfStartScanButton2').prop('value', "Start a Wordfence Scan");
594
- }, 3000);
595
- this.ajax('wordfence_scan', {}, function(res){ } );
596
- },
597
- loadIssues: function(callback){
598
- if(this.mode != 'scan'){
599
- return;
600
- }
601
- var self = this;
602
- this.ajax('wordfence_loadIssues', { }, function(res){
603
- self.displayIssues(res, callback);
604
- });
605
- },
606
- sev2num: function(str){
607
- if(/wfProbSev1/.test(str)){
608
- return 1;
609
- } else if(/wfProbSev2/.test(str)){
610
- return 2;
611
- } else {
612
- return 0;
613
- }
614
- },
615
- displayIssues: function(res, callback){
616
- var self = this;
617
- try {
618
- res.summary['lastScanCompleted'] = res['lastScanCompleted'];
619
- } catch(err){
620
- res.summary['lastScanCompleted'] = 'Never';
621
- }
622
- jQuery('.wfIssuesContainer').hide();
623
- for(var issueStatus in res.issuesLists){
624
- var containerID = 'wfIssues_dataTable_' + issueStatus;
625
- var tableID = 'wfIssuesTable_' + issueStatus;
626
- if(jQuery('#' + containerID).length < 1){
627
- //Invalid issue status
628
- continue;
629
- }
630
- if(res.issuesLists[issueStatus].length < 1){
631
- if(issueStatus == 'new'){
632
- if(res.lastScanCompleted == 'ok'){
633
- jQuery('#' + containerID).html('<p style="font-size: 20px; color: #0A0;">Congratulations! No security problems were detected by Wordfence.</p>');
634
- } else if(res['lastScanCompleted']){
635
- //jQuery('#' + containerID).html('<p style="font-size: 12px; color: #A00;">The latest scan failed: ' + res.lastScanCompleted + '</p>');
636
  } else {
637
- jQuery('#' + containerID).html();
638
  }
639
-
640
- } else {
641
- jQuery('#' + containerID).html('<p>There are currently <strong>no issues</strong> being ignored on this site.</p>');
642
- }
643
- continue;
644
- }
645
- jQuery('#' + containerID).html('<table cellpadding="0" cellspacing="0" border="0" class="display" id="' + tableID + '"></table>');
646
-
647
- jQuery.fn.dataTableExt.oSort['severity-asc'] = function(y,x){ x = WFAD.sev2num(x); y = WFAD.sev2num(y); if(x < y){ return 1; } if(x > y){ return -1; } return 0; };
648
- jQuery.fn.dataTableExt.oSort['severity-desc'] = function(y,x){ x = WFAD.sev2num(x); y = WFAD.sev2num(y); if(x > y){ return 1; } if(x < y){ return -1; } return 0; };
649
-
650
- jQuery('#' + tableID).dataTable({
651
- "bFilter": false,
652
- "bInfo": false,
653
- "bPaginate": false,
654
- "bLengthChange": false,
655
- "bAutoWidth": false,
656
- "aaData": res.issuesLists[issueStatus],
657
- "aoColumns": [
658
- {
659
- "sTitle": '<div class="th_wrapp">Severity</div>',
660
- "sWidth": '128px',
661
- "sClass": "center",
662
- "sType": 'severity',
663
- "fnRender": function(obj) {
664
- var cls = 'wfProbSev' + obj.aData.severity;
665
- return '<span class="' + cls + '"></span>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
666
  }
667
- },
668
- {
669
- "sTitle": '<div class="th_wrapp">Issue</div>',
670
- "bSortable": false,
671
- "sWidth": '400px',
672
- "sType": 'html',
673
- fnRender: function(obj){
674
- var tmplName = 'issueTmpl_' + obj.aData.type;
675
- return jQuery('#' + tmplName).tmpl(obj.aData).html();
676
- }
677
- }
678
- ]
679
- });
680
- }
681
- if(callback){
682
- jQuery('#wfIssues_' + this.visibleIssuesPanel).fadeIn(500, function(){ callback(); });
683
- } else {
684
- jQuery('#wfIssues_' + this.visibleIssuesPanel).fadeIn(500);
685
- }
686
- return true;
687
- },
688
- ajax: function(action, data, cb, cbErr, noLoading){
689
- if(typeof(data) == 'string'){
690
- if(data.length > 0){
691
- data += '&';
692
- }
693
- data += 'action=' + action + '&nonce=' + this.nonce;
694
- } else if(typeof(data) == 'object'){
695
- data['action'] = action;
696
- data['nonce'] = this.nonce;
697
- }
698
- if(! cbErr){
699
- cbErr = function(){};
700
- }
701
- var self = this;
702
- if(! noLoading){
703
- this.showLoading();
704
- }
705
- jQuery.ajax({
706
- type: 'POST',
707
- url: WordfenceAdminVars.ajaxURL,
708
- dataType: "json",
709
- data: data,
710
- success: function(json){
711
- if(! noLoading){
712
- self.removeLoading();
713
- }
714
- if(json && json.nonce){
715
- self.nonce = json.nonce;
716
- }
717
- if(json && json.errorMsg){
718
- self.colorbox('400px', 'An error occurred', json.errorMsg);
719
- }
720
- cb(json);
721
- },
722
- error: function(){
723
- if(! noLoading){
724
- self.removeLoading();
725
- }
726
- cbErr();
727
- }
728
- });
729
- },
730
- colorbox: function(width, heading, body){
731
- this.colorboxQueue.push([width, heading, body]);
732
- this.colorboxServiceQueue();
733
- },
734
- colorboxServiceQueue: function(){
735
- if(this.colorboxIsOpen){ return; }
736
- if(this.colorboxQueue.length < 1){ return; }
737
- var elem = this.colorboxQueue.shift();
738
- this.colorboxOpen(elem[0], elem[1], elem[2]);
739
- },
740
- colorboxOpen: function(width, heading, body){
741
- this.colorboxIsOpen = true;
742
- jQuery.colorbox({ width: width, html: "<h3>" + heading + "</h3><p>" + body + "</p>"});
743
- },
744
- scanRunningMsg: function(){ this.colorbox('400px', "A scan is running", "A scan is currently in progress. Please wait until it finishes before starting another scan."); },
745
- errorMsg: function(msg){ this.colorbox('400px', "An error occurred:", msg); },
746
- bulkOperation: function(op){
747
- var self = this;
748
- if(op == 'del' || op == 'repair'){
749
- var ids = jQuery('input.wf' + op + 'Checkbox:checked').map(function(){ return jQuery(this).val(); }).get();
750
- if(ids.length < 1){
751
- this.colorbox('400px', "No files were selected", "You need to select files to perform a bulk operation. There is a checkbox in each issue that lets you select that file. You can then select a bulk operation and hit the button to perform that bulk operation.");
752
- return;
753
- }
754
- if(op == 'del'){
755
- this.colorbox('400px', "Are you sure you want to delete?", "Are you sure you want to delete a total of " + ids.length + " files? Do not delete files on your system unless you're ABSOLUTELY sure you know what you're doing. If you delete the wrong file it could cause your WordPress website to stop functioning and you will probably have to restore from backups. If you're unsure, Cancel and work with your hosting provider to clean your system of infected files.<br /><br /><input type=\"button\" value=\"Delete Files\" onclick=\"WFAD.bulkOperationConfirmed('" + op + "');\" />&nbsp;&nbsp;<input type=\"button\" value=\"Cancel\" onclick=\"jQuery.colorbox.close();\" /><br />");
756
- } else if(op == 'repair'){
757
- this.colorbox('400px', "Are you sure you want to repair?", "Are you sure you want to repair a total of " + ids.length + " files? Do not repair files on your system unless you're sure you have reviewed the differences between the original file and your version of the file in the files you are repairing. If you repair a file that has been customized for your system by a developer or your hosting provider it may leave your system unusable. If you're unsure, Cancel and work with your hosting provider to clean your system of infected files.<br /><br /><input type=\"button\" value=\"Repair Files\" onclick=\"WFAD.bulkOperationConfirmed('" + op + "');\" />&nbsp;&nbsp;<input type=\"button\" value=\"Cancel\" onclick=\"jQuery.colorbox.close();\" /><br />");
758
- }
759
- } else {
760
- return;
761
- }
762
- },
763
- bulkOperationConfirmed: function(op){
764
- jQuery.colorbox.close();
765
- var self = this;
766
- this.ajax('wordfence_bulkOperation', {
767
- op: op,
768
- ids: jQuery('input.wf' + op + 'Checkbox:checked').map(function(){ return jQuery(this).val(); }).get()
769
- }, function(res){ self.doneBulkOperation(res); });
770
- },
771
- doneBulkOperation: function(res){
772
- var self = this;
773
- if(res.ok){
774
- this.loadIssues(function(){ self.colorbox('400px', res.bulkHeading, res.bulkBody); });
775
- } else {
776
- this.loadIssues(function(){});
777
- }
778
- },
779
- deleteFile: function(issueID){
780
- var self = this;
781
- this.ajax('wordfence_deleteFile', {
782
- issueID: issueID
783
- }, function(res){ self.doneDeleteFile(res); });
784
- },
785
- doneDeleteFile: function(res){
786
- var cb = false;
787
- var self = this;
788
- if(res.ok){
789
- this.loadIssues(function(){ self.colorbox('400px', "Success deleting file", "The file " + res.file + " was successfully deleted."); });
790
- } else if(res.cerrorMsg){
791
- this.loadIssues(function(){ self.colorbox('400px', 'An error occurred', res.cerrorMsg); });
792
- }
793
- },
794
- restoreFile: function(issueID){
795
- var self = this;
796
- this.ajax('wordfence_restoreFile', {
797
- issueID: issueID
798
- }, function(res){ self.doneRestoreFile(res); });
799
- },
800
- doneRestoreFile: function(res){
801
- var self = this;
802
- if(res.ok){
803
- this.loadIssues(function(){ self.colorbox("400px", "File restored OK", "The file " + res.file + " was restored succesfully."); });
804
- } else if(res.cerrorMsg){
805
- this.loadIssues(function(){ self.colorbox('400px', 'An error occurred', res.cerrorMsg); });
806
- }
807
- },
808
- deleteIssue: function(id){
809
- var self = this;
810
- this.ajax('wordfence_deleteIssue', { id: id }, function(res){
811
- self.loadIssues();
812
- });
813
- },
814
- updateIssueStatus: function(id, st){
815
- var self = this;
816
- this.ajax('wordfence_updateIssueStatus', { id: id, 'status': st }, function(res){
817
- if(res.ok){
818
- self.loadIssues();
819
- }
820
- });
821
- },
822
- updateAllIssues: function(op){ // deleteIgnored, deleteNew, ignoreAllNew
823
- var head = "Please confirm";
824
- var body;
825
- if(op == 'deleteIgnored'){
826
- body = "You have chosen to remove all ignored issues. Once these issues are removed they will be re-scanned by Wordfence and if they have not been fixed, they will appear in the 'new issues' list. Are you sure you want to do this?";
827
- } else if(op == 'deleteNew'){
828
- body = "You have chosen to mark all new issues as fixed. If you have not really fixed these issues, they will reappear in the new issues list on the next scan. If you have not fixed them and want them excluded from scans you should choose to 'ignore' them instead. Are you sure you want to mark all new issues as fixed?";
829
- } else if(op == 'ignoreAllNew'){
830
- body = "You have chosen to ignore all new issues. That means they will be excluded from future scans. You should only do this if you're sure all new issues are not a problem. Are you sure you want to ignore all new issues?";
831
- } else {
832
- return;
833
- }
834
- this.colorbox('450px', head, body + '<br /><br /><center><input type="button" name="but1" value="Cancel" onclick="jQuery.colorbox.close();" />&nbsp;&nbsp;&nbsp;<input type="button" name="but2" value="Yes I\'m sure" onclick="jQuery.colorbox.close(); WFAD.confirmUpdateAllIssues(\'' + op + '\');" /><br />');
835
- },
836
- confirmUpdateAllIssues: function(op){
837
- var self = this;
838
- this.ajax('wordfence_updateAllIssues', { op: op }, function(res){ self.loadIssues(); });
839
- },
840
- es: function(val){
841
- if(val){
842
- return val;
843
- } else {
844
- return "";
845
- }
846
- },
847
- noQuotes: function(str){
848
- return str.replace(/"/g,'&#34;').replace(/\'/g, '&#145;');
849
- },
850
- commify: function(num){
851
- return ("" + num).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
852
- },
853
- switchToLiveTab: function(elem){
854
- jQuery('.wfTab1').removeClass('selected');
855
- jQuery(elem).addClass('selected');
856
- jQuery('.wfDataPanel').hide();
857
- var self = this;
858
- jQuery('#wfActivity').fadeIn(function(){ self.completeLiveTabSwitch(); });
859
- },
860
- completeLiveTabSwitch: function(){
861
- this.ajax('wordfence_loadActivityLog', {}, function(res){
862
- var html = '<a href="#" class="wfALogMailLink" onclick="WFAD.emailActivityLog(); return false;"></a><a href="#" class="wfALogReloadLink" onclick="WFAD.reloadActivityData(); return false;"></a>';
863
- if(res.events && res.events.length > 0){
864
- jQuery('#wfActivity').empty();
865
- for(var i = 0; i < res.events.length; i++){
866
- var timeTaken = '0.0000';
867
- if(res.events[i + 1]){
868
- timeTaken = (res.events[i].ctime - res.events[i + 1].ctime).toFixed(4);
869
- }
870
- var red = "";
871
- if(res.events[i].type == 'error'){
872
- red = ' class="wfWarn" ';
873
- }
874
- html += '<div ' + red + 'class="wfALogEntry"><span ' + red + 'class="wfALogTime">[' + res.events[i].type + '&nbsp;:&nbsp;' + timeTaken + '&nbsp;:&nbsp;' + res.events[i].timeAgo + ' ago]</span>&nbsp;' + res.events[i].msg + "</div>";
875
- }
876
- jQuery('#wfActivity').html(html);
877
- } else {
878
- jQuery('#wfActivity').html("<p>&nbsp;&nbsp;No activity to report yet. Please complete your first scan.</p>");
879
- }
880
- });
881
- },
882
- emailActivityLog: function(){
883
- this.colorbox('400px', 'Email Wordfence Activity Log', "Enter the email address you would like to send the Wordfence activity log to. Note that the activity log may contain thousands of lines of data. This log is usually only sent to a member of the Wordfence support team. It also contains your PHP configuration from the phpinfo() function for diagnostic data.<br /><br /><input type='text' value='support@wordfence.com' size='20' id='wfALogRecip' /><input type='button' value='Send' onclick=\"WFAD.completeEmailActivityLog();\" /><input type='button' value='Cancel' onclick='jQuery.colorbox.close();' /><br /><br />");
884
- },
885
- completeEmailActivityLog: function(){
886
- jQuery.colorbox.close();
887
- var email = jQuery('#wfALogRecip').val();
888
- if(! /^[^@]+@[^@]+$/.test(email)){
889
- alert("Please enter a valid email address.");
890
- return;
891
- }
892
- var self = this;
893
- this.ajax('wordfence_sendActivityLog', { email: jQuery('#wfALogRecip').val() }, function(res){
894
- if(res.ok){
895
- self.colorbox('400px', 'Activity Log Sent', "Your Wordfence activity log was sent to " + email + "<br /><br /><input type='button' value='Close' onclick='jQuery.colorbox.close();' /><br /><br />");
896
- }
897
- });
898
- },
899
- reloadActivityData: function(){
900
- jQuery('#wfActivity').html('<div class="wfLoadingWhite32"></div>'); //&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />
901
- this.completeLiveTabSwitch();
902
- },
903
- switchToSummaryTab: function(elem){
904
- jQuery('.wfTab1').removeClass('selected');
905
- jQuery(elem).addClass('selected');
906
- jQuery('.wfDataPanel').hide();
907
- jQuery('#wfSummaryTables').fadeIn();
908
- },
909
- switchIssuesTab: function(elem, type){
910
- jQuery('.wfTab2').removeClass('selected');
911
- jQuery('.wfIssuesContainer').hide();
912
- jQuery(elem).addClass('selected');
913
- this.visibleIssuesPanel = type;
914
- jQuery('#wfIssues_' + type).fadeIn();
915
- },
916
- switchTab: function(tabElement, tabClass, contentClass, selectedContentID, callback){
917
- jQuery('.' + tabClass).removeClass('selected');
918
- jQuery(tabElement).addClass('selected');
919
- jQuery('.' + contentClass).hide().html('<div class="wfLoadingWhite32"></div>');
920
- var func = function(){};
921
- if(callback){
922
- func = function(){ callback(); };
923
- }
924
- jQuery('#' + selectedContentID).fadeIn(func);
925
- },
926
- activityTabChanged: function(){
927
- var mode = jQuery('.wfDataPanel:visible')[0].id.replace('wfActivity_','');
928
- if(! mode){ return; }
929
- this.activityMode = mode;
930
- this.reloadActivities();
931
- },
932
- reloadActivities: function(){
933
- jQuery('#wfActivity_' + this.activityMode).html('<div class="wfLoadingWhite32"></div>');
934
- this.newestActivityTime = 0;
935
- this.updateTicker(true);
936
- },
937
- staticTabChanged: function(){
938
- var mode = jQuery('.wfDataPanel:visible')[0].id.replace('wfActivity_','');
939
- if(! mode){ return; }
940
- this.activityMode = mode;
941
-
942
- var self = this;
943
- this.ajax('wordfence_loadStaticPanel', {
944
- mode: this.activityMode
945
- }, function(res){
946
- self.completeLoadStaticPanel(res);
947
- });
948
- },
949
- completeLoadStaticPanel: function(res){
950
- var contentElem = '#wfActivity_' + this.activityMode;
951
- jQuery(contentElem).empty();
952
- if(res.results && res.results.length > 0){
953
- var tmpl;
954
- if(this.activityMode == 'topScanners' || this.activityMode == 'topLeechers'){
955
- tmpl = '#wfLeechersTmpl';
956
- } else if(this.activityMode == 'blockedIPs'){
957
- tmpl = '#wfBlockedIPsTmpl';
958
- } else if(this.activityMode == 'lockedOutIPs'){
959
- tmpl = '#wfLockedOutIPsTmpl';
960
- } else if(this.activityMode == 'throttledIPs'){
961
- tmpl = '#wfThrottledIPsTmpl';
962
- } else { return; }
963
- var i, j, chunk = 1000;
964
- var bigArray = res.results.slice(0);
965
- res.results = false;
966
- for(i = 0, j = bigArray.length; i < j; i += chunk){
967
- res.results = bigArray.slice(i, i + chunk);
968
- jQuery(tmpl).tmpl(res).appendTo(contentElem);
969
- }
970
- this.reverseLookupIPs();
971
- } else {
972
- if(this.activityMode == 'topScanners' || this.activityMode == 'topLeechers'){
973
- jQuery(contentElem).html("No site hits have been logged yet. Check back soon.");
974
- } else if(this.activityMode == 'blockedIPs'){
975
- jQuery(contentElem).html("No IP addresses have been blocked yet. If you manually block an IP address or if Wordfence automatically blocks one, it will appear here.");
976
- } else if(this.activityMode == 'lockedOutIPs'){
977
- jQuery(contentElem).html("No IP addresses have been locked out from signing in or using the password recovery system.");
978
- } else if(this.activityMode == 'throttledIPs'){
979
- jQuery(contentElem).html("No IP addresses have been throttled yet. If an IP address accesses the site too quickly and breaks one of the Wordfence rules, it will appear here.");
980
- } else { return; }
981
- }
982
- },
983
- ucfirst: function(str){
984
- str = "" + str;
985
- return str.charAt(0).toUpperCase() + str.slice(1);
986
- },
987
- makeIPTrafLink: function(IP){
988
- return WordfenceAdminVars.siteBaseURL + '?_wfsf=IPTraf&nonce=' + this.nonce + '&IP=' + encodeURIComponent(IP);
989
- },
990
- makeDiffLink: function(dat){
991
- return WordfenceAdminVars.siteBaseURL + '?_wfsf=diff&nonce=' + this.nonce +
992
- '&file=' + encodeURIComponent(this.es(dat['file'])) +
993
- '&cType=' + encodeURIComponent(this.es(dat['cType'])) +
994
- '&cKey=' + encodeURIComponent(this.es(dat['cKey'])) +
995
- '&cName=' + encodeURIComponent(this.es(dat['cName'])) +
996
- '&cVersion=' + encodeURIComponent(this.es(dat['cVersion']));
997
- },
998
- makeViewFileLink: function(file){
999
- return WordfenceAdminVars.siteBaseURL + '?_wfsf=view&nonce=' + this.nonce + '&file=' + encodeURIComponent(file);
1000
- },
1001
- makeTimeAgo: function(t){
1002
- var months = Math.floor(t / (86400 * 30));
1003
- var days = Math.floor(t / 86400);
1004
- var hours = Math.floor(t / 3600);
1005
- var minutes = Math.floor(t / 60);
1006
- if(months > 0){
1007
- days -= months * 30;
1008
- return this.pluralize(months, 'month', days, 'day');
1009
- } else if(days > 0){
1010
- hours -= days * 24;
1011
- return this.pluralize(days, 'day', hours, 'hour');
1012
- } else if(hours > 0) {
1013
- minutes -= hours * 60;
1014
- return this.pluralize(hours, 'hour', minutes, 'min');
1015
- } else if(minutes > 0) {
1016
- //t -= minutes * 60;
1017
- return this.pluralize(minutes, 'minute');
1018
- } else {
1019
- return Math.round(t) + " seconds";
1020
- }
1021
- },
1022
- pluralize: function(m1, t1, m2, t2){
1023
- if(m1 != 1) {
1024
- t1 = t1 + 's';
1025
- }
1026
- if(m2 != 1) {
1027
- t2 = t2 + 's';
1028
- }
1029
- if(m1 && m2){
1030
- return m1 + ' ' + t1 + ' ' + m2 + ' ' + t2;
1031
- } else {
1032
- return m1 + ' ' + t1;
1033
- }
1034
- },
1035
- calcRangeTotal: function(){
1036
- var range = jQuery('#ipRange').val();
1037
- if(! range){ return; }
1038
- range = range.replace(/ /g, '');
1039
- if(range && /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\s*\-\s*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(range)){
1040
- var ips = range.split('-');
1041
- var total = this.inet_aton(ips[1]) - this.inet_aton(ips[0]) + 1;
1042
- if(total < 1){
1043
- jQuery('#wfShowRangeTotal').html("<span style=\"color: #F00;\">Invalid. Starting IP is greater than ending IP.</span>");
1044
- return;
1045
- }
1046
- jQuery('#wfShowRangeTotal').html("<span style=\"color: #0A0;\">Valid: " + total + " addresses in range.</span>");
1047
- } else {
1048
- jQuery('#wfShowRangeTotal').empty();
1049
- }
1050
- },
1051
- loadBlockRanges: function(){
1052
- var self = this;
1053
- this.ajax('wordfence_loadBlockRanges', {}, function(res){ self.completeLoadBlockRanges(res); });
1054
-
1055
- },
1056
- completeLoadBlockRanges: function(res){
1057
- jQuery('#currentBlocks').empty();
1058
- if(res.results && res.results.length > 0){
1059
- jQuery('#wfBlockedRangesTmpl').tmpl(res).prependTo('#currentBlocks');
1060
- } else {
1061
- jQuery('#currentBlocks').html("You have not blocked any IP ranges or other patterns yet.");
1062
- }
1063
- },
1064
- whois: function(val){
1065
- val = val.replace(' ','');
1066
- if( ! /\w+/.test(val)){
1067
- this.colorbox('300px', "Enter a valid IP or domain", "Please enter a valid IP address or domain name for your whois lookup.");
1068
- return;
1069
- }
1070
- var self = this;
1071
- jQuery('#whoisbutton').attr('disabled', 'disabled');
1072
- jQuery('#whoisbutton').attr('value', 'Loading...');
1073
- this.ajax('wordfence_whois', {
1074
- val: val
1075
- }, function(res){
1076
- jQuery('#whoisbutton').removeAttr('disabled');
1077
- jQuery('#whoisbutton').attr('value', 'Look up IP or Domain');
1078
- if(res.ok){
1079
- self.completeWhois(res);
1080
- }
1081
- });
1082
- },
1083
- completeWhois: function(res){
1084
- if(res.ok && res.result && res.result.rawdata && res.result.rawdata.length > 0){
1085
- var rawhtml = "";
1086
- for(var i = 0; i < res.result.rawdata.length; i++){
1087
- res.result.rawdata[i] = jQuery('<div />').text(res.result.rawdata[i]).html();
1088
- res.result.rawdata[i] = res.result.rawdata[i].replace(/([^\s\t\r\n:;]+@[^\s\t\r\n:;\.]+\.[^\s\t\r\n:;]+)/, "<a href=\"mailto:$1\">$1<\/a>");
1089
- res.result.rawdata[i] = res.result.rawdata[i].replace(/(https?:\/\/[^\/]+[^\s\r\n\t]+)/, "<a target=\"_blank\" href=\"$1\">$1<\/a>");
1090
- var redStyle = "";
1091
- if(this.getQueryParam('wfnetworkblock')){
1092
- redStyle = " style=\"color: #F00;\"";
1093
  }
1094
  var self = this;
1095
- function wfm21(str, ipRange, offset, totalStr){
1096
- var ips = ipRange.split(/\s*\-\s*/);
1097
- var ip1num = self.inet_aton(ips[0]);
1098
- var ip2num = self.inet_aton(ips[1]);
1099
- var totalIPs = ip2num - ip1num + 1;
1100
- return "<a href=\"admin.php?page=WordfenceRangeBlocking&wfBlockRange=" + ipRange + "\"" + redStyle + ">" + ipRange + " [<strong>" + totalIPs + "</strong> addresses in this network. Click to block this network]<\/a>";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1101
  }
1102
-
1103
- res.result.rawdata[i] = res.result.rawdata[i].replace(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} - \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/, wfm21);
1104
- rawhtml += res.result.rawdata[i] + "<br />";
1105
- }
1106
- jQuery('#wfrawhtml').html(rawhtml);
1107
- } else {
1108
- jQuery('#wfrawhtml').html('<span style="color: #F00;">Sorry, but no data for that IP or domain was found.</span>');
1109
- }
1110
- },
1111
- blockIPUARange: function(ipRange, uaRange, referer, reason){
1112
- if(! /\w+/.test(reason)){
1113
- this.colorbox('300px', "Please specify a reason", "You forgot to include a reason you're blocking this IP range. We ask you to include this for your own record keeping.");
1114
- return;
1115
- }
1116
- ipRange = ipRange.replace(/ /g, '');
1117
- if(ipRange){
1118
- if(! /^\d+\.\d+\.\d+\.\d+\-\d+\.\d+\.\d+\.\d+$/.test(ipRange)){
1119
- this.colorbox('300px', 'Specify a valid IP range', "Please specify a valid IP address range in the form of \"1.2.3.4 - 1.2.3.5\" without quotes. Make sure the dash between the IP addresses in a normal dash (a minus sign on your keyboard) and not another character that looks like a dash.");
1120
- return;
1121
- }
1122
- }
1123
- if( ! (/\w+/.test(ipRange) || /\w+/.test(uaRange) || /\w+/.test(referer)) ){
1124
- this.colorbox('300px', 'Specify an IP range or Browser pattern', "Please specify either an IP address range or a web browser pattern to match.");
1125
- return;
1126
- }
1127
- var self = this;
1128
- this.ajax('wordfence_blockIPUARange', {
1129
- ipRange: ipRange,
1130
- uaRange: uaRange,
1131
- referer: referer,
1132
- reason: reason
1133
- }, function(res){
1134
- if(res.ok){
1135
- self.loadBlockRanges();
1136
- return;
1137
  }
1138
- });
1139
- },
1140
- unblockRange: function(id){
1141
- var self = this;
1142
- this.ajax('wordfence_unblockRange', {
1143
- id: id
1144
- }, function(res){
1145
- self.loadBlockRanges();
1146
- });
1147
- },
1148
- blockIP: function(IP, reason){
1149
- var self = this;
1150
- this.ajax('wordfence_blockIP', {
1151
- IP: IP,
1152
- reason: reason
1153
- }, function(res){
1154
- if(res.errorMsg){
1155
- return;
1156
  } else {
1157
- self.reloadActivities();
1158
- }
1159
- });
1160
- },
1161
- blockIPTwo: function(IP, reason, perm){
1162
- var self = this;
1163
- this.ajax('wordfence_blockIP', {
1164
- IP: IP,
1165
- reason: reason,
1166
- perm: (perm ? '1' : '0')
1167
- }, function(res){
1168
- if(res.errorMsg){
1169
  return;
1170
- } else {
1171
- self.staticTabChanged();
1172
  }
1173
- });
1174
- },
1175
- unlockOutIP: function(IP){
1176
- var self = this;
1177
- this.ajax('wordfence_unlockOutIP', {
1178
- IP: IP
1179
- }, function(res){ self.staticTabChanged(); });
1180
- },
1181
- unblockIP: function(IP){
1182
- var self = this;
1183
- this.ajax('wordfence_unblockIP', {
1184
- IP: IP
1185
- }, function(res){
1186
- self.reloadActivities();
1187
- });
1188
- },
1189
- unblockIPTwo: function(IP){
1190
- var self = this;
1191
- this.ajax('wordfence_unblockIP', {
1192
- IP: IP
1193
- }, function(res){
1194
- self.staticTabChanged();
1195
  });
1196
- },
1197
- permBlockIP: function(IP){
1198
- var self = this;
1199
- this.ajax('wordfence_permBlockIP', {
1200
- IP: IP
1201
- }, function(res){ self.staticTabChanged(); });
1202
- },
1203
- makeElemID: function(){
1204
- return 'wfElemGen' + this.elementGeneratorIter++;
1205
- },
1206
- pulse: function(sel){
1207
- jQuery(sel).fadeIn(function(){
1208
- setTimeout(function(){ jQuery(sel).fadeOut(); }, 2000);
1209
- });
1210
- },
1211
- getCacheStats: function(){
1212
- var self = this;
1213
- this.ajax('wordfence_getCacheStats', {}, function(res){
1214
- if(res.ok){
1215
- self.colorbox('400px', res.heading, res.body);
1216
- }
1217
- });
1218
- },
1219
- clearPageCache: function(){
1220
- var self = this;
1221
- this.ajax('wordfence_clearPageCache', {}, function(res){
1222
- if(res.ok){
1223
- self.colorbox('400px', res.heading, res.body);
1224
- }
1225
- });
1226
- },
1227
- switchToFalcon: function(){
1228
- var self = this;
1229
- this.ajax('wordfence_checkFalconHtaccess', {
1230
- }, function(res){
1231
- if(res.ok){
1232
- self.colorbox('400px', "Enabling Falcon Engine", 'First read this <a href="http://www.wordfence.com/introduction-to-wordfence-falcon-engine/" target="_blank">Introduction to Falcon Engine</a>. Falcon modifies your website configuration file which is called your .htaccess file. To enable Falcon we ask that you make a backup of this file. This is a safety precaution in case for some reason Falcon is not compatible with your site.<br /><br /><a href="' + WordfenceAdminVars.ajaxURL + '?action=wordfence_downloadHtaccess&nonce=' + self.nonce + '" onclick="jQuery(\'#wfNextBut\').prop(\'disabled\', false); return true;">Click here to download a backup copy of your .htaccess file now</a><br /><br /><input type="button" name="but1" id="wfNextBut" value="Click to Enable Falcon Engine" disabled="disabled" onclick="WFAD.confirmSwitchToFalcon(0);" />');
1233
- } else if(res.nginx){
1234
- self.colorbox('400px', "Enabling Falcon Engine", 'You are using an Nginx web server and using a FastCGI processor like PHP5-FPM. To use Falcon you will need to manually modify your nginx.conf configuration file and reload your Nginx server for the changes to take effect. You can find the <a href="http://www.wordfence.com/blog/2014/05/nginx-wordfence-falcon-engine-php-fpm-fastcgi-fast-cgi/" target="_blank">rules you need to make these changes to nginx.conf on this page on wordfence.com</a>. Once you have made these changes, compressed cached files will be served to your visitors directly from Nginx making your site extremely fast. When you have made the changes and reloaded your Nginx server, you can click the button below to enable Falcon.<br /><br /><input type="button" name="but1" id="wfNextBut" value="Click to Enable Falcon Engine" onclick="WFAD.confirmSwitchToFalcon(1);" />');
1235
- } else if(res.err){
1236
- self.colorbox('400px', "We encountered a problem", "We can't modify your .htaccess file for you because: " + res.err + "<br /><br />Advanced users: If you would like to manually enable Falcon yourself by editing your .htaccess, you can add the rules below to the beginning of your .htaccess file. Then click the button below to enable Falcon. Don't do this unless you understand website configuration.<br /><textarea style='width: 300px; height:100px;' readonly>" + jQuery('<div/>').text(res.code).html() + "</textarea><br /><input type='button' value='Enable Falcon after manually editing .htaccess' onclick='WFAD.confirmSwitchToFalcon(1);' />");
1237
- }
1238
- });
1239
- },
1240
- confirmSwitchToFalcon: function(noEditHtaccess){
1241
- jQuery.colorbox.close();
1242
- var cacheType = 'falcon';
1243
- var self = this;
1244
- this.ajax('wordfence_saveCacheConfig', {
1245
- cacheType: cacheType,
1246
- noEditHtaccess: noEditHtaccess
1247
- }, function(res){
1248
- if(res.ok){
1249
- self.colorbox('400px', res.heading, res.body);
1250
  }
1251
- }
1252
- );
1253
- },
1254
- saveCacheConfig: function(){
1255
- var cacheType = jQuery('input:radio[name=cacheType]:checked').val();
1256
- if(cacheType == 'falcon'){
1257
- return this.switchToFalcon();
1258
- }
1259
- var self = this;
1260
- this.ajax('wordfence_saveCacheConfig', {
1261
- cacheType: cacheType
1262
- }, function(res){
1263
- if(res.ok){
1264
- self.colorbox('400px', res.heading, res.body);
1265
  }
1266
- }
1267
- );
1268
- },
1269
- saveCacheOptions: function(){
1270
- var self = this;
1271
- this.ajax('wordfence_saveCacheOptions', {
1272
- allowHTTPSCaching: (jQuery('#wfallowHTTPSCaching').is(':checked') ? 1 : 0),
1273
- addCacheComment: (jQuery('#wfaddCacheComment').is(':checked') ? 1 : 0),
1274
- clearCacheSched: (jQuery('#wfclearCacheSched').is(':checked') ? 1 : 0)
1275
- }, function(res){
1276
- if(res.updateErr){
1277
- self.colorbox('400px', "You need to manually update your .htaccess", res.updateErr + "<br />Your option was updated but you need to change the Wordfence code in your .htaccess to the following:<br /><textarea style='width: 300px; height: 120px;'>" + jQuery('<div/>').text(res.code).html() + '</textarea>');
1278
  }
1279
- }
1280
- );
1281
- },
1282
- saveConfig: function(){
1283
- var qstr = jQuery('#wfConfigForm').serialize();
1284
- var self = this;
1285
- jQuery('.wfSavedMsg').hide();
1286
- jQuery('.wfAjax24').show();
1287
- this.ajax('wordfence_saveConfig', qstr, function(res){
1288
- jQuery('.wfAjax24').hide();
1289
- if(res.ok){
1290
- if(res['paidKeyMsg']){
1291
- self.colorbox('400px', "Congratulations! You have been upgraded to Premium Scanning.", "You have upgraded to a Premium API key. Once this page reloads, you can choose which premium scanning options you would like to enable and then click save. Click the button below to reload this page now.<br /><br /><center><input type='button' name='wfReload' value='Reload page and enable Premium options' onclick='window.location.reload(true);' /></center>");
1292
  return;
1293
- } else if(res['reload'] == 'reload' || WFAD.reloadConfigPage){
1294
- self.colorbox('400px', "Please reload this page", "You selected a config option that requires a page reload. Click the button below to reload this page to update the menu.<br /><br /><center><input type='button' name='wfReload' value='Reload page' onclick='window.location.reload(true);' /></center>");
 
 
 
 
 
1295
  return;
1296
- } else {
1297
- self.pulse('.wfSavedMsg');
1298
  }
1299
- } else if(res.errorMsg){
1300
- return;
1301
- } else {
1302
- self.colorbox('400px', 'An error occurred', 'We encountered an error trying to save your changes.');
1303
- }
1304
- });
1305
- },
1306
- changeSecurityLevel: function(){
1307
- var level = jQuery('#securityLevel').val();
1308
- for(var k in WFSLevels[level].checkboxes){
1309
- if(k != 'liveTraf_ignorePublishers'){
1310
- jQuery('#' + k).prop("checked", WFSLevels[level].checkboxes[k]);
1311
- }
1312
- }
1313
- for(var k in WFSLevels[level].otherParams){
1314
- if(! /^(?:apiKey|securityLevel|alertEmails|liveTraf_ignoreUsers|liveTraf_ignoreIPs|liveTraf_ignoreUA|liveTraf_hitsMaxSize|maxMem|maxExecutionTime|actUpdateInterval)$/.test(k)){
1315
- jQuery('#' + k).val(WFSLevels[level].otherParams[k]);
1316
- }
1317
- }
1318
- },
1319
- clearAllBlocked: function(op){
1320
- if(op == 'blocked'){
1321
- body = "Are you sure you want to clear all blocked IP addresses and allow visitors from those addresses to access the site again?";
1322
- } else if(op == 'locked'){
1323
- body = "Are you sure you want to clear all locked IP addresses and allow visitors from those addresses to sign in again?";
1324
- } else {
1325
- return;
1326
- }
1327
- this.colorbox('450px', "Please confirm", body +
1328
- '<br /><br /><center><input type="button" name="but1" value="Cancel" onclick="jQuery.colorbox.close();" />&nbsp;&nbsp;&nbsp;' +
1329
- '<input type="button" name="but2" value="Yes I\'m sure" onclick="jQuery.colorbox.close(); WFAD.confirmClearAllBlocked(\'' + op + '\');"><br />');
1330
- },
1331
- confirmClearAllBlocked: function(op){
1332
- var self = this;
1333
- this.ajax('wordfence_clearAllBlocked', { op: op }, function(res){
1334
- self.staticTabChanged();
1335
- });
1336
- },
1337
- setOwnCountry: function(code){
1338
- this.ownCountry = (code + "").toUpperCase();
1339
- },
1340
- loadBlockedCountries: function(str){
1341
- var codes = str.split(',');
1342
- for(var i = 0; i < codes.length; i++){
1343
- jQuery('#wfCountryCheckbox_' + codes[i]).prop('checked', true);
1344
- }
1345
- },
1346
- saveCountryBlocking: function(){
1347
- var action = jQuery('#wfBlockAction').val();
1348
- var redirURL = jQuery('#wfRedirURL').val();
1349
- var bypassRedirURL = jQuery('#wfBypassRedirURL').val();
1350
- var bypassRedirDest = jQuery('#wfBypassRedirDest').val();
1351
- var bypassViewURL = jQuery('#wfBypassViewURL').val();
1352
-
1353
- if(action == 'redir' && (! /^https?:\/\/[^\/]+/i.test(redirURL))){
1354
- this.colorbox('400px', "Please enter a URL for redirection", "You have chosen to redirect blocked countries to a specific page. You need to enter a URL in the text box provided that starts with http:// or https://");
1355
- return;
1356
- }
1357
- if( bypassRedirURL || bypassRedirDest ){
1358
- if(! (bypassRedirURL && bypassRedirDest)){
1359
- this.colorbox('400px', "Missing data from form", "If you want to set up a URL that will bypass country blocking, you must enter a URL that a visitor can hit and the destination they will be redirected to. You have only entered one of these components. Please enter both.");
1360
- return;
1361
- }
1362
- if(bypassRedirURL == bypassRedirDest){
1363
- this.colorbox('400px', "URLs are the same", "The URL that a user hits to bypass country blocking and the URL they are redirected to are the same. This would cause a circular redirect. Please fix this.");
1364
- return;
1365
- }
1366
- }
1367
- if(bypassRedirURL && (! /^(?:\/|http:\/\/)/.test(bypassRedirURL))){ this.invalidCountryURLMsg(bypassRedirURL); return; }
1368
- if(bypassRedirDest && (! /^(?:\/|http:\/\/)/.test(bypassRedirDest))){ this.invalidCountryURLMsg(bypassRedirDest); return; }
1369
- if(bypassViewURL && (! /^(?:\/|http:\/\/)/.test(bypassViewURL))){ this.invalidCountryURLMsg(bypassViewURL); return; }
1370
 
1371
- var codesArr = [];
1372
- var ownCountryBlocked = false;
1373
- var self = this;
1374
- jQuery('.wfCountryCheckbox').each(function(idx, elem){
1375
- if(jQuery(elem).is(':checked')){
1376
- var code = jQuery(elem).val();
1377
- codesArr.push(code);
1378
- if(code == self.ownCountry){
1379
- ownCountryBlocked = true;
 
 
 
 
 
 
 
 
 
1380
  }
1381
- }
1382
- });
1383
- this.countryCodesToSave = codesArr.join(',');
1384
- if(ownCountryBlocked){
1385
- this.colorbox('400px', "Please confirm blocking yourself", "You are about to block your own country. This could lead to you being locked out. Please make sure that your user profile on this machine has a current and valid email address and make sure you know what it is. That way if you are locked out, you can send yourself an unlock email. If you're sure you want to block your own country, click 'Confirm' below, otherwise click 'Cancel'.<br />" +
1386
- '<input type="button" name="but1" value="Confirm" onclick="jQuery.colorbox.close(); WFAD.confirmSaveCountryBlocking();" />&nbsp;<input type="button" name="but1" value="Cancel" onclick="jQuery.colorbox.close();" />');
1387
- } else {
1388
- this.confirmSaveCountryBlocking();
1389
- }
1390
- },
1391
- invalidCountryURLMsg: function(URL){
1392
- this.colorbox('400px', "Invalid URL", "URL's that you provide for bypassing country blocking must start with '/' or 'http://' without quotes. The URL that is invalid is: " + URL);
1393
- return;
1394
- },
1395
- confirmSaveCountryBlocking: function(){
1396
- var action = jQuery('#wfBlockAction').val();
1397
- var redirURL = jQuery('#wfRedirURL').val();
1398
- var loggedInBlocked = jQuery('#wfLoggedInBlocked').is(':checked') ? '1' : '0';
1399
- var loginFormBlocked = jQuery('#wfLoginFormBlocked').is(':checked') ? '1' : '0';
1400
- var restOfSiteBlocked = jQuery('#wfRestOfSiteBlocked').is(':checked') ? '1' : '0';
1401
- var bypassRedirURL = jQuery('#wfBypassRedirURL').val();
1402
- var bypassRedirDest = jQuery('#wfBypassRedirDest').val();
1403
- var bypassViewURL = jQuery('#wfBypassViewURL').val();
1404
 
1405
- jQuery('.wfAjax24').show();
1406
- var self = this;
1407
- this.ajax('wordfence_saveCountryBlocking', {
1408
- blockAction: action,
1409
- redirURL: redirURL,
1410
- loggedInBlocked: loggedInBlocked,
1411
- loginFormBlocked: loginFormBlocked,
1412
- restOfSiteBlocked: restOfSiteBlocked,
1413
- bypassRedirURL: bypassRedirURL,
1414
- bypassRedirDest: bypassRedirDest,
1415
- bypassViewURL: bypassViewURL,
1416
- codes: this.countryCodesToSave
1417
- }, function(res){
1418
- jQuery('.wfAjax24').hide();
1419
- self.pulse('.wfSavedMsg');
1420
  });
1421
- },
1422
- paidUsersOnly: function(msg){
1423
- var pos = jQuery('#paidWrap').position();
1424
- var width = jQuery('#paidWrap').width();
1425
- var height = jQuery('#paidWrap').height();
1426
- jQuery('<div style="position: absolute; left: ' + pos.left + 'px; top: ' + pos.top + 'px; background-color: #FFF; width: ' + width + 'px; height: ' + height + 'px;"><div class="paidInnerMsg">' + msg + ' <a href="https://www.wordfence.com/wordfence-signup/" target="_blank">Click here to upgrade and gain access to this feature.</div></div>').insertAfter('#paidWrap').fadeTo(10000, 0.7);
1427
- },
1428
- sched_modeChange: function(){
1429
- var self = this;
1430
- if(jQuery('#schedMode').val() == 'auto'){
1431
- jQuery('.wfSchedCheckbox').attr('disabled', true);
1432
- } else {
1433
- jQuery('.wfSchedCheckbox').attr('disabled', false);
1434
- }
1435
- },
1436
- sched_shortcut: function(mode){
1437
- if(jQuery('#schedMode').val() == 'auto'){
1438
- this.colorbox('400px', 'Change the scan mode', "You need to change the scan mode to manually scheduled scans if you want to select scan times.");
1439
- return;
1440
- }
1441
- jQuery('.wfSchedCheckbox').prop('checked', false);
1442
- if(this.schedStartHour === false){
1443
- this.schedStartHour = Math.floor((Math.random()*24));
1444
- } else {
1445
- this.schedStartHour++;
1446
- if(this.schedStartHour > 23){
1447
- this.schedStartHour = 0;
1448
- }
1449
- }
1450
- if(mode == 'onceDaily'){
1451
- for(var i = 0; i <= 6; i++){
1452
- jQuery('#wfSchedDay_' + i + '_' + this.schedStartHour).attr('checked', true);
1453
- }
1454
- } else if(mode == 'twiceDaily'){
1455
- var secondHour = this.schedStartHour + 12;
1456
- if(secondHour >= 24){ secondHour = secondHour - 24; }
1457
- for(var i = 0; i <= 6; i++){
1458
- jQuery('#wfSchedDay_' + i + '_' + this.schedStartHour).attr('checked', true);
1459
- jQuery('#wfSchedDay_' + i + '_' + secondHour).attr('checked', true);
1460
- }
1461
- } else if(mode == 'oddDaysWE'){
1462
- var startDay = Math.floor((Math.random()));
1463
- jQuery('#wfSchedDay_1_' + this.schedStartHour).attr('checked', true);
1464
- jQuery('#wfSchedDay_3_' + this.schedStartHour).attr('checked', true);
1465
- jQuery('#wfSchedDay_5_' + this.schedStartHour).attr('checked', true);
1466
- jQuery('#wfSchedDay_6_' + this.schedStartHour).attr('checked', true);
1467
- jQuery('#wfSchedDay_0_' + this.schedStartHour).attr('checked', true);
1468
- } else if(mode == 'weekends'){
1469
- var startDay = Math.floor((Math.random()));
1470
- jQuery('#wfSchedDay_6_' + this.schedStartHour).attr('checked', true);
1471
- jQuery('#wfSchedDay_0_' + this.schedStartHour).attr('checked', true);
1472
- } else if(mode == 'every6hours'){
1473
- for(var i = 0; i <= 6; i++){
1474
- for(var hour = this.schedStartHour; hour < this.schedStartHour + 24; hour = hour + 6){
1475
- var displayHour = hour;
1476
- if(displayHour >= 24){ displayHour = displayHour - 24; }
1477
- jQuery('#wfSchedDay_' + i + '_' + displayHour).attr('checked', true);
 
 
 
 
 
 
1478
  }
1479
- }
1480
- }
1481
 
1482
- },
1483
- sched_save: function(){
1484
- var schedMode = jQuery('#schedMode').val();
1485
- var schedule = [];
1486
- for(var day = 0; day <= 6; day++){
1487
- var hours = [];
1488
- for(var hour = 0; hour <= 23; hour++){
1489
- var elemID = '#wfSchedDay_' + day + '_' + hour;
1490
- hours[hour] = jQuery(elemID).is(':checked') ? '1' : '0';
1491
- }
1492
- schedule[day] = hours.join(',');
1493
- }
1494
- var scheduleTxt = schedule.join('|');
1495
- var self = this;
1496
- this.ajax('wordfence_saveScanSchedule', {
1497
- schedMode: schedMode,
1498
- schedTxt: scheduleTxt
1499
- }, function(res){
1500
- jQuery('#wfScanStartTime').html(res.nextStart);
1501
- jQuery('.wfAjax24').hide();
1502
- self.pulse('.wfSaveMsg');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1503
  });
1504
- },
1505
- twoFacStatus: function(msg){
1506
- jQuery('#wfTwoFacMsg').html(msg);
1507
- jQuery('#wfTwoFacMsg').fadeIn(function(){
1508
- setTimeout(function(){ jQuery('#wfTwoFacMsg').fadeOut(); }, 2000);
1509
- });
1510
- },
1511
- addTwoFactor: function(username, phone){
1512
- var self = this;
1513
- this.ajax('wordfence_addTwoFactor', {
1514
- username: username,
1515
- phone: phone
1516
- }, function(res){
1517
- if(res.ok){
1518
- self.twoFacStatus('User added! Check the user\'s phone to get the activation code.');
1519
- jQuery('<div id="twoFacCont_' + res.userID + '">' + jQuery('#wfTwoFacUserTmpl').tmpl(res).html() + '</div>').prependTo(jQuery('#wfTwoFacUsers'));
1520
- }
1521
- });
1522
- },
1523
- twoFacActivate: function(userID, code){
1524
- var self = this;
1525
- this.ajax('wordfence_twoFacActivate', {
1526
- userID: userID,
1527
- code: code
1528
- }, function(res){
1529
- if(res.ok){
1530
- jQuery('#twoFacCont_' + res.userID).html(
1531
- jQuery('#wfTwoFacUserTmpl').tmpl(res)
1532
  );
1533
- self.twoFacStatus('Cellphone Sign-in activated for user.');
1534
- }
1535
- });
1536
- },
1537
- delTwoFac: function(userID){
1538
- this.ajax('wordfence_twoFacDel', {
1539
- userID: userID
1540
- }, function(res){
1541
- if(res.ok){
1542
- jQuery('#twoFacCont_' + res.userID).fadeOut(function(){ jQuery(this).remove(); });
1543
- }
1544
- });
1545
- },
1546
- loadTwoFactor: function(){
1547
- this.ajax('wordfence_loadTwoFactor', {}, function(res){
1548
- if(res.users && res.users.length > 0){
1549
- for(var i = 0; i < res.users.length; i++){
1550
- jQuery('<div id="twoFacCont_' + res.users[i].userID + '">' +
1551
- jQuery('#wfTwoFacUserTmpl').tmpl(res.users[i]).html() +
1552
- + '</div>').appendTo(jQuery('#wfTwoFacUsers'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1553
  }
1554
- }
1555
- });
1556
- },
1557
- getQueryParam: function(name){
1558
- name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
1559
- var regexS = "[\\?&]" + name + "=([^&#]*)";
1560
- var regex = new RegExp(regexS);
1561
- var results = regex.exec(window.location.search);
1562
- if(results == null){
1563
- return "";
1564
- } else {
1565
- return decodeURIComponent(results[1].replace(/\+/g, " "));
1566
- }
1567
- },
1568
- inet_aton: function(dot) {
1569
- var d = dot.split('.');
1570
- return ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]);
1571
- },
1572
- inet_ntoa: function(num){
1573
- var d = num % 256;
1574
- for(var i = 3; i > 0; i--) {
1575
- num = Math.floor(num/256);
1576
- d = num%256 + '.' + d;
1577
- }
1578
- return d;
1579
- },
1580
- removeCacheExclusion: function(id){
1581
- this.ajax('wordfence_removeCacheExclusion', { id: id }, function(res){ window.location.reload(true); });
1582
- },
1583
- addCacheExclusion: function(patternType, pattern){
1584
- if(/^https?:\/\//.test(pattern)){
1585
- this.colorbox('400px', "Incorrect pattern for exclusion", "You can not enter full URL's for exclusion from caching. You entered a full URL that started with http:// or https://. You must enter relative URL's e.g. /exclude/this/page/. You can also enter text that might be contained in the path part of a URL or at the end of the path part of a URL.");
1586
- return;
1587
- }
1588
-
1589
- this.ajax('wordfence_addCacheExclusion', {
1590
- patternType: patternType,
1591
- pattern: pattern
1592
- }, function(res){
1593
- if(res.ok){ //Otherwise errorMsg will get caught
1594
  window.location.reload(true);
 
 
 
 
 
 
1595
  }
1596
- });
1597
- },
1598
- loadCacheExclusions: function(){
1599
- this.ajax('wordfence_loadCacheExclusions', {}, function(res){
1600
- if(res.ex instanceof Array && res.ex.length > 0){
1601
- for(var i = 0; i < res.ex.length; i++){
1602
- var newElem = jQuery('#wfCacheExclusionTmpl').tmpl(res.ex[i]);
1603
- newElem.prependTo('#wfCacheExclusions').fadeIn();
1604
- }
1605
- jQuery('<h2>Cache Exclusions</h2>').prependTo('#wfCacheExclusions');
1606
- } else {
1607
- jQuery('<h2>Cache Exclusions</h2><p style="width: 500px;">There are not currently any exclusions. If you have a site that does not change often, it is perfectly normal to not have any pages you want to exclude from the cache.</p>').prependTo('#wfCacheExclusions');
1608
- }
1609
 
1610
- });
1611
- },
1612
- exportSettings: function(){
1613
- var self = this;
1614
- this.ajax('wordfence_exportSettings', {}, function(res){
1615
- if(res.ok && res.token){
1616
- self.colorbox('400px', "Export Successful", "We successfully exported your site settings. To import your site settings on another site, copy and paste the token below into the import text box on the destination site. Keep this token secret. It is like a password. If anyone else discovers the token it will allow them to import your settings excluding your API key.<br /><br />Token:<input type=\"text\" size=\"20\" value=\"" + res.token + "\" onclick=\"this.select();\" /><br />");
1617
- } else if(res.err){
1618
- self.colorbox('400px', "Error during Export", res.err);
1619
- } else {
1620
- self.colorbox('400px', "An unknown error occurred", "An unknown error occurred during the export. We received an undefined error from your web server.");
1621
- }
1622
- });
1623
- },
1624
- importSettings: function(token){
1625
- var self = this;
1626
- this.ajax('wordfence_importSettings', { token: token }, function(res){
1627
- if(res.ok){
1628
- self.colorbox('400px', "Import Successful", "You successfully imported " + res.totalSet + " options. Your import is complete. Please reload this page or click the button below to reload it:<br /><br /><input type=\"button\" value=\"Reload Page\" onclick=\"window.location.reload(true);\" />");
1629
- } else if(res.err){
1630
- self.colorbox('400px', "Error during Import", res.err);
1631
- } else {
1632
- self.colorbox('400px', "Error during Export", "An unknown error occurred during the import");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1633
  }
1634
- });
 
1635
  }
1636
- };
1637
- window['WFAD'] = window['wordfenceAdmin'];
1638
- }
1639
- jQuery(function(){
1640
- wordfenceAdmin.init();
1641
- });
1
+ (function($) {
2
+ if (!window['wordfenceAdmin']) { //To compile for checking: java -jar /usr/local/bin/closure.jar --js=admin.js --js_output_file=test.js
3
+ window['wordfenceAdmin'] = {
4
+ loading16: '<div class="wfLoading16"></div>',
5
+ loadingCount: 0,
6
+ dbCheckTables: [],
7
+ dbCheckCount_ok: 0,
8
+ dbCheckCount_skipped: 0,
9
+ dbCheckCount_errors: 0,
10
+ issues: [],
11
+ ignoreData: false,
12
+ iconErrorMsgs: [],
13
+ scanIDLoaded: 0,
14
+ colorboxQueue: [],
15
+ mode: '',
16
+ visibleIssuesPanel: 'new',
17
+ preFirstScanMsgsLoaded: false,
18
+ newestActivityTime: 0, //must be 0 to force loading of all initially
19
+ elementGeneratorIter: 1,
20
+ reloadConfigPage: false,
21
+ nonce: false,
22
+ tickerUpdatePending: false,
23
+ activityLogUpdatePending: false,
24
+ lastALogCtime: 0,
25
+ activityQueue: [],
26
+ totalActAdded: 0,
27
+ maxActivityLogItems: 1000,
28
+ scanReqAnimation: false,
29
+ debugOn: false,
30
+ blockedCountriesPending: [],
31
+ ownCountry: "",
32
+ schedStartHour: false,
33
+ currentPointer: false,
34
+ countryMap: false,
35
+ countryCodesToSave: "",
36
+ performanceScale: 3,
37
+ performanceMinWidth: 20,
38
+ tourClosed: false,
39
+ welcomeClosed: false,
40
+ passwdAuditUpdateInt: false,
41
+ init: function() {
42
+ this.nonce = WordfenceAdminVars.firstNonce;
43
+ this.debugOn = WordfenceAdminVars.debugOn == '1' ? true : false;
44
+ this.tourClosed = WordfenceAdminVars.tourClosed == '1' ? true : false;
45
+ this.welcomeClosed = WordfenceAdminVars.welcomeClosed == '1' ? true : false;
46
+ var startTicker = false;
47
+ var self = this;
48
+ if (jQuery('#wordfenceMode_scan').length > 0) {
49
+ this.mode = 'scan';
50
+ jQuery('#wfALogViewLink').prop('href', WordfenceAdminVars.siteBaseURL + '?_wfsf=viewActivityLog&nonce=' + this.nonce);
51
+ jQuery('#consoleActivity').scrollTop(jQuery('#consoleActivity').prop('scrollHeight'));
52
+ jQuery('#consoleScan').scrollTop(jQuery('#consoleScan').prop('scrollHeight'));
53
+ this.noScanHTML = jQuery('#wfNoScanYetTmpl').tmpl().html();
54
+ this.loadIssues();
55
+ this.startActivityLogUpdates();
56
+ if (this.needTour()) {
57
+ this.scanTourStart();
58
+ }
59
+ } else if (jQuery('#wordfenceMode_activity').length > 0) {
60
+ this.mode = 'activity';
61
+ this.setupSwitches('wfLiveTrafficOnOff', 'liveTrafficEnabled', function() {
62
+ });
63
+ jQuery('#wfLiveTrafficOnOff').change(function() {
64
+ if (/^(?:falcon|php)$/.test(WordfenceAdminVars.cacheType)) {
65
+ jQuery('#wfLiveTrafficOnOff').attr('checked', false);
66
+ self.colorbox('400px', "Live Traffic not available in high performance mode", "Please note that you can't enable live traffic when Falcon Engine or basic caching is enabled. This is done for performance reasons. If you want live traffic, go to the 'Performance Setup' menu and disable caching.");
67
+ } else {
68
+ self.updateSwitch('wfLiveTrafficOnOff', 'liveTrafficEnabled', function() {
69
+ window.location.reload(true);
70
+ });
71
+ }
72
+ });
73
 
74
+ if (WordfenceAdminVars.liveTrafficEnabled) {
75
+ this.activityMode = 'hit';
76
+ } else {
77
+ this.activityMode = 'loginLogout';
78
+ this.switchTab(jQuery('#wfLoginLogoutTab'), 'wfTab1', 'wfDataPanel', 'wfActivity_loginLogout', function() {
79
+ WFAD.activityTabChanged();
80
+ });
81
+ }
82
+ startTicker = true;
83
+ if (this.needTour()) {
84
+ this.tour('wfWelcomeContent3', 'wfHeading', 'top', 'left', "Learn about Site Performance", function() {
85
+ self.tourRedir('WordfenceSitePerf');
86
+ });
87
+ }
88
+ } else if (jQuery('#wordfenceMode_options').length > 0) {
89
+ this.mode = 'options';
90
+ jQuery('.wfConfigElem').change(function() {
91
+ jQuery('#securityLevel').val('CUSTOM');
92
+ });
93
+ this.updateTicker(true);
94
+ startTicker = true;
95
+ if (this.needTour()) {
96
+ this.tour('wfContentBasicOptions', 'wfMarkerBasicOptions', 'top', 'left', "Learn about Live Traffic Options", function() {
97
+ self.tour('wfContentLiveTrafficOptions', 'wfMarkerLiveTrafficOptions', 'bottom', 'left', "Learn about Scanning Options", function() {
98
+ self.tour('wfContentScansToInclude', 'wfMarkerScansToInclude', 'bottom', 'left', "Learn about Firewall Rules", function() {
99
+ self.tour('wfContentFirewallRules', 'wfMarkerFirewallRules', 'bottom', 'left', "Learn about Login Security", function() {
100
+ self.tour('wfContentLoginSecurity', 'wfMarkerLoginSecurity', 'bottom', 'left', "Learn about Other Options", function() {
101
+ self.tour('wfContentOtherOptions', 'wfMarkerOtherOptions', 'bottom', 'left', false, false);
102
+ });
103
  });
104
  });
105
  });
106
  });
107
+ }
108
+ } else if (jQuery('#wordfenceMode_blockedIPs').length > 0) {
109
+ this.mode = 'blocked';
110
+ this.staticTabChanged();
111
+ this.updateTicker(true);
112
+ startTicker = true;
113
+ if (this.needTour()) {
114
+ this.tour('wfWelcomeContent4', 'wfHeading', 'top', 'left', "Learn about Auditing Passwords", function() {
115
+ self.tourRedir('WordfencePasswdAudit');
116
+ });
117
+ }
118
+ } else if (jQuery('#wordfenceMode_passwd').length > 0) {
119
+ this.mode = 'passwd';
120
+ startTicker = false;
121
+ this.doPasswdAuditUpdate();
122
+ if (this.needTour()) {
123
+ this.tour('wfWelcomePasswd', 'wfHeading', 'top', 'left', "Learn about Cellphone Sign-in", function() {
124
+ self.tourRedir('WordfenceTwoFactor');
125
+ });
126
+ }
127
+ } else if (jQuery('#wordfenceMode_twoFactor').length > 0) {
128
+ this.mode = 'twoFactor';
129
+ startTicker = false;
130
+ if (this.needTour()) {
131
+ this.tour('wfWelcomeTwoFactor', 'wfHeading', 'top', 'left', "Learn how to Block Countries", function() {
132
+ self.tourRedir('WordfenceCountryBlocking');
133
+ });
134
+ }
135
+ this.loadTwoFactor();
136
+
137
+ } else if (jQuery('#wordfenceMode_countryBlocking').length > 0) {
138
+ this.mode = 'countryBlocking';
139
+ startTicker = false;
140
+ if (this.needTour()) {
141
+ this.tour('wfWelcomeContentCntBlk', 'wfHeading', 'top', 'left', "Learn how to Schedule Scans", function() {
142
+ self.tourRedir('WordfenceScanSchedule');
143
+ });
144
+ }
145
+ } else if (jQuery('#wordfenceMode_rangeBlocking').length > 0) {
146
+ this.mode = 'rangeBlocking';
147
+ startTicker = false;
148
+ if (this.needTour()) {
149
+ this.tour('wfWelcomeContentRangeBlocking', 'wfHeading', 'top', 'left', "Learn how to Customize Wordfence", function() {
150
+ self.tourRedir('WordfenceSecOpt');
151
+ });
152
+ }
153
+ this.calcRangeTotal();
154
+ this.loadBlockRanges();
155
+ } else if (jQuery('#wordfenceMode_whois').length > 0) {
156
+ this.mode = 'whois';
157
+ startTicker = false;
158
+ if (this.needTour()) {
159
+ this.tour('wfWelcomeContentWhois', 'wfHeading', 'top', 'left', "Learn how to use Advanced Blocking", function() {
160
+ self.tourRedir('WordfenceRangeBlocking');
161
+ });
162
+ }
163
+ this.calcRangeTotal();
164
+ this.loadBlockRanges();
165
+
166
+ } else if (jQuery('#wordfenceMode_scanScheduling').length > 0) {
167
+ this.mode = 'scanScheduling';
168
+ startTicker = false;
169
+ this.sched_modeChange();
170
+ if (this.needTour()) {
171
+ this.tour('wfWelcomeContentScanSched', 'wfHeading', 'top', 'left', "Learn about WHOIS", function() {
172
+ self.tourRedir('WordfenceWhois');
173
+ });
174
+ }
175
+ } else if (jQuery('#wordfenceMode_caching').length > 0) {
176
+ this.mode = 'caching';
177
+ startTicker = false;
178
+ if (this.needTour()) {
179
+ this.tour('wfWelcomeContentCaching', 'wfHeading', 'top', 'left', "Learn about IP Blocking", function() {
180
+ self.tourRedir('WordfenceBlockedIPs');
181
+ });
182
+ }
183
+ this.loadCacheExclusions();
184
+ } else {
185
+ this.mode = false;
186
+ }
187
+ if (this.mode) { //We are in a Wordfence page
188
+ if (startTicker) {
189
+ this.updateTicker();
190
+ this.liveInt = setInterval(function() {
191
+ self.updateTicker();
192
+ }, WordfenceAdminVars.actUpdateInterval);
193
+ }
194
+ jQuery(document).bind('cbox_closed', function() {
195
+ self.colorboxIsOpen = false;
196
+ self.colorboxServiceQueue();
197
  });
198
+ }
199
+ },
200
+ needTour: function() {
201
+ if ((!this.tourClosed) && this.welcomeClosed) {
202
+ return true;
203
+ } else {
204
+ return false;
205
+ }
206
+ },
207
+ sendTestEmail: function(email) {
208
+ var self = this;
209
+ this.ajax('wordfence_sendTestEmail', {email: email}, function(res) {
210
+ if (res.result) {
211
+ self.colorbox('400px', "Test Email Sent", "Your test email was sent to the requested email address. The result we received from the WordPress wp_mail() function was: " +
212
+ res.result + "<br /><br />A 'True' result means WordPress thinks the mail was sent without errors. A 'False' result means that WordPress encountered an error sending your mail. Note that it's possible to get a 'True' response with an error elsewhere in your mail system that may cause emails to not be delivered.");
213
+ }
214
+ });
215
+ },
216
+ loadAvgSitePerf: function() {
217
+ var self = this;
218
+ this.ajax('wordfence_loadAvgSitePerf', {limit: jQuery('#wfAvgPerfNum').val()}, function(res) {
219
+ res['scale'] = self.performanceScale;
220
+ res['min'] = self.performanceMinWidth;
221
+ jQuery('#wfAvgSitePerfContent').empty();
222
+ var newElem = jQuery('#wfAvgPerfTmpl').tmpl(res);
223
+ newElem.prependTo('#wfAvgSitePerfContent').fadeIn();
224
+ });
225
+ },
226
+ updateSwitch: function(elemID, configItem, cb) {
227
+ var setting = jQuery('#' + elemID).is(':checked');
228
+ this.updateConfig(configItem, jQuery('#' + elemID).is(':checked') ? 1 : 0, cb);
229
+ },
230
+ setupSwitches: function(elemID, configItem, cb) {
231
+ jQuery('.wfOnOffSwitch-checkbox').change(function() {
232
+ jQuery.data(this, 'lastSwitchChange', (new Date()).getTime());
233
+ });
234
+ var self = this;
235
+ jQuery('div.wfOnOffSwitch').mouseup(function() {
236
+ var elem = jQuery(this);
237
+ setTimeout(function() {
238
+ var checkedElem = elem.find('.wfOnOffSwitch-checkbox');
239
+ if ((new Date()).getTime() - jQuery.data(checkedElem[0], 'lastSwitchChange') > 300) {
240
+ checkedElem.prop('checked', !checkedElem.is(':checked'));
241
+ self.updateSwitch(elemID, configItem, cb);
242
+ }
243
+ }, 50);
244
+ });
245
+ },
246
+ scanTourStart: function() {
247
+ var self = this;
248
+ this.tour('wfWelcomeContent1', 'wfHeading', 'top', 'left', "Continue the Tour", function() {
249
+ self.tour('wfWelcomeContent2', 'wfHeading', 'top', 'left', "Learn how to use Wordfence", function() {
250
+ self.tour('wfWelcomeContent3', 'wfHeading', 'top', 'left', "Learn about Live Traffic", function() {
251
+ self.tourRedir('WordfenceActivity');
252
+ });
253
+ });
254
+ });
255
+ },
256
+ tourRedir: function(menuItem) {
257
+ window.location.href = 'admin.php?page=' + menuItem;
258
+ },
259
+ updateConfig: function(key, val, cb) {
260
+ this.ajax('wordfence_updateConfig', {key: key, val: val}, function() {
261
+ cb();
262
+ });
263
+ },
264
+ tourFinish: function() {
265
+ this.ajax('wordfence_tourClosed', {}, function(res) {
266
+ });
267
+ },
268
+ downgradeLicense: function() {
269
+ this.colorbox('400px', "Confirm Downgrade", "Are you sure you want to downgrade your Wordfence Premium License? This will disable all Premium features and return you to the free version of Wordfence. <a href=\"https://www.wordfence.com/manage-wordfence-api-keys/\" target=\"_blank\">Click here to renew your paid membership</a> or click the button below to confirm you want to downgrade.<br /><br /><input type=\"button\" value=\"Downgrade and disable Premium features\" onclick=\"WFAD.downgradeLicenseConfirm();\" /><br />");
270
+ },
271
+ downgradeLicenseConfirm: function() {
272
+ jQuery.colorbox.close();
273
+ this.ajax('wordfence_downgradeLicense', {}, function(res) {
274
+ location.reload(true);
275
+ });
276
+ },
277
+ tour: function(contentID, elemID, edge, align, buttonLabel, buttonCallback) {
278
+ var self = this;
279
+ if (this.currentPointer) {
280
+ this.currentPointer.pointer('destroy');
281
+ this.currentPointer = false;
282
+ }
283
+ var options = {
284
+ buttons: function(event, t) {
285
+ var buttonElem = jQuery('<div id="wfTourButCont"><a id="pointer-close" style="margin-left:5px" class="button-secondary">End the Tour</a></div><div><a id="wfRateLink" href="http://wordpress.org/extend/plugins/wordfence/" target="_blank" style="font-size: 10px; font-family: Verdana;">Help spread the word by rating us 5&#9733; on WordPress.org</a></div>');
286
+ buttonElem.find('#pointer-close').bind('click.pointer', function(evtObj) {
287
+ var evtSourceElem = evtObj.srcElement ? evtObj.srcElement : evtObj.target;
288
+ if (evtSourceElem.id == 'wfRateLink') {
289
+ return true;
290
+ }
291
+ self.tourFinish();
292
+ t.element.pointer('close');
293
+ return false;
294
+ });
295
+ return buttonElem;
296
+ },
297
+ close: function() {
298
+ },
299
+ content: jQuery('#' + contentID).tmpl().html(),
300
+ pointerWidth: 400,
301
+ position: {
302
+ edge: edge,
303
+ align: align
304
+ }
305
+ };
306
+ this.currentPointer = jQuery('#' + elemID).pointer(options).pointer('open');
307
+ if (buttonLabel && buttonCallback) {
308
+ jQuery('#pointer-close').after('<a id="pointer-primary" class="button-primary">' + buttonLabel + '</a>');
309
+ jQuery('#pointer-primary').click(buttonCallback);
310
+ }
311
+ },
312
+ startTourAgain: function() {
313
+ var self = this;
314
+ this.ajax('wordfence_startTourAgain', {}, function(res) {
315
+ self.tourClosed = false;
316
+ self.scanTourStart();
317
+ });
318
+ },
319
+ showLoading: function() {
320
+ this.loadingCount++;
321
+ if (this.loadingCount == 1) {
322
+ jQuery('<div id="wordfenceWorking">Wordfence is working...</div>').appendTo('body');
323
+ }
324
+ },
325
+ removeLoading: function() {
326
+ this.loadingCount--;
327
+ if (this.loadingCount == 0) {
328
+ jQuery('#wordfenceWorking').remove();
329
+ }
330
+ },
331
+ startActivityLogUpdates: function() {
332
+ var self = this;
333
+ setInterval(function() {
334
+ self.updateActivityLog();
335
+ }, parseInt(WordfenceAdminVars.actUpdateInterval));
336
+ },
337
+ updateActivityLog: function() {
338
+ if (this.activityLogUpdatePending) {
339
+ return;
340
+ }
341
+ this.activityLogUpdatePending = true;
342
+ var self = this;
343
+ this.ajax('wordfence_activityLogUpdate', {
344
+ lastctime: this.lastALogCtime
345
+ }, function(res) {
346
+ self.doneUpdateActivityLog(res);
347
+ }, function() {
348
+ self.activityLogUpdatePending = false;
349
+ }, true);
350
 
351
+ },
352
+ doneUpdateActivityLog: function(res) {
353
+ this.actNextUpdateAt = (new Date()).getTime() + parseInt(WordfenceAdminVars.actUpdateInterval);
354
+ if (res.ok) {
355
+ if (res.items.length > 0) {
356
+ this.activityQueue.push.apply(this.activityQueue, res.items);
357
+ this.lastALogCtime = res.items[res.items.length - 1].ctime;
358
+ this.processActQueue(res.currentScanID);
359
+ }
360
+ }
361
+ this.activityLogUpdatePending = false;
362
+ },
363
+ processActQueue: function(currentScanID) {
364
+ if (this.activityQueue.length > 0) {
365
+ this.addActItem(this.activityQueue.shift());
366
+ this.totalActAdded++;
367
+ if (this.totalActAdded > this.maxActivityLogItems) {
368
+ jQuery('#consoleActivity div:first').remove();
369
+ this.totalActAdded--;
370
+ }
371
+ var timeTillNextUpdate = this.actNextUpdateAt - (new Date()).getTime();
372
+ var maxRate = 50 / 1000; //Rate per millisecond
373
+ var bulkTotal = 0;
374
+ while (this.activityQueue.length > 0 && this.activityQueue.length / timeTillNextUpdate > maxRate) {
375
+ var item = this.activityQueue.shift();
376
+ if (item) {
377
+ bulkTotal++;
378
+ this.addActItem(item);
379
+ }
380
+ }
381
+ this.totalActAdded += bulkTotal;
382
+ if (this.totalActAdded > this.maxActivityLogItems) {
383
+ jQuery('#consoleActivity div:lt(' + bulkTotal + ')').remove();
384
+ this.totalActAdded -= bulkTotal;
385
+ }
386
+ var minDelay = 100;
387
+ var delay = minDelay;
388
+ if (timeTillNextUpdate < 1) {
389
+ delay = minDelay;
390
+ } else {
391
+ delay = Math.round(timeTillNextUpdate / this.activityQueue.length);
392
+ if (delay < minDelay) {
393
+ delay = minDelay;
394
+ }
395
+ }
396
+ var self = this;
397
+ setTimeout(function() {
398
+ self.processActQueue();
399
+ }, delay);
400
+ }
401
+ jQuery('#consoleActivity').scrollTop(jQuery('#consoleActivity').prop('scrollHeight'));
402
+ },
403
+ processActArray: function(arr) {
404
+ for (var i = 0; i < arr.length; i++) {
405
+ this.addActItem(arr[i]);
406
+ }
407
+ },
408
+ addActItem: function(item) {
409
+ if (!item) {
410
+ return;
411
+ }
412
+ if (!item.msg) {
413
+ return;
414
+ }
415
+ if (item.msg.indexOf('SUM_') == 0) {
416
+ this.processSummaryLine(item);
417
+ jQuery('#consoleSummary').scrollTop(jQuery('#consoleSummary').prop('scrollHeight'));
418
+ jQuery('#wfStartingScan').addClass('wfSummaryOK').html('Done.');
419
+ } else if (this.debugOn || item.level < 4) {
420
 
421
+ var html = '<div class="wfActivityLine';
422
+ if (this.debugOn) {
423
+ html += ' wf' + item.type;
424
+ }
425
+ html += '">[' + item.date + ']&nbsp;' + item.msg + '</div>';
426
+ jQuery('#consoleActivity').append(html);
427
+ if (/Scan complete\./i.test(item.msg)) {
428
+ this.loadIssues();
429
+ }
430
+ }
431
+ },
432
+ processSummaryLine: function(item) {
433
+ var msg, summaryUpdated;
434
+ if (item.msg.indexOf('SUM_START:') != -1) {
435
+ msg = item.msg.replace('SUM_START:', '');
436
+ jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult"><div class="wfSummaryLoading"></div></div><div class="wfClear"></div>');
437
+ summaryUpdated = true;
438
+ } else if (item.msg.indexOf('SUM_ENDBAD') != -1) {
439
+ msg = item.msg.replace('SUM_ENDBAD:', '');
440
+ jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Problems found.');
441
+ summaryUpdated = true;
442
+ } else if (item.msg.indexOf('SUM_ENDFAILED') != -1) {
443
+ msg = item.msg.replace('SUM_ENDFAILED:', '');
444
+ jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Failed.');
445
+ summaryUpdated = true;
446
+ } else if (item.msg.indexOf('SUM_ENDOK') != -1) {
447
+ msg = item.msg.replace('SUM_ENDOK:', '');
448
+ jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Secure.');
449
+ summaryUpdated = true;
450
+ } else if (item.msg.indexOf('SUM_ENDSUCCESS') != -1) {
451
+ msg = item.msg.replace('SUM_ENDSUCCESS:', '');
452
+ jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Success.');
453
+ summaryUpdated = true;
454
+ } else if (item.msg.indexOf('SUM_ENDERR') != -1) {
455
+ msg = item.msg.replace('SUM_ENDERR:', '');
456
+ jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryErr').html('An error occurred.');
457
+ summaryUpdated = true;
458
+ } else if (item.msg.indexOf('SUM_DISABLED:') != -1) {
459
+ msg = item.msg.replace('SUM_DISABLED:', '');
460
+ jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult">Disabled [<a href="admin.php?page=WordfenceSecOpt">Visit Options to Enable</a>]</div><div class="wfClear"></div>');
461
+ summaryUpdated = true;
462
+ } else if (item.msg.indexOf('SUM_PAIDONLY:') != -1) {
463
+ msg = item.msg.replace('SUM_PAIDONLY:', '');
464
+ jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult"><a href="https://www.wordfence.com/wordfence-signup/" target="_blank">Paid Members Only</a></div><div class="wfClear"></div>');
465
+ summaryUpdated = true;
466
+ } else if (item.msg.indexOf('SUM_FINAL:') != -1) {
467
+ msg = item.msg.replace('SUM_FINAL:', '');
468
+ jQuery('#consoleSummary').append('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg wfSummaryFinal">' + msg + '</div><div class="wfSummaryResult wfSummaryOK">Scan Complete.</div><div class="wfClear"></div>');
469
+ } else if (item.msg.indexOf('SUM_PREP:') != -1) {
470
+ msg = item.msg.replace('SUM_PREP:', '');
471
+ jQuery('#consoleSummary').empty().html('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult" id="wfStartingScan"><div class="wfSummaryLoading"></div></div><div class="wfClear"></div>');
472
+ } else if (item.msg.indexOf('SUM_KILLED:') != -1) {
473
+ msg = item.msg.replace('SUM_KILLED:', '');
474
+ jQuery('#consoleSummary').empty().html('<div class="wfSummaryLine"><div class="wfSummaryDate">[' + item.date + ']</div><div class="wfSummaryMsg">' + msg + '</div><div class="wfSummaryResult wfSummaryOK">Scan Complete.</div><div class="wfClear"></div>');
475
+ }
476
+ },
477
+ processActQueueItem: function() {
478
+ var item = this.activityQueue.shift();
479
+ if (item) {
480
+ jQuery('#consoleActivity').append('<div class="wfActivityLine wf' + item.type + '">[' + item.date + ']&nbsp;' + item.msg + '</div>');
481
+ this.totalActAdded++;
482
+ if (this.totalActAdded > this.maxActivityLogItems) {
483
+ jQuery('#consoleActivity div:first').remove();
484
+ this.totalActAdded--;
485
+ }
486
+ if (item.msg == 'Scan complete.') {
487
+ this.loadIssues();
488
+ }
489
+ }
490
+ },
491
+ updateTicker: function(forceUpdate) {
492
+ if ((!forceUpdate) && this.tickerUpdatePending) {
493
+ return;
494
+ }
495
+ this.tickerUpdatePending = true;
496
+ var self = this;
497
+ var alsoGet = '';
498
+ var otherParams = '';
499
+ if (this.mode == 'activity' && /^(?:404|hit|human|ruser|gCrawler|crawler|loginLogout)$/.test(this.activityMode)) {
500
+ alsoGet = 'logList_' + this.activityMode;
501
+ otherParams = this.newestActivityTime;
502
+ } else if (this.mode == 'perfStats') {
503
+ alsoGet = 'perfStats';
504
+ otherParams = this.newestActivityTime;
505
+ }
506
+ this.ajax('wordfence_ticker', {
507
+ alsoGet: alsoGet,
508
+ otherParams: otherParams
509
+ }, function(res) {
510
+ self.handleTickerReturn(res);
511
+ }, function() {
512
+ self.tickerUpdatePending = false;
513
+ }, true);
514
+ },
515
+ handleTickerReturn: function(res) {
516
+ this.tickerUpdatePending = false;
517
+ var newMsg = "";
518
+ var oldMsg = jQuery('#wfLiveStatus').text();
519
+ if (res.msg) {
520
+ newMsg = res.msg;
521
+ } else {
522
+ newMsg = "Idle";
523
+ }
524
+ if (newMsg && newMsg != oldMsg) {
525
+ jQuery('#wfLiveStatus').hide().html(newMsg).fadeIn(200);
526
+ }
527
+ var haveEvents, newElem;
528
+ if (this.mode == 'activity') {
529
+ if (res.alsoGet != 'logList_' + this.activityMode) {
530
+ return;
531
+ } //user switched panels since ajax request started
532
+ if (res.events.length > 0) {
533
+ this.newestActivityTime = res.events[0]['ctime'];
534
+ }
535
+ haveEvents = false;
536
+ if (jQuery('#wfActivity_' + this.activityMode + ' .wfActEvent').length > 0) {
537
+ haveEvents = true;
538
+ }
539
+ if (res.events.length > 0) {
540
+ if (!haveEvents) {
541
+ jQuery('#wfActivity_' + this.activityMode).empty();
542
+ }
543
+ for (i = res.events.length - 1; i >= 0; i--) {
544
+ var elemID = '#wfActEvent_' + res.events[i].id;
545
+ if (jQuery(elemID).length < 1) {
546
+ res.events[i]['activityMode'] = this.activityMode;
547
+ if (this.activityMode == 'loginLogout') {
548
+ newElem = jQuery('#wfLoginLogoutEventTmpl').tmpl(res.events[i]);
549
+ } else {
550
+ newElem = jQuery('#wfHitsEventTmpl').tmpl(res.events[i]);
551
+ }
552
+ jQuery(newElem).find('.wfTimeAgo').data('wfctime', res.events[i].ctime);
553
+ newElem.prependTo('#wfActivity_' + this.activityMode).fadeIn();
554
+ }
555
+ }
556
+ this.reverseLookupIPs();
557
+ } else {
558
+ if (!haveEvents) {
559
+ jQuery('#wfActivity_' + this.activityMode).html('<div>No events to report yet.</div>');
560
+ }
561
+ }
562
+ var self = this;
563
+ jQuery('.wfTimeAgo').each(function(idx, elem) {
564
+ jQuery(elem).html(self.makeTimeAgo(res.serverTime - jQuery(elem).data('wfctime')) + ' ago');
565
+ });
566
+ } else if (this.mode == 'perfStats') {
567
+ haveEvents = false;
568
+ if (jQuery('#wfPerfStats .wfPerfEvent').length > 0) {
569
+ haveEvents = true;
570
+ }
571
+ if (res.events.length > 0) {
572
+ if (!haveEvents) {
573
+ jQuery('#wfPerfStats').empty();
574
+ }
575
+ var curLength = parseInt(jQuery('#wfPerfStats').css('width'));
576
+ if (res.longestLine > curLength) {
577
+ jQuery('#wfPerfStats').css('width', (res.longestLine + 200) + 'px');
578
+ }
579
+ this.newestActivityTime = res.events[0]['ctime'];
580
+ for (var i = res.events.length - 1; i >= 0; i--) {
581
+ res.events[i]['scale'] = this.performanceScale;
582
+ res.events[i]['min'] = this.performanceMinWidth;
583
+ newElem = jQuery('#wfPerfStatTmpl').tmpl(res.events[i]);
584
+ jQuery(newElem).find('.wfTimeAgo').data('wfctime', res.events[i].ctime);
585
+ newElem.prependTo('#wfPerfStats').fadeIn();
586
+ }
587
+ } else {
588
+ if (!haveEvents) {
589
+ jQuery('#wfPerfStats').html('<p>No events to report yet.</p>');
590
+ }
591
+ }
592
+ jQuery('.wfTimeAgo').each(function(idx, elem) {
593
+ jQuery(elem).html(self.makeTimeAgo(res.serverTime - jQuery(elem).data('wfctime')) + ' ago');
594
+ });
595
+ }
596
+ },
597
+ reverseLookupIPs: function() {
598
+ var ips = [];
599
+ jQuery('.wfReverseLookup').each(function(idx, elem) {
600
+ var txt = jQuery(elem).text();
601
+ if (/^\d+\.\d+\.\d+\.\d+$/.test(txt) && (!jQuery(elem).data('wfReverseDone'))) {
602
+ jQuery(elem).data('wfReverseDone', true);
603
+ ips.push(jQuery(elem).text());
604
+ }
605
+ });
606
+ if (ips.length < 1) {
607
+ return;
608
+ }
609
+ var uni = {};
610
+ var uniqueIPs = [];
611
+ for (var i = 0; i < ips.length; i++) {
612
+ if (!uni[ips[i]]) {
613
+ uni[ips[i]] = true;
614
+ uniqueIPs.push(ips[i]);
615
+ }
616
+ }
617
+ this.ajax('wordfence_reverseLookup', {
618
+ ips: uniqueIPs.join(',')
619
+ },
620
+ function(res) {
621
+ if (res.ok) {
622
+ jQuery('.wfReverseLookup').each(function(idx, elem) {
623
+ var txt = jQuery(elem).text();
624
+ for (var ip in res.ips) {
625
+ if (txt == ip) {
626
+ if (res.ips[ip]) {
627
+ jQuery(elem).html('<strong>Hostname:</strong>&nbsp;' + res.ips[ip]);
628
+ } else {
629
+ jQuery(elem).html('');
630
+ }
631
+ }
632
+ }
633
+ });
634
+ }
635
+ }, false, false);
636
+ },
637
+ killScan: function() {
638
+ var self = this;
639
+ this.ajax('wordfence_killScan', {}, function(res) {
640
+ if (res.ok) {
641
+ self.colorbox('400px', "Kill requested", "A termination request has been sent to any running scans.");
642
+ } else {
643
+ self.colorbox('400px', "Kill failed", "We failed to send a termination request.");
644
+ }
645
+ });
646
+ },
647
+ startScan: function() {
648
+ var scanReqAnimation = setInterval(function() {
649
+ var str = jQuery('#wfStartScanButton1').prop('value');
650
+ var ch = str.charAt(str.length - 1);
651
+ if (ch == '/') {
652
+ ch = '-';
653
+ }
654
+ else if (ch == '-') {
655
+ ch = '\\';
656
+ }
657
+ else if (ch == '\\') {
658
+ ch = '|';
659
+ }
660
+ else if (ch == '|') {
661
+ ch = '/';
662
+ }
663
+ else {
664
+ ch = '/';
665
+ }
666
+ jQuery('#wfStartScanButton1,#wfStartScanButton2').prop('value', "Requesting a New Scan " + ch);
667
+ }, 100);
668
+ setTimeout(function(res) {
669
+ clearInterval(scanReqAnimation);
670
+ jQuery('#wfStartScanButton1,#wfStartScanButton2').prop('value', "Start a Wordfence Scan");
671
+ }, 3000);
672
+ this.ajax('wordfence_scan', {}, function(res) {
673
+ });
674
+ },
675
+ displayPWAuditJobs: function(res) {
676
+ if (res && res.results && res.results.length > 0) {
677
+ var wfAuditJobs = $('#wfAuditJobs');
678
+ jQuery('#wfAuditJobs').empty();
679
+ jQuery('#wfAuditJobsTable').tmpl().appendTo(wfAuditJobs);
680
+ var wfAuditJobsBody = wfAuditJobs.find('.wf-pw-audit-tbody');
681
+ for (var i = 0; i < res.results.length; i++) {
682
+ jQuery('#wfAuditJobsInProg').tmpl(res.results[i]).appendTo(wfAuditJobsBody);
683
+ }
684
+ } else {
685
+ jQuery('#wfAuditJobs').empty().html("<p>You don't have any password auditing jobs in progress or completed yet.</p>");
686
+ }
687
+ },
688
+ loadIssues: function(callback) {
689
+ if (this.mode != 'scan') {
690
+ return;
691
+ }
692
+ var self = this;
693
+ this.ajax('wordfence_loadIssues', {}, function(res) {
694
+ self.displayIssues(res, callback);
695
+ });
696
+ },
697
+ sev2num: function(str) {
698
+ if (/wfProbSev1/.test(str)) {
699
+ return 1;
700
+ } else if (/wfProbSev2/.test(str)) {
701
+ return 2;
702
+ } else {
703
+ return 0;
704
+ }
705
+ },
706
+ displayIssues: function(res, callback) {
707
+ var self = this;
708
+ try {
709
+ res.summary['lastScanCompleted'] = res['lastScanCompleted'];
710
+ } catch (err) {
711
+ res.summary['lastScanCompleted'] = 'Never';
712
+ }
713
+ jQuery('.wfIssuesContainer').hide();
714
+ for (var issueStatus in res.issuesLists) {
715
+ var containerID = 'wfIssues_dataTable_' + issueStatus;
716
+ var tableID = 'wfIssuesTable_' + issueStatus;
717
+ if (jQuery('#' + containerID).length < 1) {
718
+ //Invalid issue status
719
+ continue;
720
+ }
721
+ if (res.issuesLists[issueStatus].length < 1) {
722
+ if (issueStatus == 'new') {
723
+ if (res.lastScanCompleted == 'ok') {
724
+ jQuery('#' + containerID).html('<p style="font-size: 20px; color: #0A0;">Congratulations! No security problems were detected by Wordfence.</p>');
725
+ } else if (res['lastScanCompleted']) {
726
+ //jQuery('#' + containerID).html('<p style="font-size: 12px; color: #A00;">The latest scan failed: ' + res.lastScanCompleted + '</p>');
727
+ } else {
728
+ jQuery('#' + containerID).html();
729
+ }
730
+
731
+ } else {
732
+ jQuery('#' + containerID).html('<p>There are currently <strong>no issues</strong> being ignored on this site.</p>');
733
+ }
734
+ continue;
735
+ }
736
+ jQuery('#' + containerID).html('<table cellpadding="0" cellspacing="0" border="0" class="display" id="' + tableID + '"></table>');
737
+
738
+ jQuery.fn.dataTableExt.oSort['severity-asc'] = function(y, x) {
739
+ x = WFAD.sev2num(x);
740
+ y = WFAD.sev2num(y);
741
+ if (x < y) {
742
+ return 1;
743
+ }
744
+ if (x > y) {
745
+ return -1;
746
+ }
747
+ return 0;
748
+ };
749
+ jQuery.fn.dataTableExt.oSort['severity-desc'] = function(y, x) {
750
+ x = WFAD.sev2num(x);
751
+ y = WFAD.sev2num(y);
752
+ if (x > y) {
753
+ return 1;
754
+ }
755
+ if (x < y) {
756
+ return -1;
757
+ }
758
+ return 0;
759
+ };
760
+
761
+ jQuery('#' + tableID).dataTable({
762
+ "bFilter": false,
763
+ "bInfo": false,
764
+ "bPaginate": false,
765
+ "bLengthChange": false,
766
+ "bAutoWidth": false,
767
+ "aaData": res.issuesLists[issueStatus],
768
+ "aoColumns": [
769
+ {
770
+ "sTitle": '<div class="th_wrapp">Severity</div>',
771
+ "sWidth": '128px',
772
+ "sClass": "center",
773
+ "sType": 'severity',
774
+ "fnRender": function(obj) {
775
+ var cls = 'wfProbSev' + obj.aData.severity;
776
+ return '<span class="' + cls + '"></span>';
777
+ }
778
+ },
779
+ {
780
+ "sTitle": '<div class="th_wrapp">Issue</div>',
781
+ "bSortable": false,
782
+ "sWidth": '400px',
783
+ "sType": 'html',
784
+ fnRender: function(obj) {
785
+ var tmplName = 'issueTmpl_' + obj.aData.type;
786
+ return jQuery('#' + tmplName).tmpl(obj.aData).html();
787
+ }
788
+ }
789
+ ]
790
+ });
791
+ }
792
+ if (callback) {
793
+ jQuery('#wfIssues_' + this.visibleIssuesPanel).fadeIn(500, function() {
794
+ callback();
795
+ });
796
+ } else {
797
+ jQuery('#wfIssues_' + this.visibleIssuesPanel).fadeIn(500);
798
+ }
799
+ return true;
800
+ },
801
+ ajax: function(action, data, cb, cbErr, noLoading) {
802
+ if (typeof(data) == 'string') {
803
+ if (data.length > 0) {
804
+ data += '&';
805
+ }
806
+ data += 'action=' + action + '&nonce=' + this.nonce;
807
+ } else if (typeof(data) == 'object') {
808
+ data['action'] = action;
809
+ data['nonce'] = this.nonce;
810
+ }
811
+ if (!cbErr) {
812
+ cbErr = function() {
813
+ };
814
+ }
815
+ var self = this;
816
+ if (!noLoading) {
817
+ this.showLoading();
818
+ }
819
+ jQuery.ajax({
820
+ type: 'POST',
821
+ url: WordfenceAdminVars.ajaxURL,
822
+ dataType: "json",
823
+ data: data,
824
+ success: function(json) {
825
+ if (!noLoading) {
826
+ self.removeLoading();
827
+ }
828
+ if (json && json.nonce) {
829
+ self.nonce = json.nonce;
830
+ }
831
+ if (json && json.errorMsg) {
832
+ self.colorbox('400px', 'An error occurred', json.errorMsg);
833
+ }
834
+ cb(json);
835
+ },
836
+ error: function() {
837
+ if (!noLoading) {
838
+ self.removeLoading();
839
+ }
840
+ cbErr();
841
+ }
842
+ });
843
+ },
844
+ colorbox: function(width, heading, body) {
845
+ this.colorboxQueue.push([width, heading, body]);
846
+ this.colorboxServiceQueue();
847
+ },
848
+ colorboxServiceQueue: function() {
849
+ if (this.colorboxIsOpen) {
850
+ return;
851
+ }
852
+ if (this.colorboxQueue.length < 1) {
853
+ return;
854
+ }
855
+ var elem = this.colorboxQueue.shift();
856
+ this.colorboxOpen(elem[0], elem[1], elem[2]);
857
+ },
858
+ colorboxOpen: function(width, heading, body) {
859
+ this.colorboxIsOpen = true;
860
+ jQuery.colorbox({width: width, html: "<h3>" + heading + "</h3><p>" + body + "</p>"});
861
+ },
862
+ scanRunningMsg: function() {
863
+ this.colorbox('400px', "A scan is running", "A scan is currently in progress. Please wait until it finishes before starting another scan.");
864
+ },
865
+ errorMsg: function(msg) {
866
+ this.colorbox('400px', "An error occurred:", msg);
867
+ },
868
+ bulkOperation: function(op) {
869
+ var self = this;
870
+ if (op == 'del' || op == 'repair') {
871
+ var ids = jQuery('input.wf' + op + 'Checkbox:checked').map(function() {
872
+ return jQuery(this).val();
873
+ }).get();
874
+ if (ids.length < 1) {
875
+ this.colorbox('400px', "No files were selected", "You need to select files to perform a bulk operation. There is a checkbox in each issue that lets you select that file. You can then select a bulk operation and hit the button to perform that bulk operation.");
876
+ return;
877
+ }
878
+ if (op == 'del') {
879
+ this.colorbox('400px', "Are you sure you want to delete?", "Are you sure you want to delete a total of " + ids.length + " files? Do not delete files on your system unless you're ABSOLUTELY sure you know what you're doing. If you delete the wrong file it could cause your WordPress website to stop functioning and you will probably have to restore from backups. If you're unsure, Cancel and work with your hosting provider to clean your system of infected files.<br /><br /><input type=\"button\" value=\"Delete Files\" onclick=\"WFAD.bulkOperationConfirmed('" + op + "');\" />&nbsp;&nbsp;<input type=\"button\" value=\"Cancel\" onclick=\"jQuery.colorbox.close();\" /><br />");
880
+ } else if (op == 'repair') {
881
+ this.colorbox('400px', "Are you sure you want to repair?", "Are you sure you want to repair a total of " + ids.length + " files? Do not repair files on your system unless you're sure you have reviewed the differences between the original file and your version of the file in the files you are repairing. If you repair a file that has been customized for your system by a developer or your hosting provider it may leave your system unusable. If you're unsure, Cancel and work with your hosting provider to clean your system of infected files.<br /><br /><input type=\"button\" value=\"Repair Files\" onclick=\"WFAD.bulkOperationConfirmed('" + op + "');\" />&nbsp;&nbsp;<input type=\"button\" value=\"Cancel\" onclick=\"jQuery.colorbox.close();\" /><br />");
882
+ }
883
+ } else {
884
+ return;
885
+ }
886
+ },
887
+ bulkOperationConfirmed: function(op) {
888
+ jQuery.colorbox.close();
889
+ var self = this;
890
+ this.ajax('wordfence_bulkOperation', {
891
+ op: op,
892
+ ids: jQuery('input.wf' + op + 'Checkbox:checked').map(function() {
893
+ return jQuery(this).val();
894
+ }).get()
895
+ }, function(res) {
896
+ self.doneBulkOperation(res);
897
+ });
898
+ },
899
+ doneBulkOperation: function(res) {
900
+ var self = this;
901
+ if (res.ok) {
902
+ this.loadIssues(function() {
903
+ self.colorbox('400px', res.bulkHeading, res.bulkBody);
904
+ });
905
+ } else {
906
+ this.loadIssues(function() {
907
+ });
908
+ }
909
+ },
910
+ deleteFile: function(issueID) {
911
+ var self = this;
912
+ this.ajax('wordfence_deleteFile', {
913
+ issueID: issueID
914
+ }, function(res) {
915
+ self.doneDeleteFile(res);
916
+ });
917
+ },
918
+ doneDeleteFile: function(res) {
919
+ var cb = false;
920
+ var self = this;
921
+ if (res.ok) {
922
+ this.loadIssues(function() {
923
+ self.colorbox('400px', "Success deleting file", "The file " + res.file + " was successfully deleted.");
924
+ });
925
+ } else if (res.cerrorMsg) {
926
+ this.loadIssues(function() {
927
+ self.colorbox('400px', 'An error occurred', res.cerrorMsg);
928
+ });
929
+ }
930
+ },
931
+ restoreFile: function(issueID) {
932
+ var self = this;
933
+ this.ajax('wordfence_restoreFile', {
934
+ issueID: issueID
935
+ }, function(res) {
936
+ self.doneRestoreFile(res);
937
+ });
938
+ },
939
+ doneRestoreFile: function(res) {
940
+ var self = this;
941
+ if (res.ok) {
942
+ this.loadIssues(function() {
943
+ self.colorbox("400px", "File restored OK", "The file " + res.file + " was restored succesfully.");
944
+ });
945
+ } else if (res.cerrorMsg) {
946
+ this.loadIssues(function() {
947
+ self.colorbox('400px', 'An error occurred', res.cerrorMsg);
948
+ });
949
+ }
950
+ },
951
+ deleteIssue: function(id) {
952
+ var self = this;
953
+ this.ajax('wordfence_deleteIssue', {id: id}, function(res) {
954
+ self.loadIssues();
955
+ });
956
+ },
957
+ updateIssueStatus: function(id, st) {
958
+ var self = this;
959
+ this.ajax('wordfence_updateIssueStatus', {id: id, 'status': st}, function(res) {
960
+ if (res.ok) {
961
+ self.loadIssues();
962
+ }
963
+ });
964
+ },
965
+ updateAllIssues: function(op) { // deleteIgnored, deleteNew, ignoreAllNew
966
+ var head = "Please confirm";
967
+ var body;
968
+ if (op == 'deleteIgnored') {
969
+ body = "You have chosen to remove all ignored issues. Once these issues are removed they will be re-scanned by Wordfence and if they have not been fixed, they will appear in the 'new issues' list. Are you sure you want to do this?";
970
+ } else if (op == 'deleteNew') {
971
+ body = "You have chosen to mark all new issues as fixed. If you have not really fixed these issues, they will reappear in the new issues list on the next scan. If you have not fixed them and want them excluded from scans you should choose to 'ignore' them instead. Are you sure you want to mark all new issues as fixed?";
972
+ } else if (op == 'ignoreAllNew') {
973
+ body = "You have chosen to ignore all new issues. That means they will be excluded from future scans. You should only do this if you're sure all new issues are not a problem. Are you sure you want to ignore all new issues?";
974
+ } else {
975
+ return;
976
+ }
977
+ this.colorbox('450px', head, body + '<br /><br /><center><input type="button" name="but1" value="Cancel" onclick="jQuery.colorbox.close();" />&nbsp;&nbsp;&nbsp;<input type="button" name="but2" value="Yes I\'m sure" onclick="jQuery.colorbox.close(); WFAD.confirmUpdateAllIssues(\'' + op + '\');" /><br />');
978
+ },
979
+ confirmUpdateAllIssues: function(op) {
980
+ var self = this;
981
+ this.ajax('wordfence_updateAllIssues', {op: op}, function(res) {
982
+ self.loadIssues();
983
+ });
984
+ },
985
+ es: function(val) {
986
+ if (val) {
987
+ return val;
988
+ } else {
989
+ return "";
990
+ }
991
+ },
992
+ noQuotes: function(str) {
993
+ return str.replace(/"/g, '&#34;').replace(/\'/g, '&#145;');
994
+ },
995
+ commify: function(num) {
996
+ return ("" + num).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
997
+ },
998
+ switchToLiveTab: function(elem) {
999
+ jQuery('.wfTab1').removeClass('selected');
1000
+ jQuery(elem).addClass('selected');
1001
+ jQuery('.wfDataPanel').hide();
1002
+ var self = this;
1003
+ jQuery('#wfActivity').fadeIn(function() {
1004
+ self.completeLiveTabSwitch();
1005
+ });
1006
+ },
1007
+ completeLiveTabSwitch: function() {
1008
+ this.ajax('wordfence_loadActivityLog', {}, function(res) {
1009
+ var html = '<a href="#" class="wfALogMailLink" onclick="WFAD.emailActivityLog(); return false;"></a><a href="#" class="wfALogReloadLink" onclick="WFAD.reloadActivityData(); return false;"></a>';
1010
+ if (res.events && res.events.length > 0) {
1011
+ jQuery('#wfActivity').empty();
1012
+ for (var i = 0; i < res.events.length; i++) {
1013
+ var timeTaken = '0.0000';
1014
+ if (res.events[i + 1]) {
1015
+ timeTaken = (res.events[i].ctime - res.events[i + 1].ctime).toFixed(4);
1016
+ }
1017
+ var red = "";
1018
+ if (res.events[i].type == 'error') {
1019
+ red = ' class="wfWarn" ';
1020
+ }
1021
+ html += '<div ' + red + 'class="wfALogEntry"><span ' + red + 'class="wfALogTime">[' + res.events[i].type + '&nbsp;:&nbsp;' + timeTaken + '&nbsp;:&nbsp;' + res.events[i].timeAgo + ' ago]</span>&nbsp;' + res.events[i].msg + "</div>";
1022
+ }
1023
+ jQuery('#wfActivity').html(html);
1024
+ } else {
1025
+ jQuery('#wfActivity').html("<p>&nbsp;&nbsp;No activity to report yet. Please complete your first scan.</p>");
1026
+ }
1027
+ });
1028
+ },
1029
+ emailActivityLog: function() {
1030
+ this.colorbox('400px', 'Email Wordfence Activity Log', "Enter the email address you would like to send the Wordfence activity log to. Note that the activity log may contain thousands of lines of data. This log is usually only sent to a member of the Wordfence support team. It also contains your PHP configuration from the phpinfo() function for diagnostic data.<br /><br /><input type='text' value='support@wordfence.com' size='20' id='wfALogRecip' /><input type='button' value='Send' onclick=\"WFAD.completeEmailActivityLog();\" /><input type='button' value='Cancel' onclick='jQuery.colorbox.close();' /><br /><br />");
1031
+ },
1032
+ completeEmailActivityLog: function() {
1033
+ jQuery.colorbox.close();
1034
+ var email = jQuery('#wfALogRecip').val();
1035
+ if (!/^[^@]+@[^@]+$/.test(email)) {
1036
+ alert("Please enter a valid email address.");
1037
+ return;
1038
+ }
1039
+ var self = this;
1040
+ this.ajax('wordfence_sendActivityLog', {email: jQuery('#wfALogRecip').val()}, function(res) {
1041
+ if (res.ok) {
1042
+ self.colorbox('400px', 'Activity Log Sent', "Your Wordfence activity log was sent to " + email + "<br /><br /><input type='button' value='Close' onclick='jQuery.colorbox.close();' /><br /><br />");
1043
+ }
1044
+ });
1045
+ },
1046
+ reloadActivityData: function() {
1047
+ jQuery('#wfActivity').html('<div class="wfLoadingWhite32"></div>'); //&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br />
1048
+ this.completeLiveTabSwitch();
1049
+ },
1050
+ switchToSummaryTab: function(elem) {
1051
+ jQuery('.wfTab1').removeClass('selected');
1052
+ jQuery(elem).addClass('selected');
1053
+ jQuery('.wfDataPanel').hide();
1054
+ jQuery('#wfSummaryTables').fadeIn();
1055
+ },
1056
+ switchIssuesTab: function(elem, type) {
1057
+ jQuery('.wfTab2').removeClass('selected');
1058
+ jQuery('.wfIssuesContainer').hide();
1059
+ jQuery(elem).addClass('selected');
1060
+ this.visibleIssuesPanel = type;
1061
+ jQuery('#wfIssues_' + type).fadeIn();
1062
+ },
1063
+ switchTab: function(tabElement, tabClass, contentClass, selectedContentID, callback) {
1064
+ jQuery('.' + tabClass).removeClass('selected');
1065
+ jQuery(tabElement).addClass('selected');
1066
+ jQuery('.' + contentClass).hide().html('<div class="wfLoadingWhite32"></div>');
1067
+ var func = function() {
1068
+ };
1069
+ if (callback) {
1070
+ func = function() {
1071
+ callback();
1072
+ };
1073
+ }
1074
+ jQuery('#' + selectedContentID).fadeIn(func);
1075
+ },
1076
+ activityTabChanged: function() {
1077
+ var mode = jQuery('.wfDataPanel:visible')[0].id.replace('wfActivity_', '');
1078
+ if (!mode) {
1079
+ return;
1080
+ }
1081
+ this.activityMode = mode;
1082
+ this.reloadActivities();
1083
+ },
1084
+ reloadActivities: function() {
1085
+ jQuery('#wfActivity_' + this.activityMode).html('<div class="wfLoadingWhite32"></div>');
1086
+ this.newestActivityTime = 0;
1087
+ this.updateTicker(true);
1088
+ },
1089
+ staticTabChanged: function() {
1090
+ var mode = jQuery('.wfDataPanel:visible')[0].id.replace('wfActivity_', '');
1091
+ if (!mode) {
1092
+ return;
1093
+ }
1094
+ this.activityMode = mode;
1095
+
1096
+ var self = this;
1097
+ this.ajax('wordfence_loadStaticPanel', {
1098
+ mode: this.activityMode
1099
+ }, function(res) {
1100
+ self.completeLoadStaticPanel(res);
1101
+ });
1102
+ },
1103
+ completeLoadStaticPanel: function(res) {
1104
+ var contentElem = '#wfActivity_' + this.activityMode;
1105
+ jQuery(contentElem).empty();
1106
+ if (res.results && res.results.length > 0) {
1107
+ var tmpl;
1108
+ if (this.activityMode == 'topScanners' || this.activityMode == 'topLeechers') {
1109
+ tmpl = '#wfLeechersTmpl';
1110
+ } else if (this.activityMode == 'blockedIPs') {
1111
+ tmpl = '#wfBlockedIPsTmpl';
1112
+ } else if (this.activityMode == 'lockedOutIPs') {
1113
+ tmpl = '#wfLockedOutIPsTmpl';
1114
+ } else if (this.activityMode == 'throttledIPs') {
1115
+ tmpl = '#wfThrottledIPsTmpl';
1116
+ } else {
1117
+ return;
1118
+ }
1119
+ var i, j, chunk = 1000;
1120
+ var bigArray = res.results.slice(0);
1121
+ res.results = false;
1122
+ for (i = 0, j = bigArray.length; i < j; i += chunk) {
1123
+ res.results = bigArray.slice(i, i + chunk);
1124
+ jQuery(tmpl).tmpl(res).appendTo(contentElem);
1125
+ }
1126
+ this.reverseLookupIPs();
1127
+ } else {
1128
+ if (this.activityMode == 'topScanners' || this.activityMode == 'topLeechers') {
1129
+ jQuery(contentElem).html("No site hits have been logged yet. Check back soon.");
1130
+ } else if (this.activityMode == 'blockedIPs') {
1131
+ jQuery(contentElem).html("No IP addresses have been blocked yet. If you manually block an IP address or if Wordfence automatically blocks one, it will appear here.");
1132
+ } else if (this.activityMode == 'lockedOutIPs') {
1133
+ jQuery(contentElem).html("No IP addresses have been locked out from signing in or using the password recovery system.");
1134
+ } else if (this.activityMode == 'throttledIPs') {
1135
+ jQuery(contentElem).html("No IP addresses have been throttled yet. If an IP address accesses the site too quickly and breaks one of the Wordfence rules, it will appear here.");
1136
+ } else {
1137
+ return;
1138
+ }
1139
+ }
1140
+ },
1141
+ loadPasswdAuditResults: function() {
1142
+ var self = this;
1143
+ this.ajax('wordfence_passwdLoadResults', {}, function(res) {
1144
+ self.displayPWAuditResults(res);
1145
+ });
1146
+ },
1147
+ doPasswdAuditUpdate: function(freq) {
1148
+ this.loadPasswdAuditJobs();
1149
+ this.loadPasswdAuditResults();
1150
+ },
1151
+ stopPasswdAuditUpdate: function() {
1152
+ clearInterval(this.passwdAuditUpdateInt);
1153
+ },
1154
+ killPasswdAudit: function(jobID) {
1155
+ var self = this;
1156
+ this.ajax('wordfence_killPasswdAudit', {jobID: jobID}, function(res) {
1157
+ if (res.ok) {
1158
+ self.colorbox('300px', "Stop Requested", "We have sent a request to stop the password audit in progress. It may take a few minutes before results stop appearing. You can immediately start another audit if you'd like.");
1159
+ }
1160
+ });
1161
+ },
1162
+ displayPWAuditResults: function(res) {
1163
+ if (res && res.results && res.results.length > 0) {
1164
+ var wfAuditResults = $('#wfAuditResults');
1165
+ jQuery('#wfAuditResults').empty();
1166
+ jQuery('#wfAuditResultsTable').tmpl().appendTo(wfAuditResults);
1167
+ var wfAuditResultsBody = wfAuditResults.find('.wf-pw-audit-tbody');
1168
+ for (var i = 0; i < res.results.length; i++) {
1169
+ jQuery('#wfAuditResultsRow').tmpl(res.results[i]).appendTo(wfAuditResultsBody);
1170
+ }
1171
+ } else {
1172
+ jQuery('#wfAuditResults').empty().html("<p>You don't have any user accounts with a weak password at this time.</p>");
1173
+ }
1174
+ },
1175
+ loadPasswdAuditJobs: function() {
1176
+ var self = this;
1177
+ this.ajax('wordfence_passwdLoadJobs', {}, function(res) {
1178
+ if (res && res.results && res.results.length > 0) {
1179
+ var stat = res.results[0].jobStatus;
1180
+ if (stat == 'running' || stat == 'queued') {
1181
+ setTimeout(function() {
1182
+ self.doPasswdAuditUpdate()
1183
+ }, 10000);
1184
+ }
1185
+ }
1186
+
1187
+ self.displayPWAuditJobs(res);
1188
+ });
1189
+ },
1190
+ deletePasswdAudit: function(jobID) {
1191
+ var self = this;
1192
+ this.ajax('wordfence_deletePasswdAudit', {jobID: jobID}, function(res) {
1193
+ self.loadPasswdAuditJobs(res);
1194
+ });
1195
+ },
1196
+ doFixWeakPasswords: function() {
1197
+ var self = this;
1198
+ var mode = jQuery('#wfPasswdFixAction').val();
1199
+ var ids = jQuery('input.wfUserCheck:checked').map(function() {
1200
+ return jQuery(this).val();
1201
+ }).get();
1202
+ if (ids.length < 1) {
1203
+ self.colorbox('300px', "Please select users", "You did not select any users from the list. Select which site members you want to email or to change their passwords.");
1204
+ return;
1205
+ }
1206
+ this.ajax('wordfence_weakPasswordsFix', {
1207
+ mode: mode,
1208
+ ids: ids.join(',')
1209
+ }, function(res) {
1210
+ if (res.ok && res.title && res.msg) {
1211
+ self.colorbox('300px', res.title, res.msg);
1212
+ }
1213
+ });
1214
+ },
1215
+ ucfirst: function(str) {
1216
+ str = "" + str;
1217
+ return str.charAt(0).toUpperCase() + str.slice(1);
1218
+ },
1219
+ makeIPTrafLink: function(IP) {
1220
+ return WordfenceAdminVars.siteBaseURL + '?_wfsf=IPTraf&nonce=' + this.nonce + '&IP=' + encodeURIComponent(IP);
1221
+ },
1222
+ makeDiffLink: function(dat) {
1223
+ return WordfenceAdminVars.siteBaseURL + '?_wfsf=diff&nonce=' + this.nonce +
1224
+ '&file=' + encodeURIComponent(this.es(dat['file'])) +
1225
+ '&cType=' + encodeURIComponent(this.es(dat['cType'])) +
1226
+ '&cKey=' + encodeURIComponent(this.es(dat['cKey'])) +
1227
+ '&cName=' + encodeURIComponent(this.es(dat['cName'])) +
1228
+ '&cVersion=' + encodeURIComponent(this.es(dat['cVersion']));
1229
+ },
1230
+ makeViewFileLink: function(file) {
1231
+ return WordfenceAdminVars.siteBaseURL + '?_wfsf=view&nonce=' + this.nonce + '&file=' + encodeURIComponent(file);
1232
+ },
1233
+ makeTimeAgo: function(t) {
1234
+ var months = Math.floor(t / (86400 * 30));
1235
+ var days = Math.floor(t / 86400);
1236
+ var hours = Math.floor(t / 3600);
1237
+ var minutes = Math.floor(t / 60);
1238
+ if (months > 0) {
1239
+ days -= months * 30;
1240
+ return this.pluralize(months, 'month', days, 'day');
1241
+ } else if (days > 0) {
1242
+ hours -= days * 24;
1243
+ return this.pluralize(days, 'day', hours, 'hour');
1244
+ } else if (hours > 0) {
1245
+ minutes -= hours * 60;
1246
+ return this.pluralize(hours, 'hour', minutes, 'min');
1247
+ } else if (minutes > 0) {
1248
+ //t -= minutes * 60;
1249
+ return this.pluralize(minutes, 'minute');
1250
+ } else {
1251
+ return Math.round(t) + " seconds";
1252
+ }
1253
+ },
1254
+ pluralize: function(m1, t1, m2, t2) {
1255
+ if (m1 != 1) {
1256
+ t1 = t1 + 's';
1257
+ }
1258
+ if (m2 != 1) {
1259
+ t2 = t2 + 's';
1260
+ }
1261
+ if (m1 && m2) {
1262
+ return m1 + ' ' + t1 + ' ' + m2 + ' ' + t2;
1263
+ } else {
1264
+ return m1 + ' ' + t1;
1265
+ }
1266
+ },
1267
+ calcRangeTotal: function() {
1268
+ var range = jQuery('#ipRange').val();
1269
+ if (!range) {
1270
+ return;
1271
+ }
1272
+ range = range.replace(/ /g, '');
1273
+ if (range && /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\s*\-\s*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(range)) {
1274
+ var ips = range.split('-');
1275
+ var total = this.inet_aton(ips[1]) - this.inet_aton(ips[0]) + 1;
1276
+ if (total < 1) {
1277
+ jQuery('#wfShowRangeTotal').html("<span style=\"color: #F00;\">Invalid. Starting IP is greater than ending IP.</span>");
1278
+ return;
1279
+ }
1280
+ jQuery('#wfShowRangeTotal').html("<span style=\"color: #0A0;\">Valid: " + total + " addresses in range.</span>");
1281
+ } else {
1282
+ jQuery('#wfShowRangeTotal').empty();
1283
+ }
1284
+ },
1285
+ loadBlockRanges: function() {
1286
+ var self = this;
1287
+ this.ajax('wordfence_loadBlockRanges', {}, function(res) {
1288
+ self.completeLoadBlockRanges(res);
1289
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1290
 
1291
+ },
1292
+ completeLoadBlockRanges: function(res) {
1293
+ jQuery('#currentBlocks').empty();
1294
+ if (res.results && res.results.length > 0) {
1295
+ jQuery('#wfBlockedRangesTmpl').tmpl(res).prependTo('#currentBlocks');
1296
+ } else {
1297
+ jQuery('#currentBlocks').html("You have not blocked any IP ranges or other patterns yet.");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1298
  }
1299
+ },
1300
+ whois: function(val) {
1301
+ val = val.replace(' ', '');
1302
+ if (!/\w+/.test(val)) {
1303
+ this.colorbox('300px', "Enter a valid IP or domain", "Please enter a valid IP address or domain name for your whois lookup.");
1304
+ return;
1305
+ }
1306
+ var self = this;
1307
+ jQuery('#whoisbutton').attr('disabled', 'disabled');
1308
+ jQuery('#whoisbutton').attr('value', 'Loading...');
1309
+ this.ajax('wordfence_whois', {
1310
+ val: val
1311
+ }, function(res) {
1312
+ jQuery('#whoisbutton').removeAttr('disabled');
1313
+ jQuery('#whoisbutton').attr('value', 'Look up IP or Domain');
1314
+ if (res.ok) {
1315
+ self.completeWhois(res);
1316
+ }
1317
+ });
1318
+ },
1319
+ completeWhois: function(res) {
1320
+ if (res.ok && res.result && res.result.rawdata && res.result.rawdata.length > 0) {
1321
+ var rawhtml = "";
1322
+ for (var i = 0; i < res.result.rawdata.length; i++) {
1323
+ res.result.rawdata[i] = jQuery('<div />').text(res.result.rawdata[i]).html();
1324
+ res.result.rawdata[i] = res.result.rawdata[i].replace(/([^\s\t\r\n:;]+@[^\s\t\r\n:;\.]+\.[^\s\t\r\n:;]+)/, "<a href=\"mailto:$1\">$1<\/a>");
1325
+ res.result.rawdata[i] = res.result.rawdata[i].replace(/(https?:\/\/[^\/]+[^\s\r\n\t]+)/, "<a target=\"_blank\" href=\"$1\">$1<\/a>");
1326
+ var redStyle = "";
1327
+ if (this.getQueryParam('wfnetworkblock')) {
1328
+ redStyle = " style=\"color: #F00;\"";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1329
  }
1330
+ var self = this;
1331
+
1332
+ function wfm21(str, ipRange, offset, totalStr) {
1333
+ var ips = ipRange.split(/\s*\-\s*/);
1334
+ var ip1num = self.inet_aton(ips[0]);
1335
+ var ip2num = self.inet_aton(ips[1]);
1336
+ var totalIPs = ip2num - ip1num + 1;
1337
+ return "<a href=\"admin.php?page=WordfenceRangeBlocking&wfBlockRange=" + ipRange + "\"" + redStyle + ">" + ipRange + " [<strong>" + totalIPs + "</strong> addresses in this network. Click to block this network]<\/a>";
1338
+ }
1339
+
1340
+ res.result.rawdata[i] = res.result.rawdata[i].replace(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} - \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/, wfm21);
1341
+ rawhtml += res.result.rawdata[i] + "<br />";
1342
  }
1343
+ jQuery('#wfrawhtml').html(rawhtml);
1344
+ } else {
1345
+ jQuery('#wfrawhtml').html('<span style="color: #F00;">Sorry, but no data for that IP or domain was found.</span>');
1346
  }
1347
+ },
1348
+ blockIPUARange: function(ipRange, uaRange, referer, reason) {
1349
+ if (!/\w+/.test(reason)) {
1350
+ this.colorbox('300px', "Please specify a reason", "You forgot to include a reason you're blocking this IP range. We ask you to include this for your own record keeping.");
1351
+ return;
1352
  }
1353
+ ipRange = ipRange.replace(/ /g, '');
1354
+ if (ipRange) {
1355
+ if (!/^\d+\.\d+\.\d+\.\d+\-\d+\.\d+\.\d+\.\d+$/.test(ipRange)) {
1356
+ this.colorbox('300px', 'Specify a valid IP range', "Please specify a valid IP address range in the form of \"1.2.3.4 - 1.2.3.5\" without quotes. Make sure the dash between the IP addresses in a normal dash (a minus sign on your keyboard) and not another character that looks like a dash.");
1357
+ return;
1358
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1359
  }
1360
+ if (!(/\w+/.test(ipRange) || /\w+/.test(uaRange) || /\w+/.test(referer))) {
1361
+ this.colorbox('300px', 'Specify an IP range or Browser pattern', "Please specify either an IP address range or a web browser pattern to match.");
1362
+ return;
1363
+ }
1364
+ var self = this;
1365
+ this.ajax('wordfence_blockIPUARange', {
1366
+ ipRange: ipRange,
1367
+ uaRange: uaRange,
1368
+ referer: referer,
1369
+ reason: reason
1370
+ }, function(res) {
1371
+ if (res.ok) {
1372
+ self.loadBlockRanges();
1373
+ return;
1374
+ }
1375
  });
1376
+ },
1377
+ unblockRange: function(id) {
1378
+ var self = this;
1379
+ this.ajax('wordfence_unblockRange', {
1380
+ id: id
1381
+ }, function(res) {
1382
+ self.loadBlockRanges();
1383
+ });
1384
+ },
1385
+ blockIP: function(IP, reason) {
1386
+ var self = this;
1387
+ this.ajax('wordfence_blockIP', {
1388
+ IP: IP,
1389
+ reason: reason
1390
+ }, function(res) {
1391
+ if (res.errorMsg) {
1392
+ return;
1393
+ } else {
1394
+ self.reloadActivities();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1395
  }
1396
+ });
1397
+ },
1398
+ blockIPTwo: function(IP, reason, perm) {
1399
+ var self = this;
1400
+ this.ajax('wordfence_blockIP', {
1401
+ IP: IP,
1402
+ reason: reason,
1403
+ perm: (perm ? '1' : '0')
1404
+ }, function(res) {
1405
+ if (res.errorMsg) {
1406
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1407
  } else {
1408
+ self.staticTabChanged();
1409
  }
1410
+ });
1411
+ },
1412
+ unlockOutIP: function(IP) {
1413
+ var self = this;
1414
+ this.ajax('wordfence_unlockOutIP', {
1415
+ IP: IP
1416
+ }, function(res) {
1417
+ self.staticTabChanged();
1418
+ });
1419
+ },
1420
+ unblockIP: function(IP) {
1421
+ var self = this;
1422
+ this.ajax('wordfence_unblockIP', {
1423
+ IP: IP
1424
+ }, function(res) {
1425
+ self.reloadActivities();
1426
+ });
1427
+ },
1428
+ unblockIPTwo: function(IP) {
1429
+ var self = this;
1430
+ this.ajax('wordfence_unblockIP', {
1431
+ IP: IP
1432
+ }, function(res) {
1433
+ self.staticTabChanged();
1434
+ });
1435
+ },
1436
+ permBlockIP: function(IP) {
1437
+ var self = this;
1438
+ this.ajax('wordfence_permBlockIP', {
1439
+ IP: IP
1440
+ }, function(res) {
1441
+ self.staticTabChanged();
1442
+ });
1443
+ },
1444
+ makeElemID: function() {
1445
+ return 'wfElemGen' + this.elementGeneratorIter++;
1446
+ },
1447
+ pulse: function(sel) {
1448
+ jQuery(sel).fadeIn(function() {
1449
+ setTimeout(function() {
1450
+ jQuery(sel).fadeOut();
1451
+ }, 2000);
1452
+ });
1453
+ },
1454
+ getCacheStats: function() {
1455
+ var self = this;
1456
+ this.ajax('wordfence_getCacheStats', {}, function(res) {
1457
+ if (res.ok) {
1458
+ self.colorbox('400px', res.heading, res.body);
1459
+ }
1460
+ });
1461
+ },
1462
+ clearPageCache: function() {
1463
+ var self = this;
1464
+ this.ajax('wordfence_clearPageCache', {}, function(res) {
1465
+ if (res.ok) {
1466
+ self.colorbox('400px', res.heading, res.body);
1467
+ }
1468
+ });
1469
+ },
1470
+ switchToFalcon: function() {
1471
+ var self = this;
1472
+ this.ajax('wordfence_checkFalconHtaccess', {}, function(res) {
1473
+ if (res.ok) {
1474
+ self.colorbox('400px', "Enabling Falcon Engine", 'First read this <a href="http://www.wordfence.com/introduction-to-wordfence-falcon-engine/" target="_blank">Introduction to Falcon Engine</a>. Falcon modifies your website configuration file which is called your .htaccess file. To enable Falcon we ask that you make a backup of this file. This is a safety precaution in case for some reason Falcon is not compatible with your site.<br /><br /><a href="' + WordfenceAdminVars.ajaxURL + '?action=wordfence_downloadHtaccess&nonce=' + self.nonce + '" onclick="jQuery(\'#wfNextBut\').prop(\'disabled\', false); return true;">Click here to download a backup copy of your .htaccess file now</a><br /><br /><input type="button" name="but1" id="wfNextBut" value="Click to Enable Falcon Engine" disabled="disabled" onclick="WFAD.confirmSwitchToFalcon(0);" />');
1475
+ } else if (res.nginx) {
1476
+ self.colorbox('400px', "Enabling Falcon Engine", 'You are using an Nginx web server and using a FastCGI processor like PHP5-FPM. To use Falcon you will need to manually modify your nginx.conf configuration file and reload your Nginx server for the changes to take effect. You can find the <a href="http://www.wordfence.com/blog/2014/05/nginx-wordfence-falcon-engine-php-fpm-fastcgi-fast-cgi/" target="_blank">rules you need to make these changes to nginx.conf on this page on wordfence.com</a>. Once you have made these changes, compressed cached files will be served to your visitors directly from Nginx making your site extremely fast. When you have made the changes and reloaded your Nginx server, you can click the button below to enable Falcon.<br /><br /><input type="button" name="but1" id="wfNextBut" value="Click to Enable Falcon Engine" onclick="WFAD.confirmSwitchToFalcon(1);" />');
1477
+ } else if (res.err) {
1478
+ self.colorbox('400px', "We encountered a problem", "We can't modify your .htaccess file for you because: " + res.err + "<br /><br />Advanced users: If you would like to manually enable Falcon yourself by editing your .htaccess, you can add the rules below to the beginning of your .htaccess file. Then click the button below to enable Falcon. Don't do this unless you understand website configuration.<br /><textarea style='width: 300px; height:100px;' readonly>" + jQuery('<div/>').text(res.code).html() + "</textarea><br /><input type='button' value='Enable Falcon after manually editing .htaccess' onclick='WFAD.confirmSwitchToFalcon(1);' />");
1479
+ }
1480
+ });
1481
+ },
1482
+ confirmSwitchToFalcon: function(noEditHtaccess) {
1483
+ jQuery.colorbox.close();
1484
+ var cacheType = 'falcon';
1485
+ var self = this;
1486
+ this.ajax('wordfence_saveCacheConfig', {
1487
+ cacheType: cacheType,
1488
+ noEditHtaccess: noEditHtaccess
1489
+ }, function(res) {
1490
+ if (res.ok) {
1491
+ self.colorbox('400px', res.heading, res.body);
1492
  }
1493
+ }
1494
+ );
1495
+ },
1496
+ saveCacheConfig: function() {
1497
+ var cacheType = jQuery('input:radio[name=cacheType]:checked').val();
1498
+ if (cacheType == 'falcon') {
1499
+ return this.switchToFalcon();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1500
  }
1501
  var self = this;
1502
+ this.ajax('wordfence_saveCacheConfig', {
1503
+ cacheType: cacheType
1504
+ }, function(res) {
1505
+ if (res.ok) {
1506
+ self.colorbox('400px', res.heading, res.body);
1507
+ }
1508
+ }
1509
+ );
1510
+ },
1511
+ saveCacheOptions: function() {
1512
+ var self = this;
1513
+ this.ajax('wordfence_saveCacheOptions', {
1514
+ allowHTTPSCaching: (jQuery('#wfallowHTTPSCaching').is(':checked') ? 1 : 0),
1515
+ addCacheComment: (jQuery('#wfaddCacheComment').is(':checked') ? 1 : 0),
1516
+ clearCacheSched: (jQuery('#wfclearCacheSched').is(':checked') ? 1 : 0)
1517
+ }, function(res) {
1518
+ if (res.updateErr) {
1519
+ self.colorbox('400px', "You need to manually update your .htaccess", res.updateErr + "<br />Your option was updated but you need to change the Wordfence code in your .htaccess to the following:<br /><textarea style='width: 300px; height: 120px;'>" + jQuery('<div/>').text(res.code).html() + '</textarea>');
1520
+ }
1521
+ }
1522
+ );
1523
+ },
1524
+ saveConfig: function() {
1525
+ var qstr = jQuery('#wfConfigForm').serialize();
1526
+ var self = this;
1527
+ jQuery('.wfSavedMsg').hide();
1528
+ jQuery('.wfAjax24').show();
1529
+ this.ajax('wordfence_saveConfig', qstr, function(res) {
1530
+ jQuery('.wfAjax24').hide();
1531
+ if (res.ok) {
1532
+ if (res['paidKeyMsg']) {
1533
+ self.colorbox('400px', "Congratulations! You have been upgraded to Premium Scanning.", "You have upgraded to a Premium API key. Once this page reloads, you can choose which premium scanning options you would like to enable and then click save. Click the button below to reload this page now.<br /><br /><center><input type='button' name='wfReload' value='Reload page and enable Premium options' onclick='window.location.reload(true);' /></center>");
1534
+ return;
1535
+ } else if (res['reload'] == 'reload' || WFAD.reloadConfigPage) {
1536
+ self.colorbox('400px', "Please reload this page", "You selected a config option that requires a page reload. Click the button below to reload this page to update the menu.<br /><br /><center><input type='button' name='wfReload' value='Reload page' onclick='window.location.reload(true);' /></center>");
1537
+ return;
1538
+ } else {
1539
+ self.pulse('.wfSavedMsg');
1540
+ }
1541
+ } else if (res.errorMsg) {
1542
+ return;
1543
+ } else {
1544
+ self.colorbox('400px', 'An error occurred', 'We encountered an error trying to save your changes.');
1545
+ }
1546
+ });
1547
+ },
1548
+ changeSecurityLevel: function() {
1549
+ var level = jQuery('#securityLevel').val();
1550
+ for (var k in WFSLevels[level].checkboxes) {
1551
+ if (k != 'liveTraf_ignorePublishers') {
1552
+ jQuery('#' + k).prop("checked", WFSLevels[level].checkboxes[k]);
1553
+ }
1554
  }
1555
+ for (var k in WFSLevels[level].otherParams) {
1556
+ if (!/^(?:apiKey|securityLevel|alertEmails|liveTraf_ignoreUsers|liveTraf_ignoreIPs|liveTraf_ignoreUA|liveTraf_hitsMaxSize|maxMem|maxExecutionTime|actUpdateInterval)$/.test(k)) {
1557
+ jQuery('#' + k).val(WFSLevels[level].otherParams[k]);
1558
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1559
  }
1560
+ },
1561
+ clearAllBlocked: function(op) {
1562
+ if (op == 'blocked') {
1563
+ body = "Are you sure you want to clear all blocked IP addresses and allow visitors from those addresses to access the site again?";
1564
+ } else if (op == 'locked') {
1565
+ body = "Are you sure you want to clear all locked IP addresses and allow visitors from those addresses to sign in again?";
 
 
 
 
 
 
 
 
 
 
 
 
1566
  } else {
 
 
 
 
 
 
 
 
 
 
 
 
1567
  return;
 
 
1568
  }
1569
+ this.colorbox('450px', "Please confirm", body +
1570
+ '<br /><br /><center><input type="button" name="but1" value="Cancel" onclick="jQuery.colorbox.close();" />&nbsp;&nbsp;&nbsp;' +
1571
+ '<input type="button" name="but2" value="Yes I\'m sure" onclick="jQuery.colorbox.close(); WFAD.confirmClearAllBlocked(\'' + op + '\');"><br />');
1572
+ },
1573
+ confirmClearAllBlocked: function(op) {
1574
+ var self = this;
1575
+ this.ajax('wordfence_clearAllBlocked', {op: op}, function(res) {
1576
+ self.staticTabChanged();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1577
  });
1578
+ },
1579
+ setOwnCountry: function(code) {
1580
+ this.ownCountry = (code + "").toUpperCase();
1581
+ },
1582
+ loadBlockedCountries: function(str) {
1583
+ var codes = str.split(',');
1584
+ for (var i = 0; i < codes.length; i++) {
1585
+ jQuery('#wfCountryCheckbox_' + codes[i]).prop('checked', true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1586
  }
1587
+ },
1588
+ saveCountryBlocking: function() {
1589
+ var action = jQuery('#wfBlockAction').val();
1590
+ var redirURL = jQuery('#wfRedirURL').val();
1591
+ var bypassRedirURL = jQuery('#wfBypassRedirURL').val();
1592
+ var bypassRedirDest = jQuery('#wfBypassRedirDest').val();
1593
+ var bypassViewURL = jQuery('#wfBypassViewURL').val();
1594
+
1595
+ if (action == 'redir' && (!/^https?:\/\/[^\/]+/i.test(redirURL))) {
1596
+ this.colorbox('400px', "Please enter a URL for redirection", "You have chosen to redirect blocked countries to a specific page. You need to enter a URL in the text box provided that starts with http:// or https://");
1597
+ return;
 
 
 
1598
  }
1599
+ if (bypassRedirURL || bypassRedirDest) {
1600
+ if (!(bypassRedirURL && bypassRedirDest)) {
1601
+ this.colorbox('400px', "Missing data from form", "If you want to set up a URL that will bypass country blocking, you must enter a URL that a visitor can hit and the destination they will be redirected to. You have only entered one of these components. Please enter both.");
1602
+ return;
1603
+ }
1604
+ if (bypassRedirURL == bypassRedirDest) {
1605
+ this.colorbox('400px', "URLs are the same", "The URL that a user hits to bypass country blocking and the URL they are redirected to are the same. This would cause a circular redirect. Please fix this.");
1606
+ return;
1607
+ }
 
 
 
1608
  }
1609
+ if (bypassRedirURL && (!/^(?:\/|http:\/\/)/.test(bypassRedirURL))) {
1610
+ this.invalidCountryURLMsg(bypassRedirURL);
 
 
 
 
 
 
 
 
 
 
 
1611
  return;
1612
+ }
1613
+ if (bypassRedirDest && (!/^(?:\/|http:\/\/)/.test(bypassRedirDest))) {
1614
+ this.invalidCountryURLMsg(bypassRedirDest);
1615
+ return;
1616
+ }
1617
+ if (bypassViewURL && (!/^(?:\/|http:\/\/)/.test(bypassViewURL))) {
1618
+ this.invalidCountryURLMsg(bypassViewURL);
1619
  return;
 
 
1620
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1621
 
1622
+ var codesArr = [];
1623
+ var ownCountryBlocked = false;
1624
+ var self = this;
1625
+ jQuery('.wfCountryCheckbox').each(function(idx, elem) {
1626
+ if (jQuery(elem).is(':checked')) {
1627
+ var code = jQuery(elem).val();
1628
+ codesArr.push(code);
1629
+ if (code == self.ownCountry) {
1630
+ ownCountryBlocked = true;
1631
+ }
1632
+ }
1633
+ });
1634
+ this.countryCodesToSave = codesArr.join(',');
1635
+ if (ownCountryBlocked) {
1636
+ this.colorbox('400px', "Please confirm blocking yourself", "You are about to block your own country. This could lead to you being locked out. Please make sure that your user profile on this machine has a current and valid email address and make sure you know what it is. That way if you are locked out, you can send yourself an unlock email. If you're sure you want to block your own country, click 'Confirm' below, otherwise click 'Cancel'.<br />" +
1637
+ '<input type="button" name="but1" value="Confirm" onclick="jQuery.colorbox.close(); WFAD.confirmSaveCountryBlocking();" />&nbsp;<input type="button" name="but1" value="Cancel" onclick="jQuery.colorbox.close();" />');
1638
+ } else {
1639
+ this.confirmSaveCountryBlocking();
1640
  }
1641
+ },
1642
+ invalidCountryURLMsg: function(URL) {
1643
+ this.colorbox('400px', "Invalid URL", "URL's that you provide for bypassing country blocking must start with '/' or 'http://' without quotes. The URL that is invalid is: " + URL);
1644
+ return;
1645
+ },
1646
+ confirmSaveCountryBlocking: function() {
1647
+ var action = jQuery('#wfBlockAction').val();
1648
+ var redirURL = jQuery('#wfRedirURL').val();
1649
+ var loggedInBlocked = jQuery('#wfLoggedInBlocked').is(':checked') ? '1' : '0';
1650
+ var loginFormBlocked = jQuery('#wfLoginFormBlocked').is(':checked') ? '1' : '0';
1651
+ var restOfSiteBlocked = jQuery('#wfRestOfSiteBlocked').is(':checked') ? '1' : '0';
1652
+ var bypassRedirURL = jQuery('#wfBypassRedirURL').val();
1653
+ var bypassRedirDest = jQuery('#wfBypassRedirDest').val();
1654
+ var bypassViewURL = jQuery('#wfBypassViewURL').val();
 
 
 
 
 
 
 
 
 
1655
 
1656
+ jQuery('.wfAjax24').show();
1657
+ var self = this;
1658
+ this.ajax('wordfence_saveCountryBlocking', {
1659
+ blockAction: action,
1660
+ redirURL: redirURL,
1661
+ loggedInBlocked: loggedInBlocked,
1662
+ loginFormBlocked: loginFormBlocked,
1663
+ restOfSiteBlocked: restOfSiteBlocked,
1664
+ bypassRedirURL: bypassRedirURL,
1665
+ bypassRedirDest: bypassRedirDest,
1666
+ bypassViewURL: bypassViewURL,
1667
+ codes: this.countryCodesToSave
1668
+ }, function(res) {
1669
+ jQuery('.wfAjax24').hide();
1670
+ self.pulse('.wfSavedMsg');
1671
  });
1672
+ },
1673
+ paidUsersOnly: function(msg) {
1674
+ var pos = jQuery('#paidWrap').position();
1675
+ var width = jQuery('#paidWrap').width();
1676
+ var height = jQuery('#paidWrap').height();
1677
+ jQuery('<div style="position: absolute; left: ' + pos.left + 'px; top: ' + pos.top + 'px; background-color: #FFF; width: ' + width + 'px; height: ' + height + 'px;"><div class="paidInnerMsg">' + msg + ' <a href="https://www.wordfence.com/wordfence-signup/" target="_blank">Click here to upgrade and gain access to this feature.</div></div>').insertAfter('#paidWrap').fadeTo(10000, 0.7);
1678
+ },
1679
+ sched_modeChange: function() {
1680
+ var self = this;
1681
+ if (jQuery('#schedMode').val() == 'auto') {
1682
+ jQuery('.wfSchedCheckbox').attr('disabled', true);
1683
+ } else {
1684
+ jQuery('.wfSchedCheckbox').attr('disabled', false);
1685
+ }
1686
+ },
1687
+ sched_shortcut: function(mode) {
1688
+ if (jQuery('#schedMode').val() == 'auto') {
1689
+ this.colorbox('400px', 'Change the scan mode', "You need to change the scan mode to manually scheduled scans if you want to select scan times.");
1690
+ return;
1691
+ }
1692
+ jQuery('.wfSchedCheckbox').prop('checked', false);
1693
+ if (this.schedStartHour === false) {
1694
+ this.schedStartHour = Math.floor((Math.random() * 24));
1695
+ } else {
1696
+ this.schedStartHour++;
1697
+ if (this.schedStartHour > 23) {
1698
+ this.schedStartHour = 0;
1699
+ }
1700
+ }
1701
+ if (mode == 'onceDaily') {
1702
+ for (var i = 0; i <= 6; i++) {
1703
+ jQuery('#wfSchedDay_' + i + '_' + this.schedStartHour).attr('checked', true);
1704
+ }
1705
+ } else if (mode == 'twiceDaily') {
1706
+ var secondHour = this.schedStartHour + 12;
1707
+ if (secondHour >= 24) {
1708
+ secondHour = secondHour - 24;
1709
+ }
1710
+ for (var i = 0; i <= 6; i++) {
1711
+ jQuery('#wfSchedDay_' + i + '_' + this.schedStartHour).attr('checked', true);
1712
+ jQuery('#wfSchedDay_' + i + '_' + secondHour).attr('checked', true);
1713
+ }
1714
+ } else if (mode == 'oddDaysWE') {
1715
+ var startDay = Math.floor((Math.random()));
1716
+ jQuery('#wfSchedDay_1_' + this.schedStartHour).attr('checked', true);
1717
+ jQuery('#wfSchedDay_3_' + this.schedStartHour).attr('checked', true);
1718
+ jQuery('#wfSchedDay_5_' + this.schedStartHour).attr('checked', true);
1719
+ jQuery('#wfSchedDay_6_' + this.schedStartHour).attr('checked', true);
1720
+ jQuery('#wfSchedDay_0_' + this.schedStartHour).attr('checked', true);
1721
+ } else if (mode == 'weekends') {
1722
+ var startDay = Math.floor((Math.random()));
1723
+ jQuery('#wfSchedDay_6_' + this.schedStartHour).attr('checked', true);
1724
+ jQuery('#wfSchedDay_0_' + this.schedStartHour).attr('checked', true);
1725
+ } else if (mode == 'every6hours') {
1726
+ for (var i = 0; i <= 6; i++) {
1727
+ for (var hour = this.schedStartHour; hour < this.schedStartHour + 24; hour = hour + 6) {
1728
+ var displayHour = hour;
1729
+ if (displayHour >= 24) {
1730
+ displayHour = displayHour - 24;
1731
+ }
1732
+ jQuery('#wfSchedDay_' + i + '_' + displayHour).attr('checked', true);
1733
+ }
1734
+ }
1735
  }
 
 
1736
 
1737
+ },
1738
+ sched_save: function() {
1739
+ var schedMode = jQuery('#schedMode').val();
1740
+ var schedule = [];
1741
+ for (var day = 0; day <= 6; day++) {
1742
+ var hours = [];
1743
+ for (var hour = 0; hour <= 23; hour++) {
1744
+ var elemID = '#wfSchedDay_' + day + '_' + hour;
1745
+ hours[hour] = jQuery(elemID).is(':checked') ? '1' : '0';
1746
+ }
1747
+ schedule[day] = hours.join(',');
1748
+ }
1749
+ var scheduleTxt = schedule.join('|');
1750
+ var self = this;
1751
+ this.ajax('wordfence_saveScanSchedule', {
1752
+ schedMode: schedMode,
1753
+ schedTxt: scheduleTxt
1754
+ }, function(res) {
1755
+ jQuery('#wfScanStartTime').html(res.nextStart);
1756
+ jQuery('.wfAjax24').hide();
1757
+ self.pulse('.wfSaveMsg');
1758
+ });
1759
+ },
1760
+ twoFacStatus: function(msg) {
1761
+ jQuery('#wfTwoFacMsg').html(msg);
1762
+ jQuery('#wfTwoFacMsg').fadeIn(function() {
1763
+ setTimeout(function() {
1764
+ jQuery('#wfTwoFacMsg').fadeOut();
1765
+ }, 2000);
1766
+ });
1767
+ },
1768
+ addTwoFactor: function(username, phone) {
1769
+ var self = this;
1770
+ this.ajax('wordfence_addTwoFactor', {
1771
+ username: username,
1772
+ phone: phone
1773
+ }, function(res) {
1774
+ if (res.ok) {
1775
+ self.twoFacStatus('User added! Check the user\'s phone to get the activation code.');
1776
+ jQuery('<div id="twoFacCont_' + res.userID + '">' + jQuery('#wfTwoFacUserTmpl').tmpl(res).html() + '</div>').prependTo(jQuery('#wfTwoFacUsers'));
1777
+ }
1778
  });
1779
+ },
1780
+ twoFacActivate: function(userID, code) {
1781
+ var self = this;
1782
+ this.ajax('wordfence_twoFacActivate', {
1783
+ userID: userID,
1784
+ code: code
1785
+ }, function(res) {
1786
+ if (res.ok) {
1787
+ jQuery('#twoFacCont_' + res.userID).html(
1788
+ jQuery('#wfTwoFacUserTmpl').tmpl(res)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1789
  );
1790
+ self.twoFacStatus('Cellphone Sign-in activated for user.');
1791
+ }
1792
+ });
1793
+ },
1794
+ delTwoFac: function(userID) {
1795
+ this.ajax('wordfence_twoFacDel', {
1796
+ userID: userID
1797
+ }, function(res) {
1798
+ if (res.ok) {
1799
+ jQuery('#twoFacCont_' + res.userID).fadeOut(function() {
1800
+ jQuery(this).remove();
1801
+ });
1802
+ }
1803
+ });
1804
+ },
1805
+ loadTwoFactor: function() {
1806
+ this.ajax('wordfence_loadTwoFactor', {}, function(res) {
1807
+ if (res.users && res.users.length > 0) {
1808
+ for (var i = 0; i < res.users.length; i++) {
1809
+ jQuery('<div id="twoFacCont_' + res.users[i].userID + '">' +
1810
+ jQuery('#wfTwoFacUserTmpl').tmpl(res.users[i]).html() + '</div>').appendTo(jQuery('#wfTwoFacUsers'));
1811
+ }
1812
+ }
1813
+ });
1814
+ },
1815
+ getQueryParam: function(name) {
1816
+ name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
1817
+ var regexS = "[\\?&]" + name + "=([^&#]*)";
1818
+ var regex = new RegExp(regexS);
1819
+ var results = regex.exec(window.location.search);
1820
+ if (results == null) {
1821
+ return "";
1822
+ } else {
1823
+ return decodeURIComponent(results[1].replace(/\+/g, " "));
1824
  }
1825
+ },
1826
+ inet_aton: function(dot) {
1827
+ var d = dot.split('.');
1828
+ return ((((((+d[0]) * 256) + (+d[1])) * 256) + (+d[2])) * 256) + (+d[3]);
1829
+ },
1830
+ inet_ntoa: function(num) {
1831
+ var d = num % 256;
1832
+ for (var i = 3; i > 0; i--) {
1833
+ num = Math.floor(num / 256);
1834
+ d = num % 256 + '.' + d;
1835
+ }
1836
+ return d;
1837
+ },
1838
+ removeCacheExclusion: function(id) {
1839
+ this.ajax('wordfence_removeCacheExclusion', {id: id}, function(res) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1840
  window.location.reload(true);
1841
+ });
1842
+ },
1843
+ addCacheExclusion: function(patternType, pattern) {
1844
+ if (/^https?:\/\//.test(pattern)) {
1845
+ this.colorbox('400px', "Incorrect pattern for exclusion", "You can not enter full URL's for exclusion from caching. You entered a full URL that started with http:// or https://. You must enter relative URL's e.g. /exclude/this/page/. You can also enter text that might be contained in the path part of a URL or at the end of the path part of a URL.");
1846
+ return;
1847
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1848
 
1849
+ this.ajax('wordfence_addCacheExclusion', {
1850
+ patternType: patternType,
1851
+ pattern: pattern
1852
+ }, function(res) {
1853
+ if (res.ok) { //Otherwise errorMsg will get caught
1854
+ window.location.reload(true);
1855
+ }
1856
+ });
1857
+ },
1858
+ loadCacheExclusions: function() {
1859
+ this.ajax('wordfence_loadCacheExclusions', {}, function(res) {
1860
+ if (res.ex instanceof Array && res.ex.length > 0) {
1861
+ for (var i = 0; i < res.ex.length; i++) {
1862
+ var newElem = jQuery('#wfCacheExclusionTmpl').tmpl(res.ex[i]);
1863
+ newElem.prependTo('#wfCacheExclusions').fadeIn();
1864
+ }
1865
+ jQuery('<h2>Cache Exclusions</h2>').prependTo('#wfCacheExclusions');
1866
+ } else {
1867
+ jQuery('<h2>Cache Exclusions</h2><p style="width: 500px;">There are not currently any exclusions. If you have a site that does not change often, it is perfectly normal to not have any pages you want to exclude from the cache.</p>').prependTo('#wfCacheExclusions');
1868
+ }
1869
+
1870
+ });
1871
+ },
1872
+ exportSettings: function() {
1873
+ var self = this;
1874
+ this.ajax('wordfence_exportSettings', {}, function(res) {
1875
+ if (res.ok && res.token) {
1876
+ self.colorbox('400px', "Export Successful", "We successfully exported your site settings. To import your site settings on another site, copy and paste the token below into the import text box on the destination site. Keep this token secret. It is like a password. If anyone else discovers the token it will allow them to import your settings excluding your API key.<br /><br />Token:<input type=\"text\" size=\"20\" value=\"" + res.token + "\" onclick=\"this.select();\" /><br />");
1877
+ } else if (res.err) {
1878
+ self.colorbox('400px', "Error during Export", res.err);
1879
+ } else {
1880
+ self.colorbox('400px', "An unknown error occurred", "An unknown error occurred during the export. We received an undefined error from your web server.");
1881
+ }
1882
+ });
1883
+ },
1884
+ importSettings: function(token) {
1885
+ var self = this;
1886
+ this.ajax('wordfence_importSettings', {token: token}, function(res) {
1887
+ if (res.ok) {
1888
+ self.colorbox('400px', "Import Successful", "You successfully imported " + res.totalSet + " options. Your import is complete. Please reload this page or click the button below to reload it:<br /><br /><input type=\"button\" value=\"Reload Page\" onclick=\"window.location.reload(true);\" />");
1889
+ } else if (res.err) {
1890
+ self.colorbox('400px', "Error during Import", res.err);
1891
+ } else {
1892
+ self.colorbox('400px', "Error during Export", "An unknown error occurred during the import");
1893
+ }
1894
+ });
1895
+ },
1896
+ startPasswdAudit: function(auditType, emailAddr) {
1897
+ var self = this;
1898
+ this.ajax('wordfence_startPasswdAudit', {auditType: auditType, emailAddr: emailAddr}, function(res) {
1899
+ self.loadPasswdAuditJobs();
1900
+ if (res.ok) {
1901
+ self.colorbox('400px', "Password Audit Started", "Your password audit started successfully. The results will appear here once it is complete. You will also receive an email letting you know the results are ready at: " + emailAddr);
1902
+ } else if (!res.errorMsg) { //error displayed
1903
+ self.colorbox('400px', "Error Starting Audit", "An unknown error occurred when trying to start your password audit.");
1904
+ }
1905
+ });
1906
  }
1907
+ };
1908
+ window['WFAD'] = window['wordfenceAdmin'];
1909
  }
1910
+ jQuery(function() {
1911
+ wordfenceAdmin.init();
1912
+ });
1913
+ })(jQuery);
 
 
lib/email_passwdChanged.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This email was sent from <?php echo $homeURL; ?>.
2
+
3
+ We recently used Wordfence to audit the strength of passwords on our website.
4
+ We have detected that you have a weak password.
5
+
6
+ To ensure your user account remains secure we have changed your password to:
7
+
8
+ <?php echo $passwd; ?>
9
+
10
+ Please sign into <?php echo $homeURL; ?> now using this new password to verify
11
+ that it works. Your username is: <?php echo $username; ?>
12
+
13
+ We recommend that once you are signed-in you immediately change
14
+ this password to one of your own choosing. We suggest you use a pass-phrase or
15
+ a combination of upper-case, lower-case, numbers and symbols.
16
+
17
+ You can sign in here:
18
+
19
+ <?php echo $loginURL; ?>
20
+
21
+ Thank you.
22
+
23
+ Email generated by Wordfence. Learn more at http://www.wordfence.com/
lib/email_pleaseChangePasswd.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This email was sent from <?php echo $homeURL; ?>.
2
+
3
+ We recently used Wordfence to audit the strength of passwords on our website.
4
+ We have detected that you have a weak password.
5
+
6
+ This may mean that you have a password that is easy to guess. It may also mean that
7
+ you are using a password that is known to hackers.
8
+
9
+ Please sign into <?php echo $homeURL; ?> now and change your password to ensure
10
+ your account remains secure. You can access our sign-in page at:
11
+
12
+ <?php echo $loginURL; ?>
13
+
14
+
15
+ Your username is: <?php echo $username; ?>
16
+
17
+
18
+ We recommend that you use a pass-phrase or a combination of upper-case, lower-case,
19
+ numbers and symbols in your password.
20
+
21
+ Thank you.
22
+
23
+ Email generated by Wordfence. Learn more at http://www.wordfence.com/
lib/menu_countryBlocking.php CHANGED
@@ -37,7 +37,7 @@ WFAD.countryMap = <?php echo json_encode($wfBulkCountries); ?>;
37
  <option value="redir"<?php if(wfConfig::get('cbl_action') == 'redir'){ echo ' selected'; } ?>>Redirect to the URL below</option>
38
  </select>
39
  </td></tr>
40
- <tr><th>URL to redirect blocked users to:</th><td><input type="text" id="wfRedirURL" size="40" value="<?php if(wfConfig::get('cbl_redirURL')){ echo wp_kses(wfConfig::get('cbl_redirURL'), array()); } ?>" />
41
  <br />
42
  <span style="color: #999;">Must start with http:// for example http://yoursite.com/blocked/</span></td></tr>
43
  <tr><th>Block countries even if they are logged in:</th><td><input type="checkbox" id="wfLoggedInBlocked" value="1" <?php if(wfConfig::get('cbl_loggedInBlocked')){ echo 'checked'; } ?> /></td></tr>
@@ -46,9 +46,9 @@ WFAD.countryMap = <?php echo json_encode($wfBulkCountries); ?>;
46
  <tr><td colspan="2"><h2>Advanced Country Blocking Options</h2></td></tr>
47
  <tr><th colspan="2">
48
  If user hits the URL
49
- <input type="text" id="wfBypassRedirURL" value="<?php echo wp_kses(wfConfig::get('cbl_bypassRedirURL', ""), array()); ?>" size="20" />
50
  then redirect that user to
51
- <input type="text" id="wfBypassRedirDest" value="<?php echo wp_kses(wfConfig::get('cbl_bypassRedirDest', ""), array()); ?>" size="20" /> and set a cookie that will bypass all country blocking.
52
  </th></tr>
53
  <tr><th colspan="2">
54
  If user who is allowed to access the site views the URL
37
  <option value="redir"<?php if(wfConfig::get('cbl_action') == 'redir'){ echo ' selected'; } ?>>Redirect to the URL below</option>
38
  </select>
39
  </td></tr>
40
+ <tr><th>URL to redirect blocked users to:</th><td><input type="text" id="wfRedirURL" size="40" value="<?php if(wfConfig::get('cbl_redirURL')){ echo esc_attr(wfConfig::get('cbl_redirURL')); } ?>" />
41
  <br />
42
  <span style="color: #999;">Must start with http:// for example http://yoursite.com/blocked/</span></td></tr>
43
  <tr><th>Block countries even if they are logged in:</th><td><input type="checkbox" id="wfLoggedInBlocked" value="1" <?php if(wfConfig::get('cbl_loggedInBlocked')){ echo 'checked'; } ?> /></td></tr>
46
  <tr><td colspan="2"><h2>Advanced Country Blocking Options</h2></td></tr>
47
  <tr><th colspan="2">
48
  If user hits the URL
49
+ <input type="text" id="wfBypassRedirURL" value="<?php echo esc_attr(wfConfig::get('cbl_bypassRedirURL'), array()); ?>" size="20" />
50
  then redirect that user to
51
+ <input type="text" id="wfBypassRedirDest" value="<?php echo esc_attr(wfConfig::get('cbl_bypassRedirDest'), array()); ?>" size="20" /> and set a cookie that will bypass all country blocking.
52
  </th></tr>
53
  <tr><th colspan="2">
54
  If user who is allowed to access the site views the URL
lib/menu_options.php CHANGED
@@ -318,6 +318,51 @@ $w = new wfConfig();
318
  alerts will be sent.
319
  </td>
320
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  <tr>
322
  <td colspan="2">
323
  <div class="wfMarker" id="wfMarkerLiveTrafficOptions"></div>
@@ -359,7 +404,7 @@ $w = new wfConfig();
359
  href="http://docs.wordfence.com/en/Wordfence_options#Scan_public_facing_site"
360
  target="_blank" class="wfhelp"></a></th>
361
  <td><input type="checkbox" id="scansEnabled_public" class="wfConfigElem"
362
- name="scansEnabled_public" value="1" <?php $w->cb( 'scansEnabled_public' ); ?></td>
363
  </tr>
364
  <?php } else { ?>
365
  <tr>
@@ -377,7 +422,7 @@ $w = new wfConfig();
377
  href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_the_HeartBleed_vulnerability"
378
  target="_blank" class="wfhelp"></a></th>
379
  <td><input type="checkbox" id="scansEnabled_heartbleed" class="wfConfigElem"
380
- name="scansEnabled_heartbleed" value="1" <?php $w->cb( 'scansEnabled_heartbleed' ); ?>
381
  </td>
382
  </tr>
383
  <tr>
318
  alerts will be sent.
319
  </td>
320
  </tr>
321
+ <tr>
322
+ <td colspan="2">
323
+ <div class="wfMarker" id="wfMarkerEmailSummary"></div>
324
+ <h3 class="wfConfigHeading">Email Summary<a
325
+ href="http://docs.wordfence.com/en/Wordfence_options#Email_Summary" target="_blank"
326
+ class="wfhelp"></a></h3>
327
+ </td>
328
+ </tr>
329
+ <tr>
330
+ <th>Enable email summary:</th>
331
+ <td>&nbsp;<input type="checkbox" id="email_summary_enabled" name="email_summary_enabled"
332
+ value="1" <?php $w->cb('email_summary_enabled'); ?> />
333
+ </td>
334
+ </tr>
335
+ <tr>
336
+ <th>Email summary frequency:</th>
337
+ <td>
338
+ <select id="email_summary_interval" class="wfConfigElem" name="email_summary_interval">
339
+ <option value="weekly"<?php $w->sel( 'email_summary_interval', 'weekly' ); ?>>Once a week</option>
340
+ <option value="biweekly"<?php $w->sel( 'email_summary_interval', 'biweekly' ); ?>>Once every 2 weeks</option>
341
+ <option value="monthly"<?php $w->sel( 'email_summary_interval', 'monthly' ); ?>>Once a month</option>
342
+ </select>
343
+ </td>
344
+ </tr>
345
+ <tr>
346
+ <th>Comma-separated list of directories to exclude from recently modified file list:</th>
347
+ <td>
348
+ <input name="email_summary_excluded_directories" type="text" value="<?php $w->f('email_summary_excluded_directories') ?>"/>
349
+ </td>
350
+ </tr>
351
+ <?php if ((defined('WP_DEBUG') && WP_DEBUG) || wfConfig::get('debugOn', 0)): ?>
352
+ <tr>
353
+ <th>Send test email:</th>
354
+ <td>
355
+ <input type="email" id="email_summary_email_address_debug" />
356
+ <a class="button" href="javascript:void(0);" onclick="WFAD.ajax('wordfence_email_summary_email_address_debug', {email: jQuery('#email_summary_email_address_debug').val()});">Send Email</a>
357
+ </td>
358
+ </tr>
359
+ <?php endif ?>
360
+ <tr>
361
+ <th>Enable activity report widget on dashboard:</th>
362
+ <td>&nbsp;<input type="checkbox" id="email_summary_dashboard_widget_enabled" name="email_summary_dashboard_widget_enabled"
363
+ value="1" <?php $w->cb('email_summary_dashboard_widget_enabled'); ?> />
364
+ </td>
365
+ </tr>
366
  <tr>
367
  <td colspan="2">
368
  <div class="wfMarker" id="wfMarkerLiveTrafficOptions"></div>
404
  href="http://docs.wordfence.com/en/Wordfence_options#Scan_public_facing_site"
405
  target="_blank" class="wfhelp"></a></th>
406
  <td><input type="checkbox" id="scansEnabled_public" class="wfConfigElem"
407
+ name="scansEnabled_public" value="1" <?php $w->cb( 'scansEnabled_public' ); ?> /></td>
408
  </tr>
409
  <?php } else { ?>
410
  <tr>
422
  href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_the_HeartBleed_vulnerability"
423
  target="_blank" class="wfhelp"></a></th>
424
  <td><input type="checkbox" id="scansEnabled_heartbleed" class="wfConfigElem"
425
+ name="scansEnabled_heartbleed" value="1" <?php $w->cb( 'scansEnabled_heartbleed' ); ?> />
426
  </td>
427
  </tr>
428
  <tr>
lib/menu_passwd.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wordfenceModeElem" id="wordfenceMode_passwd"></div>
2
+ <div class="wrap" id="paidWrap">
3
+ <?php require('menuHeader.php'); ?>
4
+ <?php $pageTitle = "Audit the Strength of your Passwords";
5
+ $helpLink = "http://docs.wordfence.com/en/Wordfence_Password_Auditing";
6
+ $helpLabel = "Learn more about Password Auditing";
7
+ include('pageTitle.php'); ?>
8
+ <?php if (!wfConfig::get('isPaid')) { ?>
9
+ <div class="wfPaidOnlyNotice">
10
+ <strong>Password Auditing is only available to Premium Members at this time</strong><br/><br/>
11
+ Wordfence Password Auditing uses our high performance password auditing cluster to test the strength of your admin and user passwords.
12
+ We securely simulate a high-performance password cracking attack on your password database and will alert you to weak passwords.
13
+ We then provide a way to change weak passwords or alert members that they need to improve their password strength.
14
+ To activate this feature, simply
15
+ <a href="https://www.wordfence.com/wordfence-signup/" target="_blank">click here and get a premium Wordfence API Key</a>, and then copy and paste it into your options page. You can
16
+ <a href="http://docs.wordfence.com/en/Wordfence_Password_Auditing" target="_blank">learn more about Password Auditing on our Documentation Website</a>.
17
+ </div>
18
+ <?php } ?>
19
+
20
+ <div class="wordfenceWrap" style="margin: 20px 20px 20px 30px;">
21
+ <h2>Start a Password Audit</h2>
22
+ <table class="wfConfigForm" width="800px">
23
+ <tr>
24
+ <td colspan="2">Audit your site passwords by having
25
+ us securely simulate a password cracking attempt using our high performance servers.
26
+ Your report will appear here and you can easily alert your users to a weak password or change their passwords and email them the change.
27
+ </td>
28
+ </tr>
29
+ <tr>
30
+ <th colspan="2">Select the kind of audit you would like to do:</th>
31
+ </tr>
32
+ <tr>
33
+ <th colspan="2">
34
+ <select id="auditType">
35
+ <option value="admin">Audit administrator level accounts (extensive audit against a large dictionary of approx. 260 Million passwords)</option>
36
+ <option value="user">Audit user level accounts (less extensive against a dictionary of approximately 50,000 passwords)</option>
37
+ <option value="both">Audit all WordPress accounts</option>
38
+ </select>
39
+ </th>
40
+ </tr>
41
+ <tr>
42
+ <th>Results will appear on this page. We will email you when they're ready. Enter the email address we should email:</th>
43
+ <td><input type="text" id="emailAddr" size="50" maxlength="255" value="<?php wfConfig::f('alertEmails') ?>"/></td>
44
+ </tr>
45
+ <tr>
46
+ <td colspan="2"><input type="button" name="but4" class="button-primary" value="Start Password Audit"
47
+ onclick="WFAD.startPasswdAudit(jQuery('#auditType').val(), jQuery('#emailAddr').val());"/>
48
+ </td>
49
+ </tr>
50
+
51
+ </table>
52
+ <h2 style="margin-top: 20px;">Audit Status:</h2>
53
+
54
+ <div id="wfAuditJobs">
55
+ </div>
56
+ <h2 style="margin-top: 20px;">Password Audit Results:</h2>
57
+
58
+ <div id="wfAuditResults">
59
+ </div>
60
+ </div>
61
+
62
+ </div>
63
+ <script type="text/x-jquery-template" id="wfAuditResultsTable">
64
+ <div style="margin: 0 0 20px 0;">
65
+ <select id="wfPasswdFixAction">
66
+ <option value="email">Action: Email selected users and ask them to change their weak password.</option>
67
+ <option value="fix">Action: Change weak passwords to a strong password and email users the new password.</option>
68
+ </select><input type="button" value="Fix Weak Passwords" onclick="WFAD.doFixWeakPasswords(); return false;" class="button-primary"/>
69
+ </div>
70
+ <table class="wf-table">
71
+ <thead>
72
+ <th style="text-align: center">
73
+ <input type="checkbox" id="wfSelectAll" onclick="jQuery('.wfUserCheck').attr('checked', this.checked);" />
74
+ </th>
75
+ <th>User Level</th>
76
+ <th>Username</th>
77
+ <th>Full Name</th>
78
+ <th>Email</th>
79
+ <th>Password</th>
80
+ <th>Crack Time</th>
81
+ <th>Crack Difficulty</th>
82
+ </thead>
83
+ <tbody class="wf-pw-audit-tbody"></tbody>
84
+ </table>
85
+ </script>
86
+
87
+ <script type="text/x-jquery-template" id="wfAuditResultsRow">
88
+ <tr>
89
+ <td style="text-align: center;">
90
+ <input type="checkbox" class="wfUserCheck" value="${wpUserID}"/>
91
+ </td>
92
+ <td>{{if wpIsAdmin == '1'}}<span style="color: #F00;">Admin</span>{{else}}User{{/if}}</td>
93
+ <td>${username}</td>
94
+ <td>${firstName} ${lastName}</td>
95
+ <td>${email}</td>
96
+ <td>${starredPassword}</td>
97
+ <td>${crackTime}</td>
98
+ <td>${crackDifficulty}</td>
99
+ </tr>
100
+ </script>
101
+
102
+ <script type="text/x-jquery-template" id="wfAuditJobsTable">
103
+ <table class="wf-table">
104
+ <thead>
105
+ <th>Audit Type</th>
106
+ <th>Admin Accounts</th>
107
+ <th>User Accounts</th>
108
+ <th>Run Time</th>
109
+ <th>Email results to</th>
110
+ <th>Weak Passwords Found</th>
111
+ <th colspan="2">Status</th>
112
+ </thead>
113
+ <tbody class="wf-pw-audit-tbody"></tbody>
114
+ </table>
115
+ </script>
116
+ <script type="text/x-jquery-template" id="wfAuditJobsInProg">
117
+ <tr>
118
+ <td>
119
+ {{if auditType == 'admin'}}
120
+ Admin Accounts
121
+ {{else auditType == 'user'}}
122
+ User Accounts
123
+ {{else auditType == 'both'}}
124
+ All WordPress Accounts
125
+ {{/if}}
126
+ </td>
127
+ <td>${totalAdmins}</td>
128
+ <td>${totalUsers}</td>
129
+ <td>${WFAD.makeTimeAgo(timeTaken)}</td>
130
+ <td>${email}</td>
131
+ <td>${weakFound}</td>
132
+ {{if jobStatus == 'done'}}
133
+ <td colspan="2">
134
+ <span style="color: #FFC200;">Complete</span>
135
+ </td>
136
+ {{else jobStatus == 'killed'}}
137
+ <td colspan="2">
138
+ <span style="color: #A00;">Stopped</span>
139
+ </td>
140
+ {{else jobStatus == 'queued'}}
141
+ <td>
142
+ <span style="color: #F00;">Queued</span>
143
+ </td>
144
+ <td>
145
+ <a href="#" onclick="WFAD.killPasswdAudit('${id}'); return false;">Cancel Audit</a>
146
+ </td>
147
+ {{else jobStatus == 'running'}}
148
+ <td>
149
+ <span style="color: #0A0;">Running</span>
150
+ </td>
151
+ <td>
152
+ <a href="#" onclick="WFAD.killPasswdAudit('${id}'); return false;">Stop Audit</a>
153
+ </td>
154
+ {{/if}}
155
+ </tr>
156
+ </script>
157
+ <script type="text/x-jquery-template" id="wfWelcomePasswd">
158
+ <div>
159
+ <h3>Premium Feature: Audit your Password Strength</h3>
160
+ <strong><p>Want to know how easily a hacker can crack your passwords?</p></strong>
161
+
162
+ <p>
163
+ Wordfence Premium includes password auditing. Using this feature
164
+ we securely test your passwords against a cracking program that hackers use.
165
+ The difference is that we use extremely fast servers in our data center which
166
+ allow us to quickly simulate a complex password cracking attack. We then tell
167
+ you which passwords on your system are weak and help you easily fix the problem.
168
+ </p>
169
+
170
+ <p>
171
+ <?php
172
+ if (wfConfig::get('isPaid')){
173
+ ?>
174
+ You have upgraded to the premium version of Wordfence and have full access
175
+ to this feature along with our other premium features and priority support.
176
+ <?php
177
+ } else {
178
+ ?>
179
+ If you would like access to this premium feature, please
180
+ <a href="https://www.wordfence.com/wordfence-signup/" target="_blank">upgrade to our premium version</a>.
181
+ </p>
182
+ <?php
183
+ }
184
+ ?>
185
+ </div>
186
+ </script>
lib/wfAPI.php CHANGED
@@ -1,181 +1,98 @@
1
  <?php
2
  require_once('wordfenceConstants.php');
3
  require_once('wordfenceClass.php');
 
4
  class wfAPI {
5
  public $lastHTTPStatus = '';
6
  public $lastCurlErrorNo = '';
7
  private $curlContent = 0;
8
  private $APIKey = '';
9
  private $wordpressVersion = '';
10
- public function __construct($apiKey, $wordpressVersion){
 
11
  $this->APIKey = $apiKey;
12
  $this->wordpressVersion = $wordpressVersion;
13
  }
14
- public function getStaticURL($url){ // In the form '/something.bin' without quotes
 
15
  return $this->getURL($this->getAPIURL() . $url);
16
  }
17
- public function call($action, $getParams = array(), $postParams = array()){
18
- $json = $this->getURL($this->getAPIURL() . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&' . self::buildQuery(
19
- array_merge(
20
- array('action' => $action),
21
- $getParams
 
 
 
 
 
 
 
22
  )), $postParams);
23
- if(! $json){
24
  throw new Exception("We received an empty data response from the Wordfence scanning servers when calling the '$action' function.");
25
  }
26
 
27
  $dat = json_decode($json, true);
28
- if(isset($dat['_isPaidKey'])){
29
  wfConfig::set('keyExpDays', $dat['_keyExpDays']);
30
- if($dat['_keyExpDays'] > -1){
31
  wfConfig::set('isPaid', 1);
32
- } else if($dat['_keyExpDays'] < 0){
33
  wfConfig::set('isPaid', '');
34
  }
35
  }
36
-
37
- if(! is_array($dat)){
38
  throw new Exception("We received a data structure that is not the expected array when contacting the Wordfence scanning servers and calling the '$action' function.");
39
  }
40
- if(is_array($dat) && isset($dat['errorMsg'])){
41
  throw new Exception($dat['errorMsg']);
42
  }
43
  return $dat;
44
  }
45
- public function curlWrite($h, $d){
46
- $this->curlContent .= $d;
47
- return strlen($d);
48
- }
49
- protected function getURL($url, $postParams = array()){
50
- if(function_exists('curl_init')){
51
- $this->curlDataWritten = 0;
52
- $this->curlContent = "";
53
- $curl = curl_init($url);
54
- if(defined('WP_PROXY_HOST') && defined('WP_PROXY_PORT') && wfUtils::hostNotExcludedFromProxy($url) ){
55
- curl_setopt($curl, CURLOPT_HTTPPROXYTUNNEL, 0);
56
- curl_setopt($curl, CURLOPT_PROXY, WP_PROXY_HOST . ':' . WP_PROXY_PORT);
57
- if(defined('WP_PROXY_USERNAME') && defined('WP_PROXY_PASSWORD')){
58
- curl_setopt($curl, CURLOPT_PROXYUSERPWD, WP_PROXY_USERNAME . ':' . WP_PROXY_PASSWORD);
59
- }
60
- }
61
- curl_setopt ($curl, CURLOPT_TIMEOUT, 900);
62
- curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]') );
63
- curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
64
- curl_setopt ($curl, CURLOPT_HEADER, 0);
65
- curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, false);
66
- curl_setopt ($curl, CURLOPT_SSL_VERIFYHOST, false);
67
- curl_setopt ($curl, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
68
- curl_setopt($curl, CURLOPT_POST, true);
69
- curl_setopt($curl, CURLOPT_POSTFIELDS, $postParams);
70
- wordfence::status(4, 'info', "CURL fetching URL: " . $url);
71
- curl_exec($curl);
72
-
73
- $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
74
- $this->lastCurlErrorNo = curl_errno($curl);
75
- if($httpStatus == 200){
76
- curl_close($curl);
77
- return $this->curlContent;
78
- } else {
79
- $cerror = curl_error($curl);
80
- curl_close($curl);
81
- throw new Exception("We received an error response when trying to contact the Wordfence scanning servers. The HTTP status code was [$httpStatus] and the curl error number was [" . $this->lastCurlErrorNo . "] " . ($cerror ? (' and the error from CURL was: ' . $cerror) : ''));
82
- }
83
- } else {
84
- wordfence::status(4, 'info', "Fetching URL with file_get: " . $url);
85
- $data = $this->fileGet($url, $postParams);
86
- if($data === false){
87
- $err = error_get_last();
88
- if($err){
89
- throw new Exception("We received an error response when trying to contact the Wordfence scanning servers using PHP's file_get_contents function. The error was: " . var_export($err, true));
90
- } else {
91
- throw new Exception("We received an empty response when trying to contact the Wordfence scanning servers using PHP's file_get_contents function.");
92
- }
93
- }
94
- return $data;
95
  }
96
 
97
- }
98
- private function fileGet($url, $postParams){
99
- if(is_array($postParams)){
100
- $bodyArr = array();
101
- foreach($postParams as $key => $val){
102
- $bodyArr[] = urlencode($key) . '=' . urlencode($val);
103
- }
104
- $body = implode('&', $bodyArr);
105
- } else {
106
- $body = $postParams;
107
  }
108
- $opts = array('http' =>
109
- array(
110
- 'method' => 'POST',
111
- 'content' => $body,
112
- 'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
113
- 'timeout' => 60
114
- )
115
- );
116
- $context = stream_context_create($opts);
117
- return @file_get_contents($url, false, $context, -1);
118
  }
119
- public function binCall($func, $postData){
 
120
  $url = $this->getAPIURL() . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&action=' . $func;
121
- if(function_exists('curl_init')){
122
- $curl = curl_init($url);
123
- if(defined('WP_PROXY_HOST') && defined('WP_PROXY_PORT') && wfUtils::hostNotExcludedFromProxy($url) ){
124
- error_log("BINCALL PROXY");
125
- curl_setopt($curl, CURLOPT_HTTPPROXYTUNNEL, 0);
126
- curl_setopt($curl, CURLOPT_PROXY, WP_PROXY_HOST . ':' . WP_PROXY_PORT);
127
- if(defined('WP_PROXY_USERNAME') && defined('WP_PROXY_PASSWORD')){
128
- curl_setopt($curl, CURLOPT_PROXYUSERPWD, WP_PROXY_USERNAME . ':' . WP_PROXY_PASSWORD);
129
- }
130
- }
131
- curl_setopt ($curl, CURLOPT_TIMEOUT, 900);
132
- //curl_setopt($curl, CURLOPT_VERBOSE, true);
133
- curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence");
134
- curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
135
- curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, false);
136
- curl_setopt ($curl, CURLOPT_SSL_VERIFYHOST, false);
137
- curl_setopt($curl, CURLOPT_POST, true);
138
- if($postData){
139
- curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
140
- } else {
141
- curl_setopt($curl, CURLOPT_POSTFIELDS, array());
142
- }
143
- $data = curl_exec($curl);
144
-
145
- $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
146
- if($httpStatus != 200){
147
- $cError = curl_error($curl);
148
- curl_close($curl);
149
- if($cError){
150
- throw new Exception("We received an error response when trying to fetch binary data from the Wordfence scanning server. The HTTP status was [$httpStatus] with error: $cError");
151
- } else {
152
- throw new Exception("We received an error HTTP response when trying to fetch binary data from the Wordfence scanning server: [$httpStatus]");
153
- }
154
- }
155
- } else {
156
- $data = $this->fileGet($url, $postData);
157
- if($data === false){
158
- $err = error_get_last();
159
- if($err){
160
- throw new Exception("We received an error response when trying to fetch binary data from the Wordfence scanning server using file_get_contents: $err");
161
- } else {
162
- throw new Exception("We received an error when trying to fetch binary data from the Wordfence scanning server using file_get_contents. There was no message explaining the error.");
163
- }
164
- }
165
- $httpStatus = '200';
166
- }
167
- if(preg_match('/\{.*errorMsg/', $data)){
168
  $jdat = @json_decode($data, true);
169
- if(is_array($jdat) && $jdat['errorMsg']){
170
  throw new Exception($jdat['errorMsg']);
171
  }
172
  }
173
- return array('code' => $httpStatus, 'data' => $data);
174
  }
175
- public function makeAPIQueryString(){
 
176
  $siteurl = '';
177
- if(function_exists('get_bloginfo')){
178
- if(is_multisite()){
179
  $siteurl = network_home_url();
180
  $siteurl = rtrim($siteurl, '/'); //Because previously we used get_bloginfo and it returns http://example.com without a '/' char.
181
  } else {
@@ -183,25 +100,29 @@ class wfAPI {
183
  }
184
  }
185
  return self::buildQuery(array(
186
- 'v' => $this->wordpressVersion,
187
- 's' => $siteurl,
188
  'k' => $this->APIKey
189
- ));
190
  }
191
- private function buildQuery($data){
192
- if(version_compare(phpversion(), '5.1.2', '>=')){
 
193
  return http_build_query($data, '', '&'); //arg_separator parameter was only added in PHP 5.1.2. We do this because some PHP.ini's have arg_separator.output set to '&amp;'
194
  } else {
195
  return http_build_query($data);
196
  }
197
  }
198
- private function getAPIURL(){
199
- $ssl_supported = false;
200
- if(defined('CURL_VERSION_SSL') && function_exists('curl_version')){
201
- $version = curl_version();
202
- $ssl_supported = ($version['features'] & CURL_VERSION_SSL);
 
 
 
203
  }
204
- return $ssl_supported ? WORDFENCE_API_URL_SEC : WORDFENCE_API_URL_NONSEC;
205
  }
206
  }
207
 
1
  <?php
2
  require_once('wordfenceConstants.php');
3
  require_once('wordfenceClass.php');
4
+
5
  class wfAPI {
6
  public $lastHTTPStatus = '';
7
  public $lastCurlErrorNo = '';
8
  private $curlContent = 0;
9
  private $APIKey = '';
10
  private $wordpressVersion = '';
11
+
12
+ public function __construct($apiKey, $wordpressVersion) {
13
  $this->APIKey = $apiKey;
14
  $this->wordpressVersion = $wordpressVersion;
15
  }
16
+
17
+ public function getStaticURL($url) { // In the form '/something.bin' without quotes
18
  return $this->getURL($this->getAPIURL() . $url);
19
  }
20
+
21
+ public function call($action, $getParams = array(), $postParams = array(), $forceSSL = false) {
22
+ $apiURL = $this->getAPIURL();
23
+ //Sanity check. Developer should call wfAPI::SSLEnabled() to check if SSL is enabled before forcing SSL and return a user friendly msg if it's not.
24
+ if ($forceSSL && (!preg_match('/^https:/i', $apiURL))) {
25
+ //User's should never see this message unless we aren't calling SSLEnabled() to check if SSL is enabled before using call() with forceSSL
26
+ throw new Exception("SSL is not supported by your web server and is required to use this function. Please ask your hosting provider or site admin to install cURL with openSSL to use this feature.");
27
+ }
28
+ $json = $this->getURL($apiURL . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&' . self::buildQuery(
29
+ array_merge(
30
+ array('action' => $action),
31
+ $getParams
32
  )), $postParams);
33
+ if (!$json) {
34
  throw new Exception("We received an empty data response from the Wordfence scanning servers when calling the '$action' function.");
35
  }
36
 
37
  $dat = json_decode($json, true);
38
+ if (isset($dat['_isPaidKey'])) {
39
  wfConfig::set('keyExpDays', $dat['_keyExpDays']);
40
+ if ($dat['_keyExpDays'] > -1) {
41
  wfConfig::set('isPaid', 1);
42
+ } else if ($dat['_keyExpDays'] < 0) {
43
  wfConfig::set('isPaid', '');
44
  }
45
  }
46
+
47
+ if (!is_array($dat)) {
48
  throw new Exception("We received a data structure that is not the expected array when contacting the Wordfence scanning servers and calling the '$action' function.");
49
  }
50
+ if (is_array($dat) && isset($dat['errorMsg'])) {
51
  throw new Exception($dat['errorMsg']);
52
  }
53
  return $dat;
54
  }
55
+
56
+ protected function getURL($url, $postParams = array()) {
57
+ wordfence::status(4, 'info', "Calling Wordfence API v" . WORDFENCE_API_VERSION . ":" . $url);
58
+
59
+ if (!function_exists('wp_remote_post')) {
60
+ require_once ABSPATH . WPINC . 'http.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
 
63
+ $response = wp_remote_post($url, array(
64
+ 'timeout' => 900,
65
+ 'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'),
66
+ 'body' => $postParams,
67
+ ));
68
+
69
+ $this->lastHTTPStatus = (int) wp_remote_retrieve_response_code($response);
70
+ if (is_wp_error($response) || 200 != $this->lastHTTPStatus) {
71
+ throw new Exception("We received an error response when trying to contact the Wordfence scanning servers. The HTTP status code was [$this->lastHTTPStatus]");
 
72
  }
73
+
74
+ $this->curlContent = wp_remote_retrieve_body($response);
75
+ return $this->curlContent;
 
 
 
 
 
 
 
76
  }
77
+
78
+ public function binCall($func, $postData) {
79
  $url = $this->getAPIURL() . '/v' . WORDFENCE_API_VERSION . '/?' . $this->makeAPIQueryString() . '&action=' . $func;
80
+
81
+ $data = $this->getURL($url, $postData);
82
+
83
+ if (preg_match('/\{.*errorMsg/', $data)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  $jdat = @json_decode($data, true);
85
+ if (is_array($jdat) && $jdat['errorMsg']) {
86
  throw new Exception($jdat['errorMsg']);
87
  }
88
  }
89
+ return array('code' => $this->lastHTTPStatus, 'data' => $data);
90
  }
91
+
92
+ public function makeAPIQueryString() {
93
  $siteurl = '';
94
+ if (function_exists('get_bloginfo')) {
95
+ if (is_multisite()) {
96
  $siteurl = network_home_url();
97
  $siteurl = rtrim($siteurl, '/'); //Because previously we used get_bloginfo and it returns http://example.com without a '/' char.
98
  } else {
100
  }
101
  }
102
  return self::buildQuery(array(
103
+ 'v' => $this->wordpressVersion,
104
+ 's' => $siteurl,
105
  'k' => $this->APIKey
106
+ ));
107
  }
108
+
109
+ private function buildQuery($data) {
110
+ if (version_compare(phpversion(), '5.1.2', '>=')) {
111
  return http_build_query($data, '', '&'); //arg_separator parameter was only added in PHP 5.1.2. We do this because some PHP.ini's have arg_separator.output set to '&amp;'
112
  } else {
113
  return http_build_query($data);
114
  }
115
  }
116
+
117
+ private function getAPIURL() {
118
+ return self::SSLEnabled() ? WORDFENCE_API_URL_SEC : WORDFENCE_API_URL_NONSEC;
119
+ }
120
+
121
+ public static function SSLEnabled() {
122
+ if (!function_exists('wp_http_supports')) {
123
+ require_once ABSPATH . WPINC . 'http.php';
124
  }
125
+ return wp_http_supports(array('ssl'));
126
  }
127
  }
128
 
lib/wfActivityReport.php ADDED
@@ -0,0 +1,462 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class wfActivityReport {
4
+
5
+ /**
6
+ * @var int
7
+ */
8
+ private $limit = 10;
9
+
10
+ /**
11
+ * @var wpdb
12
+ */
13
+ private $db;
14
+
15
+ /**
16
+ * @param int $limit
17
+ */
18
+ public function __construct($limit = 10) {
19
+ global $wpdb;
20
+ $this->db = $wpdb;
21
+ $this->limit = $limit;
22
+ }
23
+
24
+ /**
25
+ * Schedule the activity report cron job.
26
+ */
27
+ public static function scheduleCronJob() {
28
+ self::clearCronJobs();
29
+
30
+ if (!wfConfig::get('email_summary_enabled', 1)) {
31
+ return;
32
+ }
33
+
34
+ list(, $end_time) = wfActivityReport::getReportDateRange();
35
+ wp_schedule_single_event($end_time, 'wordfence_email_activity_report');
36
+ }
37
+
38
+ /**
39
+ * Remove the activity report cron job.
40
+ */
41
+ public static function disableCronJob() {
42
+ self::clearCronJobs();
43
+ }
44
+
45
+ public static function clearCronJobs() {
46
+ wp_clear_scheduled_hook('wordfence_email_activity_report');
47
+ }
48
+
49
+ /**
50
+ * Send out the report and reschedule the next report's cron job.
51
+ */
52
+ public static function executeCronJob() {
53
+ $report = new self();
54
+ $report->sendReportViaEmail(wfConfig::getAlertEmails());
55
+ self::scheduleCronJob();
56
+ }
57
+
58
+ /**
59
+ * Output a compact version of the email for the WP dashboard.
60
+ */
61
+ public static function outputDashboardWidget() {
62
+ $report = new self(5);
63
+ echo $report->toWidgetView();
64
+ }
65
+
66
+ /**
67
+ * @return array
68
+ */
69
+ public static function getReportDateRange() {
70
+ $interval = wfConfig::get('email_summary_interval', 'weekly');
71
+ $offset = get_option('gmt_offset');
72
+ return self::_getReportDateRange($interval, $offset);
73
+ }
74
+
75
+ /**
76
+ * Testable code.
77
+ *
78
+ * @param string $interval
79
+ * @param int $offset
80
+ * @param null $time
81
+ * @return array
82
+ */
83
+ public static function _getReportDateRange($interval = 'weekly', $offset = 0, $time = null) {
84
+ if ($time === null) {
85
+ $time = time();
86
+ }
87
+
88
+ $day = (int) gmdate('w', $time);
89
+ $month = (int) gmdate("n", $time);
90
+ $day_of_month = (int) gmdate("j", $time);
91
+ $year = (int) gmdate("Y", $time);
92
+
93
+ $start_time = 0;
94
+ $end_time = 0;
95
+
96
+ switch ($interval) {
97
+ // Send a report 4pm every Monday
98
+ case 'weekly':
99
+ $start_time = gmmktime(16, 0, 0, $month, $day_of_month - $day + 1, $year) + (-$offset * 60 * 60);
100
+ $end_time = $start_time + (86400 * 7);
101
+ break;
102
+
103
+ // Send a report 4pm every other Monday
104
+ case 'biweekly':
105
+ $start_time = gmmktime(16, 0, 0, $month, $day_of_month - $day + 1, $year) + (-$offset * 60 * 60);
106
+ $end_time = $start_time + (86400 * 14);
107
+ break;
108
+
109
+ // Send a report at 4pm the first of every month
110
+ case 'monthly':
111
+ $start_time = gmmktime(16, 0, 0, $month, 1, $year) + (-$offset * 60 * 60);
112
+ $end_time = gmmktime(16, 0, 0, $month + 1, 1, $year) + (-$offset * 60 * 60);
113
+ break;
114
+ }
115
+
116
+ return array($start_time, $end_time);
117
+ }
118
+
119
+ /**
120
+ * @return array
121
+ */
122
+ public function getFullReport() {
123
+ $start_time = microtime(true);
124
+ return array(
125
+ 'top_ips_blocked' => $this->getTopIPsBlocked($this->limit),
126
+ 'top_countries_blocked' => $this->getTopCountriesBlocked($this->limit),
127
+ 'top_failed_logins' => $this->getTopFailedLogins($this->limit),
128
+ 'recently_modified_files' => $this->getRecentFilesModified($this->limit),
129
+ 'updates_needed' => $this->getUpdatesNeeded(),
130
+ 'microseconds' => microtime(true) - $start_time,
131
+ );
132
+ }
133
+
134
+ /**
135
+ * @return array
136
+ */
137
+ public function getWidgetReport() {
138
+ $start_time = microtime(true);
139
+ return array(
140
+ 'top_ips_blocked' => $this->getTopIPsBlocked($this->limit),
141
+ 'top_countries_blocked' => $this->getTopCountriesBlocked($this->limit),
142
+ 'top_failed_logins' => $this->getTopFailedLogins($this->limit),
143
+ 'updates_needed' => $this->getUpdatesNeeded(),
144
+ 'microseconds' => microtime(true) - $start_time,
145
+ );
146
+ }
147
+
148
+ /**
149
+ * @param int $limit
150
+ * @return mixed
151
+ */
152
+ public function getTopIPsBlocked($limit = 10) {
153
+ $results = $this->db->get_results($this->db->prepare(<<<SQL
154
+ SELECT * FROM {$this->db->prefix}wfBlockedIPLog
155
+ ORDER BY blockCount DESC
156
+ LIMIT %d
157
+ SQL
158
+ , $limit));
159
+ return $results;
160
+ }
161
+
162
+ /**
163
+ * @param int $limit
164
+ * @return array
165
+ */
166
+ public function getTopCountriesBlocked($limit = 10) {
167
+ $results = $this->db->get_results($this->db->prepare(<<<SQL
168
+ SELECT *, COUNT(IP) as totalIPs, SUM(blockCount) as totalBlockCount
169
+ FROM {$this->db->base_prefix}wfBlockedIPLog
170
+ GROUP BY countryCode
171
+ ORDER BY totalBlockCount DESC
172
+ LIMIT %d
173
+ SQL
174
+ , $limit));
175
+ return $results;
176
+ }
177
+
178
+ /**
179
+ * @param int $limit
180
+ * @return mixed
181
+ */
182
+ public function getTopFailedLogins($limit = 10) {
183
+ $results = $this->db->get_results($this->db->prepare(<<<SQL
184
+ SELECT *, sum(fail) as fail_count
185
+ FROM {$this->db->base_prefix}wfLogins
186
+ WHERE fail = 1
187
+ GROUP BY username
188
+ ORDER BY fail_count DESC
189
+ LIMIT %d
190
+ SQL
191
+ , $limit));
192
+ return $results;
193
+ }
194
+
195
+ /**
196
+ * Returns any updates needs or false if everything is up to date.
197
+ *
198
+ * @return array|bool
199
+ */
200
+ public function getUpdatesNeeded() {
201
+ $update_check = new wfUpdateCheck();
202
+ $needs_update = $update_check->checkAllUpdates()
203
+ ->needsAnyUpdates();
204
+ if ($needs_update) {
205
+ return array(
206
+ 'core' => $update_check->getCoreUpdateVersion(),
207
+ 'plugins' => $update_check->getPluginUpdates(),
208
+ 'themes' => $update_check->getThemeUpdates(),
209
+ );
210
+ }
211
+ return false;
212
+ }
213
+
214
+ /**
215
+ * Returns list of files modified within given timeframe.
216
+ *
217
+ * @todo Add option to configure the regex used to filter files allowed in this list.
218
+ * @todo Add option to exclude directories (such as cache directories).
219
+ *
220
+ * @param string $directory Search for files within this directory
221
+ * @param int $time_range One week
222
+ * @param int $limit Max files to return in results
223
+ * @param int $directory_limit Hard limit for number of files to search within a directory.
224
+ * @return array
225
+ */
226
+ public function getRecentFilesModified($limit = 300, $directory = ABSPATH, $time_range = 604800, $directory_limit = 20000) {
227
+ $recently_modified = new wfRecentlyModifiedFiles($directory);
228
+ $recently_modified->run();
229
+ return $recently_modified->mostRecentFiles($limit);
230
+ }
231
+
232
+ /**
233
+ * Remove entries older than a week in the IP log.
234
+ */
235
+ public function rotateIPLog() {
236
+ // default to weekly
237
+ $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 7 day)) / 86400)';
238
+ switch (wfConfig::get('email_summary_interval', 'weekly')) {
239
+ case 'biweekly':
240
+ $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 14 day)) / 86400)';
241
+ break;
242
+ case 'monthly':
243
+ $interval = 'FLOOR(UNIX_TIMESTAMP(DATE_SUB(NOW(), interval 1 month)) / 86400)';
244
+ break;
245
+ }
246
+ $this->db->query(<<<SQL
247
+ DELETE FROM {$this->db->base_prefix}wfBlockedIPLog
248
+ WHERE unixday < $interval
249
+ SQL
250
+ );
251
+ }
252
+
253
+ /**
254
+ * @param mixed $ip_address
255
+ * @param null $unixday
256
+ */
257
+ public static function logBlockedIP($ip_address, $unixday = null) {
258
+ global $wpdb;
259
+
260
+ if (is_string($ip_address) && !is_numeric($ip_address)) {
261
+ $ip_address = wfUtils::inet_aton($ip_address);
262
+ }
263
+
264
+ $blocked_table = "{$wpdb->base_prefix}wfBlockedIPLog";
265
+
266
+ $unixday_insert = 'FLOOR(UNIX_TIMESTAMP() / 86400)';
267
+ if (is_int($unixday)) {
268
+ $unixday_insert = absint($unixday);
269
+ }
270
+
271
+ $country = wfUtils::IP2Country(is_numeric($ip_address) ? wfUtils::inet_ntoa($ip_address) : $ip_address);
272
+
273
+ $wpdb->query($wpdb->prepare(<<<SQL
274
+ INSERT INTO $blocked_table (IP, countryCode, blockCount, unixday)
275
+ VALUES (%s, %s, 1, $unixday_insert)
276
+ ON DUPLICATE KEY UPDATE blockCount = blockCount + 1
277
+ SQL
278
+ , $ip_address, $country));
279
+ }
280
+
281
+ /**
282
+ * @return wfActivityReportView
283
+ */
284
+ public function toView() {
285
+ return new wfActivityReportView('reports/activity-report', $this->getFullReport() + array(
286
+ 'limit' => $this->getLimit(),
287
+ ));
288
+ }
289
+
290
+ /**
291
+ * @return wfActivityReportView
292
+ */
293
+ public function toWidgetView() {
294
+ return new wfActivityReportView('reports/activity-report', $this->getWidgetReport() + array(
295
+ 'limit' => $this->getLimit(),
296
+ ));
297
+ }
298
+
299
+ /**
300
+ * @return wfActivityReportView
301
+ */
302
+ public function toEmailView() {
303
+ return new wfActivityReportView('reports/activity-report-email-inline', $this->getFullReport());
304
+ }
305
+
306
+ /**
307
+ * @param $email_addresses string|array
308
+ * @return bool
309
+ */
310
+ public function sendReportViaEmail($email_addresses) {
311
+ // TODO: setup a title that contains activity range
312
+ return wp_mail($email_addresses, 'Wordfence activity for ' . date_i18n(get_option('date_format')), $this->toEmailView()->__toString(), 'Content-Type: text/html');
313
+ }
314
+
315
+ /**
316
+ * @return string
317
+ * @throws wfViewNotFoundException
318
+ */
319
+ public function render() {
320
+ return $this->toView()
321
+ ->render();
322
+ }
323
+
324
+ /**
325
+ * @return string
326
+ */
327
+ public function __toString() {
328
+ return $this->toView()
329
+ ->__toString();
330
+ }
331
+
332
+ /**
333
+ * @return int
334
+ */
335
+ public function getLimit() {
336
+ return $this->limit;
337
+ }
338
+
339
+ /**
340
+ * @param int $limit
341
+ */
342
+ public function setLimit($limit) {
343
+ $this->limit = $limit;
344
+ }
345
+ }
346
+
347
+
348
+ class wfRecentlyModifiedFiles extends wfDirectoryIterator {
349
+
350
+ /**
351
+ * @var int
352
+ */
353
+ private $time_range = 604800;
354
+
355
+ /**
356
+ * @var array
357
+ */
358
+ private $files = array();
359
+ private $excluded_directories;
360
+
361
+ /**
362
+ * @param string $directory
363
+ * @param int $max_files_per_directory
364
+ * @param int $max_iterations
365
+ * @param int $time_range
366
+ */
367
+ public function __construct($directory = ABSPATH, $max_files_per_directory = 20000, $max_iterations = 250000, $time_range = 604800) {
368
+ parent::__construct($directory, $max_files_per_directory, $max_iterations);
369
+ $this->time_range = $time_range;
370
+ $excluded_directories = explode(',', (string) wfConfig::get('email_summary_excluded_directories'));
371
+ $this->excluded_directories = array();
372
+ foreach ($excluded_directories as $index => $path) {
373
+ if (($dir = realpath(ABSPATH . $path)) !== false) {
374
+ $this->excluded_directories[$dir] = 1;
375
+ }
376
+ }
377
+ }
378
+
379
+ /**
380
+ * @param $dir
381
+ * @return bool
382
+ */
383
+ protected function scan($dir) {
384
+ if (!array_key_exists(realpath($dir), $this->excluded_directories)) {
385
+ return parent::scan($dir);
386
+ }
387
+ return true;
388
+ }
389
+
390
+
391
+ /**
392
+ * @param string $file
393
+ */
394
+ public function file($file) {
395
+ $mtime = filemtime($file);
396
+ if (time() - $mtime < $this->time_range) {
397
+ $this->files[] = array($file, $mtime);
398
+ }
399
+ }
400
+
401
+ /**
402
+ * @param int $limit
403
+ * @return array
404
+ */
405
+ public function mostRecentFiles($limit = 300) {
406
+ usort($this->files, array(
407
+ $this,
408
+ '_sortMostRecentFiles',
409
+ ));
410
+ return array_slice($this->files, 0, $limit);
411
+ }
412
+
413
+ /**
414
+ * Sort in descending order.
415
+ *
416
+ * @param $a
417
+ * @param $b
418
+ * @return int
419
+ */
420
+ private function _sortMostRecentFiles($a, $b) {
421
+ if ($a[1] > $b[1]) {
422
+ return -1;
423
+ }
424
+ if ($a[1] < $b[1]) {
425
+ return 1;
426
+ }
427
+ return 0;
428
+ }
429
+
430
+ /**
431
+ * @return mixed
432
+ */
433
+ public function getFiles() {
434
+ return $this->files;
435
+ }
436
+ }
437
+
438
+
439
+ class wfActivityReportView extends wfView {
440
+
441
+ /**
442
+ * @param $file
443
+ * @return string
444
+ */
445
+ public function displayFile($file) {
446
+ if (stripos($file, ABSPATH) === 0) {
447
+ return substr($file, strlen(ABSPATH));
448
+ }
449
+ return $file;
450
+ }
451
+
452
+ /**
453
+ * @param null $unix_time
454
+ * @return string
455
+ */
456
+ public function modTime($unix_time = null) {
457
+ if ($unix_time === null) {
458
+ $unix_time = time();
459
+ }
460
+ return date_i18n('F j, Y g:ia', $unix_time);
461
+ }
462
+ }
lib/wfCache.php CHANGED
@@ -142,6 +142,7 @@ class wfCache {
142
 
143
  $file = self::fileFromRequest( ($_SERVER['HTTP_HOST'] ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME']), $_SERVER['REQUEST_URI']);
144
  self::makeDirIfNeeded($file);
 
145
  $append = "";
146
  $appendGzip = "";
147
  if(wfConfig::get('addCacheComment', false)){
@@ -219,8 +220,29 @@ class wfCache {
219
  }
220
  return $msg;
221
  }
222
- return false; //Everything is OK
223
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  public static function action_publishPost($id){
225
  $perm = get_permalink($id);
226
  self::deleteFileFromPermalink($perm);
142
 
143
  $file = self::fileFromRequest( ($_SERVER['HTTP_HOST'] ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME']), $_SERVER['REQUEST_URI']);
144
  self::makeDirIfNeeded($file);
145
+ self::writeCacheDirectoryHtaccess();
146
  $append = "";
147
  $appendGzip = "";
148
  if(wfConfig::get('addCacheComment', false)){
220
  }
221
  return $msg;
222
  }
223
+ return self::writeCacheDirectoryHtaccess(); //Everything is OK
224
  }
225
+
226
+ /**
227
+ * Returns false on success to match wfCache::cacheDirectoryTest
228
+ *
229
+ * @see wfCache::cacheDirectoryTest
230
+ *
231
+ * @return bool|string
232
+ */
233
+ public static function writeCacheDirectoryHtaccess() {
234
+ $cacheDir = WP_CONTENT_DIR . '/wfcache/';
235
+ if (!file_exists($cacheDir . '.htaccess') && !@file_put_contents($cacheDir . '.htaccess', 'Deny from all', LOCK_EX)) {
236
+ $err = error_get_last();
237
+ $msg = "We could not write to the file $cacheDir" . ".htaccess.";
238
+ if($err){
239
+ $msg .= " The error was: " . $err['message'];
240
+ }
241
+ return $msg;
242
+ }
243
+ return false;
244
+ }
245
+
246
  public static function action_publishPost($id){
247
  $perm = get_permalink($id);
248
  self::deleteFileFromPermalink($perm);
lib/wfConfig.php CHANGED
@@ -64,8 +64,11 @@ class wfConfig {
64
  "startScansRemotely" => false,
65
  "disableConfigCaching" => false,
66
  "addCacheComment" => false,
 
67
  "allowHTTPSCaching" => false,
68
- "debugOn" => false
 
 
69
  ),
70
  "otherParams" => array(
71
  'securityLevel' => '0',
@@ -88,7 +91,9 @@ class wfConfig {
88
  'max404Humans_action' => "throttle",
89
  'maxScanHits' => "DISABLED",
90
  'maxScanHits_action' => "throttle",
91
- 'blockedTime' => "300"
 
 
92
  )
93
  ),
94
  array( //level 1
@@ -146,8 +151,11 @@ class wfConfig {
146
  "startScansRemotely" => false,
147
  "disableConfigCaching" => false,
148
  "addCacheComment" => false,
 
149
  "allowHTTPSCaching" => false,
150
- "debugOn" => false
 
 
151
  ),
152
  "otherParams" => array(
153
  'securityLevel' => '1',
@@ -170,7 +178,9 @@ class wfConfig {
170
  'max404Humans_action' => "throttle",
171
  'maxScanHits' => "DISABLED",
172
  'maxScanHits_action' => "throttle",
173
- 'blockedTime' => "300"
 
 
174
  )
175
  ),
176
  array( //level 2
@@ -230,7 +240,9 @@ class wfConfig {
230
  "addCacheComment" => false,
231
  "disableCodeExecutionUploads" => false,
232
  "allowHTTPSCaching" => false,
233
- "debugOn" => false
 
 
234
  ),
235
  "otherParams" => array(
236
  'securityLevel' => '2',
@@ -253,7 +265,9 @@ class wfConfig {
253
  'max404Humans_action' => "throttle",
254
  'maxScanHits' => "DISABLED",
255
  'maxScanHits_action' => "throttle",
256
- 'blockedTime' => "300"
 
 
257
  )
258
  ),
259
  array( //level 3
@@ -311,8 +325,11 @@ class wfConfig {
311
  "startScansRemotely" => false,
312
  "disableConfigCaching" => false,
313
  "addCacheComment" => false,
 
314
  "allowHTTPSCaching" => false,
315
- "debugOn" => false
 
 
316
  ),
317
  "otherParams" => array(
318
  'securityLevel' => '3',
@@ -335,7 +352,9 @@ class wfConfig {
335
  'max404Humans_action' => "throttle",
336
  'maxScanHits' => "30",
337
  'maxScanHits_action' => "throttle",
338
- 'blockedTime' => "1800"
 
 
339
  )
340
  ),
341
  array( //level 4
@@ -393,8 +412,11 @@ class wfConfig {
393
  "startScansRemotely" => false,
394
  "disableConfigCaching" => false,
395
  "addCacheComment" => false,
 
396
  "allowHTTPSCaching" => false,
397
- "debugOn" => false
 
 
398
  ),
399
  "otherParams" => array(
400
  'securityLevel' => '4',
@@ -417,7 +439,9 @@ class wfConfig {
417
  'max404Humans_action' => "block",
418
  'maxScanHits' => "10",
419
  'maxScanHits_action' => "block",
420
- 'blockedTime' => "7200"
 
 
421
  )
422
  )
423
  );
@@ -439,6 +463,12 @@ class wfConfig {
439
  if(self::get('other_scanOutside', false) === false){
440
  self::set('other_scanOutside', 0);
441
  }
 
 
 
 
 
 
442
  }
443
  public static function getExportableOptionsKeys(){
444
  $ret = array();
@@ -790,7 +820,9 @@ class wfConfig {
790
  function show_message($msg = 'null'){}
791
  }
792
  */
793
- define('FS_METHOD', 'direct');
 
 
794
  require_once(ABSPATH . 'wp-includes/update.php');
795
  require_once(ABSPATH . 'wp-admin/includes/file.php');
796
  wp_update_plugins();
64
  "startScansRemotely" => false,
65
  "disableConfigCaching" => false,
66
  "addCacheComment" => false,
67
+ "disableCodeExecutionUploads" => false,
68
  "allowHTTPSCaching" => false,
69
+ "debugOn" => false,
70
+ 'email_summary_enabled' => true,
71
+ 'email_summary_dashboard_widget_enabled' => true,
72
  ),
73
  "otherParams" => array(
74
  'securityLevel' => '0',
91
  'max404Humans_action' => "throttle",
92
  'maxScanHits' => "DISABLED",
93
  'maxScanHits_action' => "throttle",
94
+ 'blockedTime' => "300",
95
+ 'email_summary_interval' => 'biweekly',
96
+ 'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wfcache,wp-content/plugins/wordfence/tmp',
97
  )
98
  ),
99
  array( //level 1
151
  "startScansRemotely" => false,
152
  "disableConfigCaching" => false,
153
  "addCacheComment" => false,
154
+ "disableCodeExecutionUploads" => false,
155
  "allowHTTPSCaching" => false,
156
+ "debugOn" => false,
157
+ 'email_summary_enabled' => true,
158
+ 'email_summary_dashboard_widget_enabled' => true,
159
  ),
160
  "otherParams" => array(
161
  'securityLevel' => '1',
178
  'max404Humans_action' => "throttle",
179
  'maxScanHits' => "DISABLED",
180
  'maxScanHits_action' => "throttle",
181
+ 'blockedTime' => "300",
182
+ 'email_summary_interval' => 'biweekly',
183
+ 'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wfcache,wp-content/plugins/wordfence/tmp',
184
  )
185
  ),
186
  array( //level 2
240
  "addCacheComment" => false,
241
  "disableCodeExecutionUploads" => false,
242
  "allowHTTPSCaching" => false,
243
+ "debugOn" => false,
244
+ 'email_summary_enabled' => true,
245
+ 'email_summary_dashboard_widget_enabled' => true,
246
  ),
247
  "otherParams" => array(
248
  'securityLevel' => '2',
265
  'max404Humans_action' => "throttle",
266
  'maxScanHits' => "DISABLED",
267
  'maxScanHits_action' => "throttle",
268
+ 'blockedTime' => "300",
269
+ 'email_summary_interval' => 'biweekly',
270
+ 'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wfcache,wp-content/plugins/wordfence/tmp',
271
  )
272
  ),
273
  array( //level 3
325
  "startScansRemotely" => false,
326
  "disableConfigCaching" => false,
327
  "addCacheComment" => false,
328
+ "disableCodeExecutionUploads" => false,
329
  "allowHTTPSCaching" => false,
330
+ "debugOn" => false,
331
+ 'email_summary_enabled' => true,
332
+ 'email_summary_dashboard_widget_enabled' => true,
333
  ),
334
  "otherParams" => array(
335
  'securityLevel' => '3',
352
  'max404Humans_action' => "throttle",
353
  'maxScanHits' => "30",
354
  'maxScanHits_action' => "throttle",
355
+ 'blockedTime' => "1800",
356
+ 'email_summary_interval' => 'biweekly',
357
+ 'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wfcache,wp-content/plugins/wordfence/tmp',
358
  )
359
  ),
360
  array( //level 4
412
  "startScansRemotely" => false,
413
  "disableConfigCaching" => false,
414
  "addCacheComment" => false,
415
+ "disableCodeExecutionUploads" => false,
416
  "allowHTTPSCaching" => false,
417
+ "debugOn" => false,
418
+ 'email_summary_enabled' => true,
419
+ 'email_summary_dashboard_widget_enabled' => true,
420
  ),
421
  "otherParams" => array(
422
  'securityLevel' => '4',
439
  'max404Humans_action' => "block",
440
  'maxScanHits' => "10",
441
  'maxScanHits_action' => "block",
442
+ 'blockedTime' => "7200",
443
+ 'email_summary_interval' => 'biweekly',
444
+ 'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wfcache,wp-content/plugins/wordfence/tmp',
445
  )
446
  )
447
  );
463
  if(self::get('other_scanOutside', false) === false){
464
  self::set('other_scanOutside', 0);
465
  }
466
+
467
+ if (self::get('email_summary_enabled')) {
468
+ wfActivityReport::scheduleCronJob();
469
+ } else {
470
+ wfActivityReport::disableCronJob();
471
+ }
472
  }
473
  public static function getExportableOptionsKeys(){
474
  $ret = array();
820
  function show_message($msg = 'null'){}
821
  }
822
  */
823
+ if(! defined('FS_METHOD')){
824
+ define('FS_METHOD', 'direct'); //May be defined already and might not be 'direct' so this could cause problems. But we were getting reports of a warning that this is already defined, so this check added.
825
+ }
826
  require_once(ABSPATH . 'wp-includes/update.php');
827
  require_once(ABSPATH . 'wp-admin/includes/file.php');
828
  wp_update_plugins();
lib/wfCrypt.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class wfCrypt {
3
+ private static function getPubKey(){
4
+ #Command to generate our keypair was: openssl req -x509 -newkey rsa:2048 -keyout mycert.key -out mycert.pem -nodes -subj "/C=US/ST=Washington/L=Seattle/O=Wordfence/OU=IT/CN=wordfence.com" -days 7300
5
+ #This is a 2048 bit key using SHA256 with RSA.
6
+ $key = <<<ENDKEY
7
+ -----BEGIN CERTIFICATE-----
8
+ MIIDrTCCApWgAwIBAgIJAIg6Va5tcvwyMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV
9
+ BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRIw
10
+ EAYDVQQKDAlXb3JkZmVuY2UxCzAJBgNVBAsMAklUMRYwFAYDVQQDDA13b3JkZmVu
11
+ Y2UuY29tMB4XDTE1MDMxMjA1NTIzMFoXDTM1MDMwNzA1NTIzMFowbTELMAkGA1UE
12
+ BhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxEjAQ
13
+ BgNVBAoMCVdvcmRmZW5jZTELMAkGA1UECwwCSVQxFjAUBgNVBAMMDXdvcmRmZW5j
14
+ ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/9Ogj1PIQsuZu
15
+ dTUNWlG0zaDNWpeY1ZiB/6oBS/YXkGFuG8R/nZ/kYsRmBm6yRp/3jC/HiPjg+7Zc
16
+ bA/CKoHdUlNjFZ+10DmS369wVX+c0oV9f720b/a0xN0qeKxJTiN2NsAl5szYv2CQ
17
+ Bvzjeb5VfKgrfV9tgYr38swudxvexponYaK0OlDL3u/Xca4SLRKmB+ZYCcZJttoG
18
+ SNFsQMlLHWWmM0FJH9qZ3x8MtRM5KsNEWO+/op511Rr36ZnLJdzUnETsaxHKwuCv
19
+ 0+D9b0mwk8K/c67l63v4+zywXNkdYIslgo7Aeeyb6t0lyyfruXutEyMinmApACT2
20
+ sDMAbYk7AgMBAAGjUDBOMB0GA1UdDgQWBBTstr/AoPQyLLIt4/peFSjj0FFXHzAf
21
+ BgNVHSMEGDAWgBTstr/AoPQyLLIt4/peFSjj0FFXHzAMBgNVHRMEBTADAQH/MA0G
22
+ CSqGSIb3DQEBCwUAA4IBAQA9HsK+XdZh2MGP2SDdggA+MxkNBCCFBtcsmQrpiLUW
23
+ 67xt59FPRMwTgSA9Lt8uqcWaXoHXiaTnXTRtN/BKZR0F71HQfiV6zy511blIRlk2
24
+ nV+vYzwLUENCZ31hQEZsY+uYqBSTiHecUKohn8A9pOOEpis2YEn2zVo4cobdyGa1
25
+ zCnaAN99KT8s9lOO0UW0J52qZhvv4y8YhELtrXKBsFatGEsVIM0NFI+ZDsNpMnSQ
26
+ cmUtLiIJtk5hxNbOaIz2vzbOkbzJ3ehzODJ1X5rya7X0v2akLLhwP9jqz5ua6ttP
27
+ duLv4Q6v3LY6pwDoyKQMDqNNxVjaFmx5HyFWRPofpu/T
28
+ -----END CERTIFICATE-----
29
+ ENDKEY;
30
+ return $key;
31
+ }
32
+ public static function makeSymHexKey($length){
33
+ $charset='ABCDEF0123456789';
34
+ $str = '';
35
+ $count = strlen($charset);
36
+ while($length--) {
37
+ $str .= $charset[mt_rand(0, $count-1)];
38
+ }
39
+ return $str;
40
+ }
41
+ public static function pubCrypt($symKey){ #encrypts a symmetric key and returns it base64
42
+ openssl_public_encrypt($symKey, $encSymKey, self::getPubKey(), OPENSSL_PKCS1_PADDING); //OPENSSL_PKCS1_PADDING is the default but setting explicitly because that's what we expect on the server.
43
+ return base64_encode($encSymKey);
44
+ }
45
+ }
46
+ ?>
lib/wfDirectoryIterator.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class wfDirectoryIterator {
4
+
5
+ abstract public function file($file);
6
+
7
+ /**
8
+ * @var string
9
+ */
10
+ private $directory;
11
+
12
+ /**
13
+ * @var int
14
+ */
15
+ private $directory_limit;
16
+
17
+ /**
18
+ * @var callback
19
+ */
20
+ private $callback;
21
+ /**
22
+ * @var int
23
+ */
24
+ private $max_iterations;
25
+ private $iterations;
26
+
27
+ /**
28
+ * @param string $directory
29
+ * @param int $max_files_per_directory
30
+ * @param int $max_iterations
31
+ */
32
+ public function __construct($directory = ABSPATH, $max_files_per_directory = 20000, $max_iterations = 1000000) {
33
+ $this->directory = $directory;
34
+ $this->directory_limit = $max_files_per_directory;
35
+ $this->max_iterations = $max_iterations;
36
+ }
37
+
38
+ public function run() {
39
+ $this->iterations = 0;
40
+ $this->scan($this->directory);
41
+ }
42
+
43
+ protected function scan($dir) {
44
+ $dir = rtrim($dir, DIRECTORY_SEPARATOR);
45
+ $handle = opendir($dir);
46
+ $file_count = 0;
47
+ while ($file = readdir($handle)) {
48
+ if ($file == '.' || $file == '..') {
49
+ continue;
50
+ }
51
+ $file_path = $dir . '/' . $file;
52
+ if (is_dir($file_path)) {
53
+ if ($this->scan($file_path) === false) {
54
+ closedir($handle);
55
+ return false;
56
+ }
57
+ } else {
58
+ if ($this->file($file_path) === false) {
59
+ closedir($handle);
60
+ return false;
61
+ }
62
+ }
63
+ if ($file_count++ >= $this->directory_limit) {
64
+ break;
65
+ }
66
+ if ($this->iterations++ >= $this->max_iterations) {
67
+ closedir($handle);
68
+ return false;
69
+ }
70
+ }
71
+ closedir($handle);
72
+ }
73
+ }
74
+
lib/wfHelperString.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class wfHelperString {
5
+
6
+ /**
7
+ * cycle through arguments
8
+ *
9
+ * @return mixed
10
+ */
11
+ public static function cycle() {
12
+ static $counter = 0;
13
+ $args = func_get_args();
14
+ if (empty($args)) {
15
+ $counter = 0;
16
+ return null;
17
+ }
18
+ $return_val = $args[$counter % count($args)];
19
+ $counter++;
20
+ return $return_val;
21
+ }
22
+ }
lib/wfLog.php CHANGED
@@ -297,6 +297,9 @@ class wfLog {
297
  $wfsn
298
  );
299
  }
 
 
 
300
  wfCache::updateBlockedIPs('add');
301
  wfConfig::inc('totalIPsBlocked');
302
  return true;
297
  $wfsn
298
  );
299
  }
300
+
301
+ wfActivityReport::logBlockedIP($IP);
302
+
303
  wfCache::updateBlockedIPs('add');
304
  wfConfig::inc('totalIPsBlocked');
305
  return true;
lib/wfScanEngine.php CHANGED
@@ -852,6 +852,10 @@ class wfScanEngine {
852
  }
853
  wordfence::statusEnd($this->statusIDX['dns'], $haveIssues);
854
  }
 
 
 
 
855
  private function scan_oldVersions(){
856
  $this->statusIDX['oldVersions'] = wordfence::statusStart("Scanning for old themes, plugins and core files");
857
  if(! function_exists( 'get_preferred_from_update_core')){
852
  }
853
  wordfence::statusEnd($this->statusIDX['dns'], $haveIssues);
854
  }
855
+
856
+ /**
857
+ * @todo move the update login into wfUpdateCheck
858
+ */
859
  private function scan_oldVersions(){
860
  $this->statusIDX['oldVersions'] = wordfence::statusStart("Scanning for old themes, plugins and core files");
861
  if(! function_exists( 'get_preferred_from_update_core')){
lib/wfSchema.php CHANGED
@@ -159,6 +159,13 @@ class wfSchema {
159
  reason varchar(255) NOT NULL,
160
  totalBlocked int UNSIGNED default 0,
161
  lastBlocked int UNSIGNED default 0
 
 
 
 
 
 
 
162
  ) default charset=utf8"
163
  /*
164
  'wfPerfLog' => "(
159
  reason varchar(255) NOT NULL,
160
  totalBlocked int UNSIGNED default 0,
161
  lastBlocked int UNSIGNED default 0
162
+ ) default charset=utf8",
163
+ 'wfBlockedIPLog' => "(
164
+ IP int UNSIGNED NOT NULL,
165
+ countryCode VARCHAR(2) NOT NULL,
166
+ blockCount int UNSIGNED NOT NULL DEFAULT 0,
167
+ unixday int UNSIGNED NOT NULL,
168
+ PRIMARY KEY(IP, unixday)
169
  ) default charset=utf8"
170
  /*
171
  'wfPerfLog' => "(
lib/wfUpdateCheck.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class wfUpdateCheck {
4
+
5
+ private $needs_core_update = false;
6
+ private $core_update_version = 0;
7
+ private $plugin_updates = array();
8
+ private $theme_updates = array();
9
+
10
+ public function __construct() {
11
+
12
+ }
13
+
14
+ /**
15
+ * @return bool
16
+ */
17
+ public function needsAnyUpdates() {
18
+ return $this->needsCoreUpdate() || count($this->getPluginUpdates()) > 0 || count($this->getThemeUpdates()) > 0;
19
+ }
20
+
21
+ /**
22
+ * Check for any core, plugin or theme updates.
23
+ *
24
+ * @return $this
25
+ */
26
+ public function checkAllUpdates() {
27
+ return $this->checkCoreUpdates()
28
+ ->checkPluginUpdates()
29
+ ->checkThemeUpdates();
30
+ }
31
+
32
+ /**
33
+ * Check if there is an update to the WordPress core.
34
+ *
35
+ * @return $this
36
+ */
37
+ public function checkCoreUpdates() {
38
+ $this->needs_core_update = false;
39
+ if (!function_exists('get_preferred_from_update_core')) {
40
+ require_once(ABSPATH . 'wp-admin/includes/update.php');
41
+ }
42
+ $cur = get_preferred_from_update_core();
43
+ if (isset($cur->response) && $cur->response == 'upgrade') {
44
+ $this->needs_core_update = true;
45
+ $this->core_update_version = $cur->current;
46
+ }
47
+
48
+ return $this;
49
+ }
50
+
51
+ /**
52
+ * Check if any plugins need an update.
53
+ *
54
+ * @return $this
55
+ */
56
+ public function checkPluginUpdates() {
57
+ $this->plugin_updates = array();
58
+
59
+ $update_plugins = get_site_transient('update_plugins');
60
+ if ($update_plugins && !empty($update_plugins->response)) {
61
+ foreach ($update_plugins->response as $plugin => $vals) {
62
+ if (!function_exists('get_plugin_data')) {
63
+ require_once ABSPATH . '/wp-admin/includes/plugin.php';
64
+ }
65
+ $pluginFile = wfUtils::getPluginBaseDir() . $plugin;
66
+ $data = get_plugin_data($pluginFile);
67
+ $data['newVersion'] = $vals->new_version;
68
+ $this->plugin_updates[] = $data;
69
+ }
70
+ }
71
+
72
+ return $this;
73
+ }
74
+
75
+ /**
76
+ * Check if any themes need an update.
77
+ *
78
+ * @return $this
79
+ */
80
+ public function checkThemeUpdates() {
81
+ $this->theme_updates = array();
82
+
83
+ $update_themes = get_site_transient('update_themes');
84
+ if ($update_themes && (!empty($update_themes->response))) {
85
+ if (!function_exists('wp_get_themes')) {
86
+ require_once ABSPATH . '/wp-includes/theme.php';
87
+ }
88
+ $themes = wp_get_themes();
89
+ foreach ($update_themes->response as $theme => $vals) {
90
+ foreach ($themes as $name => $themeData) {
91
+ if (strtolower($name) == $theme) {
92
+ $this->theme_updates[] = array(
93
+ 'newVersion' => $vals['new_version'],
94
+ 'package' => $vals['package'],
95
+ 'URL' => $vals['url'],
96
+ 'name' => $themeData['Name'],
97
+ 'version' => $themeData['Version']
98
+ );
99
+ }
100
+ }
101
+ }
102
+ }
103
+ return $this;
104
+ }
105
+
106
+ /**
107
+ * @return boolean
108
+ */
109
+ public function needsCoreUpdate() {
110
+ return $this->needs_core_update;
111
+ }
112
+
113
+ /**
114
+ * @return int
115
+ */
116
+ public function getCoreUpdateVersion() {
117
+ return $this->core_update_version;
118
+ }
119
+
120
+ /**
121
+ * @return array
122
+ */
123
+ public function getPluginUpdates() {
124
+ return $this->plugin_updates;
125
+ }
126
+
127
+ /**
128
+ * @return array
129
+ */
130
+ public function getThemeUpdates() {
131
+ return $this->theme_updates;
132
+ }
133
+ }
lib/wfView.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class wfView {
4
+
5
+ /**
6
+ * @var string
7
+ */
8
+ protected $view_path;
9
+
10
+ /**
11
+ * @var string
12
+ */
13
+ protected $view_file_extension = '.php';
14
+
15
+ /**
16
+ * @var string
17
+ */
18
+ protected $view;
19
+
20
+ /**
21
+ * @var array
22
+ */
23
+ protected $data;
24
+
25
+ /**
26
+ * @param string $view
27
+ * @param array $data
28
+ * @return wfView
29
+ */
30
+ public static function create($view, $data = array()) {
31
+ return new self($view, $data);
32
+ }
33
+
34
+ /**
35
+ * @param string $view
36
+ * @param array $data
37
+ */
38
+ public function __construct($view, $data = array()) {
39
+ $this->view_path = WP_PLUGIN_DIR . '/wordfence/views';
40
+ $this->view = $view;
41
+ $this->data = $data;
42
+ }
43
+
44
+ /**
45
+ * @return string
46
+ * @throws wfViewNotFoundException
47
+ */
48
+ public function render() {
49
+ $view = preg_replace('/\.{2,}/', '.', $this->view);
50
+ $view_path = $this->view_path . '/' . $view . $this->view_file_extension;
51
+ if (!file_exists($view_path)) {
52
+ throw new wfViewNotFoundException('The view ' . $view_path . ' does not exist or is not readable.');
53
+ }
54
+
55
+ extract($this->data, EXTR_SKIP);
56
+
57
+ ob_start();
58
+ /** @noinspection PhpIncludeInspection */
59
+ include $view_path;
60
+ return ob_get_clean();
61
+ }
62
+
63
+ /**
64
+ * @return string
65
+ */
66
+ public function __toString() {
67
+ try {
68
+ return $this->render();
69
+ } catch (wfViewNotFoundException $e) {
70
+ return defined('WP_DEBUG') && WP_DEBUG ? $e->getMessage() : 'The view could not be loaded.';
71
+ }
72
+ }
73
+
74
+ /**
75
+ * @param $data
76
+ * @return $this
77
+ */
78
+ public function addData($data) {
79
+ $this->data = array_merge($data, $this->data);
80
+ return $this;
81
+ }
82
+
83
+ /**
84
+ * @return array
85
+ */
86
+ public function getData() {
87
+ return $this->data;
88
+ }
89
+
90
+ /**
91
+ * @param array $data
92
+ * @return $this
93
+ */
94
+ public function setData($data) {
95
+ $this->data = $data;
96
+ return $this;
97
+ }
98
+
99
+ /**
100
+ * @return string
101
+ */
102
+ public function getView() {
103
+ return $this->view;
104
+ }
105
+
106
+ /**
107
+ * @param string $view
108
+ * @return $this
109
+ */
110
+ public function setView($view) {
111
+ $this->view = $view;
112
+ return $this;
113
+ }
114
+
115
+ /**
116
+ * Prevent POP
117
+ */
118
+ public function __wakeup() {
119
+ $this->view_path = WP_PLUGIN_DIR . '/wordfence/views';
120
+ $this->view = null;
121
+ $this->data = array();
122
+ $this->view_file_extension = '.php';
123
+ }
124
+ }
125
+
126
+ class wfViewNotFoundException extends Exception {
127
+ }
lib/wordfenceClass.php CHANGED
@@ -12,6 +12,13 @@ require_once('wfLog.php');
12
  require_once('wfConfig.php');
13
  require_once('wfSchema.php');
14
  require_once('wfCache.php');
 
 
 
 
 
 
 
15
  class wordfence {
16
  public static $printStatus = false;
17
  public static $wordfence_wp_version = false;
@@ -34,32 +41,32 @@ class wordfence {
34
  update_option('wordfenceActivated', 1);
35
  }
36
  public static function uninstallPlugin(){
37
- //Check if caching is enabled and if it is, disable it and fix the .htaccess file.
38
  $cacheType = wfConfig::get('cacheType', false);
39
  if($cacheType == 'falcon'){
40
  wfCache::addHtaccessCode('remove');
41
  wfCache::updateBlockedIPs('remove');
42
  wfConfig::set('cacheType', false);
43
-
44
- //We currently don't clear the cache when plugin is disabled because it will take too long if done synchronously and won't work because plugin is disabled if done asynchronously.
45
  //wfCache::scheduleCacheClear();
46
  } else if($cacheType == 'php'){
47
  wfConfig::set('cacheType', false);
48
  }
49
-
50
 
51
  //Used by MU code below
52
  update_option('wordfenceActivated', 0);
53
  wp_clear_scheduled_hook('wordfence_daily_cron');
54
  wp_clear_scheduled_hook('wordfence_hourly_cron');
55
  wp_clear_scheduled_hook('wordfence_daily_autoUpdate');
56
-
57
  //Remove old legacy cron job if it exists
58
  wp_clear_scheduled_hook('wordfence_scheduled_scan');
59
-
60
  //Remove all scheduled scans.
61
  self::unscheduleAllScans();
62
-
63
  wfConfig::clearDiskCache();
64
  if(wfConfig::get('deleteTablesOnDeact')){
65
  $schema = new wfSchema();
@@ -113,7 +120,7 @@ class wordfence {
113
  for($i = 0; $i < $len; $i += 4){
114
  list($ipLong) = array_values(unpack('N', substr($resp['data'], $i, 4)));
115
  $IPStr = long2ip($ipLong);
116
- if(! self::getLog()->isWhitelisted($IPStr)){
117
  self::getLog()->blockIP($IPStr, $reason, true);
118
  }
119
  }
@@ -173,7 +180,7 @@ class wordfence {
173
  wordfence::status(4, 'error', "Could not fetch vulnerability patterns in scheduled job: " . $e->getMessage());
174
  }
175
 
176
- $wfdb->queryWrite("delete from $p"."wfLocs where ctime < unix_timestamp() - %d", WORDFENCE_MAX_IPLOC_AGE);
177
  $wfdb->truncate($p . "wfBadLeechers"); //only uses date that's less than 1 minute old
178
  $wfdb->queryWrite("delete from $p"."wfBlocks where (blockedTime + %s < unix_timestamp()) and permanent=0", wfConfig::get('blockedTime'));
179
  $wfdb->queryWrite("delete from $p"."wfCrawlers where lastUpdate < unix_timestamp() - (86400 * 7)");
@@ -220,7 +227,7 @@ class wordfence {
220
  $count4 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus");
221
  if($count4 > 100000){
222
  $wfdb->truncate($p . "wfStatus");
223
- } else if($count4 > 1000){ //max status events we keep. This determines how much gets emailed to us when users sends us a debug report.
224
  $wfdb->queryWrite("delete from $p"."wfStatus where level != 10 order by ctime asc limit %d", ($count4 - 1000));
225
  $count5 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus where level=10");
226
  if($count5 > 100){
@@ -228,6 +235,8 @@ class wordfence {
228
  }
229
  }
230
 
 
 
231
  }
232
  public static function runInstall(){
233
  if(self::$runInstallCalled){ return; }
@@ -302,16 +311,16 @@ class wordfence {
302
  $db->queryWriteIgnoreError("alter table $prefix"."wfLockedOut modify column blockedTime bigint signed NOT NULL");
303
  $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileQueue");
304
  $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileChanges");
305
- //Adding primary key to this table because some backup apps use primary key during backup.
306
  $db->queryWriteIgnoreError("alter table wp_wfStatus add id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY");
307
 
308
  $optScanEnabled = $db->querySingle("select val from $prefix"."wfConfig where name='scansEnabled_options'");
309
  if($optScanEnabled != '0' && $optScanEnabled != '1'){
310
  $db->queryWrite("update $prefix"."wfConfig set val='1' where name='scansEnabled_options'");
311
  }
312
-
313
  $optScanEnabled = $db->querySingle("select val from $prefix"."wfConfig where name='scansEnabled_heartbleed'");
314
- if($optScanEnabled != '0' && $optScanEnabled != '1'){ //Enable heartbleed if no value is set.
315
  wfConfig::set('scansEnabled_heartbleed', 1);
316
  }
317
 
@@ -325,7 +334,7 @@ class wordfence {
325
  } else {
326
  $wfLog->logLeechAndBlock('hit');
327
  }
328
- if(wfConfig::liveTrafficEnabled()){
329
  self::$hitID = $wfLog->logHit();
330
  add_action('wp_head', 'wordfence::wfLogHumanHeader');
331
  }
@@ -376,10 +385,8 @@ class wordfence {
376
  add_action('wp_ajax_wordfence_logHuman', 'wordfence::ajax_logHuman_callback');
377
  add_action('wp_ajax_wordfence_doScan', 'wordfence::ajax_doScan_callback');
378
  add_action('wp_ajax_wordfence_testAjax', 'wordfence::ajax_testAjax_callback');
379
- /*
380
- add_action('wp_dashboard_setup', 'wordfence::addDashboardWidget');
381
- */
382
 
 
383
  }
384
 
385
 
@@ -391,7 +398,7 @@ class wordfence {
391
  add_action('init', 'wordfence::initAction');
392
  add_action('template_redirect', 'wordfence::templateRedir');
393
  add_action('shutdown', 'wordfence::shutdownAction');
394
-
395
  if(version_compare(PHP_VERSION, '5.4.0') >= 0){
396
  add_action('wp_authenticate','wordfence::authActionNew', 1, 2);
397
  } else {
@@ -411,9 +418,12 @@ class wordfence {
411
  add_action('publish_future_post', 'wordfence::publishFuturePost');
412
  add_action('mobile_setup', 'wordfence::jetpackMobileSetup'); //Action called in Jetpack Mobile Theme: modules/minileven/minileven.php
413
 
 
 
 
414
  //For debugging
415
  //add_filter( 'cron_schedules', 'wordfence::cronAddSchedules' );
416
-
417
  add_filter('wp_redirect', 'wordfence::wpRedirectFilter', 99, 2);
418
  add_filter('pre_comment_approved', 'wordfence::preCommentApprovedFilter', '99', 2);
419
  //html|xhtml|atom|rss2|rdf|comment|export
@@ -460,7 +470,7 @@ class wordfence {
460
  }
461
  */
462
  public static function jetpackMobileSetup(){
463
- define('WFDONOTCACHE', true); //Don't cache jetpack mobile theme pages.
464
  }
465
  public static function wpRedirectFilter($URL, $status){
466
  if(isset($_GET['author']) && preg_match('/\/author\/.+/i', $URL) && wfConfig::get('loginSec_disableAuthorScan') ){ //author query variable is present and we're about to redirect to a URL that starts with http://blah/author/...
@@ -491,8 +501,8 @@ class wordfence {
491
  }
492
  $UA = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
493
  $URL = $_POST['URL'];
494
- $wfLog->logPerf(wfUtils::getIP(), $UA, $URL, $data);
495
- die(json_encode(array('ok' => 1)));
496
  }
497
  public static function ajax_logHuman_callback(){
498
  $browscap = new wfBrowscap();
@@ -506,7 +516,7 @@ class wordfence {
506
  }
507
 
508
  @ob_end_clean();
509
- if(! headers_sent()){
510
  header('Content-type: text/javascript');
511
  header("Connection: close");
512
  header("Content-Length: 0");
@@ -528,7 +538,7 @@ class wordfence {
528
  }
529
  $func = (isset($_POST['action']) && $_POST['action']) ? $_POST['action'] : $_GET['action'];
530
  $nonce = (isset($_POST['nonce']) && $_POST['nonce']) ? $_POST['nonce'] : $_GET['nonce'];
531
- if(! wp_verify_nonce($nonce, 'wp-ajax')){
532
  die(json_encode(array('errorMsg' => "Your browser sent an invalid security token to Wordfence. Please try reloading this page or signing out and in again.")));
533
  }
534
  //func is e.g. wordfence_ticker so need to munge it
@@ -537,7 +547,7 @@ class wordfence {
537
  if($returnArr === false){
538
  $returnArr = array('errorMsg' => "Wordfence encountered an internal error executing that request.");
539
  }
540
-
541
  if(! is_array($returnArr)){
542
  error_log("Function " . wp_kses($func, array()) . " did not return an array and did not generate an error.");
543
  $returnArr = array();
@@ -584,7 +594,7 @@ class wordfence {
584
  return $errors;
585
  }
586
  public static function isStrongPasswd($passwd, $username ) {
587
- $strength = 0;
588
  if(strlen( trim( $passwd ) ) < 5)
589
  return false;
590
  if(strtolower( $passwd ) == strtolower( $username ) )
@@ -601,7 +611,7 @@ class wordfence {
601
  $strength += 26;
602
  if ($num = preg_match_all( "/[^a-zA-Z0-9]/", $passwd, $matches)){
603
  $strength += (31 * (int)$num);
604
-
605
  }
606
  if($strength > 60){
607
  return true;
@@ -747,7 +757,7 @@ class wordfence {
747
  public static function loginAction($username){
748
  if(sizeof($_POST) < 1){ return; } //only execute if login form is posted
749
  if(! $username){ return; }
750
- wfConfig::inc('totalLogins');
751
  $user = get_user_by('login', $username);
752
  $userID = $user ? $user->ID : 0;
753
  self::getLog()->logLogin('loginOK', 0, $username);
@@ -763,7 +773,7 @@ class wordfence {
763
  }
764
 
765
  if(user_can($userID, 'update_core')){
766
- if(wfConfig::get('alertOn_adminLogin')){
767
  wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.", wfUtils::getIP());
768
  }
769
  } else {
@@ -780,18 +790,18 @@ class wordfence {
780
  }
781
  public static function authenticateFilter($authUser, $username, $passwd){
782
  wfConfig::inc('totalLoginHits'); //The total hits to wp-login.php including logins, logouts and just hits.
783
- $IP = wfUtils::getIP();
784
  $secEnabled = wfConfig::get('loginSecurityEnabled');
785
  if($secEnabled && (! self::getLog()->isWhitelisted($IP)) && wfConfig::get('isPaid') ){
786
  $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
787
  if(isset($twoFactorUsers) && is_array($twoFactorUsers) && sizeof($twoFactorUsers) > 0){
788
  $userDat = (isset($_POST['wordfence_userDat']) ? $_POST['wordfence_userDat'] : false);
789
- if(is_object($userDat) && get_class($authUser) == 'WP_User'){ //Valid username and password either with or without the 'wf...' code. Users is now logged in at this point.
790
  if(isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor']){ //user entered a valid user and password with ' wf....' appended
791
  foreach($twoFactorUsers as &$t){
792
  if($t[0] == $userDat->ID && $t[3] == 'activated'){
793
  if($_POST['wordfence_authFactor'] == $t[2] && $t[4] > time()){
794
- //Do nothing and allow user to sign in. Their passwd has already been modified to be the passwd without the code.
795
  } else if($_POST['wordfence_authFactor'] == $t[2]){
796
  $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
797
  $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $t[1]) );
@@ -806,7 +816,7 @@ class wordfence {
806
  } else {
807
  break; //No new code was received. Let them sign in with the expired code.
808
  }
809
- } else { //Bad code, so cancel the login and return an error to user.
810
  return new WP_Error( 'twofactor_required', __( '<strong>INVALID CODE</strong>: You need to enter your password followed by a space and the code we sent to your phone. The code should start with \'wf\' and should be four characters. e.g. wfAB12. In this case you would enter your password as: \'mypassword wfAB12\' without quotes.'));
811
  }
812
  } //No user matches and has TF activated so let user sign in.
@@ -825,7 +835,7 @@ class wordfence {
825
  $t[4] = time() + 1800; //30 minutes until code expires
826
  wfConfig::set_ser('twoFactorUsers', $twoFactorUsers); //save the code the user needs to enter and return an error.
827
  return new WP_Error( 'twofactor_required', __( '<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space and the code to the end of your password.' ) );
828
- } else { //oops, our API returned an error.
829
  break; //Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems.
830
  }
831
  } //User is not present in two factor list or is not activated. Sign in without twofactor.
@@ -842,7 +852,7 @@ class wordfence {
842
  if($maxBlockTime = self::wfsnIsBlocked($IP, 'brute')){
843
  self::getLog()->blockIP($IP, "Blocked by Wordfence Security Network", true, false, $maxBlockTime);
844
  }
845
-
846
  }
847
  if($secEnabled){
848
  if(is_wp_error($authUser) && $authUser->get_error_code() == 'invalid_username'){
@@ -883,9 +893,9 @@ class wordfence {
883
  }
884
  if(is_wp_error($authUser)){
885
  if($authUser->get_error_code() == 'invalid_username'){
886
- self::getLog()->logLogin('loginFailInvalidUsername', 1, $username);
887
  } else {
888
- self::getLog()->logLogin('loginFailValidUsername', 1, $username);
889
  }
890
  }
891
 
@@ -948,7 +958,7 @@ class wordfence {
948
  $userID = get_current_user_id();
949
  $userDat = get_user_by('id', $userID);
950
  if(is_object($userDat)){
951
- self::getLog()->logLogin('logout', 0, $userDat->user_login);
952
  }
953
  }
954
  public static function loginInitAction(){
@@ -960,10 +970,10 @@ class wordfence {
960
  if(self::isLockedOut(wfUtils::getIP())){
961
  require('wfLockedOut.php');
962
  }
963
- if(! $username){ return; }
964
  $userDat = get_user_by('login', $username);
965
  $_POST['wordfence_userDat'] = $userDat;
966
- if(preg_match(self::$passwordCodePattern, $passwd, $matches)){
967
  $_POST['wordfence_authFactor'] = $matches[1];
968
  $passwd = preg_replace('/^(.+)\s+(wf[a-z0-9]+)$/i', '$1', $passwd);
969
  $_POST['pwd'] = $passwd;
@@ -973,10 +983,10 @@ class wordfence {
973
  if(self::isLockedOut(wfUtils::getIP())){
974
  require('wfLockedOut.php');
975
  }
976
- if(! $username){ return; }
977
  $userDat = get_user_by('login', $username);
978
  $_POST['wordfence_userDat'] = $userDat;
979
- if(preg_match(self::$passwordCodePattern, $passwd, $matches)){
980
  $_POST['wordfence_authFactor'] = $matches[1];
981
  $passwd = preg_replace('/^(.+)\s+(wf[a-z0-9]+)$/i', '$1', $passwd);
982
  $_POST['pwd'] = $passwd;
@@ -1168,12 +1178,12 @@ class wordfence {
1168
  $nextTime = self::getNextScanStartTime();
1169
  return array(
1170
  'ok' => 1,
1171
- 'nextStart' => ($nextTime ? $nextTime : '')
1172
  );
1173
  }
1174
  public static function getNextScanStartTime(){
1175
  $nextTime = false;
1176
- $cron = _get_cron_array();
1177
  foreach($cron as $key => $val){
1178
  if(isset($val['wordfence_start_scheduled_scan'])){
1179
  $nextTime = $key;
@@ -1183,7 +1193,7 @@ class wordfence {
1183
  return ($nextTime ? date('l jS \of F Y H:i:s A', $nextTime + (3600 * get_option('gmt_offset'))) : '');
1184
  }
1185
  public static function wordfenceStartScheduledScan(){
1186
-
1187
  //If scheduled scans are not enabled in the global config option, then don't run a scheduled scan.
1188
  if(wfConfig::get('scheduledScansEnabled') != '1'){
1189
  return;
@@ -1198,7 +1208,7 @@ class wordfence {
1198
  }
1199
  wfConfig::set('lastScheduledScanStart', time());
1200
  wordfence::status(1, 'info', "Scheduled Wordfence scan starting at " . date('l jS \of F Y h:i:s A', current_time('timestamp')) );
1201
-
1202
  //We call this before the scan actually starts to advance the schedule for the next week.
1203
  //This ensures that if the scan crashes for some reason, the schedule will hold.
1204
  wordfence::scheduleScans();
@@ -1210,7 +1220,7 @@ class wordfence {
1210
  $sched = wfConfig::get_ser('scanSched', array());
1211
  $mode = wfConfig::get('schedMode');
1212
  if($mode == 'manual' && is_array($sched) && is_array($sched[0]) ){
1213
- //Use sched as it is
1214
  } else { //Default to setting scans to run once a day at a randomly selected time.
1215
  $sched = array();
1216
  $runAt = rand(0,23);
@@ -1283,15 +1293,15 @@ class wordfence {
1283
  $content .= date(DATE_RFC822, $r['ctime'] + $timeOffset) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . wp_kses_data( (string) $r['msg']) . "\n";
1284
  }
1285
  $content .= "\n\n";
1286
-
1287
  ob_start();
1288
  phpinfo();
1289
  $phpinfo = ob_get_contents();
1290
  ob_get_clean();
1291
 
1292
  $content .= $phpinfo;
1293
-
1294
- wp_mail($_POST['email'], "Wordfence Activity Log", $content);
1295
  return array('ok' => 1);
1296
  }
1297
  public static function ajax_startTourAgain_callback(){
@@ -1305,7 +1315,7 @@ class wordfence {
1305
  if($keyData['ok'] && $keyData['apiKey']){
1306
  wfConfig::set('apiKey', $keyData['apiKey']);
1307
  wfConfig::set('isPaid', 0);
1308
- //When downgrading we must disable all two factor authentication because it can lock an admin out if we don't.
1309
  wfConfig::set_ser('twoFactorUsers', array());
1310
  } else {
1311
  throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key.");
@@ -1348,9 +1358,9 @@ class wordfence {
1348
  public static function disablePermalinksFilter($newVal, $oldVal){
1349
  if(wfConfig::get('cacheType', false) == 'falcon' && $oldVal && (! $newVal) ){ //Falcon is enabled and admin is disabling permalinks
1350
  wfCache::addHtaccessCode('remove');
1351
- //if($err){ return $oldVal; } //We might want to not allow the user to disable permalinks if we can't disable falcon. Allowing it for now.
1352
  wfCache::updateBlockedIPs('remove');
1353
- //if($err){ return $oldVal; } //We might want to not allow the user to disable permalinks if we can't disable falcon. Allowing it for now.
1354
  wfConfig::set('cacheType', false);
1355
  }
1356
  return $newVal;
@@ -1447,12 +1457,12 @@ class wordfence {
1447
  return array('ok' => 1, 'heading' => "Could not write to cache directory", 'body' => "To enable caching, Wordfence needs to be able to create and write to the /wp-content/wfcache/ directory. We did some tests that indicate this is not possible. You need to manually create the /wp-content/wfcache/ directory and make it writable by Wordfence. The error we encountered was during our tests was: $err");
1448
  }
1449
  }
1450
-
1451
- //Mainly we clear the cache here so that any footer cache diagnostic comments are rebuilt. We could just leave it intact unless caching is being disabled.
1452
  if($cacheType != wfConfig::get('cacheType', false)){
1453
  wfCache::scheduleCacheClear();
1454
  }
1455
- $htMsg = "";
1456
  if($warnHtaccess){
1457
  $htMsg = " <strong style='color: #F00;'>Warning: We could not remove the caching code from your .htaccess file. you need to remove this manually yourself.</strong> ";
1458
  }
@@ -1480,11 +1490,11 @@ class wordfence {
1480
  if($s['files'] == 0){
1481
  return array('ok' => 1, 'heading' => 'Cache Stats', 'body' => "The cache is currently empty. It may be disabled or it may have been recently cleared.");
1482
  }
1483
- $body = 'Total files in cache: ' . $s['files'] .
1484
- '<br />Total directories in cache: ' . $s['dirs'] .
1485
  '<br />Total data: ' . $s['data'] . 'KB';
1486
  if($s['compressedFiles'] > 0){
1487
- $body .= '<br />Files: ' . $s['uncompressedFiles'] .
1488
  '<br />Data: ' . $s['uncompressedKBytes'] . 'KB' .
1489
  '<br />Compressed files: ' . $s['compressedFiles'] .
1490
  '<br />Compressed data: ' . $s['compressedKBytes'] . 'KB';
@@ -1585,7 +1595,7 @@ class wordfence {
1585
  }
1586
  $ex = unserialize($ex);
1587
  $rewriteHtaccess = false;
1588
- for($i = 0; $i < sizeof($ex); $i++){
1589
  if((string)$ex[$i]['id'] == (string)$id){
1590
  if(wfConfig::get('cacheType', false) == 'falcon' && preg_match('/^(?:uac|uaeq|cc)$/', $ex[$i]['pt'])){
1591
  $rewriteHtaccess = true;
@@ -1708,13 +1718,13 @@ class wordfence {
1708
  if(sizeof($validIPs) > 0){
1709
  $opts['liveTraf_ignoreIPs'] = implode(',', $validIPs);
1710
  }
1711
-
1712
  if(preg_match('/[a-zA-Z0-9\d]+/', $opts['liveTraf_ignoreUA'])){
1713
  $opts['liveTraf_ignoreUA'] = trim($opts['liveTraf_ignoreUA']);
1714
  } else {
1715
  $opts['liveTraf_ignoreUA'] = '';
1716
  }
1717
- if(! $opts['other_WFNet']){
1718
  $wfdb = new wfDB();
1719
  global $wpdb;
1720
  $p = $wpdb->base_prefix;
@@ -1742,7 +1752,7 @@ class wordfence {
1742
  } else if($opts['autoUpdate'] == '0'){
1743
  wfConfig::disableAutoUpdate();
1744
  }
1745
-
1746
  try {
1747
  if ($opts['disableCodeExecutionUploads']) {
1748
  wfConfig::disableCodeExecutionForUploads();
@@ -1752,6 +1762,17 @@ class wordfence {
1752
  } catch (wfConfigException $e) {
1753
  return array('errorMsg' => $e->getMessage());
1754
  }
 
 
 
 
 
 
 
 
 
 
 
1755
 
1756
  $paidKeyMsg = false;
1757
 
@@ -1797,7 +1818,7 @@ class wordfence {
1797
  $op = $_POST['op'];
1798
  $wfLog = self::getLog();
1799
  if($op == 'blocked'){
1800
- wordfence::status(1, 'info', "Ajax request received to unblock All IP's including permanent blocks.");
1801
  $wfLog->unblockAllIPs();
1802
  } else if($op == 'locked'){
1803
  $wfLog->unlockAllIPs();
@@ -1897,7 +1918,7 @@ class wordfence {
1897
  if(self::getLog()->isWhitelisted($IP)){
1898
  return array('err' => 1, 'errorMsg' => "The IP address " . wp_kses($IP, array()) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page.");
1899
  }
1900
- if(wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers'){ //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google
1901
  if(wfCrawl::verifyCrawlerPTR('/\.googlebot\.com$/i', $IP)){
1902
  return array('err' => 1, 'errorMsg' => "The IP address you're trying to block belongs to Google. Your options are currently set to not block these crawlers. Change this in Wordfence options if you want to manually block Google.");
1903
  }
@@ -1923,7 +1944,7 @@ class wordfence {
1923
  $op = $_POST['op'];
1924
  $i = new wfIssues();
1925
  if($op == 'deleteIgnored'){
1926
- $i->deleteIgnored();
1927
  } else if($op == 'deleteNew'){
1928
  $i->deleteNew();
1929
  } else if($op == 'ignoreAllNew'){
@@ -2021,7 +2042,7 @@ class wordfence {
2021
  $errors = array();
2022
  $issues = new wfIssues();
2023
  foreach($ids as $id){
2024
- $id = intval($id); //Make sure input is a number.
2025
  $issue = $issues->getIssueByID($id);
2026
  if(! $issue){
2027
  $errors[] = "Could not delete one of the files because we could not find the issue. Perhaps it's been resolved?";
@@ -2043,7 +2064,7 @@ class wordfence {
2043
  $errors[] = "Could not delete file " . wp_kses($file, array()) . ". Error was: " . wp_kses($err['message'], array());
2044
  }
2045
  } else if($op == 'repair'){
2046
- $dat = $issue['data'];
2047
  $result = self::getWPFileContent($dat['file'], $dat['cType'], $dat['cName'], $dat['cVersion']);
2048
  if($result['cerrorMsg']){
2049
  $errors[] = $result['cerrorMsg'];
@@ -2052,7 +2073,7 @@ class wordfence {
2052
  $errors[] = "We could not get the original file of " . wp_kses($file, array()) . " to do a repair.";
2053
  continue;
2054
  }
2055
-
2056
  if(preg_match('/\.\./', $file)){
2057
  $errors[] = "An invalid file " . wp_kses($file, array()) . " was specified for repair.";
2058
  continue;
@@ -2136,7 +2157,7 @@ class wordfence {
2136
  if(! $issue){
2137
  return array('cerrorMsg' => "We could not find that issue in our database.");
2138
  }
2139
- $dat = $issue['data'];
2140
  $result = self::getWPFileContent($dat['file'], $dat['cType'], (isset($dat['cName']) ? $dat['cName'] : ''), (isset($dat['cVersion']) ? $dat['cVersion'] : ''));
2141
  $file = $dat['file'];
2142
  if(isset($result['cerrorMsg']) && $result['cerrorMsg']){
@@ -2144,7 +2165,7 @@ class wordfence {
2144
  } else if(! $result['fileContent']){
2145
  return array('cerrorMsg' => "We could not get the original file to do a repair.");
2146
  }
2147
-
2148
  if(preg_match('/\.\./', $file)){
2149
  return array('cerrorMsg' => "An invalid file was specified for repair.");
2150
  }
@@ -2202,7 +2223,7 @@ class wordfence {
2202
  throw new Exception("Invalid response: " . var_export($res, true));
2203
  }
2204
  } catch(Exception $e){
2205
- return array('err' => "An error occurred: " . $e->getMessage());
2206
  }
2207
  }
2208
  public static function importSettings($token){
@@ -2229,7 +2250,205 @@ class wordfence {
2229
  } else {
2230
  throw new Exception("Invalid response from Wordfence servers during import.");
2231
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2232
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2233
  public static function ajax_importSettings_callback(){
2234
  $token = $_POST['token'];
2235
  try {
@@ -2246,8 +2465,8 @@ class wordfence {
2246
  wfScanEngine::startScan();
2247
  }
2248
  public static function templateRedir(){
2249
- $wfFunc = get_query_var('_wfsf');
2250
-
2251
  //Logging
2252
  self::doEarlyAccessLogging();
2253
  //End logging
@@ -2287,7 +2506,7 @@ class wordfence {
2287
  self::wfFunc_testmem();
2288
  } else if($wfFunc == 'testtime'){
2289
  self::wfFunc_testtime();
2290
- }
2291
  exit(0);
2292
  }
2293
  public static function memtest_error_handler($errno, $errstr, $errfile, $errline){
@@ -2365,7 +2584,7 @@ wfscr.type = 'text/javascript';
2365
  wfscr.async = true;
2366
  wfscr.src = url;
2367
  (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(wfscr);
2368
- })('$scriptURL');
2369
  </script>
2370
  EOL;
2371
  }
@@ -2382,7 +2601,7 @@ wfscr.type = 'text/javascript';
2382
  wfscr.async = true;
2383
  wfscr.src = url + '&r=' + Math.random();
2384
  (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(wfscr);
2385
- })('$URL');
2386
  </script>
2387
  EOL;
2388
  }
@@ -2401,7 +2620,7 @@ EOL;
2401
  $reverseLookup = wfUtils::reverseLookup($IP);
2402
  $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2403
  $results = array_merge(
2404
- $wfLog->getHits('hits', 'hit', 0, 10000, $IP),
2405
  $wfLog->getHits('hits', '404', 0, 10000, $IP)
2406
  );
2407
  usort($results, 'wordfence::iptrafsort');
@@ -2443,7 +2662,7 @@ EOL;
2443
  $fileMTime = @filemtime($localFile);
2444
  $fileMTime = date('l jS \of F Y h:i:s A', $fileMTime);
2445
  try {
2446
- if(wfUtils::fileOver2Gigs($localFile)){
2447
  $fileSize = "Greater than 2 Gigs";
2448
  } else {
2449
  $fileSize = @filesize($localFile); //Checked if over 2 gigs above
@@ -2476,8 +2695,8 @@ EOL;
2476
  } else {
2477
  $diff = new Diff(
2478
  //Treat DOS and Unix files the same
2479
- preg_split("/(?:\r\n|\n)/", $result['fileContent']),
2480
- preg_split("/(?:\r\n|\n)/", $localContents),
2481
  array()
2482
  );
2483
  $renderer = new Diff_Renderer_Html_SideBySide;
@@ -2507,7 +2726,7 @@ EOL;
2507
  }
2508
  public static function admin_init(){
2509
  if(! wfUtils::isAdmin()){ return; }
2510
- foreach(array('activate', 'scan', 'updateAlertEmail', 'sendActivityLog', 'restoreFile', 'exportSettings', 'importSettings', 'bulkOperation', 'deleteFile', 'removeExclusion', 'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues', 'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP', 'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'downloadHtaccess', 'checkFalconHtaccess', 'updateConfig', 'saveCacheConfig', 'removeFromCache', 'autoUpdateChoice', 'saveCacheOptions', 'clearPageCache', 'getCacheStats', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed', 'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel', 'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion', 'loadCacheExclusions') as $func){
2511
  add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
2512
  }
2513
 
@@ -2612,13 +2831,15 @@ EOL;
2612
  }
2613
  }
2614
  }
2615
-
2616
  add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan');
2617
- add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', wfUtils::getBaseURL() . 'images/wordfence-logo-16x16.png');
2618
  add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
2619
  /* add_submenu_page('Wordfence', 'Site Performance', 'Site Performance', 'activate_plugins', 'WordfenceSitePerfStats', 'wordfence::menu_sitePerfStats'); */
2620
  add_submenu_page('Wordfence', 'Performance Setup', 'Performance Setup', 'activate_plugins', 'WordfenceSitePerf', 'wordfence::menu_sitePerf');
2621
  add_submenu_page('Wordfence', 'Blocked IPs', 'Blocked IPs', 'activate_plugins', 'WordfenceBlockedIPs', 'wordfence::menu_blockedIPs');
 
 
2622
  add_submenu_page("Wordfence", "Cellphone Sign-in", "Cellphone Sign-in", "activate_plugins", "WordfenceTwoFactor", 'wordfence::menu_twoFactor');
2623
  add_submenu_page("Wordfence", "Country Blocking", "Country Blocking", "activate_plugins", "WordfenceCountryBlocking", 'wordfence::menu_countryBlocking');
2624
  add_submenu_page("Wordfence", "Scan Schedule", "Scan Schedule", "activate_plugins", "WordfenceScanSchedule", 'wordfence::menu_scanSchedule');
@@ -2638,6 +2859,10 @@ EOL;
2638
  public static function menu_blockedIPs(){
2639
  require 'menu_blockedIPs.php';
2640
  }
 
 
 
 
2641
  public static function menu_scanSchedule(){
2642
  require 'menu_scanSchedule.php';
2643
  }
@@ -2684,7 +2909,7 @@ EOL;
2684
  if(wfConfig::get('other_pwStrengthOnUpdate')){
2685
  $oldDat = get_userdata($userID);
2686
  if($newDat->user_pass != $oldDat->user_pass){
2687
- $wf = new wfScanEngine();
2688
  $wf->scanUserPassword($userID);
2689
  $wf->emailNewIssues();
2690
  }
@@ -2709,16 +2934,16 @@ EOL;
2709
  if( $approved == 1 && (! is_user_logged_in()) && wfConfig::get('other_noAnonMemberComments') ){
2710
  $user = get_user_by('email', trim($cData['comment_author_email']));
2711
  if($user){
2712
- wfConfig::inc('totalSpamStopped');
2713
  return 0; //hold for moderation if the user is not signed in but used a members email
2714
  }
2715
  }
2716
-
2717
  if(($approved == 1 || $approved == 0) && wfConfig::get('other_scanComments')){
2718
  $wf = new wfScanEngine();
2719
  try {
2720
  if($wf->isBadComment($cData['comment_author'], $cData['comment_author_email'], $cData['comment_author_url'], $cData['comment_author_IP'], $cData['comment_content'])){
2721
- wfConfig::inc('totalSpamStopped');
2722
  return 'spam';
2723
  }
2724
  } catch(Exception $e){
@@ -2733,7 +2958,7 @@ EOL;
2733
  preg_replace_callback('/https?:\/\/([a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+[a-zA-Z0-9])/i', 'wordfence::pushCommentSpamHost', $cData['comment_content']);
2734
  $hosts = self::$commentSpamItems;
2735
  self::$commentSpamItems = array();
2736
- try {
2737
  $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2738
  $res = $api->call('advanced_comment_scan', array(), array(
2739
  'author' => $cData['comment_author'],
@@ -2745,14 +2970,14 @@ EOL;
2745
  'IPs' => (sizeof($IPs) > 0 ? implode(',', $IPs) : '')
2746
  ));
2747
  if(is_array($res) && isset($res['spam']) && $res['spam'] == 1){
2748
- wfConfig::inc('totalSpamStopped');
2749
  return 'spam';
2750
  }
2751
  } catch(Exception $e){
2752
  //API server is probably down
2753
  }
2754
  }
2755
- wfConfig::inc('totalCommentsFiltered');
2756
  return $approved;
2757
  }
2758
  public static function getMyHomeURL(){
@@ -2763,7 +2988,7 @@ EOL;
2763
  }
2764
 
2765
  public static function alert($subject, $alertMsg, $IP){
2766
- wfConfig::inc('totalAlertsSent');
2767
  $emails = wfConfig::getAlertEmails();
2768
  if(sizeof($emails) < 1){ return; }
2769
 
@@ -2782,7 +3007,7 @@ EOL;
2782
  }
2783
  $IPMsg .= $userLoc['countryName'] . "\n";
2784
  }
2785
- }
2786
  $content = wfUtils::tmpl('email_genericAlert.php', array(
2787
  'isPaid' => wfConfig::get('isPaid'),
2788
  'subject' => $subject,
@@ -2825,7 +3050,7 @@ EOL;
2825
  }
2826
  }
2827
  }
2828
- wfConfig::set('lastEmailHash', time() . ':' . $hash);
2829
  wp_mail(implode(',', $emails), $subject, $content);
2830
  }
2831
  public static function getLog(){
@@ -2897,7 +3122,7 @@ EOL;
2897
  return self::$debugOn;
2898
  }
2899
  //PUBLIC API
2900
- public static function doNotCache(){ //Call this to prevent Wordfence from caching the current page.
2901
  wfCache::doNotCache();
2902
  return true;
2903
  }
@@ -2919,5 +3144,34 @@ EOL;
2919
  wfConfig::set('whitelisted', implode(',', $arr2));
2920
  return true;
2921
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2922
  }
2923
  ?>
12
  require_once('wfConfig.php');
13
  require_once('wfSchema.php');
14
  require_once('wfCache.php');
15
+ require_once('wfCrypt.php');
16
+ require_once 'wfView.php';
17
+ require_once 'wfHelperString.php';
18
+ require_once 'wfDirectoryIterator.php';
19
+ require_once 'wfUpdateCheck.php';
20
+ require_once 'wfActivityReport.php';
21
+
22
  class wordfence {
23
  public static $printStatus = false;
24
  public static $wordfence_wp_version = false;
41
  update_option('wordfenceActivated', 1);
42
  }
43
  public static function uninstallPlugin(){
44
+ //Check if caching is enabled and if it is, disable it and fix the .htaccess file.
45
  $cacheType = wfConfig::get('cacheType', false);
46
  if($cacheType == 'falcon'){
47
  wfCache::addHtaccessCode('remove');
48
  wfCache::updateBlockedIPs('remove');
49
  wfConfig::set('cacheType', false);
50
+
51
+ //We currently don't clear the cache when plugin is disabled because it will take too long if done synchronously and won't work because plugin is disabled if done asynchronously.
52
  //wfCache::scheduleCacheClear();
53
  } else if($cacheType == 'php'){
54
  wfConfig::set('cacheType', false);
55
  }
56
+
57
 
58
  //Used by MU code below
59
  update_option('wordfenceActivated', 0);
60
  wp_clear_scheduled_hook('wordfence_daily_cron');
61
  wp_clear_scheduled_hook('wordfence_hourly_cron');
62
  wp_clear_scheduled_hook('wordfence_daily_autoUpdate');
63
+
64
  //Remove old legacy cron job if it exists
65
  wp_clear_scheduled_hook('wordfence_scheduled_scan');
66
+
67
  //Remove all scheduled scans.
68
  self::unscheduleAllScans();
69
+
70
  wfConfig::clearDiskCache();
71
  if(wfConfig::get('deleteTablesOnDeact')){
72
  $schema = new wfSchema();
120
  for($i = 0; $i < $len; $i += 4){
121
  list($ipLong) = array_values(unpack('N', substr($resp['data'], $i, 4)));
122
  $IPStr = long2ip($ipLong);
123
+ if(! self::getLog()->isWhitelisted($IPStr)){
124
  self::getLog()->blockIP($IPStr, $reason, true);
125
  }
126
  }
180
  wordfence::status(4, 'error', "Could not fetch vulnerability patterns in scheduled job: " . $e->getMessage());
181
  }
182
 
183
+ $wfdb->queryWrite("delete from $p"."wfLocs where ctime < unix_timestamp() - %d", WORDFENCE_MAX_IPLOC_AGE);
184
  $wfdb->truncate($p . "wfBadLeechers"); //only uses date that's less than 1 minute old
185
  $wfdb->queryWrite("delete from $p"."wfBlocks where (blockedTime + %s < unix_timestamp()) and permanent=0", wfConfig::get('blockedTime'));
186
  $wfdb->queryWrite("delete from $p"."wfCrawlers where lastUpdate < unix_timestamp() - (86400 * 7)");
227
  $count4 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus");
228
  if($count4 > 100000){
229
  $wfdb->truncate($p . "wfStatus");
230
+ } else if($count4 > 1000){ //max status events we keep. This determines how much gets emailed to us when users sends us a debug report.
231
  $wfdb->queryWrite("delete from $p"."wfStatus where level != 10 order by ctime asc limit %d", ($count4 - 1000));
232
  $count5 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus where level=10");
233
  if($count5 > 100){
235
  }
236
  }
237
 
238
+ $report = new wfActivityReport();
239
+ $report->rotateIPLog();
240
  }
241
  public static function runInstall(){
242
  if(self::$runInstallCalled){ return; }
311
  $db->queryWriteIgnoreError("alter table $prefix"."wfLockedOut modify column blockedTime bigint signed NOT NULL");
312
  $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileQueue");
313
  $db->queryWriteIgnoreError("drop table if exists $prefix"."wfFileChanges");
314
+ //Adding primary key to this table because some backup apps use primary key during backup.
315
  $db->queryWriteIgnoreError("alter table wp_wfStatus add id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY");
316
 
317
  $optScanEnabled = $db->querySingle("select val from $prefix"."wfConfig where name='scansEnabled_options'");
318
  if($optScanEnabled != '0' && $optScanEnabled != '1'){
319
  $db->queryWrite("update $prefix"."wfConfig set val='1' where name='scansEnabled_options'");
320
  }
321
+
322
  $optScanEnabled = $db->querySingle("select val from $prefix"."wfConfig where name='scansEnabled_heartbleed'");
323
+ if($optScanEnabled != '0' && $optScanEnabled != '1'){ //Enable heartbleed if no value is set.
324
  wfConfig::set('scansEnabled_heartbleed', 1);
325
  }
326
 
334
  } else {
335
  $wfLog->logLeechAndBlock('hit');
336
  }
337
+ if(wfConfig::liveTrafficEnabled()){
338
  self::$hitID = $wfLog->logHit();
339
  add_action('wp_head', 'wordfence::wfLogHumanHeader');
340
  }
385
  add_action('wp_ajax_wordfence_logHuman', 'wordfence::ajax_logHuman_callback');
386
  add_action('wp_ajax_wordfence_doScan', 'wordfence::ajax_doScan_callback');
387
  add_action('wp_ajax_wordfence_testAjax', 'wordfence::ajax_testAjax_callback');
 
 
 
388
 
389
+ add_action('wp_dashboard_setup', 'wordfence::addDashboardWidget');
390
  }
391
 
392
 
398
  add_action('init', 'wordfence::initAction');
399
  add_action('template_redirect', 'wordfence::templateRedir');
400
  add_action('shutdown', 'wordfence::shutdownAction');
401
+
402
  if(version_compare(PHP_VERSION, '5.4.0') >= 0){
403
  add_action('wp_authenticate','wordfence::authActionNew', 1, 2);
404
  } else {
418
  add_action('publish_future_post', 'wordfence::publishFuturePost');
419
  add_action('mobile_setup', 'wordfence::jetpackMobileSetup'); //Action called in Jetpack Mobile Theme: modules/minileven/minileven.php
420
 
421
+ // Add actions for the email summary
422
+ add_action('wordfence_email_activity_report', array('wfActivityReport', 'executeCronJob'));
423
+
424
  //For debugging
425
  //add_filter( 'cron_schedules', 'wordfence::cronAddSchedules' );
426
+
427
  add_filter('wp_redirect', 'wordfence::wpRedirectFilter', 99, 2);
428
  add_filter('pre_comment_approved', 'wordfence::preCommentApprovedFilter', '99', 2);
429
  //html|xhtml|atom|rss2|rdf|comment|export
470
  }
471
  */
472
  public static function jetpackMobileSetup(){
473
+ define('WFDONOTCACHE', true); //Don't cache jetpack mobile theme pages.
474
  }
475
  public static function wpRedirectFilter($URL, $status){
476
  if(isset($_GET['author']) && preg_match('/\/author\/.+/i', $URL) && wfConfig::get('loginSec_disableAuthorScan') ){ //author query variable is present and we're about to redirect to a URL that starts with http://blah/author/...
501
  }
502
  $UA = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
503
  $URL = $_POST['URL'];
504
+ $wfLog->logPerf(wfUtils::getIP(), $UA, $URL, $data);
505
+ die(json_encode(array('ok' => 1)));
506
  }
507
  public static function ajax_logHuman_callback(){
508
  $browscap = new wfBrowscap();
516
  }
517
 
518
  @ob_end_clean();
519
+ if(! headers_sent()){
520
  header('Content-type: text/javascript');
521
  header("Connection: close");
522
  header("Content-Length: 0");
538
  }
539
  $func = (isset($_POST['action']) && $_POST['action']) ? $_POST['action'] : $_GET['action'];
540
  $nonce = (isset($_POST['nonce']) && $_POST['nonce']) ? $_POST['nonce'] : $_GET['nonce'];
541
+ if(! wp_verify_nonce($nonce, 'wp-ajax')){
542
  die(json_encode(array('errorMsg' => "Your browser sent an invalid security token to Wordfence. Please try reloading this page or signing out and in again.")));
543
  }
544
  //func is e.g. wordfence_ticker so need to munge it
547
  if($returnArr === false){
548
  $returnArr = array('errorMsg' => "Wordfence encountered an internal error executing that request.");
549
  }
550
+
551
  if(! is_array($returnArr)){
552
  error_log("Function " . wp_kses($func, array()) . " did not return an array and did not generate an error.");
553
  $returnArr = array();
594
  return $errors;
595
  }
596
  public static function isStrongPasswd($passwd, $username ) {
597
+ $strength = 0;
598
  if(strlen( trim( $passwd ) ) < 5)
599
  return false;
600
  if(strtolower( $passwd ) == strtolower( $username ) )
611
  $strength += 26;
612
  if ($num = preg_match_all( "/[^a-zA-Z0-9]/", $passwd, $matches)){
613
  $strength += (31 * (int)$num);
614
+
615
  }
616
  if($strength > 60){
617
  return true;
757
  public static function loginAction($username){
758
  if(sizeof($_POST) < 1){ return; } //only execute if login form is posted
759
  if(! $username){ return; }
760
+ wfConfig::inc('totalLogins');
761
  $user = get_user_by('login', $username);
762
  $userID = $user ? $user->ID : 0;
763
  self::getLog()->logLogin('loginOK', 0, $username);
773
  }
774
 
775
  if(user_can($userID, 'update_core')){
776
+ if(wfConfig::get('alertOn_adminLogin')){
777
  wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.", wfUtils::getIP());
778
  }
779
  } else {
790
  }
791
  public static function authenticateFilter($authUser, $username, $passwd){
792
  wfConfig::inc('totalLoginHits'); //The total hits to wp-login.php including logins, logouts and just hits.
793
+ $IP = wfUtils::getIP();
794
  $secEnabled = wfConfig::get('loginSecurityEnabled');
795
  if($secEnabled && (! self::getLog()->isWhitelisted($IP)) && wfConfig::get('isPaid') ){
796
  $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
797
  if(isset($twoFactorUsers) && is_array($twoFactorUsers) && sizeof($twoFactorUsers) > 0){
798
  $userDat = (isset($_POST['wordfence_userDat']) ? $_POST['wordfence_userDat'] : false);
799
+ if(is_object($userDat) && get_class($authUser) == 'WP_User'){ //Valid username and password either with or without the 'wf...' code. Users is now logged in at this point.
800
  if(isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor']){ //user entered a valid user and password with ' wf....' appended
801
  foreach($twoFactorUsers as &$t){
802
  if($t[0] == $userDat->ID && $t[3] == 'activated'){
803
  if($_POST['wordfence_authFactor'] == $t[2] && $t[4] > time()){
804
+ //Do nothing and allow user to sign in. Their passwd has already been modified to be the passwd without the code.
805
  } else if($_POST['wordfence_authFactor'] == $t[2]){
806
  $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
807
  $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $t[1]) );
816
  } else {
817
  break; //No new code was received. Let them sign in with the expired code.
818
  }
819
+ } else { //Bad code, so cancel the login and return an error to user.
820
  return new WP_Error( 'twofactor_required', __( '<strong>INVALID CODE</strong>: You need to enter your password followed by a space and the code we sent to your phone. The code should start with \'wf\' and should be four characters. e.g. wfAB12. In this case you would enter your password as: \'mypassword wfAB12\' without quotes.'));
821
  }
822
  } //No user matches and has TF activated so let user sign in.
835
  $t[4] = time() + 1800; //30 minutes until code expires
836
  wfConfig::set_ser('twoFactorUsers', $twoFactorUsers); //save the code the user needs to enter and return an error.
837
  return new WP_Error( 'twofactor_required', __( '<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space and the code to the end of your password.' ) );
838
+ } else { //oops, our API returned an error.
839
  break; //Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems.
840
  }
841
  } //User is not present in two factor list or is not activated. Sign in without twofactor.
852
  if($maxBlockTime = self::wfsnIsBlocked($IP, 'brute')){
853
  self::getLog()->blockIP($IP, "Blocked by Wordfence Security Network", true, false, $maxBlockTime);
854
  }
855
+
856
  }
857
  if($secEnabled){
858
  if(is_wp_error($authUser) && $authUser->get_error_code() == 'invalid_username'){
893
  }
894
  if(is_wp_error($authUser)){
895
  if($authUser->get_error_code() == 'invalid_username'){
896
+ self::getLog()->logLogin('loginFailInvalidUsername', 1, $username);
897
  } else {
898
+ self::getLog()->logLogin('loginFailValidUsername', 1, $username);
899
  }
900
  }
901
 
958
  $userID = get_current_user_id();
959
  $userDat = get_user_by('id', $userID);
960
  if(is_object($userDat)){
961
+ self::getLog()->logLogin('logout', 0, $userDat->user_login);
962
  }
963
  }
964
  public static function loginInitAction(){
970
  if(self::isLockedOut(wfUtils::getIP())){
971
  require('wfLockedOut.php');
972
  }
973
+ if(! $username){ return; }
974
  $userDat = get_user_by('login', $username);
975
  $_POST['wordfence_userDat'] = $userDat;
976
+ if(preg_match(self::$passwordCodePattern, $passwd, $matches)){
977
  $_POST['wordfence_authFactor'] = $matches[1];
978
  $passwd = preg_replace('/^(.+)\s+(wf[a-z0-9]+)$/i', '$1', $passwd);
979
  $_POST['pwd'] = $passwd;
983
  if(self::isLockedOut(wfUtils::getIP())){
984
  require('wfLockedOut.php');
985
  }
986
+ if(! $username){ return; }
987
  $userDat = get_user_by('login', $username);
988
  $_POST['wordfence_userDat'] = $userDat;
989
+ if(preg_match(self::$passwordCodePattern, $passwd, $matches)){
990
  $_POST['wordfence_authFactor'] = $matches[1];
991
  $passwd = preg_replace('/^(.+)\s+(wf[a-z0-9]+)$/i', '$1', $passwd);
992
  $_POST['pwd'] = $passwd;
1178
  $nextTime = self::getNextScanStartTime();
1179
  return array(
1180
  'ok' => 1,
1181
+ 'nextStart' => ($nextTime ? $nextTime : '')
1182
  );
1183
  }
1184
  public static function getNextScanStartTime(){
1185
  $nextTime = false;
1186
+ $cron = _get_cron_array();
1187
  foreach($cron as $key => $val){
1188
  if(isset($val['wordfence_start_scheduled_scan'])){
1189
  $nextTime = $key;
1193
  return ($nextTime ? date('l jS \of F Y H:i:s A', $nextTime + (3600 * get_option('gmt_offset'))) : '');
1194
  }
1195
  public static function wordfenceStartScheduledScan(){
1196
+
1197
  //If scheduled scans are not enabled in the global config option, then don't run a scheduled scan.
1198
  if(wfConfig::get('scheduledScansEnabled') != '1'){
1199
  return;
1208
  }
1209
  wfConfig::set('lastScheduledScanStart', time());
1210
  wordfence::status(1, 'info', "Scheduled Wordfence scan starting at " . date('l jS \of F Y h:i:s A', current_time('timestamp')) );
1211
+
1212
  //We call this before the scan actually starts to advance the schedule for the next week.
1213
  //This ensures that if the scan crashes for some reason, the schedule will hold.
1214
  wordfence::scheduleScans();
1220
  $sched = wfConfig::get_ser('scanSched', array());
1221
  $mode = wfConfig::get('schedMode');
1222
  if($mode == 'manual' && is_array($sched) && is_array($sched[0]) ){
1223
+ //Use sched as it is
1224
  } else { //Default to setting scans to run once a day at a randomly selected time.
1225
  $sched = array();
1226
  $runAt = rand(0,23);
1293
  $content .= date(DATE_RFC822, $r['ctime'] + $timeOffset) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . wp_kses_data( (string) $r['msg']) . "\n";
1294
  }
1295
  $content .= "\n\n";
1296
+
1297
  ob_start();
1298
  phpinfo();
1299
  $phpinfo = ob_get_contents();
1300
  ob_get_clean();
1301
 
1302
  $content .= $phpinfo;
1303
+
1304
+ wp_mail($_POST['email'], "Wordfence Activity Log", $content);
1305
  return array('ok' => 1);
1306
  }
1307
  public static function ajax_startTourAgain_callback(){
1315
  if($keyData['ok'] && $keyData['apiKey']){
1316
  wfConfig::set('apiKey', $keyData['apiKey']);
1317
  wfConfig::set('isPaid', 0);
1318
+ //When downgrading we must disable all two factor authentication because it can lock an admin out if we don't.
1319
  wfConfig::set_ser('twoFactorUsers', array());
1320
  } else {
1321
  throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key.");
1358
  public static function disablePermalinksFilter($newVal, $oldVal){
1359
  if(wfConfig::get('cacheType', false) == 'falcon' && $oldVal && (! $newVal) ){ //Falcon is enabled and admin is disabling permalinks
1360
  wfCache::addHtaccessCode('remove');
1361
+ //if($err){ return $oldVal; } //We might want to not allow the user to disable permalinks if we can't disable falcon. Allowing it for now.
1362
  wfCache::updateBlockedIPs('remove');
1363
+ //if($err){ return $oldVal; } //We might want to not allow the user to disable permalinks if we can't disable falcon. Allowing it for now.
1364
  wfConfig::set('cacheType', false);
1365
  }
1366
  return $newVal;
1457
  return array('ok' => 1, 'heading' => "Could not write to cache directory", 'body' => "To enable caching, Wordfence needs to be able to create and write to the /wp-content/wfcache/ directory. We did some tests that indicate this is not possible. You need to manually create the /wp-content/wfcache/ directory and make it writable by Wordfence. The error we encountered was during our tests was: $err");
1458
  }
1459
  }
1460
+
1461
+ //Mainly we clear the cache here so that any footer cache diagnostic comments are rebuilt. We could just leave it intact unless caching is being disabled.
1462
  if($cacheType != wfConfig::get('cacheType', false)){
1463
  wfCache::scheduleCacheClear();
1464
  }
1465
+ $htMsg = "";
1466
  if($warnHtaccess){
1467
  $htMsg = " <strong style='color: #F00;'>Warning: We could not remove the caching code from your .htaccess file. you need to remove this manually yourself.</strong> ";
1468
  }
1490
  if($s['files'] == 0){
1491
  return array('ok' => 1, 'heading' => 'Cache Stats', 'body' => "The cache is currently empty. It may be disabled or it may have been recently cleared.");
1492
  }
1493
+ $body = 'Total files in cache: ' . $s['files'] .
1494
+ '<br />Total directories in cache: ' . $s['dirs'] .
1495
  '<br />Total data: ' . $s['data'] . 'KB';
1496
  if($s['compressedFiles'] > 0){
1497
+ $body .= '<br />Files: ' . $s['uncompressedFiles'] .
1498
  '<br />Data: ' . $s['uncompressedKBytes'] . 'KB' .
1499
  '<br />Compressed files: ' . $s['compressedFiles'] .
1500
  '<br />Compressed data: ' . $s['compressedKBytes'] . 'KB';
1595
  }
1596
  $ex = unserialize($ex);
1597
  $rewriteHtaccess = false;
1598
+ for($i = 0; $i < sizeof($ex); $i++){
1599
  if((string)$ex[$i]['id'] == (string)$id){
1600
  if(wfConfig::get('cacheType', false) == 'falcon' && preg_match('/^(?:uac|uaeq|cc)$/', $ex[$i]['pt'])){
1601
  $rewriteHtaccess = true;
1718
  if(sizeof($validIPs) > 0){
1719
  $opts['liveTraf_ignoreIPs'] = implode(',', $validIPs);
1720
  }
1721
+
1722
  if(preg_match('/[a-zA-Z0-9\d]+/', $opts['liveTraf_ignoreUA'])){
1723
  $opts['liveTraf_ignoreUA'] = trim($opts['liveTraf_ignoreUA']);
1724
  } else {
1725
  $opts['liveTraf_ignoreUA'] = '';
1726
  }
1727
+ if(! $opts['other_WFNet']){
1728
  $wfdb = new wfDB();
1729
  global $wpdb;
1730
  $p = $wpdb->base_prefix;
1752
  } else if($opts['autoUpdate'] == '0'){
1753
  wfConfig::disableAutoUpdate();
1754
  }
1755
+
1756
  try {
1757
  if ($opts['disableCodeExecutionUploads']) {
1758
  wfConfig::disableCodeExecutionForUploads();
1762
  } catch (wfConfigException $e) {
1763
  return array('errorMsg' => $e->getMessage());
1764
  }
1765
+
1766
+ if (!empty($opts['email_summary_enabled'])) {
1767
+ wfConfig::set('email_summary_enabled', 1);
1768
+ wfConfig::set('email_summary_interval', $opts['email_summary_interval']);
1769
+ wfConfig::set('email_summary_excluded_directories', $opts['email_summary_excluded_directories']);
1770
+ wfActivityReport::scheduleCronJob();
1771
+ } else {
1772
+ wfConfig::set('email_summary_enabled', 0);
1773
+ wfActivityReport::disableCronJob();
1774
+ }
1775
+
1776
 
1777
  $paidKeyMsg = false;
1778
 
1818
  $op = $_POST['op'];
1819
  $wfLog = self::getLog();
1820
  if($op == 'blocked'){
1821
+ wordfence::status(1, 'info', "Ajax request received to unblock All IP's including permanent blocks.");
1822
  $wfLog->unblockAllIPs();
1823
  } else if($op == 'locked'){
1824
  $wfLog->unlockAllIPs();
1918
  if(self::getLog()->isWhitelisted($IP)){
1919
  return array('err' => 1, 'errorMsg' => "The IP address " . wp_kses($IP, array()) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page.");
1920
  }
1921
+ if(wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers'){ //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google
1922
  if(wfCrawl::verifyCrawlerPTR('/\.googlebot\.com$/i', $IP)){
1923
  return array('err' => 1, 'errorMsg' => "The IP address you're trying to block belongs to Google. Your options are currently set to not block these crawlers. Change this in Wordfence options if you want to manually block Google.");
1924
  }
1944
  $op = $_POST['op'];
1945
  $i = new wfIssues();
1946
  if($op == 'deleteIgnored'){
1947
+ $i->deleteIgnored();
1948
  } else if($op == 'deleteNew'){
1949
  $i->deleteNew();
1950
  } else if($op == 'ignoreAllNew'){
2042
  $errors = array();
2043
  $issues = new wfIssues();
2044
  foreach($ids as $id){
2045
+ $id = intval($id); //Make sure input is a number.
2046
  $issue = $issues->getIssueByID($id);
2047
  if(! $issue){
2048
  $errors[] = "Could not delete one of the files because we could not find the issue. Perhaps it's been resolved?";
2064
  $errors[] = "Could not delete file " . wp_kses($file, array()) . ". Error was: " . wp_kses($err['message'], array());
2065
  }
2066
  } else if($op == 'repair'){
2067
+ $dat = $issue['data'];
2068
  $result = self::getWPFileContent($dat['file'], $dat['cType'], $dat['cName'], $dat['cVersion']);
2069
  if($result['cerrorMsg']){
2070
  $errors[] = $result['cerrorMsg'];
2073
  $errors[] = "We could not get the original file of " . wp_kses($file, array()) . " to do a repair.";
2074
  continue;
2075
  }
2076
+
2077
  if(preg_match('/\.\./', $file)){
2078
  $errors[] = "An invalid file " . wp_kses($file, array()) . " was specified for repair.";
2079
  continue;
2157
  if(! $issue){
2158
  return array('cerrorMsg' => "We could not find that issue in our database.");
2159
  }
2160
+ $dat = $issue['data'];
2161
  $result = self::getWPFileContent($dat['file'], $dat['cType'], (isset($dat['cName']) ? $dat['cName'] : ''), (isset($dat['cVersion']) ? $dat['cVersion'] : ''));
2162
  $file = $dat['file'];
2163
  if(isset($result['cerrorMsg']) && $result['cerrorMsg']){
2165
  } else if(! $result['fileContent']){
2166
  return array('cerrorMsg' => "We could not get the original file to do a repair.");
2167
  }
2168
+
2169
  if(preg_match('/\.\./', $file)){
2170
  return array('cerrorMsg' => "An invalid file was specified for repair.");
2171
  }
2223
  throw new Exception("Invalid response: " . var_export($res, true));
2224
  }
2225
  } catch(Exception $e){
2226
+ return array('err' => "An error occurred: " . $e->getMessage());
2227
  }
2228
  }
2229
  public static function importSettings($token){
2250
  } else {
2251
  throw new Exception("Invalid response from Wordfence servers during import.");
2252
  }
2253
+ }
2254
+ public static function ajax_startPasswdAudit_callback(){
2255
+ if(! wfAPI::SSLEnabled()){
2256
+ return array('errorMsg' => "Your server does not support SSL via cURL. To use this feature you need to make sure you have the PHP cURL library installed and enabled and have openSSL enabled so that you can communicate securely with our servers. This ensures that your password hashes remain secure and are double-encrypted when this feature is used. To fix this, please contact your hosting provider or site admin and ask him or her to install and enable cURL and openssl.");
2257
+ }
2258
+ if(! function_exists('openssl_public_encrypt')){
2259
+ return array('errorMsg' => "Your server does not have openssl installed. Specifically we require the openssl_public_encrypt() function to use this feature. Please ask your site admin or hosting provider to install 'openssl' and the openssl PHP libraries. We use these for public key encryption to securely send your password hashes to our server for auditing.");
2260
+ }
2261
+ global $wpdb; $p = $wpdb->base_prefix;
2262
+ $email = $_POST['emailAddr'];
2263
+ if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
2264
+ return array(
2265
+ 'errorMsg' => "Please enter a valid email address.",
2266
+ );
2267
+ }
2268
+ $auditType = $_POST['auditType'];
2269
+ $symKey = wfCrypt::makeSymHexKey(32); #hex digits which is 128 bits
2270
+ $wfdb = new wfDB();
2271
+ $q1 = $wfdb->querySelect("select ID, AES_ENCRYPT(user_pass, '%s') as crypt_pass from $p"."users", $symKey);
2272
+ $admins = "";
2273
+ $users = "";
2274
+ foreach($q1 as $rec) {
2275
+ $isAdmin = user_can($rec['ID'], 'manage_options');
2276
+ if($isAdmin && ($auditType == 'admin' || $auditType == 'both') ) {
2277
+ $admins .= $rec['ID'] . ':' . base64_encode($rec['crypt_pass']) . '|';
2278
+ } else if($auditType == 'user' || $auditType == 'both') {
2279
+ $users .= $rec['ID'] . ':' . base64_encode($rec['crypt_pass']) . '|';
2280
+ }
2281
+
2282
+ }
2283
+ $admins = rtrim($admins,'|');
2284
+ $users = rtrim($users,'|');
2285
+ //error_log($admins);
2286
+ //error_log($users);
2287
+
2288
+ $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2289
+ try {
2290
+ $res = $api->call('password_audit', array(), array(
2291
+ 'auditType' => $auditType,
2292
+ 'email' => $email,
2293
+ 'pubCryptSymKey' => wfCrypt::pubCrypt($symKey),
2294
+ 'users' => $users,
2295
+ 'admins' => $admins
2296
+ ), true); //Force SSL
2297
+ if(is_array($res)){
2298
+ if(isset($res['ok']) && $res['ok'] == '1'){
2299
+ return array(
2300
+ 'ok' => 1
2301
+ );
2302
+ } else if(isset($res['notPaid']) && $res['notPaid'] == '1'){
2303
+ return array(
2304
+ 'errorMsg' => "You are not using a Premium version of Wordfence. This feature is available to Premium Wordfence members only.",
2305
+ );
2306
+ } else if(isset($res['tooManyJobs']) && $res['tooManyJobs'] == '1'){
2307
+ return array(
2308
+ 'errorMsg' => "You already have a password audit running. Please wait until it finishes before starting another.",
2309
+ );
2310
+ } else {
2311
+ throw new Exception("An unrecognized response was received from the Wordfence servers.");
2312
+ }
2313
+ } else {
2314
+ return array(
2315
+ 'errorMsg' => "We received an invalid response when trying to submit your password audit.",
2316
+ );
2317
+ }
2318
+ } catch(Exception $e){
2319
+ return array(
2320
+ 'errMsg' => "We could not submit your password audit: " . $e,
2321
+ );
2322
+ }
2323
+ }
2324
+ public static function ajax_weakPasswordsFix_callback(){
2325
+ $mode = $_POST['mode'];
2326
+ $ids = explode(',', $_POST['ids']);
2327
+ $homeURL = home_url();
2328
+ $count = 0;
2329
+ if($mode == 'fix'){
2330
+ foreach($ids as $userID){
2331
+ $user = get_user_by('id', $userID);
2332
+ if($user){
2333
+ $passwd = wp_generate_password();
2334
+ $count++;
2335
+ wp_set_password($passwd, $userID);
2336
+ wp_mail($user->user_email, "Your Password on $homeURL Has Been Changed.", wfUtils::tmpl('email_passwdChanged.php', array(
2337
+ 'siteURL' => site_url(),
2338
+ 'homeURL' => home_url(),
2339
+ 'loginURL' => wp_login_url(),
2340
+ 'username' => $user->user_login,
2341
+ 'passwd' => $passwd,
2342
+ )));
2343
+ }
2344
+ }
2345
+ return array(
2346
+ 'ok' => 1,
2347
+ 'title' => "Fixed $count Weak Passwords",
2348
+ 'msg' => "We created new passwords for $count site members and emailed them the new password with instructions."
2349
+ );
2350
+
2351
+ } else if($mode == 'email'){
2352
+ foreach($ids as $userID){
2353
+ $user = get_user_by('id', $userID);
2354
+ if($user){
2355
+ $count++;
2356
+ wp_mail($user->user_email, "Please Change Your Password on $homeURL", wfUtils::tmpl('email_pleaseChangePasswd.php', array(
2357
+ 'siteURL' => site_url(),
2358
+ 'homeURL' => home_url(),
2359
+ 'username' => $user->user_login,
2360
+ 'loginURL' => wp_login_url()
2361
+ )));
2362
+ }
2363
+ }
2364
+ return array(
2365
+ 'ok' => 1,
2366
+ 'title' => "Notified $count Users",
2367
+ 'msg' => "We sent an email to $count site members letting them know that they have a weak password and suggesting that they sign in and change their password to a stronger one."
2368
+ );
2369
+ }
2370
+ }
2371
+ public static function ajax_passwdLoadResults_callback(){
2372
+ if(! wfAPI::SSLEnabled()){ return array('ok' => 1); } //If user hits start passwd audit they will get a helpful message. We don't want an error popping up for every ajax call if SSL is not supported.
2373
+ $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2374
+ try {
2375
+ $res = $api->call('password_load_results', array(), array(), true);
2376
+ } catch(Exception $e){
2377
+ return array('errorMsg' => "Could not load password audit results: " . $e);
2378
+ }
2379
+ $finalResults = array();
2380
+ if(is_array($res) && $res['ok']){
2381
+ if(is_array($res['results'])){
2382
+ for($i = 0; $i < sizeof($res['results']); $i++){
2383
+ //$meta = get_user_meta($res['results'][$i]['userID'], 'wp_capabilities', true);
2384
+ //$res['results'][$i]['isAdmin'] = (isset($meta['administrator']) && $meta['administrator']) ? '1' : '';
2385
+ $user = new WP_User($res['results'][$i]['wpUserID']);
2386
+ if(is_object($user)){
2387
+ $passMD5 = strtoupper(md5($user->user_pass));
2388
+ if($passMD5 != $res['results'][$i]['hashMD5']){ //Password has changed, so exclude this result
2389
+ continue;
2390
+ }
2391
+ $item = $res['results'][$i];
2392
+ $item['username'] = $user->user_login;
2393
+ $item['email'] = $user->user_email;
2394
+ $item['firstName'] = $user->first_name;
2395
+ $item['lastName'] = $user->last_name;
2396
+ $item['starredPassword'] = $res['results'][$i]['pwFirstLetter'] . str_repeat('*', $res['results'][$i]['pwLength'] - 1);
2397
+ //crackTime and crackDifficulty are fields too.
2398
+ $finalResults[] = $item;
2399
+ }
2400
+ }
2401
+ }
2402
+
2403
+ return array(
2404
+ 'ok' => 1,
2405
+ 'results' => $finalResults,
2406
+ );
2407
+ } else {
2408
+ return array('ok' => 1); //fail silently
2409
+ }
2410
  }
2411
+ public static function ajax_passwdLoadJobs_callback(){
2412
+ if(! wfAPI::SSLEnabled()){ return array('ok' => 1); } //If user hits start passwd audit they will get a helpful message. We don't want an error popping up for every ajax call if SSL is not supported.
2413
+ $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2414
+ try {
2415
+ $res = $api->call('password_load_jobs', array(), array(), true);
2416
+ } catch(Exception $e){
2417
+ return array('errorMsg' => "Could not load password audit jobs: " . $e);
2418
+ }
2419
+ if(is_array($res) && $res['ok']){
2420
+ return array(
2421
+ 'ok' => 1,
2422
+ 'results' => $res['results'],
2423
+ );
2424
+ } else {
2425
+ return array('ok' => 1); //fail silently
2426
+ }
2427
+ }
2428
+ public static function ajax_killPasswdAudit_callback(){
2429
+ $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2430
+ try {
2431
+ $res = $api->call('password_kill_job', array(), array( 'jobID' => $_POST['jobID'] ), true);
2432
+ } catch(Exception $e){
2433
+ return array('errorMsg' => "Could not stop requested audit: " . $e);
2434
+ }
2435
+ if(is_array($res) && $res['ok']){
2436
+ return array(
2437
+ 'ok' => 1,
2438
+ );
2439
+ } else {
2440
+ return array('errorMsg' => "We could not stop the requested password audit."); //fail silently
2441
+ }
2442
+ }
2443
+ public static function ajax_deletePasswdAudit_callback(){
2444
+ $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2445
+ try {
2446
+ $res = $api->call('password_delete_job', array(), array( 'jobID' => $_POST['jobID']));
2447
+ } catch(Exception $e){
2448
+ return array('errorMsg' => "Could not delete the job you specified: " . $e);
2449
+ }
2450
+ return array('ok' => 1);
2451
+ }
2452
  public static function ajax_importSettings_callback(){
2453
  $token = $_POST['token'];
2454
  try {
2465
  wfScanEngine::startScan();
2466
  }
2467
  public static function templateRedir(){
2468
+ $wfFunc = get_query_var('_wfsf');
2469
+
2470
  //Logging
2471
  self::doEarlyAccessLogging();
2472
  //End logging
2506
  self::wfFunc_testmem();
2507
  } else if($wfFunc == 'testtime'){
2508
  self::wfFunc_testtime();
2509
+ }
2510
  exit(0);
2511
  }
2512
  public static function memtest_error_handler($errno, $errstr, $errfile, $errline){
2584
  wfscr.async = true;
2585
  wfscr.src = url;
2586
  (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(wfscr);
2587
+ })('$scriptURL');
2588
  </script>
2589
  EOL;
2590
  }
2601
  wfscr.async = true;
2602
  wfscr.src = url + '&r=' + Math.random();
2603
  (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(wfscr);
2604
+ })('$URL');
2605
  </script>
2606
  EOL;
2607
  }
2620
  $reverseLookup = wfUtils::reverseLookup($IP);
2621
  $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2622
  $results = array_merge(
2623
+ $wfLog->getHits('hits', 'hit', 0, 10000, $IP),
2624
  $wfLog->getHits('hits', '404', 0, 10000, $IP)
2625
  );
2626
  usort($results, 'wordfence::iptrafsort');
2662
  $fileMTime = @filemtime($localFile);
2663
  $fileMTime = date('l jS \of F Y h:i:s A', $fileMTime);
2664
  try {
2665
+ if(wfUtils::fileOver2Gigs($localFile)){
2666
  $fileSize = "Greater than 2 Gigs";
2667
  } else {
2668
  $fileSize = @filesize($localFile); //Checked if over 2 gigs above
2695
  } else {
2696
  $diff = new Diff(
2697
  //Treat DOS and Unix files the same
2698
+ preg_split("/(?:\r\n|\n)/", $result['fileContent']),
2699
+ preg_split("/(?:\r\n|\n)/", $localContents),
2700
  array()
2701
  );
2702
  $renderer = new Diff_Renderer_Html_SideBySide;
2726
  }
2727
  public static function admin_init(){
2728
  if(! wfUtils::isAdmin()){ return; }
2729
+ foreach(array('activate', 'scan', 'updateAlertEmail', 'sendActivityLog', 'restoreFile', 'startPasswdAudit', 'deletePasswdAudit', 'weakPasswordsFix', 'passwdLoadResults', 'killPasswdAudit', 'passwdLoadJobs', 'exportSettings', 'importSettings', 'bulkOperation', 'deleteFile', 'removeExclusion', 'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues', 'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP', 'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'downloadHtaccess', 'checkFalconHtaccess', 'updateConfig', 'saveCacheConfig', 'removeFromCache', 'autoUpdateChoice', 'saveCacheOptions', 'clearPageCache', 'getCacheStats', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed', 'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel', 'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion', 'loadCacheExclusions', 'email_summary_email_address_debug') as $func){
2730
  add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
2731
  }
2732
 
2831
  }
2832
  }
2833
  }
2834
+
2835
  add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan');
2836
+ add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', wfUtils::getBaseURL() . 'images/wordfence-logo-16x16.png');
2837
  add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
2838
  /* add_submenu_page('Wordfence', 'Site Performance', 'Site Performance', 'activate_plugins', 'WordfenceSitePerfStats', 'wordfence::menu_sitePerfStats'); */
2839
  add_submenu_page('Wordfence', 'Performance Setup', 'Performance Setup', 'activate_plugins', 'WordfenceSitePerf', 'wordfence::menu_sitePerf');
2840
  add_submenu_page('Wordfence', 'Blocked IPs', 'Blocked IPs', 'activate_plugins', 'WordfenceBlockedIPs', 'wordfence::menu_blockedIPs');
2841
+ add_submenu_page('Wordfence', 'Password Audit', 'Password Audit', 'activate_plugins', 'WordfencePasswdAudit', 'wordfence::menu_passwd');
2842
+
2843
  add_submenu_page("Wordfence", "Cellphone Sign-in", "Cellphone Sign-in", "activate_plugins", "WordfenceTwoFactor", 'wordfence::menu_twoFactor');
2844
  add_submenu_page("Wordfence", "Country Blocking", "Country Blocking", "activate_plugins", "WordfenceCountryBlocking", 'wordfence::menu_countryBlocking');
2845
  add_submenu_page("Wordfence", "Scan Schedule", "Scan Schedule", "activate_plugins", "WordfenceScanSchedule", 'wordfence::menu_scanSchedule');
2859
  public static function menu_blockedIPs(){
2860
  require 'menu_blockedIPs.php';
2861
  }
2862
+ public static function menu_passwd()
2863
+ {
2864
+ require 'menu_passwd.php';
2865
+ }
2866
  public static function menu_scanSchedule(){
2867
  require 'menu_scanSchedule.php';
2868
  }
2909
  if(wfConfig::get('other_pwStrengthOnUpdate')){
2910
  $oldDat = get_userdata($userID);
2911
  if($newDat->user_pass != $oldDat->user_pass){
2912
+ $wf = new wfScanEngine();
2913
  $wf->scanUserPassword($userID);
2914
  $wf->emailNewIssues();
2915
  }
2934
  if( $approved == 1 && (! is_user_logged_in()) && wfConfig::get('other_noAnonMemberComments') ){
2935
  $user = get_user_by('email', trim($cData['comment_author_email']));
2936
  if($user){
2937
+ wfConfig::inc('totalSpamStopped');
2938
  return 0; //hold for moderation if the user is not signed in but used a members email
2939
  }
2940
  }
2941
+
2942
  if(($approved == 1 || $approved == 0) && wfConfig::get('other_scanComments')){
2943
  $wf = new wfScanEngine();
2944
  try {
2945
  if($wf->isBadComment($cData['comment_author'], $cData['comment_author_email'], $cData['comment_author_url'], $cData['comment_author_IP'], $cData['comment_content'])){
2946
+ wfConfig::inc('totalSpamStopped');
2947
  return 'spam';
2948
  }
2949
  } catch(Exception $e){
2958
  preg_replace_callback('/https?:\/\/([a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+[a-zA-Z0-9])/i', 'wordfence::pushCommentSpamHost', $cData['comment_content']);
2959
  $hosts = self::$commentSpamItems;
2960
  self::$commentSpamItems = array();
2961
+ try {
2962
  $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
2963
  $res = $api->call('advanced_comment_scan', array(), array(
2964
  'author' => $cData['comment_author'],
2970
  'IPs' => (sizeof($IPs) > 0 ? implode(',', $IPs) : '')
2971
  ));
2972
  if(is_array($res) && isset($res['spam']) && $res['spam'] == 1){
2973
+ wfConfig::inc('totalSpamStopped');
2974
  return 'spam';
2975
  }
2976
  } catch(Exception $e){
2977
  //API server is probably down
2978
  }
2979
  }
2980
+ wfConfig::inc('totalCommentsFiltered');
2981
  return $approved;
2982
  }
2983
  public static function getMyHomeURL(){
2988
  }
2989
 
2990
  public static function alert($subject, $alertMsg, $IP){
2991
+ wfConfig::inc('totalAlertsSent');
2992
  $emails = wfConfig::getAlertEmails();
2993
  if(sizeof($emails) < 1){ return; }
2994
 
3007
  }
3008
  $IPMsg .= $userLoc['countryName'] . "\n";
3009
  }
3010
+ }
3011
  $content = wfUtils::tmpl('email_genericAlert.php', array(
3012
  'isPaid' => wfConfig::get('isPaid'),
3013
  'subject' => $subject,
3050
  }
3051
  }
3052
  }
3053
+ wfConfig::set('lastEmailHash', time() . ':' . $hash);
3054
  wp_mail(implode(',', $emails), $subject, $content);
3055
  }
3056
  public static function getLog(){
3122
  return self::$debugOn;
3123
  }
3124
  //PUBLIC API
3125
+ public static function doNotCache(){ //Call this to prevent Wordfence from caching the current page.
3126
  wfCache::doNotCache();
3127
  return true;
3128
  }
3144
  wfConfig::set('whitelisted', implode(',', $arr2));
3145
  return true;
3146
  }
3147
+
3148
+ public static function ajax_email_summary_email_address_debug_callback() {
3149
+ $email = !empty($_REQUEST['email']) ? $_REQUEST['email'] : null;
3150
+ $report = new wfActivityReport();
3151
+ return $report->sendReportViaEmail($email) ?
3152
+ array('ok' => 1, 'result' => 'Test email sent successfully') :
3153
+ array('err' => "Test email failed to send.");
3154
+ }
3155
+
3156
+ public static function addDashboardWidget() {
3157
+ if (wfConfig::get('email_summary_dashboard_widget_enabled')) {
3158
+ wp_enqueue_style('wordfence-activity-report-widget', wfUtils::getBaseURL() . 'css/activity-report-widget.css', '', WORDFENCE_VERSION);
3159
+ $report_date_range = 'week';
3160
+ switch (wfConfig::get('email_summary_interval')) {
3161
+ case 'biweekly':
3162
+ $report_date_range = '2 weeks';
3163
+ break;
3164
+
3165
+ case 'monthly':
3166
+ $report_date_range = 'month';
3167
+ break;
3168
+ }
3169
+ wp_add_dashboard_widget(
3170
+ 'wordfence_activity_report_widget',
3171
+ 'Wordfence activity in the past ' . $report_date_range,
3172
+ array('wfActivityReport', 'outputDashboardWidget')
3173
+ );
3174
+ }
3175
+ }
3176
  }
3177
  ?>
lib/wordfenceConstants.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- define('WORDFENCE_API_VERSION', '2.14');
3
  define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
4
  define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
5
  define('WORDFENCE_MAX_SCAN_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
1
  <?php
2
+ define('WORDFENCE_API_VERSION', '2.15');
3
  define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
4
  define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
5
  define('WORDFENCE_MAX_SCAN_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching
4
  Requires at least: 3.9
5
  Tested up to: 4.1.1
6
- Stable tag: 5.3.8
7
 
8
  Wordfence Security is a free enterprise class security and performance plugin that makes your site up to 50 times faster and more secure.
9
 
@@ -165,6 +165,14 @@ cause a security hole on your site.
165
 
166
  == Changelog ==
167
 
 
 
 
 
 
 
 
 
168
  = 5.3.8 =
169
  * Customers running WP versions older than 3.9 don't support wp_normalize_path(). Added support for older WP versions to fix an error being thrown.
170
 
@@ -1099,3 +1107,4 @@ Wordfence Security to zero (we simply reuse the WordPress DB handle), reduces th
1099
  about 1% of the previous version by removing unneeded status messages and fixes a bug that
1100
  could cause Wordfence Security to launch multiple concurrent scans that can put high load on your system.
1101
  This is a critical release. Upgrade immediately.
 
3
  Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching
4
  Requires at least: 3.9
5
  Tested up to: 4.1.1
6
+ Stable tag: 5.3.9
7
 
8
  Wordfence Security is a free enterprise class security and performance plugin that makes your site up to 50 times faster and more secure.
9
 
165
 
166
  == Changelog ==
167
 
168
+ = 5.3.9 =
169
+ * Premium Feature: Password Auditing. Audit the strength of your admin and user-level passwords against our GPU based auditing cluster. Easily alert users to weak passwords or force a password change.
170
+ * Feature: Activity email summary. See options page to enable a weekly, bi-weekly or monthly activity summary.
171
+ * Feature: Activity summary dashboard widget.
172
+ * Fix: Fixed bug on plugin activation where the configuration table was being queried before it was created.
173
+ * Improvement: Added .htaccess to wfcache directory.
174
+ * Improvement: Switched to using wp_remote_post for Wordfence cloud API calls to improved SSL support and a more standards based approach.
175
+
176
  = 5.3.8 =
177
  * Customers running WP versions older than 3.9 don't support wp_normalize_path(). Added support for older WP versions to fix an error being thrown.
178
 
1107
  about 1% of the previous version by removing unneeded status messages and fixes a bug that
1108
  could cause Wordfence Security to launch multiple concurrent scans that can put high load on your system.
1109
  This is a critical release. Upgrade immediately.
1110
+
views/reports/activity-report-email-inline.php ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var wfActivityReportView $this
4
+ */
5
+ list($start_time, $end_time) = wfActivityReport::getReportDateRange();
6
+ $title = 'Wordfence activity from<br><strong>' . date_i18n(get_option('date_format'), $start_time) . '</strong> to <strong>' . date_i18n(get_option('date_format'), $end_time) . '</strong>';
7
+ $bg_colors = array(
8
+ 'even' => 'background-color: #eeeeee;',
9
+ 'odd' => '',
10
+ );
11
+ ?>
12
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
13
+ <html xmlns="http://www.w3.org/1999/xhtml">
14
+ <head>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=<?php echo get_option('blog_charset') ?>"/>
16
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
17
+ <title><?php echo esc_html(strip_tags($title)) ?></title>
18
+ <!-- Targeting Windows Mobile -->
19
+ <!--[if IEMobile 7]>
20
+ <style type="text/css">
21
+
22
+ </style>
23
+ <![endif]-->
24
+
25
+ <!-- ***********************************************
26
+ ****************************************************
27
+ END MOBILE TARGETING
28
+ ****************************************************
29
+ ************************************************ -->
30
+
31
+ <!--[if gte mso 9]>
32
+ <style>
33
+ /* Target Outlook 2007 and 2010 */
34
+ </style>
35
+ <![endif]-->
36
+ </head>
37
+ <body style="font-size: 10pt; vertical-align: baseline; line-height: 1; font-family: Helvetica, Arial, sans-serif; text-rendering: optimizeLegibility; color: #000; background-image: none !important; width: 100% !important; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; background-color: #e6e6e6; margin: 0; padding: 0; border: 0;" bgcolor="#e6e6e6"><style type="text/css">
38
+ blockquote:before { content: none !important; }
39
+ blockquote:after { content: none !important; }
40
+ q:before { content: none !important; }
41
+ q:after { content: none !important; }
42
+ a:focus { outline: thin dotted !important; }
43
+ .clear:after { clear: both !important; }
44
+ .wrapper:after { clear: both !important; }
45
+ .format-status .entry-header:after { clear: both !important; }
46
+ .clear:before { display: table !important; content: "" !important; }
47
+ .clear:after { display: table !important; content: "" !important; }
48
+ .wrapper:before { display: table !important; content: "" !important; }
49
+ .wrapper:after { display: table !important; content: "" !important; }
50
+ .format-status .entry-header:before { display: table !important; content: "" !important; }
51
+ .format-status .entry-header:after { display: table !important; content: "" !important; }
52
+ .menu-toggle:hover { color: #5e5e5e !important; background-color: #ebebeb !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #f9f9f9, #ebebeb) !important; }
53
+ .menu-toggle:focus { color: #5e5e5e !important; background-color: #ebebeb !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #f9f9f9, #ebebeb) !important; }
54
+ button:hover { color: #5e5e5e !important; background-color: #ebebeb !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #f9f9f9, #ebebeb) !important; }
55
+ input[type="submit"]:hover { color: #5e5e5e !important; background-color: #ebebeb !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #f9f9f9, #ebebeb) !important; }
56
+ input[type="button"]:hover { color: #5e5e5e !important; background-color: #ebebeb !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #f9f9f9, #ebebeb) !important; }
57
+ input[type="reset"]:hover { color: #5e5e5e !important; background-color: #ebebeb !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #f9f9f9, #ebebeb) !important; }
58
+ article.post-password-required input[type=submit]:hover { color: #5e5e5e !important; background-color: #ebebeb !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #f9f9f9, #ebebeb) !important; }
59
+ .menu-toggle:active { color: #757575 !important; background-color: #e1e1e1 !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #ebebeb, #e1e1e1) !important; box-shadow: inset 0 0 8px 2px #c6c6c6, 0 1px 0 0 #f4f4f4 !important; border-color: transparent !important; }
60
+ button:active { color: #757575 !important; background-color: #e1e1e1 !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #ebebeb, #e1e1e1) !important; box-shadow: inset 0 0 8px 2px #c6c6c6, 0 1px 0 0 #f4f4f4 !important; border-color: transparent !important; }
61
+ input[type="submit"]:active { color: #757575 !important; background-color: #e1e1e1 !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #ebebeb, #e1e1e1) !important; box-shadow: inset 0 0 8px 2px #c6c6c6, 0 1px 0 0 #f4f4f4 !important; border-color: transparent !important; }
62
+ input[type="button"]:active { color: #757575 !important; background-color: #e1e1e1 !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #ebebeb, #e1e1e1) !important; box-shadow: inset 0 0 8px 2px #c6c6c6, 0 1px 0 0 #f4f4f4 !important; border-color: transparent !important; }
63
+ input[type="reset"]:active { color: #757575 !important; background-color: #e1e1e1 !important; background-repeat: repeat-x !important; background-image: linear-gradient(top, #ebebeb, #e1e1e1) !important; box-shadow: inset 0 0 8px 2px #c6c6c6, 0 1px 0 0 #f4f4f4 !important; border-color: transparent !important; }
64
+ a:hover { color: #0f3647 !important; }
65
+ .main-navigation .assistive-text:focus { background: #fff !important; border: 2px solid #333 !important; border-radius: 3px !important; clip: auto !important; color: #000 !important; display: block !important; font-size: 12px !important; padding: 12px !important; position: absolute !important; top: 5px !important; left: 5px !important; z-index: 100000 !important; }
66
+ .site-header h1 a:hover { color: #21759b !important; }
67
+ .site-header h2 a:hover { color: #21759b !important; }
68
+ .main-navigation a:hover { color: #21759b !important; }
69
+ .main-navigation a:focus { color: #21759b !important; }
70
+ .widget-area .widget a:hover { color: #21759b !important; }
71
+ .widget-area .widget a:visited { color: #9f9f9f !important; }
72
+ footer[role="contentinfo"] a:hover { color: #21759b !important; }
73
+ .comments-link a:hover { color: #21759b !important; }
74
+ .entry-meta a:hover { color: #21759b !important; }
75
+ .entry-content a:visited { color: #9f9f9f !important; }
76
+ .comment-content a:visited { color: #9f9f9f !important; }
77
+ article.format-aside h1 a:hover { color: #2e3542 !important; }
78
+ .format-status .entry-header header a:hover { color: #21759b !important; }
79
+ .comments-area article header a:hover { color: #21759b !important; }
80
+ .comments-area article header cite a:hover { text-decoration: underline !important; }
81
+ a.comment-reply-link:hover { color: #21759b !important; }
82
+ a.comment-edit-link:hover { color: #21759b !important; }
83
+ .template-front-page .widget-area .widget li a:hover { color: #21759b !important; }
84
+ @-ms-viewport { width: device-width !important; }
85
+ @viewport { width: device-width !important; }
86
+ .main-navigation li a:hover { color: #000 !important; }
87
+ .main-navigation li a:focus { color: #000 !important; }
88
+ .main-navigation ul li:hover &gt; ul { border-left: 0 !important; clip: inherit !important; overflow: inherit !important; height: inherit !important; width: inherit !important; }
89
+ .main-navigation ul li:focus &gt; ul { border-left: 0 !important; clip: inherit !important; overflow: inherit !important; height: inherit !important; width: inherit !important; }
90
+ .main-navigation li ul li a:hover { background: #e3e3e3 !important; color: #444 !important; }
91
+ .main-navigation li ul li a:focus { background: #e3e3e3 !important; color: #444 !important; }
92
+ footer a[rel=bookmark]:after { content: " [" attr(href) "] " !important; }
93
+ footer a[rel=bookmark]:visited:after { content: " [" attr(href) "] " !important; }
94
+ h1 a:active { color: red !important; }
95
+ h2 a:active { color: red !important; }
96
+ h3 a:active { color: red !important; }
97
+ h4 a:active { color: red !important; }
98
+ h5 a:active { color: red !important; }
99
+ h6 a:active { color: red !important; }
100
+ h1 a:visited { color: purple !important; }
101
+ h2 a:visited { color: purple !important; }
102
+ h3 a:visited { color: purple !important; }
103
+ h4 a:visited { color: purple !important; }
104
+ h5 a:visited { color: purple !important; }
105
+ h6 a:visited { color: purple !important; }
106
+ .button:hover { background: none repeat scroll 0 0 #1E8CBE !important; border-color: #0074A2 !important; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.6) inset !important; color: #FFF !important; }
107
+ .button:active { background: none repeat scroll 0 0 #1E8CBE !important; border-color: #0074A2 !important; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.6) inset !important; color: #FFF !important; }
108
+ .button:focus { background: none repeat scroll 0 0 #1E8CBE !important; border-color: #0074A2 !important; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.6) inset !important; color: #FFF !important; }
109
+ </style>
110
+ <!-- Wrapper/Container Table: Use a wrapper table to control the width and the background color consistently of your email. Use this approach instead of setting attributes on the body tag. -->
111
+ <table cellpadding="0" cellspacing="0" border="0" id="backgroundTable" style="font-size: 100%; vertical-align: baseline; border-collapse: collapse; border-spacing: 0; width: 100% !important; line-height: 100% !important; mso-table-lspace: 0pt; mso-table-rspace: 0pt; margin: 0 auto; padding: 0; border: 0;">
112
+ <tr style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
113
+ <td valign="top" style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 0; border: 0;" align="left">
114
+ <div class="wrapper wp-core-ui" style="font-size: 100%; vertical-align: baseline; border-top-style: none; box-shadow: none; line-height: 1.4; width: 600px; background-color: #FFFFFF; margin: 0 auto; padding: 20px; border: 0;">
115
+ <div style="float: right; text-align: right; line-height: 1.1; color: #666666; font-size: 100%; vertical-align: baseline; margin: 20px 0 0; padding: 0; border: 0;" align="right">
116
+ <?php echo $title ?>
117
+ </div>
118
+ <a href="http://www.wordfence.com/" style="font-size: 100%; vertical-align: baseline; outline: none; color: orange; text-decoration: none; margin: 0; padding: 0; border: 0;"><img src="http://www.wordfence.com/wp-content/themes/parallelus-salutation/wfCustomHome/images/wordfenceLogo.png" alt="" style="font-size: 100%; vertical-align: baseline; -ms-interpolation-mode: bicubic; outline: none; text-decoration: none; margin: 0; padding: 0; border: 0 none;" /></a>
119
+
120
+ <?php if (!wfConfig::get('isPaid')): ?>
121
+ <p>For access to Premium features like Country Blocking, Password Auditing, Cellphone Sign-in and more, <a href="http://www.wordfence.com/?utm_source=plugin&utm_medium=UI&utm_campaign=summaryEmail">click here to upgrade to Wordfence Premium now</a>.</p>
122
+ <?php endif ?>
123
+
124
+ <h2 style="font-size: 20px; vertical-align: baseline; clear: both; color: #222 !important; margin: 20px 0 4px; padding: 0; border: 0;">
125
+ Top 10 IP's Blocked
126
+ </h2>
127
+
128
+ <?php wfHelperString::cycle(); ?>
129
+
130
+ <table class="activity-table" style="font-size: 100%; vertical-align: baseline; border-collapse: collapse; border-spacing: 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; max-width: 100%; margin: 0; padding: 0; border: 0;">
131
+ <thead style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
132
+ <tr style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
133
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">IP</th>
134
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Country</th>
135
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Block Count</th>
136
+ </tr>
137
+ </thead>
138
+ <tbody style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
139
+ <?php if ($top_ips_blocked): ?>
140
+ <?php foreach ($top_ips_blocked as $row): ?>
141
+ <?php
142
+ $stripe = wfHelperString::cycle('odd', 'even');
143
+ ?>
144
+ <tr class="<?php echo $stripe ?>" style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
145
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline"><code style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;"><?php echo wfUtils::inet_ntoa($row->IP) ?></code></td>
146
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline">
147
+ <?php if ($row->countryCode): ?>
148
+ <img src="http://www.wordfence.com/images/flags/<?php echo esc_attr(strtolower($row->countryCode)) ?>.png" class="wfFlag" height="11" width="16" style="font-size: 100%; vertical-align: baseline; -ms-interpolation-mode: bicubic; outline: none; text-decoration: none; margin: 0; padding: 0; border: 0;">
149
+ &nbsp;
150
+ <?php echo esc_html($row->countryCode) ?>
151
+ <?php else: ?>
152
+ (Unknown)
153
+ <?php endif ?>
154
+ </td>
155
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline"><?php echo (int)$row->blockCount ?></td>
156
+ </tr>
157
+ <?php endforeach ?>
158
+ <?php else: ?>
159
+ <tr>
160
+ <td colspan="3">
161
+ No data currently.
162
+ </td>
163
+ </tr>
164
+ <?php endif ?>
165
+ </tbody>
166
+ </table>
167
+
168
+ <p style="font-size: 100%; vertical-align: baseline; margin: 1em 0; padding: 0; border: 0;">
169
+ <a class="button" href="<?php echo admin_url('admin.php?page=WordfenceBlockedIPs') ?>" style="font-size: 13px; vertical-align: baseline; outline: none; color: #FFF; text-decoration: none; display: inline-block; line-height: 26px; height: 28px; cursor: pointer; border-radius: 3px; white-space: nowrap; box-sizing: border-box; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.5) inset, 0 1px 0 rgba(0, 0, 0, 0.15); background-image: none; background-attachment: scroll; background-repeat: repeat; background-color: #2EA2CC; margin: 0; padding: 0 10px 1px; border: 1px solid #0074a2;">Update Blocked IPs</a>
170
+ </p>
171
+
172
+ <?php wfHelperString::cycle(); ?>
173
+
174
+ <h2 style="font-size: 20px; vertical-align: baseline; clear: both; color: #222 !important; margin: 20px 0 4px; padding: 0; border: 0;">Top 10 Countries Blocked</h2>
175
+
176
+ <table class="activity-table" style="font-size: 100%; vertical-align: baseline; border-collapse: collapse; border-spacing: 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; max-width: 100%; margin: 0; padding: 0; border: 0;">
177
+ <thead style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
178
+ <tr style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
179
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Country</th>
180
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Total IPs Blocked</th>
181
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Block Count</th>
182
+ </tr>
183
+ </thead>
184
+ <tbody style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
185
+ <?php if ($top_countries_blocked): ?>
186
+ <?php foreach ($top_countries_blocked as $row): ?>
187
+ <?php
188
+ $stripe = wfHelperString::cycle('odd', 'even');
189
+ ?>
190
+ <tr class="<?php echo $stripe ?>" style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
191
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline">
192
+ <?php if ($row->countryCode): ?>
193
+ <img src="http://www.wordfence.com/images/flags/<?php echo strtolower($row->countryCode) ?>.png" class="wfFlag" height="11" width="16" style="font-size: 100%; vertical-align: baseline; -ms-interpolation-mode: bicubic; outline: none; text-decoration: none; margin: 0; padding: 0; border: 0;">
194
+ &nbsp;
195
+ <?php echo esc_html($row->countryCode) ?>
196
+ <?php else: ?>
197
+ (Unknown)
198
+ <?php endif ?>
199
+ </td>
200
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline"><?php echo esc_html($row->totalIPs) ?></td>
201
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline"><?php echo (int)$row->totalBlockCount ?></td>
202
+ </tr>
203
+ <?php endforeach ?>
204
+ <?php else: ?>
205
+ <tr>
206
+ <td colspan="3">
207
+ No data currently.
208
+ </td>
209
+ </tr>
210
+ <?php endif ?>
211
+ </tbody>
212
+ </table>
213
+
214
+ <p style="font-size: 100%; vertical-align: baseline; margin: 1em 0; padding: 0; border: 0;">
215
+ <a class="button" href="<?php echo admin_url('admin.php?page=WordfenceCountryBlocking') ?>" style="font-size: 13px; vertical-align: baseline; outline: none; color: #FFF; text-decoration: none; display: inline-block; line-height: 26px; height: 28px; cursor: pointer; border-radius: 3px; white-space: nowrap; box-sizing: border-box; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.5) inset, 0 1px 0 rgba(0, 0, 0, 0.15); background-image: none; background-attachment: scroll; background-repeat: repeat; background-color: #2EA2CC; margin: 0; padding: 0 10px 1px; border: 1px solid #0074a2;">Update Blocked Countries</a>
216
+ </p>
217
+
218
+ <?php wfHelperString::cycle(); ?>
219
+
220
+ <h2 style="font-size: 20px; vertical-align: baseline; clear: both; color: #222 !important; margin: 20px 0 4px; padding: 0; border: 0;">Top 10 Failed Logins</h2>
221
+
222
+ <table class="activity-table" style="font-size: 100%; vertical-align: baseline; border-collapse: collapse; border-spacing: 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; max-width: 100%; margin: 0; padding: 0; border: 0;">
223
+ <thead style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
224
+ <tr style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
225
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Username</th>
226
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Login Attempts</th>
227
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Existing User</th>
228
+ </tr>
229
+ </thead>
230
+ <tbody style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
231
+ <?php if ($top_failed_logins): ?>
232
+ <?php foreach ($top_failed_logins as $row): ?>
233
+ <?php
234
+ $stripe = wfHelperString::cycle('odd', 'even');
235
+ ?>
236
+ <tr class="<?php echo $stripe ?>" style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
237
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline"><?php echo esc_html($row->username) ?></td>
238
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline"><?php echo esc_html($row->fail_count) ?></td>
239
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline" class="<?php echo sanitize_html_class($row->action) ?>"><?php echo $row->action == 'loginFailValidUsername' ? 'Yes' : 'No' ?></td>
240
+ </tr>
241
+ <?php endforeach ?>
242
+ <?php else: ?>
243
+ <tr>
244
+ <td colspan="3">
245
+ No failed logins yet.
246
+ </td>
247
+ </tr>
248
+ <?php endif ?>
249
+ </tbody>
250
+ </table>
251
+
252
+ <p style="font-size: 100%; vertical-align: baseline; margin: 1em 0; padding: 0; border: 0;">
253
+ <a class="button" href="<?php echo admin_url('admin.php?page=WordfenceSecOpt#wfMarkerLoginSecurity') ?>" style="font-size: 13px; vertical-align: baseline; outline: none; color: #FFF; text-decoration: none; display: inline-block; line-height: 26px; height: 28px; cursor: pointer; border-radius: 3px; white-space: nowrap; box-sizing: border-box; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.5) inset, 0 1px 0 rgba(0, 0, 0, 0.15); background-image: none; background-attachment: scroll; background-repeat: repeat; background-color: #2EA2CC; margin: 0; padding: 0 10px 1px; border: 1px solid #0074a2;">Update Login Security Options</a>
254
+ </p>
255
+
256
+ <?php wfHelperString::cycle(); ?>
257
+
258
+ <h2 style="font-size: 20px; vertical-align: baseline; clear: both; color: #222 !important; margin: 20px 0 4px; padding: 0; border: 0;">Recently Modified Files</h2>
259
+
260
+ <table class="activity-table" style="font-size: 100%; vertical-align: baseline; border-collapse: collapse; border-spacing: 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; max-width: 100%; margin: 0; padding: 0; border: 0;">
261
+ <thead style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
262
+ <tr style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
263
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">Modified</th>
264
+ <th style="font-size: 100%; vertical-align: baseline; font-weight: bold; text-align: left; color: #FFFFFF; background-color: #222; margin: 0; padding: 6px 4px; border: 1px solid #474747;" align="left" bgcolor="#222" valign="baseline">File</th>
265
+ </tr>
266
+ </thead>
267
+ <tbody style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
268
+ <?php foreach ($recently_modified_files as $file_row):
269
+ list($file, $mod_time) = $file_row;
270
+ ?>
271
+ <?php
272
+ $stripe = wfHelperString::cycle('odd', 'even');
273
+ ?>
274
+ <tr class="<?php echo $stripe ?>" style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
275
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;white-space: nowrap;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline"><?php echo $this->modTime($mod_time) ?></td>
276
+ <td style="font-size: 100%; vertical-align: baseline; font-weight: normal; text-align: left; border-collapse: collapse; margin: 0; padding: 6px 4px; border: 1px solid #cccccc;<?php echo $bg_colors[$stripe] ?>" align="left" valign="baseline">
277
+ <pre class="display-file" style="font-size: 12px; vertical-align: baseline; width: 420px; overflow: auto; margin: 0; padding: 0; border: 0;"><?php echo esc_html($this->displayFile($file)) ?></pre>
278
+ </td>
279
+ </tr>
280
+ <?php endforeach ?>
281
+ </tbody>
282
+ </table>
283
+
284
+ <?php wfHelperString::cycle(); ?>
285
+
286
+ <h2 style="font-size: 20px; vertical-align: baseline; clear: both; color: #222 !important; margin: 20px 0 4px; padding: 0; border: 0;">Updates Needed</h2>
287
+
288
+ <?php if ($updates_needed['core']): ?>
289
+ <h4 style="font-size: 16px; vertical-align: baseline; clear: both; color: #666666 !important; margin: 20px 0 4px; padding: 0; border: 0;">Core</h4>
290
+ <ul style="font-size: 100%; vertical-align: baseline; list-style-type: none; margin: 0; padding: 0; border: 0;">
291
+ <li style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">A new version of WordPress (v<?php echo esc_html($updates_needed['core']) ?>) is available.</li>
292
+ </ul>
293
+ <?php endif ?>
294
+ <?php if ($updates_needed['plugins']): ?>
295
+ <h4 style="font-size: 16px; vertical-align: baseline; clear: both; color: #666666 !important; margin: 20px 0 4px; padding: 0; border: 0;">Plugins</h4>
296
+ <ul style="font-size: 100%; vertical-align: baseline; list-style-type: none; margin: 0; padding: 0; border: 0;">
297
+ <?php foreach ($updates_needed['plugins'] as $plugin): ?>
298
+ <li style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
299
+ A new version of the plugin "<?php echo esc_html("{$plugin['Name']} (v{$plugin['newVersion']})") ?>" is available.
300
+ </li>
301
+ <?php endforeach ?>
302
+ </ul>
303
+ <?php endif ?>
304
+ <?php if ($updates_needed['themes']): ?>
305
+ <h4 style="font-size: 16px; vertical-align: baseline; clear: both; color: #666666 !important; margin: 20px 0 4px; padding: 0; border: 0;">Themes</h4>
306
+ <ul style="font-size: 100%; vertical-align: baseline; list-style-type: none; margin: 0; padding: 0; border: 0;">
307
+ <?php foreach ($updates_needed['themes'] as $theme): ?>
308
+ <li style="font-size: 100%; vertical-align: baseline; margin: 0; padding: 0; border: 0;">
309
+ A new version of the theme "<?php echo esc_html("{$theme['name']} (v{$theme['newVersion']})") ?>" is available.
310
+ </li>
311
+ <?php endforeach ?>
312
+ </ul>
313
+ <?php endif ?>
314
+
315
+ <?php if ($updates_needed['core'] || $updates_needed['plugins'] || $updates_needed['themes']): ?>
316
+ <p style="font-size: 100%; vertical-align: baseline; margin: 1em 0; padding: 0; border: 0;">
317
+ <a class="button" href="<?php echo esc_attr(admin_url('update-core.php')) ?>" style="font-size: 13px; vertical-align: baseline; outline: none; color: #FFF; text-decoration: none; display: inline-block; line-height: 26px; height: 28px; cursor: pointer; border-radius: 3px; white-space: nowrap; box-sizing: border-box; box-shadow: 0 1px 0 rgba(120, 200, 230, 0.5) inset, 0 1px 0 rgba(0, 0, 0, 0.15); background-image: none; background-attachment: scroll; background-repeat: repeat; background-color: #2EA2CC; margin: 0; padding: 0 10px 1px; border: 1px solid #0074a2;">Update Now</a>
318
+ </p>
319
+ <?php else: ?>
320
+ <p style="font-size: 100%; vertical-align: baseline; margin: 1em 0; padding: 0; border: 0;">
321
+ No updates are available at this time.
322
+ </p>
323
+ <?php endif ?>
324
+ </div>
325
+ </td>
326
+ </tr>
327
+ </table>
328
+ <!-- End of wrapper table -->
329
+ </body>
330
+ </html>
views/reports/activity-report-email.php ADDED
@@ -0,0 +1,465 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var wfActivityReportView $this
4
+ */
5
+ $title = 'Wordfence Activity for the week of ' . date_i18n(get_option('date_format'));
6
+ ?>
7
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
8
+ <html xmlns="http://www.w3.org/1999/xhtml">
9
+ <head>
10
+ <meta http-equiv="Content-Type" content="text/html; charset=<?php echo get_option('blog_charset') ?>"/>
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
12
+ <title><?php echo esc_html($title) ?></title>
13
+ <link rel="stylesheet" href="http://www.wordfence.com/blog/wp-content/themes/twentytwelve/style.css?ver=4.1.1" title=""/>
14
+
15
+ <style type="text/css">
16
+ /* Based on The MailChimp Reset INLINE: Yes. */
17
+ /* Client-specific Styles */
18
+ #outlook a {
19
+ padding: 0;
20
+ }
21
+
22
+ /* Force Outlook to provide a "view in browser" menu link. */
23
+ body {
24
+ width: 100% !important;
25
+ -webkit-text-size-adjust: 100%;
26
+ -ms-text-size-adjust: 100%;
27
+ margin: 0;
28
+ padding: 0;
29
+ }
30
+
31
+ /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
32
+ .ExternalClass {
33
+ width: 100%;
34
+ }
35
+
36
+ /* Force Hotmail to display emails at full width */
37
+ .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {
38
+ line-height: 100%;
39
+ }
40
+
41
+ /* Forces Hotmail to display normal line spacing. More on that: http://www.emailonacid.com/forum/viewthread/43/ */
42
+ #backgroundTable {
43
+ margin: 0 auto;
44
+ padding: 0;
45
+ width: 100% !important;
46
+ line-height: 100% !important;
47
+ }
48
+
49
+ /* End reset */
50
+
51
+ /* Some sensible defaults for images
52
+ Bring inline: Yes. */
53
+ img {
54
+ outline: none;
55
+ text-decoration: none;
56
+ -ms-interpolation-mode: bicubic;
57
+ }
58
+
59
+ a img {
60
+ border: none;
61
+ }
62
+
63
+ .image_fix {
64
+ display: block;
65
+ }
66
+
67
+ /* Yahoo paragraph fix
68
+ Bring inline: Yes. */
69
+ p {
70
+ margin: 1em 0;
71
+ }
72
+
73
+ /* Hotmail header color reset
74
+ Bring inline: Yes. */
75
+ h1, h2, h3, h4, h5, h6 {
76
+ color: black !important;
77
+ }
78
+
79
+ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
80
+ color: blue !important;
81
+ }
82
+
83
+ h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {
84
+ color: red !important; /* Preferably not the same color as the normal header link color. There is limited support for psuedo classes in email clients, this was added just for good measure. */
85
+ }
86
+
87
+ h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {
88
+ color: purple !important; /* Preferably not the same color as the normal header link color. There is limited support for psuedo classes in email clients, this was added just for good measure. */
89
+ }
90
+
91
+ /* Outlook 07, 10 Padding issue fix
92
+ Bring inline: No.*/
93
+ table td {
94
+ border-collapse: collapse;
95
+ }
96
+
97
+ /* Remove spacing around Outlook 07, 10 tables
98
+ Bring inline: Yes */
99
+ table {
100
+ border-collapse: collapse;
101
+ mso-table-lspace: 0pt;
102
+ mso-table-rspace: 0pt;
103
+ }
104
+
105
+ /* Styling your links has become much simpler with the new Yahoo. In fact, it falls in line with the main credo of styling in email and make sure to bring your styles inline. Your link colors will be uniform across clients when brought inline.
106
+ Bring inline: Yes. */
107
+ a {
108
+ color: orange;
109
+ }
110
+
111
+ .wrapper {
112
+ line-height: 1.4;
113
+ width: 600px;
114
+ padding: 20px;
115
+ margin: 0 auto;
116
+ background-color: #FFFFFF;
117
+ }
118
+
119
+ h1, h2, h3, h4 {
120
+ margin: 20px 0 4px;
121
+ color: #222 !important;
122
+ }
123
+
124
+ h1 {
125
+ float: right;
126
+ text-align: right;
127
+ font-size: 30px;
128
+ color: #444444 !important;
129
+ line-height: 1.1;
130
+ }
131
+
132
+ h2 {
133
+ font-size: 20px;
134
+ }
135
+
136
+ h4 {
137
+ font-size: 16px;
138
+ color:#666666 !important;
139
+ }
140
+
141
+ table.wf-table {
142
+ width: 100%;
143
+ max-width: 100%;
144
+ }
145
+
146
+ table.wf-table th,
147
+ table.wf-table td {
148
+ padding: 6px 4px;
149
+ border: 1px solid #cccccc;
150
+ }
151
+
152
+ table.wf-table thead th,
153
+ table.wf-table thead td {
154
+ background-color: #222;
155
+ color: #FFFFFF;
156
+ font-weight: bold;
157
+ border-color: #474747;
158
+ }
159
+
160
+ table.wf-table tbody tr.even td {
161
+ background-color: #eeeeee;
162
+ }
163
+
164
+ .loginFailValidUsername {
165
+ color: #00c000;
166
+ font-weight: bold;
167
+ }
168
+
169
+ .loginFailInvalidUsername {
170
+ color: #e74a2a;
171
+ font-weight: bold;
172
+ }
173
+
174
+ .display-file {
175
+ font-size: 12px;
176
+ width: 420px;
177
+ overflow: auto;
178
+ }
179
+
180
+ .button {
181
+ display: inline-block;
182
+ font-size: 13px;
183
+ line-height: 26px;
184
+ height: 28px;
185
+ margin: 0;
186
+ padding: 0 10px 1px;
187
+ cursor: pointer;
188
+ border-radius: 3px;
189
+ white-space: nowrap;
190
+ box-sizing: border-box;
191
+ background: none repeat scroll 0 0 #2EA2CC;
192
+ border: 1px solid #0074A2;
193
+ box-shadow: 0 1px 0 rgba(120, 200, 230, 0.5) inset, 0 1px 0 rgba(0, 0, 0, 0.15);
194
+ color: #FFF;
195
+ text-decoration: none;
196
+ }
197
+ .button:hover,
198
+ .button:active,
199
+ .button:focus {
200
+ background: none repeat scroll 0 0 #1E8CBE;
201
+ border-color: #0074A2;
202
+ box-shadow: 0 1px 0 rgba(120, 200, 230, 0.6) inset;
203
+ color: #FFF;
204
+ }
205
+
206
+ /***************************************************
207
+ ****************************************************
208
+ MOBILE TARGETING
209
+ ****************************************************
210
+ ***************************************************/
211
+ @media only screen and (max-device-width: 480px) {
212
+ /* Part one of controlling phone number linking for mobile. */
213
+ a[href^="tel"], a[href^="sms"] {
214
+ text-decoration: none;
215
+ color: blue; /* or whatever your want */
216
+ pointer-events: none;
217
+ cursor: default;
218
+ }
219
+
220
+ .mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
221
+ text-decoration: default;
222
+ color: orange !important;
223
+ pointer-events: auto;
224
+ cursor: default;
225
+ }
226
+
227
+ }
228
+
229
+ /* More Specific Targeting */
230
+
231
+ @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
232
+ /* You guessed it, ipad (tablets, smaller screens, etc) */
233
+ /* repeating for the ipad */
234
+ a[href^="tel"], a[href^="sms"] {
235
+ text-decoration: none;
236
+ color: blue; /* or whatever your want */
237
+ pointer-events: none;
238
+ cursor: default;
239
+ }
240
+
241
+ .mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
242
+ text-decoration: default;
243
+ color: orange !important;
244
+ pointer-events: auto;
245
+ cursor: default;
246
+ }
247
+ }
248
+
249
+ @media only screen and (-webkit-min-device-pixel-ratio: 2) {
250
+ /* Put your iPhone 4g styles in here */
251
+ }
252
+
253
+ /* Android targeting */
254
+ @media only screen and (-webkit-device-pixel-ratio: .75) {
255
+ /* Put CSS for low density (ldpi) Android layouts in here */
256
+ }
257
+
258
+ @media only screen and (-webkit-device-pixel-ratio: 1) {
259
+ /* Put CSS for medium density (mdpi) Android layouts in here */
260
+ }
261
+
262
+ @media only screen and (-webkit-device-pixel-ratio: 1.5) {
263
+ /* Put CSS for high density (hdpi) Android layouts in here */
264
+ }
265
+
266
+ /* end Android targeting */
267
+
268
+ </style>
269
+
270
+ <!-- Targeting Windows Mobile -->
271
+ <!--[if IEMobile 7]>
272
+ <style type="text/css">
273
+
274
+ </style>
275
+ <![endif]-->
276
+
277
+ <!-- ***********************************************
278
+ ****************************************************
279
+ END MOBILE TARGETING
280
+ ****************************************************
281
+ ************************************************ -->
282
+
283
+ <!--[if gte mso 9]>
284
+ <style>
285
+ /* Target Outlook 2007 and 2010 */
286
+ </style>
287
+ <![endif]-->
288
+ </head>
289
+ <body>
290
+ <!-- Wrapper/Container Table: Use a wrapper table to control the width and the background color consistently of your email. Use this approach instead of setting attributes on the body tag. -->
291
+ <table cellpadding="0" cellspacing="0" border="0" id="backgroundTable">
292
+ <tr>
293
+ <td valign="top">
294
+ <div class="wrapper wp-core-ui">
295
+ <div style="float: right;text-align: right;line-height:1.1;color: #666666;margin:20px 0 0;">
296
+ Activity for week of<br> <strong><?php echo date_i18n(get_option('date_format')) ?></strong>
297
+ </div>
298
+ <a href="http://www.wordfence.com/"><img src="http://www.wordfence.com/wp-content/themes/parallelus-salutation/wfCustomHome/images/wordfenceLogo.png" alt=""/></a>
299
+
300
+ <h2>Top 10 IP's Blocked</h2>
301
+
302
+ <?php wfHelperString::cycle(); ?>
303
+
304
+ <table class="wf-table">
305
+ <thead>
306
+ <tr>
307
+ <th>IP</th>
308
+ <th>Country</th>
309
+ <th>Block Count</th>
310
+ </tr>
311
+ </thead>
312
+ <tbody>
313
+ <?php foreach ($top_ips_blocked as $row): ?>
314
+ <tr class="<?php echo wfHelperString::cycle('odd', 'even') ?>">
315
+ <td><code><?php echo wfUtils::inet_ntoa($row->IP) ?></code></td>
316
+ <td>
317
+ <?php if ($row->countryCode): ?>
318
+ <img src="http://www.wordfence.com/images/flags/<?php echo esc_attr(strtolower($row->countryCode)) ?>.png" class="wfFlag" height="11" width="16">
319
+ &nbsp;
320
+ <?php echo esc_html($row->countryCode) ?>
321
+ <?php else: ?>
322
+ (Unknown)
323
+ <?php endif ?>
324
+ </td>
325
+ <td><?php echo (int)$row->blockCount ?></td>
326
+ </tr>
327
+ <?php endforeach ?>
328
+ </tbody>
329
+ </table>
330
+
331
+ <p>
332
+ <a class="button" href="<?php echo admin_url('admin.php?page=WordfenceBlockedIPs') ?>">Update Blocked IPs</a>
333
+ </p>
334
+
335
+ <?php wfHelperString::cycle(); ?>
336
+
337
+ <h2>Top 10 Countries Blocked</h2>
338
+
339
+ <table class="wf-table">
340
+ <thead>
341
+ <tr>
342
+ <th>Country</th>
343
+ <th>Total IPs Blocked</th>
344
+ <th>Block Count</th>
345
+ </tr>
346
+ </thead>
347
+ <tbody>
348
+ <?php foreach ($top_countries_blocked as $row): ?>
349
+ <tr class="<?php echo wfHelperString::cycle('odd', 'even') ?>">
350
+ <td>
351
+ <?php if ($row->countryCode): ?>
352
+ <img src="http://www.wordfence.com/images/flags/<?php echo strtolower($row->countryCode) ?>.png" class="wfFlag" height="11" width="16">
353
+ &nbsp;
354
+ <?php echo esc_html($row->countryCode) ?>
355
+ <?php else: ?>
356
+ (Unknown)
357
+ <?php endif ?>
358
+ </td>
359
+ <td><?php echo esc_html($row->totalIPs) ?></td>
360
+ <td><?php echo (int)$row->totalBlockCount ?></td>
361
+ </tr>
362
+ <?php endforeach ?>
363
+ </tbody>
364
+ </table>
365
+
366
+ <p>
367
+ <a class="button" href="<?php echo admin_url('admin.php?page=WordfenceCountryBlocking') ?>">Update Blocked Countries</a>
368
+ </p>
369
+
370
+ <?php wfHelperString::cycle(); ?>
371
+
372
+ <h2>Top 10 Failed Logins</h2>
373
+
374
+ <table class="wf-table">
375
+ <thead>
376
+ <tr>
377
+ <th>Username</th>
378
+ <th>Login Attempts</th>
379
+ <th>Existing User</th>
380
+ </tr>
381
+ </thead>
382
+ <tbody>
383
+ <?php foreach ($top_failed_logins as $row): ?>
384
+ <tr class="<?php echo wfHelperString::cycle('odd', 'even') ?>">
385
+ <td><?php echo esc_html($row->username) ?></td>
386
+ <td><?php echo esc_html($row->fail_count) ?></td>
387
+ <td class="<?php echo sanitize_html_class($row->action) ?>"><?php echo $row->action == 'loginFailValidUsername' ? 'Yes' : 'No' ?></td>
388
+ </tr>
389
+ <?php endforeach ?>
390
+ </tbody>
391
+ </table>
392
+
393
+ <p>
394
+ <a class="button" href="<?php echo admin_url('admin.php?page=WordfenceSecOpt#wfMarkerLoginSecurity') ?>">Update Login Security Options</a>
395
+ </p>
396
+
397
+ <?php wfHelperString::cycle(); ?>
398
+
399
+ <h2>Recently Modified Files</h2>
400
+
401
+ <table class="wf-table">
402
+ <thead>
403
+ <tr>
404
+ <th>Modified</th>
405
+ <th>File</th>
406
+ </tr>
407
+ </thead>
408
+ <tbody>
409
+ <?php foreach ($recently_modified_files as $file_row):
410
+ list($file, $mod_time) = $file_row;
411
+ ?>
412
+ <tr class="<?php echo wfHelperString::cycle('odd', 'even') ?>">
413
+ <td style="white-space: nowrap;"><?php echo $this->modTime($mod_time) ?></td>
414
+ <td>
415
+ <pre class="display-file"><?php echo esc_html($this->displayFile($file)) ?></pre>
416
+ </td>
417
+ </tr>
418
+ <?php endforeach ?>
419
+ </tbody>
420
+ </table>
421
+
422
+ <?php wfHelperString::cycle(); ?>
423
+
424
+ <h2>Updates Needed</h2>
425
+
426
+ <?php if ($updates_needed['core']): ?>
427
+ <h4>Core</h4>
428
+ <ul>
429
+ <li>A new version of WordPress (v<?php echo esc_html($updates_needed['core']) ?>) is available.</li>
430
+ </ul>
431
+ <?php endif ?>
432
+ <?php if ($updates_needed['plugins']): ?>
433
+ <h4>Plugins</h4>
434
+ <ul>
435
+ <?php foreach ($updates_needed['plugins'] as $plugin): ?>
436
+ <li>
437
+ A new version of the plugin "<?php echo esc_html("{$plugin['Name']} (v{$plugin['newVersion']})") ?>" is available.
438
+ </li>
439
+ <?php endforeach ?>
440
+ </ul>
441
+ <?php endif ?>
442
+ <?php if ($updates_needed['themes']): ?>
443
+ <h4>Themes</h4>
444
+ <ul>
445
+ <?php foreach ($updates_needed['themes'] as $theme): ?>
446
+ <li>
447
+ A new version of the theme "<?php echo esc_html("{$theme['name']} (v{$theme['newVersion']})") ?>" is available.
448
+ </li>
449
+ <?php endforeach ?>
450
+ </ul>
451
+ <?php endif ?>
452
+
453
+ <?php if ($updates_needed['core'] || $updates_needed['plugins'] || $updates_needed['themes']): ?>
454
+ <p><a class="button" href="<?php echo esc_attr(admin_url('update-core.php')) ?>">Update Now</a></p>
455
+ <?php endif ?>
456
+
457
+ <!-- <p>Generated in --><?php //printf('%.4f seconds', $microseconds) ?><!--</p>-->
458
+
459
+ </div>
460
+ </td>
461
+ </tr>
462
+ </table>
463
+ <!-- End of wrapper table -->
464
+ </body>
465
+ </html>
views/reports/activity-report.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var wfActivityReportView $this
4
+ */
5
+ ?>
6
+ <a href="http://www.wordfence.com/"><img src="http://www.wordfence.com/wp-content/themes/parallelus-salutation/wfCustomHome/images/wordfenceLogo.png" alt=""/></a>
7
+
8
+ <h2>Top <?php echo (int) $limit; ?> IP's Blocked</h2>
9
+
10
+ <?php wfHelperString::cycle(); ?>
11
+
12
+ <table class="wf-table">
13
+ <thead>
14
+ <tr>
15
+ <th>IP</th>
16
+ <th>Country</th>
17
+ <th>Block Count</th>
18
+ </tr>
19
+ </thead>
20
+ <tbody>
21
+ <?php if ($top_ips_blocked): ?>
22
+ <?php foreach ($top_ips_blocked as $row): ?>
23
+ <tr class="<?php echo wfHelperString::cycle('odd', 'even') ?>">
24
+ <td><code><?php echo wfUtils::inet_ntoa($row->IP) ?></code></td>
25
+ <td>
26
+ <?php if ($row->countryCode): ?>
27
+ <img src="http://www.wordfence.com/images/flags/<?php echo esc_attr(strtolower($row->countryCode)) ?>.png" class="wfFlag" height="11" width="16">
28
+ &nbsp;
29
+ <?php echo esc_html($row->countryCode) ?>
30
+ <?php else: ?>
31
+ (Unknown)
32
+ <?php endif ?>
33
+ </td>
34
+ <td><?php echo (int) $row->blockCount ?></td>
35
+ </tr>
36
+ <?php endforeach ?>
37
+ <?php else: ?>
38
+ <tr>
39
+ <td colspan="3">
40
+ We're collecting data, please check back soon.
41
+ </td>
42
+ </tr>
43
+ <?php endif ?>
44
+ </tbody>
45
+ </table>
46
+
47
+ <p>
48
+ <a class="button button-primary" href="<?php echo admin_url('admin.php?page=WordfenceBlockedIPs') ?>">Update Blocked IPs</a>
49
+ </p>
50
+
51
+ <?php wfHelperString::cycle(); ?>
52
+
53
+ <h2>Top <?php echo (int) $limit; ?> Countries Blocked</h2>
54
+
55
+ <table class="wf-table">
56
+ <thead>
57
+ <tr>
58
+ <th>Country</th>
59
+ <th>Total IPs Blocked</th>
60
+ <th>Block Count</th>
61
+ </tr>
62
+ </thead>
63
+ <tbody>
64
+ <?php if ($top_countries_blocked): ?>
65
+ <?php foreach ($top_countries_blocked as $row): ?>
66
+ <tr class="<?php echo wfHelperString::cycle('odd', 'even') ?>">
67
+ <td>
68
+ <?php if ($row->countryCode): ?>
69
+ <img src="http://www.wordfence.com/images/flags/<?php echo strtolower($row->countryCode) ?>.png" class="wfFlag" height="11" width="16">
70
+ &nbsp;
71
+ <?php echo esc_html($row->countryCode) ?>
72
+ <?php else: ?>
73
+ (Unknown)
74
+ <?php endif ?>
75
+ </td>
76
+ <td><?php echo esc_html($row->totalIPs) ?></td>
77
+ <td><?php echo (int) $row->totalBlockCount ?></td>
78
+ </tr>
79
+ <?php endforeach ?>
80
+ <?php else: ?>
81
+ <tr>
82
+ <td colspan="3">
83
+ We're collecting data, please check back soon.
84
+ </td>
85
+ </tr>
86
+ <?php endif ?>
87
+ </tbody>
88
+ </table>
89
+
90
+ <p>
91
+ <a class="button button-primary" href="<?php echo admin_url('admin.php?page=WordfenceCountryBlocking') ?>">Update Blocked Countries</a>
92
+ </p>
93
+
94
+ <?php wfHelperString::cycle(); ?>
95
+
96
+ <h2>Top <?php echo (int) $limit; ?> Failed Logins</h2>
97
+
98
+ <table class="wf-table">
99
+ <thead>
100
+ <tr>
101
+ <th>Username</th>
102
+ <th>Login Attempts</th>
103
+ <th>Existing User</th>
104
+ </tr>
105
+ </thead>
106
+ <tbody>
107
+ <?php if ($top_failed_logins): ?>
108
+ <?php foreach ($top_failed_logins as $row): ?>
109
+ <tr class="<?php echo wfHelperString::cycle('odd', 'even') ?>">
110
+ <td><?php echo esc_html($row->username) ?></td>
111
+ <td><?php echo esc_html($row->fail_count) ?></td>
112
+ <td class="<?php echo sanitize_html_class($row->action) ?>"><?php echo $row->action == 'loginFailValidUsername' ? 'Yes' : 'No' ?></td>
113
+ </tr>
114
+ <?php endforeach ?>
115
+ <?php else: ?>
116
+ <tr>
117
+ <td colspan="3">
118
+ We're collecting data, please check back soon.
119
+ </td>
120
+ </tr>
121
+ <?php endif ?>
122
+ </tbody>
123
+ </table>
124
+
125
+ <p>
126
+ <a class="button button-primary" href="<?php echo admin_url('admin.php?page=WordfenceSecOpt#wfMarkerLoginSecurity') ?>">Update Login Security Options</a>
127
+ </p>
128
+
129
+ <?php wfHelperString::cycle(); ?>
130
+
131
+ <?php /*?>
132
+ <h2>Recently Modified Files</h2>
133
+
134
+ <table class="activity-table recently-modified-files">
135
+ <thead>
136
+ <tr>
137
+ <th>Modified</th>
138
+ <th>File</th>
139
+ </tr>
140
+ </thead>
141
+ <tbody>
142
+ <?php foreach ($recently_modified_files as $file_row):
143
+ list($file, $mod_time) = $file_row;
144
+ ?>
145
+ <tr class="<?php echo wfHelperString::cycle('odd', 'even') ?>">
146
+ <td style="white-space: nowrap;"><?php echo $this->modTime($mod_time) ?></td>
147
+ <td class="display-file-table-cell">
148
+ <pre class="display-file"><?php echo esc_html($this->displayFile($file)) ?></pre>
149
+ </td>
150
+ </tr>
151
+ <?php endforeach ?>
152
+ </tbody>
153
+ </table>
154
+ <?php */ ?>
155
+
156
+
157
+ <?php wfHelperString::cycle(); ?>
158
+
159
+ <h2>Updates Needed</h2>
160
+
161
+ <?php if ($updates_needed['core']): ?>
162
+ <h4>Core</h4>
163
+ <ul>
164
+ <li>A new version of WordPress (v<?php echo esc_html($updates_needed['core']) ?>) is available.</li>
165
+ </ul>
166
+ <?php endif ?>
167
+ <?php if ($updates_needed['plugins']): ?>
168
+ <h4>Plugins</h4>
169
+ <ul>
170
+ <?php foreach ($updates_needed['plugins'] as $plugin): ?>
171
+ <li>
172
+ A new version of the plugin "<?php echo esc_html("{$plugin['Name']} (v{$plugin['newVersion']})") ?>" is available.
173
+ </li>
174
+ <?php endforeach ?>
175
+ </ul>
176
+ <?php endif ?>
177
+ <?php if ($updates_needed['themes']): ?>
178
+ <h4>Themes</h4>
179
+ <ul>
180
+ <?php foreach ($updates_needed['themes'] as $theme): ?>
181
+ <li>
182
+ A new version of the theme "<?php echo esc_html("{$theme['name']} (v{$theme['newVersion']})") ?>" is available.
183
+ </li>
184
+ <?php endforeach ?>
185
+ </ul>
186
+ <?php endif ?>
187
+
188
+ <?php if ($updates_needed['core'] || $updates_needed['plugins'] || $updates_needed['themes']): ?>
189
+ <p><a class="button button-primary" href="<?php echo esc_attr(admin_url('update-core.php')) ?>">Update Now</a></p>
190
+ <?php else: ?>
191
+ <p>No updates are available at this time.</p>
192
+ <?php endif ?>
193
+ <?php if ((defined('WP_DEBUG') && WP_DEBUG) || wfConfig::get('debugOn')): ?>
194
+ <p>Generated in <?php printf('%.4f seconds', $microseconds) ?></p>
195
+ <?php endif ?>
wordfence.php CHANGED
@@ -4,13 +4,13 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
- Version: 5.3.8
8
  Author URI: http://www.wordfence.com/
9
  */
10
  if(defined('WP_INSTALLING') && WP_INSTALLING){
11
  return;
12
  }
13
- define('WORDFENCE_VERSION', '5.3.8');
14
  if(get_option('wordfenceActivated') != 1){
15
  add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
16
  }
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache
6
  Author: Wordfence
7
+ Version: 5.3.9
8
  Author URI: http://www.wordfence.com/
9
  */
10
  if(defined('WP_INSTALLING') && WP_INSTALLING){
11
  return;
12
  }
13
+ define('WORDFENCE_VERSION', '5.3.9');
14
  if(get_option('wordfenceActivated') != 1){
15
  add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
16
  }