| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.forms.formmodel; |
| |
| import java.security.NoSuchAlgorithmException; |
| import java.security.SecureRandom; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import org.apache.avalon.framework.CascadingRuntimeException; |
| import org.apache.avalon.framework.context.Context; |
| import org.apache.cocoon.components.ContextHelper; |
| import org.apache.cocoon.environment.ObjectModelHelper; |
| import org.apache.cocoon.environment.Session; |
| import org.apache.cocoon.forms.FormsConstants; |
| import org.apache.cocoon.xml.AttributesImpl; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.SAXException; |
| |
| |
| /** |
| * A {@link Field} for CAPTCHA validation. Upon generation, a secret random string is stored |
| * in a session attribute having a randomly generated name, for use by a |
| * {@link org.apache.cocoon.forms.validation.impl.CaptchaValidator}. |
| * <br> |
| * Usage sample: |
| * <pre> |
| <fd:captcha id="f1" required="true"> |
| <fd:label>Copy the number shown into the input field</fd:label> |
| <fd:datatype base="string"/> |
| <fd:validation> |
| <fd:captcha/> |
| </fd:validation> |
| </fd:captcha> |
| * </pre> |
| * |
| * @see <a href="http://www.captcha.net/">captcha.net</a> |
| * @version $Id$ |
| */ |
| public class CaptchaField extends Field { |
| |
| public static final String SESSION_ATTR_PREFIX = "captcha-"; |
| |
| private static final String IMAGE_EL = "captcha-image"; |
| private static final String SECRET_CHARS = "abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ123456789"; |
| private static final int SESSION_ATTR_NAME_LENGTH = 6; |
| |
| private Context avalonContext; |
| private int length; |
| |
| /** |
| * Random number generator used to create session attribute name. |
| */ |
| protected static final SecureRandom random; |
| |
| static { |
| SecureRandom sr = null; |
| try { |
| sr = SecureRandom.getInstance("SHA1PRNG"); |
| } catch (java.security.NoSuchAlgorithmException nsae) { |
| // Maybe we are on IBM's SDK |
| try { |
| sr = SecureRandom.getInstance("IBMSecureRandom"); |
| } catch (NoSuchAlgorithmException e) { |
| throw new CascadingRuntimeException("No random number generator available", e); |
| } |
| } finally { |
| random = sr; |
| } |
| random.setSeed(System.currentTimeMillis()); |
| } |
| |
| public CaptchaField(CaptchaFieldDefinition fieldDefinition, Context avalonContext) { |
| super(fieldDefinition); |
| this.avalonContext = avalonContext; |
| this.length = fieldDefinition.getLength(); |
| } |
| |
| private String generateSecret() { |
| StringBuffer secret = new StringBuffer(length); |
| for (int n = 0 ; n < length ; n++) { |
| int randomnumber = random.nextInt(SECRET_CHARS.length()); |
| secret.append(SECRET_CHARS.charAt(randomnumber)); |
| } |
| return secret.toString(); |
| } |
| |
| public void generateItemSaxFragment(ContentHandler contentHandler, Locale locale) throws SAXException { |
| super.generateItemSaxFragment(contentHandler, locale); |
| byte[] bytes = new byte[SESSION_ATTR_NAME_LENGTH]; |
| char[] result = new char[bytes.length * 2]; |
| random.nextBytes(bytes); |
| for (int i = 0; i < SESSION_ATTR_NAME_LENGTH; i++) { |
| byte ch = bytes[i]; |
| result[2 * i] = Character.forDigit(Math.abs(ch >> 4), 16); |
| result[2 * i + 1] = Character.forDigit(Math.abs(ch & 0x0f), 16); |
| } |
| String id = new String(result); |
| Map objectModel = ContextHelper.getObjectModel(this.avalonContext); |
| Session session = ObjectModelHelper.getRequest(objectModel).getSession(true); |
| String secret = generateSecret(); |
| session.setAttribute(SESSION_ATTR_PREFIX + id, secret); |
| this.setAttribute("secret", secret); |
| AttributesImpl attrs = new AttributesImpl(); |
| attrs.addAttribute("", "id", "id", "PCDATA", id); |
| contentHandler.startElement(FormsConstants.INSTANCE_NS, IMAGE_EL, FormsConstants.INSTANCE_PREFIX_COLON + IMAGE_EL, attrs); |
| contentHandler.endElement(FormsConstants.INSTANCE_NS, IMAGE_EL, FormsConstants.INSTANCE_PREFIX_COLON + IMAGE_EL); |
| } |
| } |