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/files/file_path.h" 16#include "base/files/file_util.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 31namespace { 32 33// The duration of the silent period before we start nagging the user again. 34const int kSilenceDurationDays = 100; 35 36// The number of warnings to be shown on consequtive starts of Chrome before the 37// silent period starts. 38const int kMaxWarnings = 2; 39 40// Implementation of chrome::BrowserListObserver used to wait for a browser 41// window. 42class 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 52BrowserListObserver::~BrowserListObserver() { 53} 54 55void BrowserListObserver::OnBrowserAdded(Browser* browser) { 56} 57 58void BrowserListObserver::OnBrowserRemoved(Browser* browser) { 59} 60 61void 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. 69const char kMetricNetworkedProfileCheck[] = "NetworkedProfile.Check"; 70 71} // namespace 72 73// static 74bool NetworkProfileBubble::notification_shown_ = false; 75 76// static 77bool 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 92void 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 155void NetworkProfileBubble::SetNotificationShown(bool shown) { 156 notification_shown_ = shown; 157} 158 159// static 160void 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 173void NetworkProfileBubble::RecordUmaEvent(MetricNetworkedProfileCheck event) { 174 UMA_HISTOGRAM_ENUMERATION(kMetricNetworkedProfileCheck, 175 event, 176 METRIC_NETWORKED_PROFILE_CHECK_SIZE); 177} 178 179// static 180void 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