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