A bunch of half implemented shit, also r20160207

This commit is contained in:
flash 2016-02-07 20:36:13 +01:00
parent 58776ee047
commit ed00e852d8
17 changed files with 250 additions and 330 deletions

View file

@ -1,14 +0,0 @@
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
104.16.0.0/12
108.162.192.0/18
141.101.64.0/18
162.158.0.0/15
172.64.0.0/13
173.245.48.0/20
188.114.96.0/20
190.93.240.0/20
197.234.240.0/22
198.41.128.0/17
199.27.128.0/21

View file

@ -1,5 +0,0 @@
2400:cb00::/32
2405:8100::/32
2405:b500::/32
2606:4700::/32
2803:f800::/32

View file

@ -33,12 +33,6 @@ prefix = sakura_
; Data files relative to the root directory
[data]
; File containing CloudFlare IPv4 CIDRs
cfipv4 = config/cloudflare.ipv4
; File containing CloudFlare IPv6 CIDRs
cfipv6 = config/cloudflare.ipv6
; JSON file containing WHOIS servers
whoisservers = config/whois.json

View file

@ -91,7 +91,7 @@ class Meta
Template::vars([
'page' => [
'title' => 'Frequently Asked Questions',
'questions' => Utils::getFaqData(),
'questions' => Database::fetch('faq', true, null, ['faq_id']),
],
]);
@ -117,7 +117,7 @@ class Meta
$id = strtolower($id);
// Get info page data from the database
if ($ipData = Utils::loadInfoPage($id)) {
if ($ipData = Database::fetch('infopages', false, ['page_shorthand' => [$id, '=']])) {
// Assign new proper variable
$renderData['page'] = [
'id' => $id,

View file

@ -12,6 +12,7 @@ use Sakura\Database;
use Sakura\User;
use Sakura\BBcode;
use Sakura\Config;
use Sakura\Net;
/**
* Used to serve, create and update posts.
@ -212,7 +213,7 @@ class Post
'topic_id' => $thread->id,
'forum_id' => $thread->forum,
'poster_id' => $this->poster->id,
'poster_ip' => Utils::getRemoteIP(),
'poster_ip' => Net::pton(Net::IP()),
'post_time' => $this->time,
'post_subject' => $this->subject,
'post_text' => $this->text,

212
libraries/Net.php Normal file
View file

@ -0,0 +1,212 @@
<?php
/**
* Holds the everything networking.
*
* @package Sakura
*/
namespace Sakura;
/**
* Networking methods.
*
* @package Sakura
* @author Julian van de Groep <me@flash.moe>
*/
class Net {
/**
* Returns the connecting IP.
*
* @return string The IP.
*/
public static function IP()
{
return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '::1';
}
/**
* Detect the version of an IP.
*
* @param string $ipAddr The IP.
*
* @return int Either 0, 4 or 6.
*/
public static function detectIPVersion($ipAddr)
{
// Check if var is IP
if (filter_var($ipAddr, FILTER_VALIDATE_IP)) {
// v4
if (filter_var($ipAddr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return 4;
}
// v6
if (filter_var($ipAddr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
return 6;
}
}
// Not an IP or unknown type
return 0;
}
/**
* Converts a printable IP address into an unpacked binary string.
*
* @param string $ip Printable IP string.
*
* @throws \Exception Thrown if an invalid IP is supplied.
*
* @return string Unpacked IP address.
*/
public static function pton($ip)
{
// Detect the IP version
$ipv = self::detectIPVersion($ip);
// Check for IPv4 first since that's most common
if ($ipv === 4) {
return current(unpack("A4", inet_pton($ip)));
}
// Then attempt IPv6
if ($ipv === 6) {
return current(unpack("A16", inet_pton($ip)));
}
// Throw an exception if an invalid IP was supplied
throw new \Exception("Invalid IP address supplied.");
}
/**
* Converts a binary unpacked IP to a printable packed IP.
*
* @param string $bin The unpacked IP.
*
* @throws \Exception Thrown if the unpacked IP is invalid.
*
* @return string The packed IP.
*/
public static function ntop($bin)
{
// Get the length of the binary string
$len = strlen($bin);
// Throw an exception if it's not 4 or 16 bytes
if ($len !== 4 && $len !== 16) {
throw new \Exception("Could not handle this IP type.");
}
// Finally pack the IP
return inet_ntop(pack("A{$len}", $bin));
}
public static function matchCIDR($ip, $range)
{
// Break the range up in parts
list($net, $mask) = explode('/', $range);
// Check IP version
$ipv = self::detectIPVersion($ip);
$rangev = self::detectIPVersion($net);
// Return false if it's not a valid IP
if ($ipv !== $rangev && !$ipv) {
return false;
}
// v4
if ($ipv === 4) {
return self::matchCIDRv4($ip, $net, $mask);
}
// v6
if ($ipv === 6) {
return self::matchCIDRv6($ip, $net, $mask);
}
// Return false by default
return false;
}
/**
* Match an IPv4 CIDR
*
* @param string $ip The IP address to match.
* @param string $net The Net address to match.
* @param string $mask The Mask to match.
*
* @return bool Returns true if it matches.
*/
private static function matchCIDRv4($ip, $net, $mask)
{
// Convert IP and Net address to long
$ip = ip2long($ip);
$net = ip2long($net);
// Generate mask
$mask = -1 << (32 - $mask);
// Do the check
return ($ip & $mask) === $net;
}
/**
* Converts an IPv6 mask to a byte array
*
* @param int $mask The mask.
*
* @return string The byte array.
*/
private static function maskToByteArray($mask)
{
// Generate an address from the mask
$addr = str_repeat("f", $mask / 4);
// Append uneven bit
switch ($mask % 4) {
case 1:
$addr .= '8';
break;
case 2:
$addr .= 'c';
break;
case 3:
$addr .= 'e';
break;
}
// Pad the address with zeroes
$addr = str_pad($addr, 32, '0');
// Pack the address
$addr = pack('H*', $addr);
// Return the packed address
return $addr;
}
/**
* Match an IPv6 CIDR
*
* @param string $ip The IP address to match.
* @param string $net The Net address to match.
* @param int $mask The net mask.
*
* @return bool Returns true if it's a successful match.
*/
private static function matchCIDRv6($ip, $net, $mask)
{
// Pack the IP and Net addresses
$ip = inet_pton($ip);
$net = inet_pton($net);
// Convert the mask to a byte array
$mask = self::maskToByteArray($mask);
// Compare them
return ($ip & $mask) === $net;
}
}

View file

@ -145,8 +145,10 @@ class Router
try {
try {
return self::$dispatcher->dispatch($method, $url);
} catch (HttpMethodNotAllowedException $e) {}
} catch (HttpRouteNotFoundException $e) {}
} catch (HttpMethodNotAllowedException $e) {
}
} catch (HttpRouteNotFoundException $e) {
}
// Default to the not found page
return Template::render('global/notfound');

View file

@ -95,7 +95,7 @@ class Session
// Insert the session into the database
Database::insert('sessions', [
'user_id' => $this->userId,
'user_ip' => Utils::getRemoteIP(),
'user_ip' => Net::pton(Net::IP()),
'user_agent' => Utils::cleanString(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'No user agent header.'),
'session_key' => $session,
'session_start' => time(),
@ -132,13 +132,13 @@ class Session
}
// IP Check
$ipCheck = Config::get('session_check');
$ipCheck = false;// Config::get('session_check');
// Origin checking
if ($ipCheck) {
// Split both IPs up
$sessionIP = explode('.', $session['user_ip']);
$userIP = explode('.', Utils::getRemoteIP());
$userIP = explode('.', Net::IP());
// Take 1 off the ipCheck variable so it's equal to the array keys
$ipCheck = $ipCheck - 1;

View file

@ -267,8 +267,8 @@ class User
'password_iter' => $password[1],
'email' => $emailClean,
'rank_main' => 0,
'register_ip' => Utils::getRemoteIP(),
'last_ip' => Utils::getRemoteIP(),
'register_ip' => Net::pton(Net::IP()),
'last_ip' => Net::pton(Net::IP()),
'user_registered' => time(),
'user_last_online' => 0,
'user_country' => Utils::getCountryCode(),

View file

@ -120,7 +120,7 @@ class Users
// Check if we haven't hit the rate limit
$rates = Database::fetch('login_attempts', true, [
'attempt_ip' => [Utils::getRemoteIP(), '='],
'attempt_ip' => [Net::pton(Net::IP()), '='],
'attempt_timestamp' => [time() - 1800, '>'],
'attempt_success' => [0, '='],
]);

View file

@ -17,16 +17,6 @@ use PHPMailer;
*/
class Utils
{
/**
* Get the emoticons.
*
* @return array Array containing emoticons.
*/
public static function getEmotes()
{
return Database::fetch('emoticons');
}
/**
* Parse the emoticons.
*
@ -36,9 +26,8 @@ class Utils
*/
public static function parseEmotes($text)
{
// Get emoticons from the database
$emotes = self::getEmotes();
$emotes = Database::fetch('emoticons');
// Do the replacements
foreach ($emotes as $emote) {
@ -62,7 +51,6 @@ class Utils
*/
public static function verifyCaptcha($response)
{
// Attempt to get the response
$resp = @file_get_contents(
'https://www.google.com/recaptcha/api/siteverify?secret='
@ -93,7 +81,6 @@ class Utils
*/
public static function errorHandler($errno, $errstr, $errfile, $errline)
{
// Remove ROOT path from the error string and file location
$errstr = str_replace(ROOT, '', $errstr);
$errfile = str_replace(ROOT, '', $errfile);
@ -254,7 +241,6 @@ class Utils
*/
public static function sendMail($to, $subject, $body)
{
// Initialise PHPMailer
$mail = new PHPMailer();
@ -323,7 +309,6 @@ class Utils
*/
public static function cleanString($string, $lower = false, $noSpecial = false, $replaceSpecial = '')
{
// Run common sanitisation function over string
$string = htmlentities($string, ENT_NOQUOTES | ENT_HTML401, Config::get('charset'));
$string = stripslashes($string);
@ -343,23 +328,6 @@ class Utils
return $string;
}
/**
* Load contents of an infopage.
*
* @param string $id ID of the info page.
*
* @return array|bool Contents of the info page.
*/
public static function loadInfoPage($id)
{
// Get contents from the database
$infopage = Database::fetch('infopages', false, ['page_shorthand' => [$id, '=']]);
// Return the data if there is any else just return false
return count($infopage) ? $infopage : false;
}
/**
* Validate MX records.
*
@ -369,7 +337,6 @@ class Utils
*/
public static function checkMXRecord($email)
{
// Get the domain from the e-mail address
$domain = substr(strstr($email, '@'), 1);
@ -380,174 +347,6 @@ class Utils
return $record;
}
/**
* Detect the version of an IP.
*
* @param string $ip The IP.
*
* @return int Either 0, 4 or 6.
*/
public static function ipVersion($ip)
{
// Check if var is IP
if (filter_var($ip, FILTER_VALIDATE_IP)) {
// IPv4
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return 4;
}
// IPv6
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
return 6;
}
}
// Not an IP or unknown type
return 0;
}
/**
* Unpack an IPv6
*
* @param mixed $inet IP address.
*
* @return null|string The unpacked IP.
*/
public static function inetToBits($inet)
{
// Unpack string
$unpacked = unpack('A16', $inet);
// Split the string
$unpacked = str_split($unpacked[1]);
// Define variable
$binaryIP = null;
// "Build" binary IP
foreach ($unpacked as $char) {
$binaryIP .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
}
// Return IP
return $binaryIP;
}
/**
* Match a subnet.
*
* @param string $ip the IP.
* @param string $range The range.
*
* @return bool|int Success.
*/
public static function matchSubnet($ip, $range)
{
// Use the proper IP type
switch (self::ipVersion($ip)) {
case 4:
// Break the range up in parts
list($subnet, $bits) = explode('/', $range);
// Convert IP and Subnet to long
$ip = ip2long($ip);
$subnet = ip2long($subnet);
$mask = -1 << (32 - $bits);
// In case the supplied subnet wasn't correctly aligned
$subnet &= $mask;
// Return true if IP is in subnet
return ($ip & $mask) == $subnet;
case 6:
// Break the range up in parts
list($subnet, $bits) = explode('/', $range);
// Convert subnet to packed address and convert it to binary
$subnet = inet_pton($subnet);
$binarySubnet = self::inetToBits($subnet);
// Convert IPv6 to packed address and convert it to binary as well
$ip = inet_pton($ip);
$binaryIP = self::inetToBits($ip);
// Return bits of the strings according to the bits
$ipBits = substr($binaryIP, 0, $bits);
$subnetBits = substr($binarySubnet, 0, $bits);
return ($ipBits === $subnetBits);
default:
return 0;
}
}
/**
* Check if an IP originates from CloudFlare.
*
* @param string $ip The IP.
*
* @return bool Success.
*/
public static function checkCFIP($ip)
{
// Get CloudFlare Subnet list
$cfhosts = file_get_contents(
ROOT . Config::local('data', 'cfipv' . (self::ipVersion($ip)))
);
// Replace \r\n with \n
$cfhosts = str_replace("\r\n", "\n", $cfhosts);
// Explode the file into an array
$cfhosts = explode("\n", $cfhosts);
// Check if IP is in a CloudFlare subnet
foreach ($cfhosts as $subnet) {
// Check if the subnet isn't empty (git newline prevention)
if (strlen($subnet) < 1) {
continue;
}
// Return true if found
if (self::matchSubnet($ip, $subnet)) {
return true;
}
}
// Return false if fails
return false;
}
/**
* Get the IP of a visitor.
*
* @return string The IP.
*/
public static function getRemoteIP()
{
// Assign REMOTE_ADDR to a variables
$ip = $_SERVER['REMOTE_ADDR'];
// Check if the IP is a CloudFlare IP
if (self::checkCFIP($ip)) {
// If it is check if the CloudFlare IP header is set and if it is assign it to the ip variable
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
}
// Return the correct IP
return $ip;
}
/**
* Get the country code of a visitor.
*
@ -557,7 +356,7 @@ class Utils
{
// Attempt to get country code using PHP's built in geo thing
if (function_exists("geoip_country_code_by_name")) {
$code = geoip_country_code_by_name(self::getRemoteIP());
$code = geoip_country_code_by_name(Net::IP());
// Check if $code is anything
if ($code) {
@ -583,7 +382,6 @@ class Utils
*/
public static function pwdEntropy($pw)
{
// Decode utf-8 chars
$pw = utf8_decode($pw);
@ -600,7 +398,6 @@ class Utils
*/
public static function getCountryName($code)
{
// Parse JSON file
$iso3166 = json_decode(
utf8_encode(
@ -620,72 +417,6 @@ class Utils
return 'Unknown';
}
/**
* Get the FAQ table data (why is this a function).
*
* @return array FAQ data.
*/
public static function getFaqData()
{
// Do database call
$faq = Database::fetch('faq', true, null, ['faq_id']);
// Return FAQ data
return $faq;
}
/**
* Get the type of log in text (unused and will probably be removwed).
* @param mixed $type
* @return mixed
*/
public static function getLogStringFromType($type)
{
// Query the database
$return = Database::fetch('logtypes', false, ['id' => [$type, '=']]);
// Check if type exists and else return a unformattable string
if (count($return) < 2) {
return 'Unknown action.';
}
// Return the string
return $return['string'];
}
/**
* Get a user's logs (unused, probably removed in future).
* @param mixed $uid
* @return array
*/
public static function getUserLogs($uid = 0)
{
// Check if a user is specified
$conditions = ($uid ? ['uid' => [$uid, '=']] : null);
// Get data from database
$logsDB = Database::fetch('logs', true, $conditions, ['id', true]);
// Storage array
$logs = [];
// Iterate over entries
foreach ($logsDB as $log) {
// Store usable data
$logs[$log['id']] = [
'user' => $_USER = Users::getUser($log['uid']),
'rank' => Users::getRank($_USER['rank_main']),
'string' => vsprintf(self::getLogStringFromType($log['action']), json_decode($log['attribs'], true)),
];
}
// Return new logs
return $logs;
}
/**
* Get the byte symbol for a unit from bytes.
*
@ -695,7 +426,6 @@ class Utils
*/
public static function getByteSymbol($bytes)
{
// Return nothing if the input was 0
if (!$bytes) {
return;
@ -721,7 +451,6 @@ class Utils
*/
public static function getPremiumTrackerData()
{
// Create data array
$data = [];
@ -762,12 +491,10 @@ class Utils
public static function updatePremiumTracker($id, $amount, $comment)
{
Database::insert('premium_log', [
'user_id' => $id,
'transaction_amount' => $amount,
'transaction_date' => time(),
'transaction_comment' => $comment,
]);
}

View file

@ -187,7 +187,7 @@ if (isset($_REQUEST['mode'])) {
Database::insert('login_attempts', [
'attempt_success' => $login[0],
'attempt_timestamp' => time(),
'attempt_ip' => Utils::getRemoteIP(),
'attempt_ip' => Net::pton(Net::IP()),
'user_id' => isset($login[2]) ? $login[2] : 0,
]);
}
@ -315,7 +315,7 @@ if (Users::checkLogin()) {
}
// Check if a user has already registered from the current IP address
if (count($regUserIP = Users::getUsersByIP(Utils::getRemoteIP()))) {
if (count($regUserIP = Users::getUsersByIP(Net::pton(Net::IP())))) {
$renderData['auth']['blockRegister'] = [
'do' => true,

View file

@ -118,12 +118,13 @@ var Sakura = (function () {
}
// Times array
var times = {
31536000: 'year',
2592000: 'month',
86400: 'day',
3600: 'hour',
60: 'minute',
1: 'second'
31536000: ['year', 'a'],
2592000: ['month', 'a'],
604800: ['week', 'a'],
86400: ['day', 'a'],
3600: ['hour', 'an'],
60: ['minute', 'a'],
1: ['second', 'a']
};
//
var timeKeys = Object.keys(times).reverse();
@ -136,7 +137,7 @@ var Sakura = (function () {
// Round the number
var display = Math.floor(calc);
// Return the formatted string
return display + " " + times[timeKeys[i]] + (display === 1 ? '' : 's') + append;
return (display === 1 ? times[timeKeys[i]][1] : display) + " " + times[timeKeys[i]][0] + (display === 1 ? '' : 's') + append;
}
}
// If everything fails just return none

View file

@ -141,12 +141,13 @@ class Sakura {
// Times array
var times: Object = {
31536000: 'year',
2592000: 'month',
86400: 'day',
3600: 'hour',
60: 'minute',
1: 'second'
31536000: ['year', 'a'],
2592000: ['month', 'a'],
604800: ['week', 'a'],
86400: ['day', 'a'],
3600: ['hour', 'an'],
60: ['minute', 'a'],
1: ['second', 'a']
};
//
@ -163,7 +164,7 @@ class Sakura {
var display: number = Math.floor(calc);
// Return the formatted string
return display + " " + times[timeKeys[i]] + (display === 1 ? '' : 's') + append;
return (display === 1 ? times[timeKeys[i]][1] : display) + " " + times[timeKeys[i]][0] + (display === 1 ? '' : 's') + append;
}
}

View file

@ -7,7 +7,7 @@
namespace Sakura;
// Include components
require_once str_replace(basename(__DIR__), '', dirname(__FILE__)) . 'sakura.php';
require_once __DIR__ . '/../sakura.php';
// Handle requests
echo Router::handle($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);

View file

@ -69,7 +69,7 @@ $mode = isset($_GET['f']) ? 'f' : (isset($_GET['t']) ? 't' : (isset($_GET['p'])
// Include emotes and bbcodes
$posting = [
'emoticons' => Utils::getEmotes(),
'emoticons' => Database::fetch('emoticons'),
];
// Check if we're in reply mode

View file

@ -8,7 +8,7 @@
namespace Sakura;
// Define Sakura version
define('SAKURA_VERSION', '20160205');
define('SAKURA_VERSION', '20160207');
define('SAKURA_VLABEL', 'Amethyst');
define('SAKURA_COLOUR', '#9966CC');
@ -45,6 +45,7 @@ require_once ROOT . 'libraries/CSRF.php';
require_once ROOT . 'libraries/Database.php';
require_once ROOT . 'libraries/File.php';
require_once ROOT . 'libraries/Hashing.php';
require_once ROOT . 'libraries/Net.php';
require_once ROOT . 'libraries/News.php';
require_once ROOT . 'libraries/Payments.php';
require_once ROOT . 'libraries/Perms.php';