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
|
require_relative "core"
require_relative "concatenated_io"
require "webrick"
require "erb"
class Puke::Server
THREADS_PER_PAGE = 20
def initialize
@access_log = File.open(File.join(Puke.datadir, "access.log"), "a")
@access_log.sync = true
al = [[@access_log, WEBrick::AccessLog::COMMON_LOG_FORMAT]]
@server = WEBrick::HTTPServer.new(:BindAddress => Puke.listen_address,
:Port => Puke.listen_port,
:AccessLog => al)
@server.mount_proc("/", method(:root))
@server.mount_proc("/mid", method(:mid))
@server.mount_proc("/new", method(:new))
@root_template = ERB.new(ROOT_TEMPLATE)
end
def root(req, res)
case req.path_info
when "/"
page = req.query.fetch("page", "1").to_i
last_tid = Puke.last_tid
if last_tid >= 0
b = (last_tid-page*THREADS_PER_PAGE+1).clamp(0, last_tid)
e = (last_tid-(page-1)*THREADS_PER_PAGE).clamp(0, last_tid)
threads = e.downto(b)
.map { |tid| Puke.thread(tid) }
.map { |mids| mids.map { |mid| Puke.metadata(mid) } }
else
threads = []
end
res.content_type = "text/html; charset=UTF-8"
res.body = @root_template.result(binding)
when "/robots.txt"
# I don't want search engines to index puke
res.content_type = "text/plain"
res.body = <<EOF
User-agent: *
Disallow: /
EOF
else
res.status = 404
end
end
def mid(req, res)
/\A\/(?<mid>.+@.+?)(?:\/(?<unya>\w+)\.mbox)?\z/ =~ req.path_info and
tid = Puke.mid?(mid) or (res.status = 404 and return)
# NB: Puke::ConcatenatedIO is not really an IO object and
# 'res.body.is_a?(IO)' returns false, leading the non-existent 'bytesize'
# method to be called by WEBrick, unless chunked response is enabled.
res.chunked = true
case unya
when "thread"
files = Puke.thread(tid).map { |mid| Puke.open(mid) }
res.body = Puke::ConcatenatedIO.new(files)
when nil, "raw"
res.body = Puke.open(mid)
else
res.status = 400
return
end
res.content_type = "text/plain"
end
# FIXME: How about large messages?
def new(req, res)
Puke.secret and
req.path_info == "/#{Puke.secret}" or (res.status = 404 and return)
# Parse query string ourselves because req.query does not work for POST
# requests as for GET requests.
query = WEBrick::HTTPUtils.parse_query(req.query_string)
if format = query["format"]
is_raw = format == "raw"
else
is_raw = req.body[0, 5] != "From "
end
mids = Puke.create(StringIO.new(req.body), is_raw)
res.content_type = "text/plain"
res.body = "#{mids}\n"
end
def start
@server.start
end
def shutdown
@server.shutdown
@access_log.close
end
private
include ERB::Util
def fdate(time)
time.strftime("%Y-%m-%d %H:%M:%S %:z")
end
ROOT_TEMPLATE = <<'EOF'
<html>
<head>
<title>random texts (page: <%=page%>)</title>
<style>
pre { whitespace: pre-wrap; }
</style>
</head>
<body><h1>random texts (page: <%=page%>)</h1>
<hr>
<%threads.each do |ms| root, *child = ms%><pre>
--<%=h fdate root[:date]%> <a href="/mid/<%=h root[:mid]%>"><%=h root[:subject]%></a> <a href="/mid/<%=h root[:mid]%>/thread.mbox">[thread.mbox]</a><%child.each do |m|%>
`<%=h fdate m[:date]%> <a href="/mid/<%=h m[:mid]%>"><%=h m[:subject]%></a><%end%></pre>
<%end%>
<hr>
page: <%if page > 1%><a href="/?page=<%=page - 1%>"><< prev</a><%end%>
<%if threads.last&.dig(0, :tid)&.> 0%><a href="/?page=<%=page + 1%>">next >></a><%end%>
</body>
</html>
EOF
end
|