| 1 | """ |
|---|
| 2 | A quick and dirty captcha for use with AccountManagerPlugin registration |
|---|
| 3 | """ |
|---|
| 4 | # Plugin for trac 0.11 |
|---|
| 5 | |
|---|
| 6 | from acct_mgr.web_ui import RegistrationModule |
|---|
| 7 | |
|---|
| 8 | from componentdependencies import IRequireComponents |
|---|
| 9 | |
|---|
| 10 | from genshi.builder import Markup |
|---|
| 11 | from genshi.builder import tag |
|---|
| 12 | from genshi.filters.transform import Transformer |
|---|
| 13 | |
|---|
| 14 | from skimpyGimpy import skimpyAPI |
|---|
| 15 | |
|---|
| 16 | from trac.config import Option |
|---|
| 17 | from trac.core import * |
|---|
| 18 | from trac.web import IRequestFilter |
|---|
| 19 | from trac.web import ITemplateStreamFilter |
|---|
| 20 | from trac.web.api import IRequestHandler |
|---|
| 21 | from trac.web.chrome import add_warning |
|---|
| 22 | |
|---|
| 23 | from utils import random_word |
|---|
| 24 | from web_ui import ImageCaptcha |
|---|
| 25 | |
|---|
| 26 | class RegistrationCaptcha(Component): |
|---|
| 27 | |
|---|
| 28 | ### class data |
|---|
| 29 | implements(IRequestFilter, ITemplateStreamFilter, IRequireComponents) |
|---|
| 30 | dict_file = Option('captchaauth', 'dictionary_file', |
|---|
| 31 | default="http://java.sun.com/docs/books/tutorial/collections/interfaces/examples/dictionary.txt") |
|---|
| 32 | captcha_type = Option('captchaauth', 'type', |
|---|
| 33 | default="png") |
|---|
| 34 | |
|---|
| 35 | |
|---|
| 36 | ### IRequestFilter methods |
|---|
| 37 | |
|---|
| 38 | def pre_process_request(self, req, handler): |
|---|
| 39 | """Called after initial handler selection, and can be used to change |
|---|
| 40 | the selected handler or redirect request. |
|---|
| 41 | |
|---|
| 42 | Always returns the request handler, even if unchanged. |
|---|
| 43 | """ |
|---|
| 44 | |
|---|
| 45 | if req.path_info.strip('/') == "register": |
|---|
| 46 | |
|---|
| 47 | if req.method == "POST": |
|---|
| 48 | correct_answer = req.session.pop('captcha', None) |
|---|
| 49 | req.session.save() |
|---|
| 50 | if req.args['captcha'].lower() != correct_answer: |
|---|
| 51 | |
|---|
| 52 | # XXX this bizaarely doesn't work |
|---|
| 53 | # req.session['captchaauth_message'] = "You typed the wrong word. Please try again." |
|---|
| 54 | |
|---|
| 55 | # req.session.save() |
|---|
| 56 | req.redirect(req.href('register', failed='')) |
|---|
| 57 | if req.method == "GET": |
|---|
| 58 | if 'failed' in req.args: |
|---|
| 59 | add_warning(req, "You typed the wrong word. Please try again.") |
|---|
| 60 | |
|---|
| 61 | # XXX this bizaarely doesn't work |
|---|
| 62 | # message = req.session.pop('captchaauth_message', None) |
|---|
| 63 | # if message: |
|---|
| 64 | # add_warning(req, message) |
|---|
| 65 | |
|---|
| 66 | return handler |
|---|
| 67 | |
|---|
| 68 | # for ClearSilver templates |
|---|
| 69 | def post_process_request(self, req, template, content_type): |
|---|
| 70 | """Do any post-processing the request might need; typically adding |
|---|
| 71 | values to req.hdf, or changing template or mime type. |
|---|
| 72 | |
|---|
| 73 | Always returns a tuple of (template, content_type), even if |
|---|
| 74 | unchanged. |
|---|
| 75 | |
|---|
| 76 | Note that `template`, `content_type` will be `None` if: |
|---|
| 77 | - called when processing an error page |
|---|
| 78 | - the default request handler did not return any result |
|---|
| 79 | |
|---|
| 80 | (for 0.10 compatibility; only used together with ClearSilver templates) |
|---|
| 81 | """ |
|---|
| 82 | |
|---|
| 83 | return (template, content_type) |
|---|
| 84 | |
|---|
| 85 | # for Genshi templates |
|---|
| 86 | def post_process_request(self, req, template, data, content_type): |
|---|
| 87 | """Do any post-processing the request might need; typically adding |
|---|
| 88 | values to the template `data` dictionary, or changing template or |
|---|
| 89 | mime type. |
|---|
| 90 | |
|---|
| 91 | `data` may be update in place. |
|---|
| 92 | |
|---|
| 93 | Always returns a tuple of (template, data, content_type), even if |
|---|
| 94 | unchanged. |
|---|
| 95 | |
|---|
| 96 | Note that `template`, `data`, `content_type` will be `None` if: |
|---|
| 97 | - called when processing an error page |
|---|
| 98 | - the default request handler did not return any result |
|---|
| 99 | |
|---|
| 100 | (Since 0.11) |
|---|
| 101 | """ |
|---|
| 102 | return (template, data, content_type) |
|---|
| 103 | |
|---|
| 104 | |
|---|
| 105 | ### ITemplateStreamFilter method |
|---|
| 106 | |
|---|
| 107 | def filter_stream(self, req, method, filename, stream, data): |
|---|
| 108 | """Return a filtered Genshi event stream, or the original unfiltered |
|---|
| 109 | stream if no match. |
|---|
| 110 | |
|---|
| 111 | `req` is the current request object, `method` is the Genshi render |
|---|
| 112 | method (xml, xhtml or text), `filename` is the filename of the template |
|---|
| 113 | to be rendered, `stream` is the event stream and `data` is the data for |
|---|
| 114 | the current template. |
|---|
| 115 | |
|---|
| 116 | See the Genshi documentation for more information. |
|---|
| 117 | """ |
|---|
| 118 | # move these someplace sensible? |
|---|
| 119 | form_id = "acctmgr_registerform" # id of the registration form |
|---|
| 120 | msg = "Please enter the text below to prove you're not a machine." |
|---|
| 121 | |
|---|
| 122 | if filename == "register.html": |
|---|
| 123 | word = random_word(self.dict_file) |
|---|
| 124 | req.session['captcha'] = word |
|---|
| 125 | req.session.save() |
|---|
| 126 | if self.captcha_type == 'png': |
|---|
| 127 | captcha = '<img src="%s"/>' % req.href('captcha.png') |
|---|
| 128 | else: |
|---|
| 129 | captcha = skimpyAPI.Pre(word).data() |
|---|
| 130 | content = "<p>%s</p><p>%s</p>" % (msg, captcha) |
|---|
| 131 | content += '<label>Confirm: <input type="text" name="captcha" class="textwidget" size="20"/></label>' |
|---|
| 132 | stream |= Transformer('//form[@id="%s"]/fieldset[1]' % form_id).append(tag.div(Markup(content))) |
|---|
| 133 | |
|---|
| 134 | return stream |
|---|
| 135 | |
|---|
| 136 | ### method for IRequireComponents |
|---|
| 137 | |
|---|
| 138 | def requires(self): |
|---|
| 139 | """list of component classes that this component depends on""" |
|---|
| 140 | return [ImageCaptcha, RegistrationModule] |
|---|