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.mail;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.UnsupportedEncodingException;
23  import java.util.BitSet;
24  import java.util.Random;
25  
26  import javax.mail.MessagingException;
27  import javax.mail.internet.MimeMessage;
28  
29  import org.apache.commons.mail.util.MimeMessageUtils;
30  
31  /**
32   * Utility methods used by commons-email.
33   *
34   * <p>
35   * These methods are copied from other commons components (commons-lang) to avoid creating
36   * a dependency for such a small component.
37   * </p>
38   *
39   * <p>
40   * This is a package scoped class, and should not be used directly by users.
41   * </p>
42   *
43   * @since 1.0
44   */
45  final class EmailUtils
46  {
47      /**
48       * Random object used by random method. This has to be not local to the random method
49       * so as to not return the same value in the same millisecond.
50       */
51      private static final Random RANDOM = new Random();
52  
53      /**
54       * The default charset used for URL encoding.
55       */
56      private static final String US_ASCII = "US-ASCII";
57  
58      /**
59       * Radix used in encoding.
60       */
61      private static final int RADIX = 16;
62  
63      /**
64       * The escape character used for the URL encoding scheme.
65       */
66      private static final char ESCAPE_CHAR = '%';
67  
68      /**
69       * BitSet of RFC 2392 safe URL characters.
70       */
71      private static final BitSet SAFE_URL = new BitSet(256);
72  
73      // Static initializer for safe_uri
74      static {
75          // alpha characters
76          for (int i = 'a'; i <= 'z'; i++)
77          {
78              SAFE_URL.set(i);
79          }
80          for (int i = 'A'; i <= 'Z'; i++)
81          {
82              SAFE_URL.set(i);
83          }
84          // numeric characters
85          for (int i = '0'; i <= '9'; i++)
86          {
87              SAFE_URL.set(i);
88          }
89  
90          // safe chars
91          SAFE_URL.set('-');
92          SAFE_URL.set('_');
93          SAFE_URL.set('.');
94          SAFE_URL.set('*');
95          SAFE_URL.set('+');
96          SAFE_URL.set('$');
97          SAFE_URL.set('!');
98          SAFE_URL.set('\'');
99          SAFE_URL.set('(');
100         SAFE_URL.set(')');
101         SAFE_URL.set(',');
102         SAFE_URL.set('@');
103     }
104 
105     /**
106      * Constructs a new <code>EmailException</code> with no detail message.
107      */
108     private EmailUtils()
109     {
110         super();
111     }
112 
113     /**
114      * Checks if a String is empty ("") or null.
115      *
116      * @param str the String to check, may be null
117      *
118      * @return <code>true</code> if the String is empty or null
119      *
120      * @since Commons Lang v2.1, svn 240418
121      */
122     static boolean isEmpty(final String str)
123     {
124         return (str == null) || (str.length() == 0);
125     }
126 
127     /**
128      * Checks if a String is not empty ("") and not null.
129      *
130      * @param str the String to check, may be null
131      *
132      * @return <code>true</code> if the String is not empty and not null
133      *
134      * @since Commons Lang v2.1, svn 240418
135      */
136     static boolean isNotEmpty(final String str)
137     {
138         return (str != null) && (str.length() > 0);
139     }
140 
141     /**
142      * Validate an argument, throwing <code>IllegalArgumentException</code>
143      * if the argument is <code>null</code>.
144      *
145      * @param object the object to check is not <code>null</code>
146      * @param message the exception message you would like to see if the object is <code>null</code>
147      *
148      * @throws IllegalArgumentException if the object is <code>null</code>
149      *
150      * @since Commons Lang v2.1, svn 201930
151      */
152     static void notNull(final Object object, final String message)
153     {
154         if (object == null)
155         {
156             throw new IllegalArgumentException(message);
157         }
158     }
159 
160     /**
161      * Creates a random string whose length is the number of characters specified.
162      *
163      * <p>
164      * Characters will be chosen from the set of alphabetic characters.
165      * </p>
166      *
167      * @param count the length of random string to create
168      *
169      * @return the random string
170      *
171      * @since Commons Lang v2.1, svn 201930
172      */
173     static String randomAlphabetic(final int count)
174     {
175         return random(count, 0, 0, true, false, null, RANDOM);
176     }
177 
178     /**
179      * Creates a random string based on a variety of options, using supplied source of randomness.
180      *
181      * <p>
182      * If start and end are both <code>0</code>, start and end are set to <code>' '</code> and <code>'z'</code>,
183      * the ASCII printable characters, will be used, unless letters and numbers are both <code>false</code>,
184      * in which case, start and end are set to <code>0</code> and <code>Integer.MAX_VALUE</code>.
185      * </p>
186      *
187      * <p>
188      * If set is not <code>null</code>, characters between start and end are chosen.
189      * </p>
190      *
191      * <p>
192      * This method accepts a user-supplied {@link Random} instance to use as a source of randomness. By seeding a
193      * single {@link Random} instance with a fixed seed and using it for each call, the same random sequence of strings
194      * can be generated repeatedly and predictably.
195      * </p>
196      *
197      * @param count the length of random string to create
198      * @param start the position in set of chars to start at
199      * @param end the position in set of chars to end before
200      * @param letters only allow letters?
201      * @param numbers only allow numbers?
202      * @param chars the set of chars to choose randoms from. If <code>null</code>,
203      *              then it will use the set of all chars.
204      * @param random a source of randomness.
205      *
206      * @return the random string
207      *
208      * @throws IllegalArgumentException if <code>count</code> &lt; 0.
209      *
210      * @since Commons Lang v2.1, svn 201930
211      */
212     private static String random(
213         int count,
214         int start,
215         int end,
216         final boolean letters,
217         final boolean numbers,
218         final char [] chars,
219         final Random random)
220     {
221         if (count == 0)
222         {
223             return "";
224         }
225         else if (count < 0)
226         {
227             throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
228         }
229 
230         if ((start == 0) && (end == 0))
231         {
232             end = 'z' + 1;
233             start = ' ';
234 
235             if (!letters && !numbers)
236             {
237                 start = 0;
238                 end = Integer.MAX_VALUE;
239             }
240         }
241 
242         final StringBuffer buffer = new StringBuffer();
243         final int gap = end - start;
244 
245         while (count-- != 0)
246         {
247             char ch;
248 
249             if (chars == null)
250             {
251                 ch = (char) (random.nextInt(gap) + start);
252             }
253             else
254             {
255                 ch = chars[random.nextInt(gap) + start];
256             }
257 
258             if ((letters && numbers && Character.isLetterOrDigit(ch)) || (letters && Character.isLetter(ch))
259                             || (numbers && Character.isDigit(ch)) || (!letters && !numbers))
260             {
261                 buffer.append(ch);
262             }
263             else
264             {
265                 count++;
266             }
267         }
268 
269         return buffer.toString();
270     }
271 
272     /**
273      * Replaces end-of-line characters with spaces.
274      *
275      * @param input the input string to be scanned.
276      * @return a clean string
277      */
278     static String replaceEndOfLineCharactersWithSpaces(final String input)
279     {
280         return input == null ? null : input.replace('\n', ' ').replace('\r', ' ');
281     }
282 
283     /**
284      * Encodes an input string according to RFC 2392. Unsafe characters are escaped.
285      *
286      * @param input the input string to be URL encoded
287      * @return a URL encoded string
288      * @throws UnsupportedEncodingException if "US-ASCII" charset is not available
289      * @see <a href="http://tools.ietf.org/html/rfc2392">RFC 2392</a>
290      */
291     static String encodeUrl(final String input) throws UnsupportedEncodingException
292     {
293         if (input == null)
294         {
295             return null;
296         }
297 
298         final StringBuilder builder = new StringBuilder();
299         for (final byte c : input.getBytes(US_ASCII))
300         {
301             int b = c;
302             if (b < 0)
303             {
304                 b = 256 + b;
305             }
306             if (SAFE_URL.get(b))
307             {
308                 builder.append((char) b);
309             }
310             else
311             {
312                 builder.append(ESCAPE_CHAR);
313                 final char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX));
314                 final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX));
315                 builder.append(hex1);
316                 builder.append(hex2);
317             }
318         }
319         return builder.toString();
320     }
321 
322     /**
323      * Convenience method to write a MimeMessage into a file.
324      *
325      * @param resultFile the file containing the MimeMessgae
326      * @param mimeMessage the MimeMessage to write
327      * @throws IOException writing the MimeMessage failed
328      * @throws MessagingException writing the MimeMessage failed
329      */
330     static void writeMimeMessage(final File resultFile, final MimeMessage mimeMessage)
331             throws IOException, MessagingException
332     {
333         MimeMessageUtils.writeMimeMessage(mimeMessage, resultFile);
334     }
335 }