|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2023 the original author or authors. |
|
|
|
* Copyright 2002-2024 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -20,7 +20,6 @@ import java.io.ByteArrayOutputStream; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.time.ZonedDateTime; |
|
|
|
import java.time.ZonedDateTime; |
|
|
|
import java.time.format.DateTimeParseException; |
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Base64; |
|
|
|
import java.util.Base64; |
|
|
|
import java.util.BitSet; |
|
|
|
import java.util.BitSet; |
|
|
|
@ -36,7 +35,6 @@ import org.springframework.util.StreamUtils; |
|
|
|
import static java.nio.charset.StandardCharsets.ISO_8859_1; |
|
|
|
import static java.nio.charset.StandardCharsets.ISO_8859_1; |
|
|
|
import static java.nio.charset.StandardCharsets.US_ASCII; |
|
|
|
import static java.nio.charset.StandardCharsets.US_ASCII; |
|
|
|
import static java.nio.charset.StandardCharsets.UTF_8; |
|
|
|
import static java.nio.charset.StandardCharsets.UTF_8; |
|
|
|
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Representation of the Content-Disposition type and parameters as defined in RFC 6266. |
|
|
|
* Representation of the Content-Disposition type and parameters as defined in RFC 6266. |
|
|
|
@ -85,34 +83,17 @@ public final class ContentDisposition { |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private final Charset charset; |
|
|
|
private final Charset charset; |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private final Long size; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private final ZonedDateTime creationDate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private final ZonedDateTime modificationDate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private final ZonedDateTime readDate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Private constructor. See static factory methods in this class. |
|
|
|
* Private constructor. See static factory methods in this class. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private ContentDisposition(@Nullable String type, @Nullable String name, @Nullable String filename, |
|
|
|
private ContentDisposition(@Nullable String type, @Nullable String name, @Nullable String filename, |
|
|
|
@Nullable Charset charset, @Nullable Long size, @Nullable ZonedDateTime creationDate, |
|
|
|
@Nullable Charset charset) { |
|
|
|
@Nullable ZonedDateTime modificationDate, @Nullable ZonedDateTime readDate) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.type = type; |
|
|
|
this.type = type; |
|
|
|
this.name = name; |
|
|
|
this.name = name; |
|
|
|
this.filename = filename; |
|
|
|
this.filename = filename; |
|
|
|
this.charset = charset; |
|
|
|
this.charset = charset; |
|
|
|
this.size = size; |
|
|
|
|
|
|
|
this.creationDate = creationDate; |
|
|
|
|
|
|
|
this.modificationDate = modificationDate; |
|
|
|
|
|
|
|
this.readDate = readDate; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -177,53 +158,6 @@ public final class ContentDisposition { |
|
|
|
return this.charset; |
|
|
|
return this.charset; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Return the value of the {@literal size} parameter, or {@code null} if not defined. |
|
|
|
|
|
|
|
* @deprecated since 5.2.3 as per |
|
|
|
|
|
|
|
* <a href="https://tools.ietf.org/html/rfc6266#appendix-B">RFC 6266, Appendix B</a>, |
|
|
|
|
|
|
|
* to be removed in a future release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
public Long getSize() { |
|
|
|
|
|
|
|
return this.size; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Return the value of the {@literal creation-date} parameter, or {@code null} if not defined. |
|
|
|
|
|
|
|
* @deprecated since 5.2.3 as per |
|
|
|
|
|
|
|
* <a href="https://tools.ietf.org/html/rfc6266#appendix-B">RFC 6266, Appendix B</a>, |
|
|
|
|
|
|
|
* to be removed in a future release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
public ZonedDateTime getCreationDate() { |
|
|
|
|
|
|
|
return this.creationDate; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Return the value of the {@literal modification-date} parameter, or {@code null} if not defined. |
|
|
|
|
|
|
|
* @deprecated since 5.2.3 as per |
|
|
|
|
|
|
|
* <a href="https://tools.ietf.org/html/rfc6266#appendix-B">RFC 6266, Appendix B</a>, |
|
|
|
|
|
|
|
* to be removed in a future release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
public ZonedDateTime getModificationDate() { |
|
|
|
|
|
|
|
return this.modificationDate; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Return the value of the {@literal read-date} parameter, or {@code null} if not defined. |
|
|
|
|
|
|
|
* @deprecated since 5.2.3 as per |
|
|
|
|
|
|
|
* <a href="https://tools.ietf.org/html/rfc6266#appendix-B">RFC 6266, Appendix B</a>, |
|
|
|
|
|
|
|
* to be removed in a future release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
public ZonedDateTime getReadDate() { |
|
|
|
|
|
|
|
return this.readDate; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public boolean equals(@Nullable Object other) { |
|
|
|
public boolean equals(@Nullable Object other) { |
|
|
|
@ -231,17 +165,12 @@ public final class ContentDisposition { |
|
|
|
ObjectUtils.nullSafeEquals(this.type, that.type) && |
|
|
|
ObjectUtils.nullSafeEquals(this.type, that.type) && |
|
|
|
ObjectUtils.nullSafeEquals(this.name, that.name) && |
|
|
|
ObjectUtils.nullSafeEquals(this.name, that.name) && |
|
|
|
ObjectUtils.nullSafeEquals(this.filename, that.filename) && |
|
|
|
ObjectUtils.nullSafeEquals(this.filename, that.filename) && |
|
|
|
ObjectUtils.nullSafeEquals(this.charset, that.charset) && |
|
|
|
ObjectUtils.nullSafeEquals(this.charset, that.charset))); |
|
|
|
ObjectUtils.nullSafeEquals(this.size, that.size) && |
|
|
|
|
|
|
|
ObjectUtils.nullSafeEquals(this.creationDate, that.creationDate)&& |
|
|
|
|
|
|
|
ObjectUtils.nullSafeEquals(this.modificationDate, that.modificationDate)&& |
|
|
|
|
|
|
|
ObjectUtils.nullSafeEquals(this.readDate, that.readDate))); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public int hashCode() { |
|
|
|
public int hashCode() { |
|
|
|
return ObjectUtils.nullSafeHash(this.type, this.name,this.filename, |
|
|
|
return ObjectUtils.nullSafeHash(this.type, this.name,this.filename, this.charset); |
|
|
|
this.charset, this.size, this.creationDate, this.modificationDate, this.readDate); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -270,25 +199,6 @@ public final class ContentDisposition { |
|
|
|
sb.append(encodeRfc5987Filename(this.filename, this.charset)); |
|
|
|
sb.append(encodeRfc5987Filename(this.filename, this.charset)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (this.size != null) { |
|
|
|
|
|
|
|
sb.append("; size="); |
|
|
|
|
|
|
|
sb.append(this.size); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (this.creationDate != null) { |
|
|
|
|
|
|
|
sb.append("; creation-date=\""); |
|
|
|
|
|
|
|
sb.append(RFC_1123_DATE_TIME.format(this.creationDate)); |
|
|
|
|
|
|
|
sb.append('\"'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (this.modificationDate != null) { |
|
|
|
|
|
|
|
sb.append("; modification-date=\""); |
|
|
|
|
|
|
|
sb.append(RFC_1123_DATE_TIME.format(this.modificationDate)); |
|
|
|
|
|
|
|
sb.append('\"'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (this.readDate != null) { |
|
|
|
|
|
|
|
sb.append("; read-date=\""); |
|
|
|
|
|
|
|
sb.append(RFC_1123_DATE_TIME.format(this.readDate)); |
|
|
|
|
|
|
|
sb.append('\"'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return sb.toString(); |
|
|
|
return sb.toString(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -331,7 +241,7 @@ public final class ContentDisposition { |
|
|
|
* Return an empty content disposition. |
|
|
|
* Return an empty content disposition. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static ContentDisposition empty() { |
|
|
|
public static ContentDisposition empty() { |
|
|
|
return new ContentDisposition("", null, null, null, null, null, null, null); |
|
|
|
return new ContentDisposition("", null, null, null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -376,7 +286,7 @@ public final class ContentDisposition { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (attribute.equals("filename") && (filename == null)) { |
|
|
|
else if (attribute.equals("filename") && (filename == null)) { |
|
|
|
if (value.startsWith("=?") ) { |
|
|
|
if (value.startsWith("=?")) { |
|
|
|
Matcher matcher = BASE64_ENCODED_PATTERN.matcher(value); |
|
|
|
Matcher matcher = BASE64_ENCODED_PATTERN.matcher(value); |
|
|
|
if (matcher.find()) { |
|
|
|
if (matcher.find()) { |
|
|
|
Base64.Decoder decoder = Base64.getDecoder(); |
|
|
|
Base64.Decoder decoder = Base64.getDecoder(); |
|
|
|
@ -415,39 +325,12 @@ public final class ContentDisposition { |
|
|
|
filename = value; |
|
|
|
filename = value; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (attribute.equals("size") ) { |
|
|
|
|
|
|
|
size = Long.parseLong(value); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (attribute.equals("creation-date")) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
creationDate = ZonedDateTime.parse(value, RFC_1123_DATE_TIME); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (DateTimeParseException ex) { |
|
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (attribute.equals("modification-date")) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
modificationDate = ZonedDateTime.parse(value, RFC_1123_DATE_TIME); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (DateTimeParseException ex) { |
|
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (attribute.equals("read-date")) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
readDate = ZonedDateTime.parse(value, RFC_1123_DATE_TIME); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (DateTimeParseException ex) { |
|
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
throw new IllegalArgumentException("Invalid content disposition format"); |
|
|
|
throw new IllegalArgumentException("Invalid content disposition format"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return new ContentDisposition(type, name, filename, charset, size, creationDate, modificationDate, readDate); |
|
|
|
return new ContentDisposition(type, name, filename, charset); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static List<String> tokenize(String headerValue) { |
|
|
|
private static List<String> tokenize(String headerValue) { |
|
|
|
@ -714,42 +597,6 @@ public final class ContentDisposition { |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
Builder filename(@Nullable String filename, @Nullable Charset charset); |
|
|
|
Builder filename(@Nullable String filename, @Nullable Charset charset); |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Set the value of the {@literal size} parameter. |
|
|
|
|
|
|
|
* @deprecated since 5.2.3 as per |
|
|
|
|
|
|
|
* <a href="https://tools.ietf.org/html/rfc6266#appendix-B">RFC 6266, Appendix B</a>, |
|
|
|
|
|
|
|
* to be removed in a future release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
|
|
|
|
Builder size(@Nullable Long size); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Set the value of the {@literal creation-date} parameter. |
|
|
|
|
|
|
|
* @deprecated since 5.2.3 as per |
|
|
|
|
|
|
|
* <a href="https://tools.ietf.org/html/rfc6266#appendix-B">RFC 6266, Appendix B</a>, |
|
|
|
|
|
|
|
* to be removed in a future release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
|
|
|
|
Builder creationDate(@Nullable ZonedDateTime creationDate); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Set the value of the {@literal modification-date} parameter. |
|
|
|
|
|
|
|
* @deprecated since 5.2.3 as per |
|
|
|
|
|
|
|
* <a href="https://tools.ietf.org/html/rfc6266#appendix-B">RFC 6266, Appendix B</a>, |
|
|
|
|
|
|
|
* to be removed in a future release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
|
|
|
|
Builder modificationDate(@Nullable ZonedDateTime modificationDate); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Set the value of the {@literal read-date} parameter. |
|
|
|
|
|
|
|
* @deprecated since 5.2.3 as per |
|
|
|
|
|
|
|
* <a href="https://tools.ietf.org/html/rfc6266#appendix-B">RFC 6266, Appendix B</a>, |
|
|
|
|
|
|
|
* to be removed in a future release. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@Deprecated |
|
|
|
|
|
|
|
Builder readDate(@Nullable ZonedDateTime readDate); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Build the content disposition. |
|
|
|
* Build the content disposition. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -770,17 +617,6 @@ public final class ContentDisposition { |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private Charset charset; |
|
|
|
private Charset charset; |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private Long size; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private ZonedDateTime creationDate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private ZonedDateTime modificationDate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
|
|
|
|
private ZonedDateTime readDate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public BuilderImpl(String type) { |
|
|
|
public BuilderImpl(String type) { |
|
|
|
Assert.hasText(type, "'type' must not be not empty"); |
|
|
|
Assert.hasText(type, "'type' must not be not empty"); |
|
|
|
@ -806,38 +642,9 @@ public final class ContentDisposition { |
|
|
|
return this; |
|
|
|
return this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
@SuppressWarnings("deprecation") |
|
|
|
|
|
|
|
public Builder size(@Nullable Long size) { |
|
|
|
|
|
|
|
this.size = size; |
|
|
|
|
|
|
|
return this; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
@SuppressWarnings("deprecation") |
|
|
|
|
|
|
|
public Builder creationDate(@Nullable ZonedDateTime creationDate) { |
|
|
|
|
|
|
|
this.creationDate = creationDate; |
|
|
|
|
|
|
|
return this; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
@SuppressWarnings("deprecation") |
|
|
|
|
|
|
|
public Builder modificationDate(@Nullable ZonedDateTime modificationDate) { |
|
|
|
|
|
|
|
this.modificationDate = modificationDate; |
|
|
|
|
|
|
|
return this; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
@SuppressWarnings("deprecation") |
|
|
|
|
|
|
|
public Builder readDate(@Nullable ZonedDateTime readDate) { |
|
|
|
|
|
|
|
this.readDate = readDate; |
|
|
|
|
|
|
|
return this; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public ContentDisposition build() { |
|
|
|
public ContentDisposition build() { |
|
|
|
return new ContentDisposition(this.type, this.name, this.filename, this.charset, |
|
|
|
return new ContentDisposition(this.type, this.name, this.filename, this.charset); |
|
|
|
this.size, this.creationDate, this.modificationDate, this.readDate); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|