From aa131405927fc7f597ed06aff71abb0a30b59926 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Fri, 26 Apr 2013 20:04:37 +0200
Subject: Replace third-party Base64 by our own algorithm.

Change-Id: I48037fbc475c84f55dd74054bceb187a4798244a

diff --git a/Swiften/StringCodecs/Base64.cpp b/Swiften/StringCodecs/Base64.cpp
index 76e2833..1b039ac 100644
--- a/Swiften/StringCodecs/Base64.cpp
+++ b/Swiften/StringCodecs/Base64.cpp
@@ -1,121 +1,100 @@
 /*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2013 Remko Tronçon
  * Licensed under the GNU General Public License v3.
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-// Code copied & adapted from LibTomCrypt by Tom St Denis
-
-#include <boost/numeric/conversion/cast.hpp>
-
-#include <algorithm>
-
 #include <Swiften/StringCodecs/Base64.h>
-#include <Swiften/Base/Algorithm.h>
 
-#pragma GCC diagnostic ignored "-Wold-style-cast"
 #pragma clang diagnostic ignored "-Wconversion"
 
-
-static const char *codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+using namespace Swift;
 
 namespace {
-	template<typename TargetType, typename SourceType>
- 	TargetType base64Encode(const SourceType& in) {
- 		TargetType out;
+	const char* encodeMap =
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	const unsigned char decodeMap[255] = {
+		255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255, 
+		255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 255, 255, 255, 255, 255,
+		255, 255, 255, 62,  255, 255, 255, 63,
+		52,  53,  54,  55,  56,  57,  58,  59,
+		60,  61,  255, 255, 255, 255, 255, 255,
+		255, 0,   1,   2,   3,   4,   5,   6,
+		7,   8,   9,   10,  11,  12,  13,  14,
+		15,  16,  17,  18,  19,  20,  21,  22,
+		23,  24,  25,  255, 255, 255, 255, 255,
+		255, 26,  27,  28,  29,  30,  31,  32,
+		33,  34,  35,  36,  37,  38,  39,  40,
+		41,  42,  43,  44,  45,  46,  47,  48,
+		49,  50,  51,  255, 255, 255, 255, 255
+	};
+
+	template<typename ResultType, typename InputType>
+	ResultType encodeDetail(const InputType& input) {
+		ResultType result;
+		size_t i = 0;
+		for (; i < (input.size()/3)*3; i += 3) {
+			unsigned int c = input[i+2] | (input[i+1]<<8) | (input[i]<<16);
+			result.push_back(encodeMap[(c&0xFC0000)>>18]);
+			result.push_back(encodeMap[(c&0x3F000)>>12]);
+			result.push_back(encodeMap[(c&0xFC0)>>6]);
+			result.push_back(encodeMap[c&0x3F]);
+		}
+		if (input.size() % 3 == 2) {
+			unsigned int c = (input[i+1]<<8) | (input[i]<<16);
+			result.push_back(encodeMap[(c&0xFC0000)>>18]);
+			result.push_back(encodeMap[(c&0x3F000)>>12]);
+			result.push_back(encodeMap[(c&0xFC0)>>6]);
+			result.push_back('=');
+		}
+		else if (input.size() % 3 == 1) {
+			unsigned int c = input[i]<<16;
+			result.push_back(encodeMap[(c&0xFC0000)>>18]);
+			result.push_back(encodeMap[(c&0x3F000)>>12]);
+			result.push_back('=');
+			result.push_back('=');
+		}
+		return result;
+	}
+}
 
- 		size_t i;
- 		unsigned int leven = 3*(in.size() / 3);
- 		for (i = 0; i < leven; i += 3) {
- 			out.push_back(codes[(in[i] >> 2) & 0x3F]);
- 			out.push_back(codes[(((in[i] & 3) << 4) + (in[i+1] >> 4)) & 0x3F]);
- 			out.push_back(codes[(((in[i+1] & 0xf) << 2) + (in[i+2] >> 6)) & 0x3F]);
- 			out.push_back(codes[in[i+2] & 0x3F]);
- 		}
- 		if (i < in.size()) {
- 			unsigned int a = in[i];
- 			unsigned int b = (i+1 < in.size()) ? in[i+1] : 0;
 
- 			out.push_back(codes[(a >> 2) & 0x3F]);
- 			out.push_back(codes[(((a & 3) << 4) + (b >> 4)) & 0x3F]);
- 			out.push_back((i+1 < in.size()) ? codes[(((b & 0xf) << 2)) & 0x3F] : '=');
- 			out.push_back('=');
- 		}
- 		return out;
- 	}
+std::string Base64::encode(const ByteArray& s) {
+	return encodeDetail<std::string>(s);
 }
 
-static const unsigned char map[256] = {
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
-	52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
-	255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
-	7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,
-	19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
-	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
-	37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
-	49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-	255, 255, 255, 255 };
-
-namespace Swift {
-	std::string Base64::encode(const ByteArray &s) {
-		return base64Encode<std::string, ByteArray>(s);
-	}
+SafeByteArray Base64::encode(const SafeByteArray& s) {
+	return encodeDetail<SafeByteArray>(s);
+}
 
-	SafeByteArray Base64::encode(const SafeByteArray &s) {
-		return base64Encode<SafeByteArray, SafeByteArray>(s);
+ByteArray Base64::decode(const std::string& input) {
+	ByteArray result;
+	if (input.size() % 4) {
+		return ByteArray();
 	}
-
-	ByteArray Base64::decode(const std::string& input) {
-		std::string inputWithoutNewlines(input);
-		erase(inputWithoutNewlines, '\n');
-
-		const std::string& s = inputWithoutNewlines;
-		ByteArray out;
-
-		int g = 3;
-		size_t y, t;
-		for (size_t x = y = t = 0; x < s.size(); ++x) {
-			unsigned char c = map[s[x]&0xFF];
-			if (c == 255) {
-				continue;
-			}
-		 /* the final = symbols are read and used to trim the remaining bytes */
-			if (c == 254) { 
-				c = 0; 
-				/* prevent g < 0 which would potentially allow an overflow later */
-				if (--g < 0) {
-					return ByteArray();
-				}
-			} else if (g != 3) {
-				/* we only allow = to be at the end */
-				return ByteArray();
-			}
-
-			t = (t<<6)|c;
-
-			if (++y == 4) {
-				out.push_back((unsigned char)((t>>16)&255));
-				if (g > 1) out.push_back((unsigned char)((t>>8)&255));
-				if (g > 2) out.push_back((unsigned char)(t&255));
-				y = t = 0;
-			}
+	for (size_t i = 0; i < input.size(); i += 4) {
+		unsigned char c1 = input[i+0];
+		unsigned char c2 = input[i+1];
+		unsigned char c3 = input[i+2];
+		unsigned char c4 = input[i+3];
+		if (c3 == '=') {
+			unsigned int c = (((decodeMap[c1]<<6)|decodeMap[c2])&0xFF0)>>4;
+			result.push_back(c);
+		}
+		else if (c4 == '=') {
+			unsigned int c = (((decodeMap[c1]<<12)|(decodeMap[c2]<<6)|decodeMap[c3])&0x3FFFC)>>2;
+			result.push_back((c&0xFF00) >> 8);
+			result.push_back(c&0xFF);
 		}
-		if (y != 0) {
-			return ByteArray();
+		else {
+			unsigned int c = (decodeMap[c1]<<18) | (decodeMap[c2]<<12) | (decodeMap[c3]<<6) | decodeMap[c4];
+			result.push_back((c&0xFF0000) >> 16);
+			result.push_back((c&0xFF00) >> 8);
+			result.push_back(c&0xFF);
 		}
-		return out;
 	}
+	return result;
 }
