Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / rlz / lib / financial_ping.cc
blobb58ce96f962cf107c31493f57b60073a07431b3a
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.
4 //
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"
23 #if !defined(OS_WIN)
24 #include "base/time/time.h"
25 #endif
27 #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET)
29 #include <windows.h>
30 #include <wininet.h>
32 namespace {
34 class InternetHandle {
35 public:
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); }
41 private:
42 HINTERNET handle_;
45 } // namespace
47 #else
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"
58 #include "url/gurl.h"
60 #endif
62 namespace {
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() {
68 #if defined(OS_WIN)
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;
77 #else
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);
81 #endif
84 } // namespace
87 namespace rlz_lib {
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) {
96 if (!request) {
97 ASSERT_STRING("FinancialPing::FormRequest: request is NULL");
98 return false;
101 request->clear();
103 ScopedRlzValueStoreLock lock;
104 RlzValueStore* store = lock.GetStore();
105 if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
106 return false;
108 if (!access_points) {
109 ASSERT_STRING("FinancialPing::FormRequest: access_points is NULL");
110 return false;
113 if (!product_signature) {
114 ASSERT_STRING("FinancialPing::FormRequest: product_signature is NULL");
115 return false;
118 if (!SupplementaryBranding::GetBrand().empty()) {
119 if (SupplementaryBranding::GetBrand() != product_brand) {
120 ASSERT_STRING("FinancialPing::FormRequest: supplementary branding bad");
121 return false;
125 base::StringAppendF(request, "%s?", kFinancialPingPath);
127 // Add the signature, brand, product id and language.
128 base::StringAppendF(request, "%s=%s", kProductSignatureCgiVariable,
129 product_signature);
130 if (product_brand)
131 base::StringAppendF(request, "&%s=%s", kProductBrandCgiVariable,
132 product_brand);
134 if (product_id)
135 base::StringAppendF(request, "&%s=%s", kProductIdCgiVariable, product_id);
137 if (product_lang)
138 base::StringAppendF(request, "&%s=%s", kProductLanguageCgiVariable,
139 product_lang);
141 // Add the product events.
142 char cgi[kMaxCgiLength + 1];
143 cgi[0] = 0;
144 bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi));
145 if (has_events)
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];
152 if (!has_events) {
153 char rlz[kMaxRlzLength + 1];
154 int idx = 0;
155 for (int ap = NO_ACCESS_POINT + 1; ap < LAST_ACCESS_POINT; ap++) {
156 rlz[0] = 0;
157 AccessPoint point = static_cast<AccessPoint>(ap);
158 if (GetAccessPointRlz(point, rlz, arraysize(rlz)) &&
159 rlz[0] != '\0')
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.
167 cgi[0] = 0;
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,
176 machine_id.c_str());
180 return true;
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
186 // threads.
187 AtomicWord g_context;
189 bool FinancialPing::SetURLRequestContext(
190 net::URLRequestContextGetter* context) {
191 base::subtle::Release_Store(
192 &g_context, reinterpret_cast<AtomicWord>(context));
193 return true;
196 namespace {
198 class FinancialPingUrlFetcherDelegate : public net::URLFetcherDelegate {
199 public:
200 FinancialPingUrlFetcherDelegate(const base::Closure& callback)
201 : callback_(callback) {
203 void OnURLFetchComplete(const net::URLFetcher* source) override;
205 private:
206 base::Closure callback_;
209 void FinancialPingUrlFetcherDelegate::OnURLFetchComplete(
210 const net::URLFetcher* source) {
211 callback_.Run();
214 bool send_financial_ping_interrupted_for_test = false;
216 } // namespace
218 void ShutdownCheck(base::WeakPtr<base::RunLoop> weak) {
219 if (!weak.get())
220 return;
221 if (!base::subtle::Acquire_Load(&g_context)) {
222 send_financial_ping_interrupted_for_test = true;
223 weak->QuitClosure().Run();
224 return;
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(
230 FROM_HERE,
231 base::Bind(&ShutdownCheck, weak),
232 kInterval);
234 #endif
236 bool FinancialPing::PingServer(const char* request, std::string* response) {
237 if (!response)
238 return false;
240 response->clear();
242 #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET)
243 // Initialize WinInet.
244 InternetHandle inet_handle = InternetOpenA(kFinancialPingUserAgent,
245 INTERNET_OPEN_TYPE_PRECONFIG,
246 NULL, NULL, 0);
247 if (!inet_handle)
248 return false;
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)
255 return false;
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);
261 if (!http_handle)
262 return false;
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))
269 return false;
271 // Check the response status.
272 DWORD 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) ||
276 200 != status)
277 return false;
279 // Get the response text.
280 scoped_ptr<char[]> buffer(new char[kMaxPingResponseLength]);
281 if (buffer.get() == NULL)
282 return false;
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);
288 bytes_read = 0;
291 return true;
292 #else
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::Acquire_Load(&g_context));
300 // Browser shutdown will cause the context to be reset to NULL.
301 if (!context)
302 return false;
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);
309 base::RunLoop loop;
310 FinancialPingUrlFetcherDelegate delegate(loop.QuitClosure());
312 std::string url = base::StringPrintf("http://%s:%d%s",
313 kFinancialServer, kFinancialPort,
314 request);
316 scoped_ptr<net::URLFetcher> fetcher =
317 net::URLFetcher::Create(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_SEND_COOKIES |
322 net::LOAD_DO_NOT_SAVE_COOKIES);
324 // Ensure rlz_lib::SetURLRequestContext() has been called before sending
325 // pings.
326 fetcher->SetRequestContext(context);
328 base::WeakPtrFactory<base::RunLoop> weak(&loop);
330 const base::TimeDelta kTimeout = base::TimeDelta::FromMinutes(5);
331 base::MessageLoop::ScopedNestableTaskAllower allow_nested(
332 base::MessageLoop::current());
333 base::MessageLoop::current()->PostTask(
334 FROM_HERE,
335 base::Bind(&ShutdownCheck, weak.GetWeakPtr()));
336 base::MessageLoop::current()->PostTask(
337 FROM_HERE,
338 base::Bind(&net::URLFetcher::Start, base::Unretained(fetcher.get())));
339 base::MessageLoop::current()->PostDelayedTask(
340 FROM_HERE, loop.QuitClosure(), kTimeout);
342 loop.Run();
344 if (fetcher->GetResponseCode() != 200)
345 return false;
347 return fetcher->GetResponseAsString(response);
348 #endif
351 bool FinancialPing::IsPingTime(Product product, bool no_delay) {
352 ScopedRlzValueStoreLock lock;
353 RlzValueStore* store = lock.GetStore();
354 if (!store || !store->HasAccess(RlzValueStore::kReadAccess))
355 return false;
357 int64 last_ping = 0;
358 if (!store->ReadPingTime(product, &last_ping))
359 return true;
361 uint64 now = GetSystemTimeAsInt64();
362 int64 interval = now - last_ping;
364 // If interval is negative, clock was probably reset. So ping.
365 if (interval < 0)
366 return true;
368 // Check if this product has any unreported events.
369 char cgi[kMaxCgiLength + 1];
370 cgi[0] = 0;
371 bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi));
372 if (no_delay && has_events)
373 return true;
375 return interval >= (has_events ? kEventsPingInterval : kNoEventsPingInterval);
379 bool FinancialPing::UpdateLastPingTime(Product product) {
380 ScopedRlzValueStoreLock lock;
381 RlzValueStore* store = lock.GetStore();
382 if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
383 return false;
385 uint64 now = GetSystemTimeAsInt64();
386 return store->WritePingTime(product, now);
390 bool FinancialPing::ClearLastPingTime(Product product) {
391 ScopedRlzValueStoreLock lock;
392 RlzValueStore* store = lock.GetStore();
393 if (!store || !store->HasAccess(RlzValueStore::kWriteAccess))
394 return false;
395 return store->ClearPingTime(product);
398 #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
399 namespace test {
401 void ResetSendFinancialPingInterrupted() {
402 send_financial_ping_interrupted_for_test = false;
405 bool WasSendFinancialPingInterrupted() {
406 return send_financial_ping_interrupted_for_test;
409 } // namespace test
410 #endif
412 } // namespace rlz_lib