Initial commit

This commit is contained in:
eleksir
2017-03-17 19:56:33 +03:00
commit ea3052fb3a
2 changed files with 492 additions and 0 deletions

118
README.md Normal file
View File

@@ -0,0 +1,118 @@
## Paste. ##
This is pastebin replacement. It does not use ads, it is opensource it is relatively small.
And it has minimal pastebin API stub, at least pastebinit can paste to it with minimal config changes.
Besides pastebinit there are 2 cli clients available on main page of this app.
### How do I get set up? ###
This paste utilizes PDO SQLite driver, well, at least for now :) and mbstrings.
Tested php versions are 5.6.30 and 7.0.12. Paste uses **paste/paste.sqlite** file as db.
Tested on apache/fcgid (windows) for dev and nginx/php-fpm for prod.
Default installation assumes that paste runs as / app on site, but you can change it (BASEURL) in index.php.
Nginx config snippet:
```
# limit paste size to ~ 200k
client_max_body_size 256k;
location /raw {
rewrite "^\/raw\/([a-z|A-Z]*)$" /index.php?$1;
}
rewrite "^/([a-z|A-Z]*)$" /index.php?$1;
location / {
root /var/www/htdocs/empty;
index index.php;
location ~ \.php$ {
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 499;
}
root /var/www/htdocs;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_pass php;
}
}
```
Database initialises automatically :)
### Contribution guidelines ###
Clean code, preferably with comments for some quirky places.
### PasteBinIt configuration ###
default pastebin site, __~/.pastebinit.xml__:
```
<pastebinit>
<pastebin>http://your.site</pastebin>
<author>Stephane Graber</author>
<jabberid>stgraber@stgraber.org</jabberid>
<format>text</format>
</pastebinit>
```
and how to work with it __~/.pastebinit.d/your.site.conf__:
```
[pastebin]
basename = your.site
regexp = http://((([a-zA-Z0-9\-_\.]*)(your\.site)))
[format]
content = api_paste_code
user = api_paste_name
subdomain = api_paste_subdomain
private = api_paste_private
expiry = api_paste_expire_date
format = api_paste_format
email = api_paste_email
page = page
submit = submit
regexp = regexp
api_dev_key = api_dev_key
api_option = api_option
[defaults]
submit = submit
format = text
private = 0
expiry = 1M
subdomain =
email =
api_dev_key = 253ce2f0a45140ee0a44ca99aa492260
api_option = paste
page = /
regexp = (.*)
```
FYI difference between pastebin and paste are minimal:
```diff
diff -ruN pastebin.com.conf your.site.conf
--- pastebin.com.conf
+++ your.site.conf
@@ -1,6 +1,6 @@
[pastebin]
-basename = pastebin.com
-regexp = http://((([a-zA-Z0-9\-_\.]*)(pastebin\.com)))
+basename = your.site
+regexp = http://((([a-zA-Z0-9\-_\.]*)(your\.site)))
[format]
content = api_paste_code
@@ -25,5 +25,5 @@
email =
api_dev_key = 253ce2f0a45140ee0a44ca99aa492260
api_option = paste
-page = /api/api_post.php
+page = /
regexp = (.*)
```

374
index.php Normal file
View File

