1192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta/* 2192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Copyright (C) 2014 The Android Open Source Project 3192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * 4192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Licensed under the Apache License, Version 2.0 (the "License"); 5192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * you may not use this file except in compliance with the License. 6192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * You may obtain a copy of the License at 7192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * 8192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * http://www.apache.org/licenses/LICENSE-2.0 9192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * 10192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Unless required by applicable law or agreed to in writing, software 11192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * distributed under the License is distributed on an "AS IS" BASIS, 12192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * See the License for the specific language governing permissions and 14192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * limitations under the License. 15192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 16192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 17192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptapackage android.bluetooth.client.map; 18192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 19192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.util.Log; 20192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 21192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardEntry; 22192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardEntryConstructor; 23192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardEntryHandler; 24192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardParser; 25192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardParser_V21; 26192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.VCardParser_V30; 27192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.exception.VCardException; 28192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport com.android.vcard.exception.VCardVersionException; 29192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.client.map.BluetoothMapBmessage.Status; 30192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.client.map.BluetoothMapBmessage.Type; 31192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.client.map.utils.BmsgTokenizer; 32192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.client.map.utils.BmsgTokenizer.Property; 33192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 34192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.io.ByteArrayInputStream; 35192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.io.IOException; 36192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.text.ParseException; 37192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 38192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaclass BluetoothMapBmessageParser { 39192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 40192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static String TAG = "BluetoothMapBmessageParser"; 41192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 42192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static String CRLF = "\r\n"; 43192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 44192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_BMSG = new Property("BEGIN", "BMSG"); 45192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_BMSG = new Property("END", "BMSG"); 46192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 47192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_VCARD = new Property("BEGIN", "VCARD"); 48192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_VCARD = new Property("END", "VCARD"); 49192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 50192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_BENV = new Property("BEGIN", "BENV"); 51192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_BENV = new Property("END", "BENV"); 52192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 53192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_BBODY = new Property("BEGIN", "BBODY"); 54192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_BBODY = new Property("END", "BBODY"); 55192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 56192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property BEGIN_MSG = new Property("BEGIN", "MSG"); 57192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static Property END_MSG = new Property("END", "MSG"); 58192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 59192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static int CRLF_LEN = 2; 60192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 61192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 62192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * length of "container" for 'message' in bmessage-body-content: 63192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * BEGIN:MSG<CRLF> + <CRLF> + END:MSG<CRFL> 64192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 65192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final static int MSG_CONTAINER_LEN = 22; 66192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 67192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private BmsgTokenizer mParser; 68192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 69192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final BluetoothMapBmessage mBmsg; 70192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 71192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private BluetoothMapBmessageParser() { 72192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg = new BluetoothMapBmessage(); 73192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 74192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 75192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta static public BluetoothMapBmessage createBmessage(String str) { 76192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta BluetoothMapBmessageParser p = new BluetoothMapBmessageParser(); 77192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 78192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 79192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.parse(str); 80192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (IOException e) { 81192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.e(TAG, "I/O exception when parsing bMessage", e); 82192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return null; 83192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (ParseException e) { 84192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.e(TAG, "Cannot parse bMessage", e); 85192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return null; 86192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 87192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 88192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return p.mBmsg; 89192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 90192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 91192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private ParseException expected(Property... props) { 92192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta boolean first = true; 93192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta StringBuilder sb = new StringBuilder(); 94192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 95192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta for (Property prop : props) { 96192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!first) { 97192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta sb.append(" or "); 98192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 99192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta sb.append(prop); 100192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta first = false; 101192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 102192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 103192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return new ParseException("Expected: " + sb.toString(), mParser.pos()); 104192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 105192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 106192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private void parse(String str) throws IOException, ParseException { 107192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 108192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 109192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 110192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 111192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-object>::= { "BEGIN:BMSG" <CRLF> <bmessage-property> 112192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * [<bmessage-originator>]* <bmessage-envelope> "END:BMSG" <CRLF> } 113192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 114192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 115192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser = new BmsgTokenizer(str + CRLF); 116192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 117192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 118192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(BEGIN_BMSG)) { 119192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(BEGIN_BMSG); 120192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 121192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 122192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = parseProperties(); 123192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 124192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta while (prop.equals(BEGIN_VCARD)) { 125192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 126192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* <bmessage-originator>::= <vcard> <CRLF> */ 127192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 128192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta StringBuilder vcard = new StringBuilder(); 129192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = extractVcard(vcard); 130192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 131192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntry entry = parseVcard(vcard.toString()); 132192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mOriginators.add(entry); 133192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 134192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 135192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(BEGIN_BENV)) { 136192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(BEGIN_BENV); 137192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 138192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 139192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = parseEnvelope(1); 140192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 141192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(END_BMSG)) { 142192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(END_BENV); 143192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 144192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 145192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 146192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * there should be no meaningful data left in stream here so we just 147192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * ignore whatever is left 148192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 149192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 150192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser = null; 151192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 152192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 153192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private Property parseProperties() throws ParseException { 154192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 155192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 156192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 157192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 158192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-property>::=<bmessage-version-property> 159192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-readstatus-property> <bmessage-type-property> 160192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-folder-property> <bmessage-version-property>::="VERSION:" 161192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <common-digit>*"."<common-digit>* <CRLF> 162192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-readstatus-property>::="STATUS:" 'readstatus' <CRLF> 163192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-type-property>::="TYPE:" 'type' <CRLF> 164192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-folder-property>::="FOLDER:" 'foldername' <CRLF> 165192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 166192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 167192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta do { 168192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 169192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 170192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.name.equals("VERSION")) { 171192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBmsgVersion = prop.value; 172192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 173192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("STATUS")) { 174192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta for (Status s : Status.values()) { 175192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.value.equals(s.toString())) { 176192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBmsgStatus = s; 177192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta break; 178192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 179192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 180192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 181192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("TYPE")) { 182192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta for (Type t : Type.values()) { 183192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.value.equals(t.toString())) { 184192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBmsgType = t; 185192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta break; 186192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 187192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 188192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 189192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("FOLDER")) { 190192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBmsgFolder = prop.value; 191192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 192192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 193192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 194192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } while (!prop.equals(BEGIN_VCARD) && !prop.equals(BEGIN_BENV)); 195192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 196192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return prop; 197192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 198192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 199192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private Property parseEnvelope(int level) throws IOException, ParseException { 200192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 201192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 202192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 203192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 204192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * we can support as many nesting level as we want, but MAP spec clearly 205192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * defines that there should be no more than 3 levels. so we verify it 206192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * here. 207192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 208192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 209192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (level > 3) { 210192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw new ParseException("bEnvelope is nested more than 3 times", mParser.pos()); 211192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 212192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 213192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 214192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-envelope> ::= { "BEGIN:BENV" <CRLF> [<bmessage-recipient>]* 215192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-envelope> | <bmessage-content> "END:BENV" <CRLF> } 216192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 217192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 218192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 219192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 220192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta while (prop.equals(BEGIN_VCARD)) { 221192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 222192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* <bmessage-originator>::= <vcard> <CRLF> */ 223192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 224192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta StringBuilder vcard = new StringBuilder(); 225192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = extractVcard(vcard); 226192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 227192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (level == 1) { 228192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntry entry = parseVcard(vcard.toString()); 229192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mRecipients.add(entry); 230192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 231192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 232192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 233192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.equals(BEGIN_BENV)) { 234192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = parseEnvelope(level + 1); 235192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 236192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.equals(BEGIN_BBODY)) { 237192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = parseBody(); 238192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 239192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else { 240192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(BEGIN_BENV, BEGIN_BBODY); 241192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 242192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 243192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(END_BENV)) { 244192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(END_BENV); 245192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 246192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 247192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return mParser.next(); 248192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 249192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 250192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private Property parseBody() throws IOException, ParseException { 251192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 252192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 253192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 254192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 255192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-content>::= { "BEGIN:BBODY"<CRLF> [<bmessage-body-part-ID> 256192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <CRLF>] <bmessage-body-property> <bmessage-body-content>* <CRLF> 257192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * "END:BBODY"<CRLF> } <bmessage-body-part-ID>::="PARTID:" 'Part-ID' 258192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-property>::=[<bmessage-body-encoding-property>] 259192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * [<bmessage-body-charset-property>] 260192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * [<bmessage-body-language-property>] 261192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-content-length-property> 262192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-encoding-property>::="ENCODING:"'encoding' <CRLF> 263192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-charset-property>::="CHARSET:"'charset' <CRLF> 264192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-language-property>::="LANGUAGE:"'language' <CRLF> 265192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-content-length-property>::= "LENGTH:" <common-digit>* 266192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <CRLF> 267192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 268192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 269192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta do { 270192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 271192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 272192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop.name.equals("PARTID")) { 273192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("ENCODING")) { 274192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBbodyEncoding = prop.value; 275192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 276192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("CHARSET")) { 277192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBbodyCharset = prop.value; 278192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 279192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("LANGUAGE")) { 280192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBbodyLanguage = prop.value; 281192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 282192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else if (prop.name.equals("LENGTH")) { 283192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 284192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mBbodyLength = Integer.valueOf(prop.value); 285192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (NumberFormatException e) { 286192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw new ParseException("Invalid LENGTH value", mParser.pos()); 287192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 288192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 289192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 290192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 291192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } while (!prop.equals(BEGIN_MSG)); 292192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 293192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 294192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * <bmessage-body-content>::={ "BEGIN:MSG"<CRLF> 'message'<CRLF> 295192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * "END:MSG"<CRLF> } 296192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 297192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 298192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta int messageLen = mBmsg.mBbodyLength - MSG_CONTAINER_LEN; 299192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta int offset = messageLen + CRLF_LEN; 300192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta int restartPos = mParser.pos() + offset; 301192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 302192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 303192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * length is specified in bytes so we need to convert from unicode 304192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * string back to bytes array 305192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 306192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 307192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta String remng = mParser.remaining(); 308192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta byte[] data = remng.getBytes(); 309192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 310192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* restart parsing from after 'message'<CRLF> */ 311192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser = new BmsgTokenizer(new String(data, offset, data.length - offset), restartPos); 312192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 313192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(true); 314192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 315192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (prop != null && prop.equals(END_MSG)) { 316192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mMessage = new String(data, 0, messageLen); 317192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else { 318192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 319192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta data = null; 320192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 321192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 322192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * now we check if bMessage can be parsed if LENGTH is handled as 323192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * number of characters instead of number of bytes 324192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 325192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 326192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.w(TAG, "byte LENGTH seems to be invalid, trying with char length"); 327192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 328192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser = new BmsgTokenizer(remng.substring(offset)); 329192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 330192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 331192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 332192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(END_MSG)) { 333192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(END_MSG); 334192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 335192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 336192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mBmsg.mMessage = remng.substring(0, messageLen); 337192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 338192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 339192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 340192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 341192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!prop.equals(END_BBODY)) { 342192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw expected(END_BBODY); 343192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 344192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 345192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return mParser.next(); 346192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 347192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 348192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private Property extractVcard(StringBuilder out) throws IOException, ParseException { 349192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Property prop; 350192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 351192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta out.append(BEGIN_VCARD).append(CRLF); 352192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 353192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta do { 354192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta prop = mParser.next(); 355192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta out.append(prop).append(CRLF); 356192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } while (!prop.equals(END_VCARD)); 357192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 358192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return mParser.next(); 359192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 360192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 361192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private class VcardHandler implements VCardEntryHandler { 362192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 363192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntry vcard; 364192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 365192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta @Override 366192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void onStart() { 367192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 368192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 369192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta @Override 370192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void onEntryCreated(VCardEntry entry) { 371192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta vcard = entry; 372192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 373192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 374192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta @Override 375192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void onEnd() { 376192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 377192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta }; 378192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 379192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private VCardEntry parseVcard(String str) throws IOException, ParseException { 380192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntry vcard = null; 381192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 382192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 383192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardParser p = new VCardParser_V21(); 384192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntryConstructor c = new VCardEntryConstructor(); 385192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VcardHandler handler = new VcardHandler(); 386192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta c.addEntryHandler(handler); 387192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.addInterpreter(c); 388192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.parse(new ByteArrayInputStream(str.getBytes())); 389192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 390192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta vcard = handler.vcard; 391192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 392192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (VCardVersionException e1) { 393192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 394192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 395192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardParser p = new VCardParser_V30(); 396192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VCardEntryConstructor c = new VCardEntryConstructor(); 397192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta VcardHandler handler = new VcardHandler(); 398192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta c.addEntryHandler(handler); 399192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.addInterpreter(c); 400192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta p.parse(new ByteArrayInputStream(str.getBytes())); 401192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 402192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta vcard = handler.vcard; 403192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 404192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (VCardVersionException e2) { 405192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta // will throw below 406192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (VCardException e2) { 407192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta // will throw below 408192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 409192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 410192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (VCardException e1) { 411192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta // will throw below 412192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 413192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 414192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (vcard == null) { 415192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta throw new ParseException("Cannot parse vCard object (neither 2.1 nor 3.0?)", 416192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mParser.pos()); 417192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 418192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 419192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return vcard; 420192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 421192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta} 422