diff --git a/README b/README index b49661b..0e16ac2 100644 --- a/README +++ b/README @@ -12,6 +12,7 @@ Gem Requirements * rmagick * RedCloth * rubyzip (for mass image imports) +* libxml-ruby * mysql (recommended -- others may work fine, too) Special installation instructions diff --git a/app/controllers/comments.rb b/app/controllers/comments.rb index 33b92d9..9ef71e1 100644 --- a/app/controllers/comments.rb +++ b/app/controllers/comments.rb @@ -1,4 +1,7 @@ class Comments < Application + + include Ambethia::ReCaptcha::Controller + def new only_provides :html @page = Page.find_by_name(params[:page_id].gsub(/_/, ' ')) @@ -12,7 +15,7 @@ class Comments < Application raise NotFound unless @page @comment = Comment.new(params[:comment]) @comment.page_id = @page.id - if @comment.save + if (logged_in? or verify_recaptcha(@comment)) and @comment.save flash[:notice] = 'Great success!' redirect url(:page, :id => params[:page_id]) else diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb index 5075515..6456368 100644 --- a/app/helpers/comments_helper.rb +++ b/app/helpers/comments_helper.rb @@ -1,5 +1,5 @@ module Merb module CommentsHelper - + include Ambethia::ReCaptcha::Helper end -end \ No newline at end of file +end diff --git a/app/views/comments/_comment_form.html.erb b/app/views/comments/_comment_form.html.erb index 3165494..c752f71 100644 --- a/app/views/comments/_comment_form.html.erb +++ b/app/views/comments/_comment_form.html.erb @@ -18,4 +18,10 @@

<%= text_area_control :comment, :label => 'Comment: ', :rows => 8, :cols => 60 %>

+<% unless logged_in? -%> +

+ Ident + <%= recaptcha_tags %> +

+<% end -%> diff --git a/app/views/comments/new.html.erb b/app/views/comments/new.html.erb index bc3b66a..891839c 100644 --- a/app/views/comments/new.html.erb +++ b/app/views/comments/new.html.erb @@ -4,9 +4,9 @@ <%= error_messages_for :comment %> -<% form_for :comment, :action => url(:comment) do %> +<% form_for :comment, :action => url(:comments) do %> <%= partial :comment_form %>

<%= submit_button 'Create' %>

