From 5ce9e19ef0744f530a797c30a82e9723eb7ea306 Mon Sep 17 00:00:00 2001
From: Edwin Mons <edwin.mons@isode.com>
Date: Wed, 24 Oct 2018 22:40:51 +0200
Subject: Strip off trailing dot from domainpart of jid

RFC 6122 specifies that if a domainpart ends in a dot, it must be
stripped off before any other canonicalisation steps are taken.

Unit tests have been added to check that various JID scenarios with a
domain ending in a dot pass or are rejected as expected.

Test-Information:

Unit tests pass on macOS 10.13.
Manual tests in sluift show expected behaviour.

Change-Id: Id6813aaa4422a81bff0a4559eacd6855ef104dc3

diff --git a/Swiften/JID/JID.cpp b/Swiften/JID/JID.cpp
index a31c19f..fff88e9 100644
--- a/Swiften/JID/JID.cpp
+++ b/Swiften/JID/JID.cpp
@@ -111,7 +111,12 @@ void JID::nameprepAndSetComponents(const std::string& node, const std::string& d
 
     try {
         node_ = idnConverter->getStringPrepared(node, IDNConverter::XMPPNodePrep);
-        domain_ = idnConverter->getStringPrepared(domain, IDNConverter::NamePrep);
+        if (domain.back() == '.') {
+            domain_ = idnConverter->getStringPrepared(domain.substr(0, domain.size() - 1), IDNConverter::NamePrep);
+        }
+        else {
+            domain_ = idnConverter->getStringPrepared(domain, IDNConverter::NamePrep);
+        }
         resource_ = idnConverter->getStringPrepared(resource, IDNConverter::XMPPResourcePrep);
     } catch (...) {
         valid_ = false;
diff --git a/Swiften/JID/UnitTest/JIDTest.cpp b/Swiften/JID/UnitTest/JIDTest.cpp
index aefda33..0753fb5 100644
--- a/Swiften/JID/UnitTest/JIDTest.cpp
+++ b/Swiften/JID/UnitTest/JIDTest.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
  * All rights reserved.
  * See the COPYING file for more information.
  */
@@ -20,13 +20,19 @@ class JIDTest : public CppUnit::TestFixture
         CPPUNIT_TEST(testConstructorWithString_NoNode);
         CPPUNIT_TEST(testConstructorWithString_EmptyResource);
         CPPUNIT_TEST(testConstructorWithString_OnlyDomain);
+        CPPUNIT_TEST(testConstructorWithString_OnlyDomainWithDot);
+        CPPUNIT_TEST(testConstructorWithString_OnlyDomainDotStrippedOff);
+        CPPUNIT_TEST(testConstructorWithString_InvalidOnlyDomainSingleDot);
         CPPUNIT_TEST(testConstructorWithString_InvalidDomain);
+        CPPUNIT_TEST(testConstructorWithString_InvalidDomainEmptyLabel);
         CPPUNIT_TEST(testConstructorWithString_UpperCaseNode);
         CPPUNIT_TEST(testConstructorWithString_UpperCaseDomain);
         CPPUNIT_TEST(testConstructorWithString_UpperCaseResource);
         CPPUNIT_TEST(testConstructorWithString_EmptyNode);
         CPPUNIT_TEST(testConstructorWithString_EmptyDomain);
         CPPUNIT_TEST(testConstructorWithString_EmptyDomainWithResource);
+        CPPUNIT_TEST(testConstructorWithString_DotDomain);
+        CPPUNIT_TEST(testConstructorWithString_DotDomainWithResource);
         CPPUNIT_TEST(testConstructorWithString_IllegalResource);
         CPPUNIT_TEST(testConstructorWithString_SpacesInNode);
         CPPUNIT_TEST(testConstructorWithStrings);
@@ -122,10 +128,38 @@ class JIDTest : public CppUnit::TestFixture
             CPPUNIT_ASSERT(testling.isValid());
         }
 
+        void testConstructorWithString_OnlyDomainWithDot() {
+            JID testling("bar.");
+
+            CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getNode());
+            CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain());
+            CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getResource());
+            CPPUNIT_ASSERT(testling.isBare());
+            CPPUNIT_ASSERT(testling.isValid());
+        }
+
+        void testConstructorWithString_OnlyDomainDotStrippedOff() {
+            JID testling("foo.@bar./resource.");
+
+            CPPUNIT_ASSERT_EQUAL(std::string("foo."), testling.getNode());
+            CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain());
+            CPPUNIT_ASSERT_EQUAL(std::string("resource."), testling.getResource());
+            CPPUNIT_ASSERT(!testling.isBare());
+            CPPUNIT_ASSERT(testling.isValid());
+        }
+
+        void testConstructorWithString_InvalidOnlyDomainSingleDot() {
+            CPPUNIT_ASSERT(!JID(".").isValid());
+        }
+
         void testConstructorWithString_InvalidDomain() {
             CPPUNIT_ASSERT(!JID("foo@bar,baz").isValid());
         }
 
+        void testConstructorWithString_InvalidDomainEmptyLabel() {
+            CPPUNIT_ASSERT(!JID("foo@bar..").isValid());
+        }
+
         void testConstructorWithString_UpperCaseNode() {
             JID testling("Fo\xCE\xA9@bar");
 
@@ -172,6 +206,18 @@ class JIDTest : public CppUnit::TestFixture
             CPPUNIT_ASSERT(!testling.isValid());
         }
 
+        void testConstructorWithString_DotDomain() {
+            JID testling("bar@.");
+
+            CPPUNIT_ASSERT(!testling.isValid());
+        }
+
+        void testConstructorWithString_DotDomainWithResource() {
+            JID testling("bar@./resource");
+
+            CPPUNIT_ASSERT(!testling.isValid());
+        }
+
         void testConstructorWithString_IllegalResource() {
             JID testling("foo@bar.com/\xd8\xb1\xd9\x85\xd9\x82\xd9\x87\x20\xd8\xaa\xd8\xb1\xd9\x86\xd8\xb3\x20");
 
-- 
cgit v0.10.2-6-g49f6