1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.mail2.core;
19
20 import java.nio.charset.StandardCharsets;
21 import java.util.BitSet;
22 import java.util.Collection;
23 import java.util.Locale;
24 import java.util.Map;
25 import java.util.Random;
26
27 /**
28 * Utility methods used by commons-email.
29 * <p>
30 * These methods are copied from other commons components (commons-lang) to avoid creating a dependency for such a small component.
31 * </p>
32 * <p>
33 * This is a package scoped class, and should not be used directly by users.
34 * </p>
35 *
36 * @since 2.0.0
37 */
38 public final class EmailUtils {
39
40 /**
41 * 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.
42 */
43 private static final Random RANDOM = new Random();
44
45 /**
46 * Radix used in encoding.
47 */
48 private static final int RADIX = 16;
49
50 /**
51 * The escape character used for the URL encoding scheme.
52 */
53 private static final char ESCAPE_CHAR = '%';
54
55 /**
56 * BitSet of RFC 2392 safe URL characters.
57 */
58 private static final BitSet SAFE_URL = new BitSet(256);
59
60 // Static initializer for safe_uri
61 static {
62 // alpha characters
63 for (int i = 'a'; i <= 'z'; i++) {
64 SAFE_URL.set(i);
65 }
66 for (int i = 'A'; i <= 'Z'; i++) {
67 SAFE_URL.set(i);
68 }
69 // numeric characters
70 for (int i = '0'; i <= '9'; i++) {
71 SAFE_URL.set(i);
72 }
73
74 // safe chars
75 SAFE_URL.set('-');
76 SAFE_URL.set('_');
77 SAFE_URL.set('.');
78 SAFE_URL.set('*');
79 SAFE_URL.set('+');
80 SAFE_URL.set('$');
81 SAFE_URL.set('!');
82 SAFE_URL.set('\'');
83 SAFE_URL.set('(');
84 SAFE_URL.set(')');
85 SAFE_URL.set(',');
86 SAFE_URL.set('@');
87 }
88
89 /**
90 * Encodes an input string according to RFC 2392. Unsafe characters are escaped.
91 *
92 * @param input the input string to be URL encoded
93 * @return a URL encoded string
94 * @see <a href="https://tools.ietf.org/html/rfc2392">RFC 2392</a>
95 */
96 public static String encodeUrl(final String input) {
97 if (input == null) {
98 return null;
99 }
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 }