-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathzpaste.cgi
More file actions
executable file
·249 lines (187 loc) · 4.77 KB
/
zpaste.cgi
File metadata and controls
executable file
·249 lines (187 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#! /usr/bin/env perl
use strict;
use warnings;
# settings
use constant KEY => 'zi8ahkoobahko,xaefeetuphei6eaCee';
use constant DATADIR => '/space/www/data/zpaste';
use constant METADIR => '/space/www/data/zpaste/.web';
use constant METASUFFIX => '.meta';
use constant BASEURL => 'http://p.example.com/';
# documentation
=head1 NAME
zpaste.cgi - zpaste script for accepting paste requests
=head1 SYNOPSIS
https://www.example.com/zpaste.cgi (POST)
=head1 DESCRIPTION
This scripts accepts a paste request from the command-line client
(B<zpaste>). The "form" submitted by B<zpaste> has the following
fields:
=over 4
=item I<key> (required)
The pre-shared authentication key: an arbitrary string. Just make
sure the C<KEY> constants in both this script and the B<zpaste> client
match.
=item I<data> (required, unless I<del> is set)
Contents of the paste. In most cases, this should (and has to) be a
file attachment field, the contents of which will be directly written
as the contents of the paste. The single exception is if I<link> is
set: in that case, this field needs to be a regular plain old text
field, containing the URL to redirect to.
=item I<name> (optional, unless I<del> is set)
Name of the paste. If not specified, a random name will be generated.
=item I<link> (optional, boolean)
If set, the paste is instead a link to redirect to.
=item I<force> (optional, boolean)
If set, a paste with the same name than an existing one overwrites the
old one. If not set, a duplicate name is an error.
=item I<del> (optional, boolean)
If set, deletes the named paste instead of adding a new one.
=back
=cut
use CGI;
use File::Spec::Functions;
use Fcntl;
use SDBM_File;
#use GDBM_File;
# CGI setup
my $q = CGI->new;
# check for authentication
my $key = $q->param('key') || '';
if ($key ne KEY)
{
print $q->header(-type => 'text/plain', -charset => 'utf-8', -status => '403');
print "invalid authentication key\n";
exit;
}
print $q->header(-type => 'text/plain', -charset => 'utf-8');
# attach to the rewrite mapping db
my $rewritedb = catfile(DATADIR, 'rewrite.db');
my %rewrites;
tie %rewrites, 'SDBM_File', $rewritedb, O_RDWR|O_CREAT, 0644;
# tie %rewrites, 'GDBM_File', $rewritedb, &GDBM_WRCREAT, 0644;
unless (tied %rewrites)
{
print "unable to attach: $rewritedb: $!";
exit;
}
# decide on file name
my $name = $q->param('name');
if ($name)
{
$name =~ s/[^a-zA-Z0-9_-]//g;
}
else
{
# invent a random non-existing name
RANDNAME:
foreach my $len (4 .. 8)
{
for (my $count = 10**$len; $count > 0; $count--)
{
$name = randname($len);
last RANDNAME unless -e $name || $rewrites{$name};
}
}
if (!$name || -e $name)
{
print "unable to invent a name\n";
exit;
}
}
# for pre-existing names, abort or wipe the old
if ($rewrites{$name})
{
if ($q->param('force') || $q->param('del'))
{
delete $rewrites{$name};
}
else
{
print "link '$name' exists already\n";
exit;
}
if ($q->param('del'))
{
print "link '$name' deleted\n";
exit;
}
}
elsif (-e catfile(DATADIR, $name))
{
if ($q->param('force') || $q->param('del'))
{
unlink catfile(DATADIR, $name), catfile(METADIR, $name.METASUFFIX);
}
else
{
print "paste '$name' exists already\n";
exit;
}
if ($q->param('del'))
{
print "paste '$name' deleted\n";
exit;
}
}
elsif ($q->param('del'))
{
print "paste '$name' does not exist\n";
exit;
}
# make sure there's some data in the request
my $data = $q->param('data');
unless ($data)
{
print "request data field empty\n";
exit;
}
# handle link-redirection "pastes"
if ($q->param('link'))
{
$rewrites{$name} = $data;
untie %rewrites;
print BASEURL, $name, "\n";
exit;
}
# write paste metafile if necessary
my $type = $q->param('type');
if ($type)
{
my $metafile = catfile(METADIR, $name.METASUFFIX);
open my $meta, '>:utf8', $metafile;
unless ($meta)
{
print "unable to write: $metafile: $!\n";
exit;
}
chmod 0644, $metafile;
print $meta "Content-Type: $type\n";
close $meta;
}
# write the paste contents to file
my $in = $q->upload('data');
unless ($in)
{
print "request data field not a file\n";
exit;
}
my $datafile = catfile(DATADIR, $name);
open my $out, '>:raw', $datafile;
unless ($out)
{
print "unable to write: $datafile: $!\n";
exit;
}
chmod 0644, $datafile;
my $buf;
print $out $buf while read($in, $buf, 65536) > 0;
close $in;
close $out;
# return a link to the created paste
print BASEURL, $name, "\n";
# helper subs
sub randname
{
my $len = shift;
return join('', map { my $r = int(rand(36)); $r < 10 ? chr(0x30+$r) : chr(87+$r) } (1 .. $len));
}