reCaptcha in XQuery

Ever fancied adding a Captcha to your eXist Web Application?

reCaptcha makes it very easy to add a Captcha system to any application and by using reCaptcha you will be supporting a good cause - digitizing books.

With reCaptcha and a little bit of XQuery we can easily setup a working Captcha system. I have written a reCaptcha XQuery module that you anyone may use...

Store each of the files below in your /db collection, make sure to replace the instances of YOUR-PRIVATE-KEY and YOUR-PUBLIC-KEY with your reCaptcha public and private keys which you can obtain from signing up at the reCaptcha website.

Then simply call from your web-browser (assuming you are running eXist in Jetty mode).

Example (X)HTML Page (example.html)

<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>reCaptacha Example</title> </head> <body> <form method="post" action="example.xql"> <!-- start Client API reCaptcha code --> <script type="text/javascript" src="http://api.recaptcha.net/challenge?k=YOUR-PUBLIC-KEY"></script> <noscript> <iframe src="http://api.recaptcha.net/noscript?k=YOUR-PUBLIC-KEY" height="300" width="500" frameborder="0"></iframe><br/> <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea> <input type="hidden" name="recaptcha_response_field" value="manual_challenge"/> </noscript> <!-- end Client API reCaptcha code --> <input type="submit"/> </form> </body> </html>

Example XQuery handler (example.xql)

xquery version "1.0"; declare namespace request = "http://exist-db.org/xquery/request"; import module namespace recap = "http://www.exist-db.org/xquery/util/recapture" at "xmldb:exist:///db/recaptcha.xqm"; let $recapture-private-key := "YOUR-PRIVATE-KEY" return recap:validate($recapture-private-key, request:get-parameter("recaptcha_challenge_field", ()), request:get-parameter("recaptcha_response_field",()))

reCaptcha XQuery Module (recaptcha.xqm)

module namespace recap="http://www.exist-db.org/xquery/util/recapture"; declare namespace httpclient = "http://exist-db.org/xquery/httpclient"; declare variable $recap:VALIDATE_URI as xs:anyURI := xs:anyURI("http://api-verify.recaptcha.net/verify"); (:~ : Module for working with reCaptcha :) declare function recap:validate($private-key as xs:string, $recaptcha-challenge as xs:string, $recaptcha-response as xs:string) as xs:boolean { (: let $client-ip := request:get-remote-addr(), :) let $client-ip := request:get-header("X-Real-IP"), (: if behind webserver proxy :) $post-fields := <httpclient:fields> <httpclient:field name="privatekey" value="{$private-key}"/> <httpclient:field name="remoteip" value="{$client-ip}"/> <httpclient:field name="challenge" value="{$recaptcha-challenge}"/> <httpclient:field name="response" value="{$recaptcha-response}"/> </httpclient:fields> return let $response := httpclient:post-form($recap:VALIDATE_URI, $post-fields, false(), ()) return let $recapture-response := $response/httpclient:body/text() return if(starts-with($recapture-response, "true"))then ( true() ) else ( (: util:log("debug", concat("reCaptcha response='", $capture-response, "'")), :) (: uncomment to debug reCaptcha response :) false() ) };