@@ -0,0 +1,374 @@
<?php
define ('BASEURL', "/"); # must end with '/'
define ('KEY_MIN', 3);
define ('KEY_MAX', 9);
define ('DBPATH', 'paste/paste.sqlite');
define ('SITE_ADDRESS', 'http://127.0.0.1/');
define ('CDN_ADDRESS', 'http://127.0.0.1/');
define ('APP_ADDRESS', substr(SITE_ADDRESS, 0, -1) . BASEURL);
function showindex($text) {
header('Content-Language: en, ru');
header('Pragma: no-cache');
header('Cache-Control: no-cache');
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<style>
body { background-color: #adc0fc; text-align: center; }
textarea {
background-image: url("<?php print CDN_ADDRESS; ?>bg_logo.png");
background-repeat: no-repeat;
background-position: center center;
width: 90%;
height: 70vh;
}
</style>
<title>Paste.</title>
</head>
<body>
<h3>Paste</h3>
<form method="post" action="<?php print APP_ADDRESS; ?>" enctype="multipart/form-data">
<textarea name="exs" id="textbox" autofocus></textarea><br />
<input type="submit" value="Post it!" style="font-size:115%;" /> for
<select name="etime" style="font-size: 85%; text-align:left;">
<option value="1" style="font-size: 85%; text-align:left;">1 Hour</option>
<option value="2" style="font-size: 85%; text-align:left;">12 Hours</option>
<option value="3" style="font-size: 85%; text-align:left;">1 day</option>
<option value="4" style="font-size: 85%; text-align:left;">1 week</option>
<option value="5" style="font-size: 85%; text-align:left;">1 Month</option>
<option value="6" style="font-size: 85%; text-align:left;">3 Months</option>
<option value="7" selected style="font-size: 85%; text-align:left;">6 Months</option>
<option value="8" style="font-size: 85%; text-align:left;">1 Year</option>
<option value="9" style="font-size: 85%; text-align:left;">2 Years</option>
<option value="10" style="font-size: 85%; text-align:left;">3 Years</option>
</select>
</form>
<?php
if ($text != '') {
printf (" <p><table align=\"center\" frame=\"void\" border=\"0\"><td>%s</td></table></p>\n", $text);
}
?>
<p style="font-size: small;">
Console client can be found here: <a href="<?php print APP_ADDRESS; ?>e"><?php print APP_ADDRESS; ?>e</a>&nbsp; &nbsp; thanks <a href="http://sprunge.us">sprunge.us</a> for their client :)<br/>
More minimalist console client can be found here: <a href="<?php print APP_ADDRESS; ?>d"><?php print APP_ADDRESS; ?>d</a><br/>
<br/>
2015
</p>
</body>
</html>
<?php
}
function eclient() {
header('Content-Language: en, ru');
header('Content-Type: text/plain');
header('Content-Disposition: attachment; filename="e"');
?>
#!/bin/bash
usage() {
description | fmt -s >&2
}
description() {
cat << HERE
DESCRIPTION
Upload data and fetch URL from the pastebin <?php print SITE_ADDRESS; ?>
USAGE
$0 filename.txt
$0 text string
$0 < filename.txt
piped_data | $0
NOTES
--------------------------------------------------------------------------
* INPUT METHODS *
$0 can accept piped data, STDIN redirection [<filename.txt], text strings following the command as arguments, or filenames as arguments. Only one of these methods can be used at a time, so please see the note on precedence. Also, note that using a pipe or STDIN redirection will treat tabs as spaces, or disregard them entirely (if they appear at the beginning of a line). So I suggest using a filename as an argument if tabs are important either to the function or readability of the code.
* PRECEDENCE *
STDIN redirection has precedence, then piped input, then a filename as an argument, and finally text strings as an arguments.
EXAMPLE:
echo piped | "$0" arguments.txt < stdin_redirection.txt
In this example, the contents of file_as_stdin_redirection.txt would be uploaded. Both the piped_text and the file_as_argument.txt are ignored. If there is piped input and arguments, the arguments will be ignored, and the piped input uploaded.
* FILENAMES *
If a filename is misspelled or doesn\'t have the necessary path description, it will NOT generate an error, but will instead treat it as a text string and upload it.
--------------------------------------------------------------------------
HERE
exit
}
if [ -t 0 ]; then
echo Running interactively, checking for arguments... >&2
if [ "$*" ]; then
echo Arguments present... >&2
if [ -f "$*" ]; then
echo Uploading the contents of "$*"... >&2
cat "$*"
else
echo Uploading the text: \""$*"\"... >&2
echo "$*"
fi | curl -F "exs=<-" <?php print SITE_ADDRESS; ?>
else
echo No arguments found, printing USAGE and exiting. >&2
usage
fi
else
echo Using input from a pipe or STDIN redirection... >&2
while read -r line ; do
echo $line
done | curl -F "exs=<-" <?php print SITE_ADDRESS; ?>
fi
<?php
exit;
}
function dclient() {
header('Content-Language: en, ru');
header('Content-Type: text/plain');
header('Content-Disposition: attachment; filename="d";');
?>
#!/usr/bin/env bash
if [[ "$1" == "--help" ]]; then
echo "Usage: $0 < file"
echo " echo foo | $0"
elif [[ -f "$1" ]]; then
curl -F "exs=<-" "<?php print SITE_ADDRESS; ?>" <"$1"
else
curl -F "exs=<-" "<?php print SITE_ADDRESS; ?>"
fi
exit 0
';
<?php
exit;
}
function gen_id() {
$urllen = rand (KEY_MIN, KEY_MAX);
$string = '';
$a = Array(); $a = array_merge(range('A', 'Z'), range('a', 'z'));
for($i = 0; $i < $urllen; $i++){
$string .= $a[rand (0, count($a) - 1)];
}
return($string);
}
function init_db() {
# we init db, eh? drop db file and re-create it!
if (file_exists(DBPATH)) { unlink(DBPATH); }
if (! file_exists(dirname(DBPATH))) { mkdir(dirname(DBPATH), 0777, true); }
$dbh = new PDO("sqlite:" . DBPATH);
$r = $dbh->exec(
"CREATE TABLE IF NOT EXISTS paste (
id TEXT PRIMARY KEY,
paste TEXT,
time INTEGER,
etime INTEGER
)"
);
if($r === false) {
$e = $r->errorInfo();
showindex($e[2]);
}
$dbh = null;
}
function query_db($id) {
if (! file_exists(DBPATH)) {
init_db();
return false;
}
$dbh = new PDO("sqlite:" . DBPATH);
$sth = $dbh->query("SELECT paste FROM paste WHERE id='$id'");
if ($sth === false) {
$e = $r->errorInfo();
showindex($e[2]);
exit;
}
$sth->setFetchMode(PDO::FETCH_ASSOC);
$data = $sth->fetch();
$dbh = null;
if ($data === false) {
return($data);
} else {
if (isset($data['etime']) and ($data['etime'] < time())){
return false;
}
$data = $data['paste'];
return($data); # false if no data or no key in db
}
}
function store_data($paste) {
if (! file_exists(DBPATH)) { init_db(); }
$dbh = new PDO("sqlite:" . DBPATH);
$q = 0;
do {
if ($q > 100) { # assume key-value space depleted if we try more than 100 times
$q = false;
break;
}
$id = gen_id();
$q++;
} while (! $dbh->query("SELECT id FROM paste WHERE id='$id'"));
if($q === false) {
return('Storage depleted.');
}
$ctime = time();
$etime = $ctime + 14515200;
if(isset($_POST['etime'])) {
switch ($_POST['etime']) {
case '1': # 1 hour
$etime = $ctime + 3600;
break;
case '2': # 12 hours
$etime = $ctime + 43200;
break;
case '3': # 1 day
$etime = $ctime + 86400;
break;
case '4': # 1 week
$etime = $ctime + 604800;
break;
case '5': # 1 month
$etime = $ctime + 2419200;
break;
case '6': # 3 months
$etime = $ctime + 7257600;
break;
case '7': # 6 months
$etime = $ctime + 14515200;
break;
case '8': # 1 year
$etime = $ctime + 29030400;
break;
case '9': # 2 years
$etime = $ctime + 58060800;
break;
case '10': # 3 years
$etime = $ctime + 87091200;
break;
case '13': # forever
$etime = "null";
break;
}
}
$sth = $dbh->prepare("INSERT INTO paste VALUES('$id', '$paste', $ctime, $etime)");
$sth->execute();
if ($sth === false) {
$e = $sth->errorInfo();
showindex($e[2]);
exit;
}
$dbh = null;
return($id);
}
$text = '';
if (($_SERVER['REQUEST_METHOD'] === 'GET') or ($_SERVER['REQUEST_METHOD'] === 'HEAD')) {
# substr returns empty string (boolean false) on error
$str = $_SERVER['REQUEST_URI']; $str = substr($str, mb_strlen(BASEURL, 'UTF-8'));
if ($str == 'e') {
eclient();
exit;
} elseif ($str == 'd') {
dclient();
exit;
}
# pastebin returns raw pate when paste' key prepended with /raw/, let's handle it
if (preg_match('/^raw\//', $str) == 1) {
$str = substr($str, mb_strlen('raw/', 'UTF-8'));
}
if ((mb_strlen($str, 'UTF-8') >= KEY_MIN) and ((mb_strlen($str, 'UTF-8') <= KEY_MAX))) {
if (preg_match('/^[a-z|A-Z]+$/', $str) == 1) {
$response = query_db($str);
if ($response === false) {
$text = 'No such paste.';
} else {
header('Content-Type: text/plain');
print base64_decode($response);
exit;
}
}
}
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['exs']) and ($_POST['exs'] != '')) {
$text = APP_ADDRESS . store_data(base64_encode($_POST['exs']));
} elseif (isset($_POST['api_option'])){ # some pastebin API compatibility stuff
if ($_POST['api_option'] == 'paste') {
if ((isset($_POST['api_paste_code'])) and ($_POST['api_paste_code'] != '')) {
header('Content-Type: text/plain; charset=utf-8');
print mb_convert_encoding(APP_ADDRESS . store_data(base64_encode($_POST['api_paste_code'])), 'UTF-8', 'auto');
exit;
}
} elseif ($_POST['api_option'] == 'show_paste') {
if (isset($_POST['api_paste_key'])) {
$str = $_POST['api_paste_key'];
if ((mb_strlen($str, 'UTF-8') >= KEY_MIN) and ((mb_strlen($str, 'UTF-8') <= KEY_MAX))) {
if (preg_match('/^[a-z|A-Z]+$/', $str) == 1) {
$response = query_db($str);
if ($response === false) {
header('Content-Type: text/plain; charset=utf-8');
print "Bad API request, invalid permission to view this paste or invalid api_paste_key";
exit;
} else {
header('Content-Type: text/plain');
print base64_decode($response);
exit;
}
}
}
}
}
}
} else {
# ???
}
showindex($text);
?>