/** * Copyright (C) 2011 JTalks.org Team * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jtalks.jcommune.plugin.kaptcha; import org.jtalks.jcommune.model.dto.UserDto; import org.jtalks.jcommune.model.entity.PluginProperty; import org.jtalks.jcommune.plugin.api.core.ExtendedPlugin; import org.jtalks.jcommune.plugin.api.core.RegistrationPlugin; import org.jtalks.jcommune.plugin.api.core.StatefullPlugin; import org.jtalks.jcommune.plugin.api.exceptions.NoConnectionException; import org.jtalks.jcommune.plugin.api.exceptions.PluginConfigurationException; import org.jtalks.jcommune.plugin.api.exceptions.UnexpectedErrorException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; import static org.jtalks.jcommune.model.entity.PluginProperty.Type.INT; import static org.jtalks.jcommune.model.entity.PluginProperty.Type.STRING; /** * Provides refresh, validating captcha, and obtaining captcha as html for registration form. * Uses {@link com.google.code.kaptcha} for captcha functionality. * * @author Andrey Pogorelov */ public class KaptchaPlugin extends StatefullPlugin implements RegistrationPlugin, ExtendedPlugin { protected static final Logger LOGGER = LoggerFactory.getLogger(KaptchaPlugin.class); protected static final String WIDTH_PROPERTY = "Width"; protected static final String HEIGHT_PROPERTY = "Height"; protected static final String LENGTH_PROPERTY = "Length"; protected static final String POSSIBLE_SYMBOLS_PROPERTY = "Possible Symbols"; private static final String MESSAGE_PATH = "org.jtalks.jcommune.plugin.kaptcha.messages"; private static final String DEFAULT_LOCALE_CODE = "en"; private List<PluginProperty> pluginProperties; private KaptchaPluginService service; @Override protected Map<PluginProperty, String> applyConfiguration(List<PluginProperty> properties) throws PluginConfigurationException { int width = 0; int height = 0; int length = 0; String possibleSymbols = ""; for (PluginProperty property : properties) { try { switch (property.getName()) { case WIDTH_PROPERTY: width = Integer.valueOf(property.getValue()); break; case HEIGHT_PROPERTY: height = Integer.valueOf(property.getValue()); break; case LENGTH_PROPERTY: length = Integer.valueOf(property.getValue()); break; case POSSIBLE_SYMBOLS_PROPERTY: possibleSymbols = property.getValue(); break; } } catch (NumberFormatException ex) { LOGGER.error(property.getValue() + " is not valid value for property " + property.getName(), ex); } } if (width < 1 || height < 1 || length < 1 || possibleSymbols.length() < 1) { // this should be returned as a map, but this mechanism should be implemented in the plugin API first throw new PluginConfigurationException( "Can't apply configuration: Width, height, length and possible symbols properties should not be empty."); } service = new KaptchaPluginService(width, height, length, possibleSymbols); pluginProperties = properties; return new HashMap<>(); } @Override public boolean supportsJCommuneVersion(String version) { return true; } @Override public String getName() { return "Kaptcha"; } @Override public List<PluginProperty> getConfiguration() { return pluginProperties; } @Override public List<PluginProperty> getDefaultConfiguration() { PluginProperty width = new PluginProperty(WIDTH_PROPERTY, INT, "100"); PluginProperty height = new PluginProperty(HEIGHT_PROPERTY, INT, "50"); PluginProperty length = new PluginProperty(LENGTH_PROPERTY, INT, "4"); PluginProperty possibleSymbols = new PluginProperty(POSSIBLE_SYMBOLS_PROPERTY, STRING, "0123456789"); return Arrays.asList(width, height, length, possibleSymbols); } @Override public String translateLabel(String code, Locale locale) { String fullCode = "label.plugins.plugin.kaptcha.property.name." + code.replaceAll(" ", "_"); ResourceBundle messages = ResourceBundle.getBundle(MESSAGE_PATH, locale); if (messages.containsKey(fullCode)) { return messages.getString(fullCode); } else { ResourceBundle defaultMessages = ResourceBundle.getBundle(MESSAGE_PATH, new Locale(DEFAULT_LOCALE_CODE)); return defaultMessages.containsKey(fullCode) ? defaultMessages.getString(fullCode) : code; } } @Override public Map<String, String> registerUser(UserDto userDto, Long pluginId) throws NoConnectionException, UnexpectedErrorException { Map<String, String> errors = getService().validateCaptcha(userDto, pluginId); getService().removeCurrentCaptcha(); return errors; } @Override public Map<String, String> validateUser(UserDto userDto, Long pluginId) throws NoConnectionException, UnexpectedErrorException { return getService().validateCaptcha(userDto, pluginId); } @Override public String getHtml(HttpServletRequest request, String pluginId, Locale locale) { return getService().getHtml(request, pluginId, locale); } @Override public Object doAction(String pluginId, String action, HttpServletRequest request, HttpServletResponse response) { try { if ("refreshCaptcha".equalsIgnoreCase(action)) { getService().refreshCaptchaImage(request, response); return Boolean.TRUE; } } catch (IOException ex) { LOGGER.error("Exception at writing captcha image {}", ex); } return Boolean.FALSE; } protected KaptchaPluginService getService() { return this.service; } }