network_state.h revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#ifndef CHROMEOS_NETWORK_NETWORK_STATE_H_ 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define CHROMEOS_NETWORK_NETWORK_STATE_H_ 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <vector> 1090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chromeos/network/managed_state.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chromeos/network/network_ui_data.h" 14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "components/onc/onc_constants.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "url/gurl.h" 16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)namespace base { 18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)class DictionaryValue; 1990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)class Value; 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace chromeos { 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Simple class to provide network state information about a network service. 25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// This class should always be passed as a const* and should never be held 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// on to. Store network_state->path() (defined in ManagedState) instead and 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// call NetworkStateHandler::GetNetworkState(path) to retrieve the state for 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the network. 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note: NetworkStateHandler will store an entry for each member of 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Manager.ServiceCompleteList. The visible() method indicates whether the 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// network is visible, and the IsInProfile() method indicates whether the 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// network is saved in a profile. 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class CHROMEOS_EXPORT NetworkState : public ManagedState { 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) explicit NetworkState(const std::string& path); 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual ~NetworkState(); 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // ManagedState overrides 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If you change this method, update GetProperties too. 4190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) virtual bool PropertyChanged(const std::string& key, 4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) const base::Value& value) OVERRIDE; 4390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) virtual bool InitialPropertiesReceived( 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::DictionaryValue& properties) OVERRIDE; 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void GetStateProperties( 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::DictionaryValue* dictionary) const OVERRIDE; 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void IPConfigPropertiesChanged(const base::DictionaryValue& properties); 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Returns true, if the network requires a service activation. 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool RequiresActivation() const; 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Accessors 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool visible() const { return visible_; } 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& security() const { return security_; } 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& device_path() const { return device_path_; } 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& guid() const { return guid_; } 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& profile_path() const { return profile_path_; } 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& error() const { return error_; } 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& last_error() const { return last_error_; } 61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) void clear_last_error() { last_error_.clear(); } 62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 63a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) // Returns |connection_state_| if visible, kStateDisconnect otherwise. 64a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) std::string connection_state() const; 65a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 66a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) const NetworkUIData& ui_data() const { return ui_data_; } 67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const base::DictionaryValue& proxy_config() const { return proxy_config_; } 68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // IPConfig Properties. These require an extra call to ShillIPConfigClient, 70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // so cache them to avoid excessively complex client code. 71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const std::string& ip_address() const { return ip_address_; } 72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const std::string& gateway() const { return gateway_; } 73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const std::vector<std::string>& dns_servers() const { return dns_servers_; } 74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const GURL& web_proxy_auto_discovery_url() const { 75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return web_proxy_auto_discovery_url_; 76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Wireless property accessors 79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool connectable() const { return connectable_; } 80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int signal_strength() const { return signal_strength_; } 81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Wifi property accessors 83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const std::string& eap_method() const { return eap_method_; } 84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Cellular property accessors 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& network_technology() const { 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return network_technology_; 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& activation_state() const { return activation_state_; } 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& roaming() const { return roaming_; } 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool activate_over_non_cellular_networks() const { 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return activate_over_non_cellular_networks_; 93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool cellular_out_of_credits() const { return cellular_out_of_credits_; } 95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Whether this network has a CACertNSS nickname set. 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool HasCACertNSS() const { return has_ca_cert_nss_; } 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Returns true if |connection_state_| is a connected/connecting state. 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool IsConnectedState() const; 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool IsConnectingState() const; 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Returns true if this is a network stored in a profile. 104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool IsInProfile() const; 105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Returns true if the network properties are stored in a user profile. 107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool IsPrivate() const; 108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 10990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Returns a comma separated string of name servers. 11090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) std::string GetDnsServersAsString() const; 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Converts the prefix length to a netmask string. 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string GetNetmask() const; 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Returns a specifier for identifying this network in the absence of a GUID. 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This should only be used by NetworkStateHandler for keeping track of 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // GUIDs assigned to unsaved networks. 118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::string GetSpecifier() const; 119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Set the GUID. Called exclusively by NetworkStateHandler. 121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) void SetGuid(const std::string& guid); 122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Helpers (used e.g. when a state or error is cached) 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static bool StateIsConnected(const std::string& connection_state); 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static bool StateIsConnecting(const std::string& connection_state); 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static bool ErrorIsValid(const std::string& error); 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) private: 129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) friend class MobileActivatorTest; 130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) friend class NetworkStateHandler; 131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) friend class NetworkChangeNotifierChromeosUpdateTest; 132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Updates |name_| from WiFi.HexSSID if provided, and validates |name_|. 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Returns true if |name_| changes. 135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool UpdateName(const base::DictionaryValue& properties); 136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) void set_visible(bool visible) { visible_ = visible; } 138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Set to true if the network is a member of Manager.Services. 140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool visible_; 141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Network Service properties. Avoid adding any additional properties here. 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Instead use NetworkConfigurationHandler::GetProperties() to asynchronously 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // request properties from Shill. 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string security_; 146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::string eap_method_; // Needed for WiFi EAP networks 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string device_path_; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string guid_; 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string connection_state_; 150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::string profile_path_; 151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool connectable_; 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Reflects the current Shill Service.Error property. This might get cleared 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // by Shill shortly after a failure. 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string error_; 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Last non empty Service.Error property. Cleared by NetworkConnectionHandler 1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // when a connection attempt is initiated. 1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::string last_error_; 160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // This is convenient to keep cached for now, but shouldn't be necessary; 162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // avoid using it if possible. 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NetworkUIData ui_data_; 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // IPConfig properties. 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Note: These do not correspond to actual Shill.Service properties 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // but are derived from the service's corresponding IPConfig object. 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string ip_address_; 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string gateway_; 170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<std::string> dns_servers_; 171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int prefix_length_; // Used by GetNetmask() 172c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) GURL web_proxy_auto_discovery_url_; 173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 174c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Wireless properties, used for icons and Connect logic. 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int signal_strength_; 176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Cellular properties, used for icons, Connect, and Activation. 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string network_technology_; 179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::string activation_state_; 180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::string roaming_; 181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool activate_over_non_cellular_networks_; 182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool cellular_out_of_credits_; 183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Whether a deprecated CaCertNSS property of this network is set. Required 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // for migration to PEM. 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool has_ca_cert_nss_; 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(pneubeck): Remove this once (Managed)NetworkConfigurationHandler 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // provides proxy configuration. crbug.com/241775 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::DictionaryValue proxy_config_; 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(NetworkState); 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} // namespace chromeos 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif // CHROMEOS_NETWORK_NETWORK_STATE_H_ 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)