PluginProbe ʕ •ᴥ•ʔ
Wordfence Security – Firewall, Malware Scan, and Login Security / 6.0.2
Wordfence Security – Firewall, Malware Scan, and Login Security v6.0.2
8.2.2 8.2.1 8.2.0 3.7.1 3.7.2 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.8.8 3.8.9 3.9.1 4.0.1 4.0.2 4.0.3 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.0.9 5.1.1 5.1.2 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.2.1 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.1 5.3.10 5.3.11 5.3.12 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.3.8 5.3.9 6.0.1 6.0.10 6.0.11 6.0.12 6.0.14 6.0.15 6.0.16 6.0.17 6.0.18 6.0.19 6.0.2 6.0.20 6.0.21 6.0.22 6.0.23 6.0.24 6.0.25 6.0.3 6.0.4 6.0.5 6.0.6 6.0.7 6.0.8 6.0.9 6.1.1 6.1.10 6.1.11 6.1.12 6.1.14 6.1.15 6.1.16 6.1.17 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7 6.1.8 6.1.9 6.2.0 6.2.1 6.2.10 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.7 6.2.8 6.2.9 6.3.0 6.3.1 6.3.10 6.3.11 6.3.12 6.3.14 6.3.15 6.3.16 6.3.17 6.3.18 6.3.19 6.3.2 6.3.20 6.3.21 6.3.22 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.3.8 6.3.9 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1.0 7.1.1 7.1.10 7.1.11 7.1.12 7.1.14 7.1.15 7.1.16 7.1.17 7.1.18 7.1.19 7.1.2 7.1.20 7.1.3 7.1.4 7.1.5 7.1.6 7.1.7 7.1.8 7.1.9 7.10.0 7.10.1 7.10.2 7.10.3 7.10.4 7.10.5 7.10.6 7.10.7 7.11.0 7.11.1 7.11.2 7.11.3 7.11.4 7.11.5 7.11.6 7.11.7 7.2.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.4.0 7.4.1 7.4.10 7.4.11 7.4.12 7.4.14 7.4.2 7.4.3 trunk 7.4.4 1.1 7.4.5 1.2 7.4.6 1.3 7.4.7 1.3.1 7.4.8 1.3.2 7.4.9 1.3.3 7.5.0 1.4.2 7.5.1 1.4.3 7.5.10 1.4.4 7.5.11 1.4.5 7.5.2 1.4.6 7.5.3 1.4.7 7.5.4 1.4.8 7.5.5 1.5.1 7.5.6 1.5.2 7.5.7 1.5.3 7.5.8 1.5.4 7.5.9 1.5.5 7.6.0 1.5.6 7.6.1 2.0.1 7.6.2 2.0.2 7.7.0 2.0.3 7.7.1 2.0.5 7.8.0 2.0.6 7.8.1 2.0.7 7.8.2 2.1.0 7.9.0 2.1.1 7.9.1 2.1.2 7.9.2 2.1.3 7.9.3 2.1.4 8.0.0 2.1.5 8.0.1 3.0.2 8.0.2 3.0.3 8.0.3 3.0.4 8.0.4 3.0.5 8.0.5 3.0.6 8.1.0 3.0.7 8.1.1 3.0.8 8.1.2 3.0.9 8.1.3 3.1.0 8.1.4 3.1.1 v1.4.1 3.1.2 3.1.4 3.1.6 3.2.1 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.1 3.4.4 3.4.5 3.5.1 3.5.2 3.6.1 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9
wordfence / js / admin.js
wordfence / js Last commit date
admin.js 11 years ago jquery.colorbox-min.js 14 years ago jquery.dataTables.min.js 14 years ago jquery.tmpl.min.js 14 years ago jquery.tools.min.js 14 years ago perf.js 12 years ago tourTip.js 11 years ago
admin.js
2043 lines
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 _windowHasFocus: true,
42 init: function() {
43 this.nonce = WordfenceAdminVars.firstNonce;
44 this.debugOn = WordfenceAdminVars.debugOn == '1' ? true : false;
45 this.tourClosed = WordfenceAdminVars.tourClosed == '1' ? true : false;
46 this.welcomeClosed = WordfenceAdminVars.welcomeClosed == '1' ? true : false;
47 var startTicker = false;
48 var self = this;
49
50 $(window).on('blur', function() {
51 self._windowHasFocus = false;
52 }).on('focus', function() {
53 self._windowHasFocus = true;
54 }).focus();
55
56 $(document).focus();
57
58 if (jQuery('#wordfenceMode_scan').length > 0) {
59 this.mode = 'scan';
60 jQuery('#wfALogViewLink').prop('href', WordfenceAdminVars.siteBaseURL + '?_wfsf=viewActivityLog&nonce=' + this.nonce);
61 jQuery('#consoleActivity').scrollTop(jQuery('#consoleActivity').prop('scrollHeight'));
62 jQuery('#consoleScan').scrollTop(jQuery('#consoleScan').prop('scrollHeight'));
63 this.noScanHTML = jQuery('#wfNoScanYetTmpl').tmpl().html();
64 this.loadIssues();
65 this.startActivityLogUpdates();
66 if (this.needTour()) {
67 this.scanTourStart();
68 }
69 } else if (jQuery('#wordfenceMode_activity').length > 0) {
70 this.mode = 'activity';
71 this.setupSwitches('wfLiveTrafficOnOff', 'liveTrafficEnabled', function() {
72 });
73 jQuery('#wfLiveTrafficOnOff').change(function() {
74 if (/^(?:falcon|php)$/.test(WordfenceAdminVars.cacheType)) {
75 jQuery('#wfLiveTrafficOnOff').attr('checked', false);
76 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.");
77 } else {
78 self.updateSwitch('wfLiveTrafficOnOff', 'liveTrafficEnabled', function() {
79 window.location.reload(true);
80 });
81 }
82 });
83
84 if (WordfenceAdminVars.liveTrafficEnabled) {
85 this.activityMode = 'hit';
86 } else {
87 this.activityMode = 'loginLogout';
88 this.switchTab(jQuery('#wfLoginLogoutTab'), 'wfTab1', 'wfDataPanel', 'wfActivity_loginLogout', function() {
89 WFAD.activityTabChanged();
90 });
91 }
92 startTicker = true;
93 if (this.needTour()) {
94 this.tour('wfWelcomeContent3', 'wfHeading', 'top', 'left', "Learn about Site Performance", function() {
95 self.tourRedir('WordfenceSitePerf');
96 });
97 }
98 } else if (jQuery('#wordfenceMode_options').length > 0) {
99 this.mode = 'options';
100 jQuery('.wfConfigElem').change(function() {
101 jQuery('#securityLevel').val('CUSTOM');
102 });
103 this.updateTicker(true);
104 startTicker = true;
105 if (this.needTour()) {
106 this.tour('wfContentBasicOptions', 'wfMarkerBasicOptions', 'top', 'left', "Learn about Live Traffic Options", function() {
107 self.tour('wfContentLiveTrafficOptions', 'wfMarkerLiveTrafficOptions', 'bottom', 'left', "Learn about Scanning Options", function() {
108 self.tour('wfContentScansToInclude', 'wfMarkerScansToInclude', 'bottom', 'left', "Learn about Firewall Rules", function() {
109 self.tour('wfContentFirewallRules', 'wfMarkerFirewallRules', 'bottom', 'left', "Learn about Login Security", function() {
110 self.tour('wfContentLoginSecurity', 'wfMarkerLoginSecurity', 'bottom', 'left', "Learn about Other Options", function() {
111 self.tour('wfContentOtherOptions', 'wfMarkerOtherOptions', 'bottom', 'left', false, false);
112 });
113 });
114 });
115 });
116 });
117 }
118 } else if (jQuery('#wordfenceMode_blockedIPs').length > 0) {
119 this.mode = 'blocked';
120 this.staticTabChanged();
121 this.updateTicker(true);
122 startTicker = true;
123 if (this.needTour()) {
124 this.tour('wfWelcomeContent4', 'wfHeading', 'top', 'left', "Learn about Auditing Passwords", function() {
125 self.tourRedir('WordfencePasswdAudit');
126 });
127 }
128 } else if (jQuery('#wordfenceMode_passwd').length > 0) {
129 this.mode = 'passwd';
130 startTicker = false;
131 this.doPasswdAuditUpdate();
132 if (this.needTour()) {
133 this.tour('wfWelcomePasswd', 'wfHeading', 'top', 'left', "Learn about Cellphone Sign-in", function() {
134 self.tourRedir('WordfenceTwoFactor');
135 });
136 }
137 } else if (jQuery('#wordfenceMode_twoFactor').length > 0) {
138 this.mode = 'twoFactor';
139 startTicker = false;
140 if (this.needTour()) {
141 this.tour('wfWelcomeTwoFactor', 'wfHeading', 'top', 'left', "Learn how to Block Countries", function() {
142 self.tourRedir('WordfenceCountryBlocking');
143 });
144 }
145 this.loadTwoFactor();
146
147 } else if (jQuery('#wordfenceMode_countryBlocking').length > 0) {
148 this.mode = 'countryBlocking';
149 startTicker = false;
150 if (this.needTour()) {
151 this.tour('wfWelcomeContentCntBlk', 'wfHeading', 'top', 'left', "Learn how to Schedule Scans", function() {
152 self.tourRedir('WordfenceScanSchedule');
153 });
154 }
155 } else if (jQuery('#wordfenceMode_rangeBlocking').length > 0) {
156 this.mode = 'rangeBlocking';
157 startTicker = false;
158 if (this.needTour()) {
159 this.tour('wfWelcomeContentRangeBlocking', 'wfHeading', 'top', 'left', "Learn how to Customize Wordfence", function() {
160 self.tourRedir('WordfenceSecOpt');
161 });
162 }
163 this.calcRangeTotal();
164 this.loadBlockRanges();
165 } else if (jQuery('#wordfenceMode_whois').length > 0) {
166 this.mode = 'whois';
167 startTicker = false;
168 if (this.needTour()) {
169 this.tour('wfWelcomeContentWhois', 'wfHeading', 'top', 'left', "Learn how to use Advanced Blocking", function() {
170 self.tourRedir('WordfenceRangeBlocking');
171 });
172 }
173 this.calcRangeTotal();
174 this.loadBlockRanges();
175
176 } else if (jQuery('#wordfenceMode_scanScheduling').length > 0) {
177 this.mode = 'scanScheduling';
178 startTicker = false;
179 this.sched_modeChange();
180 if (this.needTour()) {
181 this.tour('wfWelcomeContentScanSched', 'wfHeading', 'top', 'left', "Learn about WHOIS", function() {
182 self.tourRedir('WordfenceWhois');
183 });
184 }
185 } else if (jQuery('#wordfenceMode_caching').length > 0) {
186 this.mode = 'caching';
187 startTicker = false;
188 if (this.needTour()) {
189 this.tour('wfWelcomeContentCaching', 'wfHeading', 'top', 'left', "Learn about IP Blocking", function() {
190 self.tourRedir('WordfenceBlockedIPs');
191 });
192 }
193 this.loadCacheExclusions();
194 } else {
195 this.mode = false;
196 }
197 if (this.mode) { //We are in a Wordfence page
198 if (startTicker) {
199 this.updateTicker();
200 this.liveInt = setInterval(function() {
201 self.updateTicker();
202 }, WordfenceAdminVars.actUpdateInterval);
203 }
204 jQuery(document).bind('cbox_closed', function() {
205 self.colorboxIsOpen = false;
206 self.colorboxServiceQueue();
207 });
208 }
209 },
210 needTour: function() {
211 if ((!this.tourClosed) && this.welcomeClosed) {
212 return true;
213 } else {
214 return false;
215 }
216 },
217 sendTestEmail: function(email) {
218 var self = this;
219 this.ajax('wordfence_sendTestEmail', {email: email}, function(res) {
220 if (res.result) {
221 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: " +
222 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.");
223 }
224 });
225 },
226 loadAvgSitePerf: function() {
227 var self = this;
228 this.ajax('wordfence_loadAvgSitePerf', {limit: jQuery('#wfAvgPerfNum').val()}, function(res) {
229 res['scale'] = self.performanceScale;
230 res['min'] = self.performanceMinWidth;
231 jQuery('#wfAvgSitePerfContent').empty();
232 var newElem = jQuery('#wfAvgPerfTmpl').tmpl(res);
233 newElem.prependTo('#wfAvgSitePerfContent').fadeIn();
234 });
235 },
236 updateSwitch: function(elemID, configItem, cb) {
237 var setting = jQuery('#' + elemID).is(':checked');
238 this.updateConfig(configItem, jQuery('#' + elemID).is(':checked') ? 1 : 0, cb);
239 },
240 setupSwitches: function(elemID, configItem, cb) {
241 jQuery('.wfOnOffSwitch-checkbox').change(function() {
242 jQuery.data(this, 'lastSwitchChange', (new Date()).getTime());
243 });
244 var self = this;
245 jQuery('div.wfOnOffSwitch').mouseup(function() {
246 var elem = jQuery(this);
247 setTimeout(function() {
248 var checkedElem = elem.find('.wfOnOffSwitch-checkbox');
249 if ((new Date()).getTime() - jQuery.data(checkedElem[0], 'lastSwitchChange') > 300) {
250 checkedElem.prop('checked', !checkedElem.is(':checked'));
251 self.updateSwitch(elemID, configItem, cb);
252 }
253 }, 50);
254 });
255 },
256 scanTourStart: function() {
257 var self = this;
258 this.tour('wfWelcomeContent1', 'wfHeading', 'top', 'left', "Continue the Tour", function() {
259 self.tour('wfWelcomeContent2', 'wfHeading', 'top', 'left', "Learn how to use Wordfence", function() {
260 self.tour('wfWelcomeContent3', 'wfHeading', 'top', 'left', "Learn about Live Traffic", function() {
261 self.tourRedir('WordfenceActivity');
262 });
263 });
264 });
265 },
266 tourRedir: function(menuItem) {
267 window.location.href = 'admin.php?page=' + menuItem;
268 },
269 updateConfig: function(key, val, cb) {
270 this.ajax('wordfence_updateConfig', {key: key, val: val}, function() {
271 cb();
272 });
273 },
274 tourFinish: function() {
275 this.ajax('wordfence_tourClosed', {}, function(res) {
276 });
277 },
278 downgradeLicense: function() {
279 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 />");
280 },
281 downgradeLicenseConfirm: function() {
282 jQuery.colorbox.close();
283 this.ajax('wordfence_downgradeLicense', {}, function(res) {
284 location.reload(true);
285 });
286 },
287 tour: function(contentID, elemID, edge, align, buttonLabel, buttonCallback) {
288 var self = this;
289 if (this.currentPointer) {
290 this.currentPointer.pointer('destroy');
291 this.currentPointer = false;
292 }
293 var options = {
294 buttons: function(event, t) {
295 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>');
296 buttonElem.find('#pointer-close').bind('click.pointer', function(evtObj) {
297 var evtSourceElem = evtObj.srcElement ? evtObj.srcElement : evtObj.target;
298 if (evtSourceElem.id == 'wfRateLink') {
299 return true;
300 }
301 self.tourFinish();
302 t.element.pointer('close');
303 return false;
304 });
305 return buttonElem;
306 },
307 close: function() {
308 },
309 content: jQuery('#' + contentID).tmpl().html(),
310 pointerWidth: 400,
311 position: {
312 edge: edge,
313 align: align
314 }
315 };
316 this.currentPointer = jQuery('#' + elemID).pointer(options).pointer('open');
317 if (buttonLabel && buttonCallback) {
318 jQuery('#pointer-close').after('<a id="pointer-primary" class="button-primary">' + buttonLabel + '</a>');
319 jQuery('#pointer-primary').click(buttonCallback);
320 }
321 },
322 startTourAgain: function() {
323 var self = this;
324 this.ajax('wordfence_startTourAgain', {}, function(res) {
325 self.tourClosed = false;
326 self.scanTourStart();
327 });
328 },
329 showLoading: function() {
330 this.loadingCount++;
331 if (this.loadingCount == 1) {
332 jQuery('<div id="wordfenceWorking">Wordfence is working...</div>').appendTo('body');
333 }
334 },
335 removeLoading: function() {
336 this.loadingCount--;
337 if (this.loadingCount == 0) {
338 jQuery('#wordfenceWorking').remove();
339 }
340 },
341 startActivityLogUpdates: function() {
342 var self = this;
343 setInterval(function() {
344 self.updateActivityLog();
345 }, parseInt(WordfenceAdminVars.actUpdateInterval));
346 },
347 updateActivityLog: function() {
348 if (this.activityLogUpdatePending) {
349 return;
350 }
351 this.activityLogUpdatePending = true;
352 var self = this;
353 this.ajax('wordfence_activityLogUpdate', {
354 lastctime: this.lastALogCtime
355 }, function(res) {
356 self.doneUpdateActivityLog(res);
357 }, function() {
358 self.activityLogUpdatePending = false;
359 }, true);
360
361 },
362 doneUpdateActivityLog: function(res) {
363 this.actNextUpdateAt = (new Date()).getTime() + parseInt(WordfenceAdminVars.actUpdateInterval);
364 if (res.ok) {
365 if (res.items.length > 0) {
366 this.activityQueue.push.apply(this.activityQueue, res.items);
367 this.lastALogCtime = res.items[res.items.length - 1].ctime;
368 this.processActQueue(res.currentScanID);
369 }
370 }
371 this.activityLogUpdatePending = false;
372 },
373 processActQueue: function(currentScanID) {
374 if (this.activityQueue.length > 0) {
375 this.addActItem(this.activityQueue.shift());
376 this.totalActAdded++;
377 if (this.totalActAdded > this.maxActivityLogItems) {
378 jQuery('#consoleActivity div:first').remove();
379 this.totalActAdded--;
380 }
381 var timeTillNextUpdate = this.actNextUpdateAt - (new Date()).getTime();
382 var maxRate = 50 / 1000; //Rate per millisecond
383 var bulkTotal = 0;
384 while (this.activityQueue.length > 0 && this.activityQueue.length / timeTillNextUpdate > maxRate) {
385 var item = this.activityQueue.shift();
386 if (item) {
387 bulkTotal++;
388 this.addActItem(item);
389 }
390 }
391 this.totalActAdded += bulkTotal;
392 if (this.totalActAdded > this.maxActivityLogItems) {
393 jQuery('#consoleActivity div:lt(' + bulkTotal + ')').remove();
394 this.totalActAdded -= bulkTotal;
395 }
396 var minDelay = 100;
397 var delay = minDelay;
398 if (timeTillNextUpdate < 1) {
399 delay = minDelay;
400 } else {
401 delay = Math.round(timeTillNextUpdate / this.activityQueue.length);
402 if (delay < minDelay) {
403 delay = minDelay;
404 }
405 }
406 var self = this;
407 setTimeout(function() {
408 self.processActQueue();
409 }, delay);
410 }
411 jQuery('#consoleActivity').scrollTop(jQuery('#consoleActivity').prop('scrollHeight'));
412 },
413 processActArray: function(arr) {
414 for (var i = 0; i < arr.length; i++) {
415 this.addActItem(arr[i]);
416 }
417 },
418 addActItem: function(item) {
419 if (!item) {
420 return;
421 }
422 if (!item.msg) {
423 return;
424 }
425 if (item.msg.indexOf('SUM_') == 0) {
426 this.processSummaryLine(item);
427 jQuery('#consoleSummary').scrollTop(jQuery('#consoleSummary').prop('scrollHeight'));
428 jQuery('#wfStartingScan').addClass('wfSummaryOK').html('Done.');
429 } else if (this.debugOn || item.level < 4) {
430
431 var html = '<div class="wfActivityLine';
432 if (this.debugOn) {
433 html += ' wf' + item.type;
434 }
435 html += '">[' + item.date + ']&nbsp;' + item.msg + '</div>';
436 jQuery('#consoleActivity').append(html);
437 if (/Scan complete\./i.test(item.msg)) {
438 this.loadIssues();
439 }
440 }
441 },
442 processSummaryLine: function(item) {
443 var msg, summaryUpdated;
444 if (item.msg.indexOf('SUM_START:') != -1) {
445 msg = item.msg.replace('SUM_START:', '');
446 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>');
447 summaryUpdated = true;
448 } else if (item.msg.indexOf('SUM_ENDBAD') != -1) {
449 msg = item.msg.replace('SUM_ENDBAD:', '');
450 jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Problems found.');
451 summaryUpdated = true;
452 } else if (item.msg.indexOf('SUM_ENDFAILED') != -1) {
453 msg = item.msg.replace('SUM_ENDFAILED:', '');
454 jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryBad').html('Failed.');
455 summaryUpdated = true;
456 } else if (item.msg.indexOf('SUM_ENDOK') != -1) {
457 msg = item.msg.replace('SUM_ENDOK:', '');
458 jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Secure.');
459 summaryUpdated = true;
460 } else if (item.msg.indexOf('SUM_ENDSUCCESS') != -1) {
461 msg = item.msg.replace('SUM_ENDSUCCESS:', '');
462 jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryOK').html('Success.');
463 summaryUpdated = true;
464 } else if (item.msg.indexOf('SUM_ENDERR') != -1) {
465 msg = item.msg.replace('SUM_ENDERR:', '');
466 jQuery('div.wfSummaryMsg:contains("' + msg + '")').next().addClass('wfSummaryErr').html('An error occurred.');
467 summaryUpdated = true;
468 } else if (item.msg.indexOf('SUM_DISABLED:') != -1) {
469 msg = item.msg.replace('SUM_DISABLED:', '');
470 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>');
471 summaryUpdated = true;
472 } else if (item.msg.indexOf('SUM_PAIDONLY:') != -1) {
473 msg = item.msg.replace('SUM_PAIDONLY:', '');
474 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>');
475 summaryUpdated = true;
476 } else if (item.msg.indexOf('SUM_FINAL:') != -1) {
477 msg = item.msg.replace('SUM_FINAL:', '');
478 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>');
479 } else if (item.msg.indexOf('SUM_PREP:') != -1) {
480 msg = item.msg.replace('SUM_PREP:', '');
481 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>');
482 } else if (item.msg.indexOf('SUM_KILLED:') != -1) {
483 msg = item.msg.replace('SUM_KILLED:', '');
484 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>');
485 }
486 },
487 processActQueueItem: function() {
488 var item = this.activityQueue.shift();
489 if (item) {
490 jQuery('#consoleActivity').append('<div class="wfActivityLine wf' + item.type + '">[' + item.date + ']&nbsp;' + item.msg + '</div>');
491 this.totalActAdded++;
492 if (this.totalActAdded > this.maxActivityLogItems) {
493 jQuery('#consoleActivity div:first').remove();
494 this.totalActAdded--;
495 }
496 if (item.msg == 'Scan complete.') {
497 this.loadIssues();
498 }
499 }
500 },
501 updateTicker: function(forceUpdate) {
502 if ((!forceUpdate) && (this.tickerUpdatePending || !this.windowHasFocus())) {
503 return;
504 }
505 this.tickerUpdatePending = true;
506 var self = this;
507 var alsoGet = '';
508 var otherParams = '';
509 if (this.mode == 'activity' && /^(?:404|hit|human|ruser|gCrawler|crawler|loginLogout)$/.test(this.activityMode)) {
510 alsoGet = 'logList_' + this.activityMode;
511 otherParams = this.newestActivityTime;
512 } else if (this.mode == 'perfStats') {
513 alsoGet = 'perfStats';
514 otherParams = this.newestActivityTime;
515 }
516 this.ajax('wordfence_ticker', {
517 alsoGet: alsoGet,
518 otherParams: otherParams
519 }, function(res) {
520 self.handleTickerReturn(res);
521 }, function() {
522 self.tickerUpdatePending = false;
523 }, true);
524 },
525 handleTickerReturn: function(res) {
526 this.tickerUpdatePending = false;
527 var newMsg = "";
528 var oldMsg = jQuery('#wfLiveStatus').text();
529 if (res.msg) {
530 newMsg = res.msg;
531 } else {
532 newMsg = "Idle";
533 }
534 if (newMsg && newMsg != oldMsg) {
535 jQuery('#wfLiveStatus').hide().html(newMsg).fadeIn(200);
536 }
537 var haveEvents, newElem;
538 if (this.mode == 'activity') {
539 if (res.alsoGet != 'logList_' + this.activityMode) {
540 return;
541 } //user switched panels since ajax request started
542 if (res.events.length > 0) {
543 this.newestActivityTime = res.events[0]['ctime'];
544 }
545 haveEvents = false;
546 if (jQuery('#wfActivity_' + this.activityMode + ' .wfActEvent').length > 0) {
547 haveEvents = true;
548 }
549 if (res.events.length > 0) {
550 if (!haveEvents) {
551 jQuery('#wfActivity_' + this.activityMode).empty();
552 }
553 for (i = res.events.length - 1; i >= 0; i--) {
554 var elemID = '#wfActEvent_' + res.events[i].id;
555 if (jQuery(elemID).length < 1) {
556 res.events[i]['activityMode'] = this.activityMode;
557 if (this.activityMode == 'loginLogout') {
558 newElem = jQuery('#wfLoginLogoutEventTmpl').tmpl(res.events[i]);
559 } else {
560 newElem = jQuery('#wfHitsEventTmpl').tmpl(res.events[i]);
561 }
562 jQuery(newElem).find('.wfTimeAgo').data('wfctime', res.events[i].ctime);
563 newElem.prependTo('#wfActivity_' + this.activityMode).fadeIn();
564 }
565 }
566 this.reverseLookupIPs();
567 } else {
568 if (!haveEvents) {
569 jQuery('#wfActivity_' + this.activityMode).html('<div>No events to report yet.</div>');
570 }
571 }
572 var self = this;
573 jQuery('.wfTimeAgo').each(function(idx, elem) {
574 jQuery(elem).html(self.makeTimeAgo(res.serverTime - jQuery(elem).data('wfctime')) + ' ago');
575 });
576 } else if (this.mode == 'perfStats') {
577 haveEvents = false;
578 if (jQuery('#wfPerfStats .wfPerfEvent').length > 0) {
579 haveEvents = true;
580 }
581 if (res.events.length > 0) {
582 if (!haveEvents) {
583 jQuery('#wfPerfStats').empty();
584 }
585 var curLength = parseInt(jQuery('#wfPerfStats').css('width'));
586 if (res.longestLine > curLength) {
587 jQuery('#wfPerfStats').css('width', (res.longestLine + 200) + 'px');
588 }
589 this.newestActivityTime = res.events[0]['ctime'];
590 for (var i = res.events.length - 1; i >= 0; i--) {
591 res.events[i]['scale'] = this.performanceScale;
592 res.events[i]['min'] = this.performanceMinWidth;
593 newElem = jQuery('#wfPerfStatTmpl').tmpl(res.events[i]);
594 jQuery(newElem).find('.wfTimeAgo').data('wfctime', res.events[i].ctime);
595 newElem.prependTo('#wfPerfStats').fadeIn();
596 }
597 } else {
598 if (!haveEvents) {
599 jQuery('#wfPerfStats').html('<p>No events to report yet.</p>');
600 }
601 }
602 jQuery('.wfTimeAgo').each(function(idx, elem) {
603 jQuery(elem).html(self.makeTimeAgo(res.serverTime - jQuery(elem).data('wfctime')) + ' ago');
604 });
605 }
606 },
607 reverseLookupIPs: function() {
608 var ips = [];
609 jQuery('.wfReverseLookup').each(function(idx, elem) {
610 var txt = jQuery(elem).text();
611 if (/^\d+\.\d+\.\d+\.\d+$/.test(txt) && (!jQuery(elem).data('wfReverseDone'))) {
612 jQuery(elem).data('wfReverseDone', true);
613 ips.push(jQuery(elem).text());
614 }
615 });
616 if (ips.length < 1) {
617 return;
618 }
619 var uni = {};
620 var uniqueIPs = [];
621 for (var i = 0; i < ips.length; i++) {
622 if (!uni[ips[i]]) {
623 uni[ips[i]] = true;
624 uniqueIPs.push(ips[i]);
625 }
626 }
627 this.ajax('wordfence_reverseLookup', {
628 ips: uniqueIPs.join(',')
629 },
630 function(res) {
631 if (res.ok) {
632 jQuery('.wfReverseLookup').each(function(idx, elem) {
633 var txt = jQuery(elem).text();
634 for (var ip in res.ips) {
635 if (txt == ip) {
636 if (res.ips[ip]) {
637 jQuery(elem).html('<strong>Hostname:</strong>&nbsp;' + res.ips[ip]);
638 } else {
639 jQuery(elem).html('');
640 }
641 }
642 }
643 });
644 }
645 }, false, false);
646 },
647 killScan: function() {
648 var self = this;
649 this.ajax('wordfence_killScan', {}, function(res) {
650 if (res.ok) {
651 self.colorbox('400px', "Kill requested", "A termination request has been sent to any running scans.");
652 } else {
653 self.colorbox('400px', "Kill failed", "We failed to send a termination request.");
654 }
655 });
656 },
657 startScan: function() {
658 var scanReqAnimation = setInterval(function() {
659 var str = jQuery('#wfStartScanButton1').prop('value');
660 var ch = str.charAt(str.length - 1);
661 if (ch == '/') {
662 ch = '-';
663 }
664 else if (ch == '-') {
665 ch = '\\';
666 }
667 else if (ch == '\\') {
668 ch = '|';
669 }
670 else if (ch == '|') {
671 ch = '/';
672 }
673 else {
674 ch = '/';
675 }
676 jQuery('#wfStartScanButton1,#wfStartScanButton2').prop('value', "Requesting a New Scan " + ch);
677 }, 100);
678 setTimeout(function(res) {
679 clearInterval(scanReqAnimation);
680 jQuery('#wfStartScanButton1,#wfStartScanButton2').prop('value', "Start a Wordfence Scan");
681 }, 3000);
682 this.ajax('wordfence_scan', {}, function(res) {
683 });
684 },
685 displayPWAuditJobs: function(res) {
686 if (res && res.results && res.results.length > 0) {
687 var wfAuditJobs = $('#wfAuditJobs');
688 jQuery('#wfAuditJobs').empty();
689 jQuery('#wfAuditJobsTable').tmpl().appendTo(wfAuditJobs);
690 var wfAuditJobsBody = wfAuditJobs.find('.wf-pw-audit-tbody');
691 for (var i = 0; i < res.results.length; i++) {
692 jQuery('#wfAuditJobsInProg').tmpl(res.results[i]).appendTo(wfAuditJobsBody);
693 }
694 } else {
695 jQuery('#wfAuditJobs').empty().html("<p>You don't have any password auditing jobs in progress or completed yet.</p>");
696 }
697 },
698 loadIssues: function(callback) {
699 if (this.mode != 'scan') {
700 return;
701 }
702 var self = this;
703 this.ajax('wordfence_loadIssues', {}, function(res) {
704 self.displayIssues(res, callback);
705 });
706 },
707 sev2num: function(str) {
708 if (/wfProbSev1/.test(str)) {
709 return 1;
710 } else if (/wfProbSev2/.test(str)) {
711 return 2;
712 } else {
713 return 0;
714 }
715 },
716 displayIssues: function(res, callback) {
717 var self = this;
718 try {
719 res.summary['lastScanCompleted'] = res['lastScanCompleted'];
720 } catch (err) {
721 res.summary['lastScanCompleted'] = 'Never';
722 }
723 jQuery('.wfIssuesContainer').hide();
724 for (var issueStatus in res.issuesLists) {
725 var containerID = 'wfIssues_dataTable_' + issueStatus;
726 var tableID = 'wfIssuesTable_' + issueStatus;
727 if (jQuery('#' + containerID).length < 1) {
728 //Invalid issue status
729 continue;
730 }
731 if (res.issuesLists[issueStatus].length < 1) {
732 if (issueStatus == 'new') {
733 if (res.lastScanCompleted == 'ok') {
734 jQuery('#' + containerID).html('<p style="font-size: 20px; color: #0A0;">Congratulations! No security problems were detected by Wordfence.</p>');
735 } else if (res['lastScanCompleted']) {
736 //jQuery('#' + containerID).html('<p style="font-size: 12px; color: #A00;">The latest scan failed: ' + res.lastScanCompleted + '</p>');
737 } else {
738 jQuery('#' + containerID).html();
739 }
740
741 } else {
742 jQuery('#' + containerID).html('<p>There are currently <strong>no issues</strong> being ignored on this site.</p>');
743 }
744 continue;
745 }
746 jQuery('#' + containerID).html('<table cellpadding="0" cellspacing="0" border="0" class="display" id="' + tableID + '"></table>');
747
748 jQuery.fn.dataTableExt.oSort['severity-asc'] = function(y, x) {
749 x = WFAD.sev2num(x);
750 y = WFAD.sev2num(y);
751 if (x < y) {
752 return 1;
753 }
754 if (x > y) {
755 return -1;
756 }
757 return 0;
758 };
759 jQuery.fn.dataTableExt.oSort['severity-desc'] = function(y, x) {
760 x = WFAD.sev2num(x);
761 y = WFAD.sev2num(y);
762 if (x > y) {
763 return 1;
764 }
765 if (x < y) {
766 return -1;
767 }
768 return 0;
769 };
770
771 jQuery('#' + tableID).dataTable({
772 "bFilter": false,
773 "bInfo": false,
774 "bPaginate": false,
775 "bLengthChange": false,
776 "bAutoWidth": false,
777 "aaData": res.issuesLists[issueStatus],
778 "aoColumns": [
779 {
780 "sTitle": '<div class="th_wrapp">Severity</div>',
781 "sWidth": '128px',
782 "sClass": "center",
783 "sType": 'severity',
784 "fnRender": function(obj) {
785 var cls = 'wfProbSev' + obj.aData.severity;
786 return '<span class="' + cls + '"></span>';
787 }
788 },
789 {
790 "sTitle": '<div class="th_wrapp">Issue</div>',
791 "bSortable": false,
792 "sWidth": '400px',
793 "sType": 'html',
794 fnRender: function(obj) {
795 var tmplName = 'issueTmpl_' + obj.aData.type;
796 return jQuery('#' + tmplName).tmpl(obj.aData).html();
797 }
798 }
799 ]
800 });
801 }
802 if (callback) {
803 jQuery('#wfIssues_' + this.visibleIssuesPanel).fadeIn(500, function() {
804 callback();
805 });
806 } else {
807 jQuery('#wfIssues_' + this.visibleIssuesPanel).fadeIn(500);
808 }
809 return true;
810 },
811 ajax: function(action, data, cb, cbErr, noLoading) {
812 if (typeof(data) == 'string') {
813 if (data.length > 0) {
814 data += '&';
815 }
816 data += 'action=' + action + '&nonce=' + this.nonce;
817 } else if (typeof(data) == 'object') {
818 data['action'] = action;
819 data['nonce'] = this.nonce;
820 }
821 if (!cbErr) {
822 cbErr = function() {
823 };
824 }
825 var self = this;
826 if (!noLoading) {
827 this.showLoading();
828 }
829 jQuery.ajax({
830 type: 'POST',
831 url: WordfenceAdminVars.ajaxURL,
832 dataType: "json",
833 data: data,
834 success: function(json) {
835 if (!noLoading) {
836 self.removeLoading();
837 }
838 if (json && json.nonce) {
839 self.nonce = json.nonce;
840 }
841 if (json && json.errorMsg) {
842 self.colorbox('400px', 'An error occurred', json.errorMsg);
843 }
844 cb(json);
845 },
846 error: function() {
847 if (!noLoading) {
848 self.removeLoading();
849 }
850 cbErr();
851 }
852 });
853 },
854 colorbox: function(width, heading, body) {
855 this.colorboxQueue.push([width, heading, body]);
856 this.colorboxServiceQueue();
857 },
858 colorboxServiceQueue: function() {
859 if (this.colorboxIsOpen) {
860 return;
861 }
862 if (this.colorboxQueue.length < 1) {
863 return;
864 }
865 var elem = this.colorboxQueue.shift();
866 this.colorboxOpen(elem[0], elem[1], elem[2]);
867 },
868 colorboxOpen: function(width, heading, body) {
869 this.colorboxIsOpen = true;
870 jQuery.colorbox({width: width, html: "<h3>" + heading + "</h3><p>" + body + "</p>"});
871 },
872 scanRunningMsg: function() {
873 this.colorbox('400px', "A scan is running", "A scan is currently in progress. Please wait until it finishes before starting another scan.");
874 },
875 errorMsg: function(msg) {
876 this.colorbox('400px', "An error occurred:", msg);
877 },
878 bulkOperation: function(op) {
879 var self = this;
880 if (op == 'del' || op == 'repair') {
881 var ids = jQuery('input.wf' + op + 'Checkbox:checked').map(function() {
882 return jQuery(this).val();
883 }).get();
884 if (ids.length < 1) {
885 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.");
886 return;
887 }
888 if (op == 'del') {
889 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 />");
890 } else if (op == 'repair') {
891 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 />");
892 }
893 } else {
894 return;
895 }
896 },
897 bulkOperationConfirmed: function(op) {
898 jQuery.colorbox.close();
899 var self = this;
900 this.ajax('wordfence_bulkOperation', {
901 op: op,
902 ids: jQuery('input.wf' + op + 'Checkbox:checked').map(function() {
903 return jQuery(this).val();
904 }).get()
905 }, function(res) {
906 self.doneBulkOperation(res);
907 });
908 },
909 doneBulkOperation: function(res) {
910 var self = this;
911 if (res.ok) {
912 this.loadIssues(function() {
913 self.colorbox('400px', res.bulkHeading, res.bulkBody);
914 });
915 } else {
916 this.loadIssues(function() {
917 });
918 }
919 },
920 deleteFile: function(issueID) {
921 var self = this;
922 this.ajax('wordfence_deleteFile', {
923 issueID: issueID
924 }, function(res) {
925 self.doneDeleteFile(res);
926 });
927 },
928 doneDeleteFile: function(res) {
929 var cb = false;
930 var self = this;
931 if (res.ok) {
932 this.loadIssues(function() {
933 self.colorbox('400px', "Success deleting file", "The file " + res.file + " was successfully deleted.");
934 });
935 } else if (res.cerrorMsg) {
936 this.loadIssues(function() {
937 self.colorbox('400px', 'An error occurred', res.cerrorMsg);
938 });
939 }
940 },
941 deleteDatabaseOption: function(issueID) {
942 var self = this;
943 this.ajax('wordfence_deleteDatabaseOption', {
944 issueID: issueID
945 }, function(res) {
946 self.doneDeleteDatabaseOption(res);
947 });
948 },
949 doneDeleteDatabaseOption: function(res) {
950 var cb = false;
951 var self = this;
952 if (res.ok) {
953 this.loadIssues(function() {
954 self.colorbox('400px', "Success removing option", "The option " + res.option_name + " was successfully removed.");
955 });
956 } else if (res.cerrorMsg) {
957 this.loadIssues(function() {
958 self.colorbox('400px', 'An error occurred', res.cerrorMsg);
959 });
960 }
961 },
962 restoreFile: function(issueID) {
963 var self = this;
964 this.ajax('wordfence_restoreFile', {
965 issueID: issueID
966 }, function(res) {
967 self.doneRestoreFile(res);
968 });
969 },
970 doneRestoreFile: function(res) {
971 var self = this;
972 if (res.ok) {
973 this.loadIssues(function() {
974 self.colorbox("400px", "File restored OK", "The file " + res.file + " was restored succesfully.");
975 });
976 } else if (res.cerrorMsg) {
977 this.loadIssues(function() {
978 self.colorbox('400px', 'An error occurred', res.cerrorMsg);
979 });
980 }
981 },
982 deleteIssue: function(id) {
983 var self = this;
984 this.ajax('wordfence_deleteIssue', {id: id}, function(res) {
985 self.loadIssues();
986 });
987 },
988 updateIssueStatus: function(id, st) {
989 var self = this;
990 this.ajax('wordfence_updateIssueStatus', {id: id, 'status': st}, function(res) {
991 if (res.ok) {
992 self.loadIssues();
993 }
994 });
995 },
996 updateAllIssues: function(op) { // deleteIgnored, deleteNew, ignoreAllNew
997 var head = "Please confirm";
998 var body;
999 if (op == 'deleteIgnored') {
1000 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?";
1001 } else if (op == 'deleteNew') {
1002 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?";
1003 } else if (op == 'ignoreAllNew') {
1004 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?";
1005 } else {
1006 return;
1007 }
1008 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 />');
1009 },
1010 confirmUpdateAllIssues: function(op) {
1011 var self = this;
1012 this.ajax('wordfence_updateAllIssues', {op: op}, function(res) {
1013 self.loadIssues();
1014 });
1015 },
1016 es: function(val) {
1017 if (val) {
1018 return val;
1019 } else {
1020 return "";
1021 }
1022 },
1023 noQuotes: function(str) {
1024 return str.replace(/"/g, '&#34;').replace(/\'/g, '&#145;');
1025 },
1026 commify: function(num) {
1027 return ("" + num).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
1028 },
1029 switchToLiveTab: function(elem) {
1030 jQuery('.wfTab1').removeClass('selected');
1031 jQuery(elem).addClass('selected');
1032 jQuery('.wfDataPanel').hide();
1033 var self = this;
1034 jQuery('#wfActivity').fadeIn(function() {
1035 self.completeLiveTabSwitch();
1036 });
1037 },
1038 completeLiveTabSwitch: function() {
1039 this.ajax('wordfence_loadActivityLog', {}, function(res) {
1040 var html = '<a href="#" class="wfALogMailLink" onclick="WFAD.emailActivityLog(); return false;"></a><a href="#" class="wfALogReloadLink" onclick="WFAD.reloadActivityData(); return false;"></a>';
1041 if (res.events && res.events.length > 0) {
1042 jQuery('#wfActivity').empty();
1043 for (var i = 0; i < res.events.length; i++) {
1044 var timeTaken = '0.0000';
1045 if (res.events[i + 1]) {
1046 timeTaken = (res.events[i].ctime - res.events[i + 1].ctime).toFixed(4);
1047 }
1048 var red = "";
1049 if (res.events[i].type == 'error') {
1050 red = ' class="wfWarn" ';
1051 }
1052 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>";
1053 }
1054 jQuery('#wfActivity').html(html);
1055 } else {
1056 jQuery('#wfActivity').html("<p>&nbsp;&nbsp;No activity to report yet. Please complete your first scan.</p>");
1057 }
1058 });
1059 },
1060 emailActivityLog: function() {
1061 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 />");
1062 },
1063 completeEmailActivityLog: function() {
1064 jQuery.colorbox.close();
1065 var email = jQuery('#wfALogRecip').val();
1066 if (!/^[^@]+@[^@]+$/.test(email)) {
1067 alert("Please enter a valid email address.");
1068 return;
1069 }
1070 var self = this;
1071 this.ajax('wordfence_sendActivityLog', {email: jQuery('#wfALogRecip').val()}, function(res) {
1072 if (res.ok) {
1073 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 />");
1074 }
1075 });
1076 },
1077 reloadActivityData: function() {
1078 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 />
1079 this.completeLiveTabSwitch();
1080 },
1081 switchToSummaryTab: function(elem) {
1082 jQuery('.wfTab1').removeClass('selected');
1083 jQuery(elem).addClass('selected');
1084 jQuery('.wfDataPanel').hide();
1085 jQuery('#wfSummaryTables').fadeIn();
1086 },
1087 switchIssuesTab: function(elem, type) {
1088 jQuery('.wfTab2').removeClass('selected');
1089 jQuery('.wfIssuesContainer').hide();
1090 jQuery(elem).addClass('selected');
1091 this.visibleIssuesPanel = type;
1092 jQuery('#wfIssues_' + type).fadeIn();
1093 },
1094 switchTab: function(tabElement, tabClass, contentClass, selectedContentID, callback) {
1095 jQuery('.' + tabClass).removeClass('selected');
1096 jQuery(tabElement).addClass('selected');
1097 jQuery('.' + contentClass).hide().html('<div class="wfLoadingWhite32"></div>');
1098 var func = function() {
1099 };
1100 if (callback) {
1101 func = function() {
1102 callback();
1103 };
1104 }
1105 jQuery('#' + selectedContentID).fadeIn(func);
1106 },
1107 activityTabChanged: function() {
1108 var mode = jQuery('.wfDataPanel:visible')[0].id.replace('wfActivity_', '');
1109 if (!mode) {
1110 return;
1111 }
1112 this.activityMode = mode;
1113 this.reloadActivities();
1114 },
1115 reloadActivities: function() {
1116 jQuery('#wfActivity_' + this.activityMode).html('<div class="wfLoadingWhite32"></div>');
1117 this.newestActivityTime = 0;
1118 this.updateTicker(true);
1119 },
1120 staticTabChanged: function() {
1121 var mode = jQuery('.wfDataPanel:visible')[0].id.replace('wfActivity_', '');
1122 if (!mode) {
1123 return;
1124 }
1125 this.activityMode = mode;
1126
1127 var self = this;
1128 this.ajax('wordfence_loadStaticPanel', {
1129 mode: this.activityMode
1130 }, function(res) {
1131 self.completeLoadStaticPanel(res);
1132 });
1133 },
1134 completeLoadStaticPanel: function(res) {
1135 var contentElem = '#wfActivity_' + this.activityMode;
1136 jQuery(contentElem).empty();
1137 if (res.results && res.results.length > 0) {
1138 var tmpl;
1139 if (this.activityMode == 'topScanners' || this.activityMode == 'topLeechers') {
1140 tmpl = '#wfLeechersTmpl';
1141 } else if (this.activityMode == 'blockedIPs') {
1142 tmpl = '#wfBlockedIPsTmpl';
1143 } else if (this.activityMode == 'lockedOutIPs') {
1144 tmpl = '#wfLockedOutIPsTmpl';
1145 } else if (this.activityMode == 'throttledIPs') {
1146 tmpl = '#wfThrottledIPsTmpl';
1147 } else {
1148 return;
1149 }
1150 var i, j, chunk = 1000;
1151 var bigArray = res.results.slice(0);
1152 res.results = false;
1153 for (i = 0, j = bigArray.length; i < j; i += chunk) {
1154 res.results = bigArray.slice(i, i + chunk);
1155 jQuery(tmpl).tmpl(res).appendTo(contentElem);
1156 }
1157 this.reverseLookupIPs();
1158 } else {
1159 if (this.activityMode == 'topScanners' || this.activityMode == 'topLeechers') {
1160 jQuery(contentElem).html("No site hits have been logged yet. Check back soon.");
1161 } else if (this.activityMode == 'blockedIPs') {
1162 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.");
1163 } else if (this.activityMode == 'lockedOutIPs') {
1164 jQuery(contentElem).html("No IP addresses have been locked out from signing in or using the password recovery system.");
1165 } else if (this.activityMode == 'throttledIPs') {
1166 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.");
1167 } else {
1168 return;
1169 }
1170 }
1171 },
1172 loadPasswdAuditResults: function() {
1173 var self = this;
1174 this.ajax('wordfence_passwdLoadResults', {}, function(res) {
1175 self.displayPWAuditResults(res);
1176 });
1177 },
1178 doPasswdAuditUpdate: function(freq) {
1179 this.loadPasswdAuditJobs();
1180 this.loadPasswdAuditResults();
1181 },
1182 stopPasswdAuditUpdate: function() {
1183 clearInterval(this.passwdAuditUpdateInt);
1184 },
1185 killPasswdAudit: function(jobID) {
1186 var self = this;
1187 this.ajax('wordfence_killPasswdAudit', {jobID: jobID}, function(res) {
1188 if (res.ok) {
1189 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.");
1190 }
1191 });
1192 },
1193 displayPWAuditResults: function(res) {
1194 if (res && res.results && res.results.length > 0) {
1195 var wfAuditResults = $('#wfAuditResults');
1196 jQuery('#wfAuditResults').empty();
1197 jQuery('#wfAuditResultsTable').tmpl().appendTo(wfAuditResults);
1198 var wfAuditResultsBody = wfAuditResults.find('.wf-pw-audit-tbody');
1199 for (var i = 0; i < res.results.length; i++) {
1200 jQuery('#wfAuditResultsRow').tmpl(res.results[i]).appendTo(wfAuditResultsBody);
1201 }
1202 } else {
1203 jQuery('#wfAuditResults').empty().html("<p>You don't have any user accounts with a weak password at this time.</p>");
1204 }
1205 },
1206 loadPasswdAuditJobs: function() {
1207 var self = this;
1208 this.ajax('wordfence_passwdLoadJobs', {}, function(res) {
1209 if (res && res.results && res.results.length > 0) {
1210 var stat = res.results[0].jobStatus;
1211 if (stat == 'running' || stat == 'queued') {
1212 setTimeout(function() {
1213 self.doPasswdAuditUpdate()
1214 }, 10000);
1215 }
1216 }
1217
1218 self.displayPWAuditJobs(res);
1219 });
1220 },
1221 deletePasswdAudit: function(jobID) {
1222 var self = this;
1223 this.ajax('wordfence_deletePasswdAudit', {jobID: jobID}, function(res) {
1224 self.loadPasswdAuditJobs(res);
1225 });
1226 },
1227 doFixWeakPasswords: function() {
1228 var self = this;
1229 var mode = jQuery('#wfPasswdFixAction').val();
1230 var ids = jQuery('input.wfUserCheck:checked').map(function() {
1231 return jQuery(this).val();
1232 }).get();
1233 if (ids.length < 1) {
1234 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.");
1235 return;
1236 }
1237 this.ajax('wordfence_weakPasswordsFix', {
1238 mode: mode,
1239 ids: ids.join(',')
1240 }, function(res) {
1241 if (res.ok && res.title && res.msg) {
1242 self.colorbox('300px', res.title, res.msg);
1243 }
1244 });
1245 },
1246 ucfirst: function(str) {
1247 str = "" + str;
1248 return str.charAt(0).toUpperCase() + str.slice(1);
1249 },
1250 makeIPTrafLink: function(IP) {
1251 return WordfenceAdminVars.siteBaseURL + '?_wfsf=IPTraf&nonce=' + this.nonce + '&IP=' + encodeURIComponent(IP);
1252 },
1253 makeDiffLink: function(dat) {
1254 return WordfenceAdminVars.siteBaseURL + '?_wfsf=diff&nonce=' + this.nonce +
1255 '&file=' + encodeURIComponent(this.es(dat['file'])) +
1256 '&cType=' + encodeURIComponent(this.es(dat['cType'])) +
1257 '&cKey=' + encodeURIComponent(this.es(dat['cKey'])) +
1258 '&cName=' + encodeURIComponent(this.es(dat['cName'])) +
1259 '&cVersion=' + encodeURIComponent(this.es(dat['cVersion']));
1260 },
1261 makeViewFileLink: function(file) {
1262 return WordfenceAdminVars.siteBaseURL + '?_wfsf=view&nonce=' + this.nonce + '&file=' + encodeURIComponent(file);
1263 },
1264 makeViewOptionLink: function(option, siteID) {
1265 return WordfenceAdminVars.siteBaseURL + '?_wfsf=viewOption&nonce=' + this.nonce + '&option=' + encodeURIComponent(option) + '&site_id=' + encodeURIComponent(siteID);
1266 },
1267 makeTimeAgo: function(t) {
1268 var months = Math.floor(t / (86400 * 30));
1269 var days = Math.floor(t / 86400);
1270 var hours = Math.floor(t / 3600);
1271 var minutes = Math.floor(t / 60);
1272 if (months > 0) {
1273 days -= months * 30;
1274 return this.pluralize(months, 'month', days, 'day');
1275 } else if (days > 0) {
1276 hours -= days * 24;
1277 return this.pluralize(days, 'day', hours, 'hour');
1278 } else if (hours > 0) {
1279 minutes -= hours * 60;
1280 return this.pluralize(hours, 'hour', minutes, 'min');
1281 } else if (minutes > 0) {
1282 //t -= minutes * 60;
1283 return this.pluralize(minutes, 'minute');
1284 } else {
1285 return Math.round(t) + " seconds";
1286 }
1287 },
1288 pluralize: function(m1, t1, m2, t2) {
1289 if (m1 != 1) {
1290 t1 = t1 + 's';
1291 }
1292 if (m2 != 1) {
1293 t2 = t2 + 's';
1294 }
1295 if (m1 && m2) {
1296 return m1 + ' ' + t1 + ' ' + m2 + ' ' + t2;
1297 } else {
1298 return m1 + ' ' + t1;
1299 }
1300 },
1301 calcRangeTotal: function() {
1302 var range = jQuery('#ipRange').val();
1303 if (!range) {
1304 return;
1305 }
1306 range = range.replace(/ /g, '');
1307 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)) {
1308 var ips = range.split('-');
1309 var total = this.inet_aton(ips[1]) - this.inet_aton(ips[0]) + 1;
1310 if (total < 1) {
1311 jQuery('#wfShowRangeTotal').html("<span style=\"color: #F00;\">Invalid. Starting IP is greater than ending IP.</span>");
1312 return;
1313 }
1314 jQuery('#wfShowRangeTotal').html("<span style=\"color: #0A0;\">Valid: " + total + " addresses in range.</span>");
1315 } else {
1316 jQuery('#wfShowRangeTotal').empty();
1317 }
1318 },
1319 loadBlockRanges: function() {
1320 var self = this;
1321 this.ajax('wordfence_loadBlockRanges', {}, function(res) {
1322 self.completeLoadBlockRanges(res);
1323 });
1324
1325 },
1326 completeLoadBlockRanges: function(res) {
1327 jQuery('#currentBlocks').empty();
1328 if (res.results && res.results.length > 0) {
1329 jQuery('#wfBlockedRangesTmpl').tmpl(res).prependTo('#currentBlocks');
1330 } else {
1331 jQuery('#currentBlocks').html("You have not blocked any IP ranges or other patterns yet.");
1332 }
1333 },
1334 whois: function(val) {
1335 val = val.replace(' ', '');
1336 if (!/\w+/.test(val)) {
1337 this.colorbox('300px', "Enter a valid IP or domain", "Please enter a valid IP address or domain name for your whois lookup.");
1338 return;
1339 }
1340 var self = this;
1341 jQuery('#whoisbutton').attr('disabled', 'disabled');
1342 jQuery('#whoisbutton').attr('value', 'Loading...');
1343 this.ajax('wordfence_whois', {
1344 val: val
1345 }, function(res) {
1346 jQuery('#whoisbutton').removeAttr('disabled');
1347 jQuery('#whoisbutton').attr('value', 'Look up IP or Domain');
1348 if (res.ok) {
1349 self.completeWhois(res);
1350 }
1351 });
1352 },
1353 completeWhois: function(res) {
1354 if (res.ok && res.result && res.result.rawdata && res.result.rawdata.length > 0) {
1355 var rawhtml = "";
1356 for (var i = 0; i < res.result.rawdata.length; i++) {
1357 res.result.rawdata[i] = jQuery('<div />').text(res.result.rawdata[i]).html();
1358 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>");
1359 res.result.rawdata[i] = res.result.rawdata[i].replace(/(https?:\/\/[^\/]+[^\s\r\n\t]+)/, "<a target=\"_blank\" href=\"$1\">$1<\/a>");
1360 var redStyle = "";
1361 if (this.getQueryParam('wfnetworkblock')) {
1362 redStyle = " style=\"color: #F00;\"";
1363 }
1364 var self = this;
1365
1366 function wfm21(str, ipRange, offset, totalStr) {
1367 var ips = ipRange.split(/\s*\-\s*/);
1368 var totalIPs = NaN;
1369 if (ips[0].indexOf(':') < 0) {
1370 var ip1num = self.inet_aton(ips[0]);
1371 var ip2num = self.inet_aton(ips[1]);
1372 totalIPs = ip2num - ip1num + 1;
1373 }
1374 return "<a href=\"admin.php?page=WordfenceRangeBlocking&wfBlockRange=" + ipRange + "\"" + redStyle + ">" + ipRange + " [" + (!isNaN(totalIPs) ? "<strong>" + totalIPs + "</strong> addresses in this network." : "") + "Click to block this network]<\/a>";
1375 }
1376
1377 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}|[a-f0-9:.]{3,} - [a-f0-9:.]{3,})/i, wfm21);
1378 rawhtml += res.result.rawdata[i] + "<br />";
1379 }
1380 jQuery('#wfrawhtml').html(rawhtml);
1381 } else {
1382 jQuery('#wfrawhtml').html('<span style="color: #F00;">Sorry, but no data for that IP or domain was found.</span>');
1383 }
1384 },
1385 blockIPUARange: function(ipRange, uaRange, referer, reason) {
1386 if (!/\w+/.test(reason)) {
1387 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.");
1388 return;
1389 }
1390 ipRange = ipRange.replace(/ /g, '').toLowerCase();
1391 if (ipRange) {
1392 var range = ipRange.split('-'),
1393 validRange = false;
1394 if (range[0].match(':')) {
1395 validRange = this.inet_pton(range[0]) !== false && this.inet_pton(range[1]) !== false;
1396 } else if (range[0].match('.')) {
1397 validRange = this.inet_aton(range[0]) !== false && this.inet_aton(range[1]) !== false;
1398 }
1399 if (!validRange) {
1400 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.");
1401 return;
1402 }
1403 }
1404 if (!(/\w+/.test(ipRange) || /\w+/.test(uaRange) || /\w+/.test(referer))) {
1405 this.colorbox('300px', 'Specify an IP range or Browser pattern', "Please specify either an IP address range or a web browser pattern to match.");
1406 return;
1407 }
1408 var self = this;
1409 this.ajax('wordfence_blockIPUARange', {
1410 ipRange: ipRange,
1411 uaRange: uaRange,
1412 referer: referer,
1413 reason: reason
1414 }, function(res) {
1415 if (res.ok) {
1416 self.loadBlockRanges();
1417 return;
1418 }
1419 });
1420 },
1421 unblockRange: function(id) {
1422 var self = this;
1423 this.ajax('wordfence_unblockRange', {
1424 id: id
1425 }, function(res) {
1426 self.loadBlockRanges();
1427 });
1428 },
1429 blockIP: function(IP, reason) {
1430 var self = this;
1431 this.ajax('wordfence_blockIP', {
1432 IP: IP,
1433 reason: reason
1434 }, function(res) {
1435 if (res.errorMsg) {
1436 return;
1437 } else {
1438 self.reloadActivities();
1439 }
1440 });
1441 },
1442 blockIPTwo: function(IP, reason, perm) {
1443 var self = this;
1444 this.ajax('wordfence_blockIP', {
1445 IP: IP,
1446 reason: reason,
1447 perm: (perm ? '1' : '0')
1448 }, function(res) {
1449 if (res.errorMsg) {
1450 return;
1451 } else {
1452 self.staticTabChanged();
1453 }
1454 });
1455 },
1456 unlockOutIP: function(IP) {
1457 var self = this;
1458 this.ajax('wordfence_unlockOutIP', {
1459 IP: IP
1460 }, function(res) {
1461 self.staticTabChanged();
1462 });
1463 },
1464 unblockIP: function(IP) {
1465 var self = this;
1466 this.ajax('wordfence_unblockIP', {
1467 IP: IP
1468 }, function(res) {
1469 self.reloadActivities();
1470 });
1471 },
1472 unblockIPTwo: function(IP) {
1473 var self = this;
1474 this.ajax('wordfence_unblockIP', {
1475 IP: IP
1476 }, function(res) {
1477 self.staticTabChanged();
1478 });
1479 },
1480 permBlockIP: function(IP) {
1481 var self = this;
1482 this.ajax('wordfence_permBlockIP', {
1483 IP: IP
1484 }, function(res) {
1485 self.staticTabChanged();
1486 });
1487 },
1488 makeElemID: function() {
1489 return 'wfElemGen' + this.elementGeneratorIter++;
1490 },
1491 pulse: function(sel) {
1492 jQuery(sel).fadeIn(function() {
1493 setTimeout(function() {
1494 jQuery(sel).fadeOut();
1495 }, 2000);
1496 });
1497 },
1498 getCacheStats: function() {
1499 var self = this;
1500 this.ajax('wordfence_getCacheStats', {}, function(res) {
1501 if (res.ok) {
1502 self.colorbox('400px', res.heading, res.body);
1503 }
1504 });
1505 },
1506 clearPageCache: function() {
1507 var self = this;
1508 this.ajax('wordfence_clearPageCache', {}, function(res) {
1509 if (res.ok) {
1510 self.colorbox('400px', res.heading, res.body);
1511 }
1512 });
1513 },
1514 switchToFalcon: function() {
1515 var self = this;
1516 this.ajax('wordfence_checkFalconHtaccess', {}, function(res) {
1517 if (res.ok) {
1518 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);" />');
1519 } else if (res.nginx) {
1520 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);" />');
1521 } else if (res.err) {
1522 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);' />");
1523 }
1524 });
1525 },
1526 confirmSwitchToFalcon: function(noEditHtaccess) {
1527 jQuery.colorbox.close();
1528 var cacheType = 'falcon';
1529 var self = this;
1530 this.ajax('wordfence_saveCacheConfig', {
1531 cacheType: cacheType,
1532 noEditHtaccess: noEditHtaccess
1533 }, function(res) {
1534 if (res.ok) {
1535 self.colorbox('400px', res.heading, res.body);
1536 }
1537 }
1538 );
1539 },
1540 saveCacheConfig: function() {
1541 var cacheType = jQuery('input:radio[name=cacheType]:checked').val();
1542 if (cacheType == 'falcon') {
1543 return this.switchToFalcon();
1544 }
1545 var self = this;
1546 this.ajax('wordfence_saveCacheConfig', {
1547 cacheType: cacheType
1548 }, function(res) {
1549 if (res.ok) {
1550 self.colorbox('400px', res.heading, res.body);
1551 }
1552 }
1553 );
1554 },
1555 saveCacheOptions: function() {
1556 var self = this;
1557 this.ajax('wordfence_saveCacheOptions', {
1558 allowHTTPSCaching: (jQuery('#wfallowHTTPSCaching').is(':checked') ? 1 : 0),
1559 addCacheComment: (jQuery('#wfaddCacheComment').is(':checked') ? 1 : 0),
1560 clearCacheSched: (jQuery('#wfclearCacheSched').is(':checked') ? 1 : 0)
1561 }, function(res) {
1562 if (res.updateErr) {
1563 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>');
1564 }
1565 }
1566 );
1567 },
1568 saveConfig: function() {
1569 var qstr = jQuery('#wfConfigForm').serialize();
1570 var self = this;
1571 jQuery('.wfSavedMsg').hide();
1572 jQuery('.wfAjax24').show();
1573 this.ajax('wordfence_saveConfig', qstr, function(res) {
1574 jQuery('.wfAjax24').hide();
1575 if (res.ok) {
1576 if (res['paidKeyMsg']) {
1577 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>");
1578 return;
1579 } else if (res['reload'] == 'reload' || WFAD.reloadConfigPage) {
1580 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>");
1581 return;
1582 } else {
1583 self.pulse('.wfSavedMsg');
1584 }
1585 } else if (res.errorMsg) {
1586 return;
1587 } else {
1588 self.colorbox('400px', 'An error occurred', 'We encountered an error trying to save your changes.');
1589 }
1590 });
1591 },
1592 changeSecurityLevel: function() {
1593 var level = jQuery('#securityLevel').val();
1594 for (var k in WFSLevels[level].checkboxes) {
1595 if (k != 'liveTraf_ignorePublishers') {
1596 jQuery('#' + k).prop("checked", WFSLevels[level].checkboxes[k]);
1597 }
1598 }
1599 for (var k in WFSLevels[level].otherParams) {
1600 if (!/^(?:apiKey|securityLevel|alertEmails|liveTraf_ignoreUsers|liveTraf_ignoreIPs|liveTraf_ignoreUA|liveTraf_hitsMaxSize|maxMem|maxExecutionTime|actUpdateInterval)$/.test(k)) {
1601 jQuery('#' + k).val(WFSLevels[level].otherParams[k]);
1602 }
1603 }
1604 },
1605 clearAllBlocked: function(op) {
1606 if (op == 'blocked') {
1607 body = "Are you sure you want to clear all blocked IP addresses and allow visitors from those addresses to access the site again?";
1608 } else if (op == 'locked') {
1609 body = "Are you sure you want to clear all locked IP addresses and allow visitors from those addresses to sign in again?";
1610 } else {
1611 return;
1612 }
1613 this.colorbox('450px', "Please confirm", body +
1614 '<br /><br /><center><input type="button" name="but1" value="Cancel" onclick="jQuery.colorbox.close();" />&nbsp;&nbsp;&nbsp;' +
1615 '<input type="button" name="but2" value="Yes I\'m sure" onclick="jQuery.colorbox.close(); WFAD.confirmClearAllBlocked(\'' + op + '\');"><br />');
1616 },
1617 confirmClearAllBlocked: function(op) {
1618 var self = this;
1619 this.ajax('wordfence_clearAllBlocked', {op: op}, function(res) {
1620 self.staticTabChanged();
1621 });
1622 },
1623 setOwnCountry: function(code) {
1624 this.ownCountry = (code + "").toUpperCase();
1625 },
1626 loadBlockedCountries: function(str) {
1627 var codes = str.split(',');
1628 for (var i = 0; i < codes.length; i++) {
1629 jQuery('#wfCountryCheckbox_' + codes[i]).prop('checked', true);
1630 }
1631 },
1632 saveCountryBlocking: function() {
1633 var action = jQuery('#wfBlockAction').val();
1634 var redirURL = jQuery('#wfRedirURL').val();
1635 var bypassRedirURL = jQuery('#wfBypassRedirURL').val();
1636 var bypassRedirDest = jQuery('#wfBypassRedirDest').val();
1637 var bypassViewURL = jQuery('#wfBypassViewURL').val();
1638
1639 if (action == 'redir' && (!/^https?:\/\/[^\/]+/i.test(redirURL))) {
1640 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://");
1641 return;
1642 }
1643 if (bypassRedirURL || bypassRedirDest) {
1644 if (!(bypassRedirURL && bypassRedirDest)) {
1645 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.");
1646 return;
1647 }
1648 if (bypassRedirURL == bypassRedirDest) {
1649 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.");
1650 return;
1651 }
1652 }
1653 if (bypassRedirURL && (!/^(?:\/|http:\/\/)/.test(bypassRedirURL))) {
1654 this.invalidCountryURLMsg(bypassRedirURL);
1655 return;
1656 }
1657 if (bypassRedirDest && (!/^(?:\/|http:\/\/)/.test(bypassRedirDest))) {
1658 this.invalidCountryURLMsg(bypassRedirDest);
1659 return;
1660 }
1661 if (bypassViewURL && (!/^(?:\/|http:\/\/)/.test(bypassViewURL))) {
1662 this.invalidCountryURLMsg(bypassViewURL);
1663 return;
1664 }
1665
1666 var codesArr = [];
1667 var ownCountryBlocked = false;
1668 var self = this;
1669 jQuery('.wfCountryCheckbox').each(function(idx, elem) {
1670 if (jQuery(elem).is(':checked')) {
1671 var code = jQuery(elem).val();
1672 codesArr.push(code);
1673 if (code == self.ownCountry) {
1674 ownCountryBlocked = true;
1675 }
1676 }
1677 });
1678 this.countryCodesToSave = codesArr.join(',');
1679 if (ownCountryBlocked) {
1680 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 />" +
1681 '<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();" />');
1682 } else {
1683 this.confirmSaveCountryBlocking();
1684 }
1685 },
1686 invalidCountryURLMsg: function(URL) {
1687 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);
1688 return;
1689 },
1690 confirmSaveCountryBlocking: function() {
1691 var action = jQuery('#wfBlockAction').val();
1692 var redirURL = jQuery('#wfRedirURL').val();
1693 var loggedInBlocked = jQuery('#wfLoggedInBlocked').is(':checked') ? '1' : '0';
1694 var loginFormBlocked = jQuery('#wfLoginFormBlocked').is(':checked') ? '1' : '0';
1695 var restOfSiteBlocked = jQuery('#wfRestOfSiteBlocked').is(':checked') ? '1' : '0';
1696 var bypassRedirURL = jQuery('#wfBypassRedirURL').val();
1697 var bypassRedirDest = jQuery('#wfBypassRedirDest').val();
1698 var bypassViewURL = jQuery('#wfBypassViewURL').val();
1699
1700 jQuery('.wfAjax24').show();
1701 var self = this;
1702 this.ajax('wordfence_saveCountryBlocking', {
1703 blockAction: action,
1704 redirURL: redirURL,
1705 loggedInBlocked: loggedInBlocked,
1706 loginFormBlocked: loginFormBlocked,
1707 restOfSiteBlocked: restOfSiteBlocked,
1708 bypassRedirURL: bypassRedirURL,
1709 bypassRedirDest: bypassRedirDest,
1710 bypassViewURL: bypassViewURL,
1711 codes: this.countryCodesToSave
1712 }, function(res) {
1713 jQuery('.wfAjax24').hide();
1714 self.pulse('.wfSavedMsg');
1715 });
1716 },
1717 paidUsersOnly: function(msg) {
1718 var pos = jQuery('#paidWrap').position();
1719 var width = jQuery('#paidWrap').width();
1720 var height = jQuery('#paidWrap').height();
1721 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);
1722 },
1723 sched_modeChange: function() {
1724 var self = this;
1725 if (jQuery('#schedMode').val() == 'auto') {
1726 jQuery('.wfSchedCheckbox').attr('disabled', true);
1727 } else {
1728 jQuery('.wfSchedCheckbox').attr('disabled', false);
1729 }
1730 },
1731 sched_shortcut: function(mode) {
1732 if (jQuery('#schedMode').val() == 'auto') {
1733 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.");
1734 return;
1735 }
1736 jQuery('.wfSchedCheckbox').prop('checked', false);
1737 if (this.schedStartHour === false) {
1738 this.schedStartHour = Math.floor((Math.random() * 24));
1739 } else {
1740 this.schedStartHour++;
1741 if (this.schedStartHour > 23) {
1742 this.schedStartHour = 0;
1743 }
1744 }
1745 if (mode == 'onceDaily') {
1746 for (var i = 0; i <= 6; i++) {
1747 jQuery('#wfSchedDay_' + i + '_' + this.schedStartHour).attr('checked', true);
1748 }
1749 } else if (mode == 'twiceDaily') {
1750 var secondHour = this.schedStartHour + 12;
1751 if (secondHour >= 24) {
1752 secondHour = secondHour - 24;
1753 }
1754 for (var i = 0; i <= 6; i++) {
1755 jQuery('#wfSchedDay_' + i + '_' + this.schedStartHour).attr('checked', true);
1756 jQuery('#wfSchedDay_' + i + '_' + secondHour).attr('checked', true);
1757 }
1758 } else if (mode == 'oddDaysWE') {
1759 var startDay = Math.floor((Math.random()));
1760 jQuery('#wfSchedDay_1_' + this.schedStartHour).attr('checked', true);
1761 jQuery('#wfSchedDay_3_' + this.schedStartHour).attr('checked', true);
1762 jQuery('#wfSchedDay_5_' + this.schedStartHour).attr('checked', true);
1763 jQuery('#wfSchedDay_6_' + this.schedStartHour).attr('checked', true);
1764 jQuery('#wfSchedDay_0_' + this.schedStartHour).attr('checked', true);
1765 } else if (mode == 'weekends') {
1766 var startDay = Math.floor((Math.random()));
1767 jQuery('#wfSchedDay_6_' + this.schedStartHour).attr('checked', true);
1768 jQuery('#wfSchedDay_0_' + this.schedStartHour).attr('checked', true);
1769 } else if (mode == 'every6hours') {
1770 for (var i = 0; i <= 6; i++) {
1771 for (var hour = this.schedStartHour; hour < this.schedStartHour + 24; hour = hour + 6) {
1772 var displayHour = hour;
1773 if (displayHour >= 24) {
1774 displayHour = displayHour - 24;
1775 }
1776 jQuery('#wfSchedDay_' + i + '_' + displayHour).attr('checked', true);
1777 }
1778 }
1779 }
1780
1781 },
1782 sched_save: function() {
1783 var schedMode = jQuery('#schedMode').val();
1784 var schedule = [];
1785 for (var day = 0; day <= 6; day++) {
1786 var hours = [];
1787 for (var hour = 0; hour <= 23; hour++) {
1788 var elemID = '#wfSchedDay_' + day + '_' + hour;
1789 hours[hour] = jQuery(elemID).is(':checked') ? '1' : '0';
1790 }
1791 schedule[day] = hours.join(',');
1792 }
1793 var scheduleTxt = schedule.join('|');
1794 var self = this;
1795 this.ajax('wordfence_saveScanSchedule', {
1796 schedMode: schedMode,
1797 schedTxt: scheduleTxt
1798 }, function(res) {
1799 jQuery('#wfScanStartTime').html(res.nextStart);
1800 jQuery('.wfAjax24').hide();
1801 self.pulse('.wfSaveMsg');
1802 });
1803 },
1804 twoFacStatus: function(msg) {
1805 jQuery('#wfTwoFacMsg').html(msg);
1806 jQuery('#wfTwoFacMsg').fadeIn(function() {
1807 setTimeout(function() {
1808 jQuery('#wfTwoFacMsg').fadeOut();
1809 }, 2000);
1810 });
1811 },
1812 addTwoFactor: function(username, phone) {
1813 var self = this;
1814 this.ajax('wordfence_addTwoFactor', {
1815 username: username,
1816 phone: phone
1817 }, function(res) {
1818 if (res.ok) {
1819 self.twoFacStatus('User added! Check the user\'s phone to get the activation code.');
1820 jQuery('<div id="twoFacCont_' + res.userID + '">' + jQuery('#wfTwoFacUserTmpl').tmpl(res).html() + '</div>').prependTo(jQuery('#wfTwoFacUsers'));
1821 }
1822 });
1823 },
1824 twoFacActivate: function(userID, code) {
1825 var self = this;
1826 this.ajax('wordfence_twoFacActivate', {
1827 userID: userID,
1828 code: code
1829 }, function(res) {
1830 if (res.ok) {
1831 jQuery('#twoFacCont_' + res.userID).html(
1832 jQuery('#wfTwoFacUserTmpl').tmpl(res)
1833 );
1834 self.twoFacStatus('Cellphone Sign-in activated for user.');
1835 }
1836 });
1837 },
1838 delTwoFac: function(userID) {
1839 this.ajax('wordfence_twoFacDel', {
1840 userID: userID
1841 }, function(res) {
1842 if (res.ok) {
1843 jQuery('#twoFacCont_' + res.userID).fadeOut(function() {
1844 jQuery(this).remove();
1845 });
1846 }
1847 });
1848 },
1849 loadTwoFactor: function() {
1850 this.ajax('wordfence_loadTwoFactor', {}, function(res) {
1851 if (res.users && res.users.length > 0) {
1852 for (var i = 0; i < res.users.length; i++) {
1853 jQuery('<div id="twoFacCont_' + res.users[i].userID + '">' +
1854 jQuery('#wfTwoFacUserTmpl').tmpl(res.users[i]).html() + '</div>').appendTo(jQuery('#wfTwoFacUsers'));
1855 }
1856 }
1857 });
1858 },
1859 getQueryParam: function(name) {
1860 name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
1861 var regexS = "[\\?&]" + name + "=([^&#]*)";
1862 var regex = new RegExp(regexS);
1863 var results = regex.exec(window.location.search);
1864 if (results == null) {
1865 return "";
1866 } else {
1867 return decodeURIComponent(results[1].replace(/\+/g, " "));
1868 }
1869 },
1870 inet_aton: function(dot) {
1871 var d = dot.split('.');
1872 return ((((((+d[0]) * 256) + (+d[1])) * 256) + (+d[2])) * 256) + (+d[3]);
1873 },
1874 inet_ntoa: function(num) {
1875 var d = num % 256;
1876 for (var i = 3; i > 0; i--) {
1877 num = Math.floor(num / 256);
1878 d = num % 256 + '.' + d;
1879 }
1880 return d;
1881 },
1882
1883 inet_pton: function(a) {
1884 // discuss at: http://phpjs.org/functions/inet_pton/
1885 // original by: Theriault
1886 // example 1: inet_pton('::');
1887 // returns 1: '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
1888 // example 2: inet_pton('127.0.0.1');
1889 // returns 2: '\x7F\x00\x00\x01'
1890
1891 var r, m, x, i, j, f = String.fromCharCode;
1892 m = a.match(/^(?:\d{1,3}(?:\.|$)){4}/); // IPv4
1893 if (m) {
1894 m = m[0].split('.');
1895 m = f(m[0]) + f(m[1]) + f(m[2]) + f(m[3]);
1896 // Return if 4 bytes, otherwise false.
1897 return m.length === 4 ? m : false;
1898 }
1899 r = /^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/;
1900 m = a.match(r); // IPv6
1901 if (m) {
1902 // Translate each hexadecimal value.
1903 for (j = 1; j < 4; j++) {
1904 // Indice 2 is :: and if no length, continue.
1905 if (j === 2 || m[j].length === 0) {
1906 continue;
1907 }
1908 m[j] = m[j].split(':');
1909 for (i = 0; i < m[j].length; i++) {
1910 m[j][i] = parseInt(m[j][i], 16);
1911 // Would be NaN if it was blank, return false.
1912 if (isNaN(m[j][i])) {
1913 return false; // Invalid IP.
1914 }
1915 m[j][i] = f(m[j][i] >> 8) + f(m[j][i] & 0xFF);
1916 }
1917 m[j] = m[j].join('');
1918 }
1919 x = m[1].length + m[3].length;
1920 if (x === 16) {
1921 return m[1] + m[3];
1922 } else if (x < 16 && m[2].length > 0) {
1923 return m[1] + (new Array(16 - x + 1))
1924 .join('\x00') + m[3];
1925 }
1926 }
1927 return false; // Invalid IP.
1928 },
1929 inet_ntop: function(a) {
1930 // discuss at: http://phpjs.org/functions/inet_ntop/
1931 // original by: Theriault
1932 // example 1: inet_ntop('\x7F\x00\x00\x01');
1933 // returns 1: '127.0.0.1'
1934 // example 2: inet_ntop('\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1');
1935 // returns 2: '::1'
1936
1937 var i = 0,
1938 m = '',
1939 c = [];
1940 a += '';
1941 if (a.length === 4) { // IPv4
1942 return [
1943 a.charCodeAt(0), a.charCodeAt(1), a.charCodeAt(2), a.charCodeAt(3)].join('.');
1944 } else if (a.length === 16) { // IPv6
1945 for (i = 0; i < 16; i++) {
1946 c.push(((a.charCodeAt(i++) << 8) + a.charCodeAt(i))
1947 .toString(16));
1948 }
1949 return c.join(':')
1950 .replace(/((^|:)0(?=:|$))+:?/g, function(t) {
1951 m = (t.length > m.length) ? t : m;
1952 return t;
1953 })
1954 .replace(m || ' ', '::');
1955 } else { // Invalid length
1956 return false;
1957 }
1958 },
1959
1960 removeCacheExclusion: function(id) {
1961 this.ajax('wordfence_removeCacheExclusion', {id: id}, function(res) {
1962 window.location.reload(true);
1963 });
1964 },
1965 addCacheExclusion: function(patternType, pattern) {
1966 if (/^https?:\/\//.test(pattern)) {
1967 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.");
1968 return;
1969 }
1970
1971 this.ajax('wordfence_addCacheExclusion', {
1972 patternType: patternType,
1973 pattern: pattern
1974 }, function(res) {
1975 if (res.ok) { //Otherwise errorMsg will get caught
1976 window.location.reload(true);
1977 }
1978 });
1979 },
1980 loadCacheExclusions: function() {
1981 this.ajax('wordfence_loadCacheExclusions', {}, function(res) {
1982 if (res.ex instanceof Array && res.ex.length > 0) {
1983 for (var i = 0; i < res.ex.length; i++) {
1984 var newElem = jQuery('#wfCacheExclusionTmpl').tmpl(res.ex[i]);
1985 newElem.prependTo('#wfCacheExclusions').fadeIn();
1986 }
1987 jQuery('<h2>Cache Exclusions</h2>').prependTo('#wfCacheExclusions');
1988 } else {
1989 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');
1990 }
1991
1992 });
1993 },
1994 exportSettings: function() {
1995 var self = this;
1996 this.ajax('wordfence_exportSettings', {}, function(res) {
1997 if (res.ok && res.token) {
1998 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 />");
1999 } else if (res.err) {
2000 self.colorbox('400px', "Error during Export", res.err);
2001 } else {
2002 self.colorbox('400px', "An unknown error occurred", "An unknown error occurred during the export. We received an undefined error from your web server.");
2003 }
2004 });
2005 },
2006 importSettings: function(token) {
2007 var self = this;
2008 this.ajax('wordfence_importSettings', {token: token}, function(res) {
2009 if (res.ok) {
2010 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);\" />");
2011 } else if (res.err) {
2012 self.colorbox('400px', "Error during Import", res.err);
2013 } else {
2014 self.colorbox('400px', "Error during Export", "An unknown error occurred during the import");
2015 }
2016 });
2017 },
2018 startPasswdAudit: function(auditType, emailAddr) {
2019 var self = this;
2020 this.ajax('wordfence_startPasswdAudit', {auditType: auditType, emailAddr: emailAddr}, function(res) {
2021 self.loadPasswdAuditJobs();
2022 if (res.ok) {
2023 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);
2024 } else if (!res.errorMsg) { //error displayed
2025 self.colorbox('400px', "Error Starting Audit", "An unknown error occurred when trying to start your password audit.");
2026 }
2027 });
2028 },
2029 windowHasFocus: function() {
2030 if (typeof document.hasFocus === 'function') {
2031 return document.hasFocus();
2032 }
2033 // Older versions of Opera
2034 return this._windowHasFocus;
2035 }
2036 };
2037 window['WFAD'] = window['wordfenceAdmin'];
2038 }
2039 jQuery(function() {
2040 wordfenceAdmin.init();
2041 });
2042 })(jQuery);
2043