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> < 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 }