diff --git a/Swiften/StringCodecs/UnitTest/Base64Test.cpp b/Swiften/StringCodecs/UnitTest/Base64Test.cpp
index f6a424b..4005047 100644
--- a/Swiften/StringCodecs/UnitTest/Base64Test.cpp
+++ b/Swiften/StringCodecs/UnitTest/Base64Test.cpp
@@ -16,22 +16,41 @@ using namespace Swift;
 
 class Base64Test : public CppUnit::TestFixture {
 		CPPUNIT_TEST_SUITE(Base64Test);
-		CPPUNIT_TEST(testEncode);
-		CPPUNIT_TEST(testEncode_NonAscii);
+		CPPUNIT_TEST(testEncodeDecodeAllChars);
+		CPPUNIT_TEST(testEncodeDecodeOneBytePadding);
+		CPPUNIT_TEST(testEncodeDecodeTwoBytesPadding);
 		CPPUNIT_TEST(testEncode_NoData);
-		CPPUNIT_TEST(testDecode);
 		CPPUNIT_TEST(testDecode_NoData);
 		CPPUNIT_TEST_SUITE_END();
 
 	public:
-		void testEncode() {
-			std::string result(Base64::encode(createByteArray("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890")));
-			CPPUNIT_ASSERT_EQUAL(std::string("QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejEyMzQ1Njc4OTA="), result);
+		void testEncodeDecodeAllChars() {
+			ByteArray input;
+			for (unsigned char i = 0; i < 255; ++i) {
+				input.push_back(i);
+			}
+			std::string result(Base64::encode(input));
+
+			CPPUNIT_ASSERT_EQUAL(std::string("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+"), result);
+			CPPUNIT_ASSERT_EQUAL(input, Base64::decode(result));
 		}
 
-		void testEncode_NonAscii() {
-			std::string result(Base64::encode(createByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed")));
-			CPPUNIT_ASSERT_EQUAL(std::string("QgayPKawpkPSDYmwT/WM94uAlu0="), result);
+		void testEncodeDecodeOneBytePadding() {
+			ByteArray input = createByteArray("ABCDE", 5);
+
+			std::string result = Base64::encode(input);
+
+			CPPUNIT_ASSERT_EQUAL(std::string("QUJDREU="), result);
+			CPPUNIT_ASSERT_EQUAL(input, Base64::decode(result));
+		}
+
+		void testEncodeDecodeTwoBytesPadding() {
+			ByteArray input = createByteArray("ABCD", 4);
+
+			std::string result = Base64::encode(input);
+
+			CPPUNIT_ASSERT_EQUAL(std::string("QUJDRA=="), result);
+			CPPUNIT_ASSERT_EQUAL(input, Base64::decode(result));
 		}
 
 		void testEncode_NoData() {
@@ -39,11 +58,6 @@ class Base64Test : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT_EQUAL(std::string(""), result);
 		}
 
-		void testDecode() {
-			ByteArray result(Base64::decode("QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejEyMzQ1Njc4OTA="));
-			CPPUNIT_ASSERT_EQUAL(createByteArray("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"), result);
-		}
-
 		void testDecode_NoData() {
 			ByteArray result(Base64::decode(""));
 			CPPUNIT_ASSERT_EQUAL(ByteArray(), result);
-- 
cgit v0.10.2-6-g49f6