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