1
0
mirror of https://github.com/cottow/6paster.git synced 2024-05-19 06:50:10 +00:00
Files
Rogier Krieger 2e83c4aab6 The register_globals setting is gone per PHP 5.4
May as well clean up now.
2022-04-23 21:42:04 +03:00

314 lines
7.3 KiB
PHP

<?php
/**
6core.net paster
A tiny pastebin clone. I created this because I don't perse like all my pastes ending up
on a public server somewhere. This paster can be very quickly deployed on your own system
allowing you to keep control over your pastes. It is coded with security in mind, forces
the use of HTTPS, and imposes rate limits on posters.
*/
header("Strict-Transport-Security: max-age=31536000"); // 1 year
define('CONFIG', '../config.php');
define('TPLDIR','../tpl/');
$base = dirname($_SERVER['SCRIPT_NAME']);
if( $base != '/' )
{
$base .= '/';
}
define('BASEURL', $base );
require(CONFIG);
$allowed_image_types = array(
IMAGETYPE_GIF,
IMAGETYPE_JPEG,
IMAGETYPE_PNG,
IMAGETYPE_BMP
);
function do_cleanup()
{
global $dbh;
$stmt = $dbh->prepare("DELETE FROM `pastes` WHERE `expires` < NOW()");
$stmt->execute();
}
function check_setup()
{
global $config;
// check SSL
if( !array_key_exists('HTTPS', $_SERVER) || $_SERVER['HTTPS'] != "on")
{
die('I really like encryption. Please use SSL.');
} else {
header("Strict-Transport-Security: max-age=15768000;");
}
// sane config?
if( $config['limit_hour'] > $config['limit_day'] )
{
die('You should allow fewer pastes per hour than per day, silly');
}
//
if( empty($config['server_name']) )
{
$config['server_name'] = 'https://p.6core.net';
}
// htaccess installed?
if( stristr($_SERVER['SERVER_SOFTWARE'], 'apache' ) && !file_exists('.htaccess'))
{
die('You should install the included .htaccess file in the same dir as index.php');
}
if( !function_exists( 'mysqli_connect' ) )
{
die('You do not have mysqli installed, quitting.');
}
return true;
}
function show_post( $ident )
{
global $dbh;
$stmt = $dbh->prepare("SELECT `text`,`mimetype` FROM `pastes` WHERE `ident` = ?");
if( !$stmt )
{
die( 'mysql error' );
}
$stmt->bind_param('s', $ident );
$stmt->execute();
$stmt->store_result();
$stmt->bind_result( $content, $mime_type );
if( $stmt->num_rows == 1 )
{
$stmt->fetch();
// make sure we don't put newlines in headers
$mime_type = str_replace("\n", '', $mime_type );
// fix the charset
if( $mime_type == 'text/plain' )
{
// charset is hardcoded in the db
$mime_type .= '; charset=utf-8';
// need some special tricks for Internet Explorer
if( preg_match("/MSIE (\d)/", $_SERVER['HTTP_USER_AGENT'], $matches) )
{
// MS IE
if( (int)$matches[1] < 8 )
{
// MS IE < 8 is retarded and will parse text as html even if
// content-type says text/plain
$content = htmlspecialchars($content);
} else {
// MS IE >= 8 is retarded and needs this header to prevent
// parsing html in text/plain
header("X-Content-Type-Options: nosniff");
}
}
} else {
// yes, I'm positively paranoid
if( strpos($mime_type,'image') !== 0 )
{
return;
}
}
header("Content-Type: $mime_type");
header("Vary: User-Agent");
require(TPLDIR.'post.php');
return;
}
// not found
header("HTTP/1.0 404 Not Found", true, 404);
require(TPLDIR.'404.php');
}
function show_form()
{
global $config;
require(TPLDIR.'header.php');
require(TPLDIR.'form.php');
require( TPLDIR.'footer.php');
}
function do_paste()
{
global $dbh;
global $config;
global $allowed_image_types;
require(TPLDIR.'header.php');
if( empty( $_POST['content'] ) || empty( $_POST['ttl'] ))
{
$errmsg = "You did not set all parameters";
require( TPLDIR.'error.php');
return;
}
if( strlen( $_POST['content']) > $config['paste_max_chars'] )
{
$errmsg = "Your paste exceeds the max limit of ".$config['paste_max_chars'];
require( TPLDIR.'error.php');
return;
}
$ttl = intval( $_POST['ttl'] );
if( $ttl < $config['ttl_min'] )
{
$ttl = $config['ttl_min'];
} else if( $ttl > $config['ttl_max'] ) {
$ttl = $config['ttl_max'];
}
if( limit_exceeded() )
{
$errmsg = "You have reached your throttle limit, try again later.";
require( TPLDIR.'error.php');
return;
}
$mime_type = 'text/plain';
// check if it's binary or a string
if(!mb_detect_encoding($_POST['content']))
{
// binary. is it an image?
$tmp_filename = tempnam(0, "6paster_");
$fh = fopen( $tmp_filename, "w");
fwrite( $fh, $_POST['content'] );
fclose( $fh );
$filetype = exif_imagetype( $tmp_filename );
unlink( $tmp_filename );
if( !$filetype || !in_array( $filetype, $allowed_image_types) )
{
$errmsg = "Sorry, this isn't a file sharing service. Filetype ".$filetype." not allowed.";
require( TPLDIR.'error.php');
return;
}
$mime_type = image_type_to_mime_type( $filetype );
}
// it's OK now, let's post it
$ident = generate_ident();
$stmt = $dbh->prepare("INSERT INTO `pastes` SET `ident`= ?, `ip`=?, `date`=NOW(), `text`=?, `mimetype`=?, `expires` = TIMESTAMPADD( SECOND, ?, NOW())");
$ip = hash('sha256',$_SERVER['REMOTE_ADDR']);
$stmt->bind_param('ssssi', $ident, $ip, $_POST['content'], $mime_type, $ttl );
$stmt->execute();
header("Location: ".BASEURL."p/".$ident);
}
function generate_ident()
{
global $dbh, $config;
$exists = true;
while( $exists )
{
// generate identifier. must have enough entropy (so rand()) but since we can't trust php's
// rand, we hash it together with a site-specific secret. then to compress the url, we base64
// encode instead of hex encode the result. Furthermore, + and / might give trouble in GET
// parameters, so we replace those.
$ident = substr( base64_encode( sha1 ( rand(0,10000000000) . $config['mysql_pass'], true ) ) , 0, 24 );
$ident = str_replace( '+', 'A', $ident );
$ident = str_replace( '/', 'B', $ident );
$stmt = $dbh->prepare("SELECT EXISTS ( SELECT * FROM `pastes` WHERE `ident` = ? )");
$stmt->bind_param('s', $ident );
$stmt->execute();
$stmt->bind_result( $_exists );
$exists = ( $_exists == 1 ? true : false );
}
return $ident;
}
function limit_exceeded()
{
global $dbh;
global $config;
// check day limit
return(
_limit_exceeded( 'DAY', $config['limit_day']) ||
_limit_exceeded('HOUR', $config['limit_hour'] )
);
}
function _limit_exceeded( $type, $limit )
{
global $dbh;
if( !in_array( $type, array('DAY', 'HOUR')))
return true;
$stmt = $dbh->prepare("SELECT COUNT(*) FROM `pastes` WHERE `ip`= ? AND TIMESTAMPDIFF( $type, NOW(), `date` ) <= 1");
if( !$stmt )
{
die("Couldn't perform throttle check");
}
$ip = hash('sha256',$_SERVER['REMOTE_ADDR']);
$stmt->bind_param("s", $ip);
$stmt->execute();
$stmt->bind_result( $count );
$stmt->fetch();
return( $count > $limit );
}
check_setup();
$dbh = mysqli_connect(
$config['mysql_host'],
$config['mysql_user'],
$config['mysql_pass'],
$config['mysql_db']
);
if( !$dbh )
{
die("Couldn't connect to database");
}
$ident = false;
if( array_key_exists( 'p', $_GET ) && ctype_alnum( $_GET['p'] ) )
{
$ident = $_GET['p'];
}
if( $ident )
{
show_post( $ident );
} else if( array_key_exists( 'content', $_POST ) ) {
do_paste();
} else {
show_form();
}
flush();
do_cleanup();