14f1152cb92ca13970971b4b212a4c21ff71b623
3 # Copyright (C) 2016 Ian Kelling
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # debian sets LANG=C when starting apache2.
19 # the envoding comment above fixes the internal encoding afaik,
21 # https://stackoverflow.com/questions/20521371/set-utf-8-as-default-for-ruby-1-9-3
22 # also note man ruby's -E arg.
23 Encoding
.default_external
= Encoding
::UTF_8
30 require_relative
'../b'
37 x
= (<<EOF).split("\n")
40 Would you sleep better on a bed or a keyboard?
42 Are there more or less than one million people on the Earth?
44 Which of Lilly and Robert is more commonly a woman’s name?
46 Which word has fewer letters, adorable or fox?
48 What is the normal color of milk?
50 Which of Iceland and Turkey is an island nation?
52 Is a filesystem like a tree or a rose?
54 What character is a tilde?
56 Which is brighter, the moon or the sun?
58 Which is closer, the moon or the sun?
60 What language is this sentence written in?
62 What animal says "meow" and catches mice?
64 What animal quacks and has webbed feet?
66 Which are better fliers: worms or birds?
68 Which typically runs first: a kernel or a web browser?
78 captcha_q
= CAPTCHA
.sample
[0]
79 puts
"Content-type: text/html\n\n"
80 puts
skel('comment.rb', "#{DN}/captcha", <<EOF, ' / <a href="/blog.html">blog</a> / comment-captcha')
81 <p>Hello friend. I haven't read a post from #{IP}, and I only remember for a few months, so:</p>
84 <form action="/comment.rb" method="post">
85 <input class="misc" type="text" name="url">
86 <input name="goto" type="hidden" value="#{GOTO}">
87 <input name="question" type="hidden" value="#{captcha_q}">
90 <textarea rows="10" name="comment" maxlength="1000">#{COMMENT_TXT}</textarea>
91 <input type="submit" value="Submit">
100 puts
"Content-type: text/plain\n\n"
109 File
.write('/tmp/x', GOTO
)
110 puts
'Status: 302 Found'
111 puts
"Location: #{GOTO}#comment-section\n\n"
121 ###### begin error checking & arg parsing ######
125 if cgi
.has_key
?('goto')
132 if (cgi
.has_key
?('url') && cgi
['url'] != "") || ! cgi
.has_key
?('comment')
133 fail
["comment not in form or url in form. cgi.params: #{cgi.params}"]
136 COMMENT_TXT
= cgi
["comment"]
139 if COMMENT_TXT
.length
> 1000 or GOTO
.length
> 150
140 fail
['length of comment or goto is too great']
145 if cgi
.has_key
?('answer') && cgi
.has_key
?('question')
146 if cgi
['answer'].downcase
!~
/^#{CAPTCHA.to_h[cgi['question']]}$/
155 Dir
.foreach('blog') do |entry
|
156 next if ['.','..'].any
? { |f
| f
== entry
}
157 if GOTO
== 'blog/' + entry
162 fail
['goto entry not found'] unless found
164 ######### end error checking & arg parsing ########
169 WHITELIST_CUTOFF
= NOW
- 4*DAY
172 ####### begin: state for ips we've seen before #######
175 [20, 60*60], # 60 min
176 [30, 60*60*24], # 1 day
177 [60, 60*60*24*7]] # 1 week
178 .each
do |max_posts
, date
|
180 if $db.execute(<<-SQL, [NOW - date])[0][0] > max_posts
181 select count(*) from c
182 where date > ? and ip = '#{IP}'
184 state
= 'rate_limited'
188 state
||= 'suspect' if $db.execute(<<-SQL)[0][0] > 0
189 select count(*) from c
190 where ip = '#{IP}' and (
192 state = 'rate_limited')
196 older_date
= NOW
- DAY
*2
197 last_moderated
= $db.execute(<<-SQL, [older_date])[-1][0]
199 where ip = '#{IP}' and (
200 state = 'moderated' or
201 (date < ? and (state = 'timed' or state = 'known')))
203 last_good
= $db.execute(<<-SQL, [older_date])[-1][0]
205 where ip = '#{IP}' and (
207 (date < ? and (state = 'timed' or state = 'known')))
209 if last_moderated
&& last_good
210 if last_good
> last_moderated
213 # these 2 waiting conditions are not actually needed,
214 # since waiting is the default, but meh.
223 ####### end: state for ips we've seen before #######
225 ####### begin: whitelist checking #########
226 glob
= "../blog/#{'?'*'YYYY-MM-DD-'.length}#{bn GOTO, '.*'}.md"
227 md_file
= Dir
[glob
][0]
230 post_date
= Time
.parse(b
[0..DATE_LEN
]).to_i
231 if post_date
> WHITELIST_CUTOFF
235 ###### end: whitelist checking ########
239 if state
!= 'known' && ! captchad
246 # # was posted a whitelist period, so automatically posted.
247 # # whitelist periods are per page times when legit comments are
248 # # much more likely than spam, so we automatically let comments through.
251 # # ip posted good comment before: either, one in picked state, or
252 # # a timed/known comment which is over 2 days old (I saw it and didn't remove
256 # # manually marked as a good comment, so publish it.
259 # # posting too much, consider them a spammer.
262 # # bad comment, but don't ban them
265 # # all comments from this ip dead, new comment's dont even go into the db.
268 # # waiting for manual moderation, get's posted automatically if there
269 # # is none in 24 hours
272 # # had a bad post in the past. does not
273 # # automatically get posted in time without moderation.
276 # any of the manual states
277 date
= $db.execute(<<-SQL)[0][0]
278 select max(date) from c where
279 state = 'moderated' or
284 # not the bad automatic states
286 select count(*) from c where
287 state != 'rate_limited' and
293 new_count
= $db.execute(query
+ 'and date > ?',date
)
295 new_count
= $db.execute(query
)
300 def send_email(opts
={})
301 opts
[:to] ||= ENV['USER']
302 opts
[:server] ||= 'localhost'
303 opts
[:from] ||= ENV['USER']
304 opts
[:from_alias] ||= ENV['USER']
305 opts
[:subject] ||= "test subject"
308 msg
= <<END_OF_MESSAGE
309 From: #{opts[:from_alias]} <#{opts[:from]}>
311 Subject: #{opts[:subject]}
316 Net
::SMTP.start(opts
[:server]) do |smtp
|
317 smtp
.send_message msg
, opts
[:from], opts
[:to]
320 send_email
:subject => 'new comments on iankelling.org'
323 $db.execute('insert into c values (NULL, ?, ?, ?, ?, ?)',