// Copyright 2008-2009 Google Inc. | |
// | |
// Licensed under the Apache License, Version 2.0 (the "License"); | |
// you may not use this file except in compliance with the License. | |
// You may obtain a copy of the License at | |
// | |
// http://www.apache.org/licenses/LICENSE-2.0 | |
// | |
// Unless required by applicable law or agreed to in writing, software | |
// distributed under the License is distributed on an "AS IS" BASIS, | |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
// See the License for the specific language governing permissions and | |
// limitations under the License. | |
// ======================================================================== | |
#include "omaha/goopdate/extra_args_parser.h" | |
#include "omaha/common/const_cmd_line.h" | |
#include "omaha/common/constants.h" | |
#include "omaha/common/debug.h" | |
#include "omaha/common/error.h" | |
#include "omaha/common/logging.h" | |
#include "omaha/common/string.h" | |
#include "omaha/common/utils.h" | |
#include "omaha/goopdate/command_line.h" | |
#include "omaha/goopdate/goopdate_utils.h" | |
namespace omaha { | |
// TODO(omaha): There is no enforcement of required or optional values of | |
// the extra arguments. Come up with a way to enforce this. | |
HRESULT ExtraArgsParser::Parse(const TCHAR* extra_args, | |
const TCHAR* app_args, | |
CommandLineExtraArgs* args) { | |
// TODO(omaha): If we prefix extra_args with '/' and replace all '=' with ' ' | |
// and all & with '/' then we should be able to use the CommandLineParser | |
// class to pull out the values here. We'd need to define scenarios for all | |
// permutations of ExtraArgs, but this shouldn't be difficult to get the right | |
// ones. | |
HRESULT hr = Validate(extra_args); | |
if (FAILED(hr)) { | |
return hr; | |
} | |
first_app_ = true; | |
int pos = 0; | |
CString input_str(extra_args); | |
CString token = input_str.Tokenize(kExtraArgsSeparators, pos); | |
while (!token.IsEmpty()) { | |
CORE_LOG(L2, (_T("[ExtraArgsParser::Parse][token=%s]"), token)); | |
hr = HandleToken(token, args); | |
if (FAILED(hr)) { | |
return hr; | |
} | |
// Continue parsing | |
token = input_str.Tokenize(kExtraArgsSeparators, pos); | |
} | |
// Save the arguments for the last application. | |
args->apps.push_back(cur_extra_app_args_); | |
return ParseAppArgs(app_args, args); | |
} | |
HRESULT ExtraArgsParser::ParseAppArgs(const TCHAR* app_args, | |
CommandLineExtraArgs* args) { | |
if (!app_args || !*app_args) { | |
return S_OK; | |
} | |
HRESULT hr = Validate(app_args); | |
if (FAILED(hr)) { | |
return hr; | |
} | |
int cur_app_args_index = -1; | |
int pos = 0; | |
CString input_str = app_args; | |
CString token = input_str.Tokenize(kExtraArgsSeparators, pos); | |
while (!token.IsEmpty()) { | |
CORE_LOG(L2, (_T("[ExtraArgsParser::ParseAppArgs][token=%s]"), token)); | |
hr = HandleAppArgsToken(token, args, &cur_app_args_index); | |
if (FAILED(hr)) { | |
return hr; | |
} | |
token = input_str.Tokenize(kExtraArgsSeparators, pos); | |
} | |
return S_OK; | |
} | |
HRESULT ExtraArgsParser::Validate(const TCHAR* extra_args) { | |
CString extra_args_str(extra_args); | |
if (extra_args_str.IsEmpty()) { | |
return E_INVALIDARG; | |
} | |
if (-1 != extra_args_str.FindOneOf(kDisallowedCharsInExtraArgs)) { | |
// A '/' was found in the "extra" arguments or "extra" arguments were | |
// not specified before the next command. | |
return E_INVALIDARG; | |
} | |
return S_OK; | |
} | |
// Handles tokens from the extra arguments string. | |
HRESULT ExtraArgsParser::HandleToken(const CString& token, | |
CommandLineExtraArgs* args) { | |
CString name; | |
CString value; | |
if (!ParseNameValuePair(token, kNameValueSeparatorChar, &name, &value)) { | |
return E_INVALIDARG; | |
} | |
// The first set of args apply to all apps. They may occur at any point, but | |
// only the last occurrence is recorded. | |
if (name.CompareNoCase(kExtraArgInstallationId) == 0) { | |
ASSERT1(!value.IsEmpty()); | |
if (FAILED(::CLSIDFromString(const_cast<TCHAR*>(value.GetString()), | |
&args->installation_id))) { | |
return E_INVALIDARG; | |
} | |
} else if (name.CompareNoCase(kExtraArgBrandCode) == 0) { | |
if (value.GetLength() > kBrandIdLength) { | |
return E_INVALIDARG; | |
} | |
args->brand_code = value; | |
} else if (name.CompareNoCase(kExtraArgClientId) == 0) { | |
args->client_id = value; | |
} else if (name.CompareNoCase(kExtraArgReferralId) == 0) { | |
args->referral_id = value; | |
} else if (name.CompareNoCase(kExtraArgBrowserType) == 0) { | |
BrowserType type = BROWSER_UNKNOWN; | |
if (SUCCEEDED(goopdate_utils::ConvertStringToBrowserType(value, &type))) { | |
args->browser_type = type; | |
} | |
} else if (name.CompareNoCase(kExtraArgLanguage) == 0) { | |
if (value.GetLength() > kLangMaxLength) { | |
return E_INVALIDARG; | |
} | |
// Even if we don't support the language, we want to pass it to the | |
// installer. Omaha will pick its language later. See http://b/1336966. | |
args->language = value; | |
} else if (name.CompareNoCase(kExtraArgUsageStats) == 0) { | |
if (!String_StringToTristate(value, &args->usage_stats_enable)) { | |
return E_INVALIDARG; | |
} | |
// The following args are per app. | |
} else if (name.CompareNoCase(kExtraArgAdditionalParameters) == 0) { | |
cur_extra_app_args_.ap = value; | |
} else if (name.CompareNoCase(kExtraArgTTToken) == 0) { | |
cur_extra_app_args_.tt_token = value; | |
} else if (name.CompareNoCase(kExtraArgAppGuid) == 0) { | |
if (!first_app_) { | |
// Save the arguments for the application we have been processing. | |
args->apps.push_back(cur_extra_app_args_); | |
} | |
cur_extra_app_args_ = CommandLineAppArgs(); | |
cur_extra_app_args_.app_guid = StringToGuid(value); | |
if (cur_extra_app_args_.app_guid == GUID_NULL) { | |
return E_INVALIDARG; | |
} | |
first_app_ = false; | |
} else if (name.CompareNoCase(kExtraArgAppName) == 0) { | |
if (value.GetLength() > kMaxAppNameLength) { | |
return E_INVALIDARG; | |
} | |
CString trimmed_val = value.Trim(); | |
if (trimmed_val.IsEmpty()) { | |
return E_INVALIDARG; | |
} | |
// The value is a utf8 encoded url escaped string that is stored as a | |
// unicode string, convert it into a wide string. | |
CString app_name; | |
HRESULT hr = Utf8UrlEncodedStringToWideString(trimmed_val, &app_name); | |
if (FAILED(hr)) { | |
return hr; | |
} | |
cur_extra_app_args_.app_name = app_name; | |
} else if (name.CompareNoCase(kExtraArgNeedsAdmin) == 0) { | |
if (FAILED(String_StringToBool(value, &cur_extra_app_args_.needs_admin))) { | |
return E_INVALIDARG; | |
} | |
} else if (name.CompareNoCase(kExtraArgInstallDataIndex) == 0) { | |
cur_extra_app_args_.install_data_index = value; | |
} else { | |
// Unrecognized token | |
return E_INVALIDARG; | |
} | |
return S_OK; | |
} | |
// Handles tokens from the app arguments string. | |
HRESULT ExtraArgsParser::HandleAppArgsToken(const CString& token, | |
CommandLineExtraArgs* args, | |
int* cur_app_args_index) { | |
ASSERT1(args); | |
ASSERT1(cur_app_args_index); | |
ASSERT1(*cur_app_args_index < static_cast<int>(args->apps.size())); | |
CString name; | |
CString value; | |
if (!ParseNameValuePair(token, kNameValueSeparatorChar, &name, &value)) { | |
return E_INVALIDARG; | |
} | |
if (name.CompareNoCase(kExtraArgAppGuid) == 0) { | |
*cur_app_args_index = -1; | |
for (size_t i = 0; i < args->apps.size(); ++i) { | |
if (!value.CompareNoCase(GuidToString(args->apps[i].app_guid))) { | |
*cur_app_args_index = i; | |
break; | |
} | |
} | |
if (-1 == *cur_app_args_index) { | |
return E_INVALIDARG; | |
} | |
} else if (name.CompareNoCase(kExtraArgInstallerData) == 0) { | |
if (-1 == *cur_app_args_index) { | |
return E_INVALIDARG; | |
} | |
args->apps[*cur_app_args_index].encoded_installer_data = value; | |
} else { | |
// Unrecognized token | |
return E_INVALIDARG; | |
} | |
return S_OK; | |
} | |
} // namespace omaha | |