1 // Copyright (c) 2018 Daniel Kraft
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <names/encoding.h>
6
7 #include <logging.h>
8 #include <util/system.h>
9 #include <util/strencodings.h>
10
11 #include <univalue.h>
12
13 #include <cassert>
14 #include <sstream>
15
16 namespace
17 {
18
19 NameEncoding
EncodingFromOptions(const std::string & option,const NameEncoding defaultVal)20 EncodingFromOptions (const std::string& option, const NameEncoding defaultVal)
21 {
22 const std::string value
23 = gArgs.GetArg (option, EncodingToString (defaultVal));
24 try
25 {
26 return EncodingFromString (value);
27 }
28 catch (const std::invalid_argument& exc)
29 {
30 LogPrintf ("Invalid value for %s:\n %s\n falling back to default %s\n",
31 option, exc.what (), EncodingToString (defaultVal));
32 return defaultVal;
33 }
34 }
35
36 } // anonymous namespace
37
38 NameEncoding
ConfiguredNameEncoding()39 ConfiguredNameEncoding ()
40 {
41 return EncodingFromOptions ("-nameencoding", DEFAULT_NAME_ENCODING);
42 }
43
44 NameEncoding
ConfiguredValueEncoding()45 ConfiguredValueEncoding ()
46 {
47 return EncodingFromOptions ("-valueencoding", DEFAULT_VALUE_ENCODING);
48 }
49
50 NameEncoding
EncodingFromString(const std::string & str)51 EncodingFromString (const std::string& str)
52 {
53 if (str == "ascii")
54 return NameEncoding::ASCII;
55 if (str == "utf8")
56 return NameEncoding::UTF8;
57 if (str == "hex")
58 return NameEncoding::HEX;
59
60 throw std::invalid_argument ("invalid name/value encoding: " + str);
61 }
62
63 std::string
EncodingToString(NameEncoding enc)64 EncodingToString (NameEncoding enc)
65 {
66 switch (enc)
67 {
68 case NameEncoding::ASCII:
69 return "ascii";
70 case NameEncoding::UTF8:
71 return "utf8";
72 case NameEncoding::HEX:
73 return "hex";
74 }
75
76 /* NameEncoding values are only ever created internally in the binary
77 and not received externally (except as string). Thus it should never
78 happen (and if it does, is a severe bug) that we see an unexpected
79 value. */
80 assert (false);
81 }
82
83 namespace
84 {
85
86 bool
IsStringValid(const std::string & str,const NameEncoding enc)87 IsStringValid (const std::string& str, const NameEncoding enc)
88 {
89 switch (enc)
90 {
91 case NameEncoding::ASCII:
92 for (const unsigned char c : str)
93 if (c < 0x20 || c >= 0x80)
94 return false;
95 return true;
96
97 case NameEncoding::UTF8:
98 if (!IsValidUtf8String (str))
99 return false;
100 return true;
101
102 case NameEncoding::HEX:
103 return str == "" || IsHex (str);
104 }
105
106 assert (false);
107 }
108
109 void
VerifyStringValid(const std::string & str,const NameEncoding enc)110 VerifyStringValid (const std::string& str, const NameEncoding enc)
111 {
112 if (IsStringValid (str, enc))
113 return;
114
115 throw InvalidNameString (enc, str);
116 }
117
118 std::string
InvalidNameStringMessage(const NameEncoding enc,const std::string & invalidStr)119 InvalidNameStringMessage (const NameEncoding enc, const std::string& invalidStr)
120 {
121 std::ostringstream msg;
122 msg << "invalid string for encoding " << EncodingToString (enc) << ":"
123 << " " << invalidStr;
124 return msg.str ();
125 }
126
127 } // anonymous namespace
128
InvalidNameString(const NameEncoding enc,const std::string & invalidStr)129 InvalidNameString::InvalidNameString (const NameEncoding enc,
130 const std::string& invalidStr)
131 : std::invalid_argument (InvalidNameStringMessage (enc, invalidStr))
132 {}
133
134 std::string
EncodeName(const valtype & data,const NameEncoding enc)135 EncodeName (const valtype& data, const NameEncoding enc)
136 {
137 std::string res;
138 switch (enc)
139 {
140 case NameEncoding::ASCII:
141 case NameEncoding::UTF8:
142 res = std::string (data.begin (), data.end ());
143 break;
144
145 case NameEncoding::HEX:
146 res = HexStr (data);
147 break;
148 }
149
150 VerifyStringValid (res, enc);
151 return res;
152 }
153
154 valtype
DecodeName(const std::string & str,const NameEncoding enc)155 DecodeName (const std::string& str, const NameEncoding enc)
156 {
157 VerifyStringValid (str, enc);
158
159 switch (enc)
160 {
161 case NameEncoding::ASCII:
162 case NameEncoding::UTF8:
163 return valtype (str.begin (), str.end ());
164
165 case NameEncoding::HEX:
166 return ParseHex (str);
167 }
168
169 assert (false);
170 }
171
172 std::string
EncodeNameForMessage(const valtype & data)173 EncodeNameForMessage (const valtype& data)
174 {
175 try
176 {
177 return "'" + EncodeName (data, NameEncoding::ASCII) + "'";
178 }
179 catch (const InvalidNameString& exc)
180 {
181 return "0x" + EncodeName (data, NameEncoding::HEX);
182 }
183 }
184
185 void
AddEncodedNameToUniv(UniValue & obj,const std::string & key,const valtype & data,const NameEncoding enc)186 AddEncodedNameToUniv (UniValue& obj, const std::string& key,
187 const valtype& data, const NameEncoding enc)
188 {
189 try
190 {
191 obj.pushKV (key, EncodeName (data, enc));
192 }
193 catch (const InvalidNameString& exc)
194 {
195 obj.pushKV (key + "_error", "invalid data for " + EncodingToString (enc));
196 }
197 obj.pushKV (key + "_encoding", EncodingToString (enc));
198 }
199