1 #include "url/url_util_qt.h"
2 
3 #include "base/command_line.h"
4 #include "base/no_destructor.h"
5 #include "base/numerics/safe_conversions.h"
6 #include "base/strings/string_number_conversions.h"
7 #include "base/strings/string_util.h"
8 #include "url/gurl.h"
9 #include "url/url_canon.h"
10 #include "url/url_util.h"
11 
12 namespace url {
13 
14 namespace {
15 
ToString(const CustomScheme & cs)16 std::string ToString(const CustomScheme& cs)
17 {
18   std::string serialized;
19 
20   serialized += cs.name;
21   serialized += ':';
22 
23   switch (cs.type) {
24   case SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION:
25     serialized += 'u';
26     serialized += base::NumberToString(cs.default_port);
27     break;
28   case SCHEME_WITH_HOST_AND_PORT:
29     serialized += 'p';
30     serialized += base::NumberToString(cs.default_port);
31     break;
32   case SCHEME_WITH_HOST:
33     serialized += 'h';
34     break;
35   case SCHEME_WITHOUT_AUTHORITY:
36     break;
37   }
38 
39   if (cs.flags & CustomScheme::Secure)
40     serialized += 's';
41   if (cs.flags & CustomScheme::Local)
42     serialized += 'l';
43   if (cs.flags & CustomScheme::LocalAccessAllowed)
44     serialized += 'L';
45   if (cs.flags & CustomScheme::NoAccessAllowed)
46     serialized += 'N';
47   if (cs.flags & CustomScheme::ServiceWorkersAllowed)
48     serialized += 'W';
49   if (cs.flags & CustomScheme::ViewSourceAllowed)
50     serialized += 'V';
51   if (cs.flags & CustomScheme::ContentSecurityPolicyIgnored)
52     serialized += 'C';
53   if (cs.flags & CustomScheme::CorsEnabled)
54     serialized += 'F';
55 
56   return serialized;
57 }
58 
59 class Parser {
60 public:
CharacterArrived(char ch)61   void CharacterArrived(char ch) {
62     switch (state) {
63     case NAME: CharacterArrivedWhileParsingName(ch); break;
64     case OPTIONS: CharacterArrivedWhileParsingOptions(ch); break;
65     case PORT: CharacterArrivedWhileParsingPort(ch); break;
66     }
67   }
68 
EndReached()69   void EndReached() {
70     if (!default_port_string.empty())
71       FlushPort();
72     if (!cs.name.empty())
73       Flush();
74   }
75 
76 private:
CharacterArrivedWhileParsingName(char ch)77   void CharacterArrivedWhileParsingName(char ch) {
78     switch (ch) {
79     case ':': state = OPTIONS; break;
80     case ';': Flush(); break;
81     default: cs.name += ch; break;
82     }
83   }
84 
CharacterArrivedWhileParsingOptions(char ch)85   void CharacterArrivedWhileParsingOptions(char ch) {
86     switch (ch) {
87     case 'u': cs.type = SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION; state = PORT; break;
88     case 'p': cs.type = SCHEME_WITH_HOST_AND_PORT; state = PORT; break;
89     case 'h': cs.type = SCHEME_WITH_HOST; break;
90     case 's': cs.flags |= CustomScheme::Secure; break;
91     case 'l': cs.flags |= CustomScheme::Local; break;
92     case 'L': cs.flags |= CustomScheme::LocalAccessAllowed; break;
93     case 'N': cs.flags |= CustomScheme::NoAccessAllowed; break;
94     case 'W': cs.flags |= CustomScheme::ServiceWorkersAllowed; break;
95     case 'V': cs.flags |= CustomScheme::ViewSourceAllowed; break;
96     case 'C': cs.flags |= CustomScheme::ContentSecurityPolicyIgnored; break;
97     case 'F': cs.flags |= CustomScheme::CorsEnabled; break;
98     case ';': Flush(); state = NAME; break;
99     default: CHECK(false) << "Unexpected character '" << ch << "'.";
100     }
101   }
102 
CharacterArrivedWhileParsingPort(char ch)103   void CharacterArrivedWhileParsingPort(char ch) {
104     if (base::IsAsciiDigit(ch)) {
105       default_port_string += ch;
106       return;
107     }
108 
109     FlushPort();
110 
111     state = OPTIONS;
112     CharacterArrivedWhileParsingOptions(ch);
113   }
114 
FlushPort()115   void FlushPort() {
116     CHECK(base::StringToInt(default_port_string, &cs.default_port))
117       << "Failed to parse '" << default_port_string << "'.";
118     default_port_string.clear();
119   }
120 
Flush()121   void Flush() {
122     CustomScheme::AddScheme(cs);
123     cs = CustomScheme();
124   }
125 
126   enum { NAME, OPTIONS, PORT } state = NAME;
127   CustomScheme cs;
128   std::string default_port_string;
129 };
130 
131 } // namespace
132 
GetMutableSchemes()133 std::vector<CustomScheme>& CustomScheme::GetMutableSchemes() {
134   static base::NoDestructor<std::vector<CustomScheme>> schemes;
135   return *schemes;
136 }
137 
GetSchemes()138 const std::vector<CustomScheme>& CustomScheme::GetSchemes() {
139   return GetMutableSchemes();
140 }
141 
ClearSchemes()142 void CustomScheme::ClearSchemes()
143 {
144   GetMutableSchemes().clear();
145 }
146 
AddScheme(const CustomScheme & cs)147 void CustomScheme::AddScheme(const CustomScheme& cs)
148 {
149   DCHECK(!cs.name.empty());
150   DCHECK_EQ(cs.has_port_component(), (cs.default_port != PORT_UNSPECIFIED))
151     << "Scheme '" << cs.name << "' has invalid configuration.";
152   DCHECK_EQ(base::ToLowerASCII(cs.name), cs.name)
153     << "Scheme '" << cs.name << "' should be lower-case.";
154   DCHECK(!FindScheme(cs.name))
155     << "Scheme '" << cs.name << "' already added.";
156 
157   GetMutableSchemes().push_back(cs);
158 }
159 
FindScheme(base::StringPiece name)160 const CustomScheme* CustomScheme::FindScheme(base::StringPiece name)
161 {
162   for (const CustomScheme& cs : GetSchemes())
163     if (base::LowerCaseEqualsASCII(name, cs.name))
164       return &cs;
165   return nullptr;
166 }
167 
168 const char CustomScheme::kCommandLineFlag[] = "webengine-schemes";
169 
SaveSchemes(base::CommandLine * command_line)170 void CustomScheme::SaveSchemes(base::CommandLine* command_line)
171 {
172   std::string serialized;
173 
174   for (const CustomScheme& cs : GetSchemes()) {
175     if (!serialized.empty())
176       serialized += ';';
177     serialized += ToString(cs);
178   }
179 
180   command_line->AppendSwitchASCII(kCommandLineFlag, std::move(serialized));
181 }
182 
LoadSchemes(const base::CommandLine * command_line)183 void CustomScheme::LoadSchemes(const base::CommandLine* command_line)
184 {
185   std::string serialized = command_line->GetSwitchValueASCII(kCommandLineFlag);
186   Parser parser;
187   for (char ch : serialized)
188     parser.CharacterArrived(ch);
189   parser.EndReached();
190 }
191 
192 } // namespace url
193