X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=core%2Fsrc%2Fcom%2Fgoogle%2Fzxing%2Fclient%2Fresult%2FVCardResultParser.java;h=4d53971396d504300320fa82b78a127930016319;hb=10276c1fd6c1871565ad6d6c87f0249f1c0b7e5c;hp=6b3c28229b63c6098c034acd4b2a21089591a010;hpb=2f727149e7ba214f50a9a434c565585d9ffbab72;p=zxing.git diff --git a/core/src/com/google/zxing/client/result/VCardResultParser.java b/core/src/com/google/zxing/client/result/VCardResultParser.java index 6b3c2822..4d539713 100644 --- a/core/src/com/google/zxing/client/result/VCardResultParser.java +++ b/core/src/com/google/zxing/client/result/VCardResultParser.java @@ -18,6 +18,8 @@ package com.google.zxing.client.result; import com.google.zxing.Result; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; import java.util.Vector; /** @@ -57,7 +59,7 @@ final class VCardResultParser extends ResultParser { String org = matchSingleVCardPrefixedField("ORG", rawText, true); String birthday = matchSingleVCardPrefixedField("BDAY", rawText, true); if (!isLikeVCardDate(birthday)) { - return null; + birthday = null; } String title = matchSingleVCardPrefixedField("TITLE", rawText, true); String url = matchSingleVCardPrefixedField("URL", rawText, true); @@ -69,11 +71,14 @@ final class VCardResultParser extends ResultParser { Vector matches = null; int i = 0; int max = rawText.length(); + while (i < max) { + i = rawText.indexOf(prefix, i); if (i < 0) { break; } + if (i > 0 && rawText.charAt(i - 1) != '\n') { // then this didn't start a new token, we matched in the middle of something i++; @@ -83,36 +88,182 @@ final class VCardResultParser extends ResultParser { if (rawText.charAt(i) != ':' && rawText.charAt(i) != ';') { continue; } + + int metadataStart = i; while (rawText.charAt(i) != ':') { // Skip until a colon i++; } + + boolean quotedPrintable = false; + String quotedPrintableCharset = null; + if (i > metadataStart) { + // There was something after the tag, before colon + int j = metadataStart+1; + while (j <= i) { + if (rawText.charAt(j) == ';' || rawText.charAt(j) == ':') { + String metadata = rawText.substring(metadataStart+1, j); + int equals = metadata.indexOf('='); + if (equals >= 0) { + String key = metadata.substring(0, equals); + String value = metadata.substring(equals+1); + if (key.equalsIgnoreCase("ENCODING")) { + if (value.equalsIgnoreCase("QUOTED-PRINTABLE")) { + quotedPrintable = true; + } + } else if (key.equalsIgnoreCase("CHARSET")) { + quotedPrintableCharset = value; + } + } + metadataStart = j; + } + j++; + } + } + i++; // skip colon - int start = i; // Found the start of a match here - i = rawText.indexOf((int) '\n', i); // Really, ends in \r\n + + int matchStart = i; // Found the start of a match here + + while ((i = rawText.indexOf((int) '\n', i)) >= 0) { // Really, end in \r\n + if (i < rawText.length() - 1 && // But if followed by tab or space, + (rawText.charAt(i+1) == ' ' || // this is only a continuation + rawText.charAt(i+1) == '\t')) { + i += 2; // Skip \n and continutation whitespace + } else if (quotedPrintable && // If preceded by = in quoted printable + (rawText.charAt(i-1) == '=' || // this is a continuation + rawText.charAt(i-2) == '=')) { + i++; // Skip \n + } else { + break; + } + } + if (i < 0) { // No terminating end character? uh, done. Set i such that loop terminates and break i = max; - } else if (i > start) { + } else if (i > matchStart) { // found a match if (matches == null) { - matches = new Vector(3); // lazy init + matches = new Vector(1); // lazy init + } + if (rawText.charAt(i-1) == '\r') { + i--; // Back up over \r, which really should be there } - String element = rawText.substring(start, i); + String element = rawText.substring(matchStart, i); if (trim) { element = element.trim(); } + if (quotedPrintable) { + element = decodeQuotedPrintable(element, quotedPrintableCharset); + } else { + element = stripContinuationCRLF(element); + } matches.addElement(element); i++; } else { i++; } + } + if (matches == null || matches.isEmpty()) { return null; } return toStringArray(matches); } + private static String stripContinuationCRLF(String value) { + int length = value.length(); + StringBuffer result = new StringBuffer(length); + boolean lastWasLF = false; + for (int i = 0; i < length; i++) { + if (lastWasLF) { + lastWasLF = false; + continue; + } + char c = value.charAt(i); + lastWasLF = false; + switch (c) { + case '\n': + lastWasLF = true; + break; + case '\r': + break; + default: + result.append(c); + } + } + return result.toString(); + } + + private static String decodeQuotedPrintable(String value, String charset) { + int length = value.length(); + StringBuffer result = new StringBuffer(length); + ByteArrayOutputStream fragmentBuffer = new ByteArrayOutputStream(); + for (int i = 0; i < length; i++) { + char c = value.charAt(i); + switch (c) { + case '\r': + case '\n': + break; + case '=': + if (i < length - 2) { + char nextChar = value.charAt(i+1); + if (nextChar == '\r' || nextChar == '\n') { + // Ignore, it's just a continuation symbol + } else { + char nextNextChar = value.charAt(i+2); + try { + int encodedByte = 16 * toHexValue(nextChar) + toHexValue(nextNextChar); + fragmentBuffer.write(encodedByte); + } catch (IllegalArgumentException iae) { + // continue, assume it was incorrectly encoded + } + i += 2; + } + } + break; + default: + maybeAppendFragment(fragmentBuffer, charset, result); + result.append(c); + } + } + maybeAppendFragment(fragmentBuffer, charset, result); + return result.toString(); + } + + private static int toHexValue(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + throw new IllegalArgumentException(); + } + + private static void maybeAppendFragment(ByteArrayOutputStream fragmentBuffer, + String charset, + StringBuffer result) { + if (fragmentBuffer.size() > 0) { + byte[] fragmentBytes = fragmentBuffer.toByteArray(); + String fragment; + if (charset == null) { + fragment = new String(fragmentBytes); + } else { + try { + fragment = new String(fragmentBytes, charset); + } catch (UnsupportedEncodingException e) { + // Yikes, well try anyway: + fragment = new String(fragmentBytes); + } + } + fragmentBuffer.reset(); + result.append(fragment); + } + } + static String matchSingleVCardPrefixedField(String prefix, String rawText, boolean trim) { String[] values = matchVCardPrefixedField(prefix, rawText, trim); return values == null ? null : values[0];