OLD | NEW |
| (Empty) |
1 // Copyright 2008-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 #include "omaha/common/extra_args_parser.h" | |
17 #include "omaha/base/constants.h" | |
18 #include "omaha/base/debug.h" | |
19 #include "omaha/base/error.h" | |
20 #include "omaha/base/logging.h" | |
21 #include "omaha/base/string.h" | |
22 #include "omaha/base/utils.h" | |
23 #include "omaha/common/command_line.h" | |
24 #include "omaha/common/const_cmd_line.h" | |
25 #include "omaha/common/goopdate_utils.h" | |
26 | |
27 namespace omaha { | |
28 | |
29 namespace { | |
30 | |
31 HRESULT ConvertUtf8UrlEncodedString(const CString& encoded_string, | |
32 CString* unencoded_string) { | |
33 CString trimmed_val = encoded_string; | |
34 trimmed_val.Trim(); | |
35 if (trimmed_val.IsEmpty()) { | |
36 return E_INVALIDARG; | |
37 } | |
38 | |
39 // The value is a utf8 encoded url escaped string that is stored as a | |
40 // unicode string, convert it into a wide string. | |
41 CString app_name; | |
42 HRESULT hr = Utf8UrlEncodedStringToWideString(trimmed_val, unencoded_string); | |
43 if (FAILED(hr)) { | |
44 return hr; | |
45 } | |
46 | |
47 return S_OK; | |
48 } | |
49 | |
50 HRESULT Validate(const TCHAR* extra_args) { | |
51 CString extra_args_str(extra_args); | |
52 if (extra_args_str.IsEmpty()) { | |
53 return E_INVALIDARG; | |
54 } | |
55 | |
56 if (-1 != extra_args_str.FindOneOf(kDisallowedCharsInExtraArgs)) { | |
57 // A '/' was found in the "extra" arguments or "extra" arguments were | |
58 // not specified before the next command. | |
59 return E_INVALIDARG; | |
60 } | |
61 | |
62 return S_OK; | |
63 } | |
64 | |
65 // Handles tokens from the app arguments string. | |
66 HRESULT HandleAppArgsToken(const CString& token, | |
67 CommandLineExtraArgs* args, | |
68 int* cur_app_args_index) { | |
69 ASSERT1(args); | |
70 ASSERT1(cur_app_args_index); | |
71 ASSERT1(*cur_app_args_index < static_cast<int>(args->apps.size())); | |
72 | |
73 CString name; | |
74 CString value; | |
75 if (!ParseNameValuePair(token, kNameValueSeparatorChar, &name, &value)) { | |
76 return E_INVALIDARG; | |
77 } | |
78 | |
79 if (name.CompareNoCase(kExtraArgAppGuid) == 0) { | |
80 *cur_app_args_index = -1; | |
81 for (size_t i = 0; i < args->apps.size(); ++i) { | |
82 if (!value.CompareNoCase(GuidToString(args->apps[i].app_guid))) { | |
83 *cur_app_args_index = i; | |
84 break; | |
85 } | |
86 } | |
87 | |
88 if (-1 == *cur_app_args_index) { | |
89 return E_INVALIDARG; | |
90 } | |
91 } else if (name.CompareNoCase(kExtraArgInstallerData) == 0) { | |
92 if (-1 == *cur_app_args_index) { | |
93 return E_INVALIDARG; | |
94 } | |
95 args->apps[*cur_app_args_index].encoded_installer_data = value; | |
96 } else { | |
97 // Unrecognized token | |
98 return E_INVALIDARG; | |
99 } | |
100 | |
101 return S_OK; | |
102 } | |
103 | |
104 HRESULT ParseAppArgs(const TCHAR* app_args, CommandLineExtraArgs* args) { | |
105 if (!app_args || !*app_args) { | |
106 return S_OK; | |
107 } | |
108 | |
109 HRESULT hr = Validate(app_args); | |
110 if (FAILED(hr)) { | |
111 return hr; | |
112 } | |
113 | |
114 int cur_app_args_index = -1; | |
115 int pos = 0; | |
116 CString input_str = app_args; | |
117 CString token = input_str.Tokenize(kExtraArgsSeparators, pos); | |
118 while (!token.IsEmpty()) { | |
119 hr = HandleAppArgsToken(token, args, &cur_app_args_index); | |
120 if (FAILED(hr)) { | |
121 return hr; | |
122 } | |
123 | |
124 token = input_str.Tokenize(kExtraArgsSeparators, pos); | |
125 } | |
126 | |
127 return S_OK; | |
128 } | |
129 | |
130 HRESULT StringToNeedsAdmin(const TCHAR* str, NeedsAdmin* value) { | |
131 ASSERT1(str); | |
132 ASSERT1(value); | |
133 | |
134 const TCHAR* const kFalse = _T("false"); | |
135 const TCHAR* const kTrue = _T("true"); | |
136 const TCHAR* const kPrefers = _T("prefers"); | |
137 | |
138 if (_tcsicmp(kFalse, str) == 0) { | |
139 *value = NEEDS_ADMIN_NO; | |
140 } else if (_tcsicmp(kTrue, str) == 0) { | |
141 *value = NEEDS_ADMIN_YES; | |
142 } else if (_tcsicmp(kPrefers, str) == 0) { | |
143 *value = NEEDS_ADMIN_PREFERS; | |
144 } else { | |
145 return E_INVALIDARG; | |
146 } | |
147 return S_OK; | |
148 } | |
149 | |
150 } // namespace | |
151 | |
152 // If no bundle name is specified, the first app's name is used. | |
153 // TODO(omaha): There is no enforcement of required or optional values of | |
154 // the extra arguments. Come up with a way to enforce this. | |
155 // TODO(omaha): If we prefix extra_args with '/' and replace all '=' with ' ' | |
156 // and all & with '/' then we should be able to use the CommandLineParser | |
157 // class to pull out the values here. We'd need to define scenarios for all | |
158 // permutations of ExtraArgs, but this shouldn't be difficult to get the right | |
159 // ones. | |
160 | |
161 HRESULT ExtraArgsParser::Parse(const TCHAR* extra_args, | |
162 const TCHAR* app_args, | |
163 CommandLineExtraArgs* args) { | |
164 HRESULT hr = ParseExtraArgs(extra_args, args); | |
165 if (FAILED(hr)) { | |
166 return hr; | |
167 } | |
168 | |
169 return ParseAppArgs(app_args, args); | |
170 } | |
171 | |
172 HRESULT ExtraArgsParser::ParseExtraArgs(const TCHAR* extra_args, | |
173 CommandLineExtraArgs* args) { | |
174 HRESULT hr = Validate(extra_args); | |
175 if (FAILED(hr)) { | |
176 return hr; | |
177 } | |
178 | |
179 first_app_ = true; | |
180 int pos = 0; | |
181 CString input_str(extra_args); | |
182 CString token = input_str.Tokenize(kExtraArgsSeparators, pos); | |
183 while (!token.IsEmpty()) { | |
184 CORE_LOG(L2, (_T("[ExtraArgsParser::Parse][token=%s]"), token)); | |
185 hr = HandleToken(token, args); | |
186 if (FAILED(hr)) { | |
187 return hr; | |
188 } | |
189 | |
190 // Continue parsing | |
191 token = input_str.Tokenize(kExtraArgsSeparators, pos); | |
192 } | |
193 | |
194 // Save the arguments for the last application. | |
195 args->apps.push_back(cur_extra_app_args_); | |
196 | |
197 ASSERT1(!(args->runtime_only && args->apps[0].app_guid != GUID_NULL)); | |
198 | |
199 if (args->bundle_name.IsEmpty()) { | |
200 ASSERT1(!args->apps.empty()); | |
201 args->bundle_name = args->apps[0].app_name; | |
202 } | |
203 | |
204 return S_OK; | |
205 } | |
206 | |
207 // Handles tokens from the extra arguments string. | |
208 HRESULT ExtraArgsParser::HandleToken(const CString& token, | |
209 CommandLineExtraArgs* args) { | |
210 CString name; | |
211 CString value; | |
212 if (!ParseNameValuePair(token, kNameValueSeparatorChar, &name, &value)) { | |
213 return E_INVALIDARG; | |
214 } | |
215 | |
216 // The first set of args apply to all apps. They may occur at any point, but | |
217 // only the last occurrence is recorded. | |
218 if (name.CompareNoCase(kExtraArgBundleName) == 0) { | |
219 if (value.GetLength() > kMaxNameLength) { | |
220 return E_INVALIDARG; | |
221 } | |
222 HRESULT hr = ConvertUtf8UrlEncodedString(value, | |
223 &args->bundle_name); | |
224 if (FAILED(hr)) { | |
225 return hr; | |
226 } | |
227 } else if (name.CompareNoCase(kExtraArgInstallationId) == 0) { | |
228 ASSERT1(!value.IsEmpty()); | |
229 if (FAILED(StringToGuidSafe(value, &args->installation_id))) { | |
230 return E_INVALIDARG; | |
231 } | |
232 } else if (name.CompareNoCase(kExtraArgBrandCode) == 0) { | |
233 if (value.GetLength() > kBrandIdLength) { | |
234 return E_INVALIDARG; | |
235 } | |
236 args->brand_code = value; | |
237 } else if (name.CompareNoCase(kExtraArgClientId) == 0) { | |
238 args->client_id = value; | |
239 } else if (name.CompareNoCase(kExtraArgOmahaExperimentLabels) == 0) { | |
240 HRESULT hr = ConvertUtf8UrlEncodedString(value, | |
241 &args->experiment_labels); | |
242 if (FAILED(hr)) { | |
243 return hr; | |
244 } | |
245 } else if (name.CompareNoCase(kExtraArgReferralId) == 0) { | |
246 args->referral_id = value; | |
247 } else if (name.CompareNoCase(kExtraArgBrowserType) == 0) { | |
248 BrowserType type = BROWSER_UNKNOWN; | |
249 if (SUCCEEDED(goopdate_utils::ConvertStringToBrowserType(value, &type))) { | |
250 args->browser_type = type; | |
251 } | |
252 } else if (name.CompareNoCase(kExtraArgLanguage) == 0) { | |
253 if (value.GetLength() > kLangMaxLength) { | |
254 return E_INVALIDARG; | |
255 } | |
256 // Even if we don't support the language, we want to pass it to the | |
257 // installer. Omaha will pick its language later. See http://b/1336966. | |
258 args->language = value; | |
259 } else if (name.CompareNoCase(kExtraArgUsageStats) == 0) { | |
260 if (!String_StringToTristate(value, &args->usage_stats_enable)) { | |
261 return E_INVALIDARG; | |
262 } | |
263 } else if (name.CompareNoCase(kExtraArgRuntime) == 0) { | |
264 if (!args->apps.empty() || cur_extra_app_args_.app_guid != GUID_NULL) { | |
265 return E_INVALIDARG; | |
266 } | |
267 args->runtime_only = true; | |
268 | |
269 // The following args are per-app. | |
270 } else if (name.CompareNoCase(kExtraArgAdditionalParameters) == 0) { | |
271 cur_extra_app_args_.ap = value; | |
272 } else if (name.CompareNoCase(kExtraArgTTToken) == 0) { | |
273 cur_extra_app_args_.tt_token = value; | |
274 } else if (name.CompareNoCase(kExtraArgExperimentLabels) == 0) { | |
275 HRESULT hr = ConvertUtf8UrlEncodedString( | |
276 value, | |
277 &cur_extra_app_args_.experiment_labels); | |
278 if (FAILED(hr)) { | |
279 return hr; | |
280 } | |
281 } else if (name.CompareNoCase(kExtraArgAppGuid) == 0) { | |
282 if (!first_app_) { | |
283 // Save the arguments for the application we have been processing. | |
284 args->apps.push_back(cur_extra_app_args_); | |
285 } | |
286 cur_extra_app_args_ = CommandLineAppArgs(); | |
287 | |
288 HRESULT hr = StringToGuidSafe(value, &cur_extra_app_args_.app_guid); | |
289 if (FAILED(hr)) { | |
290 return hr; | |
291 } | |
292 if (cur_extra_app_args_.app_guid == GUID_NULL || args->runtime_only) { | |
293 return E_INVALIDARG; | |
294 } | |
295 first_app_ = false; | |
296 } else if (name.CompareNoCase(kExtraArgAppName) == 0) { | |
297 if (value.GetLength() > kMaxNameLength) { | |
298 return E_INVALIDARG; | |
299 } | |
300 HRESULT hr = ConvertUtf8UrlEncodedString(value, | |
301 &cur_extra_app_args_.app_name); | |
302 if (FAILED(hr)) { | |
303 return hr; | |
304 } | |
305 } else if (name.CompareNoCase(kExtraArgNeedsAdmin) == 0) { | |
306 if (FAILED(StringToNeedsAdmin(value, &cur_extra_app_args_.needs_admin))) { | |
307 return E_INVALIDARG; | |
308 } | |
309 } else if (name.CompareNoCase(kExtraArgInstallDataIndex) == 0) { | |
310 cur_extra_app_args_.install_data_index = value; | |
311 } else { | |
312 // Unrecognized token | |
313 return E_INVALIDARG; | |
314 } | |
315 | |
316 return S_OK; | |
317 } | |
318 | |
319 } // namespace omaha | |
OLD | NEW |