blob: 3793ee675f2dce49aee8f291e1c68b58f5893f11 [file] [log] [blame]
// 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