From 1e2c3461458f4274d539daf51507ce81d845cce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be> Date: Mon, 6 Sep 2010 11:19:02 +0200 Subject: Partial VCard support without losing unknown data. diff --git a/Slimber/Server.cpp b/Slimber/Server.cpp index 7ec3b66..734f3b8 100644 --- a/Slimber/Server.cpp +++ b/Slimber/Server.cpp @@ -407,8 +407,8 @@ LinkLocalServiceInfo Server::getLinkLocalServiceInfo(boost::shared_ptr<Presence> if (!vcard->getNickname().isEmpty()) { info.setNick(vcard->getNickname()); } - if (!vcard->getEMail().isEmpty()) { - info.setEMail(vcard->getEMail()); + if (!vcard->getPreferredEMailAddress().address.isEmpty()) { + info.setEMail(vcard->getPreferredEMailAddress().address); } info.setMessage(presence->getStatus()); switch (presence->getShow()) { diff --git a/Swiften/Elements/VCard.cpp b/Swiften/Elements/VCard.cpp new file mode 100644 index 0000000..8262e07 --- /dev/null +++ b/Swiften/Elements/VCard.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Elements/VCard.h" + +#include "Swiften/Base/foreach.h" + +namespace Swift { + +VCard::EMailAddress VCard::getPreferredEMailAddress() const { + foreach(const EMailAddress& address, emailAddresses_) { + if (address.isPreferred) { + return address; + } + } + if (emailAddresses_.size() > 0) { + return emailAddresses_[0]; + } + return EMailAddress(); +} + + +} diff --git a/Swiften/Elements/VCard.h b/Swiften/Elements/VCard.h index 60f94b1..15173f1 100644 --- a/Swiften/Elements/VCard.h +++ b/Swiften/Elements/VCard.h @@ -14,8 +14,23 @@ namespace Swift { class VCard : public Payload, public Shared<VCard> { public: + struct EMailAddress { + EMailAddress() : isHome(false), isWork(false), isInternet(false), isPreferred(false), isX400(false) { + } + + bool isHome; + bool isWork; + bool isInternet; + bool isPreferred; + bool isX400; + String address; + }; + VCard() {} + void setVersion(const String& version) { version_ = version; } + const String& getVersion() const { return version_; } + void setFullName(const String& fullName) { fullName_ = fullName; } const String& getFullName() const { return fullName_; } @@ -25,8 +40,18 @@ namespace Swift { void setGivenName(const String& givenName) { givenName_ = givenName; } const String& getGivenName() const { return givenName_; } - void setEMail(const String& email) { email_ = email; } - const String& getEMail() const { return email_; } + void setMiddleName(const String& middleName) { middleName_ = middleName; } + const String& getMiddleName() const { return middleName_; } + + void setPrefix(const String& prefix) { prefix_ = prefix; } + const String& getPrefix() const { return prefix_; } + + void setSuffix(const String& suffix) { suffix_ = suffix; } + const String& getSuffix() const { return suffix_; } + + + //void setEMailAddress(const String& email) { email_ = email; } + //const String& getEMailAddress() const { return email_; } void setNickname(const String& nick) { nick_ = nick; } const String& getNickname() const { return nick_; } @@ -37,13 +62,34 @@ namespace Swift { void setPhotoType(const String& photoType) { photoType_ = photoType; } const String& getPhotoType() { return photoType_; } + const String& getUnknownContent() const { return unknownContent_; } + void addUnknownContent(const String& c) { + unknownContent_ += c; + } + + const std::vector<EMailAddress> getEMailAddresses() const { + return emailAddresses_; + } + + void addEMailAddress(const EMailAddress& email) { + emailAddresses_.push_back(email); + } + + EMailAddress getPreferredEMailAddress() const; + private: + String version_; String fullName_; String familyName_; String givenName_; - String email_; + String middleName_; + String prefix_; + String suffix_; + //String email_; ByteArray photo_; String photoType_; String nick_; + String unknownContent_; + std::vector<EMailAddress> emailAddresses_; }; } diff --git a/Swiften/Parser/PayloadParsers/RosterParser.cpp b/Swiften/Parser/PayloadParsers/RosterParser.cpp index c3a35b6..6db2b30 100644 --- a/Swiften/Parser/PayloadParsers/RosterParser.cpp +++ b/Swiften/Parser/PayloadParsers/RosterParser.cpp @@ -71,6 +71,7 @@ void RosterParser::handleEndElement(const String& element, const String& ns) { if (unknownContentParser_) { unknownContentParser_->handleEndElement(element, ns); currentItem_.addUnknownContent(unknownContentParser_->getResult()); + delete unknownContentParser_; unknownContentParser_ = NULL; } else if (element == "group") { diff --git a/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp index 999eb34..dfcc0ec 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp +++ b/Swiften/Parser/PayloadParsers/UnitTest/VCardParserTest.cpp @@ -12,15 +12,68 @@ using namespace Swift; -class VCardParserTest : public CppUnit::TestFixture -{ +class VCardParserTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(VCardParserTest); + CPPUNIT_TEST(testParse); CPPUNIT_TEST(testParse_Photo); CPPUNIT_TEST(testParse_Nickname); CPPUNIT_TEST_SUITE_END(); public: - VCardParserTest() {} + void testParse() { + PayloadsParserTester parser; + + CPPUNIT_ASSERT(parser.parse( + "<vCard xmlns=\"vcard-temp\">" + "<VERSION>2.0</VERSION>" + "<FN>Alice In Wonderland</FN>" + "<N>" + "<FAMILY>Wonderland</FAMILY>" + "<GIVEN>Alice</GIVEN>" + "<MIDDLE>In</MIDDLE>" + "<PREFIX>Mrs</PREFIX>" + "<SUFFIX>PhD</SUFFIX>" + "</N>" + "<EMAIL>" + "<USERID>alice@wonderland.lit</USERID>" + "<HOME/>" + "<INTERNET/>" + "<PREF/>" + "</EMAIL>" + "<EMAIL>" + "<USERID>alice@teaparty.lit</USERID>" + "<WORK/>" + "<X400/>" + "</EMAIL>" + "<NICKNAME>DreamGirl</NICKNAME>" + "<BDAY>1234</BDAY>" + "<MAILER>mutt</MAILER>" + "</vCard>")); + + boost::shared_ptr<VCard> payload = boost::dynamic_pointer_cast<VCard>(parser.getPayload()); + CPPUNIT_ASSERT_EQUAL(String("2.0"), payload->getVersion()); + CPPUNIT_ASSERT_EQUAL(String("Alice In Wonderland"), payload->getFullName()); + CPPUNIT_ASSERT_EQUAL(String("Alice"), payload->getGivenName()); + CPPUNIT_ASSERT_EQUAL(String("In"), payload->getMiddleName()); + CPPUNIT_ASSERT_EQUAL(String("Wonderland"), payload->getFamilyName()); + CPPUNIT_ASSERT_EQUAL(String("Mrs"), payload->getPrefix()); + CPPUNIT_ASSERT_EQUAL(String("PhD"), payload->getSuffix()); + CPPUNIT_ASSERT_EQUAL(String("DreamGirl"), payload->getNickname()); + CPPUNIT_ASSERT_EQUAL(String("<BDAY xmlns=\"vcard-temp\">1234</BDAY><MAILER xmlns=\"vcard-temp\">mutt</MAILER>"), payload->getUnknownContent()); + CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(payload->getEMailAddresses().size())); + CPPUNIT_ASSERT_EQUAL(String("alice@wonderland.lit"), payload->getEMailAddresses()[0].address); + CPPUNIT_ASSERT(payload->getEMailAddresses()[0].isHome); + CPPUNIT_ASSERT(payload->getEMailAddresses()[0].isInternet); + CPPUNIT_ASSERT(payload->getEMailAddresses()[0].isPreferred); + CPPUNIT_ASSERT(!payload->getEMailAddresses()[0].isWork); + CPPUNIT_ASSERT(!payload->getEMailAddresses()[0].isX400); + CPPUNIT_ASSERT_EQUAL(String("alice@teaparty.lit"), payload->getEMailAddresses()[1].address); + CPPUNIT_ASSERT(!payload->getEMailAddresses()[1].isHome); + CPPUNIT_ASSERT(!payload->getEMailAddresses()[1].isInternet); + CPPUNIT_ASSERT(!payload->getEMailAddresses()[1].isPreferred); + CPPUNIT_ASSERT(payload->getEMailAddresses()[1].isWork); + CPPUNIT_ASSERT(payload->getEMailAddresses()[1].isX400); + } void testParse_Photo() { PayloadsParserTester parser; diff --git a/Swiften/Parser/PayloadParsers/VCardParser.cpp b/Swiften/Parser/PayloadParsers/VCardParser.cpp index 338efc6..2f1f8dc 100644 --- a/Swiften/Parser/PayloadParsers/VCardParser.cpp +++ b/Swiften/Parser/PayloadParsers/VCardParser.cpp @@ -7,27 +7,39 @@ #include "Swiften/Parser/PayloadParsers/VCardParser.h" #include "Swiften/Base/foreach.h" #include "Swiften/StringCodecs/Base64.h" +#include "Swiften/Parser/SerializingParser.h" namespace Swift { -VCardParser::VCardParser() { +VCardParser::VCardParser() : unknownContentParser_(NULL) { } -void VCardParser::handleStartElement(const String& element, const String&, const AttributeMap&) { +void VCardParser::handleStartElement(const String& element, const String& ns, const AttributeMap& attributes) { elementStack_.push_back(element); + String elementHierarchy = getElementHierarchy(); + if (elementHierarchy == "/vCard/EMAIL") { + currentEMailAddress_ = VCard::EMailAddress(); + } + if (elementStack_.size() == 2) { + assert(!unknownContentParser_); + unknownContentParser_ = new SerializingParser(); + unknownContentParser_->handleStartElement(element, ns, attributes); + } + else if (unknownContentParser_) { + unknownContentParser_->handleStartElement(element, ns, attributes); + } + currentText_ = ""; } -void VCardParser::handleEndElement(const String&, const String&) { - String elementHierarchy = getElementHierarchy(); - if (elementHierarchy == "/vCard/PHOTO/TYPE") { - getPayloadInternal()->setPhotoType(currentText_); - } - else if (elementHierarchy == "/vCard/PHOTO/BINVAL") { - getPayloadInternal()->setPhoto(Base64::decode(currentText_)); +void VCardParser::handleEndElement(const String& element, const String& ns) { + if (unknownContentParser_) { + unknownContentParser_->handleEndElement(element, ns); } - else if (elementHierarchy == "/vCard/NICKNAME") { - getPayloadInternal()->setNickname(currentText_); + + String elementHierarchy = getElementHierarchy(); + if (elementHierarchy == "/vCard/VERSION") { + getPayloadInternal()->setVersion(currentText_); } else if (elementHierarchy == "/vCard/FN") { getPayloadInternal()->setFullName(currentText_); @@ -38,13 +50,64 @@ void VCardParser::handleEndElement(const String&, const String&) { else if (elementHierarchy == "/vCard/N/GIVEN") { getPayloadInternal()->setGivenName(currentText_); } + else if (elementHierarchy == "/vCard/N/MIDDLE") { + getPayloadInternal()->setMiddleName(currentText_); + } + else if (elementHierarchy == "/vCard/N/PREFIX") { + getPayloadInternal()->setPrefix(currentText_); + } + else if (elementHierarchy == "/vCard/N/SUFFIX") { + getPayloadInternal()->setSuffix(currentText_); + } + else if (elementHierarchy == "/vCard/N") { + } + else if (elementHierarchy == "/vCard/NICKNAME") { + getPayloadInternal()->setNickname(currentText_); + } + else if (elementHierarchy == "/vCard/PHOTO/TYPE") { + getPayloadInternal()->setPhotoType(currentText_); + } + else if (elementHierarchy == "/vCard/PHOTO/BINVAL") { + getPayloadInternal()->setPhoto(Base64::decode(currentText_)); + } + else if (elementHierarchy == "/vCard/PHOTO") { + } else if (elementHierarchy == "/vCard/EMAIL/USERID") { - getPayloadInternal()->setEMail(currentText_); + currentEMailAddress_.address = currentText_; + } + else if (elementHierarchy == "/vCard/EMAIL/HOME") { + currentEMailAddress_.isHome = true; + } + else if (elementHierarchy == "/vCard/EMAIL/WORK") { + currentEMailAddress_.isWork = true; + } + else if (elementHierarchy == "/vCard/EMAIL/INTERNET") { + currentEMailAddress_.isInternet = true; + } + else if (elementHierarchy == "/vCard/EMAIL/X400") { + currentEMailAddress_.isX400 = true; + } + else if (elementHierarchy == "/vCard/EMAIL/PREF") { + currentEMailAddress_.isPreferred = true; + } + else if (elementHierarchy == "/vCard/EMAIL") { + getPayloadInternal()->addEMailAddress(currentEMailAddress_); + } + else if (elementStack_.size() == 2 && unknownContentParser_) { + getPayloadInternal()->addUnknownContent(unknownContentParser_->getResult()); + } + + if (elementStack_.size() == 2 && unknownContentParser_) { + delete unknownContentParser_; + unknownContentParser_ = NULL; } elementStack_.pop_back(); } void VCardParser::handleCharacterData(const String& text) { + if (unknownContentParser_) { + unknownContentParser_->handleCharacterData(text); + } currentText_ += text; } diff --git a/Swiften/Parser/PayloadParsers/VCardParser.h b/Swiften/Parser/PayloadParsers/VCardParser.h index d22f8f5..f912ff1 100644 --- a/Swiften/Parser/PayloadParsers/VCardParser.h +++ b/Swiften/Parser/PayloadParsers/VCardParser.h @@ -25,6 +25,8 @@ namespace Swift { private: std::vector<String> elementStack_; + VCard::EMailAddress currentEMailAddress_; + SerializingParser* unknownContentParser_; String currentText_; }; } diff --git a/Swiften/SConscript b/Swiften/SConscript index a23efa0..3d82cfa 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -43,6 +43,7 @@ if env["SCONS_STAGE"] == "build" : "Elements/Payload.cpp", "Elements/RosterPayload.cpp", "Elements/Stanza.cpp", + "Elements/VCard.cpp", "MUC/MUC.cpp", "MUC/MUCOccupant.cpp", "MUC/MUCRegistry.cpp", @@ -218,6 +219,7 @@ if env["SCONS_STAGE"] == "build" : File("Serializer/PayloadSerializers/UnitTest/StatusSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/StatusShowSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/VCardUpdateSerializerTest.cpp"), + File("Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp"), File("Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp"), File("Serializer/UnitTest/StreamFeaturesSerializerTest.cpp"), diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp new file mode 100644 index 0000000..11b24dc --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h" + +using namespace Swift; + +class VCardSerializerTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(VCardSerializerTest); + CPPUNIT_TEST(testSerialize); + CPPUNIT_TEST_SUITE_END(); + + public: + void testSerialize() { + VCardSerializer testling; + boost::shared_ptr<VCard> vcard(new VCard()); + vcard->setVersion("2.0"); + vcard->setFullName("Alice In Wonderland"); + vcard->setPrefix("Mrs"); + vcard->setGivenName("Alice"); + vcard->setMiddleName("In"); + vcard->setFamilyName("Wonderland"); + vcard->setSuffix("PhD"); + vcard->setNickname("DreamGirl"); + vcard->setPhoto("abcdef"); + vcard->setPhotoType("image/png"); + vcard->addUnknownContent("<BDAY>1234</BDAY><MAILER>mutt</MAILER>"); + + VCard::EMailAddress address1; + address1.address = "alice@wonderland.lit"; + address1.isHome = true; + address1.isPreferred = true; + address1.isInternet = true; + vcard->addEMailAddress(address1); + + VCard::EMailAddress address2; + address2.address = "alice@teaparty.lit"; + address2.isWork = true; + address2.isX400 = true; + vcard->addEMailAddress(address2); + + String expectedResult = + "<vCard xmlns=\"vcard-temp\">" + "<VERSION>2.0</VERSION>" + "<FN>Alice In Wonderland</FN>" + "<N>" + "<FAMILY>Wonderland</FAMILY>" + "<GIVEN>Alice</GIVEN>" + "<MIDDLE>In</MIDDLE>" + "<PREFIX>Mrs</PREFIX>" + "<SUFFIX>PhD</SUFFIX>" + "</N>" + "<EMAIL>" + "<USERID>alice@wonderland.lit</USERID>" + "<HOME/>" + "<INTERNET/>" + "<PREF/>" + "</EMAIL>" + "<EMAIL>" + "<USERID>alice@teaparty.lit</USERID>" + "<WORK/>" + "<X400/>" + "</EMAIL>" + "<NICKNAME>DreamGirl</NICKNAME>" + "<PHOTO>" + "<TYPE>image/png</TYPE>" + "<BINVAL>616263646566</BINVAL>" + "</PHOTO>" + "<BDAY>1234</BDAY>" + "<MAILER>mutt</MAILER>" + "</vCard>"; + + CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(vcard)); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(VCardSerializerTest); diff --git a/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp index 8975818..5953ef6 100644 --- a/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/VCardSerializer.cpp @@ -10,7 +10,9 @@ #include "Swiften/Serializer/XML/XMLElement.h" #include "Swiften/Serializer/XML/XMLTextNode.h" +#include "Swiften/Serializer/XML/XMLRawTextNode.h" #include "Swiften/StringCodecs/Hexify.h" +#include "Swiften/Base/foreach.h" namespace Swift { @@ -19,12 +21,17 @@ VCardSerializer::VCardSerializer() : GenericPayloadSerializer<VCard>() { String VCardSerializer::serializePayload(boost::shared_ptr<VCard> vcard) const { XMLElement queryElement("vCard", "vcard-temp"); + if (!vcard->getVersion().isEmpty()) { + boost::shared_ptr<XMLElement> versionElement(new XMLElement("VERSION")); + versionElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getVersion()))); + queryElement.addNode(versionElement); + } if (!vcard->getFullName().isEmpty()) { boost::shared_ptr<XMLElement> fullNameElement(new XMLElement("FN")); fullNameElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getFullName()))); queryElement.addNode(fullNameElement); } - if (!vcard->getGivenName().isEmpty() || !vcard->getFamilyName().isEmpty()) { + if (!vcard->getGivenName().isEmpty() || !vcard->getFamilyName().isEmpty() || !vcard->getMiddleName().isEmpty() || !vcard->getPrefix().isEmpty() || !vcard->getSuffix().isEmpty()) { boost::shared_ptr<XMLElement> nameElement(new XMLElement("N")); if (!vcard->getFamilyName().isEmpty()) { boost::shared_ptr<XMLElement> familyNameElement(new XMLElement("FAMILY")); @@ -36,13 +43,43 @@ String VCardSerializer::serializePayload(boost::shared_ptr<VCard> vcard) const givenNameElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getGivenName()))); nameElement->addNode(givenNameElement); } + if (!vcard->getMiddleName().isEmpty()) { + boost::shared_ptr<XMLElement> middleNameElement(new XMLElement("MIDDLE")); + middleNameElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getMiddleName()))); + nameElement->addNode(middleNameElement); + } + if (!vcard->getPrefix().isEmpty()) { + boost::shared_ptr<XMLElement> prefixElement(new XMLElement("PREFIX")); + prefixElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getPrefix()))); + nameElement->addNode(prefixElement); + } + if (!vcard->getSuffix().isEmpty()) { + boost::shared_ptr<XMLElement> suffixElement(new XMLElement("SUFFIX")); + suffixElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getSuffix()))); + nameElement->addNode(suffixElement); + } queryElement.addNode(nameElement); } - if (!vcard->getEMail().isEmpty()) { + foreach(const VCard::EMailAddress& emailAddress, vcard->getEMailAddresses()) { boost::shared_ptr<XMLElement> emailElement(new XMLElement("EMAIL")); boost::shared_ptr<XMLElement> userIDElement(new XMLElement("USERID")); - userIDElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(vcard->getEMail()))); + userIDElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(emailAddress.address))); emailElement->addNode(userIDElement); + if (emailAddress.isHome) { + emailElement->addNode(boost::shared_ptr<XMLElement>(new XMLElement("HOME"))); + } + if (emailAddress.isWork) { + emailElement->addNode(boost::shared_ptr<XMLElement>(new XMLElement("WORK"))); + } + if (emailAddress.isInternet) { + emailElement->addNode(boost::shared_ptr<XMLElement>(new XMLElement("INTERNET"))); + } + if (emailAddress.isPreferred) { + emailElement->addNode(boost::shared_ptr<XMLElement>(new XMLElement("PREF"))); + } + if (emailAddress.isX400) { + emailElement->addNode(boost::shared_ptr<XMLElement>(new XMLElement("X400"))); + } queryElement.addNode(emailElement); } if (!vcard->getNickname().isEmpty()) { @@ -64,6 +101,9 @@ String VCardSerializer::serializePayload(boost::shared_ptr<VCard> vcard) const } queryElement.addNode(photoElement); } + if (!vcard->getUnknownContent().isEmpty()) { + queryElement.addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(vcard->getUnknownContent()))); + } return queryElement.serialize(); } -- cgit v0.10.2-6-g49f6