001/**
002 *
003 * Copyright 2003-2007 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.vcardtemp.provider;
018
019import java.io.IOException;
020
021import org.jivesoftware.smack.packet.IqData;
022import org.jivesoftware.smack.packet.XmlEnvironment;
023import org.jivesoftware.smack.provider.IqProvider;
024import org.jivesoftware.smack.util.StringUtils;
025import org.jivesoftware.smack.xml.XmlPullParser;
026import org.jivesoftware.smack.xml.XmlPullParserException;
027
028import org.jivesoftware.smackx.vcardtemp.packet.VCard;
029
030/**
031 * vCard provider.
032 *
033 * @author Gaston Dombiak
034 * @author Derek DeMoro
035 * @author Chris Deering
036 */
037public class VCardProvider extends IqProvider<VCard> {
038
039    // @formatter:off
040    private static final String[] ADR = new String[] {
041        "POSTAL",
042        "PARCEL",
043        "DOM",
044        "INTL",
045        "PREF",
046        "POBOX",
047        "EXTADR",
048        "STREET",
049        "LOCALITY",
050        "REGION",
051        "PCODE",
052        "CTRY",
053        "FF",
054    };
055
056    private static final String[] TEL = new String[] {
057        "VOICE",
058        "FAX",
059        "PAGER",
060        "MSG",
061        "CELL",
062        "VIDEO",
063        "BBS",
064        "MODEM",
065        "ISDN",
066        "PCS",
067        "PREF",
068    };
069    // @formatter:on
070
071    @Override
072    public VCard parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
073        VCard vCard = new VCard();
074        String name = null;
075
076        outerloop: while (true) {
077            XmlPullParser.Event eventType = parser.next();
078            switch (eventType) {
079            case START_ELEMENT:
080                name = parser.getName();
081                switch (name) {
082                case "N":
083                    parseName(parser, vCard);
084                    break;
085                case "ORG":
086                    parseOrg(parser, vCard);
087                    break;
088                case "TEL":
089                    parseTel(parser, vCard);
090                    break;
091                case "ADR":
092                    parseAddress(parser, vCard);
093                    break;
094                case "EMAIL":
095                    parseEmail(parser, vCard);
096                    break;
097                case "NICKNAME":
098                    vCard.setNickName(parser.nextText());
099                    break;
100                case "JABBERID":
101                    vCard.setJabberId(parser.nextText());
102                    break;
103                case "PHOTO":
104                    parsePhoto(parser, vCard);
105                    break;
106                default:
107                    break;
108                }
109                break;
110            case TEXT_CHARACTERS:
111                if (initialDepth + 1 == parser.getDepth()) {
112                    String text = parser.getText();
113                    vCard.setField(name, text);
114                }
115                break;
116            case END_ELEMENT:
117                if (parser.getDepth() == initialDepth) {
118                    break outerloop;
119                }
120                break;
121            default:
122                break;
123            }
124        }
125
126        return vCard;
127    }
128
129    private static void parseAddress(XmlPullParser parser, VCard vCard) throws XmlPullParserException, IOException {
130        final int initialDepth = parser.getDepth();
131        boolean isWork = true;
132        outerloop: while (true) {
133            XmlPullParser.Event eventType = parser.next();
134            switch (eventType) {
135            case START_ELEMENT:
136                String name = parser.getName();
137                if ("HOME".equals(name)) {
138                    isWork = false;
139                }
140                else {
141                    for (String adr : ADR) {
142                        if (adr.equals(name)) {
143                            if (isWork) {
144                                vCard.setAddressFieldWork(name, parser.nextText());
145                            }
146                            else {
147                                vCard.setAddressFieldHome(name, parser.nextText());
148                            }
149                        }
150                    }
151                }
152                break;
153            case END_ELEMENT:
154                if (parser.getDepth() == initialDepth) {
155                    break outerloop;
156                }
157                break;
158            default:
159                break;
160            }
161        }
162    }
163
164    private static void parseTel(XmlPullParser parser, VCard vCard) throws XmlPullParserException, IOException {
165        final int initialDepth = parser.getDepth();
166        boolean isWork = true;
167        String telLabel = null;
168
169        outerloop: while (true) {
170            XmlPullParser.Event eventType = parser.next();
171            switch (eventType) {
172            case START_ELEMENT:
173                String name = parser.getName();
174                if ("HOME".equals(name)) {
175                    isWork = false;
176                }
177                else {
178                    if ("NUMBER".equals(name)) {
179                        if (StringUtils.isNullOrEmpty(telLabel)) {
180                            // RFC 2426 ยง 3.3.1:
181                            // "The default type is 'voice'"
182                            telLabel = "VOICE";
183                        }
184                        if (isWork) {
185                            vCard.setPhoneWork(telLabel, parser.nextText());
186                        }
187                        else {
188                            vCard.setPhoneHome(telLabel, parser.nextText());
189                        }
190                    }
191                    else {
192                        for (String tel : TEL) {
193                            if (tel.equals(name)) {
194                                telLabel = name;
195                            }
196                        }
197                    }
198                }
199                break;
200            case END_ELEMENT:
201                if (parser.getDepth() == initialDepth) {
202                    break outerloop;
203                }
204                break;
205            default:
206                break;
207            }
208        }
209    }
210
211    private static void parseOrg(XmlPullParser parser, VCard vCard) throws XmlPullParserException, IOException {
212        final int initialDepth = parser.getDepth();
213
214        outerloop: while (true) {
215            XmlPullParser.Event eventType = parser.next();
216            switch (eventType) {
217            case START_ELEMENT:
218                String name = parser.getName();
219                switch (name) {
220                case "ORGNAME":
221                    vCard.setOrganization(parser.nextText());
222                    break;
223                case "ORGUNIT":
224                    vCard.setOrganizationUnit(parser.nextText());
225                    break;
226                default:
227                    break;
228                }
229                break;
230            case END_ELEMENT:
231                if (parser.getDepth() == initialDepth) {
232                    break outerloop;
233                }
234                break;
235            default:
236                break;
237            }
238        }
239    }
240
241    private static void parseEmail(XmlPullParser parser, VCard vCard) throws XmlPullParserException, IOException {
242        final int initialDepth = parser.getDepth();
243        boolean isWork = false;
244
245        outerloop: while (true) {
246            XmlPullParser.Event eventType = parser.next();
247            switch (eventType) {
248            case START_ELEMENT:
249                String name = parser.getName();
250                switch (name) {
251                case "WORK":
252                    isWork = true;
253                    break;
254                case "USERID":
255                    if (isWork) {
256                        vCard.setEmailWork(parser.nextText());
257                    }
258                    else {
259                        vCard.setEmailHome(parser.nextText());
260                    }
261                    break;
262                default:
263                    break;
264                }
265                break;
266            case END_ELEMENT:
267                if (parser.getDepth() == initialDepth) {
268                    break outerloop;
269                }
270                break;
271            default:
272                break;
273            }
274        }
275    }
276
277    private static void parseName(XmlPullParser parser, VCard vCard) throws XmlPullParserException, IOException {
278        final int initialDepth = parser.getDepth();
279
280        outerloop: while (true) {
281            XmlPullParser.Event eventType = parser.next();
282            switch (eventType) {
283            case START_ELEMENT:
284                String name = parser.getName();
285                switch (name) {
286                case "FAMILY":
287                    vCard.setLastName(parser.nextText());
288                    break;
289                case "GIVEN":
290                    vCard.setFirstName(parser.nextText());
291                    break;
292                case "MIDDLE":
293                    vCard.setMiddleName(parser.nextText());
294                    break;
295                case "PREFIX":
296                    vCard.setPrefix(parser.nextText());
297                    break;
298                case "SUFFIX":
299                    vCard.setSuffix(parser.nextText());
300                    break;
301                default:
302                    break;
303                }
304                break;
305            case END_ELEMENT:
306                if (parser.getDepth() == initialDepth) {
307                    break outerloop;
308                }
309                break;
310            default:
311                break;
312
313            }
314        }
315    }
316
317    private static void parsePhoto(XmlPullParser parser, VCard vCard) throws XmlPullParserException, IOException {
318        final int initialDepth = parser.getDepth();
319        String binval = null;
320        String mimetype = null;
321
322        outerloop: while (true) {
323            XmlPullParser.Event eventType = parser.next();
324            switch (eventType) {
325            case START_ELEMENT:
326                String name = parser.getName();
327                switch (name) {
328                case "BINVAL":
329                    binval = parser.nextText();
330                    break;
331                case "TYPE":
332                    mimetype = parser.nextText();
333                    break;
334                default:
335                    break;
336                }
337                break;
338            case END_ELEMENT:
339                if (parser.getDepth() == initialDepth) {
340                    break outerloop;
341                }
342                break;
343            default:
344                break;
345            }
346        }
347
348        if (binval == null || mimetype == null) {
349            return;
350        }
351
352        vCard.setAvatar(binval, mimetype);
353    }
354}