001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.mail2.core; 019 020import java.nio.charset.StandardCharsets; 021import java.util.BitSet; 022import java.util.Collection; 023import java.util.Locale; 024import java.util.Map; 025import java.util.Random; 026 027/** 028 * Utility methods used by commons-email. 029 * <p> 030 * These methods are copied from other commons components (commons-lang) to avoid creating a dependency for such a small component. 031 * </p> 032 * <p> 033 * This is a package scoped class, and should not be used directly by users. 034 * </p> 035 * 036 * @since 2.0.0 037 */ 038public final class EmailUtils { 039 040 /** 041 * Random object used by random method. This has to be not local to the random method so as to not return the same value in the same millisecond. 042 */ 043 private static final Random RANDOM = new Random(); 044 045 /** 046 * Radix used in encoding. 047 */ 048 private static final int RADIX = 16; 049 050 /** 051 * The escape character used for the URL encoding scheme. 052 */ 053 private static final char ESCAPE_CHAR = '%'; 054 055 /** 056 * BitSet of RFC 2392 safe URL characters. 057 */ 058 private static final BitSet SAFE_URL = new BitSet(256); 059 060 // Static initializer for safe_uri 061 static { 062 // alpha characters 063 for (int i = 'a'; i <= 'z'; i++) { 064 SAFE_URL.set(i); 065 } 066 for (int i = 'A'; i <= 'Z'; i++) { 067 SAFE_URL.set(i); 068 } 069 // numeric characters 070 for (int i = '0'; i <= '9'; i++) { 071 SAFE_URL.set(i); 072 } 073 074 // safe chars 075 SAFE_URL.set('-'); 076 SAFE_URL.set('_'); 077 SAFE_URL.set('.'); 078 SAFE_URL.set('*'); 079 SAFE_URL.set('+'); 080 SAFE_URL.set('$'); 081 SAFE_URL.set('!'); 082 SAFE_URL.set('\''); 083 SAFE_URL.set('('); 084 SAFE_URL.set(')'); 085 SAFE_URL.set(','); 086 SAFE_URL.set('@'); 087 } 088 089 /** 090 * Encodes an input string according to RFC 2392. Unsafe characters are escaped. 091 * 092 * @param input the input string to be URL encoded 093 * @return a URL encoded string 094 * @see <a href="https://tools.ietf.org/html/rfc2392">RFC 2392</a> 095 */ 096 public static String encodeUrl(final String input) { 097 if (input == null) { 098 return null; 099 } 100 final StringBuilder builder = new StringBuilder(); 101 for (final byte c : input.getBytes(StandardCharsets.US_ASCII)) { 102 final int b = c & 0xff; 103 if (SAFE_URL.get(b)) { 104 builder.append((char) b); 105 } else { 106 builder.append(ESCAPE_CHAR); 107 final char hex1 = Character.toUpperCase(Character.forDigit(b >> 4 & 0xF, RADIX)); 108 final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX)); 109 builder.append(hex1); 110 builder.append(hex2); 111 } 112 } 113 return builder.toString(); 114 } 115 116 public static boolean isEmpty(final Collection<?> collection) { 117 return collection == null || collection.isEmpty(); 118 } 119 120 public static boolean isEmpty(final Map<?, ?> map) { 121 return map == null || map.isEmpty(); 122 } 123 124 public static boolean isEmpty(final Object[] array) { 125 return array == null || array.length == 0; 126 } 127 128 /** 129 * Checks if a String is empty ("") or null. 130 * <p> 131 * Copied from Commons Lang 2.1, svn 240418 132 * </p> 133 * 134 * @param str the String to check, may be null 135 * @return {@code true} if the String is empty or null 136 */ 137 public static boolean isEmpty(final String str) { 138 return str == null || str.isEmpty(); 139 } 140 141 /** 142 * Checks if a String is not empty ("") and not null. 143 * <p> 144 * Copied from Commons Lang 2.1, svn 240418 145 * </p> 146 * 147 * @param str the String to check, may be null 148 * @return {@code true} if the String is not empty and not null 149 */ 150 public static boolean isNotEmpty(final String str) { 151 return str != null && !str.isEmpty(); 152 } 153 154 /** 155 * Creates a random string based on a variety of options, using supplied source of randomness. 156 * <p> 157 * If start and end are both {@code 0}, start and end are set to {@code ' '} and {@code 'z'}, the ASCII printable characters, will be used, unless letters 158 * and numbers are both {@code false}, in which case, start and end are set to {@code 0} and {@code Integer.MAX_VALUE}. 159 * </p> 160 * <p> 161 * If set is not {@code null}, characters between start and end are chosen. 162 * </p> 163 * <p> 164 * This method accepts a user-supplied {@link Random} instance to use as a source of randomness. By seeding a single {@link Random} instance with a fixed 165 * seed and using it for each call, the same random sequence of strings can be generated repeatedly and predictably. 166 * </p> 167 * <p> 168 * Copied from Commons Lang 2.1, svn 201930 169 * </p> 170 * 171 * @param count the length of random string to create 172 * @param start the position in set of chars to start at 173 * @param end the position in set of chars to end before 174 * @param letters only allow letters? 175 * @param numbers only allow numbers? 176 * @param chars the set of chars to choose randoms from. If {@code null}, then it will use the set of all chars. 177 * @param random a source of randomness. 178 * @return the random string 179 * @throws IllegalArgumentException if {@code count} < 0. 180 */ 181 private static String random(int count, int start, int end, final boolean letters, final boolean numbers, final char[] chars, final Random random) { 182 if (count == 0) { 183 return ""; 184 } 185 if (count < 0) { 186 throw new IllegalArgumentException("Requested random string length " + count + " is less than 0."); 187 } 188 189 if (start == 0 && end == 0) { 190 end = 'z' + 1; 191 start = ' '; 192 193 if (!letters && !numbers) { 194 start = 0; 195 end = Integer.MAX_VALUE; 196 } 197 } 198 199 final StringBuilder buffer = new StringBuilder(); 200 final int gap = end - start; 201 202 while (count-- != 0) { 203 char ch; 204 205 if (chars == null) { 206 ch = (char) (random.nextInt(gap) + start); 207 } else { 208 ch = chars[random.nextInt(gap) + start]; 209 } 210 211 if (letters && numbers && Character.isLetterOrDigit(ch) || letters && Character.isLetter(ch) || numbers && Character.isDigit(ch) 212 || !letters && !numbers) { 213 buffer.append(ch); 214 } else { 215 count++; 216 } 217 } 218 219 return buffer.toString(); 220 } 221 222 /** 223 * Creates a random string whose length is the number of characters specified. 224 * <p> 225 * Characters will be chosen from the set of alphabetic characters. 226 * </p> 227 * <p> 228 * Copied from Commons Lang 2.1, svn 201930 229 * </p> 230 * 231 * @param count the length of random string to create 232 * @return the random string 233 */ 234 public static String randomAlphabetic(final int count) { 235 return random(count, 0, 0, true, false, null, RANDOM); 236 } 237 238 /** 239 * Replaces end-of-line characters with spaces. 240 * 241 * @param input the input string to be scanned. 242 * @return a clean string 243 */ 244 public static String replaceEndOfLineCharactersWithSpaces(final String input) { 245 return input == null ? null : input.replace('\n', ' ').replace('\r', ' '); 246 } 247 248 public static String toLower(final String value) { 249 return value.toLowerCase(Locale.ROOT); 250 } 251 252 /** 253 * Constructs a new {@code EmailException} with no detail message. 254 */ 255 private EmailUtils() { 256 } 257}