| Index: chrome_browser_proxy_resolver.cc | 
| diff --git a/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..a481f4fbb0d1d9ef68109ab0333320d62d8db126 | 
| --- /dev/null | 
| +++ b/chrome_browser_proxy_resolver.cc | 
| @@ -0,0 +1,268 @@ | 
| +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "update_engine/chrome_browser_proxy_resolver.h" | 
| + | 
| +#include <map> | 
| +#include <string> | 
| + | 
| +#include <base/string_tokenizer.h> | 
| +#include <base/string_util.h> | 
| +#include <dbus/dbus-glib.h> | 
| +#include <dbus/dbus-glib-lowlevel.h> | 
| +#include <google/protobuf/stubs/common.h> | 
| + | 
| +#include "update_engine/dbus_constants.h" | 
| +#include "update_engine/utils.h" | 
| + | 
| +namespace chromeos_update_engine { | 
| + | 
| +using google::protobuf::Closure; | 
| +using google::protobuf::NewCallback; | 
| +using std::deque; | 
| +using std::make_pair; | 
| +using std::multimap; | 
| +using std::pair; | 
| +using std::string; | 
| + | 
| +#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved" | 
| +#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE                 \ | 
| +  "org.chromium.UpdateEngineLibcrosProxyResolvedInterface" | 
| +const char kLibCrosServiceName[] = "org.chromium.LibCrosService"; | 
| +const char kLibCrosServicePath[] = "/org/chromium/LibCrosService"; | 
| +const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface"; | 
| +const char kLibCrosServiceResolveNetworkProxyMethodName[] = | 
| +    "ResolveNetworkProxy"; | 
| +const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME; | 
| +const char kLibCrosProxyResolveSignalInterface[] = | 
| +    LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE; | 
| +const char kLibCrosProxyResolveSignalFilter[] = "type='signal', " | 
| +    "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', " | 
| +    "path='/', " | 
| +    "member='" LIB_CROS_PROXY_RESOLVE_NAME "'"; | 
| +#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE | 
| +#undef LIB_CROS_PROXY_RESOLVE_NAME | 
| + | 
| +namespace { | 
| +const int kTimeout = 5;  // seconds | 
| + | 
| +DBusGProxy* GetProxy(DbusGlibInterface* dbus) { | 
| +  GError* error = NULL; | 
| + | 
| +  DBusGConnection* bus = dbus->BusGet(DBUS_BUS_SYSTEM, &error); | 
| +  if (!bus) { | 
| +    LOG(ERROR) << "Failed to get bus"; | 
| +    return NULL; | 
| +  } | 
| +  DBusGProxy* proxy = dbus->ProxyNewForNameOwner(bus, | 
| +                                                 kLibCrosServiceName, | 
| +                                                 kLibCrosServicePath, | 
| +                                                 kLibCrosServiceInterface, | 
| +                                                 &error); | 
| +  if (!proxy) { | 
| +    LOG(ERROR) << "Error getting dbus proxy for " | 
| +               << kLibCrosServiceName << ": " << utils::GetGErrorMessage(error); | 
| +    return NULL; | 
| +  } | 
| +  return proxy; | 
| +} | 
| + | 
| +}  // namespace {} | 
| + | 
| +ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(DbusGlibInterface* dbus) | 
| +    : dbus_(dbus), proxy_(NULL), timeout_(kTimeout) {} | 
| + | 
| +bool ChromeBrowserProxyResolver::Init() { | 
| +  // Set up signal handler. Code lifted from libcros | 
| +  if (proxy_) { | 
| +    // Already initialized | 
| +    return true; | 
| +  } | 
| +  GError* gerror = NULL; | 
| +  DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror); | 
| +  TEST_AND_RETURN_FALSE(gbus); | 
| +  DBusConnection* connection = dbus_->ConnectionGetConnection(gbus); | 
| +  TEST_AND_RETURN_FALSE(connection); | 
| +  DBusError error; | 
| +  dbus_error_init(&error); | 
| +  dbus_->DbusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter, &error); | 
| +  TEST_AND_RETURN_FALSE(!dbus_error_is_set(&error)); | 
| +  TEST_AND_RETURN_FALSE(dbus_->DbusConnectionAddFilter( | 
| +      connection, | 
| +      &ChromeBrowserProxyResolver::StaticFilterMessage, | 
| +      this, | 
| +      NULL)); | 
| + | 
| +  proxy_ = GetProxy(dbus_); | 
| +  if (!proxy_) { | 
| +    dbus_->DbusConnectionRemoveFilter( | 
| +        connection, | 
| +        &ChromeBrowserProxyResolver::StaticFilterMessage, | 
| +        this); | 
| +  } | 
| +  TEST_AND_RETURN_FALSE(proxy_);  // For the error log | 
| +  return true; | 
| +} | 
| + | 
| +ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() { | 
| +  if (proxy_) { | 
| +    GError* gerror = NULL; | 
| +    DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror); | 
| +    TEST_AND_RETURN(gbus); | 
| +    DBusConnection* connection = dbus_->ConnectionGetConnection(gbus); | 
| +    dbus_->DbusConnectionRemoveFilter( | 
| +        connection, | 
| +        &ChromeBrowserProxyResolver::StaticFilterMessage, | 
| +        this); | 
| +  } | 
| +  // Kill outstanding timers | 
| +  for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e; | 
| +       ++it) { | 
| +    g_source_destroy(it->second); | 
| +    it->second = NULL; | 
| +  } | 
| +} | 
| + | 
| +bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url, | 
| +                                                  ProxiesResolvedFn callback, | 
| +                                                  void* data) { | 
| +  GError* error = NULL; | 
| +  TEST_AND_RETURN_FALSE(proxy_); | 
| +  if (!dbus_->ProxyCall( | 
| +          proxy_, | 
| +          kLibCrosServiceResolveNetworkProxyMethodName, | 
| +          &error, | 
| +          G_TYPE_STRING, url.c_str(), | 
| +          G_TYPE_STRING, kLibCrosProxyResolveSignalInterface, | 
| +          G_TYPE_STRING, kLibCrosProxyResolveName, | 
| +          G_TYPE_INVALID, G_TYPE_INVALID)) { | 
| +    LOG(ERROR) << "dbus_g_proxy_call failed: " | 
| +               << utils::GetGErrorMessage(error); | 
| +    return false; | 
| +  } | 
| +  callbacks_.insert(make_pair(url, make_pair(callback, data))); | 
| +  Closure* closure = NewCallback(this, | 
| +                                 &ChromeBrowserProxyResolver::HandleTimeout, | 
| +                                 url); | 
| +  GSource* timer = g_timeout_source_new_seconds(timeout_); | 
| +  g_source_set_callback(timer, &utils::GlibRunClosure, closure, NULL); | 
| +  g_source_attach(timer, NULL); | 
| +  timers_.insert(make_pair(url, timer)); | 
| +  return true; | 
| +} | 
| + | 
| +DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage( | 
| +    DBusConnection* connection, | 
| +    DBusMessage* message) { | 
| +  // Code lifted from libcros. | 
| +  if (!dbus_->DbusMessageIsSignal(message, | 
| +                                  kLibCrosProxyResolveSignalInterface, | 
| +                                  kLibCrosProxyResolveName)) { | 
| +    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; | 
| +  } | 
| +  // Get args | 
| +  char* source_url = NULL; | 
| +  char* proxy_list = NULL; | 
| +  char* error = NULL; | 
| +  DBusError arg_error; | 
| +  dbus_error_init(&arg_error); | 
| +  if (!dbus_->DbusMessageGetArgs(message, &arg_error, | 
| +                                 DBUS_TYPE_STRING, &source_url, | 
| +                                 DBUS_TYPE_STRING, &proxy_list, | 
| +                                 DBUS_TYPE_STRING, &error, | 
| +                                 DBUS_TYPE_INVALID)) { | 
| +    LOG(ERROR) << "Error reading dbus signal."; | 
| +    return DBUS_HANDLER_RESULT_HANDLED; | 
| +  } | 
| +  if (!source_url || !proxy_list) { | 
| +    LOG(ERROR) << "Error getting url, proxy list from dbus signal."; | 
| +    return DBUS_HANDLER_RESULT_HANDLED; | 
| +  } | 
| +  HandleReply(source_url, proxy_list); | 
| +  return DBUS_HANDLER_RESULT_HANDLED; | 
| +} | 
| + | 
| +bool ChromeBrowserProxyResolver::DeleteUrlState( | 
| +    const string& source_url, | 
| +    bool delete_timer, | 
| +    pair<ProxiesResolvedFn, void*>* callback) { | 
| +  { | 
| +    CallbacksMap::iterator it = callbacks_.lower_bound(source_url); | 
| +    TEST_AND_RETURN_FALSE(it != callbacks_.end()); | 
| +    TEST_AND_RETURN_FALSE(it->first == source_url); | 
| +    if (callback) | 
| +      *callback = it->second; | 
| +    callbacks_.erase(it); | 
| +  } | 
| +  { | 
| +    TimeoutsMap::iterator it = timers_.lower_bound(source_url); | 
| +    TEST_AND_RETURN_FALSE(it != timers_.end()); | 
| +    TEST_AND_RETURN_FALSE(it->first == source_url); | 
| +    if (delete_timer) | 
| +      g_source_destroy(it->second); | 
| +    timers_.erase(it); | 
| +  } | 
| +  return true; | 
| +} | 
| + | 
| +void ChromeBrowserProxyResolver::HandleReply(const string& source_url, | 
| +                                             const string& proxy_list) { | 
| +  pair<ProxiesResolvedFn, void*> callback; | 
| +  TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback)); | 
| +  (*callback.first)(ParseProxyString(proxy_list), callback.second); | 
| +} | 
| + | 
| +void ChromeBrowserProxyResolver::HandleTimeout(string source_url) { | 
| +  LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding."; | 
| +  pair<ProxiesResolvedFn, void*> callback; | 
| +  TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback)); | 
| +  deque<string> proxies; | 
| +  proxies.push_back(kNoProxy); | 
| +  (*callback.first)(proxies, callback.second); | 
| +} | 
| + | 
| +deque<string> ChromeBrowserProxyResolver::ParseProxyString( | 
| +    const string& input) { | 
| +  deque<string> ret; | 
| +  // Some of this code taken from | 
| +  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and | 
| +  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc | 
| +  StringTokenizer entry_tok(input, ";"); | 
| +  while (entry_tok.GetNext()) { | 
| +    string token = entry_tok.token(); | 
| +    TrimWhitespaceASCII(token, TRIM_ALL, &token); | 
| + | 
| +    // Start by finding the first space (if any). | 
| +    std::string::iterator space; | 
| +    for (space = token.begin(); space != token.end(); ++space) { | 
| +      if (IsAsciiWhitespace(*space)) { | 
| +        break; | 
| +      } | 
| +    } | 
| + | 
| +    string scheme = string(token.begin(), space); | 
| +    StringToLowerASCII(&scheme); | 
| +    // Chrome uses "socks" to mean socks4 and "proxy" to mean http. | 
| +    if (scheme == "socks") | 
| +      scheme += "4"; | 
| +    else if (scheme == "proxy") | 
| +      scheme = "http"; | 
| +    else if (scheme != "https" && | 
| +             scheme != "socks4" && | 
| +             scheme != "socks5" && | 
| +             scheme != "direct") | 
| +      continue;  // Invalid proxy scheme | 
| + | 
| +    string host_and_port = string(space, token.end()); | 
| +    TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port); | 
| +    if (scheme != "direct" && host_and_port.empty()) | 
| +      continue;  // Must supply host/port when non-direct proxy used. | 
| +    ret.push_back(scheme + "://" + host_and_port); | 
| +  } | 
| +  if (ret.empty() || *ret.rbegin() != kNoProxy) | 
| +    ret.push_back(kNoProxy); | 
| +  return ret; | 
| +} | 
| + | 
| +}  // namespace chromeos_update_engine | 
|  |