001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki.ui; 020 021import java.text.MessageFormat; 022import java.util.ResourceBundle; 023import java.util.regex.Matcher; 024import java.util.regex.Pattern; 025 026import org.apache.wiki.*; 027import org.apache.wiki.i18n.InternationalizationManager; 028import org.apache.wiki.preferences.Preferences; 029 030/** 031 * Provides basic validation services for HTTP parameters. Three standard 032 * validators are provided: email address, identifier and standard input. Standard input 033 * validator will reject any HTML-like input, and any of a number of special 034 * characters. ID validator rejects HTML and quoted strings, and a couple of special characters. 035 * @since 2.3.54 036 */ 037public final class InputValidator 038{ 039 /** Standard input validator. */ 040 public static final int STANDARD = 0; 041 042 /** Input validator for e-mail addresses. **/ 043 public static final int EMAIL = 1; 044 045 /** 046 * @since 2.4.82 047 */ 048 public static final int ID = 2; 049 050 protected static final Pattern EMAIL_PATTERN = Pattern.compile( "^[0-9a-zA-Z-_\\.\\+]+@([0-9a-zA-Z-_]+\\.)+[a-zA-Z]+$" ); 051 052 protected static final Pattern UNSAFE_PATTERN = Pattern.compile( "[\\x00\\r\\n\\x0f\"':<>\\[\\];#&@\\xff{}\\$%\\\\]" ); 053 054 /** Used when checking against IDs such as a full name when saving groups. 055 * @since 2.4.82 */ 056 protected static final Pattern ID_PATTERN = Pattern.compile( "[\\x00\\r\\n\\x0f\"'<>;&\\xff{}]" ); 057 058 private final String m_form; 059 060 private final WikiSession m_session; 061 062 private final WikiContext m_context; 063 064 /** 065 * Constructs a new input validator for a specific form and wiki session. 066 * When validation errors are detected, they will be added to the wiki 067 * session's messages. 068 * @param form the ID or name of the form this validator should be 069 * associated with 070 * @param context the wiki context 071 */ 072 public InputValidator( String form, WikiContext context ) 073 { 074 m_form = form; 075 m_context = context; 076 m_session = context.getWikiSession(); 077 } 078 079 /** 080 * Validates a string against the {@link #STANDARD} validator and 081 * additionally checks that the value is not <code>null</code> or blank. 082 * @param input the string to validate 083 * @param label the label for the string or field ("E-mail address") 084 * @return returns <code>true</code> if valid, <code>false</code> 085 * otherwise 086 */ 087 public boolean validateNotNull( String input, String label ) 088 { 089 return validateNotNull( input, label, STANDARD ); 090 } 091 092 /** 093 * Validates a string against a particular pattern type and additionally 094 * checks that the value is not <code>null</code> or blank. Delegates to 095 * {@link #validate(String, String, int)}. 096 * @param input the string to validate 097 * @param label the label for the string or field ("E-mail address") 098 * @param type the pattern type to use (<em>e.g.</em>, {@link #STANDARD}, {@link #EMAIL}. 099 * @return returns <code>true</code> if valid, <code>false</code> 100 * otherwise 101 */ 102 public boolean validateNotNull( String input, String label, int type ) 103 { 104 if ( isBlank( input ) ) 105 { 106 ResourceBundle rb = Preferences.getBundle( m_context, InternationalizationManager.CORE_BUNDLE ); 107 108 m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.cantbenull"), 109 label ) ); 110 return false; 111 } 112 return validate( input, label, type ) && !isBlank( input ); 113 } 114 115 /** 116 * Validates a string against a particular pattern type: e-mail address, 117 * standard HTML input, etc. Note that a blank or null string will 118 * always validate. 119 * @param input the string to validate 120 * @param label the label for the string or field ("E-mail address") 121 * @param type the target pattern to validate against ({@link #STANDARD}, 122 * {@link #EMAIL}) 123 * @return returns <code>true</code> if valid, <code>false</code> 124 * otherwise 125 */ 126 public boolean validate( String input, String label, int type ) 127 { 128 // If blank, it's valid 129 if ( isBlank( input ) ) 130 { 131 return true; 132 } 133 134 ResourceBundle rb = Preferences.getBundle( m_context, InternationalizationManager.CORE_BUNDLE ); 135 136 // Otherwise, see if it matches the pattern for the target type 137 Matcher matcher; 138 boolean valid; 139 switch( type ) 140 { 141 case STANDARD: 142 matcher = UNSAFE_PATTERN.matcher( input ); 143 valid = !matcher.find(); 144 if ( !valid ) 145 { 146 //MessageTag already invokes replaceEntities() 147 //Object[] args = { label, ""'<>;&[]#\\@{}%$" }; 148 Object[] args = { label, "\'\"<>;&[]#\\@{}%$" }; 149 m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.unsafechars"), args ) ); 150 } 151 return valid; 152 case EMAIL: 153 matcher = EMAIL_PATTERN.matcher( input ); 154 valid = matcher.matches(); 155 if ( !valid ) 156 { 157 Object[] args = { label }; 158 m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.invalidemail"), args ) ); 159 } 160 return valid; 161 case ID: 162 matcher = ID_PATTERN.matcher( input ); 163 valid = !matcher.find(); 164 if ( !valid ) 165 { 166 //MessageTag already invokes replaceEntities() 167 //Object[] args = { label, ""'<>;&{}" }; 168 Object[] args = { label, "\'\"<>;&{}" }; 169 m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.unsafechars"), args ) ); 170 } 171 return valid; 172 default: 173 break; 174 } 175 throw new IllegalArgumentException( "Invalid input type." ); 176 } 177 178 /** 179 * Returns <code>true</code> if a supplied string is null or blank 180 * @param input the string to check 181 * @return <code>true</code> if <code>null</code> or blank (zero-length); 182 * <code>false</code> otherwise 183 */ 184 public static boolean isBlank( String input ) 185 { 186 return input == null || input.trim().length() < 1; 187 } 188}