Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

Vul4j-8_diff

Created Diff never expires
55 removals
217 lines
24 additions
199 lines
/*
/*
* Licensed to the Apache Software Foundation (ASF) under one
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* with the License. You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing,
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* specific language governing permissions and limitations
* under the License.
* under the License.
*/
*/


package org.apache.commons.compress.archivers.zip;
package org.apache.commons.compress.archivers.zip;


import java.io.IOException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.CodingErrorAction;


/**
/**
* A ZipEncoding, which uses a java.nio {@link
* A ZipEncoding, which uses a java.nio {@link
* java.nio.charset.Charset Charset} to encode names.
* java.nio.charset.Charset Charset} to encode names.
* <p>The methods of this class are reentrant.</p>
* <p>The methods of this class are reentrant.</p>
* @Immutable
* @Immutable
*/
*/
class NioZipEncoding implements ZipEncoding, CharsetAccessor {
class NioZipEncoding implements ZipEncoding, CharsetAccessor {


private final Charset charset;
private final Charset charset;
private final boolean useReplacement;
private final boolean useReplacement;
private static final char REPLACEMENT = '?';
private static final char REPLACEMENT = '?';
private static final byte[] REPLACEMENT_BYTES = { (byte) REPLACEMENT };
private static final byte[] REPLACEMENT_BYTES = { (byte) REPLACEMENT };
private static final String REPLACEMENT_STRING = String.valueOf(REPLACEMENT);
private static final String REPLACEMENT_STRING = String.valueOf(REPLACEMENT);
private static final char[] HEX_CHARS = new char[] {
private static final char[] HEX_CHARS = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
};




/**
/**
* Construct an NioZipEncoding using the given charset.
* Construct an NioZipEncoding using the given charset.
* @param charset The character set to use.
* @param charset The character set to use.
* @param useReplacement should invalid characters be replaced, or reported.
* @param useReplacement should invalid characters be replaced, or reported.
*/
*/
NioZipEncoding(final Charset charset, boolean useReplacement) {
NioZipEncoding(final Charset charset, boolean useReplacement) {
this.charset = charset;
this.charset = charset;
this.useReplacement = useReplacement;
this.useReplacement = useReplacement;
}
}


@Override
@Override
public Charset getCharset() {
public Charset getCharset() {
return charset;
return charset;
}
}


/**
/**
* @see ZipEncoding#canEncode(java.lang.String)
* @see ZipEncoding#canEncode(java.lang.String)
*/
*/
@Override
@Override
public boolean canEncode(final String name) {
public boolean canEncode(final String name) {
final CharsetEncoder enc = newEncoder();
final CharsetEncoder enc = newEncoder();


return enc.canEncode(name);
return enc.canEncode(name);
}
}


/**
@Override
* @see ZipEncoding#encode(java.lang.String)
public ByteBuffer encode(final String name) {
*/
final CharsetEncoder enc = newEncoder();
@Override
final CharBuffer cb = CharBuffer.wrap(name);
public ByteBuffer encode(final String name) {
CharBuffer tmp = null;
final CharsetEncoder enc = newEncoder();
ByteBuffer out = ByteBuffer.allocate(estimateInitialBufferSize(enc, cb.remaining()));

CoderResult res = null;
final CharBuffer cb = CharBuffer.wrap(name);
while (cb.remaining() > 0 || (res = enc.encode(cb, out, false)).isOverflow()) {
CharBuffer tmp = null;
if (res != null && (res.isUnmappable() || res.isMalformed())) {
ByteBuffer out = ByteBuffer.allocate(estimateInitialBufferSize(enc, cb.remaining()));
int spaceForSurrogate = estimateIncrementalEncodingSize(enc, 6 * res.length());

if (spaceForSurrogate > out.remaining()) {
while (cb.remaining() > 0) {
int charCount = 0;
final CoderResult res = enc.encode(cb, out, false);
for (int i = cb.position(); i < cb.limit(); i++) {

charCount += !enc.canEncode(cb.get(i)) ? 6 : 1;
if (res.isUnmappable() || res.isMalformed()) {

// write the unmappable characters in utf-16
// pseudo-URL encoding style to ByteBuffer.

int spaceForSurrogate = estimateIncrementalEncodingSize(enc, 6 * res.length());
if (spaceForSurrogate > out.remaining()) {
// if the destination buffer isn't over sized, assume that the presence of one
// unmappable character makes it likely that there will be more. Find all the
// un-encoded characters and allocate space based on those estimates.
int charCount = 0;
for (int i = cb.position() ; i < cb.limit(); i++) {
charCount += !enc.canEncode(cb.get(i)) ? 6 : 1;
}
int totalExtraSpace = estimateIncrementalEncodingSize(enc, charCount);
out = ZipEncodingHelper.growBufferBy(out, totalExtraSpace - out.remaining());
}
if (tmp == null) {
tmp = CharBuffer.allocate(6);
}
for (int i = 0; i < res.length(); ++i) {
out = encodeFully(enc, encodeSurrogate(tmp, cb.get()), out);
}
}

int totalExtraSpace = estimateIncrementalEncodingSize(enc, charCount);
} else if (res.isOverflow()) {
out = ZipEncodingHelper.growBufferBy(out, totalExtraSpace - out.remaining());
int increment = estimateIncrementalEncodingSize(enc, cb.remaining());
}
out = ZipEncodingHelper.growBufferBy(out, increment);
if (tmp == null) {
tmp = CharBuffer.allocate(6);
}
for (int i = 0; i < res.length(); ++i) {
out = encodeFully(enc, encodeSurrogate(tmp, cb.get()), out);
}
}
} else if (res != null && res.isOverflow()) {
int increment = estimateIncrementalEncodingSize(enc, cb.remaining());
out = ZipEncodingHelper.growBufferBy(out, increment);
}
}
// tell the encoder we are done
enc.encode(cb, out, true);
// may have caused underflow, but that's been ignored traditionally

out.limit(out.position());
out.rewind();
return out;
}
}

enc.encode(cb, out, true);
out.limit(out.position());
out.rewind();
return out;
}
/**
/**
* @see
* @see
* ZipEncoding#decode(byte[])
* ZipEncoding#decode(byte[])
*/
*/
@Override
@Override
public String decode(final byte[] data) throws IOException {
public String decode(final byte[] data) throws IOException {
return newDecoder()
return newDecoder()
.decode(ByteBuffer.wrap(data)).toString();
.decode(ByteBuffer.wrap(data)).toString();
}
}


private static ByteBuffer encodeFully(CharsetEncoder enc, CharBuffer cb, ByteBuffer out) {
private static ByteBuffer encodeFully(CharsetEncoder enc, CharBuffer cb, ByteBuffer out) {
ByteBuffer o = out;
ByteBuffer o = out;
while (cb.hasRemaining()) {
while (cb.hasRemaining()) {
CoderResult result = enc.encode(cb, o, false);
CoderResult result = enc.encode(cb, o, false);
if (result.isOverflow()) {
if (result.isOverflow()) {
int increment = estimateIncrementalEncodingSize(enc, cb.remaining());
int increment = estimateIncrementalEncodingSize(enc, cb.remaining());
o = ZipEncodingHelper.growBufferBy(o, increment);
o = ZipEncodingHelper.growBufferBy(o, increment);
}
}
}
}
return o;
return o;
}
}


private static CharBuffer encodeSurrogate(CharBuffer cb, char c) {
private static CharBuffer encodeSurrogate(CharBuffer cb, char c) {
cb.position(0).limit(6);
cb.position(0).limit(6);
cb.put('%');
cb.put('%');
cb.put('U');
cb.put('U');


cb.put(HEX_CHARS[(c >> 12) & 0x0f]);
cb.put(HEX_CHARS[(c >> 12) & 0x0f]);
cb.put(HEX_CHARS[(c >> 8) & 0x0f]);
cb.put(HEX_CHARS[(c >> 8) & 0x0f]);
cb.put(HEX_CHARS[(c >> 4) & 0x0f]);
cb.put(HEX_CHARS[(c >> 4) & 0x0f]);
cb.put(HEX_CHARS[c & 0x0f]);
cb.put(HEX_CHARS[c & 0x0f]);
cb.flip();
cb.flip();
return cb;
return cb;
}
}


private CharsetEncoder newEncoder() {
private CharsetEncoder newEncoder() {
if (useReplacement) {
if (useReplacement) {
return charset.newEncoder()
return charset.newEncoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith(REPLACEMENT_BYTES);
.replaceWith(REPLACEMENT_BYTES);
} else {
} else {
return charset.newEncoder()
return charset.newEncoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
}
}
}


private CharsetDecoder newDecoder() {
private CharsetDecoder newDecoder() {
if (!useReplacement) {
if (!useReplacement) {
return this.charset.newDecoder()
return this.charset.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
.onUnmappableCharacter(CodingErrorAction.REPORT);
} else {
} else {
return charset.newDecoder()
return charset.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith(REPLACEMENT_STRING);
.replaceWith(REPLACEMENT_STRING);
}
}
}
}


/**
/**
* Estimate the initial encoded size (in bytes) for a character buffer.
* Estimate the initial encoded size (in bytes) for a character buffer.
* <p>
* <p>
* The estimate assumes that one character consumes uses the maximum length encoding,
* The estimate assumes that one character consumes uses the maximum length encoding,
* whilst the rest use an average size encoding. This accounts for any BOM for UTF-16, at
* whilst the rest use an average size encoding. This accounts for any BOM for UTF-16, at
* the expense of a couple of extra bytes for UTF-8 encoded ASCII.
* the expense of a couple of extra bytes for UTF-8 encoded ASCII.
* </p>
* </p>
*
*
* @param enc encoder to use for estimates
* @param enc encoder to use for estimates
* @param charChount number of characters in string
* @param charChount number of characters in string
* @return estimated size in bytes.
* @return estimated size in bytes.
*/
*/
private static int estimateInitialBufferSize(CharsetEncoder enc, int charChount) {
private static int estimateInitialBufferSize(CharsetEncoder enc, int charChount) {
float first = enc.maxBytesPerChar();
float first = enc.maxBytesPerChar();
float rest = (charChount - 1) * enc.averageBytesPerChar();
float rest = (charChount - 1) * enc.averageBytesPerChar();
return (int) Math.ceil(first + rest);
return (int) Math.ceil(first + rest);
}
}


/**
/**
* Estimate the size needed for remaining characters
* Estimate the size needed for remaining characters
*
*
* @param enc encoder to use for estimates
* @param enc encoder to use for estimates
* @param charCount number of characters remaining
* @param charCount number of characters remaining
* @return estimated size in bytes.
* @return estimated size in bytes.
*/
*/
private static int estimateIncrementalEncodingSize(CharsetEncoder enc, int charCount) {
private static int estimateIncrementalEncodingSize(CharsetEncoder enc, int charCount) {
return (int) Math.ceil(charCount * enc.averageBytesPerChar());
return (int) Math.ceil(charCount * enc.averageBytesPerChar());
}
}


}
}