]> www.fi.muni.cz Git - paste.git/blob - paste.pl
Allow hyphen in file names
[paste.git] / paste.pl
1 #!/usr/bin/perl
2
3 use Mojolicious::Lite -signatures;
4 use Mojo::File qw(curfile);
5
6 plugin NotYAMLConfig => { file => 'config.yml',
7         default => {
8                 appname => 'Paste Bin',
9         } };
10
11 my $datadir = curfile->sibling('data');
12 if (app->config->{datadir}) {
13         $datadir = Mojo::File->new(app->config->{datadir});
14 }
15
16 chdir curfile->dirname;
17
18 get '/' => sub ($c) {
19         $c->render(template => 'forbidden', status => 403)
20                 if !length app->config->{password};
21 } => 'index';
22
23 post '/' => sub ($c) {
24         # print STDERR "pass=" . $c->param('password') . "\n";
25         return $c->render(template => 'forbidden', status => 403)
26                 if !defined app->config->{password}
27                         || !length $c->param('password')
28                         || $c->param('password') ne app->config->{password};
29
30         my $file_content = $c->param('text');
31         my $filename = $c->param('filename');
32         my $upload = $c->param('file');
33
34         if (defined $upload && $upload->size) {
35                 # print STDERR "FILENAME = " . $upload->filename . "\n";
36                 $filename = $upload->filename;
37                 $file_content = $upload->slurp;
38         }
39
40         if ($filename !~ /\A\w[\w-\.]*\.\w+\z/) {
41                 # print STDERR "FILENAME2 = " . $upload->filename . "\n";
42                 return $c->render(template => 'forbidden', status => 403);
43         }
44
45         $datadir->child($filename)->spurt($file_content);
46         $c->redirect_to($c->req->url->base . "$filename");
47 };
48
49 get '/<filename>.<ext>'
50         => [ filename => qr/\w[\w-\.]*/, ext => qr/\w+/ ]
51         => sub ($c) {
52         my $fullname = $c->param('filename').'.'.$c->param('ext');
53         my $file = $datadir->child($fullname);
54         my $stat = $file->stat;
55         
56         return $c->reply->not_found
57                 if !defined $stat;
58
59         $c->stash(mtime => POSIX::strftime('%Y-%m-%d %H:%M:%S',
60                 localtime($stat->mtime)));
61         $c->stash(file_content => $file->slurp);
62         my $lang = $c->param('ext');
63
64         $c->stash(language => "language-$lang");
65         $c->render;
66 } => 'default';
67
68 app->mode(app->config->{mode});
69 app->start;
70
71 __DATA__
72 @@ index.html.ep
73 % layout 'default';
74 <h1><%= config->{appname} %></h1>
75 <form method="POST" enctype="multipart/form-data">
76 <label for="text">Enter some text or source code here:</label>
77 <textarea name="text" id="input_text">
78 </textarea>
79 <label for="file">Or select a file to upload:</label>
80 <input type="file" name="file"/>
81 <label for="filename">Name the file:</label>
82 <input type="text" name="filename"/>
83 <label for="password">Password:</label>
84 <input type="password" name="password" />
85 <input type="submit" value="Submit"/>
86 </form>
87
88
89 @@ default.html.ep
90 % layout 'default';
91
92 %= content_for header => begin
93     <base href="<%= config->{base} %>/">
94     <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/qtcreator-dark.min.css">
95     <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js"></script>
96     <script>hljs.highlightAll();</script>
97 % end
98
99 <h1><tt><%= $filename%>.<%= $ext %></tt>
100    <span class="unimportant">— <%= config->{appname} %></span><br/>
101 <small class="unimportant">Created: <%= $mtime %></small></h1>
102 <pre><code class="<%= $language %>"><%= $file_content %></code></pre>
103
104
105 @@ forbidden.html.ep
106 % layout 'default';
107
108 <h1>Forbidden</h1>
109
110
111 @@ layouts/default.html.ep
112 <html>
113   <head>
114     <meta charset="utf-8" />
115     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
116     <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
117     <meta name="viewport" content="width=device-width" />
118
119     <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
120     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.css">
121     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/milligram/1.4.1/milligram.css">
122     <link rel="icon" href="img/shebang-favicon.svg" />
123     <%= content 'header' %>
124     <style>
125       body {
126         background: #1c1f24;
127         color: #f2f2f2;
128       }
129       .wrapper {
130         max-width: 90rem;
131         margin: 0 auto;
132         margin-top: 1.5rem;
133       }
134       #input_text {
135         height: 40%;
136       }
137       .button, input[type="submit"] {
138         background-color: #0030c0;
139         border-color: #202080;
140       }
141       input {
142         color: #f2f2f2;
143       }
144       textarea {
145         color: #f2f2f2;
146         font-family: monospace;
147       }
148       h1 small {
149         font-size: 2.0rem;
150       }
151       .unimportant {
152         color: #999;
153       }
154       pre {
155         border: 0;
156       }
157       pre code {
158         padding: 0;
159         margin-left: 0;
160         margin-right: 0;
161       }
162       div.footer {
163         color: #999;
164         text-align: right;
165       } 
166       a {
167         color: #90c0ff;
168       }
169     </style>
170     <title><%= config->{appname} %></title>
171   </head>
172   <body><div class="wrapper">
173     <%= content %>
174     <div class="footer">
175       Created by <b>Yenya's Paste Bin</b>,
176         <a href="https://www.fi.muni.cz/~kas/git/paste.git/">www.fi.muni.cz/~kas/git/paste</a>
177     </div>
178   </div></body>
179 </html>