Hello friend. I haven't read a post from #{IP}, and I only remember for a few months, so:
+#{captcha_q}
+ + +EOF + exit 0 +end + + +def fail(msg) + if DEBUG and msg + puts "Content-type: text/plain\n\n" + puts msg + else + redir + end + exit 0 +end + +def redir + puts 'Status: 302 Found' + puts "Location: #{GOTO}#comment-section\n\n" + exit(0) +end + + +def bn(*args) + File.basename *args +end + + +###### begin error checking & arg parsing ###### +cgi = CGI.new +IP = cgi.remote_addr + +if cgi.has_key?('goto') + GOTO = cgi['goto'] +else + GOTO = '/' + fail('redir to /') +end + +if (cgi.has_key?('url') && cgi['url'] != "") || ! cgi.has_key?('comment') + fail("comment not in form or url in form. cgi.params: #{cgi.params}") +end + +COMMENT_TXT = cgi["comment"] + + +if COMMENT_TXT.length > 1000 or GOTO.length > 150 + fail('length of comment or goto is too great') +end + + +captchad = false +if cgi.has_key?('answer') && cgi.has_key?('question') + if cgi['answer'].downcase !~ /^#{CAPTCHA.to_h[cgi['question']]}$/ + do_captcha + end + captchad = true +end + + +-> { + found = false + Dir.foreach('blog') do |entry| + next if ['.','..'].any? { |f| f == entry } + if GOTO == '/blog/' + entry + found = true + break + end + end + fail('goto entry not found') unless found +}[] +######### end error checking & arg parsing ######## + + +$db = db_init +state = nil +WHITELIST_CUTOFF = NOW - 4*DAY + + +####### begin: state for ips we've seen before ####### +[[5, 60], # 1 min + [10, 60*5], # 5 min + [20, 60*60], # 60 min + [30, 60*60*24], # 1 day + [60, 60*60*24*7]] # 1 week + .each do |max_posts, date| + + if $db.execute(<<-SQL, [NOW - date])[0][0] > max_posts + select count(*) from c + where date > ? and ip = '#{IP}' +SQL + state = 'rate_limited' + end +end + +state ||= 'suspect' if $db.execute(<<-SQL)[0][0] > 0 + select count(*) from c + where ip = '#{IP}' and ( +state = 'banned' or +state = 'rate_limited') +SQL + +unless state + older_date = NOW - DAY*2 + last_moderated = $db.execute(<<-SQL, [older_date])[-1] + select date from c + where ip = '#{IP}' and ( + state = 'moderated' or + (date < ? and (state = 'timed' or state = 'known'))) +SQL + last_moderated = last_moderated[0] if last_moderated + last_good = $db.execute(<<-SQL, [older_date])[-1] + select date from c + where ip = '#{IP}' and ( + state = 'picked' or + (date < ? and (state = 'timed' or state = 'known'))) +SQL + last_good = last_good[0] if last_good + if last_moderated && last_good + if last_good > last_moderated + state = 'known' + else + # these 2 waiting conditions are not actually needed, + # since waiting is the default, but meh. + state = 'waiting' + end + elsif last_moderated + state = 'waiting' + elsif last_good + state = 'known' + end +end +####### end: state for ips we've seen before ####### + +####### begin: whitelist checking ######### +glob = "../blog/#{'?'*'YYYY-MM-DD-'.length}#{bn GOTO, '.*'}.md" +md_file = Dir[glob][0] +unless state + b = bn(md_file,'.*') + post_date = Time.parse(b[0..DATE_LEN]).to_i + if post_date > WHITELIST_CUTOFF + state = 'timed' + end +end +###### end: whitelist checking ######## + +state ||= 'waiting' + +if state != 'known' && ! captchad + do_captcha +end + + +# states: +# timed +# # was posted a whitelist period, so automatically posted. +# # whitelist periods are per page times when legit comments are +# # much more likely than spam, so we automatically let comments through. + +# known +# # ip posted good comment before: either, one in picked state, or +# # a timed/known comment which is over 2 days old (I saw it and didn't remove +# # it) + +# picked +# # manually marked as a good comment, so publish it. + +# rate_limited +# # posting too much, consider them a spammer. + +# moderated +# # bad comment, but don't ban them + +# banned +# # all comments from this ip dead, new comment's dont even go into the db. + +# waiting +# # waiting for manual moderation, get's posted automatically if there +# # is none in 24 hours + +# suspect +# # had a bad post in the past. does not +# # automatically get posted in time without moderation. + + +# any of the manual states +date = $db.execute(<<-SQL)[0][0] +select max(date) from c where +state = 'moderated' or +state = 'banned' or +state = 'picked' +SQL + +# not the bad automatic states +query = <<-SQL +select count(*) from c where +state != 'rate_limited' and +state != 'suspect' +SQL + + +if date + new_count = $db.execute(query + 'and date > ?',date) +else + new_count = $db.execute(query) +end + +if new_count == 1 + require 'net/smtp' + def send_email(opts={}) + opts[:to] ||= ENV['USER'] + opts[:server] ||= 'localhost' + opts[:from] ||= ENV['USER'] + opts[:from_alias] ||= ENV['USER'] + opts[:subject] ||= "test subject" + opts[:body] ||= "" + + msg = <