2
2
3
3
import com .sanctionco .jmail .normalization .CaseOption ;
4
4
import com .sanctionco .jmail .normalization .NormalizationOptions ;
5
- import com .sanctionco .jmail .normalization .NormalizationOptionsBuilder ;
6
5
6
+ import java .nio .charset .StandardCharsets ;
7
+ import java .security .MessageDigest ;
8
+ import java .security .NoSuchAlgorithmException ;
7
9
import java .text .Normalizer ;
8
10
import java .util .Collections ;
9
11
import java .util .List ;
@@ -235,40 +237,178 @@ public TopLevelDomain topLevelDomain() {
235
237
}
236
238
237
239
/**
238
- * <p>Return a "normalized" version of this email address. The actual result of normalization
239
- * depends on the configured normalization options (see {@link NormalizationOptions}),
240
- * but in general this method returns a version of the email address that is the same as
241
- * the original email address, except that all comments and optional parts
242
- * (identifiers, source routing) are removed. For example, the address
243
- * {@code "test@(comment)example.com"} will return {@code "[email protected] "}.</p>
240
+ * <p>Return a "normalized" version of this email address. This method returns a version of the
241
+ * email address that is the same as the original email address, except:</p>
242
+ *
243
+ * <ul>
244
+ * <li>All comments are removed</li>
245
+ * <li>All identifiers or source routing are removed</li>
246
+ * <li>Any unnecessary quotes within the local-part are removed</li>
247
+ * <li>The entire address is lowercased</li>
248
+ * </ul>
244
249
*
245
- * <p>This method uses the default set of {@link NormalizationOptions}. This default set
246
- * of options can be adjusted using system properties. See {@link NormalizationOptions}
247
- * for more details on which properties to set to adjust the defaults.</p>
250
+ * <p>For example, the address {@code "tEST@(comment)example.com"} will return
251
+ * {@code "[email protected] "}.</p>
248
252
*
249
- * <p>Alternatively, one can use the {@link Email#normalized(NormalizationOptions)} method
250
- * and pass in a custom set of options to adjust the behavior.</p>
253
+ * <p>This method uses the default set of {@link NormalizationOptions} when performing
254
+ * normalization. Use {@link #normalized(NormalizationOptions)} instead of this method
255
+ * to further customize how normalization behaves.</p>
251
256
*
252
257
* @return the normalized version of this email address
253
258
*/
254
259
public String normalized () {
255
- return normalized (NormalizationOptions .builder (). build () );
260
+ return normalized (NormalizationOptions .DEFAULT_OPTIONS );
256
261
}
257
262
258
263
/**
259
264
* <p>Return a "normalized" version of this email address. The actual result of normalization
260
265
* depends on the configured normalization options, but in general this method returns
261
- * a version of the email address that is the same as the original email address, except
262
- * that all comments and optional parts (identifiers, source routing) are removed.
263
- * For example, the address {@code "test@(comment)example.com"} will return
266
+ * a version of the email address that is the same as the original email address, except:</p>
267
+ *
268
+ * <ul>
269
+ * <li>All comments are removed</li>
270
+ * <li>All identifiers or source routing are removed</li>
271
+ * <li>Any unnecessary quotes within the local-part are removed</li>
272
+ * <li>The entire address is lowercased</li>
273
+ * </ul>
274
+ *
275
+ * <p>For example, the address {@code "tEST@(comment)example.com"} will return
264
276
* {@code "[email protected] "}.</p>
265
277
*
266
278
* <p>See {@link NormalizationOptions} for more details on all of the configurable options.</p>
267
279
*
268
280
* @param options the {@link NormalizationOptions} to use when normalizing
269
281
* @return the normalized version of this email address
282
+ * @see NormalizationOptions
270
283
*/
271
284
public String normalized (NormalizationOptions options ) {
285
+ return normalizedLocalPart (options ) + "@" + normalizedDomain (options );
286
+ }
287
+
288
+ /**
289
+ * <p>Returns an MD5 reference to the email address. This format can be useful to share references
290
+ * to the email address without sharing the actual address.</p>
291
+ *
292
+ * <p>The reference is calculated by first performing normalization on the address, and then
293
+ * taking the MD5 hash of the normalized address. See {@link #normalized()} for more details
294
+ * on how normalization works.</p>
295
+ *
296
+ * <p>This method uses the default {@link NormalizationOptions}. If you wish to customize how the
297
+ * normalization happens, use {@link #reference(NormalizationOptions)} instead.</p>
298
+ *
299
+ * @return the MD5 reference string for the address
300
+ * @throws NoSuchAlgorithmException if the MD5 algorithm is unable to be loaded
301
+ */
302
+ public String reference () throws NoSuchAlgorithmException {
303
+ return reference (NormalizationOptions .DEFAULT_OPTIONS );
304
+ }
305
+
306
+ /**
307
+ * <p>Returns an MD5 reference to the email address. This format can be useful to share references
308
+ * to the email address without sharing the actual address.</p>
309
+ *
310
+ * <p>The reference is calculated by first performing normalization on the address, and then
311
+ * taking the MD5 hash of the normalized address. See {@link #normalized(NormalizationOptions)}
312
+ * for more details on how normalization works.</p>
313
+ *
314
+ * @param options the {@link NormalizationOptions} to use when normalizing
315
+ * @return the MD5 reference string for the address
316
+ * @throws NoSuchAlgorithmException if the MD5 algorithm is unable to be loaded
317
+ */
318
+ public String reference (NormalizationOptions options ) throws NoSuchAlgorithmException {
319
+ MessageDigest md = MessageDigest .getInstance ("MD5" );
320
+
321
+ byte [] normalized = normalized (options ).getBytes (StandardCharsets .UTF_8 );
322
+ byte [] digest = md .digest (normalized );
323
+
324
+ return toHexString (digest );
325
+ }
326
+
327
+ /**
328
+ * <p>Returns a redacted version of the email address in the format {@code "{local-part}@domain"}.
329
+ * This format can be useful when storing addresses in a data store (to avoid storing the original
330
+ * address).</p>
331
+ *
332
+ * <p>The redacted address is calculated by first performing normalization on the address, and
333
+ * then taking the SHA-1 hash of the local-part of the normalized address to construct the final
334
+ * redacted version of the address. See {@link #normalized()} for more details on how
335
+ * normalization works.</p>
336
+ *
337
+ * <p>This method uses the default {@link NormalizationOptions}. If you wish to customize how the
338
+ * normalization happens, use {@link #redacted(NormalizationOptions)} instead.</p>
339
+ *
340
+ * @return the redacted version of the email address
341
+ * @throws NoSuchAlgorithmException if the SHA-A algorithm is unable to be loaded
342
+ */
343
+ public String redacted () throws NoSuchAlgorithmException {
344
+ return redacted (NormalizationOptions .DEFAULT_OPTIONS );
345
+ }
346
+
347
+ /**
348
+ * <p>Returns a redacted version of the email address in the format {@code "{local-part}@domain"}.
349
+ * This format can be useful when storing addresses in a data store (to avoid storing the original
350
+ * address).</p>
351
+ *
352
+ * <p>The redacted address is calculated by first performing normalization on the address, and
353
+ * then taking the SHA-1 hash of the local-part of the normalized address to construct the final
354
+ * redacted version of the address. See {@link #normalized(NormalizationOptions)} for more
355
+ * details on how normalization works.</p>
356
+ *
357
+ * @param options the {@link NormalizationOptions} to use when normalizing
358
+ * @return the redacted version of the email address
359
+ * @throws NoSuchAlgorithmException if the SHA-1 algorithm is unable to be loaded
360
+ */
361
+ public String redacted (NormalizationOptions options ) throws NoSuchAlgorithmException {
362
+ MessageDigest md = MessageDigest .getInstance ("SHA1" );
363
+
364
+ byte [] normalizedLocalPart = normalizedLocalPart (options ).getBytes (StandardCharsets .UTF_8 );
365
+ byte [] digest = md .digest (normalizedLocalPart );
366
+
367
+ return "{" + toHexString (digest ) + "}@" + normalizedDomain (options );
368
+ }
369
+
370
+ /**
371
+ * <p>Returns a munged version of the email address in the format {@code "lo*****@do*****"}.
372
+ * This format can be useful when displaying addresses on a user account page.
373
+ *
374
+ * <p>The munged address is calculated by first performing normalization on the address, and
375
+ * then taking the first two characters of both the local-part and the domain, and adding
376
+ * five {@code *} characters to each. See {@link #normalized()} for more
377
+ * details on how normalization works.</p>
378
+ *
379
+ * <p>This method uses the default {@link NormalizationOptions}. If you wish to customize how the
380
+ * normalization happens, use {@link #munged(NormalizationOptions)} instead.</p>
381
+ *
382
+ * @return the munged version of the email address
383
+ */
384
+ public String munged () {
385
+ return munged (NormalizationOptions .DEFAULT_OPTIONS );
386
+ }
387
+
388
+
389
+ /**
390
+ * <p>Returns a munged version of the email address in the format {@code "lo*****@do*****"}.
391
+ * This format can be useful when displaying addresses on a user account page.
392
+ *
393
+ * <p>The munged address is calculated by first performing normalization on the address, and
394
+ * then taking the first two characters of both the local-part and the domain, and adding
395
+ * five {@code *} characters to each. See {@link #normalized(NormalizationOptions)} for more
396
+ * details on how normalization works.</p>
397
+ *
398
+ * @param options the {@link NormalizationOptions} to use when normalizing
399
+ * @return the munged version of the email address
400
+ */
401
+ public String munged (NormalizationOptions options ) {
402
+ String localPart = normalizedLocalPart (options );
403
+ localPart = localPart .length () < 2 ? localPart : localPart .substring (0 , 2 );
404
+
405
+ String domain = normalizedDomain (options );
406
+ domain = domain .length () < 2 ? domain : domain .substring (0 , 2 );
407
+
408
+ return localPart + "*****@" + domain + "*****" ;
409
+ }
410
+
411
+ private String normalizedLocalPart (NormalizationOptions options ) {
272
412
String localPart = options .shouldStripQuotes ()
273
413
? localPartWithoutQuotes
274
414
: localPartWithoutComments ;
@@ -287,15 +427,34 @@ public String normalized(NormalizationOptions options) {
287
427
? caseOption .adjustLocalPart (localPart .replace ("." , "" ))
288
428
: caseOption .adjustLocalPart (localPart );
289
429
290
- String domain = isIpAddress
430
+ if (options .shouldPerformUnicodeNormalization ()) {
431
+ localPart = Normalizer .normalize (localPart , options .getUnicodeNormalizationForm ());
432
+ }
433
+
434
+ return localPart ;
435
+ }
436
+
437
+ private String normalizedDomain (NormalizationOptions options ) {
438
+ CaseOption caseOption = options .getCaseOption ();
439
+
440
+ return isIpAddress
291
441
? "[" + this .domainWithoutComments + "]"
292
442
: caseOption .adjustDomain (this .domainWithoutComments );
443
+ }
293
444
294
- if (options .shouldPerformUnicodeNormalization ()) {
295
- localPart = Normalizer .normalize (localPart , options .getUnicodeNormalizationForm ());
445
+ private String toHexString (byte [] bytes ) {
446
+ char [] hexArray = {
447
+ '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ,
448
+ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' };
449
+ char [] hexChars = new char [bytes .length * 2 ];
450
+
451
+ for (int j = 0 ; j < bytes .length ; j ++) {
452
+ int v = bytes [j ] & 0xFF ;
453
+ hexChars [j * 2 ] = hexArray [v / 16 ];
454
+ hexChars [j * 2 + 1 ] = hexArray [v % 16 ];
296
455
}
297
456
298
- return localPart + "@" + domain ;
457
+ return new String ( hexChars ) ;
299
458
}
300
459
301
460
/**
0 commit comments