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