diff options
author | Yorhel <git@yorhel.nl> | 2010-10-04 20:17:10 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2010-10-04 20:17:10 +0200 |
commit | babaec57fbcfdd575f2ab0b6ee547caee0f89090 (patch) | |
tree | 033b87a132eafa2f6d5fd32fd3f58889a1659981 /index.cgi |
n'th initial version
Diffstat (limited to 'index.cgi')
-rwxr-xr-x | index.cgi | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/index.cgi b/index.cgi new file mode 100755 index 0000000..6911196 --- /dev/null +++ b/index.cgi @@ -0,0 +1,283 @@ +#!/usr/bin/perl + + +# SQL schema: +# +# CREATE TABLE pastes ( +# code char(5) PRIMARY KEY, +# syntax varchar NOT NULL DEFAULT 'nosyntax', +# wrap boolean NOT NULL DEFAULT false, +# raw text NOT NULL, +# html text, +# date timestamp with time zone NOT NULL DEFAULT now(), +# lastvisit timestamp with time zone NOT NULL DEFAULT now(), +# ip inet +# ); + + +use strict; +use warnings; +use CGI::Minimal; +use CGI::Carp 'fatalsToBrowser'; +use DBI; +use Encode 'decode_utf8', 'encode_utf8'; + +#$SIG{__WARN__} = sub { die $_[0] }; +$ENV{LANG} = 'en_US.UTF-8'; + +my $db = DBI->connect('dbi:Pg:dbname=bpaste', 'bpaste', 'DJbj4nYZ', + { RaiseError => 1, pg_enable_utf8 => 1 }); + +my @syntax = map /([^\/]+)\.vim$/?$1:(), glob "/usr/share/vim/vim72/syntax/*.vim"; +my $cgi = CGI::Minimal->new(); +plain("Too large POST data\n") if $cgi->truncated; + + +my $q = $ENV{QUERY_STRING} || ''; +upload() if ($ENV{REQUEST_METHOD}||'') =~ /post/i; +plain("User-agent: *\nDisallow: /\n") if $q eq 'robots.txt'; +mypastes() if $q eq 'mypastes'; +view($1) if $q =~ /^([a-z0-9]+)$/; +raw($1) if $q =~ /^([a-z0-9]+)\.txt$/; +unpaste($1) if $q =~ /^([a-z0-9]+)\/unpaste$/; +form() if !$q; +plain("404\n"); + + +sub header { + printf "Content-Type: %s; charset=UTF-8\n", shift; + if(($ENV{HTTP_ACCEPT_ENCODING}||'') =~ /gzip/) { + print "Content-Encoding: gzip\n\n"; + binmode STDOUT, ':gzip'; + } else { + print "\n"; + } + binmode STDOUT, ':utf8'; +} + + +sub plain { + header('text/plain'); + print @_; + exit; +} + + +sub escape { + local $_ = shift; + return '' if !$_ && $_ ne '0'; + s/&/&/g; + s/</</g; + s/>/>/g; + s/"/"/g; + s/\r?\n/<br \/>/g; + return $_; +} + + +sub upload { + require Text::VimColor; + + my @chars = ('0'..'9', 'a'..'z'); + my $code = join '', map $chars[rand @chars], 1..5; + my $s = $cgi->param('s') || 'nosyntax'; + my $w = $cgi->param('w') ? 1 : 0; + plain("Unknown syntax code.") if !grep $s eq $_, @syntax; + my $dat = $cgi->param('u') || $cgi->param('f') || ''; + $dat =~ s/\x0D\x0A?/\n/g; + plain("Only UTF-8 encoded data is allowed!\nMake sure you're not uploading a binary file.") + if !eval { $dat = decode_utf8($dat, 1); 1; }; + + my $html = $s eq 'nosyntax' ? undef + : decode_utf8(Text::VimColor->new(string => encode_utf8($dat), filetype => $s)->html()); + $db->do('INSERT INTO pastes (code, syntax, wrap, raw, html, ip) VALUES(?,?,?,?,?,?)', + undef, $code, $s, $w, $dat, $html, $ENV{REMOTE_ADDR}); + + print "Status: 303\nLocation: http://$ENV{HTTP_HOST}/$code\nContent-type: text/plain\n\nRedirecting...\n"; + exit; +} + + +sub get { + my($code, $col) = @_; + my $q = $db->prepare(qq{ + SELECT 1 AS exists, $col, (lastvisit < (NOW()-'1 day'::interval)) AS needsupdate + FROM pastes WHERE code = ? + }); + $q->execute($code); + my $r = $q->fetchrow_hashref(); + plain('No paste with that code') if !$r->{'exists'}; + $db->do('UPDATE pastes SET lastvisit = NOW() WHERE code = ?', undef, $code); + return $r; +} + + +sub view { + my $code = shift; + my $r = get $code, 'syntax, wrap, raw, html, ip'; + + my $cnt = ($r->{raw} =~ y/\n/\n/); + $cnt += 1 if $r->{raw} !~ /\n$/; + if(!$r->{html}) { + $r->{html} = escape $r->{raw}; + } else { + $r->{html} =~ s/\n/<br \/>/g; + } + html(sprintf q| + <div id="toplinks"> + %s + <a href="/%s.txt">raw</a> + <a href="/">new paste</a> + </div> + <table> + <tr><td colspan="2" class="header"> + <h1>Blicky.net nopaste</h1> + </td></tr> + <tr><td class="numbers"><pre>%s</pre></td><td class="code"><pre%s>%s</pre></td></tr> + </table>|, + $r->{ip} && $r->{ip} eq $ENV{REMOTE_ADDR} ? qq{<a href="/$code/unpaste">unpaste</a>} : '', + $code, + $r->{wrap} ? '' : join("\n", map qq|<a name="r$_" href="#r$_">$_</a>|, 1..$cnt), + $r->{wrap} ? ' class="allowwrap"' : '', + $r->{html}, + ); + exit; +} + + +sub raw { + my $code = shift; + my $r = get $code, 'raw'; + plain($r->{raw}); +} + + +sub unpaste { + my $code = shift; + my $r = get $code, 'ip'; + plain("This is not your paste!") if !$r->{ip} || $r->{ip} ne $ENV{REMOTE_ADDR}; + $db->do('DELETE FROM pastes WHERE code = ?', undef, $code); + plain("Unpasted!"); +} + + +sub form { + my $q = $db->prepare('SELECT syntax FROM pastes GROUP BY syntax ORDER BY count(*) DESC LIMIT 7'); + $q->execute(); + my @l = map qq|<a href="#" onclick="return setsyn(this)">$_->{syntax}</a>|, @{$q->fetchall_arrayref({})}; + + my @syn = map qq|<a href="#" onclick="return setsyn(this)">$_</a>|, @syntax; + html(sprintf <<'__', join(' ', @l), join(' ', @syn)); +<form enctype="multipart/form-data" accept-charset="utf-8" method="post" action="/"> + <table> + <tr><td colspan="2" class="header"><h1>Blicky.net nopaste</h1></tr> + <tr><td class="ff"> </td><td style="border-top: 1px solid #999"> + <textarea name="f" id="f"></textarea><br /> + <input type="file" name="u" id="u" /><i style="float: right">or by file upload:</i> + <input type="checkbox" id="w" name="w" value="1" /> <label for="w">allow line wrapping</label><br /> + <input type="submit" value="Submit" id="submit" /> + <input type="text" name="s" id="s" size="10" value="nosyntax" /> <i>Popular: <b class="syntax">%s</b> | <a href="#" onclick="return showall()">Show all »</a></i> + </td></tr> + <tr id="syntax" style="display: none"><td class="ff"> </td><td class="syntax">%s</td></tr> + <tr><td class="ff"> </td><td><ul> + <li>Maximum paste size is 1MiB minus HTTP POST overhead.</li> + <li>Pastes don't expire.</li> + <li>When using the file upload: make sure the file is encoded in UTF-8.</li> + <li>If you absolutely need to have a paste removed from this site, send a mail to <a href="mailto:ayo@blicky.net">ayo@blicky.net</a>.</li> + <li>Want to paste stuff from the commandline? We have a <a href="/bpaste.pl">script</a> for that.</li> + <li>Code highlighting is provided by vim.</li> + </ul></td></tr> + </table> +</form> +__ + exit; +} + + +sub mypastes { + my $q = $db->prepare("SELECT code, to_char(date, 'YYYY-MM-DD HH24:MI:SS') AS date, syntax, substring(raw from 1 for 50) AS preview FROM pastes WHERE ip = ? ORDER BY date DESC"); + $q->execute($ENV{REMOTE_ADDR}); + html(sprintf <<' __', $ENV{REMOTE_ADDR}, + <div id="toplinks"> + <a href="/">new paste</a> + </div> + <table> + <tr><td colspan="2" class="header"><h1>Blicky.net mypastes</h1></tr> + <tr><td class="ff"> </td><td style="border-top: 1px solid #999"> + <b>Listing all the pastes from %s:</b> + <ul> + %s + </ul> + </td></tr> + </table> + __ + join "\n", map sprintf('<li>%s <a href="/%s">%2$s</a> (%s) %s</li>', $_->{date}, $_->{code}, $_->{syntax}, $_->{preview}), @{$q->fetchall_arrayref({})}); + exit; +} + + +sub html { + header('text/html'); + printf <<'__', scalar shift; +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <title>Blicky.net nopaste</title> + <style type="text/css"> + * { margin: 0; padding: 0; z-index: 2 } + body, td { color: #555 } + table { border-collapse: collapse; width: 100%% } + a { color: #888; text-decoration: none } + a:hover { text-decoration: underline } + i, label, li { font-size: 12px; font-style: normal } + textarea, input { background: #fcfcfc; color: #000; border: 1px solid #999 } + textarea:focus, input:focus { background: #fff } + #leftdiv { position: absolute; z-index: -1; left: 0; top: 0; bottom: 0; width: 40px; background: #eee; border-right: 1px solid #999; } + .numbers, .ff { padding-left: 10px; padding-right: 5px; width: 25px; min-width: 25px; text-align: right; background: #eee; border-right: 1px solid #999 } + + .header { background: #eee; padding: 5px 40px } + .header h1 { display: inline; font-size: 16px; color: #555 } + #toplinks { position: absolute; top: 5px; right: 20px } + #toplinks a { font-size: 14px; padding: 2px 0 0 15px } + + textarea { width: 95%%; height: 400px } + #u { float: right; margin-right: 5%; margin-left: 5px } + #submit { clear: right; float: right; margin-right: 5%; width: 200px; } + .syntax { font-size: 11px; padding-top: 15px; font-weight: normal } + ul { padding-top: 30px; padding-left: 3px; list-style-type: circle } + + .code { padding-left: 3px; border-top: 1px solid #999; color: #000 } + .allowwrap { white-space: pre-wrap; word-wrap: break-word; max-width: 700px } + + /* syntax highlighting */ + .synComment { color: #0000FF } + .synConstant { color: #FF00FF } + .synIdentifier { color: #008B8B } + .synStatement { color: #A52A2A ; font-weight: bold } + .synPreProc { color: #A020F0 } + .synType { color: #2E8B57 ; font-weight: bold } + .synSpecial { color: #6A5ACD } + .synUnderlined { color: #000000 ; text-decoration: underline } + .synError { color: #FFFFFF ; background: #FF0000 none } + .synTodo { color: #0000FF ; background: #FFFF00 none } + </style> + <script type="text/javascript"> + function setsyn(w) { + document.getElementById('s').value = w.textContent; + return false; + } + function showall() { + document.getElementById('syntax').style.display = document.getElementById('syntax').style.display == 'none' ? '' : 'none'; + return false; + } + </script> + </head> + <body> + <div id="leftdiv"></div> + %s + </body> +</html> +__ +} + + |