Support vCard format emitted by Windows Mobile 6.5, which contains invalid "VALUE" params and
"AGENT" line. Internal Issue number: 2247192
This commit is contained in:
@@ -21,9 +21,23 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public abstract class VCardParser {
|
||||
public static final int PARSER_MODE_DEFAULT = 0;
|
||||
/**
|
||||
* The parser should ignore "AGENT" properties and nested vCard structure.
|
||||
*/
|
||||
public static final int PARSER_MODE_SCAN = 1;
|
||||
|
||||
protected final int mParserMode;
|
||||
protected boolean mCanceled;
|
||||
|
||||
|
||||
public VCardParser() {
|
||||
mParserMode = PARSER_MODE_DEFAULT;
|
||||
}
|
||||
|
||||
public VCardParser(int parserMode) {
|
||||
mParserMode = parserMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given stream and send the VCard data into VCardBuilderBase object.
|
||||
*
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
package android.pim.vcard;
|
||||
|
||||
import android.pim.vcard.exception.VCardAgentNotSupportedException;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.pim.vcard.exception.VCardInvalidCommentLineException;
|
||||
import android.pim.vcard.exception.VCardInvalidLineException;
|
||||
import android.pim.vcard.exception.VCardNestedException;
|
||||
import android.pim.vcard.exception.VCardNotSupportedException;
|
||||
import android.pim.vcard.exception.VCardVersionException;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -91,8 +91,15 @@ public class VCardParser_V21 extends VCardParser {
|
||||
|
||||
// In order to reduce warning message as much as possible, we hold the value which made Logger
|
||||
// emit a warning message.
|
||||
protected HashSet<String> mWarningValueMap = new HashSet<String>();
|
||||
|
||||
protected HashSet<String> mUnknownTypeMap = new HashSet<String>();
|
||||
protected HashSet<String> mUnknownValueMap = new HashSet<String>();
|
||||
|
||||
// It seems Windows Mobile 6.5 uses "AGENT" property with completely wrong usage.
|
||||
// We should just ignore just one line.
|
||||
// e.g.
|
||||
// "AGENT;CHARSET=SHIFT_JIS:some text"
|
||||
private boolean mIgnoreAgentLine = false;
|
||||
|
||||
// Just for debugging
|
||||
private long mTimeTotal;
|
||||
private long mTimeReadStartRecord;
|
||||
@@ -106,21 +113,41 @@ public class VCardParser_V21 extends VCardParser {
|
||||
private long mTimeHandleMiscPropertyValue;
|
||||
private long mTimeHandleQuotedPrintable;
|
||||
private long mTimeHandleBase64;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new VCard parser.
|
||||
*/
|
||||
public VCardParser_V21() {
|
||||
super();
|
||||
this(null, PARSER_MODE_DEFAULT);
|
||||
}
|
||||
|
||||
public VCardParser_V21(int parserMode) {
|
||||
this(null, parserMode);
|
||||
}
|
||||
|
||||
public VCardParser_V21(VCardSourceDetector detector) {
|
||||
super();
|
||||
if (detector != null && detector.getType() == VCardSourceDetector.TYPE_FOMA) {
|
||||
mNestCount = 1;
|
||||
this(detector, PARSER_MODE_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Merge detector and parser mode.
|
||||
*/
|
||||
public VCardParser_V21(VCardSourceDetector detector, int parserMode) {
|
||||
super(parserMode);
|
||||
if (detector != null) {
|
||||
final int type = detector.getType();
|
||||
if (type == VCardSourceDetector.TYPE_FOMA) {
|
||||
mNestCount = 1;
|
||||
} else if (type == VCardSourceDetector.TYPE_JAPANESE_MOBILE_PHONE) {
|
||||
mIgnoreAgentLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (parserMode == PARSER_MODE_SCAN) {
|
||||
mIgnoreAgentLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the file at the given position
|
||||
* vcard_file = [wsls] vcard [wsls]
|
||||
@@ -160,8 +187,8 @@ public class VCardParser_V21 extends VCardParser {
|
||||
protected boolean isValidPropertyName(String propertyName) {
|
||||
if (!(sAvailablePropertyNameSetV21.contains(propertyName.toUpperCase()) ||
|
||||
propertyName.startsWith("X-")) &&
|
||||
!mWarningValueMap.contains(propertyName)) {
|
||||
mWarningValueMap.add(propertyName);
|
||||
!mUnknownTypeMap.contains(propertyName)) {
|
||||
mUnknownTypeMap.add(propertyName);
|
||||
Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
|
||||
}
|
||||
return true;
|
||||
@@ -554,9 +581,9 @@ public class VCardParser_V21 extends VCardParser {
|
||||
protected void handleType(final String ptypeval) {
|
||||
String upperTypeValue = ptypeval;
|
||||
if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) &&
|
||||
!mWarningValueMap.contains(ptypeval)) {
|
||||
mWarningValueMap.add(ptypeval);
|
||||
Log.w(LOG_TAG, "Type unsupported by vCard 2.1: " + ptypeval);
|
||||
!mUnknownTypeMap.contains(ptypeval)) {
|
||||
mUnknownTypeMap.add(ptypeval);
|
||||
Log.w(LOG_TAG, "TYPE unsupported by vCard 2.1: " + ptypeval);
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("TYPE");
|
||||
@@ -567,15 +594,16 @@ public class VCardParser_V21 extends VCardParser {
|
||||
/**
|
||||
* pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
|
||||
*/
|
||||
protected void handleValue(final String pvalueval) throws VCardException {
|
||||
if (sKnownValueSet.contains(pvalueval.toUpperCase()) ||
|
||||
pvalueval.startsWith("X-")) {
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("VALUE");
|
||||
mBuilder.propertyParamValue(pvalueval);
|
||||
}
|
||||
} else {
|
||||
throw new VCardException("Unknown value \"" + pvalueval + "\"");
|
||||
protected void handleValue(final String pvalueval) {
|
||||
if (!sKnownValueSet.contains(pvalueval.toUpperCase()) &&
|
||||
pvalueval.startsWith("X-") &&
|
||||
!mUnknownValueMap.contains(pvalueval)) {
|
||||
mUnknownValueMap.add(pvalueval);
|
||||
Log.w(LOG_TAG, "VALUE unsupported by vCard 2.1: " + pvalueval);
|
||||
}
|
||||
if (mBuilder != null) {
|
||||
mBuilder.propertyParamType("VALUE");
|
||||
mBuilder.propertyParamValue(pvalueval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,9 +828,14 @@ public class VCardParser_V21 extends VCardParser {
|
||||
* items *CRLF "END" [ws] ":" [ws] "VCARD"
|
||||
*
|
||||
*/
|
||||
protected void handleAgent(String propertyValue) throws VCardException {
|
||||
throw new VCardNotSupportedException("AGENT Property is not supported now.");
|
||||
/* This is insufficient support. Also, AGENT Property is very rare.
|
||||
protected void handleAgent(final String propertyValue) throws VCardException {
|
||||
if (mIgnoreAgentLine) {
|
||||
return;
|
||||
} else {
|
||||
throw new VCardAgentNotSupportedException("AGENT Property is not supported now.");
|
||||
}
|
||||
/* This is insufficient support. Also, AGENT Property is very rare and really hard to
|
||||
understand the content.
|
||||
Ignore it for now.
|
||||
|
||||
String[] strArray = propertyValue.split(":", 2);
|
||||
@@ -819,7 +852,7 @@ public class VCardParser_V21 extends VCardParser {
|
||||
/**
|
||||
* For vCard 3.0.
|
||||
*/
|
||||
protected String maybeUnescapeText(String text) {
|
||||
protected String maybeUnescapeText(final String text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -827,11 +860,11 @@ public class VCardParser_V21 extends VCardParser {
|
||||
* Returns unescaped String if the character should be unescaped. Return null otherwise.
|
||||
* e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be.
|
||||
*/
|
||||
protected String maybeUnescapeCharacter(char ch) {
|
||||
protected String maybeUnescapeCharacter(final char ch) {
|
||||
return unescapeCharacter(ch);
|
||||
}
|
||||
|
||||
public static String unescapeCharacter(char ch) {
|
||||
public static String unescapeCharacter(final char ch) {
|
||||
// Original vCard 2.1 specification does not allow transformation
|
||||
// "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous implementation of
|
||||
// this class allowed them, so keep it as is.
|
||||
@@ -843,7 +876,7 @@ public class VCardParser_V21 extends VCardParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse(InputStream is, VCardBuilder builder)
|
||||
public boolean parse(final InputStream is, final VCardBuilder builder)
|
||||
throws IOException, VCardException {
|
||||
return parse(is, VCardConfig.DEFAULT_CHARSET, builder);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,11 @@ public class VCardParser_V30 extends VCardParser_V21 {
|
||||
mStrictParsing = strictParsing;
|
||||
}
|
||||
|
||||
public VCardParser_V30(int parseMode) {
|
||||
super(parseMode);
|
||||
mStrictParsing = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return VCardConfig.FLAG_V30;
|
||||
@@ -87,18 +92,18 @@ public class VCardParser_V30 extends VCardParser_V21 {
|
||||
if (!(sAcceptablePropsWithParam.contains(propertyName) ||
|
||||
acceptablePropsWithoutParam.contains(propertyName) ||
|
||||
propertyName.startsWith("X-")) &&
|
||||
!mWarningValueMap.contains(propertyName)) {
|
||||
mWarningValueMap.add(propertyName);
|
||||
!mUnknownTypeMap.contains(propertyName)) {
|
||||
mUnknownTypeMap.add(propertyName);
|
||||
Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isValidEncoding(String encoding) {
|
||||
return sAcceptableEncodingV30.contains(encoding.toUpperCase());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getLine() throws IOException {
|
||||
if (mPreviousLine != null) {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed 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 android.pim.vcard.exception;
|
||||
|
||||
public class VCardAgentNotSupportedException extends VCardNotSupportedException {
|
||||
public VCardAgentNotSupportedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public VCardAgentNotSupportedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
10
tests/AndroidTests/res/raw/v21_winmo_65.vcf
Normal file
10
tests/AndroidTests/res/raw/v21_winmo_65.vcf
Normal file
@@ -0,0 +1,10 @@
|
||||
BEGIN:VCARD
|
||||
VERSION:2.1
|
||||
N:Example;;;;
|
||||
FN:Example
|
||||
ANNIVERSARY;VALUE=DATE:20091010
|
||||
AGENT:Invalid line which must be handled correctly.
|
||||
X-CLASS:PUBLIC
|
||||
X-REDUCTION:
|
||||
X-NO:
|
||||
END:VCARD
|
||||
@@ -23,7 +23,6 @@ import android.pim.vcard.VCardParser_V21;
|
||||
import android.pim.vcard.VCardParser_V30;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@@ -56,6 +55,12 @@ public class PropertyNodesVerifier extends VNodeBuilder {
|
||||
verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vCardType);
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
verify(mAndroidTestCase.getContext().getResources().openRawResource(resId),
|
||||
vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType) throws IOException, VCardException {
|
||||
final VCardParser vCardParser;
|
||||
if (VCardConfig.isV30(vCardType)) {
|
||||
@@ -63,6 +68,11 @@ public class PropertyNodesVerifier extends VNodeBuilder {
|
||||
} else {
|
||||
vCardParser = new VCardParser_V21();
|
||||
}
|
||||
verify(is, vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
try {
|
||||
vCardParser.parse(is, this);
|
||||
} finally {
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package com.android.unit_tests.vcard;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.pim.vcard.VCardConfig;
|
||||
import android.pim.vcard.VCardParser;
|
||||
import android.pim.vcard.VCardParser_V21;
|
||||
import android.pim.vcard.exception.VCardException;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Email;
|
||||
@@ -999,6 +1002,35 @@ public class VCardImporterTests extends VCardTestsBase {
|
||||
verifier.verify(R.raw.v21_multiple_entry, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
|
||||
}
|
||||
|
||||
public void testIgnoreAgentV21_Parse() throws IOException, VCardException {
|
||||
PropertyNodesVerifier verifier = new PropertyNodesVerifier(this);
|
||||
ContentValues contentValuesForValue = new ContentValues();
|
||||
contentValuesForValue.put("VALUE", "DATE");
|
||||
verifier.addPropertyNodesVerifierElem()
|
||||
.addNodeWithOrder("VERSION", "2.1")
|
||||
.addNodeWithOrder("N", Arrays.asList("Example", "", "", "", ""))
|
||||
.addNodeWithOrder("FN", "Example")
|
||||
.addNodeWithOrder("ANNIVERSARY", "20091010", contentValuesForValue)
|
||||
.addNodeWithOrder("AGENT", "")
|
||||
.addNodeWithOrder("X-CLASS", "PUBLIC")
|
||||
.addNodeWithOrder("X-REDUCTION", "")
|
||||
.addNodeWithOrder("X-NO", "");
|
||||
|
||||
// Only scan mode lets vCard parser accepts invalid AGENT lines like above.
|
||||
verifier.verify(R.raw.v21_winmo_65, V21,
|
||||
new VCardParser_V21(VCardParser.PARSER_MODE_SCAN));
|
||||
}
|
||||
|
||||
public void testIgnoreAgentV21() throws IOException, VCardException {
|
||||
ImportVerifier verifier = new ImportVerifier();
|
||||
ImportVerifierElem elem = verifier.addImportVerifierElem();
|
||||
elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
|
||||
.put(StructuredName.FAMILY_NAME, "Example")
|
||||
.put(StructuredName.DISPLAY_NAME, "Example");
|
||||
verifier.verify(R.raw.v21_winmo_65, V21,
|
||||
new VCardParser_V21(VCardParser.PARSER_MODE_SCAN));
|
||||
}
|
||||
|
||||
public void testPagerV30_Parse() throws IOException, VCardException {
|
||||
PropertyNodesVerifier verifier = new PropertyNodesVerifier(this);
|
||||
verifier.addPropertyNodesVerifierElem()
|
||||
|
||||
@@ -455,6 +455,11 @@ class CustomMockContext extends MockContext {
|
||||
verify(getContext().getResources().openRawResource(resId), vCardType);
|
||||
}
|
||||
|
||||
public void verify(int resId, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
verify(getContext().getResources().openRawResource(resId), vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType) throws IOException, VCardException {
|
||||
final VCardParser vCardParser;
|
||||
if (VCardConfig.isV30(vCardType)) {
|
||||
@@ -462,6 +467,11 @@ class CustomMockContext extends MockContext {
|
||||
} else {
|
||||
vCardParser = new VCardParser_V21();
|
||||
}
|
||||
verify(is, vCardType, vCardParser);
|
||||
}
|
||||
|
||||
public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
|
||||
throws IOException, VCardException {
|
||||
VCardDataBuilder builder =
|
||||
new VCardDataBuilder(null, null, false, vCardType, null);
|
||||
builder.addEntryHandler(this);
|
||||
|
||||
Reference in New Issue
Block a user