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