File indexing completed on 2024-06-02 04:07:12

0001 /*
0002  * xmpp_vcard.cpp - classes for handling vCards
0003  * Copyright (C) 2003  Michail Pishchagin
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Lesser General Public
0007  * License as published by the Free Software Foundation; either
0008  * either version 2
0009    of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Lesser General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Lesser General Public
0017  * License along with this library; if not, write to the Free Software
0018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0019  *
0020  */
0021 
0022 #include "xmpp_vcard.h"
0023 
0024 #include <QDateTime>
0025 
0026 #include <QImage> // needed for image format recognition
0027 #include <QBuffer>
0028 #include <QImageReader>
0029 #include <QImageWriter>
0030 #include <QtCrypto>
0031 
0032 #include "xmpp_xmlcommon.h"
0033 
0034 using namespace XMPP;
0035 using namespace XMLHelper;
0036 
0037 //----------------------------------------------------------------------------
0038 // VCard
0039 //----------------------------------------------------------------------------
0040 QString image2type(const QByteArray &ba)
0041 {
0042     QBuffer buf;
0043     buf.setData(ba);
0044     buf.open(QIODevice::ReadOnly);
0045     QString format = QImageReader::imageFormat( &buf );
0046 
0047     // TODO: add more formats
0048     if ( format.toUpper() == "PNG" || format == "PsiPNG" )
0049         return "image/png";
0050     if ( format.toUpper() == "MNG" )
0051         return "video/x-mng";
0052     if ( format.toUpper() == "GIF" )
0053         return "image/gif";
0054     if ( format.toUpper() == "BMP" )
0055         return "image/bmp";
0056     if ( format.toUpper() == "XPM" )
0057         return "image/x-xpm";
0058     if ( format.toUpper() == "SVG" )
0059         return "image/svg+xml";
0060     if ( format.toUpper() == "JPEG" )
0061         return "image/jpeg";
0062 
0063     const QByteArray formatAscii = format.toLatin1();
0064     qWarning("WARNING! VCard::image2type: unknown format = '%s'", format.isNull() ? "UNKNOWN" : formatAscii.constData());
0065 
0066     return "image/unknown";
0067 }
0068 
0069 // Long lines of encoded binary data SHOULD BE folded to 75 characters using the folding method defined in [MIME-DIR].
0070 static QString foldString(const QString &s)
0071 {
0072     QString ret;
0073 
0074     for (int i = 0; i < (int)s.length(); i++) {
0075         if ( !(i % 75) )
0076             ret += '\n';
0077         ret += s[i];
0078     }
0079 
0080     return ret;
0081 }
0082 
0083 class VCard::Private
0084 {
0085 public:
0086     Private();
0087     ~Private();
0088 
0089     QString version;
0090     QString fullName;
0091     QString familyName, givenName, middleName, prefixName, suffixName;
0092     QString nickName;
0093 
0094     QByteArray photo;
0095     QString photoURI;
0096 
0097     QString bday;
0098     AddressList addressList;
0099     LabelList labelList;
0100     PhoneList phoneList;
0101     EmailList emailList;
0102     QString jid;
0103     QString mailer;
0104     QString timezone;
0105     Geo geo;
0106     QString title;
0107     QString role;
0108 
0109     QByteArray logo;
0110     QString logoURI;
0111 
0112     VCard *agent;
0113     QString agentURI;
0114 
0115     Org org;
0116     QStringList categories;
0117     QString note;
0118     QString prodId;
0119     QString rev;
0120     QString sortString;
0121 
0122     QByteArray sound;
0123     QString soundURI, soundPhonetic;
0124 
0125     QString uid;
0126     QString url;
0127     QString desc;
0128     PrivacyClass privacyClass;
0129     QByteArray key;
0130 
0131     bool isEmpty();
0132 };
0133 
0134 VCard::Private::Private()
0135 {
0136     privacyClass = pcNone;
0137     agent = 0;
0138 }
0139 
0140 VCard::Private::~Private()
0141 {
0142     delete agent;
0143 }
0144 
0145 bool VCard::Private::isEmpty()
0146 {
0147     if (    !version.isEmpty() ||
0148         !fullName.isEmpty() ||
0149         !familyName.isEmpty() || !givenName.isEmpty() || !middleName.isEmpty() || !prefixName.isEmpty() || !suffixName.isEmpty() ||
0150         !nickName.isEmpty() ||
0151         !photo.isEmpty() || !photoURI.isEmpty() ||
0152         !bday.isEmpty() ||
0153         !addressList.isEmpty() ||
0154         !labelList.isEmpty() ||
0155         !phoneList.isEmpty() ||
0156         !emailList.isEmpty() ||
0157         !jid.isEmpty() ||
0158         !mailer.isEmpty() ||
0159         !timezone.isEmpty() ||
0160         !geo.lat.isEmpty() || !geo.lon.isEmpty() ||
0161         !title.isEmpty() ||
0162         !role.isEmpty() ||
0163         !logo.isEmpty() || !logoURI.isEmpty() ||
0164         (agent && !agent->isEmpty()) || !agentURI.isEmpty() ||
0165         !org.name.isEmpty() || !org.unit.isEmpty() ||
0166         !categories.isEmpty() ||
0167         !note.isEmpty() ||
0168         !prodId.isEmpty() ||
0169         !rev.isEmpty() ||
0170         !sortString.isEmpty() ||
0171         !sound.isEmpty() || !soundURI.isEmpty() || !soundPhonetic.isEmpty() ||
0172         !uid.isEmpty() ||
0173         !url.isEmpty() ||
0174         !desc.isEmpty() ||
0175         (privacyClass != pcNone) ||
0176         !key.isEmpty() )
0177     {
0178         return false;
0179     }
0180     return true;
0181 }
0182 
0183 VCard::VCard()
0184 {
0185     d = new Private;
0186 }
0187 
0188 VCard::VCard(const VCard &from)
0189 {
0190     d = new Private;
0191     *this = from;
0192 }
0193 
0194 VCard & VCard::operator=(const VCard &from)
0195 {
0196     if(d->agent) {
0197         delete d->agent;
0198         d->agent = 0;
0199     }
0200 
0201     *d = *from.d;
0202 
0203     if(from.d->agent) {
0204         // dup the agent
0205         d->agent = new VCard(*from.d->agent);
0206     }
0207 
0208     return *this;
0209 }
0210 
0211 VCard::~VCard()
0212 {
0213     delete d;
0214 }
0215 
0216 QDomElement VCard::toXml(QDomDocument *doc) const
0217 {
0218     QDomElement v = doc->createElement("vCard");
0219     v.setAttribute("version", "2.0");
0220     v.setAttribute("prodid", "-//HandGen//NONSGML vGen v1.0//EN");
0221     v.setAttribute("xmlns", "vcard-temp");
0222 
0223     if ( !d->version.isEmpty() )
0224         v.appendChild( textTag(doc, "VERSION",  d->version) );
0225     if ( !d->fullName.isEmpty() )
0226         v.appendChild( textTag(doc, "FN",   d->fullName) );
0227 
0228     if ( !d->familyName.isEmpty() || !d->givenName.isEmpty() || !d->middleName.isEmpty() ||
0229          !d->prefixName.isEmpty() || !d->suffixName.isEmpty() ) {
0230         QDomElement w = doc->createElement("N");
0231 
0232         if ( !d->familyName.isEmpty() )
0233             w.appendChild( textTag(doc, "FAMILY",   d->familyName) );
0234         if ( !d->givenName.isEmpty() )
0235             w.appendChild( textTag(doc, "GIVEN",    d->givenName) );
0236         if ( !d->middleName.isEmpty() )
0237             w.appendChild( textTag(doc, "MIDDLE",   d->middleName) );
0238         if ( !d->prefixName.isEmpty() )
0239             w.appendChild( textTag(doc, "PREFIX",   d->prefixName) );
0240         if ( !d->suffixName.isEmpty() )
0241             w.appendChild( textTag(doc, "SUFFIX",   d->suffixName) );
0242 
0243         v.appendChild(w);
0244     }
0245 
0246     if ( !d->nickName.isEmpty() )
0247         v.appendChild( textTag(doc, "NICKNAME", d->nickName) );
0248 
0249     if ( !d->photo.isEmpty() || !d->photoURI.isEmpty() ) {
0250         QDomElement w = doc->createElement("PHOTO");
0251 
0252         if ( !d->photo.isEmpty() ) {
0253             w.appendChild( textTag(doc, "TYPE", image2type(d->photo)) );
0254             w.appendChild( textTag(doc, "BINVAL",   foldString( QCA::Base64().arrayToString(d->photo)) ) );
0255         }
0256         else if ( !d->photoURI.isEmpty() )
0257             w.appendChild( textTag(doc, "EXTVAL",   d->photoURI) );
0258 
0259         v.appendChild(w);
0260     }
0261 
0262     if ( !d->bday.isEmpty() )
0263         v.appendChild( textTag(doc, "BDAY", d->bday) );
0264 
0265     if ( !d->addressList.isEmpty() ) {
0266         AddressList::Iterator it = d->addressList.begin();
0267         for ( ; it != d->addressList.end(); ++it ) {
0268             QDomElement w = doc->createElement("ADR");
0269             Address a = *it;
0270 
0271             if ( a.home )
0272                 w.appendChild( emptyTag(doc, "HOME") );
0273             if ( a.work )
0274                 w.appendChild( emptyTag(doc, "WORK") );
0275             if ( a.postal )
0276                 w.appendChild( emptyTag(doc, "POSTAL") );
0277             if ( a.parcel )
0278                 w.appendChild( emptyTag(doc, "PARCEL") );
0279             if ( a.dom )
0280                 w.appendChild( emptyTag(doc, "DOM") );
0281             if ( a.intl )
0282                 w.appendChild( emptyTag(doc, "INTL") );
0283             if ( a.pref )
0284                 w.appendChild( emptyTag(doc, "PREF") );
0285 
0286             if ( !a.pobox.isEmpty() )
0287                 w.appendChild( textTag(doc, "POBOX",    a.pobox) );
0288             if ( !a.extaddr.isEmpty() )
0289                 w.appendChild( textTag(doc, "EXTADR",   a.extaddr) );
0290             if ( !a.street.isEmpty() )
0291                 w.appendChild( textTag(doc, "STREET",   a.street) );
0292             if ( !a.locality.isEmpty() )
0293                 w.appendChild( textTag(doc, "LOCALITY", a.locality) );
0294             if ( !a.region.isEmpty() )
0295                 w.appendChild( textTag(doc, "REGION",   a.region) );
0296             if ( !a.pcode.isEmpty() )
0297                 w.appendChild( textTag(doc, "PCODE",    a.pcode) );
0298             if ( !a.country.isEmpty() )
0299                 w.appendChild( textTag(doc, "CTRY", a.country) );
0300 
0301             v.appendChild(w);
0302         }
0303     }
0304 
0305     if ( !d->labelList.isEmpty() ) {
0306         LabelList::Iterator it = d->labelList.begin();
0307         for ( ; it != d->labelList.end(); ++it ) {
0308             QDomElement w = doc->createElement("LABEL");
0309             Label l = *it;
0310 
0311             if ( l.home )
0312                 w.appendChild( emptyTag(doc, "HOME") );
0313             if ( l.work )
0314                 w.appendChild( emptyTag(doc, "WORK") );
0315             if ( l.postal )
0316                 w.appendChild( emptyTag(doc, "POSTAL") );
0317             if ( l.parcel )
0318                 w.appendChild( emptyTag(doc, "PARCEL") );
0319             if ( l.dom )
0320                 w.appendChild( emptyTag(doc, "DOM") );
0321             if ( l.intl )
0322                 w.appendChild( emptyTag(doc, "INTL") );
0323             if ( l.pref )
0324                 w.appendChild( emptyTag(doc, "PREF") );
0325 
0326             if ( !l.lines.isEmpty() ) {
0327                 QStringList::Iterator it = l.lines.begin();
0328                 for ( ; it != l.lines.end(); ++it )
0329                     w.appendChild( textTag(doc, "LINE", *it) );
0330             }
0331 
0332             v.appendChild(w);
0333         }
0334     }
0335 
0336     if ( !d->phoneList.isEmpty() ) {
0337         PhoneList::Iterator it = d->phoneList.begin();
0338         for ( ; it != d->phoneList.end(); ++it ) {
0339             QDomElement w = doc->createElement("TEL");
0340             Phone p = *it;
0341 
0342             if ( p.home )
0343                 w.appendChild( emptyTag(doc, "HOME") );
0344             if ( p.work )
0345                 w.appendChild( emptyTag(doc, "WORK") );
0346             if ( p.voice )
0347                 w.appendChild( emptyTag(doc, "VOICE") );
0348             if ( p.fax )
0349                 w.appendChild( emptyTag(doc, "FAX") );
0350             if ( p.pager )
0351                 w.appendChild( emptyTag(doc, "PAGER") );
0352             if ( p.msg )
0353                 w.appendChild( emptyTag(doc, "MSG") );
0354             if ( p.cell )
0355                 w.appendChild( emptyTag(doc, "CELL") );
0356             if ( p.video )
0357                 w.appendChild( emptyTag(doc, "VIDEO") );
0358             if ( p.bbs )
0359                 w.appendChild( emptyTag(doc, "BBS") );
0360             if ( p.modem )
0361                 w.appendChild( emptyTag(doc, "MODEM") );
0362             if ( p.isdn )
0363                 w.appendChild( emptyTag(doc, "ISDN") );
0364             if ( p.pcs )
0365                 w.appendChild( emptyTag(doc, "PCS") );
0366             if ( p.pref )
0367                 w.appendChild( emptyTag(doc, "PREF") );
0368 
0369             if ( !p.number.isEmpty() )
0370                 w.appendChild( textTag(doc, "NUMBER",   p.number) );
0371 
0372             v.appendChild(w);
0373         }
0374     }
0375 
0376     if ( !d->emailList.isEmpty() ) {
0377         EmailList::Iterator it = d->emailList.begin();
0378         for ( ; it != d->emailList.end(); ++it ) {
0379             QDomElement w = doc->createElement("EMAIL");
0380             Email e = *it;
0381 
0382             if ( e.home )
0383                 w.appendChild( emptyTag(doc, "HOME") );
0384             if ( e.work )
0385                 w.appendChild( emptyTag(doc, "WORK") );
0386             if ( e.internet )
0387                 w.appendChild( emptyTag(doc, "INTERNET") );
0388             if ( e.x400 )
0389                 w.appendChild( emptyTag(doc, "X400") );
0390 
0391             if ( !e.userid.isEmpty() )
0392                 w.appendChild( textTag(doc, "USERID",   e.userid) );
0393 
0394             v.appendChild(w);
0395         }
0396     }
0397 
0398     if ( !d->jid.isEmpty() )
0399         v.appendChild( textTag(doc, "JABBERID", d->jid) );
0400     if ( !d->mailer.isEmpty() )
0401         v.appendChild( textTag(doc, "MAILER",   d->mailer) );
0402     if ( !d->timezone.isEmpty() )
0403         v.appendChild( textTag(doc, "TZ",   d->timezone) );
0404 
0405     if ( !d->geo.lat.isEmpty() || !d->geo.lon.isEmpty() ) {
0406         QDomElement w = doc->createElement("GEO");
0407 
0408         if ( !d->geo.lat.isEmpty() )
0409             w.appendChild( textTag(doc, "LAT",  d->geo.lat) );
0410         if ( !d->geo.lon.isEmpty() )
0411             w.appendChild( textTag(doc, "LON",  d->geo.lon));
0412 
0413         v.appendChild(w);
0414     }
0415 
0416     if ( !d->title.isEmpty() )
0417         v.appendChild( textTag(doc, "TITLE",    d->title) );
0418     if ( !d->role.isEmpty() )
0419         v.appendChild( textTag(doc, "ROLE", d->role) );
0420 
0421     if ( !d->logo.isEmpty() || !d->logoURI.isEmpty() ) {
0422         QDomElement w = doc->createElement("LOGO");
0423 
0424         if ( !d->logo.isEmpty() ) {
0425             w.appendChild( textTag(doc, "TYPE", image2type(d->logo)) );
0426             w.appendChild( textTag(doc, "BINVAL",   foldString( QCA::Base64().arrayToString(d->logo)) ) );
0427         }
0428         else if ( !d->logoURI.isEmpty() )
0429             w.appendChild( textTag(doc, "EXTVAL",   d->logoURI) );
0430 
0431         v.appendChild(w);
0432     }
0433 
0434     if ( !d->agentURI.isEmpty() || (d->agent && d->agent->isEmpty()) ) {
0435         QDomElement w = doc->createElement("AGENT");
0436 
0437         if ( d->agent && !d->agent->isEmpty() )
0438             w.appendChild( d->agent->toXml(doc) );
0439         else if ( !d->agentURI.isEmpty() )
0440             w.appendChild( textTag(doc, "EXTVAL",   d->agentURI) );
0441 
0442         v.appendChild(w);
0443     }
0444 
0445     if ( !d->org.name.isEmpty() || !d->org.unit.isEmpty() ) {
0446         QDomElement w = doc->createElement("ORG");
0447 
0448         if ( !d->org.name.isEmpty() )
0449             w.appendChild( textTag(doc, "ORGNAME",  d->org.name) );
0450 
0451         if ( !d->org.unit.isEmpty() ) {
0452             QStringList::Iterator it = d->org.unit.begin();
0453             for ( ; it != d->org.unit.end(); ++it )
0454                 w.appendChild( textTag(doc, "ORGUNIT",  *it) );
0455         }
0456 
0457         v.appendChild(w);
0458     }
0459 
0460     if ( !d->categories.isEmpty() ) {
0461         QDomElement w = doc->createElement("CATEGORIES");
0462 
0463         QStringList::Iterator it = d->categories.begin();
0464         for ( ; it != d->categories.end(); ++it )
0465             w.appendChild( textTag(doc, "KEYWORD", *it) );
0466 
0467         v.appendChild(w);
0468     }
0469 
0470     if ( !d->note.isEmpty() )
0471         v.appendChild( textTag(doc, "NOTE", d->note) );
0472     if ( !d->prodId.isEmpty() )
0473         v.appendChild( textTag(doc, "PRODID",   d->prodId) );
0474     if ( !d->rev.isEmpty() )
0475         v.appendChild( textTag(doc, "REV",  d->rev) );
0476     if ( !d->sortString.isEmpty() )
0477         v.appendChild( textTag(doc, "SORT-STRING",  d->sortString) );
0478 
0479     if ( !d->sound.isEmpty() || !d->soundURI.isEmpty() || !d->soundPhonetic.isEmpty() ) {
0480         QDomElement w = doc->createElement("SOUND");
0481 
0482         if ( !d->sound.isEmpty() )
0483             w.appendChild( textTag(doc, "BINVAL",   foldString( QCA::Base64().arrayToString(d->sound)) ) );
0484         else if ( !d->soundURI.isEmpty() )
0485             w.appendChild( textTag(doc, "EXTVAL",   d->soundURI) );
0486         else if ( !d->soundPhonetic.isEmpty() )
0487             w.appendChild( textTag(doc, "PHONETIC", d->soundPhonetic) );
0488 
0489         v.appendChild(w);
0490     }
0491 
0492     if ( !d->uid.isEmpty() )
0493         v.appendChild( textTag(doc, "UID",  d->uid) );
0494     if ( !d->url.isEmpty() )
0495         v.appendChild( textTag(doc, "URL",  d->url) );
0496     if ( !d->desc.isEmpty() )
0497         v.appendChild( textTag(doc, "DESC", d->desc) );
0498 
0499     if ( d->privacyClass != pcNone ) {
0500         QDomElement w = doc->createElement("CLASS");
0501 
0502         if ( d->privacyClass == pcPublic )
0503             w.appendChild( emptyTag(doc, "PUBLIC") );
0504         else if ( d->privacyClass == pcPrivate )
0505             w.appendChild( emptyTag(doc, "PRIVATE") );
0506         else if ( d->privacyClass == pcConfidential )
0507             w.appendChild( emptyTag(doc, "CONFIDENTIAL") );
0508 
0509         v.appendChild(w);
0510     }
0511 
0512     if ( !d->key.isEmpty() ) {
0513         QDomElement w = doc->createElement("KEY");
0514 
0515         // TODO: Justin, please check out this code
0516         w.appendChild( textTag(doc, "TYPE", "text/plain")); // FIXME
0517         w.appendChild( textTag(doc, "CRED", QString::fromUtf8(d->key)) ); // FIXME
0518 
0519         v.appendChild(w);
0520     }
0521 
0522     return v;
0523 }
0524 
0525 bool VCard::fromXml(const QDomElement &q)
0526 {
0527     if ( q.tagName().toUpper() != "VCARD" )
0528         return false;
0529 
0530     QDomNode n = q.firstChild();
0531     for ( ; !n.isNull(); n = n.nextSibling() ) {
0532         QDomElement i = n.toElement();
0533         if ( i.isNull() )
0534             continue;
0535 
0536         QString tag = i.tagName().toUpper();
0537 
0538         bool found;
0539         QDomElement e;
0540 
0541         if ( tag == "VERSION" )
0542             d->version = i.text().trimmed();
0543         else if ( tag == "FN" )
0544             d->fullName = i.text().trimmed();
0545         else if ( tag == "N" ) {
0546             d->familyName = subTagText(i, "FAMILY");
0547             d->givenName  = subTagText(i, "GIVEN");
0548             d->middleName = subTagText(i, "MIDDLE");
0549             d->prefixName = subTagText(i, "PREFIX");
0550             d->suffixName = subTagText(i, "SUFFIX");
0551         }
0552         else if ( tag == "NICKNAME" )
0553             d->nickName = i.text().trimmed();
0554         else if ( tag == "PHOTO" ) {
0555             d->photo = QCA::Base64().stringToArray(subTagText(i, "BINVAL").replace("\n","")).toByteArray();
0556             d->photoURI = subTagText(i, "EXTVAL");
0557         }
0558         else if ( tag == "BDAY" )
0559             d->bday = i.text().trimmed();
0560         else if ( tag == "ADR" ) {
0561             Address a;
0562 
0563             a.home   = hasSubTag(i, "HOME");
0564             a.work   = hasSubTag(i, "WORK");
0565             a.postal = hasSubTag(i, "POSTAL");
0566             a.parcel = hasSubTag(i, "PARCEL");
0567             a.dom    = hasSubTag(i, "DOM");
0568             a.intl   = hasSubTag(i, "INTL");
0569             a.pref   = hasSubTag(i, "PREF");
0570 
0571             a.pobox    = subTagText(i, "POBOX");
0572             a.extaddr  = subTagText(i, "EXTADR");
0573             a.street   = subTagText(i, "STREET");
0574             a.locality = subTagText(i, "LOCALITY");
0575             a.region   = subTagText(i, "REGION");
0576             a.pcode    = subTagText(i, "PCODE");
0577             a.country  = subTagText(i, "CTRY");
0578 
0579             if ( a.country.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
0580                 if ( hasSubTag(i, "COUNTRY") )
0581                     a.country = subTagText(i, "COUNTRY");
0582 
0583             if ( a.extaddr.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
0584                 if ( hasSubTag(i, "EXTADD") )
0585                     a.extaddr = subTagText(i, "EXTADD");
0586 
0587             d->addressList.append ( a );
0588         }
0589         else if ( tag == "LABEL" ) {
0590             Label l;
0591 
0592             l.home   = hasSubTag(i, "HOME");
0593             l.work   = hasSubTag(i, "WORK");
0594             l.postal = hasSubTag(i, "POSTAL");
0595             l.parcel = hasSubTag(i, "PARCEL");
0596             l.dom    = hasSubTag(i, "DOM");
0597             l.intl   = hasSubTag(i, "INTL");
0598             l.pref   = hasSubTag(i, "PREF");
0599 
0600             QDomNode nn = i.firstChild();
0601             for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
0602                 QDomElement ii = nn.toElement();
0603                 if ( ii.isNull() )
0604                     continue;
0605 
0606                 if ( ii.tagName().toUpper() == "LINE" )
0607                     l.lines.append ( ii.text().trimmed() );
0608             }
0609 
0610             d->labelList.append ( l );
0611         }
0612         else if ( tag == "TEL" ) {
0613             Phone p;
0614 
0615             p.home  = hasSubTag(i, "HOME");
0616             p.work  = hasSubTag(i, "WORK");
0617             p.voice = hasSubTag(i, "VOICE");
0618             p.fax   = hasSubTag(i, "FAX");
0619             p.pager = hasSubTag(i, "PAGER");
0620             p.msg   = hasSubTag(i, "MSG");
0621             p.cell  = hasSubTag(i, "CELL");
0622             p.video = hasSubTag(i, "VIDEO");
0623             p.bbs   = hasSubTag(i, "BBS");
0624             p.modem = hasSubTag(i, "MODEM");
0625             p.isdn  = hasSubTag(i, "ISDN");
0626             p.pcs   = hasSubTag(i, "PCS");
0627             p.pref  = hasSubTag(i, "PREF");
0628 
0629             p.number = subTagText(i, "NUMBER");
0630 
0631             if ( p.number.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
0632                 if ( hasSubTag(i, "VOICE") )
0633                     p.number = subTagText(i, "VOICE");
0634 
0635             d->phoneList.append ( p );
0636         }
0637         else if ( tag == "EMAIL" ) {
0638             Email m;
0639 
0640             m.home     = hasSubTag(i, "HOME");
0641             m.work     = hasSubTag(i, "WORK");
0642             m.internet = hasSubTag(i, "INTERNET");
0643             m.x400     = hasSubTag(i, "X400");
0644 
0645             m.userid = subTagText(i, "USERID");
0646 
0647             if ( m.userid.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
0648                 if ( !i.text().isEmpty() )
0649                     m.userid = i.text().trimmed();
0650 
0651             d->emailList.append ( m );
0652         }
0653         else if ( tag == "JABBERID" )
0654             d->jid = i.text().trimmed();
0655         else if ( tag == "MAILER" )
0656             d->mailer = i.text().trimmed();
0657         else if ( tag == "TZ" )
0658             d->timezone = i.text().trimmed();
0659         else if ( tag == "GEO" ) {
0660             d->geo.lat = subTagText(i, "LAT");
0661             d->geo.lon = subTagText(i, "LON");
0662         }
0663         else if ( tag == "TITLE" )
0664             d->title = i.text().trimmed();
0665         else if ( tag == "ROLE" )
0666             d->role = i.text().trimmed();
0667         else if ( tag == "LOGO" ) {
0668             d->logo = QCA::Base64().stringToArray( subTagText(i, "BINVAL").replace("\n","") ).toByteArray();
0669             d->logoURI = subTagText(i, "EXTVAL");
0670         }
0671         else if ( tag == "AGENT" ) {
0672             e = findSubTag(i, "VCARD", &found);
0673             if ( found ) {
0674                 VCard a;
0675                 if ( a.fromXml(e) ) {
0676                     if ( !d->agent )
0677                         d->agent = new VCard;
0678                     *(d->agent) = a;
0679                 }
0680             }
0681 
0682             d->agentURI = subTagText(i, "EXTVAL");
0683         }
0684         else if ( tag == "ORG" ) {
0685             d->org.name = subTagText(i, "ORGNAME");
0686 
0687             QDomNode nn = i.firstChild();
0688             for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
0689                 QDomElement ii = nn.toElement();
0690                 if ( ii.isNull() )
0691                     continue;
0692 
0693                 if ( ii.tagName().toUpper() == "ORGUNIT" )
0694                     d->org.unit.append( ii.text().trimmed() );
0695             }
0696         }
0697         else if ( tag == "CATEGORIES") {
0698             QDomNode nn = i.firstChild();
0699             for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
0700                 QDomElement ee = nn.toElement();
0701                 if ( ee.isNull() )
0702                     continue;
0703 
0704                 if ( ee.tagName().toUpper() == "KEYWORD" )
0705                     d->categories << ee.text().trimmed();
0706             }
0707         }
0708         else if ( tag == "NOTE" )
0709             d->note = i.text().trimmed();
0710         else if ( tag == "PRODID" )
0711             d->prodId = i.text().trimmed();
0712         else if ( tag == "REV" )
0713             d->rev = i.text().trimmed();
0714         else if ( tag == "SORT-STRING" )
0715             d->sortString = i.text().trimmed();
0716         else if ( tag == "SOUND" ) {
0717             d->sound = QCA::Base64().stringToArray( subTagText(i, "BINVAL").replace("\n","") ).toByteArray();
0718             d->soundURI      = subTagText(i, "EXTVAL");
0719             d->soundPhonetic = subTagText(i, "PHONETIC");
0720         }
0721         else if ( tag == "UID" )
0722             d->uid = i.text().trimmed();
0723         else if ( tag == "URL")
0724             d->url = i.text().trimmed();
0725         else if ( tag == "DESC" )
0726             d->desc = i.text().trimmed();
0727         else if ( tag == "CLASS" ) {
0728             if ( hasSubTag(i, "PUBLIC") )
0729                 d->privacyClass = pcPublic;
0730             else if ( hasSubTag(i, "PRIVATE") )
0731                 d->privacyClass = pcPrivate;
0732             else if ( hasSubTag(i, "CONFIDENTIAL") )
0733                 d->privacyClass = pcConfidential;
0734         }
0735         else if ( tag == "KEY" ) {
0736             // TODO: Justin, please check out this code
0737             e = findSubTag(i, "TYPE", &found);
0738             QString type = "text/plain";
0739             if ( found )
0740                 type = e.text().trimmed();
0741 
0742             e = findSubTag(i, "CRED", &found );
0743             if ( !found )
0744                 e = findSubTag(i, "BINVAL", &found); // case for very clever clients ;-)
0745 
0746             if ( found )
0747                 d->key = e.text().toUtf8(); // FIXME
0748         }
0749     }
0750 
0751     return true;
0752 }
0753 
0754 bool VCard::isEmpty() const
0755 {
0756     return d->isEmpty();
0757 }
0758 
0759 // Some constructors
0760 
0761 VCard::Address::Address()
0762 {
0763     home = work = postal = parcel = dom = intl = pref = false;
0764 }
0765 
0766 VCard::Label::Label()
0767 {
0768     home = work = postal = parcel = dom = intl = pref = false;
0769 }
0770 
0771 VCard::Phone::Phone()
0772 {
0773     home = work = voice = fax = pager = msg = cell = video = bbs = modem = isdn = pcs = pref = false;
0774 }
0775 
0776 VCard::Email::Email()
0777 {
0778     home = work = internet = x400 = false;
0779 }
0780 
0781 VCard::Geo::Geo()
0782 {
0783 }
0784 
0785 VCard::Org::Org()
0786 {
0787 }
0788 
0789 // vCard properties...
0790 
0791 const QString &VCard::version() const
0792 {
0793     return d->version;
0794 }
0795 
0796 void VCard::setVersion(const QString &v)
0797 {
0798     d->version = v;
0799 }
0800 
0801 const QString &VCard::fullName() const
0802 {
0803     return d->fullName;
0804 }
0805 
0806 void VCard::setFullName(const QString &n)
0807 {
0808     d->fullName = n;
0809 }
0810 
0811 const QString &VCard::familyName() const
0812 {
0813     return d->familyName;
0814 }
0815 
0816 void VCard::setFamilyName(const QString &n)
0817 {
0818     d->familyName = n;
0819 }
0820 
0821 const QString &VCard::givenName() const
0822 {
0823     return d->givenName;
0824 }
0825 
0826 void VCard::setGivenName(const QString &n)
0827 {
0828     d->givenName = n;
0829 }
0830 
0831 const QString &VCard::middleName() const
0832 {
0833     return d->middleName;
0834 }
0835 
0836 void VCard::setMiddleName(const QString &n)
0837 {
0838     d->middleName = n;
0839 }
0840 
0841 const QString &VCard::prefixName() const
0842 {
0843     return d->prefixName;
0844 }
0845 
0846 void VCard::setPrefixName(const QString &p)
0847 {
0848     d->prefixName = p;
0849 }
0850 
0851 const QString &VCard::suffixName() const
0852 {
0853     return d->suffixName;
0854 }
0855 
0856 void VCard::setSuffixName(const QString &s)
0857 {
0858     d->suffixName = s;
0859 }
0860 
0861 const QString &VCard::nickName() const
0862 {
0863     return d->nickName;
0864 }
0865 
0866 void VCard::setNickName(const QString &n)
0867 {
0868     d->nickName = n;
0869 }
0870 
0871 const QByteArray &VCard::photo() const
0872 {
0873     return d->photo;
0874 }
0875 
0876 void VCard::setPhoto(const QByteArray &i)
0877 {
0878     d->photo = i;
0879 }
0880 
0881 const QString &VCard::photoURI() const
0882 {
0883     return d->photoURI;
0884 }
0885 
0886 void VCard::setPhotoURI(const QString &p)
0887 {
0888     d->photoURI = p;
0889 }
0890 
0891 const QDate VCard::bday() const
0892 {
0893     return QDate::fromString(d->bday);
0894 }
0895 
0896 void VCard::setBday(const QDate &date)
0897 {
0898     d->bday = date.toString();
0899 }
0900 
0901 const QString &VCard::bdayStr() const
0902 {
0903     return d->bday;
0904 }
0905 
0906 void VCard::setBdayStr(const QString &date)
0907 {
0908     d->bday = date;
0909 }
0910 
0911 const VCard::AddressList &VCard::addressList() const
0912 {
0913     return d->addressList;
0914 }
0915 
0916 void VCard::setAddressList(const VCard::AddressList &a)
0917 {
0918     d->addressList = a;
0919 }
0920 
0921 const VCard::LabelList &VCard::labelList() const
0922 {
0923     return d->labelList;
0924 }
0925 
0926 void VCard::setLabelList(const VCard::LabelList &l)
0927 {
0928     d->labelList = l;
0929 }
0930 
0931 const VCard::PhoneList &VCard::phoneList() const
0932 {
0933     return d->phoneList;
0934 }
0935 
0936 void VCard::setPhoneList(const VCard::PhoneList &p)
0937 {
0938     d->phoneList = p;
0939 }
0940 
0941 const VCard::EmailList &VCard::emailList() const
0942 {
0943     return d->emailList;
0944 }
0945 
0946 void VCard::setEmailList(const VCard::EmailList &e)
0947 {
0948     d->emailList = e;
0949 }
0950 
0951 const QString &VCard::jid() const
0952 {
0953     return d->jid;
0954 }
0955 
0956 void VCard::setJid(const QString &j)
0957 {
0958     d->jid = j;
0959 }
0960 
0961 const QString &VCard::mailer() const
0962 {
0963     return d->mailer;
0964 }
0965 
0966 void VCard::setMailer(const QString &m)
0967 {
0968     d->mailer = m;
0969 }
0970 
0971 const QString &VCard::timezone() const
0972 {
0973     return d->timezone;
0974 }
0975 
0976 void VCard::setTimezone(const QString &t)
0977 {
0978     d->timezone = t;
0979 }
0980 
0981 const VCard::Geo &VCard::geo() const
0982 {
0983     return d->geo;
0984 }
0985 
0986 void VCard::setGeo(const VCard::Geo &g)
0987 {
0988     d->geo = g;
0989 }
0990 
0991 const QString &VCard::title() const
0992 {
0993     return d->title;
0994 }
0995 
0996 void VCard::setTitle(const QString &t)
0997 {
0998     d->title = t;
0999 }
1000 
1001 const QString &VCard::role() const
1002 {
1003     return d->role;
1004 }
1005 
1006 void VCard::setRole(const QString &r)
1007 {
1008     d->role = r;
1009 }
1010 
1011 const QByteArray &VCard::logo() const
1012 {
1013     return d->logo;
1014 }
1015 
1016 void VCard::setLogo(const QByteArray &i)
1017 {
1018     d->logo = i;
1019 }
1020 
1021 const QString &VCard::logoURI() const
1022 {
1023     return d->logoURI;
1024 }
1025 
1026 void VCard::setLogoURI(const QString &l)
1027 {
1028     d->logoURI = l;
1029 }
1030 
1031 const VCard *VCard::agent() const
1032 {
1033     return d->agent;
1034 }
1035 
1036 void VCard::setAgent(const VCard &v)
1037 {
1038     if ( !d->agent )
1039         d->agent = new VCard;
1040     *(d->agent) = v;
1041 }
1042 
1043 const QString VCard::agentURI() const
1044 {
1045     return d->agentURI;
1046 }
1047 
1048 void VCard::setAgentURI(const QString &a)
1049 {
1050     d->agentURI = a;
1051 }
1052 
1053 const VCard::Org &VCard::org() const
1054 {
1055     return d->org;
1056 }
1057 
1058 void VCard::setOrg(const VCard::Org &o)
1059 {
1060     d->org = o;
1061 }
1062 
1063 const QStringList &VCard::categories() const
1064 {
1065     return d->categories;
1066 }
1067 
1068 void VCard::setCategories(const QStringList &c)
1069 {
1070     d->categories = c;
1071 }
1072 
1073 const QString &VCard::note() const
1074 {
1075     return d->note;
1076 }
1077 
1078 void VCard::setNote(const QString &n)
1079 {
1080     d->note = n;
1081 }
1082 
1083 const QString &VCard::prodId() const
1084 {
1085     return d->prodId;
1086 }
1087 
1088 void VCard::setProdId(const QString &p)
1089 {
1090     d->prodId = p;
1091 }
1092 
1093 const QString &VCard::rev() const
1094 {
1095     return d->rev;
1096 }
1097 
1098 void VCard::setRev(const QString &r)
1099 {
1100     d->rev = r;
1101 }
1102 
1103 const QString &VCard::sortString() const
1104 {
1105     return d->sortString;
1106 }
1107 
1108 void VCard::setSortString(const QString &s)
1109 {
1110     d->sortString = s;
1111 }
1112 
1113 const QByteArray &VCard::sound() const
1114 {
1115     return d->sound;
1116 }
1117 
1118 void VCard::setSound(const QByteArray &s)
1119 {
1120     d->sound = s;
1121 }
1122 
1123 const QString &VCard::soundURI() const
1124 {
1125     return d->soundURI;
1126 }
1127 
1128 void VCard::setSoundURI(const QString &s)
1129 {
1130     d->soundURI = s;
1131 }
1132 
1133 const QString &VCard::soundPhonetic() const
1134 {
1135     return d->soundPhonetic;
1136 }
1137 
1138 void VCard::setSoundPhonetic(const QString &s)
1139 {
1140     d->soundPhonetic = s;
1141 }
1142 
1143 const QString &VCard::uid() const
1144 {
1145     return d->uid;
1146 }
1147 
1148 void VCard::setUid(const QString &u)
1149 {
1150     d->uid = u;
1151 }
1152 
1153 const QString &VCard::url() const
1154 {
1155     return d->url;
1156 }
1157 
1158 void VCard::setUrl(const QString &u)
1159 {
1160     d->url = u;
1161 }
1162 
1163 const QString &VCard::desc() const
1164 {
1165     return d->desc;
1166 }
1167 
1168 void VCard::setDesc(const QString &desc)
1169 {
1170     d->desc = desc;
1171 }
1172 
1173 const VCard::PrivacyClass &VCard::privacyClass() const
1174 {
1175     return d->privacyClass;
1176 }
1177 
1178 void VCard::setPrivacyClass(const VCard::PrivacyClass &c)
1179 {
1180     d->privacyClass = c;
1181 }
1182 
1183 const QByteArray &VCard::key() const
1184 {
1185     return d->key;
1186 }
1187 
1188 void VCard::setKey(const QByteArray &k)
1189 {
1190     d->key = k;
1191 }