3) { // 3 because the cron job starts two processes and pgrep finds itself die("schedule.php is already running. Exiting ($PCount)\n"); } /*TODO: make it awesome, make it flexible! INSERT INTO users_geodistribution (Code, Users) SELECT g.Code, COUNT(u.ID) AS Users FROM geoip_country AS g JOIN users_main AS u ON INET_ATON(u.IP) BETWEEN g.StartIP AND g.EndIP WHERE u.Enabled = '1' GROUP BY g.Code ORDER BY Users DESC */ /*************************************************************************\ //--------------Schedule page -------------------------------------------// This page is run every 15 minutes, by cron. \*************************************************************************/ function next_biweek() { $Date = date('d'); if ($Date < 22 && $Date >= 8) { $Return = 22; } else { $Return = 8; } return $Return; } function next_day() { $Tomorrow = time(0, 0, 0, date('m'), date('d') + 1, date('Y')); return date('d', $Tomorrow); } function next_hour() { $Hour = time(date('H') + 1, 0, 0, date('m'), date('d'), date('Y')); return date('H', $Hour); } if ((!isset($argv[1]) || $argv[1] != SCHEDULE_KEY) && !check_perms('admin_schedule')) { // authorization, Fix to allow people with perms hit this page. error(403); } if (check_perms('admin_schedule')) { authorize(); View::show_header(); echo '
';
}

