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 */
019 package org.apache.wiki.ui;
020
021 import java.text.MessageFormat;
022 import java.util.ResourceBundle;
023 import java.util.regex.Matcher;
024 import java.util.regex.Pattern;
025
026 import org.apache.wiki.*;
027 import org.apache.wiki.i18n.InternationalizationManager;
028 import 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 */
037 public 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 Object[] args = { label, ""'<>;&[]#\\@{}%$" };
147 m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.unsafechars"),
148 args ) );
149 }
150 return valid;
151 case EMAIL:
152 matcher = EMAIL_PATTERN.matcher( input );
153 valid = matcher.matches();
154 if ( !valid )
155 {
156 Object[] args = { label };
157 m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.invalidemail"),
158 args ) );
159 }
160 return valid;
161 case ID:
162 matcher = ID_PATTERN.matcher( input );
163 valid = !matcher.find();
164 if ( !valid )
165 {
166 Object[] args = { label, ""'<>;&{}" };
167 m_session.addMessage( m_form, MessageFormat.format( rb.getString("validate.unsafechars"),
168 args ) );
169 }
170 return valid;
171 default:
172 break;
173 }
174 throw new IllegalArgumentException( "Invalid input type." );
175 }
176
177 /**
178 * Returns <code>true</code> if a supplied string is null or blank
179 * @param input the string to check
180 * @return <code>true</code> if <code>null</code> or blank (zero-length);
181 * <code>false</code> otherwise
182 */
183 public static boolean isBlank( String input )
184 {
185 return input == null || input.trim().length() < 1;
186 }
187 }