-<% end -%> \ No newline at end of file +<% end -%> diff --git a/config/init.rb b/config/init.rb index e48dcae..33db16f 100644 --- a/config/init.rb +++ b/config/init.rb @@ -16,6 +16,7 @@ require 'redcloth' require 'RMagick' require 'memcache' require 'memcache_util' +require 'recaptcha' Merb::BootLoader.after_app_loads do config_path = File.join(Merb.root, 'config', 'memcache.yml') @@ -28,4 +29,13 @@ Merb::BootLoader.after_app_loads do Merb::Mailer.config = { :sendmail_path => '/usr/sbin/sendmail' } Merb::Mailer.delivery_method = :sendmail + + recaptcha_path = File.join(Merb.root, 'config', 'recaptcha.yml') + if File.file?(recaptcha_path) and File.readable?(recaptcha_path) + rc = YAML::load_file(recaptcha_path) + ENV['RECAPTCHA_PUBLIC_KEY'] = rc[:public] + ENV['RECAPTCHA_PRIVATE_KEY'] = rc[:private] + else + raise "ReCaptcha configuration file not found!" + end end diff --git a/config/rack.rb b/config/rack.rb index f00cf55..da882dd 100644 --- a/config/rack.rb +++ b/config/rack.rb @@ -1 +1,2 @@ -run Merb::Rack::Application.new \ No newline at end of file +use Merb::Rack::Static, Merb.dir_for(:public) +run Merb::Rack::Application.new diff --git a/config/recaptcha.yml.template b/config/recaptcha.yml.template new file mode 100644 index 0000000..f38814a --- /dev/null +++ b/config/recaptcha.yml.template @@ -0,0 +1,3 @@ +--- +:public: PUBKEY +:private: PRIVKEY diff --git a/lib/LICENSE_for_recaptcha b/lib/LICENSE_for_recaptcha new file mode 100644 index 0000000..dc9c67e --- /dev/null +++ b/lib/LICENSE_for_recaptcha @@ -0,0 +1,19 @@ +Copyright (c) 2007 Jason L Perry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/lib/recaptcha.rb b/lib/recaptcha.rb new file mode 100644 index 0000000..1026763 --- /dev/null +++ b/lib/recaptcha.rb @@ -0,0 +1,78 @@ +# ReCAPTCHA +module Ambethia + module ReCaptcha + RECAPTCHA_API_SERVER = 'http://api.recaptcha.net'; + RECAPTCHA_API_SECURE_SERVER = 'https://api-secure.recaptcha.net'; + RECAPTCHA_VERIFY_SERVER = 'api-verify.recaptcha.net'; + + SKIP_VERIFY_ENV = ['test'] + + module Helper + # Your public API can be specified in the +options+ hash or preferably the environment + # variable +RECAPTCHA_PUBLIC_KEY+. + def recaptcha_tags(options = {}) + # Default options + key = options[:public_key] ||= ENV['RECAPTCHA_PUBLIC_KEY'] + error = options[:error] ||= session[:recaptcha_error] + uri = options[:ssl] ? RECAPTCHA_API_SECURE_SERVER : RECAPTCHA_API_SERVER + xhtml = Builder::XmlMarkup.new :target => out=(''), :indent => 2 # Because I can. + if options[:display] + xhtml.script(:type => "text/javascript"){ xhtml.text! "var RecaptchaOptions = #{options[:display].to_json};\n"} + end + if options[:ajax] + xhtml.div(:id => 'dynamic_recaptcha') {} + xhtml.script(:type => "text/javascript", :src => "#{uri}/js/recaptcha_ajax.js") {} + xhtml.script(:type => "text/javascript") do + xhtml.text! "Recaptcha.create('#{key}', document.getElementById('dynamic_recaptcha') );" + end + else + xhtml.script(:type => "text/javascript", :src => "#{uri}/challenge?k=#{key}&error=#{error}") {} + unless options[:noscript] == false + xhtml.noscript do + xhtml.iframe(:src => "#{uri}/noscript?k=#{key}", + :height => options[:iframe_height] ||= 300, + :width => options[:iframe_width] ||= 500, + :frameborder => 0) {}; xhtml.br + xhtml.textarea(:name => "recaptcha_challenge_field", :rows => 3, :cols => 40) {} + xhtml.input :name => "recaptcha_response_field", + :type => "hidden", :value => "manual_challenge" + end + end + end + raise ReCaptchaError, "No public key specified." unless key + return out + end # recaptcha_tags + end # Helpers + + module Controller + # Your private API key must be specified in the environment variable +RECAPTCHA_PRIVATE_KEY+ + def verify_recaptcha(model = nil) + return true if SKIP_VERIFY_ENV.include? ENV['RAILS_ENV'] + raise ReCaptchaError, "No private key specified." unless ENV['RECAPTCHA_PRIVATE_KEY'] + begin + recaptcha = Net::HTTP.post_form URI.parse("http://#{RECAPTCHA_VERIFY_SERVER}/verify"), { + :privatekey => ENV['RECAPTCHA_PRIVATE_KEY'], + :remoteip => request.remote_ip, + :challenge => params[:recaptcha_challenge_field], + :response => params[:recaptcha_response_field] + } + answer, error = recaptcha.body.split.map { |s| s.chomp } + unless answer == 'true' + session[:recaptcha_error] = error + model.valid? if model + model.errors.add_to_base "Captcha response is incorrect, please try again." if model + return false + else + session[:recaptcha_error] = nil + return true + end + rescue Exception => e + raise ReCaptchaError, e + end + end # verify_recaptcha + end # ControllerHelpers + + class ReCaptchaError < StandardError; end + + end # ReCaptcha +end # Ambethia \ No newline at end of file