mirror of
https://github.com/etesync/android
synced 2025-04-25 11:39:07 +00:00
Better invalid VCard handling
* ignore unknown properties which have invalid syntax instead of crashing * log validation warnings for created VCards * add VCard tests
This commit is contained in:
parent
334dfb13d4
commit
7e7d36584c
@ -26,6 +26,7 @@ import ezvcard.Ezvcard;
|
|||||||
import ezvcard.VCard;
|
import ezvcard.VCard;
|
||||||
import ezvcard.VCardException;
|
import ezvcard.VCardException;
|
||||||
import ezvcard.VCardVersion;
|
import ezvcard.VCardVersion;
|
||||||
|
import ezvcard.ValidationWarnings;
|
||||||
import ezvcard.parameter.EmailType;
|
import ezvcard.parameter.EmailType;
|
||||||
import ezvcard.parameter.ImageType;
|
import ezvcard.parameter.ImageType;
|
||||||
import ezvcard.parameter.TelephoneType;
|
import ezvcard.parameter.TelephoneType;
|
||||||
@ -375,6 +376,11 @@ public class Contact extends Resource {
|
|||||||
vcard.setProdId("DAVdroid/" + Constants.APP_VERSION + " (ez-vcard/" + Ezvcard.VERSION + ")");
|
vcard.setProdId("DAVdroid/" + Constants.APP_VERSION + " (ez-vcard/" + Ezvcard.VERSION + ")");
|
||||||
vcard.setRevision(Revision.now());
|
vcard.setRevision(Revision.now());
|
||||||
|
|
||||||
|
// validate and print warnings
|
||||||
|
ValidationWarnings warnings = vcard.validate(VCardVersion.V3_0);
|
||||||
|
if (!warnings.isEmpty())
|
||||||
|
Log.w(TAG, "Created potentially invalid VCard! " + warnings);
|
||||||
|
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
Ezvcard
|
Ezvcard
|
||||||
.write(vcard)
|
.write(vcard)
|
||||||
|
@ -60,7 +60,7 @@ public class TlsSniSocketFactory implements LayeredConnectionSocketFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Socket createSocket(HttpContext context) throws IOException {
|
public Socket createSocket(HttpContext context) throws IOException {
|
||||||
return new Socket();
|
return sslSocketFactory.createSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -87,6 +87,7 @@ public class TlsSniSocketFactory implements LayeredConnectionSocketFactory {
|
|||||||
SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(plain, host, port, true);
|
SSLSocket ssl = (SSLSocket)sslSocketFactory.createSocket(plain, host, port, true);
|
||||||
|
|
||||||
// already connected, but verify host name again and print some connection info
|
// already connected, but verify host name again and print some connection info
|
||||||
|
Log.w(TAG, "Setting SNI/TLSv1.2 will silently fail because the handshake is already done");
|
||||||
connectWithSNI(ssl, host);
|
connectWithSNI(ssl, host);
|
||||||
|
|
||||||
return ssl;
|
return ssl;
|
||||||
@ -95,26 +96,23 @@ public class TlsSniSocketFactory implements LayeredConnectionSocketFactory {
|
|||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||||
private void connectWithSNI(SSLSocket ssl, String host) throws SSLPeerUnverifiedException {
|
private void connectWithSNI(SSLSocket ssl, String host) throws SSLPeerUnverifiedException {
|
||||||
if (!ssl.isConnected()) {
|
// set reasonable SSL/TLS settings before the handshake:
|
||||||
// set reasonable SSL/TLS settings before the handshake:
|
// - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <4.4.3, if available)
|
||||||
// - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <4.4.3, if available)
|
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
|
||||||
ssl.setEnabledProtocols(ssl.getSupportedProtocols());
|
|
||||||
|
// - set SNI host name
|
||||||
// - set SNI host name
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
Log.d(TAG, "Using documented SNI with host name " + host);
|
||||||
Log.d(TAG, "Using documented SNI with host name " + host);
|
sslSocketFactory.setHostname(ssl, host);
|
||||||
sslSocketFactory.setHostname(ssl, host);
|
} else {
|
||||||
} else {
|
Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection");
|
||||||
Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection");
|
try {
|
||||||
try {
|
java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class);
|
||||||
java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class);
|
setHostnameMethod.invoke(ssl, host);
|
||||||
setHostnameMethod.invoke(ssl, host);
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
Log.w(TAG, "SNI not useable", e);
|
||||||
Log.w(TAG, "SNI not useable", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
Log.d(TAG, "Socket is already connected, SNI/TLv1.2 not available unless activated by Android defaults");
|
|
||||||
|
|
||||||
// verify hostname and certificate
|
// verify hostname and certificate
|
||||||
SSLSession session = ssl.getSession();
|
SSLSession session = ssl.getSession();
|
||||||
|
5
test/assets/invalid-unknown-properties.vcf
Normal file
5
test/assets/invalid-unknown-properties.vcf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
BEGIN:VCARD
|
||||||
|
VERSION:3.0
|
||||||
|
FN:VCard with invalid unknown properties
|
||||||
|
X-UNKNOWN@PROPERTY:MUST-NOT_CONTAIN?OTHER*LETTERS;
|
||||||
|
END:VCARD
|
16
test/assets/reference.vcf
Normal file
16
test/assets/reference.vcf
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
BEGIN:VCARD
|
||||||
|
VERSION:3.0
|
||||||
|
N:Gump;Forrest;Mr.
|
||||||
|
FN:Forrest Gump
|
||||||
|
ORG:Bubba Gump Shrimp Co.
|
||||||
|
TITLE:Shrimp Man
|
||||||
|
PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif
|
||||||
|
TEL;TYPE=WORK,VOICE:(111) 555-1212
|
||||||
|
TEL;TYPE=HOME,VOICE:(404) 555-1212
|
||||||
|
ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
|
||||||
|
LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America
|
||||||
|
ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
|
||||||
|
LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America
|
||||||
|
EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
|
||||||
|
REV:2008-04-24T19:52:43Z
|
||||||
|
END:VCARD
|
58
test/src/at/bitfire/davdroid/resource/test/ContactTest.java
Normal file
58
test/src/at/bitfire/davdroid/resource/test/ContactTest.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package at.bitfire.davdroid.resource.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import ezvcard.property.Email;
|
||||||
|
import ezvcard.property.Telephone;
|
||||||
|
import lombok.Cleanup;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.test.InstrumentationTestCase;
|
||||||
|
import at.bitfire.davdroid.resource.Contact;
|
||||||
|
import at.bitfire.davdroid.resource.InvalidResourceException;
|
||||||
|
|
||||||
|
public class ContactTest extends InstrumentationTestCase {
|
||||||
|
AssetManager assetMgr;
|
||||||
|
|
||||||
|
public void setUp() throws IOException, InvalidResourceException {
|
||||||
|
assetMgr = getInstrumentation().getContext().getResources().getAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testReferenceVCard() throws IOException, InvalidResourceException {
|
||||||
|
Contact c = parseVCF("reference.vcf");
|
||||||
|
assertEquals("Gump", c.getFamilyName());
|
||||||
|
assertEquals("Forrest", c.getGivenName());
|
||||||
|
assertEquals("Forrest Gump", c.getDisplayName());
|
||||||
|
assertEquals("Bubba Gump Shrimp Co.", c.getOrganization().getValues().get(0));
|
||||||
|
assertEquals("Shrimp Man", c.getJobTitle());
|
||||||
|
|
||||||
|
Telephone phone1 = c.getPhoneNumbers().get(0);
|
||||||
|
assertEquals("(111) 555-1212", phone1.getText());
|
||||||
|
assertEquals("WORK", phone1.getParameters("TYPE").get(0));
|
||||||
|
assertEquals("VOICE", phone1.getParameters("TYPE").get(1));
|
||||||
|
|
||||||
|
Telephone phone2 = c.getPhoneNumbers().get(1);
|
||||||
|
assertEquals("(404) 555-1212", phone2.getText());
|
||||||
|
assertEquals("HOME", phone2.getParameters("TYPE").get(0));
|
||||||
|
assertEquals("VOICE", phone2.getParameters("TYPE").get(1));
|
||||||
|
|
||||||
|
Email email = c.getEmails().get(0);
|
||||||
|
assertEquals("forrestgump@example.com", email.getValue());
|
||||||
|
assertEquals("PREF", email.getParameters("TYPE").get(0));
|
||||||
|
assertEquals("INTERNET", email.getParameters("TYPE").get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseInvalidUnknownProperties() throws IOException, InvalidResourceException {
|
||||||
|
Contact c = parseVCF("invalid-unknown-properties.vcf");
|
||||||
|
assertEquals("VCard with invalid unknown properties", c.getDisplayName());
|
||||||
|
assertNull(c.getUnknownProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Contact parseVCF(String fname) throws IOException, InvalidResourceException {
|
||||||
|
@Cleanup InputStream in = assetMgr.open(fname, AssetManager.ACCESS_STREAMING);
|
||||||
|
Contact c = new Contact(fname, null);
|
||||||
|
c.parseEntity(in);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
@ -41,7 +41,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
httpClient = DavHttpClient.create();
|
httpClient = DavHttpClient.create(true, true);
|
||||||
|
|
||||||
assetMgr = getInstrumentation().getContext().getResources().getAssets();
|
assetMgr = getInstrumentation().getContext().getResources().getAssets();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user