1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Library functions related to the OEM Deal Confirmation Code.
7 #include "rlz/win/lib/machine_deal.h"
12 #include "base/basictypes.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/win/registry.h"
18 #include "rlz/lib/assert.h"
19 #include "rlz/lib/lib_values.h"
20 #include "rlz/win/lib/lib_mutex.h"
21 #include "rlz/win/lib/registry_util.h"
22 #include "rlz/win/lib/rlz_value_store_registry.h"
26 const wchar_t kDccValueName
[] = L
"DCC";
28 // Current DCC can only uses [a-zA-Z0-9_-!@$*();.<>,:]
29 // We will be more liberal and allow some additional chars, but not url meta
31 bool IsGoodDccChar(char ch
) {
32 if (base::IsAsciiAlpha(ch
) || base::IsAsciiDigit(ch
))
56 // This function will remove bad rlz chars and also limit the max rlz to some
57 // reasonable size. It also assumes that normalized_dcc is at least
58 // kMaxDccLength+1 long.
59 void NormalizeDcc(const char* raw_dcc
, char* normalized_dcc
) {
61 for (; raw_dcc
[index
] != 0 && index
< rlz_lib::kMaxDccLength
; ++index
) {
62 char current
= raw_dcc
[index
];
63 if (IsGoodDccChar(current
)) {
64 normalized_dcc
[index
] = current
;
66 normalized_dcc
[index
] = '.';
70 normalized_dcc
[index
] = 0;
73 bool GetResponseLine(const char* response_text
, int response_length
,
74 int* search_index
, std::string
* response_line
) {
75 if (!response_line
|| !search_index
|| *search_index
> response_length
)
78 response_line
->clear();
80 if (*search_index
< 0)
83 int line_begin
= *search_index
;
84 const char* line_end
= strchr(response_text
+ line_begin
, '\n');
86 if (line_end
== NULL
|| line_end
- response_text
> response_length
) {
87 line_end
= response_text
+ response_length
;
90 *search_index
= line_end
- response_text
+ 1;
93 response_line
->assign(response_text
+ line_begin
,
94 line_end
- response_text
- line_begin
);
98 bool GetResponseValue(const std::string
& response_line
,
99 const std::string
& response_key
,
100 std::string
* value
) {
106 if (!base::StartsWith(response_line
, response_key
,
107 base::CompareCase::SENSITIVE
))
110 std::vector
<std::string
> tokens
= base::SplitString(
111 response_line
, ":", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
112 if (tokens
.size() != 2)
115 // The first token is the key, the second is the value. The value is already
116 // trimmed for whitespace.
125 bool MachineDealCode::Set(const char* dcc
) {
130 // TODO: if (!ProcessInfo::CanWriteMachineKey()) return false;
132 // Validate the new dcc value.
133 size_t length
= strlen(dcc
);
134 if (length
> kMaxDccLength
) {
135 ASSERT_STRING("MachineDealCode::Set: DCC length is exceeds max allowed.");
139 base::win::RegKey
hklm_key(HKEY_LOCAL_MACHINE
,
140 RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
141 KEY_READ
| KEY_WRITE
| KEY_WOW64_32KEY
);
142 if (!hklm_key
.Valid()) {
143 ASSERT_STRING("MachineDealCode::Set: Unable to create / open machine key."
144 " Did you call rlz_lib::CreateMachineState()?");
148 char normalized_dcc
[kMaxDccLength
+ 1];
149 NormalizeDcc(dcc
, normalized_dcc
);
150 VERIFY(length
== strlen(normalized_dcc
));
152 // Write the DCC to HKLM. Note that we need to include the null character
153 // when writing the string.
154 if (!RegKeyWriteValue(&hklm_key
, kDccValueName
, normalized_dcc
)) {
155 ASSERT_STRING("MachineDealCode::Set: Could not write the DCC value");
162 bool MachineDealCode::GetNewCodeFromPingResponse(const char* response
,
163 bool* has_new_dcc
, char* new_dcc
, int new_dcc_size
) {
164 if (!has_new_dcc
|| !new_dcc
|| !new_dcc_size
)
167 *has_new_dcc
= false;
170 int response_length
= -1;
171 if (!IsPingResponseValid(response
, &response_length
))
174 // Get the current DCC value to compare to later)
175 char stored_dcc
[kMaxDccLength
+ 1];
176 if (!Get(stored_dcc
, arraysize(stored_dcc
)))
179 int search_index
= 0;
180 std::string response_line
;
181 std::string new_dcc_value
;
182 bool old_dcc_confirmed
= false;
183 const std::string
dcc_cgi(kDccCgiVariable
);
184 const std::string
dcc_cgi_response(kSetDccResponseVariable
);
185 while (GetResponseLine(response
, response_length
, &search_index
,
189 if (!old_dcc_confirmed
&&
190 GetResponseValue(response_line
, dcc_cgi
, &value
)) {
191 // This is the old DCC confirmation - should match value in registry.
192 if (value
!= stored_dcc
)
193 return false; // Corrupted DCC - ignore this response.
195 old_dcc_confirmed
= true;
199 if (!(*has_new_dcc
) &&
200 GetResponseValue(response_line
, dcc_cgi_response
, &value
)) {
201 // This is the new DCC.
202 if (value
.size() > kMaxDccLength
) continue; // Too long
204 new_dcc_value
= value
;
208 old_dcc_confirmed
|= (NULL
== stored_dcc
[0]);
210 base::strlcpy(new_dcc
, new_dcc_value
.c_str(), new_dcc_size
);
211 return old_dcc_confirmed
;
214 bool MachineDealCode::SetFromPingResponse(const char* response
) {
215 bool has_new_dcc
= false;
216 char new_dcc
[kMaxDccLength
+ 1];
218 bool response_valid
= GetNewCodeFromPingResponse(
219 response
, &has_new_dcc
, new_dcc
, arraysize(new_dcc
));
221 if (response_valid
&& has_new_dcc
)
224 return response_valid
;
227 bool MachineDealCode::GetAsCgi(char* cgi
, int cgi_size
) {
228 if (!cgi
|| cgi_size
<= 0) {
229 ASSERT_STRING("MachineDealCode::GetAsCgi: Invalid buffer");
236 base::StringAppendF(&cgi_arg
, "%s=", kDccCgiVariable
);
237 int cgi_arg_length
= cgi_arg
.size();
239 if (cgi_arg_length
>= cgi_size
) {
240 ASSERT_STRING("MachineDealCode::GetAsCgi: Insufficient buffer size");
244 base::strlcpy(cgi
, cgi_arg
.c_str(), cgi_size
);
246 if (!Get(cgi
+ cgi_arg_length
, cgi_size
- cgi_arg_length
)) {
253 bool MachineDealCode::Get(char* dcc
, int dcc_size
) {
258 if (!dcc
|| dcc_size
<= 0) {
259 ASSERT_STRING("MachineDealCode::Get: Invalid buffer");
265 base::win::RegKey
dcc_key(HKEY_LOCAL_MACHINE
,
266 RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
267 KEY_READ
| KEY_WOW64_32KEY
);
268 if (!dcc_key
.Valid())
269 return false; // no DCC key.
271 size_t size
= dcc_size
;
272 if (!RegKeyReadValue(dcc_key
, kDccValueName
, dcc
, &size
)) {
273 ASSERT_STRING("MachineDealCode::Get: Insufficient buffer size");
281 bool MachineDealCode::Clear() {
282 base::win::RegKey
dcc_key(HKEY_LOCAL_MACHINE
,
283 RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
284 KEY_READ
| KEY_WRITE
| KEY_WOW64_32KEY
);
285 if (!dcc_key
.Valid())
286 return false; // no DCC key.
288 dcc_key
.DeleteValue(kDccValueName
);
291 wchar_t dcc
[kMaxDccLength
+ 1];
292 DWORD dcc_size
= arraysize(dcc
);
293 if (dcc_key
.ReadValue(kDccValueName
, dcc
, &dcc_size
, NULL
) == ERROR_SUCCESS
) {
294 ASSERT_STRING("MachineDealCode::Clear: Could not delete the DCC value.");
301 } // namespace rlz_lib