mirror of https://github.com/apache/poi.git
Add support for custom MAPI Properties (0x8000 and above, plus unknown lower ones)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1058262 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ee734212a9
commit
e1b8144c18
|
@ -28,6 +28,7 @@ import org.apache.poi.hsmf.datatypes.Types;
|
|||
import org.apache.poi.util.HexDump;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.StringUtil;
|
||||
|
||||
/**
|
||||
* A pure-MAPI attribute which applies to a {@link HMEFMessage}
|
||||
|
@ -103,9 +104,31 @@ public class MAPIAttribute {
|
|||
// MAPI property, grab the details of it
|
||||
MAPIProperty prop = MAPIProperty.get(id);
|
||||
if(id >= 0x8000 && id <= 0xFFFF) {
|
||||
// TODO
|
||||
System.err.println("Not yet implemented for id " + id);
|
||||
break;
|
||||
byte[] guid = new byte[16];
|
||||
IOUtils.readFully(inp, guid);
|
||||
int mptype = LittleEndian.readInt(inp);
|
||||
|
||||
// Get the name of it
|
||||
String name;
|
||||
if(mptype == 0) {
|
||||
// It's based on a normal one
|
||||
int mpid = LittleEndian.readInt(inp);
|
||||
MAPIProperty base = MAPIProperty.get(mpid);
|
||||
name = base.name;
|
||||
} else {
|
||||
// Custom name was stored
|
||||
int mplen = LittleEndian.readInt(inp);
|
||||
byte[] mpdata = new byte[mplen];
|
||||
IOUtils.readFully(inp, mpdata);
|
||||
name = StringUtil.getFromUnicodeLE(mpdata, 0, (mplen/2)-1);
|
||||
skipToBoundary(mplen, inp);
|
||||
}
|
||||
|
||||
// Now create
|
||||
prop = MAPIProperty.createCustom(id, type, name);
|
||||
}
|
||||
if(prop == MAPIProperty.UNKNOWN) {
|
||||
prop = MAPIProperty.createCustom(id, type, "(unknown " + Integer.toHexString(id) + ")");
|
||||
}
|
||||
|
||||
// Now read in the value(s)
|
||||
|
@ -117,6 +140,7 @@ public class MAPIAttribute {
|
|||
int len = getLength(type, inp);
|
||||
byte[] data = new byte[len];
|
||||
IOUtils.readFully(inp, data);
|
||||
skipToBoundary(len, inp);
|
||||
|
||||
// Create
|
||||
MAPIAttribute attr;
|
||||
|
@ -126,12 +150,6 @@ public class MAPIAttribute {
|
|||
attr = new MAPIAttribute(prop, type, data);
|
||||
}
|
||||
attrs.add(attr);
|
||||
|
||||
// Data is always padded out to a 4 byte boundary
|
||||
if(len % 4 != 0) {
|
||||
byte[] padding = new byte[4 - (len % 4)];
|
||||
IOUtils.readFully(inp, padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,4 +185,12 @@ public class MAPIAttribute {
|
|||
throw new IllegalArgumentException("Unknown type " + type);
|
||||
}
|
||||
}
|
||||
private static void skipToBoundary(int length, InputStream inp) throws IOException {
|
||||
// Data is always padded out to a 4 byte boundary
|
||||
if(length % 4 != 0) {
|
||||
int skip = 4 - (length % 4);
|
||||
byte[] padding = new byte[skip];
|
||||
IOUtils.readFully(inp, padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ import java.util.Map;
|
|||
* http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertyid%28v=EXCHG.140%29.aspx
|
||||
* http://msdn.microsoft.com/en-us/library/ms526356%28v=exchg.10%29.aspx
|
||||
*/
|
||||
public final class MAPIProperty {
|
||||
public class MAPIProperty {
|
||||
private static Map<Integer, MAPIProperty> attributes = new HashMap<Integer, MAPIProperty>();
|
||||
|
||||
public static final MAPIProperty AB_DEFAULT_DIR =
|
||||
|
@ -1021,6 +1021,8 @@ public final class MAPIProperty {
|
|||
new MAPIProperty(-1, -1, "Unknown", null);
|
||||
|
||||
// 0x8??? ones are outlook specific, and not standard MAPI
|
||||
private static final int ID_FIRST_CUSTOM = 0x8000;
|
||||
private static final int ID_LAST_CUSTOM = 0xFFFE;
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
|
@ -1035,7 +1037,11 @@ public final class MAPIProperty {
|
|||
this.name = name;
|
||||
this.mapiProperty = mapiProperty;
|
||||
|
||||
// Store it for lookup
|
||||
// If it isn't unknown or custom, store it for lookup
|
||||
if(id == -1 || (id >= ID_FIRST_CUSTOM && id <= ID_LAST_CUSTOM)
|
||||
|| (this instanceof CustomMAPIProperty)) {
|
||||
// Custom/Unknown, skip
|
||||
} else {
|
||||
if(attributes.containsKey(id)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate MAPI Property with ID " + id + " : " +
|
||||
|
@ -1044,6 +1050,7 @@ public final class MAPIProperty {
|
|||
}
|
||||
attributes.put(id, this);
|
||||
}
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer str = new StringBuffer();
|
||||
str.append(name);
|
||||
|
@ -1057,6 +1064,7 @@ public final class MAPIProperty {
|
|||
}
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
public static MAPIProperty get(int id) {
|
||||
MAPIProperty attr = attributes.get(id);
|
||||
if(attr != null) {
|
||||
|
@ -1068,4 +1076,14 @@ public final class MAPIProperty {
|
|||
public static Collection<MAPIProperty> getAll() {
|
||||
return Collections.unmodifiableCollection( attributes.values() );
|
||||
}
|
||||
|
||||
public static MAPIProperty createCustom(int id, int type, String name) {
|
||||
return new CustomMAPIProperty(id, type, name, null);
|
||||
}
|
||||
|
||||
private static class CustomMAPIProperty extends MAPIProperty {
|
||||
private CustomMAPIProperty(int id, int usualType, String name, String mapiProperty) {
|
||||
super(id, usualType, name, mapiProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hsmf.datatypes;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Checks various MAPIProperty related logic
|
||||
*/
|
||||
public final class TestMAPIProperty extends TestCase {
|
||||
public void testGet() throws Exception {
|
||||
assertEquals(MAPIProperty.DISPLAY_NAME, MAPIProperty.get(MAPIProperty.DISPLAY_NAME.id));
|
||||
assertEquals(MAPIProperty.DISPLAY_BCC, MAPIProperty.get(MAPIProperty.DISPLAY_BCC.id));
|
||||
assertNotSame(MAPIProperty.DISPLAY_BCC, MAPIProperty.get(MAPIProperty.DISPLAY_CC.id));
|
||||
}
|
||||
|
||||
public void testGetAll() throws Exception {
|
||||
Collection<MAPIProperty> all = MAPIProperty.getAll();
|
||||
assertEquals(true, all.contains(MAPIProperty.DISPLAY_NAME));
|
||||
assertEquals(true, all.contains(MAPIProperty.DISPLAY_CC));
|
||||
|
||||
// Won't contain custom
|
||||
assertEquals(false, all.contains(MAPIProperty.createCustom(1, 1, "")));
|
||||
|
||||
// Won't contain unknown
|
||||
assertEquals(false, all.contains(MAPIProperty.UNKNOWN));
|
||||
}
|
||||
|
||||
public void testCustom() throws Exception {
|
||||
MAPIProperty c1 = MAPIProperty.createCustom(1, 1, "");
|
||||
MAPIProperty c2a = MAPIProperty.createCustom(2, 1, "2");
|
||||
MAPIProperty c2b = MAPIProperty.createCustom(2, 1, "2");
|
||||
|
||||
// New object each time
|
||||
assertNotSame(c1, c2a);
|
||||
assertNotSame(c1, c2b);
|
||||
assertNotSame(c2a, c2b);
|
||||
|
||||
// Won't be in all list
|
||||
Collection<MAPIProperty> all = MAPIProperty.getAll();
|
||||
assertEquals(false, all.contains(c1));
|
||||
assertEquals(false, all.contains(c2a));
|
||||
assertEquals(false, all.contains(c2b));
|
||||
}
|
||||
}
|
|
@ -43,4 +43,9 @@ public final class TestTypes extends TestCase {
|
|||
assertEquals("0102", Types.asFileEnding(0x0102));
|
||||
assertEquals("FEDC", Types.asFileEnding(0xfedc));
|
||||
}
|
||||
|
||||
public void testName() {
|
||||
assertEquals("ASCII String", Types.asName(Types.ASCII_STRING));
|
||||
assertEquals("Boolean", Types.asName(Types.BOOLEAN));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue