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"
24 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 (IsAsciiAlpha(ch
) || 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 (!StartsWithASCII(response_line
, response_key
, true))
109 std::vector
<std::string
> tokens
;
110 base::SplitString(response_line
, ':', &tokens
);
111 if (tokens
.size() != 2)
114 // The first token is the key, the second is the value. The value is already
115 // trimmed for whitespace.
120 } // namespace anonymous
124 bool MachineDealCode::Set(const char* dcc
) {
129 // TODO: if (!ProcessInfo::CanWriteMachineKey()) return false;
131 // Validate the new dcc value.
132 size_t length
= strlen(dcc
);
133 if (length
> kMaxDccLength
) {
134 ASSERT_STRING("MachineDealCode::Set: DCC length is exceeds max allowed.");
138 base::win::RegKey
hklm_key(HKEY_LOCAL_MACHINE
,
139 RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
140 KEY_READ
| KEY_WRITE
| KEY_WOW64_32KEY
);
141 if (!hklm_key
.Valid()) {
142 ASSERT_STRING("MachineDealCode::Set: Unable to create / open machine key."
143 " Did you call rlz_lib::CreateMachineState()?");
147 char normalized_dcc
[kMaxDccLength
+ 1];
148 NormalizeDcc(dcc
, normalized_dcc
);
149 VERIFY(length
== strlen(normalized_dcc
));
151 // Write the DCC to HKLM. Note that we need to include the null character
152 // when writing the string.
153 if (!RegKeyWriteValue(hklm_key
, kDccValueName
, normalized_dcc
)) {
154 ASSERT_STRING("MachineDealCode::Set: Could not write the DCC value");
161 bool MachineDealCode::GetNewCodeFromPingResponse(const char* response
,
162 bool* has_new_dcc
, char* new_dcc
, int new_dcc_size
) {
163 if (!has_new_dcc
|| !new_dcc
|| !new_dcc_size
)
166 *has_new_dcc
= false;
169 int response_length
= -1;
170 if (!IsPingResponseValid(response
, &response_length
))
173 // Get the current DCC value to compare to later)
174 char stored_dcc
[kMaxDccLength
+ 1];
175 if (!Get(stored_dcc
, arraysize(stored_dcc
)))
178 int search_index
= 0;
179 std::string response_line
;
180 std::string new_dcc_value
;
181 bool old_dcc_confirmed
= false;
182 const std::string
dcc_cgi(kDccCgiVariable
);
183 const std::string
dcc_cgi_response(kSetDccResponseVariable
);
184 while (GetResponseLine(response
, response_length
, &search_index
,
188 if (!old_dcc_confirmed
&&
189 GetResponseValue(response_line
, dcc_cgi
, &value
)) {
190 // This is the old DCC confirmation - should match value in registry.
191 if (value
!= stored_dcc
)
192 return false; // Corrupted DCC - ignore this response.
194 old_dcc_confirmed
= true;
198 if (!(*has_new_dcc
) &&
199 GetResponseValue(response_line
, dcc_cgi_response
, &value
)) {
200 // This is the new DCC.
201 if (value
.size() > kMaxDccLength
) continue; // Too long
203 new_dcc_value
= value
;
207 old_dcc_confirmed
|= (NULL
== stored_dcc
[0]);
209 base::strlcpy(new_dcc
, new_dcc_value
.c_str(), new_dcc_size
);
210 return old_dcc_confirmed
;
213 bool MachineDealCode::SetFromPingResponse(const char* response
) {
214 bool has_new_dcc
= false;
215 char new_dcc
[kMaxDccLength
+ 1];
217 bool response_valid
= GetNewCodeFromPingResponse(
218 response
, &has_new_dcc
, new_dcc
, arraysize(new_dcc
));
220 if (response_valid
&& has_new_dcc
)
223 return response_valid
;
226 bool MachineDealCode::GetAsCgi(char* cgi
, int cgi_size
) {
227 if (!cgi
|| cgi_size
<= 0) {
228 ASSERT_STRING("MachineDealCode::GetAsCgi: Invalid buffer");
235 base::StringAppendF(&cgi_arg
, "%s=", kDccCgiVariable
);
236 int cgi_arg_length
= cgi_arg
.size();
238 if (cgi_arg_length
>= cgi_size
) {
239 ASSERT_STRING("MachineDealCode::GetAsCgi: Insufficient buffer size");
243 base::strlcpy(cgi
, cgi_arg
.c_str(), cgi_size
);
245 if (!Get(cgi
+ cgi_arg_length
, cgi_size
- cgi_arg_length
)) {
252 bool MachineDealCode::Get(char* dcc
, int dcc_size
) {
257 if (!dcc
|| dcc_size
<= 0) {
258 ASSERT_STRING("MachineDealCode::Get: Invalid buffer");
264 base::win::RegKey
dcc_key(HKEY_LOCAL_MACHINE
,
265 RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
266 KEY_READ
| KEY_WOW64_32KEY
);
267 if (!dcc_key
.Valid())
268 return false; // no DCC key.
270 size_t size
= dcc_size
;
271 if (!RegKeyReadValue(dcc_key
, kDccValueName
, dcc
, &size
)) {
272 ASSERT_STRING("MachineDealCode::Get: Insufficient buffer size");
280 bool MachineDealCode::Clear() {
281 base::win::RegKey
dcc_key(HKEY_LOCAL_MACHINE
,
282 RlzValueStoreRegistry::GetWideLibKeyName().c_str(),
283 KEY_READ
| KEY_WRITE
| KEY_WOW64_32KEY
);
284 if (!dcc_key
.Valid())
285 return false; // no DCC key.
287 dcc_key
.DeleteValue(kDccValueName
);
290 wchar_t dcc
[kMaxDccLength
+ 1];
291 DWORD dcc_size
= arraysize(dcc
);
292 if (dcc_key
.ReadValue(kDccValueName
, dcc
, &dcc_size
, NULL
) == ERROR_SUCCESS
) {
293 ASSERT_STRING("MachineDealCode::Clear: Could not delete the DCC value.");
300 } // namespace rlz_lib