summaryrefslogtreecommitdiff
path: root/index.cgi
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2010-10-04 20:17:10 +0200
committerYorhel <git@yorhel.nl>2010-10-04 20:17:10 +0200
commitbabaec57fbcfdd575f2ab0b6ee547caee0f89090 (patch)
tree033b87a132eafa2f6d5fd32fd3f58889a1659981 /index.cgi
n'th initial version
Diffstat (limited to 'index.cgi')
-rwxr-xr-xindex.cgi283
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/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ s/"/&quot;/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">&nbsp;</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 &raquo;</a></i>
+ </td></tr>
+ <tr id="syntax" style="display: none"><td class="ff">&nbsp;</td><td class="syntax">%s</td></tr>
+ <tr><td class="ff">&nbsp;</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">&nbsp;</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>
+__
+}
+
+