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