View Javadoc
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} &lt; 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 }