OLD | NEW |
| (Empty) |
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. | |
6 | |
7 #include "rlz/lib/financial_ping.h" | |
8 | |
9 #include "base/basictypes.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "base/string_util.h" | |
12 #include "base/stringprintf.h" | |
13 #include "base/utf_string_conversions.h" | |
14 #include "rlz/lib/assert.h" | |
15 #include "rlz/lib/lib_values.h" | |
16 #include "rlz/lib/machine_id.h" | |
17 #include "rlz/lib/rlz_lib.h" | |
18 #include "rlz/lib/rlz_value_store.h" | |
19 #include "rlz/lib/string_utils.h" | |
20 | |
21 #if !defined(OS_WIN) | |
22 #include "base/time.h" | |
23 #endif | |
24 | |
25 #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) | |
26 | |
27 #include <windows.h> | |
28 #include <wininet.h> | |
29 | |
30 namespace { | |
31 | |
32 class InternetHandle { | |
33 public: | |
34 InternetHandle(HINTERNET handle) { handle_ = handle; } | |
35 ~InternetHandle() { if (handle_) InternetCloseHandle(handle_); } | |
36 operator HINTERNET() const { return handle_; } | |
37 bool operator!() const { return (handle_ == NULL); } | |
38 | |
39 private: | |
40 HINTERNET handle_; | |
41 }; | |
42 | |
43 } // namespace | |
44 | |
45 #else | |
46 | |
47 #include "base/bind.h" | |
48 #include "base/message_loop.h" | |
49 #include "base/time.h" | |
50 #include "googleurl/src/gurl.h" | |
51 #include "net/base/load_flags.h" | |
52 #include "net/url_request/url_fetcher.h" | |
53 #include "net/url_request/url_fetcher_delegate.h" | |
54 #include "net/url_request/url_request_context.h" | |
55 #include "net/url_request/url_request_context_getter.h" | |
56 | |
57 #endif | |
58 | |
59 namespace { | |
60 | |
61 // Returns the time relative to a fixed point in the past in multiples of | |
62 // 100 ns stepts. The point in the past is arbitrary but can't change, as the | |
63 // result of this value is stored on disk. | |
64 int64 GetSystemTimeAsInt64() { | |
65 #if defined(OS_WIN) | |
66 FILETIME now_as_file_time; | |
67 // Relative to Jan 1, 1601 (UTC). | |
68 GetSystemTimeAsFileTime(&now_as_file_time); | |
69 | |
70 LARGE_INTEGER integer; | |
71 integer.HighPart = now_as_file_time.dwHighDateTime; | |
72 integer.LowPart = now_as_file_time.dwLowDateTime; | |
73 return integer.QuadPart; | |
74 #else | |
75 // Seconds since epoch (Jan 1, 1970). | |
76 double now_seconds = base::Time::Now().ToDoubleT(); | |
77 return static_cast<int64>(now_seconds * 1000 * 1000 * 10); | |
78 #endif | |
79 } | |
80 | |
81 } // namespace | |
82 | |
83 | |
84 namespace rlz_lib { | |
85 | |
86 bool FinancialPing::FormRequest(Product product, | |
87 const AccessPoint* access_points, const char* product_signature, | |
88 const char* product_brand, const char* product_id, | |
89 const char* product_lang, bool exclude_machine_id, | |
90 std::string* request) { | |
91 if (!request) { | |
92 ASSERT_STRING("FinancialPing::FormRequest: request is NULL"); | |
93 return false; | |
94 } | |
95 | |
96 request->clear(); | |
97 | |
98 ScopedRlzValueStoreLock lock; | |
99 RlzValueStore* store = lock.GetStore(); | |
100 if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) | |
101 return false; | |
102 | |
103 if (!access_points) { | |
104 ASSERT_STRING("FinancialPing::FormRequest: access_points is NULL"); | |
105 return false; | |
106 } | |
107 | |
108 if (!product_signature) { | |
109 ASSERT_STRING("FinancialPing::FormRequest: product_signature is NULL"); | |
110 return false; | |
111 } | |
112 | |
113 if (!SupplementaryBranding::GetBrand().empty()) { | |
114 if (SupplementaryBranding::GetBrand() != product_brand) { | |
115 ASSERT_STRING("FinancialPing::FormRequest: supplementary branding bad"); | |
116 return false; | |
117 } | |
118 } | |
119 | |
120 base::StringAppendF(request, "%s?", kFinancialPingPath); | |
121 | |
122 // Add the signature, brand, product id and language. | |
123 base::StringAppendF(request, "%s=%s", kProductSignatureCgiVariable, | |
124 product_signature); | |
125 if (product_brand) | |
126 base::StringAppendF(request, "&%s=%s", kProductBrandCgiVariable, | |
127 product_brand); | |
128 | |
129 if (product_id) | |
130 base::StringAppendF(request, "&%s=%s", kProductIdCgiVariable, product_id); | |
131 | |
132 if (product_lang) | |
133 base::StringAppendF(request, "&%s=%s", kProductLanguageCgiVariable, | |
134 product_lang); | |
135 | |
136 // Add the product events. | |
137 char cgi[kMaxCgiLength + 1]; | |
138 cgi[0] = 0; | |
139 bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi)); | |
140 if (has_events) | |
141 base::StringAppendF(request, "&%s", cgi); | |
142 | |
143 // If we don't have any events, we should ping all the AP's on the system | |
144 // that we know about and have a current RLZ value, even if they are not | |
145 // used by this product. | |
146 AccessPoint all_points[LAST_ACCESS_POINT]; | |
147 if (!has_events) { | |
148 char rlz[kMaxRlzLength + 1]; | |
149 int idx = 0; | |
150 for (int ap = NO_ACCESS_POINT + 1; ap < LAST_ACCESS_POINT; ap++) { | |
151 rlz[0] = 0; | |
152 AccessPoint point = static_cast<AccessPoint>(ap); | |
153 if (GetAccessPointRlz(point, rlz, arraysize(rlz)) && | |
154 rlz[0] != '\0') | |
155 all_points[idx++] = point; | |
156 } | |
157 all_points[idx] = NO_ACCESS_POINT; | |
158 } | |
159 | |
160 // Add the RLZ's and the DCC if needed. This is the same as get PingParams. | |
161 // This will also include the RLZ Exchange Protocol CGI Argument. | |
162 cgi[0] = 0; | |
163 if (GetPingParams(product, has_events ? access_points : all_points, | |
164 cgi, arraysize(cgi))) | |
165 base::StringAppendF(request, "&%s", cgi); | |
166 | |
167 if (has_events && !exclude_machine_id) { | |
168 std::string machine_id; | |
169 if (GetMachineId(&machine_id)) { | |
170 base::StringAppendF(request, "&%s=%s", kMachineIdCgiVariable, | |
171 machine_id.c_str()); | |
172 } | |
173 } | |
174 | |
175 return true; | |
176 } | |
177 | |
178 #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET) | |
179 // The URLRequestContextGetter used by FinancialPing::PingServer(). | |
180 net::URLRequestContextGetter* g_context; | |
181 | |
182 bool FinancialPing::SetURLRequestContext( | |
183 net::URLRequestContextGetter* context) { | |
184 ScopedRlzValueStoreLock lock; | |
185 RlzValueStore* store = lock.GetStore(); | |
186 if (!store) | |
187 return false; | |
188 | |
189 g_context = context; | |
190 return true; | |
191 } | |
192 | |
193 namespace { | |
194 | |
195 class FinancialPingUrlFetcherDelegate : public net::URLFetcherDelegate { | |
196 public: | |
197 FinancialPingUrlFetcherDelegate(MessageLoop* loop) : loop_(loop) { } | |
198 virtual void OnURLFetchComplete(const net::URLFetcher* source); | |
199 private: | |
200 MessageLoop* loop_; | |
201 }; | |
202 | |
203 void FinancialPingUrlFetcherDelegate::OnURLFetchComplete( | |
204 const net::URLFetcher* source) { | |
205 loop_->Quit(); | |
206 } | |
207 | |
208 } // namespace | |
209 | |
210 #endif | |
211 | |
212 bool FinancialPing::PingServer(const char* request, std::string* response) { | |
213 if (!response) | |
214 return false; | |
215 | |
216 response->clear(); | |
217 | |
218 #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) | |
219 // Initialize WinInet. | |
220 InternetHandle inet_handle = InternetOpenA(kFinancialPingUserAgent, | |
221 INTERNET_OPEN_TYPE_PRECONFIG, | |
222 NULL, NULL, 0); | |
223 if (!inet_handle) | |
224 return false; | |
225 | |
226 // Open network connection. | |
227 InternetHandle connection_handle = InternetConnectA(inet_handle, | |
228 kFinancialServer, kFinancialPort, "", "", INTERNET_SERVICE_HTTP, | |
229 INTERNET_FLAG_NO_CACHE_WRITE, 0); | |
230 if (!connection_handle) | |
231 return false; | |
232 | |
233 // Prepare the HTTP request. | |
234 InternetHandle http_handle = HttpOpenRequestA(connection_handle, | |
235 "GET", request, NULL, NULL, kFinancialPingResponseObjects, | |
236 INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES, NULL); | |
237 if (!http_handle) | |
238 return false; | |
239 | |
240 // Timeouts are probably: | |
241 // INTERNET_OPTION_SEND_TIMEOUT, INTERNET_OPTION_RECEIVE_TIMEOUT | |
242 | |
243 // Send the HTTP request. Note: Fails if user is working in off-line mode. | |
244 if (!HttpSendRequest(http_handle, NULL, 0, NULL, 0)) | |
245 return false; | |
246 | |
247 // Check the response status. | |
248 DWORD status; | |
249 DWORD status_size = sizeof(status); | |
250 if (!HttpQueryInfo(http_handle, HTTP_QUERY_STATUS_CODE | | |
251 HTTP_QUERY_FLAG_NUMBER, &status, &status_size, NULL) || | |
252 200 != status) | |
253 return false; | |
254 | |
255 // Get the response text. | |
256 scoped_array<char> buffer(new char[kMaxPingResponseLength]); | |
257 if (buffer.get() == NULL) | |
258 return false; | |
259 | |
260 DWORD bytes_read = 0; | |
261 while (InternetReadFile(http_handle, buffer.get(), kMaxPingResponseLength, | |
262 &bytes_read) && bytes_read > 0) { | |
263 response->append(buffer.get(), bytes_read); | |
264 bytes_read = 0; | |
265 }; | |
266 | |
267 return true; | |
268 #else | |
269 // Run a blocking event loop to match the win inet implementation. | |
270 MessageLoop loop; | |
271 FinancialPingUrlFetcherDelegate delegate(&loop); | |
272 | |
273 std::string url = base::StringPrintf("http://%s:%d%s", | |
274 kFinancialServer, kFinancialPort, | |
275 request); | |
276 | |
277 scoped_ptr<net::URLFetcher> fetcher(net::URLFetcher::Create( | |
278 GURL(url), net::URLFetcher::GET, &delegate)); | |
279 | |
280 fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE | | |
281 net::LOAD_DO_NOT_SEND_AUTH_DATA | | |
282 net::LOAD_DO_NOT_PROMPT_FOR_LOGIN | | |
283 net::LOAD_DO_NOT_SEND_COOKIES | | |
284 net::LOAD_DO_NOT_SAVE_COOKIES); | |
285 | |
286 // Ensure rlz_lib::SetURLRequestContext() has been called before sending | |
287 // pings. | |
288 CHECK(g_context); | |
289 fetcher->SetRequestContext(g_context); | |
290 | |
291 const base::TimeDelta kTimeout = base::TimeDelta::FromMinutes(5); | |
292 loop.PostTask( | |
293 FROM_HERE, | |
294 base::Bind(&net::URLFetcher::Start, base::Unretained(fetcher.get()))); | |
295 loop.PostNonNestableDelayedTask( | |
296 FROM_HERE, MessageLoop::QuitClosure(), kTimeout); | |
297 | |
298 loop.Run(); | |
299 | |
300 if (fetcher->GetResponseCode() != 200) | |
301 return false; | |
302 | |
303 return fetcher->GetResponseAsString(response); | |
304 #endif | |
305 } | |
306 | |
307 bool FinancialPing::IsPingTime(Product product, bool no_delay) { | |
308 ScopedRlzValueStoreLock lock; | |
309 RlzValueStore* store = lock.GetStore(); | |
310 if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) | |
311 return false; | |
312 | |
313 int64 last_ping = 0; | |
314 if (!store->ReadPingTime(product, &last_ping)) | |
315 return true; | |
316 | |
317 uint64 now = GetSystemTimeAsInt64(); | |
318 int64 interval = now - last_ping; | |
319 | |
320 // If interval is negative, clock was probably reset. So ping. | |
321 if (interval < 0) | |
322 return true; | |
323 | |
324 // Check if this product has any unreported events. | |
325 char cgi[kMaxCgiLength + 1]; | |
326 cgi[0] = 0; | |
327 bool has_events = GetProductEventsAsCgi(product, cgi, arraysize(cgi)); | |
328 if (no_delay && has_events) | |
329 return true; | |
330 | |
331 return interval >= (has_events ? kEventsPingInterval : kNoEventsPingInterval); | |
332 } | |
333 | |
334 | |
335 bool FinancialPing::UpdateLastPingTime(Product product) { | |
336 ScopedRlzValueStoreLock lock; | |
337 RlzValueStore* store = lock.GetStore(); | |
338 if (!store || !store->HasAccess(RlzValueStore::kWriteAccess)) | |
339 return false; | |
340 | |
341 uint64 now = GetSystemTimeAsInt64(); | |
342 return store->WritePingTime(product, now); | |
343 } | |
344 | |
345 | |
346 bool FinancialPing::ClearLastPingTime(Product product) { | |
347 ScopedRlzValueStoreLock lock; | |
348 RlzValueStore* store = lock.GetStore(); | |
349 if (!store || !store->HasAccess(RlzValueStore::kWriteAccess)) | |
350 return false; | |
351 return store->ClearPingTime(product); | |
352 } | |
353 | |
354 } // namespace | |
OLD | NEW |