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