mirror of
https://github.com/apache/poi.git
synced 2026-02-27 20:40:08 +08:00
[bug-66301] Add a method to properly write the header necessary for a MSG attachment. Thanks to Lyn Evans.
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1904685 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
4c3a0b4e93
commit
933948a846
@ -218,7 +218,8 @@ public abstract class PropertiesChunk extends Chunk {
|
||||
prop = MAPIProperty.createCustom(id, type, "Unknown " + id);
|
||||
}
|
||||
if (type == null) {
|
||||
LOG.atWarn().log("Invalid type found, expected {} but got {} for property {}", prop.usualType, box(typeID),prop);
|
||||
LOG.atWarn().log("Invalid type found, expected {} but got {} for property {}",
|
||||
prop.usualType, box(typeID), prop);
|
||||
going = false;
|
||||
break;
|
||||
}
|
||||
@ -391,6 +392,47 @@ public abstract class PropertiesChunk extends Chunk {
|
||||
return variableLengthProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the manually pre-calculated(have header and data written manually) properties.
|
||||
*
|
||||
* @param out
|
||||
* The {@code OutputStream}.
|
||||
* @return The variable-length properties that need to be written in another
|
||||
* node.
|
||||
* @throws IOException
|
||||
* If an I/O error occurs.
|
||||
*/
|
||||
protected List<PropertyValue> writePreCalculatedProperties(OutputStream out) throws IOException {
|
||||
List<PropertyValue> variableLengthProperties = new ArrayList<>();
|
||||
for (Entry<MAPIProperty, PropertyValue> entry : properties.entrySet()) {
|
||||
MAPIProperty property = entry.getKey();
|
||||
PropertyValue value = entry.getValue();
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
if (property.id < 0) {
|
||||
continue;
|
||||
}
|
||||
// generic header
|
||||
// page 23, point 2.4.2
|
||||
// tag is the property id and its type
|
||||
long tag = Long.parseLong(getActualTypeTag(property, value.getActualType()), 16);
|
||||
LittleEndian.putUInt(tag, out);
|
||||
LittleEndian.putUInt(value.getFlags(), out); // readable + writable
|
||||
|
||||
MAPIType type = value.getActualType();
|
||||
if (type.isFixedLength()) {
|
||||
// page 11, point 2.1.2
|
||||
writeFixedLengthValueHeader(out, property, type, value);
|
||||
} else {
|
||||
// page 12, point 2.1.3
|
||||
writeVariableLengthPreCalculatedValue(out, value);
|
||||
variableLengthProperties.add(value);
|
||||
}
|
||||
}
|
||||
return variableLengthProperties;
|
||||
}
|
||||
|
||||
private void writeFixedLengthValueHeader(OutputStream out, MAPIProperty property, MAPIType type, PropertyValue value) throws IOException {
|
||||
// fixed type header
|
||||
// page 24, point 2.4.2.1.1
|
||||
@ -402,6 +444,19 @@ public abstract class PropertiesChunk extends Chunk {
|
||||
out.write(new byte[8 - length]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out pre-calculated raw values which assume any variable length property `data`
|
||||
* field to already have size, reserved and manually written header
|
||||
* @param out
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeVariableLengthPreCalculatedValue(OutputStream out, PropertyValue value) throws IOException {
|
||||
// variable length header
|
||||
// page 24, point 2.4.2.2
|
||||
byte[] bytes = value.getRawValue();
|
||||
out.write(bytes);
|
||||
}
|
||||
|
||||
private void writeVariableLengthValueHeader(OutputStream out, MAPIProperty propertyEx, MAPIType type,
|
||||
PropertyValue value) throws IOException {
|
||||
// variable length header
|
||||
@ -419,6 +474,15 @@ public abstract class PropertiesChunk extends Chunk {
|
||||
LittleEndian.putUInt(0, out);
|
||||
}
|
||||
|
||||
private String getActualTypeTag(MAPIProperty property, MAPIType actualType) {
|
||||
StringBuilder buffer = new StringBuilder(Integer.toHexString(property.id).toUpperCase(Locale.ROOT));
|
||||
while (buffer.length() < 4) {
|
||||
buffer.insert(0, "0");
|
||||
}
|
||||
buffer.append(actualType.asFileEnding());
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private String getFileName(MAPIProperty property, MAPIType actualType) {
|
||||
StringBuilder str = new StringBuilder(Integer.toHexString(property.id).toUpperCase(Locale.ROOT));
|
||||
int need0count = 4 - str.length();
|
||||
|
||||
@ -25,7 +25,7 @@ import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* A {@link PropertiesChunk} for a Storage Properties, such as Attachments and
|
||||
* Recipients. This only has a 8 byte header
|
||||
* Recipients. This only has an 8 byte header.
|
||||
*/
|
||||
public class StoragePropertiesChunk extends PropertiesChunk {
|
||||
public StoragePropertiesChunk(ChunkGroup parentGroup) {
|
||||
@ -49,4 +49,18 @@ public class StoragePropertiesChunk extends PropertiesChunk {
|
||||
// Now properties
|
||||
writeProperties(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out pre-calculated header values which assume any variable length property `data`
|
||||
* field to already have Size and Reserved
|
||||
* @param out output stream (calling code must close this stream)
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writePreCalculatedValue(OutputStream out) throws IOException {
|
||||
// 8 bytes of reserved zeros
|
||||
out.write(new byte[8]);
|
||||
|
||||
// Now properties
|
||||
writePreCalculatedProperties(out);
|
||||
}
|
||||
}
|
||||
@ -17,10 +17,14 @@
|
||||
|
||||
package org.apache.poi.hsmf.datatypes;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Verifies that the Chunks class is actually setup properly and hasn't been changed in ways
|
||||
* that will break the library.
|
||||
@ -28,22 +32,22 @@ import org.junit.jupiter.api.Test;
|
||||
public final class TestChunkData {
|
||||
@Test
|
||||
void testChunkCreate() {
|
||||
Chunk chunk;
|
||||
Chunk chunk;
|
||||
|
||||
chunk = new StringChunk(0x0200, Types.createCustom(0x001E));
|
||||
assertEquals("__substg1.0_0200001E", chunk.getEntryName());
|
||||
assertEquals(0x0200, chunk.getChunkId());
|
||||
assertEquals(0x001E, chunk.getType().getId());
|
||||
|
||||
chunk = new StringChunk("__substg1.0_", 0x0200, Types.createCustom(0x001E));
|
||||
assertEquals("__substg1.0_0200001E", chunk.getEntryName());
|
||||
assertEquals(0x0200, chunk.getChunkId());
|
||||
assertEquals(0x001E, chunk.getType().getId());
|
||||
chunk = new StringChunk("__substg1.0_", 0x0200, Types.createCustom(0x001E));
|
||||
assertEquals("__substg1.0_0200001E", chunk.getEntryName());
|
||||
assertEquals(0x0200, chunk.getChunkId());
|
||||
assertEquals(0x001E, chunk.getType().getId());
|
||||
|
||||
chunk = new StringChunk("__substg1.0_", 0x0200, Types.getById(0x001E));
|
||||
assertEquals("__substg1.0_0200001E", chunk.getEntryName());
|
||||
assertEquals(0x0200, chunk.getChunkId());
|
||||
assertEquals(0x001E, chunk.getType().getId());
|
||||
chunk = new StringChunk("__substg1.0_", 0x0200, Types.getById(0x001E));
|
||||
assertEquals("__substg1.0_0200001E", chunk.getEntryName());
|
||||
assertEquals(0x0200, chunk.getChunkId());
|
||||
assertEquals(0x001E, chunk.getType().getId());
|
||||
|
||||
/* test the lower and upper limits of the chunk ids */
|
||||
chunk = new StringChunk(0x0000, Types.createCustom(0x001E));
|
||||
@ -65,26 +69,40 @@ public final class TestChunkData {
|
||||
@Test
|
||||
void testDisplayToChunk() {
|
||||
StringChunk chunk = new StringChunk(0x0E04, Types.UNICODE_STRING);
|
||||
assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_TO.id);
|
||||
assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_TO.id);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testDisplayCCChunk() {
|
||||
StringChunk chunk = new StringChunk(0x0E03, Types.UNICODE_STRING);
|
||||
assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_CC.id);
|
||||
assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_CC.id);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisplayBCCChunk() {
|
||||
StringChunk chunk = new StringChunk(0x0E02, Types.UNICODE_STRING);
|
||||
assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_BCC.id);
|
||||
assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_BCC.id);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSubjectChunk() {
|
||||
Chunk chunk = new StringChunk(0x0037, Types.UNICODE_STRING);
|
||||
assertEquals(chunk.getChunkId(), MAPIProperty.SUBJECT.id);
|
||||
assertEquals(chunk.getChunkId(), MAPIProperty.SUBJECT.id);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWritePreCalculatedProperties() throws IOException {
|
||||
try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
|
||||
StoragePropertiesChunk storagePropertiesChunk = new StoragePropertiesChunk(null);
|
||||
PropertyValue.LongPropertyValue attachSize =
|
||||
new PropertyValue.LongPropertyValue(MAPIProperty.ATTACH_SIZE, 6L, new byte[0]);
|
||||
PropertyValue currentValue = new PropertyValue(MAPIProperty.DISPLAY_BCC, 6L, new byte[0]);
|
||||
attachSize.setValue(3934266);
|
||||
storagePropertiesChunk.setProperty(attachSize);
|
||||
storagePropertiesChunk.setProperty(currentValue);
|
||||
List<PropertyValue> propertyValue= storagePropertiesChunk.writePreCalculatedProperties(stream);
|
||||
assertEquals(propertyValue.size(),1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user