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 #include "chrome/browser/ui/network_profile_bubble.h" 6 7 #include <windows.h> 8 9 #include <wtsapi32.h> 10 // Make sure we link the wtsapi lib file in. 11 #pragma comment(lib, "wtsapi32.lib") 12 13 #include "base/bind.h" 14 #include "base/command_line.h" 15 #include "base/file_util.h" 16 #include "base/files/file_path.h" 17 #include "base/logging.h" 18 #include "base/metrics/histogram.h" 19 #include "base/prefs/pref_service.h" 20 #include "base/time/time.h" 21 #include "chrome/browser/browser_process.h" 22 #include "chrome/browser/profiles/profile.h" 23 #include "chrome/browser/ui/browser_finder.h" 24 #include "chrome/browser/ui/browser_list.h" 25 #include "chrome/browser/ui/browser_list_observer.h" 26 #include "chrome/common/chrome_switches.h" 27 #include "chrome/common/pref_names.h" 28 #include "components/pref_registry/pref_registry_syncable.h" 29 #include "content/public/browser/browser_thread.h" 30 31 namespace { 32 33 // The duration of the silent period before we start nagging the user again. 34 const int kSilenceDurationDays = 100; 35 36 // The number of warnings to be shown on consequtive starts of Chrome before the 37 // silent period starts. 38 const int kMaxWarnings = 2; 39 40 // Implementation of chrome::BrowserListObserver used to wait for a browser 41 // window. 42 class BrowserListObserver : public chrome::BrowserListObserver { 43 private: 44 virtual ~BrowserListObserver(); 45 46 // Overridden from chrome::BrowserListObserver: 47 virtual void OnBrowserAdded(Browser* browser) OVERRIDE; 48 virtual void OnBrowserRemoved(Browser* browser) OVERRIDE; 49 virtual void OnBrowserSetLastActive(Browser* browser) OVERRIDE; 50 }; 51 52 BrowserListObserver::~BrowserListObserver() { 53 } 54 55 void BrowserListObserver::OnBrowserAdded(Browser* browser) { 56 } 57 58 void BrowserListObserver::OnBrowserRemoved(Browser* browser) { 59 } 60 61 void BrowserListObserver::OnBrowserSetLastActive(Browser* browser) { 62 NetworkProfileBubble::ShowNotification(browser); 63 // No need to observe anymore. 64 BrowserList::RemoveObserver(this); 65 delete this; 66 } 67 68 // The name of the UMA histogram collecting our stats. 69 const char kMetricNetworkedProfileCheck[] = "NetworkedProfile.Check"; 70 71 } // namespace 72 73 // static 74 bool NetworkProfileBubble::notification_shown_ = false; 75 76 // static 77 bool NetworkProfileBubble::ShouldCheckNetworkProfile(Profile* profile) { 78 PrefService* prefs = profile->GetPrefs(); 79 if (prefs->GetInteger(prefs::kNetworkProfileWarningsLeft)) 80 return !notification_shown_; 81 int64 last_check = prefs->GetInt64(prefs::kNetworkProfileLastWarningTime); 82 base::TimeDelta time_since_last_check = 83 base::Time::Now() - base::Time::FromTimeT(last_check); 84 if (time_since_last_check.InDays() > kSilenceDurationDays) { 85 prefs->SetInteger(prefs::kNetworkProfileWarningsLeft, kMaxWarnings); 86 return !notification_shown_; 87 } 88 return false; 89 } 90 91 // static 92 void NetworkProfileBubble::CheckNetworkProfile( 93 const base::FilePath& profile_folder) { 94 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); 95 // On Windows notify the users if their profiles are located on a network 96 // share as we don't officially support this setup yet. 97 // However we don't want to bother users on Cytrix setups as those have no 98 // real choice and their admins must be well aware of the risks associated. 99 // Also the command line flag --no-network-profile-warning can stop this 100 // warning from popping up. In this case we can skip the check to make the 101 // start faster. 102 // Collect a lot of stats along the way to see which cases do occur in the 103 // wild often enough. 104 if (CommandLine::ForCurrentProcess()->HasSwitch( 105 switches::kNoNetworkProfileWarning)) { 106 RecordUmaEvent(METRIC_CHECK_SUPPRESSED); 107 return; 108 } 109 110 LPWSTR buffer = NULL; 111 DWORD buffer_length = 0; 112 // Checking for RDP is cheaper than checking for a network drive so do this 113 // one first. 114 if (!::WTSQuerySessionInformation(WTS_CURRENT_SERVER, WTS_CURRENT_SESSION, 115 WTSClientProtocolType, 116 &buffer, &buffer_length)) { 117 RecordUmaEvent(METRIC_CHECK_FAILED); 118 return; 119 } 120 121 unsigned short* type = reinterpret_cast<unsigned short*>(buffer); 122 // We should warn the users if they have their profile on a network share only 123 // if running on a local session. 124 if (*type == WTS_PROTOCOL_TYPE_CONSOLE) { 125 bool profile_on_network = false; 126 if (!profile_folder.empty()) { 127 base::FilePath temp_file; 128 // Try to create some non-empty temp file in the profile dir and use 129 // it to check if there is a reparse-point free path to it. 130 if (base::CreateTemporaryFileInDir(profile_folder, &temp_file) && 131 (base::WriteFile(temp_file, ".", 1) == 1)) { 132 base::FilePath normalized_temp_file; 133 if (!base::NormalizeFilePath(temp_file, &normalized_temp_file)) 134 profile_on_network = true; 135 } else { 136 RecordUmaEvent(METRIC_CHECK_IO_FAILED); 137 } 138 base::DeleteFile(temp_file, false); 139 } 140 if (profile_on_network) { 141 RecordUmaEvent(METRIC_PROFILE_ON_NETWORK); 142 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, 143 base::Bind(&NotifyNetworkProfileDetected)); 144 } else { 145 RecordUmaEvent(METRIC_PROFILE_NOT_ON_NETWORK); 146 } 147 } else { 148 RecordUmaEvent(METRIC_REMOTE_SESSION); 149 } 150 151 ::WTSFreeMemory(buffer); 152 } 153 154 // static 155 void NetworkProfileBubble::SetNotificationShown(bool shown) { 156 notification_shown_ = shown; 157 } 158 159 // static 160 void NetworkProfileBubble::RegisterProfilePrefs( 161 user_prefs::PrefRegistrySyncable* registry) { 162 registry->RegisterIntegerPref( 163 prefs::kNetworkProfileWarningsLeft, 164 kMaxWarnings, 165 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 166 registry->RegisterInt64Pref( 167 prefs::kNetworkProfileLastWarningTime, 168 0, 169 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 170 } 171 172 // static 173 void NetworkProfileBubble::RecordUmaEvent(MetricNetworkedProfileCheck event) { 174 UMA_HISTOGRAM_ENUMERATION(kMetricNetworkedProfileCheck, 175 event, 176 METRIC_NETWORKED_PROFILE_CHECK_SIZE); 177 } 178 179 // static 180 void NetworkProfileBubble::NotifyNetworkProfileDetected() { 181 Browser* browser = chrome::FindLastActiveWithHostDesktopType( 182 chrome::GetActiveDesktop()); 183 184 if (browser) 185 ShowNotification(browser); 186 else 187 BrowserList::AddObserver(new BrowserListObserver()); 188 } 189