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 Financial Server ping.
7 #include "rlz/lib/financial_ping.h"
9 #include "base/atomicops.h"
10 #include "base/basictypes.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "rlz/lib/assert.h"
17 #include "rlz/lib/lib_values.h"
18 #include "rlz/lib/machine_id.h"
19 #include "rlz/lib/rlz_lib.h"
20 #include "rlz/lib/rlz_value_store.h"
21 #include "rlz/lib/string_utils.h"
24 #include "base/time/time.h"
27 #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET)
34 class InternetHandle
{
36 InternetHandle(HINTERNET handle
) { handle_
= handle
; }
37 ~InternetHandle() { if (handle_
) InternetCloseHandle(handle_
); }
38 operator HINTERNET() const { return handle_
; }
39 bool operator!() const { return (handle_
== NULL
); }
49 #include "base/bind.h"
50 #include "base/message_loop/message_loop.h"
51 #include "base/run_loop.h"
52 #include "base/time/time.h"
53 #include "net/base/load_flags.h"
54 #include "net/url_request/url_fetcher.h"
55 #include "net/url_request/url_fetcher_delegate.h"
56 #include "net/url_request/url_request_context.h"
57 #include "net/url_request/url_request_context_getter.h"
64 // Returns the time relative to a fixed point in the past in multiples of
65 // 100 ns stepts. The point in the past is arbitrary but can't change, as the
66 // result of this value is stored on disk.
67 int64
GetSystemTimeAsInt64() {
69 FILETIME now_as_file_time
;
70 // Relative to Jan 1, 1601 (UTC).
71 GetSystemTimeAsFileTime(&now_as_file_time
);
73 LARGE_INTEGER integer
;
74 integer
.HighPart
= now_as_file_time
.dwHighDateTime
;
75 integer
.LowPart
= now_as_file_time
.dwLowDateTime
;
76 return integer
.QuadPart
;
78 // Seconds since epoch (Jan 1, 1970).
79 double now_seconds
= base::Time::Now().ToDoubleT();
80 return static_cast<int64
>(now_seconds
* 1000 * 1000 * 10);
89 using base::subtle::AtomicWord
;
91 bool FinancialPing::FormRequest(Product product
,
92 const AccessPoint
* access_points
, const char* product_signature
,
93 const char* product_brand
, const char* product_id
,
94 const char* product_lang
, bool exclude_machine_id
,
95 std::string
* request
) {
97 ASSERT_STRING("FinancialPing::FormRequest: request is NULL");
103 ScopedRlzValueStoreLock lock
;
104 RlzValueStore
* store
= lock
.GetStore();
105 if (!store
|| !store
->HasAccess(RlzValueStore::kReadAccess
))
108 if (!access_points
) {
109 ASSERT_STRING("FinancialPing::FormRequest: access_points is NULL");
113 if (!product_signature
) {
114 ASSERT_STRING("FinancialPing::FormRequest: product_signature is NULL");
118 if (!SupplementaryBranding::GetBrand().empty()) {
119 if (SupplementaryBranding::GetBrand() != product_brand
) {
120 ASSERT_STRING("FinancialPing::FormRequest: supplementary branding bad");
125 base::StringAppendF(request
, "%s?", kFinancialPingPath
);
127 // Add the signature, brand, product id and language.
128 base::StringAppendF(request
, "%s=%s", kProductSignatureCgiVariable
,
131 base::StringAppendF(request
, "&%s=%s", kProductBrandCgiVariable
,
135 base::StringAppendF(request
, "&%s=%s", kProductIdCgiVariable
, product_id
);
138 base::StringAppendF(request
, "&%s=%s", kProductLanguageCgiVariable
,
141 // Add the product events.
142 char cgi
[kMaxCgiLength
+ 1];
144 bool has_events
= GetProductEventsAsCgi(product
, cgi
, arraysize(cgi
));
146 base::StringAppendF(request
, "&%s", cgi
);
148 // If we don't have any events, we should ping all the AP's on the system
149 // that we know about and have a current RLZ value, even if they are not
150 // used by this product.
151 AccessPoint all_points
[LAST_ACCESS_POINT
];
153 char rlz
[kMaxRlzLength
+ 1];
155 for (int ap
= NO_ACCESS_POINT
+ 1; ap
< LAST_ACCESS_POINT
; ap
++) {
157 AccessPoint point
= static_cast<AccessPoint
>(ap
);
158 if (GetAccessPointRlz(point
, rlz
, arraysize(rlz
)) &&
160 all_points
[idx
++] = point
;
162 all_points
[idx
] = NO_ACCESS_POINT
;
165 // Add the RLZ's and the DCC if needed. This is the same as get PingParams.
166 // This will also include the RLZ Exchange Protocol CGI Argument.
168 if (GetPingParams(product
, has_events
? access_points
: all_points
,
169 cgi
, arraysize(cgi
)))
170 base::StringAppendF(request
, "&%s", cgi
);
172 if (has_events
&& !exclude_machine_id
) {
173 std::string machine_id
;
174 if (GetMachineId(&machine_id
)) {
175 base::StringAppendF(request
, "&%s=%s", kMachineIdCgiVariable
,
183 #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
184 // The pointer to URLRequestContextGetter used by FinancialPing::PingServer().
185 // It is atomic pointer because it can be accessed and modified by multiple
187 AtomicWord g_context
;
189 bool FinancialPing::SetURLRequestContext(
190 net::URLRequestContextGetter
* context
) {
191 base::subtle::NoBarrier_Store(
192 &g_context
, reinterpret_cast<AtomicWord
>(context
));
198 class FinancialPingUrlFetcherDelegate
: public net::URLFetcherDelegate
{
200 FinancialPingUrlFetcherDelegate(const base::Closure
& callback
)
201 : callback_(callback
) {
203 virtual void OnURLFetchComplete(const net::URLFetcher
* source
) OVERRIDE
;
206 base::Closure callback_
;
209 void FinancialPingUrlFetcherDelegate::OnURLFetchComplete(
210 const net::URLFetcher
* source
) {
214 bool send_financial_ping_interrupted_for_test
= false;
218 void ShutdownCheck(base::WeakPtr
<base::RunLoop
> weak
) {
221 if (!base::subtle::NoBarrier_Load(&g_context
)) {
222 send_financial_ping_interrupted_for_test
= true;
223 weak
->QuitClosure().Run();
226 // How frequently the financial ping thread should check
227 // the shutdown condition?
228 const base::TimeDelta kInterval
= base::TimeDelta::FromMilliseconds(500);
229 base::MessageLoop::current()->PostDelayedTask(
231 base::Bind(&ShutdownCheck
, weak
),
236 bool FinancialPing::PingServer(const char* request
, std::string
* response
) {
242 #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET)
243 // Initialize WinInet.
244 InternetHandle inet_handle
= InternetOpenA(kFinancialPingUserAgent
,
245 INTERNET_OPEN_TYPE_PRECONFIG
,
250 // Open network connection.
251 InternetHandle connection_handle
= InternetConnectA(inet_handle
,
252 kFinancialServer
, kFinancialPort
, "", "", INTERNET_SERVICE_HTTP
,
253 INTERNET_FLAG_NO_CACHE_WRITE
, 0);
254 if (!connection_handle
)
257 // Prepare the HTTP request.
258 InternetHandle http_handle
= HttpOpenRequestA(connection_handle
,
259 "GET", request
, NULL
, NULL
, kFinancialPingResponseObjects
,
260 INTERNET_FLAG_NO_CACHE_WRITE
| INTERNET_FLAG_NO_COOKIES
, NULL
);
264 // Timeouts are probably:
265 // INTERNET_OPTION_SEND_TIMEOUT, INTERNET_OPTION_RECEIVE_TIMEOUT
267 // Send the HTTP request. Note: Fails if user is working in off-line mode.
268 if (!HttpSendRequest(http_handle
, NULL
, 0, NULL
, 0))
271 // Check the response status.
273 DWORD status_size
= sizeof(status
);
274 if (!HttpQueryInfo(http_handle
, HTTP_QUERY_STATUS_CODE
|
275 HTTP_QUERY_FLAG_NUMBER
, &status
, &status_size
, NULL
) ||
279 // Get the response text.
280 scoped_ptr
<char[]> buffer(new char[kMaxPingResponseLength
]);
281 if (buffer
.get() == NULL
)
284 DWORD bytes_read
= 0;
285 while (InternetReadFile(http_handle
, buffer
.get(), kMaxPingResponseLength
,
286 &bytes_read
) && bytes_read
> 0) {
287 response
->append(buffer
.get(), bytes_read
);
293 // Copy the pointer to stack because g_context may be set to NULL
294 // in different thread. The instance is guaranteed to exist while
295 // the method is running.
296 net::URLRequestContextGetter
* context
=
297 reinterpret_cast<net::URLRequestContextGetter
*>(
298 base::subtle::NoBarrier_Load(&g_context
));
300 // Browser shutdown will cause the context to be reset to NULL.
304 // Run a blocking event loop to match the win inet implementation.
305 scoped_ptr
<base::MessageLoop
> message_loop
;
306 // Ensure that we have a MessageLoop.
307 if (!base::MessageLoop::current())
308 message_loop
.reset(new base::MessageLoop
);
310 FinancialPingUrlFetcherDelegate
delegate(loop
.QuitClosure());
312 std::string url
= base::StringPrintf("http://%s:%d%s",
313 kFinancialServer
, kFinancialPort
,
316 scoped_ptr
<net::URLFetcher
> fetcher(net::URLFetcher::Create(
317 GURL(url
), net::URLFetcher::GET
, &delegate
));
319 fetcher
->SetLoadFlags(net::LOAD_DISABLE_CACHE
|
320 net::LOAD_DO_NOT_SEND_AUTH_DATA
|
321 net::LOAD_DO_NOT_PROMPT_FOR_LOGIN
|
322 net::LOAD_DO_NOT_SEND_COOKIES
|
323 net::LOAD_DO_NOT_SAVE_COOKIES
);
325 // Ensure rlz_lib::SetURLRequestContext() has been called before sending
327 fetcher
->SetRequestContext(context
);
329 base::WeakPtrFactory
<base::RunLoop
> weak(&loop
);
331 const base::TimeDelta kTimeout
= base::TimeDelta::FromMinutes(5);
332 base::MessageLoop::ScopedNestableTaskAllower
allow_nested(
333 base::MessageLoop::current());
334 base::MessageLoop::current()->PostTask(
336 base::Bind(&ShutdownCheck
, weak
.GetWeakPtr()));
337 base::MessageLoop::current()->PostTask(
339 base::Bind(&net::URLFetcher::Start
, base::Unretained(fetcher
.get())));
340 base::MessageLoop::current()->PostDelayedTask(
341 FROM_HERE
, loop
.QuitClosure(), kTimeout
);
345 if (fetcher
->GetResponseCode() != 200)
348 return fetcher
->GetResponseAsString(response
);
352 bool FinancialPing::IsPingTime(Product product
, bool no_delay
) {
353 ScopedRlzValueStoreLock lock
;
354 RlzValueStore
* store
= lock
.GetStore();
355 if (!store
|| !store
->HasAccess(RlzValueStore::kReadAccess
))
359 if (!store
->ReadPingTime(product
, &last_ping
))
362 uint64 now
= GetSystemTimeAsInt64();
363 int64 interval
= now
- last_ping
;
365 // If interval is negative, clock was probably reset. So ping.
369 // Check if this product has any unreported events.
370 char cgi
[kMaxCgiLength
+ 1];
372 bool has_events
= GetProductEventsAsCgi(product
, cgi
, arraysize(cgi
));
373 if (no_delay
&& has_events
)
376 return interval
>= (has_events
? kEventsPingInterval
: kNoEventsPingInterval
);
380 bool FinancialPing::UpdateLastPingTime(Product product
) {
381 ScopedRlzValueStoreLock lock
;
382 RlzValueStore
* store
= lock
.GetStore();
383 if (!store
|| !store
->HasAccess(RlzValueStore::kWriteAccess
))
386 uint64 now
= GetSystemTimeAsInt64();
387 return store
->WritePingTime(product
, now
);
391 bool FinancialPing::ClearLastPingTime(Product product
) {
392 ScopedRlzValueStoreLock lock
;
393 RlzValueStore
* store
= lock
.GetStore();
394 if (!store
|| !store
->HasAccess(RlzValueStore::kWriteAccess
))
396 return store
->ClearPingTime(product
);
399 #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
402 void ResetSendFinancialPingInterrupted() {
403 send_financial_ping_interrupted_for_test
= false;
406 bool WasSendFinancialPingInterrupted() {
407 return send_financial_ping_interrupted_for_test
;
413 } // namespace rlz_lib