1 /*
2  * xmpp_vcard.cpp - classes for handling vCards
3  * Copyright (C) 2003  Michail Pishchagin
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * either version 2
9    of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21 
22 #include "xmpp_vcard.h"
23 
24 #include <QDateTime>
25 
26 #include <QImage> // needed for image format recognition
27 #include <QBuffer>
28 #include <QImageReader>
29 #include <QImageWriter>
30 #include <QtCrypto>
31 
32 #include "xmpp_xmlcommon.h"
33 
34 using namespace XMPP;
35 using namespace XMLHelper;
36 
37 //----------------------------------------------------------------------------
38 // VCard
39 //----------------------------------------------------------------------------
image2type(const QByteArray & ba)40 QString image2type(const QByteArray &ba)
41 {
42 	QBuffer buf;
43 	buf.setData(ba);
44 	buf.open(QIODevice::ReadOnly);
45 	QString format = QImageReader::imageFormat( &buf );
46 
47 	// TODO: add more formats
48 	if ( format.toUpper() == "PNG" || format == "PsiPNG" )
49 		return "image/png";
50 	if ( format.toUpper() == "MNG" )
51 		return "video/x-mng";
52 	if ( format.toUpper() == "GIF" )
53 		return "image/gif";
54 	if ( format.toUpper() == "BMP" )
55 		return "image/bmp";
56 	if ( format.toUpper() == "XPM" )
57 		return "image/x-xpm";
58 	if ( format.toUpper() == "SVG" )
59 		return "image/svg+xml";
60 	if ( format.toUpper() == "JPEG" )
61 		return "image/jpeg";
62 
63 	const QByteArray formatAscii = format.toLatin1();
64 	qWarning("WARNING! VCard::image2type: unknown format = '%s'", format.isNull() ? "UNKNOWN" : formatAscii.constData());
65 
66 	return "image/unknown";
67 }
68 
69 // Long lines of encoded binary data SHOULD BE folded to 75 characters using the folding method defined in [MIME-DIR].
foldString(const QString & s)70 static QString foldString(const QString &s)
71 {
72 	QString ret;
73 
74 	for (int i = 0; i < (int)s.length(); i++) {
75 		if ( !(i % 75) )
76 			ret += '\n';
77 		ret += s[i];
78 	}
79 
80 	return ret;
81 }
82 
83 class VCard::Private
84 {
85 public:
86 	Private();
87 	~Private();
88 
89 	QString version;
90 	QString fullName;
91 	QString familyName, givenName, middleName, prefixName, suffixName;
92 	QString nickName;
93 
94 	QByteArray photo;
95 	QString photoURI;
96 
97 	QString bday;
98 	AddressList addressList;
99 	LabelList labelList;
100 	PhoneList phoneList;
101 	EmailList emailList;
102 	QString jid;
103 	QString mailer;
104 	QString timezone;
105 	Geo geo;
106 	QString title;
107 	QString role;
108 
109 	QByteArray logo;
110 	QString logoURI;
111 
112 	VCard *agent;
113 	QString agentURI;
114 
115 	Org org;
116 	QStringList categories;
117 	QString note;
118 	QString prodId;
119 	QString rev;
120 	QString sortString;
121 
122 	QByteArray sound;
123 	QString soundURI, soundPhonetic;
124 
125 	QString uid;
126 	QString url;
127 	QString desc;
128 	PrivacyClass privacyClass;
129 	QByteArray key;
130 
131 	bool isEmpty();
132 };
133 
Private()134 VCard::Private::Private()
135 {
136 	privacyClass = pcNone;
137 	agent = 0;
138 }
139 
~Private()140 VCard::Private::~Private()
141 {
142 	delete agent;
143 }
144 
isEmpty()145 bool VCard::Private::isEmpty()
146 {
147 	if (	!version.isEmpty() ||
148 		!fullName.isEmpty() ||
149 		!familyName.isEmpty() || !givenName.isEmpty() || !middleName.isEmpty() || !prefixName.isEmpty() || !suffixName.isEmpty() ||
150 		!nickName.isEmpty() ||
151 		!photo.isEmpty() || !photoURI.isEmpty() ||
152 		!bday.isEmpty() ||
153 		!addressList.isEmpty() ||
154 		!labelList.isEmpty() ||
155 		!phoneList.isEmpty() ||
156 		!emailList.isEmpty() ||
157 		!jid.isEmpty() ||
158 		!mailer.isEmpty() ||
159 		!timezone.isEmpty() ||
160 		!geo.lat.isEmpty() || !geo.lon.isEmpty() ||
161 		!title.isEmpty() ||
162 		!role.isEmpty() ||
163 		!logo.isEmpty() || !logoURI.isEmpty() ||
164 		(agent && !agent->isEmpty()) || !agentURI.isEmpty() ||
165 		!org.name.isEmpty() || !org.unit.isEmpty() ||
166 		!categories.isEmpty() ||
167 		!note.isEmpty() ||
168 		!prodId.isEmpty() ||
169 		!rev.isEmpty() ||
170 		!sortString.isEmpty() ||
171 		!sound.isEmpty() || !soundURI.isEmpty() || !soundPhonetic.isEmpty() ||
172 		!uid.isEmpty() ||
173 		!url.isEmpty() ||
174 		!desc.isEmpty() ||
175 		(privacyClass != pcNone) ||
176 		!key.isEmpty() )
177 	{
178 		return false;
179 	}
180 	return true;
181 }
182 
VCard()183 VCard::VCard()
184 {
185 	d = new Private;
186 }
187 
VCard(const VCard & from)188 VCard::VCard(const VCard &from)
189 {
190 	d = new Private;
191 	*this = from;
192 }
193 
operator =(const VCard & from)194 VCard & VCard::operator=(const VCard &from)
195 {
196 	if(d->agent) {
197 		delete d->agent;
198 		d->agent = 0;
199 	}
200 
201 	*d = *from.d;
202 
203 	if(from.d->agent) {
204 		// dup the agent
205 		d->agent = new VCard(*from.d->agent);
206 	}
207 
208 	return *this;
209 }
210 
~VCard()211 VCard::~VCard()
212 {
213 	delete d;
214 }
215 
toXml(QDomDocument * doc) const216 QDomElement VCard::toXml(QDomDocument *doc) const
217 {
218 	QDomElement v = doc->createElement("vCard");
219 	v.setAttribute("version", "2.0");
220 	v.setAttribute("prodid", "-//HandGen//NONSGML vGen v1.0//EN");
221 	v.setAttribute("xmlns", "vcard-temp");
222 
223 	if ( !d->version.isEmpty() )
224 		v.appendChild( textTag(doc, "VERSION",	d->version) );
225 	if ( !d->fullName.isEmpty() )
226 		v.appendChild( textTag(doc, "FN",	d->fullName) );
227 
228 	if ( !d->familyName.isEmpty() || !d->givenName.isEmpty() || !d->middleName.isEmpty() ||
229 	     !d->prefixName.isEmpty() || !d->suffixName.isEmpty() ) {
230 		QDomElement w = doc->createElement("N");
231 
232 		if ( !d->familyName.isEmpty() )
233 			w.appendChild( textTag(doc, "FAMILY",	d->familyName) );
234 		if ( !d->givenName.isEmpty() )
235 			w.appendChild( textTag(doc, "GIVEN",	d->givenName) );
236 		if ( !d->middleName.isEmpty() )
237 			w.appendChild( textTag(doc, "MIDDLE",	d->middleName) );
238 		if ( !d->prefixName.isEmpty() )
239 			w.appendChild( textTag(doc, "PREFIX",	d->prefixName) );
240 		if ( !d->suffixName.isEmpty() )
241 			w.appendChild( textTag(doc, "SUFFIX",	d->suffixName) );
242 
243 		v.appendChild(w);
244 	}
245 
246 	if ( !d->nickName.isEmpty() )
247 		v.appendChild( textTag(doc, "NICKNAME",	d->nickName) );
248 
249 	if ( !d->photo.isEmpty() || !d->photoURI.isEmpty() ) {
250 		QDomElement w = doc->createElement("PHOTO");
251 
252 		if ( !d->photo.isEmpty() ) {
253 			w.appendChild( textTag(doc, "TYPE",	image2type(d->photo)) );
254 			w.appendChild( textTag(doc, "BINVAL",	foldString( QCA::Base64().arrayToString(d->photo)) ) );
255 		}
256 		else if ( !d->photoURI.isEmpty() )
257 			w.appendChild( textTag(doc, "EXTVAL",	d->photoURI) );
258 
259 		v.appendChild(w);
260 	}
261 
262 	if ( !d->bday.isEmpty() )
263 		v.appendChild( textTag(doc, "BDAY",	d->bday) );
264 
265 	if ( !d->addressList.isEmpty() ) {
266 		AddressList::Iterator it = d->addressList.begin();
267 		for ( ; it != d->addressList.end(); ++it ) {
268 			QDomElement w = doc->createElement("ADR");
269 			Address a = *it;
270 
271 			if ( a.home )
272 				w.appendChild( emptyTag(doc, "HOME") );
273 			if ( a.work )
274 				w.appendChild( emptyTag(doc, "WORK") );
275 			if ( a.postal )
276 				w.appendChild( emptyTag(doc, "POSTAL") );
277 			if ( a.parcel )
278 				w.appendChild( emptyTag(doc, "PARCEL") );
279 			if ( a.dom )
280 				w.appendChild( emptyTag(doc, "DOM") );
281 			if ( a.intl )
282 				w.appendChild( emptyTag(doc, "INTL") );
283 			if ( a.pref )
284 				w.appendChild( emptyTag(doc, "PREF") );
285 
286 			if ( !a.pobox.isEmpty() )
287 				w.appendChild( textTag(doc, "POBOX",	a.pobox) );
288 			if ( !a.extaddr.isEmpty() )
289 				w.appendChild( textTag(doc, "EXTADR",	a.extaddr) );
290 			if ( !a.street.isEmpty() )
291 				w.appendChild( textTag(doc, "STREET",	a.street) );
292 			if ( !a.locality.isEmpty() )
293 				w.appendChild( textTag(doc, "LOCALITY",	a.locality) );
294 			if ( !a.region.isEmpty() )
295 				w.appendChild( textTag(doc, "REGION",	a.region) );
296 			if ( !a.pcode.isEmpty() )
297 				w.appendChild( textTag(doc, "PCODE",	a.pcode) );
298 			if ( !a.country.isEmpty() )
299 				w.appendChild( textTag(doc, "CTRY",	a.country) );
300 
301 			v.appendChild(w);
302 		}
303 	}
304 
305 	if ( !d->labelList.isEmpty() ) {
306 		LabelList::Iterator it = d->labelList.begin();
307 		for ( ; it != d->labelList.end(); ++it ) {
308 			QDomElement w = doc->createElement("LABEL");
309 			Label l = *it;
310 
311 			if ( l.home )
312 				w.appendChild( emptyTag(doc, "HOME") );
313 			if ( l.work )
314 				w.appendChild( emptyTag(doc, "WORK") );
315 			if ( l.postal )
316 				w.appendChild( emptyTag(doc, "POSTAL") );
317 			if ( l.parcel )
318 				w.appendChild( emptyTag(doc, "PARCEL") );
319 			if ( l.dom )
320 				w.appendChild( emptyTag(doc, "DOM") );
321 			if ( l.intl )
322 				w.appendChild( emptyTag(doc, "INTL") );
323 			if ( l.pref )
324 				w.appendChild( emptyTag(doc, "PREF") );
325 
326 			if ( !l.lines.isEmpty() ) {
327 				QStringList::Iterator it = l.lines.begin();
328 				for ( ; it != l.lines.end(); ++it )
329 					w.appendChild( textTag(doc, "LINE", *it) );
330 			}
331 
332 			v.appendChild(w);
333 		}
334 	}
335 
336 	if ( !d->phoneList.isEmpty() ) {
337 		PhoneList::Iterator it = d->phoneList.begin();
338 		for ( ; it != d->phoneList.end(); ++it ) {
339 			QDomElement w = doc->createElement("TEL");
340 			Phone p = *it;
341 
342 			if ( p.home )
343 				w.appendChild( emptyTag(doc, "HOME") );
344 			if ( p.work )
345 				w.appendChild( emptyTag(doc, "WORK") );
346 			if ( p.voice )
347 				w.appendChild( emptyTag(doc, "VOICE") );
348 			if ( p.fax )
349 				w.appendChild( emptyTag(doc, "FAX") );
350 			if ( p.pager )
351 				w.appendChild( emptyTag(doc, "PAGER") );
352 			if ( p.msg )
353 				w.appendChild( emptyTag(doc, "MSG") );
354 			if ( p.cell )
355 				w.appendChild( emptyTag(doc, "CELL") );
356 			if ( p.video )
357 				w.appendChild( emptyTag(doc, "VIDEO") );
358 			if ( p.bbs )
359 				w.appendChild( emptyTag(doc, "BBS") );
360 			if ( p.modem )
361 				w.appendChild( emptyTag(doc, "MODEM") );
362 			if ( p.isdn )
363 				w.appendChild( emptyTag(doc, "ISDN") );
364 			if ( p.pcs )
365 				w.appendChild( emptyTag(doc, "PCS") );
366 			if ( p.pref )
367 				w.appendChild( emptyTag(doc, "PREF") );
368 
369 			if ( !p.number.isEmpty() )
370 				w.appendChild( textTag(doc, "NUMBER",	p.number) );
371 
372 			v.appendChild(w);
373 		}
374 	}
375 
376 	if ( !d->emailList.isEmpty() ) {
377 		EmailList::Iterator it = d->emailList.begin();
378 		for ( ; it != d->emailList.end(); ++it ) {
379 			QDomElement w = doc->createElement("EMAIL");
380 			Email e = *it;
381 
382 			if ( e.home )
383 				w.appendChild( emptyTag(doc, "HOME") );
384 			if ( e.work )
385 				w.appendChild( emptyTag(doc, "WORK") );
386 			if ( e.internet )
387 				w.appendChild( emptyTag(doc, "INTERNET") );
388 			if ( e.x400 )
389 				w.appendChild( emptyTag(doc, "X400") );
390 
391 			if ( !e.userid.isEmpty() )
392 				w.appendChild( textTag(doc, "USERID",	e.userid) );
393 
394 			v.appendChild(w);
395 		}
396 	}
397 
398 	if ( !d->jid.isEmpty() )
399 		v.appendChild( textTag(doc, "JABBERID",	d->jid) );
400 	if ( !d->mailer.isEmpty() )
401 		v.appendChild( textTag(doc, "MAILER",	d->mailer) );
402 	if ( !d->timezone.isEmpty() )
403 		v.appendChild( textTag(doc, "TZ",	d->timezone) );
404 
405 	if ( !d->geo.lat.isEmpty() || !d->geo.lon.isEmpty() ) {
406 		QDomElement w = doc->createElement("GEO");
407 
408 		if ( !d->geo.lat.isEmpty() )
409 			w.appendChild( textTag(doc, "LAT",	d->geo.lat) );
410 		if ( !d->geo.lon.isEmpty() )
411 			w.appendChild( textTag(doc, "LON",	d->geo.lon));
412 
413 		v.appendChild(w);
414 	}
415 
416 	if ( !d->title.isEmpty() )
417 		v.appendChild( textTag(doc, "TITLE",	d->title) );
418 	if ( !d->role.isEmpty() )
419 		v.appendChild( textTag(doc, "ROLE",	d->role) );
420 
421 	if ( !d->logo.isEmpty() || !d->logoURI.isEmpty() ) {
422 		QDomElement w = doc->createElement("LOGO");
423 
424 		if ( !d->logo.isEmpty() ) {
425 			w.appendChild( textTag(doc, "TYPE",	image2type(d->logo)) );
426 			w.appendChild( textTag(doc, "BINVAL",	foldString( QCA::Base64().arrayToString(d->logo)) ) );
427 		}
428 		else if ( !d->logoURI.isEmpty() )
429 			w.appendChild( textTag(doc, "EXTVAL",	d->logoURI) );
430 
431 		v.appendChild(w);
432 	}
433 
434 	if ( !d->agentURI.isEmpty() || (d->agent && d->agent->isEmpty()) ) {
435 		QDomElement w = doc->createElement("AGENT");
436 
437 		if ( d->agent && !d->agent->isEmpty() )
438 			w.appendChild( d->agent->toXml(doc) );
439 		else if ( !d->agentURI.isEmpty() )
440 			w.appendChild( textTag(doc, "EXTVAL",	d->agentURI) );
441 
442 		v.appendChild(w);
443 	}
444 
445 	if ( !d->org.name.isEmpty() || !d->org.unit.isEmpty() ) {
446 		QDomElement w = doc->createElement("ORG");
447 
448 		if ( !d->org.name.isEmpty() )
449 			w.appendChild( textTag(doc, "ORGNAME",	d->org.name) );
450 
451 		if ( !d->org.unit.isEmpty() ) {
452 			QStringList::Iterator it = d->org.unit.begin();
453 			for ( ; it != d->org.unit.end(); ++it )
454 				w.appendChild( textTag(doc, "ORGUNIT",	*it) );
455 		}
456 
457 		v.appendChild(w);
458 	}
459 
460 	if ( !d->categories.isEmpty() ) {
461 		QDomElement w = doc->createElement("CATEGORIES");
462 
463 		QStringList::Iterator it = d->categories.begin();
464 		for ( ; it != d->categories.end(); ++it )
465 			w.appendChild( textTag(doc, "KEYWORD", *it) );
466 
467 		v.appendChild(w);
468 	}
469 
470 	if ( !d->note.isEmpty() )
471 		v.appendChild( textTag(doc, "NOTE",	d->note) );
472 	if ( !d->prodId.isEmpty() )
473 		v.appendChild( textTag(doc, "PRODID",	d->prodId) );
474 	if ( !d->rev.isEmpty() )
475 		v.appendChild( textTag(doc, "REV",	d->rev) );
476 	if ( !d->sortString.isEmpty() )
477 		v.appendChild( textTag(doc, "SORT-STRING",	d->sortString) );
478 
479 	if ( !d->sound.isEmpty() || !d->soundURI.isEmpty() || !d->soundPhonetic.isEmpty() ) {
480 		QDomElement w = doc->createElement("SOUND");
481 
482 		if ( !d->sound.isEmpty() )
483 			w.appendChild( textTag(doc, "BINVAL",	foldString( QCA::Base64().arrayToString(d->sound)) ) );
484 		else if ( !d->soundURI.isEmpty() )
485 			w.appendChild( textTag(doc, "EXTVAL",	d->soundURI) );
486 		else if ( !d->soundPhonetic.isEmpty() )
487 			w.appendChild( textTag(doc, "PHONETIC",	d->soundPhonetic) );
488 
489 		v.appendChild(w);
490 	}
491 
492 	if ( !d->uid.isEmpty() )
493 		v.appendChild( textTag(doc, "UID",	d->uid) );
494 	if ( !d->url.isEmpty() )
495 		v.appendChild( textTag(doc, "URL",	d->url) );
496 	if ( !d->desc.isEmpty() )
497 		v.appendChild( textTag(doc, "DESC",	d->desc) );
498 
499 	if ( d->privacyClass != pcNone ) {
500 		QDomElement w = doc->createElement("CLASS");
501 
502 		if ( d->privacyClass == pcPublic )
503 			w.appendChild( emptyTag(doc, "PUBLIC") );
504 		else if ( d->privacyClass == pcPrivate )
505 			w.appendChild( emptyTag(doc, "PRIVATE") );
506 		else if ( d->privacyClass == pcConfidential )
507 			w.appendChild( emptyTag(doc, "CONFIDENTIAL") );
508 
509 		v.appendChild(w);
510 	}
511 
512 	if ( !d->key.isEmpty() ) {
513 		QDomElement w = doc->createElement("KEY");
514 
515 		// TODO: Justin, please check out this code
516 		w.appendChild( textTag(doc, "TYPE", "text/plain")); // FIXME
517 		w.appendChild( textTag(doc, "CRED", QString::fromUtf8(d->key)) ); // FIXME
518 
519 		v.appendChild(w);
520 	}
521 
522 	return v;
523 }
524 
fromXml(const QDomElement & q)525 bool VCard::fromXml(const QDomElement &q)
526 {
527 	if ( q.tagName().toUpper() != "VCARD" )
528 		return false;
529 
530 	QDomNode n = q.firstChild();
531 	for ( ; !n.isNull(); n = n.nextSibling() ) {
532 		QDomElement i = n.toElement();
533 		if ( i.isNull() )
534 			continue;
535 
536 		QString tag = i.tagName().toUpper();
537 
538 		bool found;
539 		QDomElement e;
540 
541 		if ( tag == "VERSION" )
542 			d->version = i.text().trimmed();
543 		else if ( tag == "FN" )
544 			d->fullName = i.text().trimmed();
545 		else if ( tag == "N" ) {
546 			d->familyName = subTagText(i, "FAMILY");
547 			d->givenName  = subTagText(i, "GIVEN");
548 			d->middleName = subTagText(i, "MIDDLE");
549 			d->prefixName = subTagText(i, "PREFIX");
550 			d->suffixName = subTagText(i, "SUFFIX");
551 		}
552 		else if ( tag == "NICKNAME" )
553 			d->nickName = i.text().trimmed();
554 		else if ( tag == "PHOTO" ) {
555 			d->photo = QCA::Base64().stringToArray(subTagText(i, "BINVAL").replace("\n","")).toByteArray();
556 			d->photoURI = subTagText(i, "EXTVAL");
557 		}
558 		else if ( tag == "BDAY" )
559 			d->bday = i.text().trimmed();
560 		else if ( tag == "ADR" ) {
561 			Address a;
562 
563 			a.home   = hasSubTag(i, "HOME");
564 			a.work   = hasSubTag(i, "WORK");
565 			a.postal = hasSubTag(i, "POSTAL");
566 			a.parcel = hasSubTag(i, "PARCEL");
567 			a.dom    = hasSubTag(i, "DOM");
568 			a.intl   = hasSubTag(i, "INTL");
569 			a.pref   = hasSubTag(i, "PREF");
570 
571 			a.pobox    = subTagText(i, "POBOX");
572 			a.extaddr  = subTagText(i, "EXTADR");
573 			a.street   = subTagText(i, "STREET");
574 			a.locality = subTagText(i, "LOCALITY");
575 			a.region   = subTagText(i, "REGION");
576 			a.pcode    = subTagText(i, "PCODE");
577 			a.country  = subTagText(i, "CTRY");
578 
579 			if ( a.country.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
580 				if ( hasSubTag(i, "COUNTRY") )
581 					a.country = subTagText(i, "COUNTRY");
582 
583 			if ( a.extaddr.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
584 				if ( hasSubTag(i, "EXTADD") )
585 					a.extaddr = subTagText(i, "EXTADD");
586 
587 			d->addressList.append ( a );
588 		}
589 		else if ( tag == "LABEL" ) {
590 			Label l;
591 
592 			l.home   = hasSubTag(i, "HOME");
593 			l.work   = hasSubTag(i, "WORK");
594 			l.postal = hasSubTag(i, "POSTAL");
595 			l.parcel = hasSubTag(i, "PARCEL");
596 			l.dom    = hasSubTag(i, "DOM");
597 			l.intl   = hasSubTag(i, "INTL");
598 			l.pref   = hasSubTag(i, "PREF");
599 
600 			QDomNode nn = i.firstChild();
601 			for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
602 				QDomElement ii = nn.toElement();
603 				if ( ii.isNull() )
604 					continue;
605 
606 				if ( ii.tagName().toUpper() == "LINE" )
607 					l.lines.append ( ii.text().trimmed() );
608 			}
609 
610 			d->labelList.append ( l );
611 		}
612 		else if ( tag == "TEL" ) {
613 			Phone p;
614 
615 			p.home  = hasSubTag(i, "HOME");
616 			p.work  = hasSubTag(i, "WORK");
617 			p.voice = hasSubTag(i, "VOICE");
618 			p.fax   = hasSubTag(i, "FAX");
619 			p.pager = hasSubTag(i, "PAGER");
620 			p.msg   = hasSubTag(i, "MSG");
621 			p.cell  = hasSubTag(i, "CELL");
622 			p.video = hasSubTag(i, "VIDEO");
623 			p.bbs   = hasSubTag(i, "BBS");
624 			p.modem = hasSubTag(i, "MODEM");
625 			p.isdn  = hasSubTag(i, "ISDN");
626 			p.pcs   = hasSubTag(i, "PCS");
627 			p.pref  = hasSubTag(i, "PREF");
628 
629 			p.number = subTagText(i, "NUMBER");
630 
631 			if ( p.number.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
632 				if ( hasSubTag(i, "VOICE") )
633 					p.number = subTagText(i, "VOICE");
634 
635 			d->phoneList.append ( p );
636 		}
637 		else if ( tag == "EMAIL" ) {
638 			Email m;
639 
640 			m.home     = hasSubTag(i, "HOME");
641 			m.work     = hasSubTag(i, "WORK");
642 			m.internet = hasSubTag(i, "INTERNET");
643 			m.x400     = hasSubTag(i, "X400");
644 
645 			m.userid = subTagText(i, "USERID");
646 
647 			if ( m.userid.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
648 				if ( !i.text().isEmpty() )
649 					m.userid = i.text().trimmed();
650 
651 			d->emailList.append ( m );
652 		}
653 		else if ( tag == "JABBERID" )
654 			d->jid = i.text().trimmed();
655 		else if ( tag == "MAILER" )
656 			d->mailer = i.text().trimmed();
657 		else if ( tag == "TZ" )
658 			d->timezone = i.text().trimmed();
659 		else if ( tag == "GEO" ) {
660 			d->geo.lat = subTagText(i, "LAT");
661 			d->geo.lon = subTagText(i, "LON");
662 		}
663 		else if ( tag == "TITLE" )
664 			d->title = i.text().trimmed();
665 		else if ( tag == "ROLE" )
666 			d->role = i.text().trimmed();
667 		else if ( tag == "LOGO" ) {
668 			d->logo = QCA::Base64().stringToArray( subTagText(i, "BINVAL").replace("\n","") ).toByteArray();
669 			d->logoURI = subTagText(i, "EXTVAL");
670 		}
671 		else if ( tag == "AGENT" ) {
672 			e = findSubTag(i, "VCARD", &found);
673 			if ( found ) {
674 				VCard a;
675 				if ( a.fromXml(e) ) {
676 					if ( !d->agent )
677 						d->agent = new VCard;
678 					*(d->agent) = a;
679 				}
680 			}
681 
682 			d->agentURI = subTagText(i, "EXTVAL");
683 		}
684 		else if ( tag == "ORG" ) {
685 			d->org.name = subTagText(i, "ORGNAME");
686 
687 			QDomNode nn = i.firstChild();
688 			for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
689 				QDomElement ii = nn.toElement();
690 				if ( ii.isNull() )
691 					continue;
692 
693 				if ( ii.tagName().toUpper() == "ORGUNIT" )
694 					d->org.unit.append( ii.text().trimmed() );
695 			}
696 		}
697 		else if ( tag == "CATEGORIES") {
698 			QDomNode nn = i.firstChild();
699 			for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
700 				QDomElement ee = nn.toElement();
701 				if ( ee.isNull() )
702 					continue;
703 
704 				if ( ee.tagName().toUpper() == "KEYWORD" )
705 					d->categories << ee.text().trimmed();
706 			}
707 		}
708 		else if ( tag == "NOTE" )
709 			d->note = i.text().trimmed();
710 		else if ( tag == "PRODID" )
711 			d->prodId = i.text().trimmed();
712 		else if ( tag == "REV" )
713 			d->rev = i.text().trimmed();
714 		else if ( tag == "SORT-STRING" )
715 			d->sortString = i.text().trimmed();
716 		else if ( tag == "SOUND" ) {
717 			d->sound = QCA::Base64().stringToArray( subTagText(i, "BINVAL").replace("\n","") ).toByteArray();
718 			d->soundURI      = subTagText(i, "EXTVAL");
719 			d->soundPhonetic = subTagText(i, "PHONETIC");
720 		}
721 		else if ( tag == "UID" )
722 			d->uid = i.text().trimmed();
723 		else if ( tag == "URL")
724 			d->url = i.text().trimmed();
725 		else if ( tag == "DESC" )
726 			d->desc = i.text().trimmed();
727 		else if ( tag == "CLASS" ) {
728 			if ( hasSubTag(i, "PUBLIC") )
729 				d->privacyClass = pcPublic;
730 			else if ( hasSubTag(i, "PRIVATE") )
731 				d->privacyClass = pcPrivate;
732 			else if ( hasSubTag(i, "CONFIDENTIAL") )
733 				d->privacyClass = pcConfidential;
734 		}
735 		else if ( tag == "KEY" ) {
736 			// TODO: Justin, please check out this code
737 			e = findSubTag(i, "TYPE", &found);
738 			QString type = "text/plain";
739 			if ( found )
740 				type = e.text().trimmed();
741 
742 			e = findSubTag(i, "CRED", &found );
743 			if ( !found )
744 				e = findSubTag(i, "BINVAL", &found); // case for very clever clients ;-)
745 
746 			if ( found )
747 				d->key = e.text().toUtf8(); // FIXME
748 		}
749 	}
750 
751 	return true;
752 }
753 
isEmpty() const754 bool VCard::isEmpty() const
755 {
756 	return d->isEmpty();
757 }
758 
759 // Some constructors
760 
Address()761 VCard::Address::Address()
762 {
763 	home = work = postal = parcel = dom = intl = pref = false;
764 }
765 
Label()766 VCard::Label::Label()
767 {
768 	home = work = postal = parcel = dom = intl = pref = false;
769 }
770 
Phone()771 VCard::Phone::Phone()
772 {
773 	home = work = voice = fax = pager = msg = cell = video = bbs = modem = isdn = pcs = pref = false;
774 }
775 
Email()776 VCard::Email::Email()
777 {
778 	home = work = internet = x400 = false;
779 }
780 
Geo()781 VCard::Geo::Geo()
782 {
783 }
784 
Org()785 VCard::Org::Org()
786 {
787 }
788 
789 // vCard properties...
790 
version() const791 const QString &VCard::version() const
792 {
793 	return d->version;
794 }
795 
setVersion(const QString & v)796 void VCard::setVersion(const QString &v)
797 {
798 	d->version = v;
799 }
800 
fullName() const801 const QString &VCard::fullName() const
802 {
803 	return d->fullName;
804 }
805 
setFullName(const QString & n)806 void VCard::setFullName(const QString &n)
807 {
808 	d->fullName = n;
809 }
810 
familyName() const811 const QString &VCard::familyName() const
812 {
813 	return d->familyName;
814 }
815 
setFamilyName(const QString & n)816 void VCard::setFamilyName(const QString &n)
817 {
818 	d->familyName = n;
819 }
820 
givenName() const821 const QString &VCard::givenName() const
822 {
823 	return d->givenName;
824 }
825 
setGivenName(const QString & n)826 void VCard::setGivenName(const QString &n)
827 {
828 	d->givenName = n;
829 }
830 
middleName() const831 const QString &VCard::middleName() const
832 {
833 	return d->middleName;
834 }
835 
setMiddleName(const QString & n)836 void VCard::setMiddleName(const QString &n)
837 {
838 	d->middleName = n;
839 }
840 
prefixName() const841 const QString &VCard::prefixName() const
842 {
843 	return d->prefixName;
844 }
845 
setPrefixName(const QString & p)846 void VCard::setPrefixName(const QString &p)
847 {
848 	d->prefixName = p;
849 }
850 
suffixName() const851 const QString &VCard::suffixName() const
852 {
853 	return d->suffixName;
854 }
855 
setSuffixName(const QString & s)856 void VCard::setSuffixName(const QString &s)
857 {
858 	d->suffixName = s;
859 }
860 
nickName() const861 const QString &VCard::nickName() const
862 {
863 	return d->nickName;
864 }
865 
setNickName(const QString & n)866 void VCard::setNickName(const QString &n)
867 {
868 	d->nickName = n;
869 }
870 
photo() const871 const QByteArray &VCard::photo() const
872 {
873 	return d->photo;
874 }
875 
setPhoto(const QByteArray & i)876 void VCard::setPhoto(const QByteArray &i)
877 {
878 	d->photo = i;
879 }
880 
photoURI() const881 const QString &VCard::photoURI() const
882 {
883 	return d->photoURI;
884 }
885 
setPhotoURI(const QString & p)886 void VCard::setPhotoURI(const QString &p)
887 {
888 	d->photoURI = p;
889 }
890 
bday() const891 const QDate VCard::bday() const
892 {
893 	return QDate::fromString(d->bday);
894 }
895 
setBday(const QDate & date)896 void VCard::setBday(const QDate &date)
897 {
898 	d->bday = date.toString();
899 }
900 
bdayStr() const901 const QString &VCard::bdayStr() const
902 {
903 	return d->bday;
904 }
905 
setBdayStr(const QString & date)906 void VCard::setBdayStr(const QString &date)
907 {
908 	d->bday = date;
909 }
910 
addressList() const911 const VCard::AddressList &VCard::addressList() const
912 {
913 	return d->addressList;
914 }
915 
setAddressList(const VCard::AddressList & a)916 void VCard::setAddressList(const VCard::AddressList &a)
917 {
918 	d->addressList = a;
919 }
920 
labelList() const921 const VCard::LabelList &VCard::labelList() const
922 {
923 	return d->labelList;
924 }
925 
setLabelList(const VCard::LabelList & l)926 void VCard::setLabelList(const VCard::LabelList &l)
927 {
928 	d->labelList = l;
929 }
930 
phoneList() const931 const VCard::PhoneList &VCard::phoneList() const
932 {
933 	return d->phoneList;
934 }
935 
setPhoneList(const VCard::PhoneList & p)936 void VCard::setPhoneList(const VCard::PhoneList &p)
937 {
938 	d->phoneList = p;
939 }
940 
emailList() const941 const VCard::EmailList &VCard::emailList() const
942 {
943 	return d->emailList;
944 }
945 
setEmailList(const VCard::EmailList & e)946 void VCard::setEmailList(const VCard::EmailList &e)
947 {
948 	d->emailList = e;
949 }
950 
jid() const951 const QString &VCard::jid() const
952 {
953 	return d->jid;
954 }
955 
setJid(const QString & j)956 void VCard::setJid(const QString &j)
957 {
958 	d->jid = j;
959 }
960 
mailer() const961 const QString &VCard::mailer() const
962 {
963 	return d->mailer;
964 }
965 
setMailer(const QString & m)966 void VCard::setMailer(const QString &m)
967 {
968 	d->mailer = m;
969 }
970 
timezone() const971 const QString &VCard::timezone() const
972 {
973 	return d->timezone;
974 }
975 
setTimezone(const QString & t)976 void VCard::setTimezone(const QString &t)
977 {
978 	d->timezone = t;
979 }
980 
geo() const981 const VCard::Geo &VCard::geo() const
982 {
983 	return d->geo;
984 }
985 
setGeo(const VCard::Geo & g)986 void VCard::setGeo(const VCard::Geo &g)
987 {
988 	d->geo = g;
989 }
990 
title() const991 const QString &VCard::title() const
992 {
993 	return d->title;
994 }
995 
setTitle(const QString & t)996 void VCard::setTitle(const QString &t)
997 {
998 	d->title = t;
999 }
1000 
role() const1001 const QString &VCard::role() const
1002 {
1003 	return d->role;
1004 }
1005 
setRole(const QString & r)1006 void VCard::setRole(const QString &r)
1007 {
1008 	d->role = r;
1009 }
1010 
logo() const1011 const QByteArray &VCard::logo() const
1012 {
1013 	return d->logo;
1014 }
1015 
setLogo(const QByteArray & i)1016 void VCard::setLogo(const QByteArray &i)
1017 {
1018 	d->logo = i;
1019 }
1020 
logoURI() const1021 const QString &VCard::logoURI() const
1022 {
1023 	return d->logoURI;
1024 }
1025 
setLogoURI(const QString & l)1026 void VCard::setLogoURI(const QString &l)
1027 {
1028 	d->logoURI = l;
1029 }
1030 
agent() const1031 const VCard *VCard::agent() const
1032 {
1033 	return d->agent;
1034 }
1035 
setAgent(const VCard & v)1036 void VCard::setAgent(const VCard &v)
1037 {
1038 	if ( !d->agent )
1039 		d->agent = new VCard;
1040 	*(d->agent) = v;
1041 }
1042 
agentURI() const1043 const QString VCard::agentURI() const
1044 {
1045 	return d->agentURI;
1046 }
1047 
setAgentURI(const QString & a)1048 void VCard::setAgentURI(const QString &a)
1049 {
1050 	d->agentURI = a;
1051 }
1052 
org() const1053 const VCard::Org &VCard::org() const
1054 {
1055 	return d->org;
1056 }
1057 
setOrg(const VCard::Org & o)1058 void VCard::setOrg(const VCard::Org &o)
1059 {
1060 	d->org = o;
1061 }
1062 
categories() const1063 const QStringList &VCard::categories() const
1064 {
1065 	return d->categories;
1066 }
1067 
setCategories(const QStringList & c)1068 void VCard::setCategories(const QStringList &c)
1069 {
1070 	d->categories = c;
1071 }
1072 
note() const1073 const QString &VCard::note() const
1074 {
1075 	return d->note;
1076 }
1077 
setNote(const QString & n)1078 void VCard::setNote(const QString &n)
1079 {
1080 	d->note = n;
1081 }
1082 
prodId() const1083 const QString &VCard::prodId() const
1084 {
1085 	return d->prodId;
1086 }
1087 
setProdId(const QString & p)1088 void VCard::setProdId(const QString &p)
1089 {
1090 	d->prodId = p;
1091 }
1092 
rev() const1093 const QString &VCard::rev() const
1094 {
1095 	return d->rev;
1096 }
1097 
setRev(const QString & r)1098 void VCard::setRev(const QString &r)
1099 {
1100 	d->rev = r;
1101 }
1102 
sortString() const1103 const QString &VCard::sortString() const
1104 {
1105 	return d->sortString;
1106 }
1107 
setSortString(const QString & s)1108 void VCard::setSortString(const QString &s)
1109 {
1110 	d->sortString = s;
1111 }
1112 
sound() const1113 const QByteArray &VCard::sound() const
1114 {
1115 	return d->sound;
1116 }
1117 
setSound(const QByteArray & s)1118 void VCard::setSound(const QByteArray &s)
1119 {
1120 	d->sound = s;
1121 }
1122 
soundURI() const1123 const QString &VCard::soundURI() const
1124 {
1125 	return d->soundURI;
1126 }
1127 
setSoundURI(const QString & s)1128 void VCard::setSoundURI(const QString &s)
1129 {
1130 	d->soundURI = s;
1131 }
1132 
soundPhonetic() const1133 const QString &VCard::soundPhonetic() const
1134 {
1135 	return d->soundPhonetic;
1136 }
1137 
setSoundPhonetic(const QString & s)1138 void VCard::setSoundPhonetic(const QString &s)
1139 {
1140 	d->soundPhonetic = s;
1141 }
1142 
uid() const1143 const QString &VCard::uid() const
1144 {
1145 	return d->uid;
1146 }
1147 
setUid(const QString & u)1148 void VCard::setUid(const QString &u)
1149 {
1150 	d->uid = u;
1151 }
1152 
url() const1153 const QString &VCard::url() const
1154 {
1155 	return d->url;
1156 }
1157 
setUrl(const QString & u)1158 void VCard::setUrl(const QString &u)
1159 {
1160 	d->url = u;
1161 }
1162 
desc() const1163 const QString &VCard::desc() const
1164 {
1165 	return d->desc;
1166 }
1167 
setDesc(const QString & desc)1168 void VCard::setDesc(const QString &desc)
1169 {
1170 	d->desc = desc;
1171 }
1172 
privacyClass() const1173 const VCard::PrivacyClass &VCard::privacyClass() const
1174 {
1175 	return d->privacyClass;
1176 }
1177 
setPrivacyClass(const VCard::PrivacyClass & c)1178 void VCard::setPrivacyClass(const VCard::PrivacyClass &c)
1179 {
1180 	d->privacyClass = c;
1181 }
1182 
key() const1183 const QByteArray &VCard::key() const
1184 {
1185 	return d->key;
1186 }
1187 
setKey(const QByteArray & k)1188 void VCard::setKey(const QByteArray &k)
1189 {
1190 	d->key = k;
1191 }
1192