summaryrefslogtreecommitdiffstats
blob: 3ab708804dd8031f71be2162e357bc9f744a9608 (plain)
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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
 * Copyright (c) 2010-2012 Remko Tronçon
 * Licensed under the GNU General Public License v3.
 * See Documentation/Licenses/GPLv3.txt for more information.
 */

#include <Swiften/IDN/StringPrep.h>

#if defined(HAVE_ICU)
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <Swiften/IDN/ICUConverter.h>
#include <unicode/usprep.h>
#include <unicode/ucnv.h>
#elif defined(HAVE_LIBIDN)
extern "C" {
	#include <stringprep.h>
};
#endif

#include <vector>
#include <cassert>
#include <Swiften/Base/SafeAllocator.h>
#include <boost/shared_ptr.hpp>

using namespace Swift;

#if defined(HAVE_ICU)

namespace {
	static UStringPrepProfileType getICUProfileType(StringPrep::Profile profile) {
		switch(profile) {
			case StringPrep::NamePrep: return USPREP_RFC3491_NAMEPREP;
			case StringPrep::XMPPNodePrep: return USPREP_RFC3920_NODEPREP;
			case StringPrep::XMPPResourcePrep: return USPREP_RFC3920_RESOURCEPREP;
			case StringPrep::SASLPrep: return USPREP_RFC4013_SASLPREP;
		}
		assert(false);
		return USPREP_RFC3491_NAMEPREP;
	}

	template<typename StringType>
	std::vector<char, SafeAllocator<char> > getStringPrepared(const StringType& s, StringPrep::Profile profile) {
		UErrorCode status = U_ZERO_ERROR;
		ICUConverter converter;

		boost::shared_ptr<UStringPrepProfile> icuProfile(usprep_openByType(getICUProfileType(profile), &status), usprep_close);
		assert(U_SUCCESS(status));

		ICUConverter::ICUString icuInput = converter.convertToICUString(s);
		ICUConverter::ICUString icuResult;
		UParseError parseError;
		icuResult.resize(icuInput.size());
		int icuResultLength = usprep_prepare(icuProfile.get(), vecptr(icuInput), icuInput.size(), vecptr(icuResult), icuResult.size(), USPREP_ALLOW_UNASSIGNED, &parseError, &status); icuResult.resize(icuResultLength);
		if (status == U_BUFFER_OVERFLOW_ERROR) {
			status = U_ZERO_ERROR;
			icuResult.resize(icuResultLength);
			icuResultLength = usprep_prepare(icuProfile.get(), vecptr(icuInput), icuInput.size(), vecptr(icuResult), icuResult.size(), USPREP_ALLOW_UNASSIGNED, &parseError, &status); icuResult.resize(icuResultLength);
		}
		if (U_FAILURE(status)) {
			return std::vector<char, SafeAllocator<char> >();
		}
		icuResult.resize(icuResultLength);

		return converter.convertToArray(icuResult);
	}
}

namespace Swift {

std::string StringPrep::getPrepared(const std::string& s, Profile profile) {
	if (s.empty()) {
		return "";
	}
	std::vector<char, SafeAllocator<char> > preparedData = getStringPrepared(s, profile);
	if (preparedData.empty()) {
		throw std::exception();
	}
	return std::string(vecptr(preparedData));
}

SafeByteArray StringPrep::getPrepared(const SafeByteArray& s, Profile profile) {
	if (s.empty()) {
		return SafeByteArray();
	}
	std::vector<char, SafeAllocator<char> > preparedData = getStringPrepared(s, profile);
	if (preparedData.empty()) {
		throw std::exception();
	}
	return createSafeByteArray(reinterpret_cast<const char*>(vecptr(preparedData)));
}

}

////////////////////////////////////////////////////////////////////////////////

#elif defined(HAVE_LIBIDN)

namespace {
	static const int MAX_STRINGPREP_SIZE = 1024;

	const Stringprep_profile* getLibIDNProfile(StringPrep::Profile profile) {
		switch(profile) {
			case StringPrep::NamePrep: return stringprep_nameprep;
			case StringPrep::XMPPNodePrep: return stringprep_xmpp_nodeprep;
			case StringPrep::XMPPResourcePrep: return stringprep_xmpp_resourceprep;
			case StringPrep::SASLPrep: return stringprep_saslprep;
		}
		assert(false);
		return 0;
	}

	template<typename StringType, typename ContainerType>
	ContainerType getStringPrepared(const StringType& s, StringPrep::Profile profile) {
		ContainerType input(s.begin(), s.end());
		input.resize(MAX_STRINGPREP_SIZE);
		if (stringprep(&input[0], MAX_STRINGPREP_SIZE, static_cast<Stringprep_profile_flags>(0), getLibIDNProfile(profile)) == 0) {
			return input;
		}
		else {
			return ContainerType();
		}
	}
}

namespace Swift {

std::string StringPrep::getPrepared(const std::string& s, Profile profile) {
	std::vector<char> preparedData = getStringPrepared< std::string, std::vector<char> >(s, profile);
	if (preparedData.empty()) {
		throw std::exception();
	}
	return std::string(vecptr(preparedData));
}

SafeByteArray StringPrep::getPrepared(const SafeByteArray& s, Profile profile) {
	std::vector<char, SafeAllocator<char> > preparedData = getStringPrepared<SafeByteArray, std::vector<char, SafeAllocator<char> > >(s, profile);
	if (preparedData.empty()) {
		throw std::exception();
	}
	return createSafeByteArray(reinterpret_cast<const char*>(vecptr(preparedData)));
}

}

#endif