PluginProbe ʕ •ᴥ•ʔ
Wordfence Security – Firewall, Malware Scan, and Login Security / 3.2.6
Wordfence Security – Firewall, Malware Scan, and Login Security v3.2.6
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 / lib / wordfenceClass.php
wordfence / lib Last commit date
Diff 14 years ago .htaccess 14 years ago Diff.php 14 years ago GeoIP.dat 13 years ago IPTraf.php 13 years ago diffResult.php 14 years ago email_genericAlert.php 14 years ago email_newIssues.php 14 years ago email_unlockRequest.php 14 years ago menu_activity.php 13 years ago menu_blockedIPs.php 13 years ago menu_countryBlocking.php 13 years ago menu_options.php 13 years ago menu_scan.php 13 years ago menu_scanSchedule.php 13 years ago schedWeekEntry.php 13 years ago sysinfo.php 14 years ago unknownFiles.php 13 years ago viewFullActivityLog.php 13 years ago wf503.php 13 years ago wfAPI.php 13 years ago wfAction.php 14 years ago wfArray.php 13 years ago wfBrowscap.php 14 years ago wfBrowscapCache.php 14 years ago wfBulkCountries.php 13 years ago wfConfig.php 13 years ago wfCountryMap.php 13 years ago wfCrawl.php 13 years ago wfDB.php 13 years ago wfDict.php 14 years ago wfGeoIP.php 13 years ago wfIssues.php 13 years ago wfLockedOut.php 14 years ago wfLog.php 13 years ago wfModTracker.php 14 years ago wfRate.php 14 years ago wfScanEngine.php 13 years ago wfSchema.php 13 years ago wfUnlockMsg.php 14 years ago wfUtils.php 13 years ago wfViewResult.php 14 years ago wordfenceClass.php 13 years ago wordfenceConstants.php 13 years ago wordfenceHash.php 13 years ago wordfenceScanner.php 13 years ago wordfenceURLHoover.php 13 years ago
wordfenceClass.php
1529 lines
1 <?php
2 require_once('wordfenceConstants.php');
3 require_once('wfScanEngine.php');
4 require_once('wfCrawl.php');
5 require_once 'Diff.php';
6 require_once 'Diff/Renderer/Html/SideBySide.php';
7 require_once 'wfAPI.php';
8 require_once 'wfIssues.php';
9 require_once('wfDB.php');
10 require_once('wfUtils.php');
11 require_once('wfLog.php');
12 require_once('wfConfig.php');
13 require_once('wfSchema.php');
14 class wordfence {
15 public static $printStatus = false;
16 protected static $lastURLError = false;
17 protected static $curlContent = "";
18 protected static $curlDataWritten = 0;
19 protected static $hasher = '';
20 protected static $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
21 protected static $ignoreList = false;
22 public static $newVisit = false;
23 private static $wfLog = false;
24 private static $hitID = 0;
25 private static $statusStartMsgs = array();
26 private static $debugOn = null;
27 public static function installPlugin(){
28 self::runInstall();
29 //Used by MU code below
30 update_option('wordfenceActivated', 1);
31 }
32 public static function uninstallPlugin(){
33 //Used by MU code below
34 update_option('wordfenceActivated', 0);
35 wp_clear_scheduled_hook('wordfence_daily_cron');
36 wp_clear_scheduled_hook('wordfence_hourly_cron');
37
38 //Remove old legacy cron job if it exists
39 wp_clear_scheduled_hook('wordfence_scheduled_scan');
40
41 //Remove all scheduled scans.
42 self::unscheduleAllScans();
43
44 if(wfConfig::get('deleteTablesOnDeact')){
45 $schema = new wfSchema();
46 $schema->dropAll();
47 foreach(array('wordfence_version', 'wordfenceActivated') as $opt){
48 delete_option($opt);
49 }
50 }
51 }
52 public static function hourlyCron(){
53 global $wpdb; $p = $wpdb->base_prefix;
54 $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
55
56
57 if(wfConfig::get('other_WFNet')){
58 $wfdb = new wfDB();
59 $q1 = $wfdb->query("select URI from $p"."wfNet404s where ctime > unix_timestamp() - 3600 limit 1000");
60 $URIs = array();
61 while($rec = mysql_fetch_assoc($q1)){
62 array_push($URIs, $rec['URI']);
63 }
64 $wfdb->truncate($p . "wfNet404s");
65 if(sizeof($URIs) > 0){
66 try {
67 $api->call('send_net_404s', array(), array( 'URIs' => json_encode($URIs) ));
68 } catch(Exception $e){
69 //Ignore
70 }
71 }
72
73 $q2 = $wfdb->query("select INET_NTOA(IP) as IP from $p"."wfVulnScanners where ctime > unix_timestamp() - 3600");
74 $scanCont = "";
75 while($rec = mysql_fetch_assoc($q2)){
76 $scanCont .= pack('N', ip2long($rec['IP']));
77 }
78 $wfdb->truncate($p . "wfVulnScanners");
79
80 $q3 = $wfdb->query("select INET_NTOA(IP) as IP from $p"."wfLockedOut where blockedTime > unix_timestamp() - 3600");
81 $lockCont = "";
82 while($rec = mysql_fetch_assoc($q3)){
83 $lockCont .= pack('N', ip2long($rec['IP']));
84 }
85 if(strlen($lockCont) > 0 || strlen($scanCont) > 0){
86 $cont = pack('N', strlen($lockCont) / 4) . $lockCont . pack('N', strlen($scanCont) / 4) . $scanCont;
87 try {
88 $resp = $api->binCall('get_net_bad_ips', $cont);
89 if($resp['code'] == 200){
90 $len = strlen($resp['data']);
91 $reason = "WFSN: Blocked by Wordfence Security Network";
92 $wfdb->query("delete from $p"."wfBlocks where wfsn=1 and permanent=0");
93 if($len > 0 && $len % 4 == 0){
94 for($i = 0; $i < $len; $i += 4){
95 list($ipLong) = array_values(unpack('N', substr($resp['data'], $i, 4)));
96 $IPStr = long2ip($ipLong);
97 if(! self::getLog()->isWhitelisted($IPStr)){
98 self::getLog()->blockIP($IPStr, $reason, true);
99 }
100 }
101 }
102 }
103 } catch(Exception $e){
104 //Ignore
105 }
106 }
107 }
108 }
109 public static function dailyCron(){
110 $wfdb = new wfDB();
111 global $wpdb; $p = $wpdb->base_prefix;
112 $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
113 try {
114 $patData = $api->call('get_known_vuln_pattern');
115 if(is_array($patData) && $patData['pat']){
116 if(@preg_match($patData['pat'], 'wordfence_test_vuln_match')){
117 wfConfig::set('vulnRegex', $patData['pat']);
118 }
119 }
120 } catch(Exception $e){
121 wordfence::status(4, 'error', "Could not fetch vulnerability patterns in scheduled job: " . $e->getMessage());
122 }
123
124 $wfdb->query("delete from $p"."wfLocs where ctime < unix_timestamp() - %d", WORDFENCE_MAX_IPLOC_AGE);
125 $wfdb->truncate($p . "wfBadLeechers"); //only uses date that's less than 1 minute old
126 $wfdb->query("delete from $p"."wfBlocks where (blockedTime + %s < unix_timestamp()) and permanent=0", wfConfig::get('blockedTime'));
127 $wfdb->query("delete from $p"."wfCrawlers where lastUpdate < unix_timestamp() - (86400 * 7)");
128
129 $count = $wfdb->querySingle("select count(*) as cnt from $p"."wfHits");
130 if($count > 20000){
131 $wfdb->query("delete from $p"."wfHits order by id asc limit " . ($count - 20000));
132 }
133 $maxRows = 1000; //affects stuff further down too
134 foreach(array('wfLeechers', 'wfScanners') as $table){
135 //This is time based per IP so shouldn't get too big
136 $wfdb->query("delete from $p"."$table where eMin < ((unix_timestamp() - (86400 * 2)) / 60)");
137 }
138 $wfdb->query("delete from $p"."wfLockedOut where blockedTime + %s < unix_timestamp()", wfConfig::get('loginSec_lockoutMins') * 60);
139 $count2 = $wfdb->querySingle("select count(*) as cnt from $p"."wfLogins");
140 if($count2 > 20000){
141 $wfdb->truncate($p . "wfLogins"); //in case of Dos
142 } else if($count2 > $maxRows){
143 $wfdb->query("delete from $p"."wfLogins order by ctime asc limit %d", ($count2 - $maxRows));
144 }
145 $wfdb->query("delete from $p"."wfReverseCache where unix_timestamp() - lastUpdate > 86400");
146 $count3 = $wfdb->querySingle("select count(*) as cnt from $p"."wfThrottleLog");
147 if($count3 > 20000){
148 $wfdb->truncate($p . "wfThrottleLog"); //in case of DoS
149 } else if($count3 > $maxRows){
150 $wfdb->query("delete from $p"."wfThrottleLog order by endTime asc limit %d", ($count3 - $maxRows));
151 }
152 $count4 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus");
153 if($count4 > 100000){
154 $wfdb->truncate($p . "wfStatus");
155 } else if($count4 > 1000){ //max status events we keep. This determines how much gets emailed to us when users sends us a debug report.
156 $wfdb->query("delete from $p"."wfStatus where level != 10 order by ctime asc limit %d", ($count4 - 1000));
157 $count5 = $wfdb->querySingle("select count(*) as cnt from $p"."wfStatus where level=10");
158 if($count5 > 100){
159 $wfdb->query("delete from $p"."wfStatus where level = 10 order by ctime asc limit %d", ($count5 - 100) );
160 }
161 }
162
163 }
164 public static function runInstall(){
165 update_option('wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install.
166 //EVERYTHING HERE MUST BE IDEMPOTENT
167
168 //Remove old legacy cron job if exists
169 wp_clear_scheduled_hook('wordfence_scheduled_scan');
170
171 //Install new schedule. If schedule config is blank it will install the default 'auto' schedule.
172 wordfence::scheduleScans();
173
174 $schema = new wfSchema();
175 $schema->createAll(); //if not exists
176 wfConfig::setDefaults(); //If not set
177
178 if(! wfConfig::get('apiKey')){
179 $api = new wfAPI('', wfUtils::getWPVersion());
180 try {
181 $keyData = $api->call('get_anon_api_key');
182 if($keyData['ok'] && $keyData['apiKey']){
183 wfConfig::set('apiKey', $keyData['apiKey']);
184 } else {
185 throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key.");
186 }
187 } catch(Exception $e){
188 error_log("Could not fetch free API key from Wordfence: " . $e->getMessage());
189 return;
190 }
191 }
192 wp_clear_scheduled_hook('wordfence_daily_cron');
193 wp_clear_scheduled_hook('wordfence_hourly_cron');
194 wp_schedule_event(time(), 'daily', 'wordfence_daily_cron');
195 wp_schedule_event(time(), 'hourly', 'wordfence_hourly_cron');
196 $db = new wfDB();
197
198 if($db->columnExists('wfHits', 'HTTPHeaders')){ //Upgrade from 3.0.4
199 global $wpdb;
200 $prefix = $wpdb->base_prefix;
201 $count = $db->querySingle("select count(*) as cnt from $prefix"."wfHits");
202 if($count > 20000){
203 $db->query("delete from $prefix"."wfHits order by id asc limit " . ($count - 20000));
204 }
205 $db->dropColumn('wfHits', 'HTTPHeaders');
206 }
207
208 //Upgrading from 1.5.6 or earlier needs:
209 $db->createKeyIfNotExists('wfStatus', 'level', 'k2');
210 if(wfConfig::get('isPaid') == 'free'){
211 wfConfig::set('isPaid', '');
212 }
213 //End upgrade from 1.5.6
214
215 //Show an alert that user needs to enter an email address if user has not seen it before
216 if(! wfConfig::get('alertEmailMsgCount')){
217 wfConfig::set('alertEmailMsgCount', 0);
218 }
219
220 @chmod(dirname(__FILE__) . '/../wfscan.php', 0755);
221 @chmod(dirname(__FILE__) . '/../visitor.php', 0755);
222
223 global $wpdb;
224 $prefix = $wpdb->base_prefix;
225 $db->queryIgnoreError("alter table $prefix"."wfConfig modify column val longblob");
226 $db->queryIgnoreError("alter table $prefix"."wfBlocks add column permanent tinyint UNSIGNED default 0");
227 $db->queryIgnoreError("alter table $prefix"."wfStatus modify column msg varchar(1000) NOT NULL");
228 //3.1.2 to 3.1.4
229 $db->queryIgnoreError("alter table $prefix"."wfBlocks modify column blockedTime bigint signed NOT NULL");
230 //3.2.1 to 3.2.2
231 $db->queryIgnoreError("alter table $prefix"."wfLockedOut modify column blockedTime bigint signed NOT NULL");
232
233 //Must be the final line
234 }
235 public static function install_actions(){
236 $versionInOptions = get_option('wordfence_version', false);
237 if( (! $versionInOptions) || version_compare(WORDFENCE_VERSION, $versionInOptions, '>')){
238 //Either there is no version in options or the version in options is greater and we need to run the upgrade
239 self::runInstall();
240 }
241 if(defined('MULTISITE') && MULTISITE === true){
242 global $blog_id;
243 if($blog_id == 1 && get_option('wordfenceActivated') != 1){ return; } //Because the plugin is active once installed, even before it's network activated, for site 1 (WordPress team, why?!)
244 }
245 add_action('wordfence_start_scheduled_scan', 'wordfence::wordfenceStartScheduledScan');
246 add_action('wordfence_daily_cron', 'wordfence::dailyCron');
247 add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
248 add_action('plugins_loaded', 'wordfence::veryFirstAction');
249 add_action('init', 'wordfence::initAction');
250 add_action('template_redirect', 'wordfence::templateRedir');
251 add_action('shutdown', 'wordfence::shutdownAction');
252 add_action('wp_authenticate','wordfence::authAction');
253 add_action('login_init','wordfence::loginInitAction');
254 add_action('wp_login','wordfence::loginAction');
255 add_action('wp_logout','wordfence::logoutAction');
256 add_action('profile_update', 'wordfence::profileUpdateAction', '99', 2);
257 add_action('lostpassword_post', 'wordfence::lostPasswordPost', '1');
258 //add_filter('cron_schedules', 'wordfence::moreCronReccurences');
259 add_filter('pre_comment_approved', 'wordfence::preCommentApprovedFilter', '99', 2);
260 add_filter('authenticate', 'wordfence::authenticateFilter', 99, 3);
261 //html|xhtml|atom|rss2|rdf|comment|export
262 add_filter('get_the_generator_html', 'wordfence::genFilter', 99, 2);
263 add_filter('get_the_generator_xhtml', 'wordfence::genFilter', 99, 2);
264 add_filter('get_the_generator_atom', 'wordfence::genFilter', 99, 2);
265 add_filter('get_the_generator_rss2', 'wordfence::genFilter', 99, 2);
266 add_filter('get_the_generator_rdf', 'wordfence::genFilter', 99, 2);
267 add_filter('get_the_generator_comment', 'wordfence::genFilter', 99, 2);
268 add_filter('get_the_generator_export', 'wordfence::genFilter', 99, 2);
269 if(is_admin()){
270 add_action('admin_init', 'wordfence::admin_init');
271 if(is_multisite()){
272 if(wfUtils::isAdminPageMU()){
273 add_action('network_admin_menu', 'wordfence::admin_menus');
274 } //else don't show menu
275 } else {
276 add_action('admin_menu', 'wordfence::admin_menus');
277 }
278 }
279 }
280 public static function ajaxReceiver(){
281 if(! wfUtils::isAdmin()){
282 die(json_encode(array('errorMsg' => "You appear to have logged out or you are not an admin. Please sign-out and sign-in again.")));
283 }
284 $func = $_POST['action'];
285 $nonce = $_POST['nonce'];
286 if(! wp_verify_nonce($nonce, 'wp-ajax')){
287 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.")));
288 }
289 //func is e.g. wordfence_ticker so need to munge it
290 $func = str_replace('wordfence_', '', $func);
291 $returnArr = call_user_func('wordfence::ajax_' . $func . '_callback');
292 if($returnArr === false){
293 $returnArr = array('errorMsg' => "Wordfence encountered an internal error executing that request.");
294 }
295
296 if(! is_array($returnArr)){
297 error_log("Function $func did not return an array and did not generate an error.");
298 $returnArr = array();
299 }
300 if(isset($returnARr['nonce'])){
301 error_log("Wordfence ajax function return an array with 'nonce' already set. This could be a bug.");
302 }
303 $returnArr['nonce'] = wp_create_nonce('wp-ajax');
304 die(json_encode($returnArr));
305 }
306 public static function lostPasswordPost(){
307 $IP = wfUtils::getIP();
308 if(self::getLog()->isWhitelisted($IP)){
309 return;
310 }
311 if(self::isLockedOut($IP)){
312 require('wfLockedOut.php');
313 }
314 $email = $_POST['user_login'];
315 if(empty($email)){ return; }
316 $user = get_user_by('email', $_POST['user_login']);
317 if($user){
318 if(wfConfig::get('alertOn_lostPasswdForm')){
319 wordfence::alert("Password recovery attempted", "Someone tried to recover the password for user with email address: $email", $IP);
320 }
321 }
322 if(wfConfig::get('loginSecurityEnabled')){
323 $tKey = 'wffgt_' . wfUtils::inet_aton($IP);
324 $forgotAttempts = get_transient($tKey);
325 if($forgotAttempts){
326 $forgotAttempts++;
327 } else {
328 $forgotAttempts = 1;
329 }
330 if($forgotAttempts >= wfConfig::get('loginSec_maxForgotPasswd')){
331 self::lockOutIP($IP, "Exceeded the maximum number of tries to recover their password which is set at: " . wfConfig::get('loginSec_maxForgotPasswd'));
332 require('wfLockedOut.php');
333 }
334 set_transient($tKey, $forgotAttempts, wfConfig::get('loginSec_countFailMins') * 60);
335 }
336 }
337 public static function lockOutIP($IP, $reason){
338 if(wfConfig::get('alertOn_loginLockout')){
339 wordfence::alert("User locked out from signing in", "A user with IP address $IP has been locked out from the signing in or using the password recovery form for the following reason: $reason", $IP);
340 }
341 self::getLog()->lockOutIP(wfUtils::getIP(), $reason);
342 }
343 public static function isLockedOut($IP){
344 return self::getLog()->isIPLockedOut($IP);
345 }
346 public static function veryFirstAction(){
347 $wfFunc = @$_GET['_wfsf'];
348 if($wfFunc == 'unlockEmail'){
349 $email = trim($_POST['email']);
350 global $wpdb;
351 $ws = $wpdb->get_results("SELECT ID, user_login FROM $wpdb->users");
352 $users = array();
353 foreach($ws as $user){
354 $userDat = get_userdata($user->ID);
355 if($userDat->user_level > 7){
356 if($email == $userDat->user_email){
357 $found = true;
358 break;
359 }
360 }
361 }
362 if(! $found){
363 foreach(wfConfig::getAlertEmails() as $alertEmail){
364 if($alertEmail == $email){
365 $found = true;
366 break;
367 }
368 }
369 }
370 if($found){
371 $key = wfUtils::bigRandomHex();
372 $IP = wfUtils::getIP();
373 set_transient('wfunlock_' . $key, $IP, 1800);
374 $content = wfUtils::tmpl('email_unlockRequest.php', array(
375 'siteName' => get_bloginfo('name', 'raw'),
376 'siteURL' => wfUtils::getSiteBaseURL(),
377 'unlockHref' => wfUtils::getSiteBaseURL() . '?_wfsf=unlockAccess&key=' . $key,
378 'key' => $key,
379 'IP' => $IP
380 ));
381 wp_mail($email, "Unlock email requested", $content, "Content-Type: text/html");
382 }
383 echo "<html><body><h1>Your request was received</h1><p>We received a request to email \"$email\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, then they have been emailed instructions on how to regain access to this sytem. The instructions we sent will expire 30 minutes from now.</body></html>";
384 exit();
385 } else if($wfFunc == 'unlockAccess'){
386 if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', get_transient('wfunlock_' . $_GET['key']))){
387 echo "Invalid key provided for authentication.";
388 exit();
389 }
390 /* You can enable this for paranoid security leve.
391 if(get_transient('wfunlock_' . $_GET['key']) != wfUtils::getIP()){
392 echo "You can only use this link from the IP address you used to generate the unlock email.";
393 exit();
394 }
395 */
396 $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
397 if($_GET['func'] == 'unlockMyIP'){
398 $wfLog->unblockIP(wfUtils::getIP());
399 $wfLog->unlockOutIP(wfUtils::getIP());
400 header('Location: ' . wp_login_url());
401 exit();
402 } else if($_GET['func'] == 'unlockAllIPs'){
403 wordfence::status(1, 'info', "Request received via unlock email link to unblock all IP's.");
404 $wfLog->unblockAllIPs();
405 $wfLog->unlockAllIPs();
406 header('Location: ' . wp_login_url());
407 exit();
408 } else if($_GET['func'] == 'disableRules'){
409 wfConfig::set('firewallEnabled', 0);
410 wfConfig::set('loginSecurityEnabled', 0);
411 wordfence::status(1, 'info', "Request received via unlock email link to unblock all IP's via disabling firewall rules.");
412 $wfLog->unblockAllIPs();
413 $wfLog->unlockAllIPs();
414 wfConfig::set('cbl_countries', ''); //unblock all countries
415 header('Location: ' . wp_login_url());
416 exit();
417 } else {
418 echo "Invalid function specified. Please check the link we emailed you and make sure it was not cut-off by your email reader.";
419 exit();
420 }
421 }
422
423 $wfLog = self::getLog();
424 $wfLog->firewallBadIPs();
425 }
426 public static function loginAction($username){
427 if(sizeof($_POST) < 1){ return; } //only execute if login form is posted
428 if(! $username){ return; }
429 $user = get_user_by('login', $username);
430 $userID = $user ? $user->ID : 0;
431 self::getLog()->logLogin('loginOK', 0, $username);
432 if(user_can($userID, 'update_core')){
433 if(wfConfig::get('alertOn_adminLogin')){
434 wordfence::alert("Admin Login", "A user with username \"$username\" who has administrator access signed in to your WordPress site.", wfUtils::getIP());
435 }
436 } else {
437 if(wfConfig::get('alertOn_nonAdminLogin')){
438 wordfence::alert("User login", "A non-admin user with username \"$username\" signed in to your WordPress site.", wfUtils::getIP());
439 }
440 }
441 }
442 public static function authenticateFilter($authResult){
443 $IP = wfUtils::getIP();
444 if(self::getLog()->isWhitelisted($IP)){
445 return $authResult;
446 }
447 if(wfConfig::get('loginSecurityEnabled')){
448 if(is_wp_error($authResult) && $authResult->get_error_code() == 'invalid_username' && wfConfig::get('loginSec_lockInvalidUsers')){
449 self::lockOutIP($IP, "Used an invalid username to try to sign in.");
450 require('wfLockedOut.php');
451 }
452 $tKey = 'wflginfl_' . wfUtils::inet_aton($IP);
453 if(is_wp_error($authResult) && ($authResult->get_error_code() == 'invalid_username' || $authResult->get_error_code() == 'incorrect_password') ){
454 $tries = get_transient($tKey);
455 if($tries){
456 $tries++;
457 } else {
458 $tries = 1;
459 }
460 if($tries >= wfConfig::get('loginSec_maxFailures')){
461 self::lockOutIP($IP, "Exceeded the maximum number of login failures which is: " . wfConfig::get('loginSec_maxFailures'));
462 require('wfLockedOut.php');
463 }
464 set_transient($tKey, $tries, wfConfig::get('loginSec_countFailMins') * 60);
465 } else if(get_class($authResult) == 'WP_User'){
466 delete_transient($tKey); //reset counter on success
467 }
468 }
469 if(is_wp_error($authResult) && ($authResult->get_error_code() == 'invalid_username' || $authResult->get_error_code() == 'incorrect_password') && wfConfig::get('loginSec_maskLoginErrors')){
470 return new WP_Error( 'incorrect_password', sprintf( __( '<strong>ERROR</strong>: The username or password you entered is incorrect. <a href="%2$s" title="Password Lost and Found">Lost your password</a>?' ), $username, wp_lostpassword_url() ) );
471 }
472 return $authResult;
473 }
474 public static function logoutAction(){
475 $userID = get_current_user_id();
476 $userDat = get_user_by('id', $userID);
477 self::getLog()->logLogin('logout', 0, $userDat->user_login);
478 }
479 public static function loginInitAction(){
480 if(self::isLockedOut(wfUtils::getIP())){
481 require('wfLockedOut.php');
482 }
483 }
484 public static function authAction($username){
485 if(self::isLockedOut(wfUtils::getIP())){
486 require('wfLockedOut.php');
487 }
488 if(! $username){ return; }
489 $userDat = get_user_by('login', $username);
490 if($userDat){
491 require_once( ABSPATH . 'wp-includes/class-phpass.php');
492 $hasher = new PasswordHash(8, TRUE);
493 if(! $hasher->CheckPassword($_POST['pwd'], $userDat->user_pass)){
494 self::getLog()->logLogin('loginFailValidUsername', 1, $username);
495 }
496 } else {
497 self::getLog()->logLogin('loginFailInvalidUsername', 1, $username);
498 }
499 }
500 public static function getWPFileContent($file, $cType, $cName, $cVersion){
501 if($cType == 'plugin'){
502 if(preg_match('#^/?wp-content/plugins/[^/]+/#', $file)){
503 $file = preg_replace('#^/?wp-content/plugins/[^/]+/#', '', $file);
504 } else {
505 //If user is using non-standard wp-content dir, then use /plugins/ in pattern to figure out what to strip off
506 $file = preg_replace('#^.*[^/]+/plugins/[^/]+/#', '', $file);
507 }
508 } else if($cType == 'theme'){
509 if(preg_match('#/?wp-content/themes/[^/]+/#', $file)){
510 $file = preg_replace('#/?wp-content/themes/[^/]+/#', '', $file);
511 } else {
512 $file = preg_replace('#^.*[^/]+/themes/[^/]+/#', '', $file);
513 }
514 } else if($cType == 'core'){
515
516 } else {
517 return array('errorMsg' => "An invalid type was specified to get file.");
518 }
519 $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
520 try {
521 $contResult = $api->binCall('get_wp_file_content', array(
522 'v' => wfUtils::getWPVersion(),
523 'file' => $file,
524 'cType' => $cType,
525 'cName' => $cName,
526 'cVersion' => $cVersion
527 ));
528 if($contResult['data']){
529 return array('fileContent' => $contResult['data']);
530 } else {
531 throw new Exception("We could not fetch a core WordPress file from the Wordfence API.");
532 }
533 } catch (Exception $e){
534 return array('errorMsg' => $e->getMessage());
535 }
536 }
537 public static function ajax_saveScanSchedule_callback(){
538 if(! wfConfig::get('isPaid')){
539 return array('errorMsg' => "Sorry but this feature is only available for paid customers.");
540 }
541 $schedDays = explode('|', $_POST['schedTxt']);
542 $schedule = array();
543 for($day = 0; $day <= 6; $day++){
544 $schedule[$day] = explode(',', $schedDays[$day]);
545 }
546 $schedMode = $_POST['schedMode'];
547 wfConfig::set_ser('scanSched', $schedule);
548 wfConfig::set('schedMode', $schedMode);
549 wordfence::scheduleScans();
550 $nextTime = self::getNextScanStartTime();
551 return array(
552 'ok' => 1,
553 'nextStart' => ($nextTime ? $nextTime : '')
554 );
555 }
556 public static function getNextScanStartTime(){
557 $nextTime = false;
558 $cron = _get_cron_array();
559 foreach($cron as $key => $val){
560 if(isset($val['wordfence_start_scheduled_scan'])){
561 $nextTime = $key;
562 break;
563 }
564 }
565 return ($nextTime ? date('l jS \of F Y H:i:s A', $nextTime + (3600 * get_option('gmt_offset'))) : '');
566 }
567 public static function wordfenceStartScheduledScan(){
568 //This prevents scheduled scans from piling up on low traffic blogs and all being run at once.
569 //Only one scheduled scan runs within a given 60 min window. Won't run if another scan has run within 30 mins.
570 $lastScanStart = wfConfig::get('lastScheduledScanStart', 0);
571 if($lastScanStart && (time() - $lastScanStart) < 1800){
572 //A scheduled scan was started in the last 30 mins, so skip this one.
573 return;
574 }
575 wfConfig::set('lastScheduledScanStart', time());
576 wordfence::status(1, 'info', "Scheduled Wordfence scan starting at " . date('l jS \of F Y h:i:s A', current_time('timestamp')) );
577
578 //We call this before the scan actually starts to advance the schedule for the next week.
579 //This ensures that if the scan crashes for some reason, the schedule will hold.
580 wordfence::scheduleScans();
581
582 wfScanEngine::startScan();
583 }
584 public static function scheduleScans(){ //Idempotent. Deschedules everything and schedules the following week.
585 self::unscheduleAllScans();
586 $sched = wfConfig::get_ser('scanSched', array());
587 $mode = wfConfig::get('schedMode');
588 if($mode == 'manual' && is_array($sched) && is_array($sched[0]) ){
589 //Use sched as it is
590 } else { //Default to setting scans to run once a day at a randomly selected time.
591 $sched = array();
592 $runAt = rand(0,23);
593 for($day = 0; $day <= 6; $day++){
594 $sched[$day] = array();
595 for($hour = 0; $hour <= 23; $hour++){
596 if($hour == $runAt){
597 $sched[$day][$hour] = 1;
598 } else {
599 $sched[$day][$hour] = 0;
600 }
601 }
602 }
603 }
604 for($scheduledDay = 0; $scheduledDay <= 6; $scheduledDay++){
605 //0 is sunday
606 //6 is Saturday
607 for($scheduledHour = 0; $scheduledHour <= 23; $scheduledHour++){
608 if($sched[$scheduledDay][$scheduledHour]){
609 $wpTime = current_time('timestamp');
610 $currentDayOfWeek = date('w', $wpTime);
611 $daysInFuture = $scheduledDay - $currentDayOfWeek; //It's monday and scheduledDay is Wed (3) then result is 2 days in future. It's Wed and sched day is monday, then result is 3 - 1 = -2
612 if($daysInFuture < 0){ $daysInFuture += 7; } //Turns -2 into 5 days in future
613 $currentHour = date('G', $wpTime);
614 $secsOffset = ($scheduledHour - $currentHour) * 3600; //Offset from current hour, can be negative
615 $secondsInFuture = ($daysInFuture * 86400) + $secsOffset; //Can be negative, so we schedule those 1 week ahead
616 if($secondsInFuture < 1){
617 $secondsInFuture += (86400 * 7); //Add a week
618 }
619 $futureTime = time() - (time() % 3600) + $secondsInFuture; //Modulo rounds down to top of the hour
620 wordfence::status(4, 'info', "Scheduled time for day $scheduledDay hour $scheduledHour is: " . date('l jS \of F Y h:i:s A', $futureTime));
621 self::scheduleSingleScan($futureTime);
622 }
623 }
624 }
625 }
626 private static function scheduleSingleScan($futureTime){
627 wp_schedule_single_event($futureTime, 'wordfence_start_scheduled_scan', array($futureTime));
628 $schedArgs = wfConfig::get_ser('schedScanArgs', array());
629 if(! is_array($schedArgs)){ //paranoia
630 $schedArgs = array();
631 }
632 $schedArgs[] = $futureTime;
633 wfConfig::set_ser('schedScanArgs', $schedArgs);
634 }
635 private static function unscheduleAllScans(){
636 wp_clear_scheduled_hook('wordfence_start_scheduled_scan'); //Unschedule legacy scans without args
637
638 $schedArgs = wfConfig::get_ser('schedScanArgs', array());
639 foreach($schedArgs as $futureTime){
640 wp_clear_scheduled_hook('wordfence_start_scheduled_scan', array($futureTime));
641 }
642 wfConfig::set_ser('schedScanArgs', array());
643 }
644 public static function ajax_saveCountryBlocking_callback(){
645 if(! wfConfig::get('isPaid')){
646 return array('errorMsg' => "Sorry but this feature is only available for paid customers.");
647 }
648 wfConfig::set('cbl_action', $_POST['blockAction']);
649 wfConfig::set('cbl_countries', $_POST['codes']);
650 wfConfig::set('cbl_redirURL', $_POST['redirURL']);
651 wfConfig::set('cbl_loggedInBlocked', $_POST['loggedInBlocked']);
652 wfConfig::set('cbl_loginFormBlocked', $_POST['loginFormBlocked']);
653 return array('ok' => 1);
654 }
655 public static function ajax_sendActivityLog_callback(){
656 $content = "SITE: " . site_url() . "\nPLUGIN VERSION: " . WORDFENCE_VERSION . "\nWP VERSION: " . wfUtils::getWPVersion() . "\nAPI KEY: " . wfConfig::get('apiKey') . "\nADMIN EMAIL: " . get_option('admin_email') . "\nLOG:\n\n";
657 $wfdb = new wfDB();
658 global $wpdb;
659 $p = $wpdb->base_prefix;
660 $q = $wfdb->query("select ctime, level, type, msg from $p"."wfStatus order by ctime desc limit 10000");
661 while($r = mysql_fetch_assoc($q)){
662 if($r['type'] == 'error'){
663 $content .= "\n";
664 }
665 $content .= date(DATE_RFC822, $r['ctime']) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . $r['msg'] . "\n";
666 }
667 $content .= "\n\n";
668
669 ob_start();
670 phpinfo();
671 $phpinfo = ob_get_contents();
672 ob_get_clean();
673
674 $content .= $phpinfo;
675
676 wp_mail($_POST['email'], "Wordfence Activity Log", $content);
677 return array('ok' => 1);
678 }
679 public static function ajax_startTourAgain_callback(){
680 wfConfig::set('tourClosed', 0);
681 return array('ok' => 1);
682 }
683 public static function ajax_tourClosed_callback(){
684 wfConfig::set('tourClosed', 1);
685 return array('ok' => 1);
686 }
687 public static function ajax_saveConfig_callback(){
688 $opts = wfConfig::parseOptions();
689 $emails = array();
690 foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['alertEmails'])) as $email){
691 if(strlen($email) > 0){
692 array_push($emails, $email);
693 }
694 }
695 if(sizeof($emails) > 0){
696 $badEmails = array();
697 foreach($emails as $email){
698 if(! preg_match('/^[^@]+@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,8})$/i', $email)){
699 array_push($badEmails, $email);
700 }
701 }
702 if(sizeof($badEmails) > 0){
703 return array('errorMsg' => "The following emails are invalid: " . implode(', ', $badEmails));
704 }
705 $opts['alertEmails'] = implode(',', $emails);
706 } else {
707 $opts['alertEmails'] = '';
708 }
709 $whiteIPs = array();
710 foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['whitelisted'])) as $whiteIP){
711 if(strlen($whiteIP) > 0){
712 array_push($whiteIPs, $whiteIP);
713 }
714 }
715 if(sizeof($whiteIPs) > 0){
716 $badWhiteIPs = array();
717 foreach($whiteIPs as $whiteIP){
718 if(! preg_match('/^[\[\]\-\d]+\.[\[\]\-\d]+\.[\[\]\-\d]+\.[\[\]\-\d]+$/', $whiteIP)){
719 array_push($badWhiteIPs, $whiteIP);
720 }
721 }
722 if(sizeof($badWhiteIPs) > 0){
723 return array('errorMsg' => "Please make sure you separate your IP addresses with commas. The following whitelisted IP addresses are invalid: " . implode(', ', $badWhiteIPs));
724 }
725 $opts['whitelisted'] = implode(',', $whiteIPs);
726 } else {
727 $opts['whitelisted'] = '';
728 }
729 $validUsers = array();
730 $invalidUsers = array();
731 foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['liveTraf_ignoreUsers'])) as $val){
732 if(strlen($val) > 0){
733 if(get_user_by('login', $val)){
734 array_push($validUsers, $val);
735 } else {
736 array_push($invalidUsers, $val);
737 }
738 }
739 }
740 $opts['apiKey'] = trim($opts['apiKey']);
741 if($opts['apiKey'] && (! preg_match('/^[a-fA-F0-9]+$/', $opts['apiKey'])) ){ //User entered something but it's garbage.
742 return array('errorMsg' => "You entered an API key but it is not in a valid format. It must consist only of characters A to F and 0 to 9.");
743 }
744
745 if(sizeof($invalidUsers) > 0){
746 return array('errorMsg' => "The following users you selected to ignore in live traffic reports are not valid on this system: " . implode(', ', $invalidUsers));
747 }
748 if(sizeof($validUsers) > 0){
749 $opts['liveTraf_ignoreUsers'] = implode(',', $validUsers);
750 } else {
751 $opts['liveTraf_ignoreUsers'] = '';
752 }
753
754 $validIPs = array();
755 $invalidIPs = array();
756 foreach(explode(',', preg_replace('/[\r\n\s\t]+/', '', $opts['liveTraf_ignoreIPs'])) as $val){
757 if(strlen($val) > 0){
758 if(preg_match('/^\d+\.\d+\.\d+\.\d+$/', $val)){
759 array_push($validIPs, $val);
760 } else {
761 array_push($invalidIPs, $val);
762 }
763 }
764 }
765 if(sizeof($invalidIPs) > 0){
766 return array('errorMsg' => "The following IPs you selected to ignore in live traffic reports are not valid: " . implode(', ', $invalidIPs));
767 }
768 if(sizeof($validIPs) > 0){
769 $opts['liveTraf_ignoreIPs'] = implode(',', $validIPs);
770 }
771
772 if(preg_match('/[a-zA-Z0-9\d]+/', $opts['liveTraf_ignoreUA'])){
773 $opts['liveTraf_ignoreUA'] = trim($opts['liveTraf_ignoreUA']);
774 } else {
775 $opts['liveTraf_ignoreUA'] = '';
776 }
777 if(! $opts['other_WFNet']){
778 $wfdb = new wfDB();
779 global $wpdb;
780 $p = $wpdb->base_prefix;
781 $wfdb->query("delete from $p"."wfBlocks where wfsn=1 and permanent=0");
782 }
783 foreach($opts as $key => $val){
784 if($key != 'apiKey'){ //Don't save API key yet
785 wfConfig::set($key, $val);
786 }
787 }
788
789 $reload = '';
790 $paidKeyMsg = false;
791
792
793 if(! $opts['apiKey']){ //Empty API key (after trim above), then try to get one.
794 $api = new wfAPI('', wfUtils::getWPVersion());
795 try {
796 $keyData = $api->call('get_anon_api_key');
797 if($keyData['ok'] && $keyData['apiKey']){
798 wfConfig::set('apiKey', $keyData['apiKey']);
799 wfConfig::set('isPaid', 0);
800 $reload = 'reload';
801 } else {
802 throw new Exception("We could not understand the Wordfence server's response because it did not contain an 'ok' and 'apiKey' element.");
803 }
804 } catch(Exception $e){
805 return array('errorMsg' => "Your options have been saved, but we encountered a problem. You left your API key blank, so we tried to get you a free API key from the Wordfence servers. However we encountered a problem fetching the free key: " . $e->getMessage());
806 }
807 } else if($opts['apiKey'] != wfConfig::get('apiKey')){
808 $api = new wfAPI($opts['apiKey'], wfUtils::getWPVersion());
809 try {
810 $res = $api->call('check_api_key', array(), array());
811 if($res['ok'] && isset($res['isPaid'])){
812 wfConfig::set('apiKey', $opts['apiKey']);
813 $reload = 'reload';
814 wfConfig::set('isPaid', $res['isPaid']); //res['isPaid'] is boolean coming back as JSON and turned back into PHP struct. Assuming JSON to PHP handles bools.
815 if($res['isPaid']){
816 $paidKeyMsg = true;
817 }
818 } else {
819 throw new Exception("We could not understand the Wordfence API server reply when updating your API key.");
820 }
821 } catch (Exception $e){
822 return array('errorMsg' => "Your options have been saved. However we noticed you changed your API key and we tried to verify it with the Wordfence servers and received an error: " . $e->getMessage());
823 }
824 }
825
826
827
828 //Clears next scan if scans are disabled. Schedules next scan if enabled.
829 if($err){
830 return array('errorMsg' => $err);
831 } else {
832 return array('ok' => 1, 'reload' => $reload, 'paidKeyMsg' => $paidKeyMsg );
833 }
834 }
835 public static function ajax_clearAllBlocked_callback(){
836 $op = $_POST['op'];
837 $wfLog = self::getLog();
838 if($op == 'blocked'){
839 wordfence::status(1, 'info', "Ajax request received to unblock All IP's including permanent blocks.");
840 $wfLog->unblockAllIPs();
841 } else if($op == 'locked'){
842 $wfLog->unlockAllIPs();
843 }
844 return array('ok' => 1);
845 }
846 public static function ajax_unlockOutIP_callback(){
847 $IP = $_POST['IP'];
848 self::getLog()->unlockOutIP($IP);
849 return array('ok' => 1);
850 }
851 public static function ajax_unblockIP_callback(){
852 $IP = $_POST['IP'];
853 self::getLog()->unblockIP($IP);
854 return array('ok' => 1);
855 }
856 public static function ajax_permBlockIP_callback(){
857 $IP = $_POST['IP'];
858 self::getLog()->blockIP($IP, "Manual permanent block by admin", false, true);
859 return array('ok' => 1);
860 }
861 public static function ajax_loadStaticPanel_callback(){
862 $mode = $_POST['mode'];
863 $wfLog = self::getLog();
864 if($mode == 'topScanners' || $mode == 'topLeechers'){
865 $results = $wfLog->getLeechers($mode);
866 } else if($mode == 'blockedIPs'){
867 $results = $wfLog->getBlockedIPs();
868 } else if($mode == 'lockedOutIPs'){
869 $results = $wfLog->getLockedOutIPs();
870 } else if($mode == 'throttledIPs'){
871 $results = $wfLog->getThrottledIPs();
872 }
873 return array('ok' => 1, 'results' => $results);
874 }
875 public static function ajax_blockIP_callback(){
876 $IP = trim($_POST['IP']);
877 if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', $IP)){
878 return array('err' => 1, 'errorMsg' => "Please enter a valid IP address to block.");
879 }
880 if($IP == wfUtils::getIP()){
881 return array('err' => 1, 'errorMsg' => "You can't block your own IP address.");
882 }
883 if(self::getLog()->isWhitelisted($IP)){
884 return array('err' => 1, 'errorMsg' => "The IP address $IP 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.");
885 }
886 if(wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers'){ //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google
887 if(wfCrawl::verifyCrawlerPTR('/googlebot\.com$/i', $IP)){
888 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.");
889 }
890 }
891 self::getLog()->blockIP($IP, $_POST['reason']);
892 return array('ok' => 1);
893 }
894 public static function ajax_reverseLookup_callback(){
895 $ips = explode(',', $_POST['ips']);
896 $res = array();
897 foreach($ips as $ip){
898 $res[$ip] = wfUtils::reverseLookup($ip);
899 }
900 return array('ok' => 1, 'ips' => $res);
901 }
902 public static function ajax_deleteIssue_callback(){
903 $wfIssues = new wfIssues();
904 $issueID = $_POST['id'];
905 $wfIssues->deleteIssue($issueID);
906 return array('ok' => 1);
907 }
908 public static function ajax_updateAllIssues_callback(){
909 $op = $_POST['op'];
910 $i = new wfIssues();
911 if($op == 'deleteIgnored'){
912 $i->deleteIgnored();
913 } else if($op == 'deleteNew'){
914 $i->deleteNew();
915 } else if($op == 'ignoreAllNew'){
916 $i->ignoreAllNew();
917 } else {
918 return array('errorMsg' => "An invalid operation was called.");
919 }
920 return array('ok' => 1);
921 }
922 public static function ajax_updateIssueStatus_callback(){
923 $wfIssues = new wfIssues();
924 $status = $_POST['status'];
925 $issueID = $_POST['id'];
926 if(! preg_match('/^(?:new|delete|ignoreP|ignoreC)$/', $status)){
927 return array('errorMsg' => "An invalid status was specified when trying to update that issue.");
928 }
929 $wfIssues->updateIssue($issueID, $status);
930 return array('ok' => 1);
931 }
932 public static function ajax_killScan_callback(){
933 wordfence::status(1, 'info', "Scan kill request received.");
934 wordfence::status(10, 'info', "SUM_KILLED:A request was received to kill the previous scan.");
935 wfUtils::clearScanLock(); //Clear the lock now because there may not be a scan running to pick up the kill request and clear the lock
936 wfScanEngine::requestKill();
937 return array(
938 'ok' => 1,
939 );
940 }
941 public static function ajax_loadIssues_callback(){
942 $i = new wfIssues();
943 $iss = $i->getIssues();
944 return array(
945 'issuesLists' => $iss,
946 'summary' => $i->getSummaryItems(),
947 'lastScanCompleted' => wfConfig::get('lastScanCompleted')
948 );
949 }
950 public static function ajax_ticker_callback(){
951 $wfdb = new wfDB();
952 global $wpdb;
953 $p = $wpdb->base_prefix;
954
955 $serverTime = $wfdb->querySingle("select unix_timestamp()");
956 $issues = new wfIssues();
957 $jsonData = array(
958 'serverTime' => $serverTime,
959 'msg' => $wfdb->querySingle("select msg from $p"."wfStatus where level < 3 order by ctime desc limit 1")
960 );
961 $events = array();
962 $alsoGet = $_POST['alsoGet'];
963 if(preg_match('/^logList_(404|hit|human|ruser|crawler|gCrawler|loginLogout)$/', $alsoGet, $m)){
964 $type = $m[1];
965 $newestEventTime = $_POST['otherParams'];
966 $listType = 'hits';
967 if($type == 'loginLogout'){
968 $listType = 'logins';
969 }
970 $events = self::getLog()->getHits($listType, $type, $newestEventTime);
971 }
972 $jsonData['events'] = $events;
973 $jsonData['alsoGet'] = $alsoGet; //send it back so we don't load data if panel has changed
974 return $jsonData;
975 }
976 public static function ajax_activityLogUpdate_callback(){
977 $issues = new wfIssues();
978 return array(
979 'ok' => 1,
980 'items' => self::getLog()->getStatusEvents($_POST['lastctime']),
981 'currentScanID' => $issues->getScanTime()
982 );
983 }
984 public static function ajax_deleteFile_callback(){
985 $issueID = $_POST['issueID'];
986 $wfIssues = new wfIssues();
987 $issue = $wfIssues->getIssueByID($issueID);
988 if(! $issue){
989 return array('errorMsg' => "Could not delete file because we could not find that issue.");
990 }
991 if(! $issue['data']['file']){
992 return array('errorMsg' => "Could not delete file because that issue does not appear to be a file related issue.");
993 }
994 $file = $issue['data']['file'];
995 $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
996 $localFile = realpath($localFile);
997 if(strpos($localFile, ABSPATH) !== 0){
998 return array('errorMsg' => "An invalid file was requested for deletion.");
999 }
1000 if(@unlink($localFile)){
1001 $wfIssues->updateIssue($issueID, 'delete');
1002 return array(
1003 'ok' => 1,
1004 'localFile' => $localFile,
1005 'file' => $file
1006 );
1007 } else {
1008 $err = error_get_last();
1009 return array('errorMsg' => "Could not delete file $file. The error was: " . $err['message']);
1010 }
1011 }
1012 public static function ajax_restoreFile_callback(){
1013 $issueID = $_POST['issueID'];
1014 $wfIssues = new wfIssues();
1015 $issue = $wfIssues->getIssueByID($issueID);
1016 if(! $issue){
1017 return array('cerrorMsg' => "We could not find that issue in our database.");
1018 }
1019 $dat = $issue['data'];
1020 $result = self::getWPFileContent($dat['file'], $dat['cType'], $dat['cName'], $dat['cVersion']);
1021 $file = $dat['file'];
1022 if($result['cerrorMsg']){
1023 return $result;
1024 } else if(! $result['fileContent']){
1025 return array('cerrorMsg' => "We could not get the original file to do a repair.");
1026 }
1027
1028 if(preg_match('/\.\./', $file)){
1029 return array('cerrorMsg' => "An invalid file was specified for repair.");
1030 }
1031 $localFile = ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $file);
1032 $fh = fopen($localFile, 'w');
1033 if(! $fh){
1034 $err = error_get_last();
1035 if(preg_match('/Permission denied/i', $err['message'])){
1036 $errMsg = "You don't have permission to repair that file. You need to either fix the file manually using FTP or change the file permissions and ownership so that your web server has write access to repair the file.";
1037 } else {
1038 $errMsg = "We could not write to that file. The error was: " . $err['message'];
1039 }
1040 return array('cerrorMsg' => $errMsg);
1041 }
1042 flock($fh, LOCK_EX);
1043 $bytes = fwrite($fh, $result['fileContent']);
1044 flock($fh, LOCK_UN);
1045 fclose($fh);
1046 if($bytes < 1){
1047 return array('cerrorMsg' => "We could not write to that file. ($bytes bytes written) You may not have permission to modify files on your WordPress server.");
1048 }
1049 $wfIssues->updateIssue($issueID, 'delete');
1050 return array(
1051 'ok' => 1,
1052 'file' => $localFile
1053 );
1054 }
1055 public static function ajax_scan_callback(){
1056 self::status(4, 'info', "Ajax request received to start scan.");
1057 $err = wfScanEngine::startScan();
1058 if($err){
1059 return array('errorMsg' => $err);
1060 } else {
1061 return array("ok" => 1);
1062 }
1063 }
1064 public static function startScan(){
1065 wfScanEngine::startScan();
1066 }
1067 public static function templateRedir(){
1068 $wfFunc = get_query_var('_wfsf');
1069 $wfLog = self::getLog();
1070 if($wfLog->logHitOK()){
1071 if( (! empty($wfFunc)) && is_404() ){
1072 $wfLog->logLeechAndBlock('404');
1073 } else {
1074 $wfLog->logLeechAndBlock('hit');
1075 }
1076 if(wfConfig::get('liveTrafficEnabled')){
1077 self::$hitID = $wfLog->logHit();
1078 add_action('wp_head', 'wordfence::wp_head');
1079 }
1080 }
1081
1082 if(! ($wfFunc == 'diff' || $wfFunc == 'view' || $wfFunc == 'sysinfo' || $wfFunc == 'unknownFiles' || $wfFunc == 'IPTraf' || $wfFunc == 'viewActivityLog' || $wfFunc == 'testmem' || $wfFunc == 'testtime')){
1083 return;
1084 }
1085 if(! wfUtils::isAdmin()){
1086 return;
1087 }
1088
1089 $nonce = $_GET['nonce'];
1090 if(! wp_verify_nonce($nonce, 'wp-ajax')){
1091 echo "Bad security token. It may have been more than 12 hours since you reloaded the page you came from. Try reloading the page you came from. If that doesn't work, please sign out and sign-in again.";
1092 exit(0);
1093 }
1094 if($wfFunc == 'diff'){
1095 self::wfFunc_diff();
1096 } else if($wfFunc == 'view'){
1097 self::wfFunc_view();
1098 } else if($wfFunc == 'sysinfo'){
1099 require('sysinfo.php');
1100 } else if($wfFunc == 'unknownFiles'){
1101 require('unknownFiles.php');
1102 } else if($wfFunc == 'IPTraf'){
1103 self::wfFunc_IPTraf();
1104 } else if($wfFunc == 'viewActivityLog'){
1105 self::wfFunc_viewActivityLog();
1106 } else if($wfFunc == 'testmem'){
1107 self::wfFunc_testmem();
1108 } else if($wfFunc == 'testtime'){
1109 self::wfFunc_testtime();
1110 }
1111 exit(0);
1112 }
1113 public static function memtest_error_handler($errno, $errstr, $errfile, $errline){
1114 echo "Error received: $errstr\n";
1115 }
1116 private static function wfFunc_testtime(){
1117 header('Content-Type: text/plain');
1118 ini_set('max_execution_time', 1800); //30 mins
1119 @error_reporting(E_ALL);
1120 @ini_set('display_errors','On');
1121 set_error_handler('wordfence::memtest_error_handler', E_ALL);
1122
1123 echo "Wordfence process duration benchmarking utility version " . WORDFENCE_VERSION . ".\n";
1124 echo "This utility tests how long your WordPress host allows a process to run.\n\n--Starting test--\n";
1125 echo "Starting timed test. This will take at least three minutes. Seconds elapsed are printed below.\nAn error after this line is not unusual. Read it and the elapsed seconds to determine max process running time on your host.\n";
1126 for($i = 1; $i <= 180; $i++){
1127 echo "\n$i:";
1128 for($j = 0; $j < 1000; $j++){
1129 echo '.';
1130 }
1131 flush();
1132 sleep(1);
1133 }
1134 echo "\n--Test complete.--\n\nCongratulations, your web host allows your PHP processes to run at least 3 minutes.\n";
1135 exit();
1136 }
1137 private static function wfFunc_testmem(){
1138 header('Content-Type: text/plain');
1139 @error_reporting(E_ALL);
1140 @ini_set('display_errors','On');
1141 set_error_handler('wordfence::memtest_error_handler', E_ALL);
1142
1143 echo "Wordfence Memory benchmarking utility version " . WORDFENCE_VERSION . ".\n";
1144 echo "This utility tests if your WordPress host respects the maximum memory configured\nin their php.ini file, or if they are using other methods to limit your access to memory.\n\n--Starting test--\n";
1145 echo "Current maximum memory configured in php.ini: " . ini_get('memory_limit') . "\n";
1146 echo "Current memory usage: " . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . "M\n";
1147 echo "Setting max memory to 90M.\n";
1148 ini_set('memory_limit', '90M');
1149 echo "Starting memory benchmark. Seeing an error after this line is not unusual. Read the error carefully\nto determine how much memory your host allows. We have requested 90 megabytes.\n";
1150 if(memory_get_usage(true) < 1){
1151 echo "Exiting test because memory_get_usage() returned a negative number\n";
1152 }
1153 if(memory_get_usage(true) > (1024 * 1024 * 1024)){
1154 echo "Exiting because current memory usage is greater than a gigabyte.\n";
1155 }
1156 $arr = array();
1157 //256 bytes
1158 $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111222222222222222222233333333333333334444444444444444444444444555555555555666666666666666666";
1159 $finalUsage = '0';
1160 while(true){
1161 if(memory_get_usage(true) > 80 * 1024 * 1024){
1162 $finalUsage = sprintf('%.2f', (memory_get_usage(true) / 1024 / 1024));
1163 echo "Completing test after benchmarking up to " . $finalUsage . " megabytes.\n";
1164 break;
1165 }
1166 for($i = 0; $i < 1024; $i++){ //Roughly 1 megabyte if it's 256K and actual array size is 4x data size
1167 $arr[] = $chars;
1168 }
1169 }
1170 echo "--Test complete.--\n\nCongratulations, your web host allows you to use at least $finalUsage megabytes of memory for each PHP process hosting your WordPress site.\n";
1171 exit();
1172 }
1173 public static function wp_head(){
1174 echo '<script type="text/javascript">var src="' . wfUtils::getBaseURL() . 'visitor.php?hid=' . wfUtils::encrypt(self::$hitID) . '"; if(window.location.protocol == "https:"){ src = src.replace("http:", "https:"); } var wfHTImg = new Image(); wfHTImg.src=src;</script>';
1175 }
1176 public static function shutdownAction(){
1177 }
1178 public static function wfFunc_viewActivityLog(){
1179 require('viewFullActivityLog.php');
1180 exit(0);
1181 }
1182 public static function wfFunc_IPTraf(){
1183 $IP = $_GET['IP'];
1184 $reverseLookup = wfUtils::reverseLookup($IP);
1185 if(! preg_match('/^\d+\.\d+\.\d+\.\d+$/', $IP)){
1186 echo "An invalid IP address was specified.";
1187 exit(0);
1188 }
1189 $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
1190 $results = array_merge(
1191 $wfLog->getHits('hits', 'hit', 0, 10000, $IP),
1192 $wfLog->getHits('hits', '404', 0, 10000, $IP)
1193 );
1194 usort($results, 'wordfence::iptrafsort');
1195 for($i = 0; $i < sizeof($results); $i++){
1196 if(array_key_exists($i + 1, $results)){
1197 $results[$i]['timeSinceLastHit'] = sprintf('%.4f', $results[$i]['ctime'] - $results[$i + 1]['ctime']);
1198 } else {
1199 $results[$i]['timeSinceLastHit'] = '';
1200 }
1201 }
1202 require('IPTraf.php');
1203 exit(0);
1204 }
1205 public static function iptrafsort($b, $a){
1206 if($a['ctime'] == $b['ctime']){ return 0; }
1207 return ($a['ctime'] < $b['ctime']) ? -1 : 1;
1208 }
1209 public static function wfFunc_view(){
1210 $localFile = ABSPATH . '/' . preg_replace('/^(?:\.\.|[\/]+)/', '', $_GET['file']);
1211 if(strpos($localFile, '..') !== false){
1212 echo "Invalid file requested. (Relative paths not allowed)";
1213 exit();
1214 }
1215 $lang = false;
1216 $cont = @file_get_contents($localFile);
1217 $isEmpty = false;
1218 if(! $cont){
1219 if(file_exists($localFile) && filesize($localFile) === 0){ //There's a remote possibility that very large files on 32 bit systems will return 0 here, but it's about 1 in 2 billion
1220 $isEmpty = true;
1221 } else {
1222 $err = error_get_last();
1223 echo "We could not open the requested file for reading. The error was: " . $err['message'];
1224 exit(0);
1225 }
1226 }
1227 $fileMTime = @filemtime($localFile);
1228 $fileMTime = date('l jS \of F Y h:i:s A', $fileMTime);
1229 if(wfUtils::fileOver2Gigs($localFile)){
1230 $fileSize = "Greater than 2 Gigs";
1231 } else {
1232 $fileSize = @filesize($localFile); //Checked if over 2 gigs above
1233 $fileSize = number_format($fileSize, 0, '', ',') . ' bytes';
1234 }
1235
1236 require 'wfViewResult.php';
1237 exit(0);
1238 }
1239 public static function wfFunc_diff(){
1240 $result = self::getWPFileContent($_GET['file'], $_GET['cType'], $_GET['cName'], $_GET['cVersion']);
1241 if($result['errorMsg']){
1242 echo $result['errorMsg'];
1243 exit(0);
1244 } else if(! $result['fileContent']){
1245 echo "We could not get the contents of the original file to do a comparison.";
1246 exit(0);
1247 }
1248
1249 $localFile = realpath(ABSPATH . '/' . preg_replace('/^[\.\/]+/', '', $_GET['file']));
1250 $localContents = file_get_contents($localFile);
1251 if($localContents == $result['fileContent']){
1252 $diffResult = '';
1253 } else {
1254 $diff = new Diff(
1255 //Treat DOS and Unix files the same
1256 preg_split("/(?:\r\n|\n)/", $result['fileContent']),
1257 preg_split("/(?:\r\n|\n)/", $localContents),
1258 array()
1259 );
1260 $renderer = new Diff_Renderer_Html_SideBySide;
1261 $diffResult = $diff->Render($renderer);
1262 }
1263 require 'diffResult.php';
1264 exit(0);
1265 }
1266 public static function initAction(){
1267 global $wp;
1268 if (!is_object($wp)) return; //Suggested fix for compatability with "Portable phpmyadmin"
1269
1270 $wp->add_query_var('_wfsf');
1271 //add_rewrite_rule('wfStaticFunc/([a-zA-Z0-9]+)/?$', 'index.php?wfStaticFunc=' . $matches[1], 'top');
1272 $cookieName = 'wfvt_' . crc32(site_url());
1273 $c = isset($_COOKIES[$cookieName]) ? isset($_COOKIES[$cookieName]) : false;
1274 if($c){
1275 self::$newVisit = false;
1276 } else {
1277 self::$newVisit = true;
1278 }
1279 @setcookie($cookieName, uniqid(), time() + 1800, '/');
1280 }
1281 public static function admin_init(){
1282 if(! wfUtils::isAdmin()){ return; }
1283 foreach(array('activate', 'scan', 'sendActivityLog', 'restoreFile', 'deleteFile', 'removeExclusion', 'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues', 'reverseLookup', 'unlockOutIP', 'unblockIP', 'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed', 'startTourAgain') as $func){
1284 add_action('wp_ajax_wordfence_' . $func, 'wordfence::ajaxReceiver');
1285 }
1286
1287 if(preg_match('/^Wordfence/', @$_GET['page'])){
1288 wp_enqueue_style('wp-pointer');
1289 wp_enqueue_script('wp-pointer');
1290 wp_enqueue_style('wordfence-main-style', wfUtils::getBaseURL() . 'css/main.css', '', WORDFENCE_VERSION);
1291 wp_enqueue_style('wordfence-colorbox-style', wfUtils::getBaseURL() . 'css/colorbox.css', '', WORDFENCE_VERSION);
1292 wp_enqueue_style('wordfence-dttable-style', wfUtils::getBaseURL() . 'css/dt_table.css', '', WORDFENCE_VERSION);
1293
1294 wp_enqueue_script('json2');
1295 wp_enqueue_script('jquery.tmpl', wfUtils::getBaseURL() . 'js/jquery.tmpl.min.js', array('jquery'), WORDFENCE_VERSION);
1296 wp_enqueue_script('jquery.colorbox', wfUtils::getBaseURL() . 'js/jquery.colorbox-min.js', array('jquery'), WORDFENCE_VERSION);
1297 wp_enqueue_script('jquery.dataTables', wfUtils::getBaseURL() . 'js/jquery.dataTables.min.js', array('jquery'), WORDFENCE_VERSION);
1298 //wp_enqueue_script('jquery.tools', wfUtils::getBaseURL() . 'js/jquery.tools.min.js', array('jquery'));
1299 wp_enqueue_script('wordfenceAdminjs', wfUtils::getBaseURL() . 'js/admin.js', array('jquery'), WORDFENCE_VERSION);
1300 self::setupAdminVars();
1301 } else {
1302 wp_enqueue_style('wp-pointer');
1303 wp_enqueue_script('wp-pointer');
1304 wp_enqueue_script('wordfenceAdminjs', wfUtils::getBaseURL() . 'js/tourTip.js', array('jquery'), WORDFENCE_VERSION);
1305 self::setupAdminVars();
1306 }
1307
1308 }
1309 private static function setupAdminVars(){
1310 wp_localize_script('wordfenceAdminjs', 'WordfenceAdminVars', array(
1311 'ajaxURL' => admin_url('admin-ajax.php'),
1312 'firstNonce' => wp_create_nonce('wp-ajax'),
1313 'siteBaseURL' => wfUtils::getSiteBaseURL(),
1314 'debugOn' => wfConfig::get('debugOn', 0),
1315 'tourClosed' => wfConfig::get('tourClosed', 0)
1316 ));
1317 }
1318 public static function configure_warning(){
1319 if(! preg_match('/WordfenceSecOpt/', $_SERVER['REQUEST_URI'])){
1320 $numRun = wfConfig::get('alertEmailMsgCount', 0);
1321 if($numRun <= 3){
1322 echo '<div id="wordfenceConfigWarning" class="updated fade"><p><strong>Please set up an email address to receive Wordfence security alerts</strong> on the <a href="admin.php?page=WordfenceSecOpt">Wordfence Options Page</a>. This message will appear ' . (3 - $numRun) . ' more times.</p></div>';
1323 wfConfig::set('alertEmailMsgCount', ++$numRun);
1324 }
1325
1326 }
1327 }
1328 public static function noKeyError(){
1329 echo '<div id="wordfenceConfigWarning" class="fade error"><p><strong>Wordfence could not get an API key from the Wordfence scanning servers when it activated.</strong> You can try to fix this by going to the Wordfence "options" page and hitting "Save Changes". This will cause Wordfence to retry fetching an API key for you. If you keep seeing this error it usually means your WordPress server can\'t connect to our scanning servers. You can try asking your WordPress host to allow your WordPress server to connect to noc1.wordfence.com.</p></div>';
1330 }
1331 public static function admin_menus(){
1332 if(! wfUtils::isAdmin()){ return; }
1333 /* Removed this because we now have the tour.
1334 if(! wfConfig::get('alertEmails')){
1335 if(wfUtils::isAdminPageMU()){
1336 add_action('network_admin_notices', 'wordfence::configure_warning');
1337 } else {
1338 add_action('admin_notices', 'wordfence::configure_warning');
1339 }
1340 }
1341 */
1342 if(! wfConfig::get('apiKey')){
1343 if(wfUtils::isAdminPageMU()){
1344 add_action('network_admin_notices', 'wordfence::noKeyError');
1345 } else {
1346 add_action('admin_notices', 'wordfence::noKeyError');
1347 }
1348 }
1349 add_submenu_page("Wordfence", "Scan", "Scan", "activate_plugins", "Wordfence", 'wordfence::menu_scan');
1350 add_menu_page('Wordfence', 'Wordfence', 'activate_plugins', 'Wordfence', 'wordfence::menu_scan', wfUtils::getBaseURL() . 'images/wordfence-logo-16x16.png');
1351 if(wfConfig::get('liveTrafficEnabled')){
1352 add_submenu_page("Wordfence", "Live Traffic", "Live Traffic", "activate_plugins", "WordfenceActivity", 'wordfence::menu_activity');
1353 }
1354 add_submenu_page('Wordfence', 'Blocked IPs', 'Blocked IPs', 'activate_plugins', 'WordfenceBlockedIPs', 'wordfence::menu_blockedIPs');
1355 add_submenu_page("Wordfence", "Country Blocking", "Country Blocking", "activate_plugins", "WordfenceCountryBlocking", 'wordfence::menu_countryBlocking');
1356 add_submenu_page("Wordfence", "Scan Schedule", "Scan Schedule", "activate_plugins", "WordfenceScanSchedule", 'wordfence::menu_scanSchedule');
1357 add_submenu_page("Wordfence", "Options", "Options", "activate_plugins", "WordfenceSecOpt", 'wordfence::menu_options');
1358 }
1359 public static function menu_options(){
1360 require 'menu_options.php';
1361 }
1362 public static function menu_blockedIPs(){
1363 require 'menu_blockedIPs.php';
1364 }
1365 public static function menu_scanSchedule(){
1366 require 'menu_scanSchedule.php';
1367 }
1368 public static function menu_countryBlocking(){
1369 require 'menu_countryBlocking.php';
1370 }
1371 public static function menu_activity(){
1372 require 'menu_activity.php';
1373 }
1374 public static function menu_scan(){
1375 require 'menu_scan.php';
1376 }
1377 public static function status($level /* 1 has highest visibility */, $type /* info|error */, $msg){
1378 if($level > 3 && $level < 10 && (! self::isDebugOn())){ //level 10 and higher is for summary messages
1379 return false;
1380 }
1381 if($type != 'info' && $type != 'error'){ error_log("Invalid status type: $type"); return; }
1382 if(self::$printStatus){
1383 echo "STATUS: $level : $type : $msg\n";
1384 } else {
1385 self::getLog()->addStatus($level, $type, $msg);
1386 }
1387 }
1388 public static function profileUpdateAction($userID, $newDat){
1389 if(wfConfig::get('other_pwStrengthOnUpdate')){
1390 $oldDat = get_userdata($userID);
1391 if($newDat->user_pass != $oldDat->user_pass){
1392 $wf = new wfScanEngine();
1393 $wf->scanUserPassword($userID);
1394 $wf->emailNewIssues();
1395 }
1396 }
1397 }
1398 public static function genFilter($gen, $type){
1399 if(wfConfig::get('other_hideWPVersion')){
1400 return '';
1401 } else {
1402 return $gen;
1403 }
1404 }
1405 public static function preCommentApprovedFilter($approved, $cData){
1406 if( $approved == 1 && (! is_user_logged_in()) && wfConfig::get('other_noAnonMemberComments') ){
1407 $user = get_user_by('email', trim($cData['comment_author_email']));
1408 if($user){
1409 return 0; //hold for moderation if the user is not signed in but used a members email
1410 }
1411 }
1412
1413 if(($approved == 1 || $approved == 0) && wfConfig::get('other_scanComments')){
1414 $wf = new wfScanEngine();
1415 if($wf->isBadComment($cData['comment_author'], $cData['comment_author_email'], $cData['comment_author_url'], $cData['comment_author_IP'], $cData['comment_content'])){
1416 return 'spam';
1417 }
1418 }
1419 return $approved;
1420 }
1421 public static function getMyHomeURL(){
1422 return admin_url('admin.php?page=Wordfence', 'http');
1423 }
1424 public static function getMyOptionsURL(){
1425 return admin_url('admin.php?page=WordfenceSecOpt', 'http');
1426 }
1427
1428 public static function alert($subject, $alertMsg, $IP){
1429 $IPMsg = "";
1430 if($IP){
1431 $IPMsg = "User IP: $IP\n";
1432 $reverse = wfUtils::reverseLookup($IP);
1433 if($reverse){
1434 $IPMsg .= "User hostname: " . $reverse . "\n";
1435 }
1436 $userLoc = wfUtils::getIPGeo($IP);
1437 if($userLoc){
1438 $IPMsg .= "User location: ";
1439 if($userLoc['city']){
1440 $IPMsg .= $userLoc['city'] . ', ';
1441 }
1442 $IPMsg .= $userLoc['countryName'] . "\n";
1443 }
1444 }
1445 $content = wfUtils::tmpl('email_genericAlert.php', array(
1446 'subject' => $subject,
1447 'blogName' => get_bloginfo('name', 'raw'),
1448 'alertMsg' => $alertMsg,
1449 'IPMsg' => $IPMsg,
1450 'date' => date('l jS \of F Y \a\t h:i:s A'),
1451 'myHomeURL' => self::getMyHomeURL(),
1452 'myOptionsURL' => self::getMyOptionsURL()
1453 ));
1454 $emails = wfConfig::getAlertEmails();
1455 if(sizeof($emails) < 1){ return; }
1456 $shortSiteURL = preg_replace('/^https?:\/\//i', '', site_url());
1457 $subject = "[Wordfence Alert] $shortSiteURL " . $subject;
1458 wp_mail(implode(',', $emails), $subject, $content);
1459 }
1460 private static function getLog(){
1461 if(! self::$wfLog){
1462 $wfLog = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion());
1463 self::$wfLog = $wfLog;
1464 }
1465 return self::$wfLog;
1466 }
1467 public static function statusPrep(){
1468 wfConfig::set_ser('wfStatusStartMsgs', array());
1469 wordfence::status(10, 'info', "SUM_PREP:Preparing a new scan.");
1470 }
1471 //In the following functions statusStartMsgs is serialized into the DB so it persists between forks
1472 public static function statusStart($msg){
1473 $statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array());
1474 $statusStartMsgs[] = $msg;
1475 wfConfig::set_ser('wfStatusStartMsgs', $statusStartMsgs);
1476 self::status(10, 'info', 'SUM_START:' . $msg);
1477 return sizeof($statusStartMsgs) - 1;
1478 }
1479 public static function statusEnd($idx, $haveIssues){
1480 $statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array());
1481 if($haveIssues){
1482 self::status(10, 'info', 'SUM_ENDBAD:' . $statusStartMsgs[$idx]);
1483 } else {
1484 self::status(10, 'info', 'SUM_ENDOK:' . $statusStartMsgs[$idx]);
1485 }
1486 $statusStartMsgs[$idx] = '';
1487 wfConfig::set_ser('wfStatusStartMsgs', $statusStartMsgs);
1488 }
1489 public static function statusEndErr(){
1490 $statusStartMsgs = wfConfig::get_ser('wfStatusStartMsgs', array());
1491 for($i = 0; $i < sizeof($statusStartMsgs); $i++){
1492 if(empty($statusStartMsgs[$i]) === false){
1493 self::status(10, 'info', 'SUM_ENDERR:' . $statusStartMsgs[$i]);
1494 $statusStartMsgs[$i] = '';
1495 }
1496 }
1497 }
1498 public static function statusDisabled($msg){
1499 self::status(10, 'info', "SUM_DISABLED:" . $msg);
1500 }
1501 public static function statusPaidOnly($msg){
1502 self::status(10, 'info', "SUM_PAIDONLY:" . $msg);
1503 }
1504 public static function wfSchemaExists(){
1505 $db = new wfDB();
1506 global $wpdb; $prefix = $wpdb->base_prefix;
1507 $exists = $db->querySingle("show tables like '$prefix"."wfConfig'");
1508 return $exists ? true : false;
1509 }
1510 public static function isDebugOn(){
1511 if(is_null(self::$debugOn)){
1512 if(wfConfig::get('debugOn')){
1513 self::$debugOn = true;
1514 } else {
1515 self::$debugOn = false;
1516 }
1517 }
1518 return self::$debugOn;
1519 }
1520 /*
1521 public static function moreCronReccurences(){
1522 return array(
1523 'everyminute' => array('interval' => 60, 'display' => 'Once Every Minute'),
1524 );
1525 }
1526 */
1527 }
1528 ?>
1529