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.HexDump;
|
||||||
import org.apache.poi.util.IOUtils;
|
import org.apache.poi.util.IOUtils;
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
import org.apache.poi.util.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pure-MAPI attribute which applies to a {@link HMEFMessage}
|
* A pure-MAPI attribute which applies to a {@link HMEFMessage}
|
||||||
|
@ -103,9 +104,31 @@ public class MAPIAttribute {
|
||||||
// MAPI property, grab the details of it
|
// MAPI property, grab the details of it
|
||||||
MAPIProperty prop = MAPIProperty.get(id);
|
MAPIProperty prop = MAPIProperty.get(id);
|
||||||
if(id >= 0x8000 && id <= 0xFFFF) {
|
if(id >= 0x8000 && id <= 0xFFFF) {
|
||||||
// TODO
|
byte[] guid = new byte[16];
|
||||||
System.err.println("Not yet implemented for id " + id);
|
IOUtils.readFully(inp, guid);
|
||||||
break;
|
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)
|
// Now read in the value(s)
|
||||||
|
@ -117,6 +140,7 @@ public class MAPIAttribute {
|
||||||
int len = getLength(type, inp);
|
int len = getLength(type, inp);
|
||||||
byte[] data = new byte[len];
|
byte[] data = new byte[len];
|
||||||
IOUtils.readFully(inp, data);
|
IOUtils.readFully(inp, data);
|
||||||
|
skipToBoundary(len, inp);
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
MAPIAttribute attr;
|
MAPIAttribute attr;
|
||||||
|
@ -126,12 +150,6 @@ public class MAPIAttribute {
|
||||||
attr = new MAPIAttribute(prop, type, data);
|
attr = new MAPIAttribute(prop, type, data);
|
||||||
}
|
}
|
||||||
attrs.add(attr);
|
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);
|
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/microsoft.exchange.data.contenttypes.tnef.tnefpropertyid%28v=EXCHG.140%29.aspx
|
||||||
* http://msdn.microsoft.com/en-us/library/ms526356%28v=exchg.10%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>();
|
private static Map<Integer, MAPIProperty> attributes = new HashMap<Integer, MAPIProperty>();
|
||||||
|
|
||||||
public static final MAPIProperty AB_DEFAULT_DIR =
|
public static final MAPIProperty AB_DEFAULT_DIR =
|
||||||
|
@ -1021,6 +1021,8 @@ public final class MAPIProperty {
|
||||||
new MAPIProperty(-1, -1, "Unknown", null);
|
new MAPIProperty(-1, -1, "Unknown", null);
|
||||||
|
|
||||||
// 0x8??? ones are outlook specific, and not standard MAPI
|
// 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.name = name;
|
||||||
this.mapiProperty = mapiProperty;
|
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)) {
|
if(attributes.containsKey(id)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Duplicate MAPI Property with ID " + id + " : " +
|
"Duplicate MAPI Property with ID " + id + " : " +
|
||||||
|
@ -1044,6 +1050,7 @@ public final class MAPIProperty {
|
||||||
}
|
}
|
||||||
attributes.put(id, this);
|
attributes.put(id, this);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuffer str = new StringBuffer();
|
StringBuffer str = new StringBuffer();
|
||||||
str.append(name);
|
str.append(name);
|
||||||
|
@ -1057,6 +1064,7 @@ public final class MAPIProperty {
|
||||||
}
|
}
|
||||||
return str.toString();
|
return str.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MAPIProperty get(int id) {
|
public static MAPIProperty get(int id) {
|
||||||
MAPIProperty attr = attributes.get(id);
|
MAPIProperty attr = attributes.get(id);
|
||||||
if(attr != null) {
|
if(attr != null) {
|
||||||
|
@ -1068,4 +1076,14 @@ public final class MAPIProperty {
|
||||||
public static Collection<MAPIProperty> getAll() {
|
public static Collection<MAPIProperty> getAll() {
|
||||||
return Collections.unmodifiableCollection( attributes.values() );
|
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("0102", Types.asFileEnding(0x0102));
|
||||||
assertEquals("FEDC", Types.asFileEnding(0xfedc));
|
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