$DB->query("
	SELECT NextHour, NextDay, NextBiWeekly
	FROM schedule");
list($Hour, $Day, $BiWeek) = $DB->next_record();
$NextHour = next_hour();
$NextDay = next_day();
$NextBiWeek = next_biweek();

$DB->query("
	UPDATE schedule
	SET
		NextHour = $NextHour,
		NextDay = $NextDay,
		NextBiWeekly = $NextBiWeek");

$NoDaily = isset($argv[2]) && $argv[2] == 'nodaily';

$sqltime = sqltime();

echo "$sqltime\n";

/*************************************************************************\
//--------------Run every time ------------------------------------------//

These functions are run every time the script is executed (every 15
minutes).

\*************************************************************************/


echo "Ran every-time functions\n";

//------------- Freeleech -----------------------------------------------//

//We use this to control 6 hour freeleeches. They're actually 7 hours, but don't tell anyone.
/*
$TimeMinus = time_minus(3600 * 7);

$DB->query("
	SELECT DISTINCT GroupID
	FROM torrents
	WHERE FreeTorrent = '1'
		AND FreeLeechType = '3'
		AND Time < '$TimeMinus'");
while (list($GroupID) = $DB->next_record()) {
	$Cache->delete_value("torrents_details_$GroupID");
	$Cache->delete_value("torrent_group_$GroupID");
}
$DB->query("
	UPDATE torrents
	SET FreeTorrent = '0',
		FreeLeechType = '0'
	WHERE FreeTorrent = '1'
		AND FreeLeechType = '3'
		AND Time < '$TimeMinus'");
*/
sleep(5);
//------------- Delete unpopular tags -----------------------------------//
$DB->query("
	DELETE FROM torrents_tags
	WHERE NegativeVotes > PositiveVotes");

//------------- Expire old FL Tokens and clear cache where needed ------//
$sqltime = sqltime();
$DB->query("
	SELECT DISTINCT UserID
	FROM users_freeleeches
	WHERE Expired = FALSE
		AND Time < '$sqltime' - INTERVAL 4 DAY");
while (list($UserID) = $DB->next_record()) {
	$Cache->delete_value('users_tokens_'.$UserID[0]);
}

$DB->query("
	SELECT uf.UserID, t.info_hash
	FROM users_freeleeches AS uf
		JOIN torrents AS t ON uf.TorrentID = t.ID
	WHERE uf.Expired = FALSE
		AND uf.Time < '$sqltime' - INTERVAL 4 DAY");
while (list($UserID, $InfoHash) = $DB->next_record(MYSQLI_NUM, false)) {
	Tracker::update_tracker('remove_token', array('info_hash' => rawurlencode($InfoHash), 'userid' => $UserID));
}
$DB->query("
	UPDATE users_freeleeches
	SET Expired = True
	WHERE Time < '$sqltime' - INTERVAL 4 DAY");




/*************************************************************************\
//--------------Run every hour ------------------------------------------//

These functions are run every hour.

\*************************************************************************/


if ($Hour != $NextHour || $_GET['runhour'] || isset($argv[2])) {
	echo "Ran hourly functions\n";

	//------------- Front page stats ----------------------------------------//

	//Love or hate, this makes things a hell of a lot faster

	if ($Hour % 2 == 0) {
		$DB->query("
			SELECT COUNT(uid) AS Snatches
			FROM xbt_snatched");
		list($SnatchStats) = $DB->next_record();
		$Cache->cache_value('stats_snatches', $SnatchStats, 0);
	}

	$DB->query("
		SELECT IF(remaining = 0, 'Seeding', 'Leeching') AS Type,
			COUNT(uid)
		FROM xbt_files_users
		WHERE active = 1
		GROUP BY Type");
	$PeerCount = $DB->to_array(0, MYSQLI_NUM, false);
	$SeederCount = isset($PeerCount['Seeding'][1]) ? $PeerCount['Seeding'][1] : 0;
	$LeecherCount = isset($PeerCount['Leeching'][1]) ? $PeerCount['Leeching'][1] : 0;
	$Cache->cache_value('stats_peers', array($LeecherCount, $SeederCount), 0);

	$DB->query("
		SELECT COUNT(ID)
		FROM users_main
		WHERE Enabled = '1'
			AND LastAccess > '".time_minus(3600 * 24)."'");
	list($UserStats['Day']) = $DB->next_record();

	$DB->query("
		SELECT COUNT(ID)
		FROM users_main
		WHERE Enabled = '1'
			AND LastAccess > '".time_minus(3600 * 24 * 7)."'");
	list($UserStats['Week']) = $DB->next_record();

	$DB->query("
		SELECT COUNT(ID)
		FROM users_main
		WHERE Enabled = '1'
			AND LastAccess > '".time_minus(3600 * 24 * 30)."'");
	list($UserStats['Month']) = $DB->next_record();

	$Cache->cache_value('stats_users', $UserStats, 0);

	//------------- Record who's seeding how much, used for ratio watch

	$DB->query("TRUNCATE TABLE users_torrent_history_temp");

	// Find seeders that have announced within the last hour
	$DB->query("
		INSERT INTO users_torrent_history_temp
			(UserID, NumTorrents)
		SELECT uid, COUNT(DISTINCT fid)
		FROM xbt_files_users
		WHERE mtime > unix_timestamp(NOW() - INTERVAL 1 HOUR)
			AND Remaining = 0
		GROUP BY uid");

	// Mark new records as "checked" and set the current time as the time
	// the user started seeding  seeded.
	// Finished = 1 means that the user hasn't been seeding exactly  earlier today.
	// This query will only do something if the next one inserted new rows last hour.
	$DB->query("
		UPDATE users_torrent_history AS h
			JOIN users_torrent_history_temp AS t ON t.UserID = h.UserID
					AND t.NumTorrents = h.NumTorrents
		SET h.Finished = '0',
			h.LastTime = UNIX_TIMESTAMP(NOW())
		WHERE h.Finished = '1'
			AND h.Date = UTC_DATE() + 0");

	// Insert new rows for users who haven't been seeding exactly  torrents earlier today
	// and update the time spent seeding  torrents for the others.
	// Primary table index: (UserID, NumTorrents, Date).
	$DB->query("
		INSERT INTO users_torrent_history
			(UserID, NumTorrents, Date)
		SELECT UserID, NumTorrents, UTC_DATE() + 0
		FROM users_torrent_history_temp
		ON DUPLICATE KEY UPDATE
			Time = Time + UNIX_TIMESTAMP(NOW()) - LastTime,
			LastTime = UNIX_TIMESTAMP(NOW())");

	//------------- Promote users -------------------------------------------//
	sleep(5);
	$Criteria = array();
	$Criteria[] = array('From' => USER, 'To' => MEMBER,  'MinUpload' => 10 * 1024 * 1024 * 1024,  'MinRatio' => 0.7,  'MinUploads' => 0,  'MaxTime' => time_minus(3600 * 24 * 7));
	$Criteria[] = array('From' => MEMBER, 'To' => POWER, 'MinUpload' => 25 * 1024 * 1024 * 1024,  'MinRatio' => 1.05, 'MinUploads' => 5,  'MaxTime' => time_minus(3600 * 24 * 7 * 2));
	$Criteria[] = array('From' => POWER, 'To' => ELITE,  'MinUpload' => 100 * 1024 * 1024 * 1024, 'MinRatio' => 1.05, 'MinUploads' => 50, 'MaxTime' => time_minus(3600 * 24 * 7 * 4));
	$Criteria[] = array('From' => ELITE, 'To' => TORRENT_MASTER, 'MinUpload' => 500 * 1024 * 1024 * 1024, 'MinRatio' => 1.05, 'MinUploads' => 500, 'MaxTime' => time_minus(3600 * 24 * 7 * 8));
	$Criteria[] = array(
		'From' => TORRENT_MASTER,
		'To' => POWER_TM,
		'MinUpload' => 500 * 1024 * 1024 * 1024,
		'MinRatio' => 1.05,
		'MinUploads' => 500,
		'MaxTime' => time_minus(3600 * 24 * 7 * 8),
		'Extra' => '
				(
					SELECT COUNT(DISTINCT GroupID)
					FROM torrents
					WHERE UserID = users_main.ID
				) >= 500');
	$Criteria[] = array(
		'From' => POWER_TM,
		'To' => ELITE_TM,
		'MinUpload' => 500 * 1024 * 1024 * 1024,
		'MinRatio' => 1.05,
		'MinUploads' => 500,
		'MaxTime' => time_minus(3600 * 24 * 7 * 8),
		'Extra' => "
				(
					SELECT COUNT(ID)
					FROM torrents
					WHERE ((LogScore = 100 AND Format = 'FLAC')
						OR (Media = 'Vinyl' AND Format = 'FLAC')
						OR (Media = 'WEB' AND Format = 'FLAC')
						OR (Media = 'DVD' AND Format = 'FLAC')
						OR (Media = 'Soundboard' AND Format = 'FLAC')
						OR (Media = 'Cassette' AND Format = 'FLAC')
						OR (Media = 'SACD' AND Format = 'FLAC')
						OR (Media = 'Blu-ray' AND Format = 'FLAC')
						OR (Media = 'DAT' AND Format = 'FLAC')
						)
						AND UserID = users_main.ID
				) >= 500");

	 foreach ($Criteria as $L) { // $L = Level
		$Query = "
				SELECT ID
				FROM users_main
					JOIN users_info ON users_main.ID = users_info.UserID
				WHERE PermissionID = ".$L['From']."
					AND Warned = '0000-00-00 00:00:00'
					AND Uploaded >= '$L[MinUpload]'
					AND (Uploaded / Downloaded >='$L[MinRatio]' OR (Uploaded / Downloaded IS NULL))
					AND JoinDate < '$L[MaxTime]'
					AND (
						SELECT COUNT(ID)
						FROM torrents
						WHERE UserID = users_main.ID
						) >= '$L[MinUploads]'
					AND Enabled = '1'";
		if (!empty($L['Extra'])) {
			$Query .= ' AND '.$L['Extra'];
		}

		$DB->query($Query);

		$UserIDs = $DB->collect('ID');

		if (count($UserIDs) > 0) {
			foreach ($UserIDs as $UserID) {
				/*$Cache->begin_transaction("user_info_$UserID");
				$Cache->update_row(false, array('PermissionID' => $L['To']));
				$Cache->commit_transaction(0);*/
				$Cache->delete_value("user_info_$UserID");
				$Cache->delete_value("user_info_heavy_$UserID");
				$Cache->delete_value("user_stats_$UserID");
				$Cache->delete_value("enabled_$UserID");
				$DB->query("
					UPDATE users_info
					SET AdminComment = CONCAT('".sqltime()." - Class changed to ".Users::make_class_string($L['To'])." by System\n\n', AdminComment)
					WHERE UserID = $UserID");
				Misc::send_pm($UserID, 0, 'You have been promoted to '.Users::make_class_string($L['To']), 'Congratulations on your promotion to '.Users::make_class_string($L['To'])."!\n\nTo read more about ".SITE_NAME."'s user classes, read [url=https://".SSL_SITE_URL."/wiki.php?action=article&name=userclasses]this wiki article[/url].");
			}
			$DB->query("
				UPDATE users_main
				SET PermissionID = ".$L['To']."
				WHERE ID IN(".implode(',', $UserIDs).')');
		}

		// Demote users with less than the required uploads

		$Query = "
			SELECT ID
			FROM users_main
				JOIN users_info ON users_main.ID = users_info.UserID
			WHERE PermissionID = '$L[To]'
				AND ( Uploaded < '$L[MinUpload]'
					OR (
						SELECT COUNT(ID)
						FROM torrents
						WHERE UserID = users_main.ID
						) < '$L[MinUploads]'";
			if (!empty($L['Extra'])) {
				$Query .= ' OR NOT '.$L['Extra'];
			}
			$Query .= "
					)
				AND Enabled = '1'";

		$DB->query($Query);
		$UserIDs = $DB->collect('ID');

		if (count($UserIDs) > 0) {
			foreach ($UserIDs as $UserID) {
				/*$Cache->begin_transaction("user_info_$UserID");
				$Cache->update_row(false, array('PermissionID' => $L['From']));
				$Cache->commit_transaction(0);*/
				$Cache->delete_value("user_info_$UserID");
				$Cache->delete_value("user_info_heavy_$UserID");
				$Cache->delete_value("user_stats_$UserID");
				$Cache->delete_value("enabled_$UserID");
				$DB->query("
					UPDATE users_info
					SET AdminComment = CONCAT('".sqltime()." - Class changed to ".Users::make_class_string($L['From'])." by System\n\n', AdminComment)
					WHERE UserID = $UserID");
				Misc::send_pm($UserID, 0, 'You have been demoted to '.Users::make_class_string($L['From']), "You now only qualify for the \"".Users::make_class_string($L['From'])."\" user class.\n\nTo read more about ".SITE_NAME."'s user classes, read [url=https://".SSL_SITE_URL."/wiki.php?action=article&name=userclasses]this wiki article[/url].");
			}
			$DB->query("
				UPDATE users_main
				SET PermissionID = ".$L['From']."
				WHERE ID IN(".implode(',', $UserIDs).')');
		}
	}


	//------------- Expire invites ------------------------------------------//
	sleep(3);
	$DB->query("
		SELECT InviterID
		FROM invites
		WHERE Expires < '$sqltime'");
	$Users = $DB->to_array();
	foreach ($Users as $UserID) {
		list($UserID) = $UserID;
		$DB->query("
			SELECT Invites, PermissionID
			FROM users_main
			WHERE ID = $UserID");
		list($Invites, $PermID) = $DB->next_record();
		if (($Invites < 2 && $Classes[$PermID]['Level'] <= $Classes[POWER]['Level']) || ($Invites < 4 && $PermID == ELITE)) {
			$DB->query("
				UPDATE users_main
				SET Invites = Invites + 1
				WHERE ID = $UserID");
			$Cache->begin_transaction("user_info_heavy_$UserID");
			$Cache->update_row(false, array('Invites' => '+1'));
			$Cache->commit_transaction(0);
		}
	}
	$DB->query("
		DELETE FROM invites
		WHERE Expires < '$sqltime'");


	//------------- Hide old requests ---------------------------------------//
	sleep(3);
	$DB->query("
		UPDATE requests
		SET Visible = 0
		WHERE TimeFilled < (NOW() - INTERVAL 7 DAY)
			AND TimeFilled != '0000-00-00 00:00:00'");

	//------------- Remove dead peers ---------------------------------------//
	sleep(3);
	$DB->query("
		DELETE FROM xbt_files_users
		WHERE mtime < unix_timestamp(NOW() - INTERVAL 6 HOUR)");

	//------------- Remove dead sessions ---------------------------------------//
	sleep(3);

	$AgoMins = time_minus(60 * 30);
	$AgoDays = time_minus(3600 * 24 * 30);

	$SessionQuery = $DB->query("
			SELECT UserID, SessionID
			FROM users_sessions
			WHERE (LastUpdate < '$AgoDays' AND KeepLogged = '1')
				OR (LastUpdate < '$AgoMins' AND KeepLogged = '0')");
	$DB->query("
		DELETE FROM users_sessions
		WHERE (LastUpdate < '$AgoDays' AND KeepLogged = '1')
			OR (LastUpdate < '$AgoMins' AND KeepLogged = '0')");

	$DB->set_query_id($SessionQuery);
	while (list($UserID, $SessionID) = $DB->next_record()) {
		$Cache->begin_transaction("users_sessions_$UserID");
		$Cache->delete_row($SessionID);
		$Cache->commit_transaction(0);
	}


	//------------- Lower Login Attempts ------------------------------------//
	$DB->query("
		UPDATE login_attempts
		SET Attempts = Attempts - 1
		WHERE Attempts > 0");
	$DB->query("
		DELETE FROM login_attempts
		WHERE LastAttempt < '".time_minus(3600 * 24 * 90)."'");

	//------------- Remove expired warnings ---------------------------------//
	$DB->query("
		SELECT UserID
		FROM users_info
		WHERE Warned < '$sqltime'");
	while (list($UserID) = $DB->next_record()) {
			$Cache->begin_transaction("user_info_$UserID");
			$Cache->update_row(false, array('Warned' => '0000-00-00 00:00:00'));
			$Cache->commit_transaction(2592000);
	}

	$DB->query("
		UPDATE users_info
		SET Warned = '0000-00-00 00:00:00'
		WHERE Warned < '$sqltime'");

	// If a user has downloaded more than 10 GiBs while on ratio watch, disable leeching privileges, and send the user a message

	$DB->query("
		SELECT ID, torrent_pass
		FROM users_info AS i
			JOIN users_main AS m ON m.ID = i.UserID
		WHERE i.RatioWatchEnds != '0000-00-00 00:00:00'
			AND i.RatioWatchDownload + 10 * 1024 * 1024 * 1024 < m.Downloaded
			AND m.Enabled = '1'
			AND m.can_leech = '1'");
	$Users = $DB->to_pair('torrent_pass', 'ID');

	if (count($Users) > 0) {
		$Subject = 'Leeching Disabled';
		$Message = 'You have downloaded more then 10 GiB while on Ratio Watch. Your leeching privileges have been disabled. Please reread the rules and refer to this guide on how to improve your ratio https://' . SSL_SITE_URL . '/wiki.php?action=article&id=110';
		foreach ($Users as $TorrentPass => $UserID) {
			Misc::send_pm($UserID, 0, $Subject, $Message);
			Tracker::update_tracker('update_user', array('passkey' => $TorrentPass, 'can_leech' => '0'));
		}

		$DB->query("
			UPDATE users_info AS i
				JOIN users_main AS m ON m.ID = i.UserID
			SET m.can_leech = '0',
				i.AdminComment = CONCAT('$sqltime - Leeching privileges disabled by ratio watch system for downloading more than 10 GBs on ratio watch. - required ratio: ', m.RequiredRatio, '\n\n', i.AdminComment)
			WHERE m.ID IN(" . implode(',', $Users) . ')');
	}

}
/*************************************************************************\
//--------------Run every day -------------------------------------------//

These functions are run in the first 15 minutes of every day.

\*************************************************************************/

if (!$NoDaily && $Day != $NextDay || $_GET['runday']) {
	echo "Ran daily functions\n";
	if ($Day % 2 == 0) { // If we should generate the drive database (at the end)
		$GenerateDriveDB = true;
	}

	//------------- Ratio requirements

	// Clear old seed time history
	$DB->query("
		DELETE FROM users_torrent_history
		WHERE Date < DATE('".sqltime()."' - INTERVAL 7 DAY) + 0");

	// Store total seeded time for each user in a temp table
	$DB->query("TRUNCATE TABLE users_torrent_history_temp");
	$DB->query("
		INSERT INTO users_torrent_history_temp
			(UserID, SumTime)
		SELECT UserID, SUM(Time)
		FROM users_torrent_history
		GROUP BY UserID");

	// Insert new row with  = 0 with