blob: a6060e072ffe7c04fa067a7d1b37031b36ee7ff4 [file] [log] [blame]
// Copyright (c) 2012 The Chromium 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 "chrome/browser/ui/network_profile_bubble.h"
#include <windows.h>
#include <wtsapi32.h>
// Make sure we link the wtsapi lib file in.
#pragma comment(lib, "wtsapi32.lib")
#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "content/public/browser/browser_thread.h"
namespace {
// The duration of the silent period before we start nagging the user again.
const int kSilenceDurationDays = 100;
// The number of warnings to be shown on consequtive starts of Chrome before the
// silent period starts.
const int kMaxWarnings = 2;
// Implementation of chrome::BrowserListObserver used to wait for a browser
// window.
class BrowserListObserver : public chrome::BrowserListObserver {
private:
virtual ~BrowserListObserver();
// Overridden from chrome::BrowserListObserver:
virtual void OnBrowserAdded(Browser* browser) OVERRIDE;
virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;
virtual void OnBrowserSetLastActive(Browser* browser) OVERRIDE;
};
BrowserListObserver::~BrowserListObserver() {
}
void BrowserListObserver::OnBrowserAdded(Browser* browser) {
}
void BrowserListObserver::OnBrowserRemoved(Browser* browser) {
}
void BrowserListObserver::OnBrowserSetLastActive(Browser* browser) {
NetworkProfileBubble::ShowNotification(browser);
// No need to observe anymore.
BrowserList::RemoveObserver(this);
delete this;
}
// The name of the UMA histogram collecting our stats.
const char kMetricNetworkedProfileCheck[] = "NetworkedProfile.Check";
} // namespace
// static
bool NetworkProfileBubble::notification_shown_ = false;
// static
bool NetworkProfileBubble::ShouldCheckNetworkProfile(Profile* profile) {
PrefService* prefs = profile->GetPrefs();
if (prefs->GetInteger(prefs::kNetworkProfileWarningsLeft))
return !notification_shown_;
int64 last_check = prefs->GetInt64(prefs::kNetworkProfileLastWarningTime);
base::TimeDelta time_since_last_check =
base::Time::Now() - base::Time::FromTimeT(last_check);
if (time_since_last_check.InDays() > kSilenceDurationDays) {
prefs->SetInteger(prefs::kNetworkProfileWarningsLeft, kMaxWarnings);
return !notification_shown_;
}
return false;
}
// static
void NetworkProfileBubble::CheckNetworkProfile(
const base::FilePath& profile_folder) {
DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
// On Windows notify the users if their profiles are located on a network
// share as we don't officially support this setup yet.
// However we don't want to bother users on Cytrix setups as those have no
// real choice and their admins must be well aware of the risks associated.
// Also the command line flag --no-network-profile-warning can stop this
// warning from popping up. In this case we can skip the check to make the
// start faster.
// Collect a lot of stats along the way to see which cases do occur in the
// wild often enough.
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kNoNetworkProfileWarning)) {
RecordUmaEvent(METRIC_CHECK_SUPPRESSED);
return;
}
LPWSTR buffer = NULL;
DWORD buffer_length = 0;
// Checking for RDP is cheaper than checking for a network drive so do this
// one first.
if (!::WTSQuerySessionInformation(WTS_CURRENT_SERVER, WTS_CURRENT_SESSION,
WTSClientProtocolType,
&buffer, &buffer_length)) {
RecordUmaEvent(METRIC_CHECK_FAILED);
return;
}
unsigned short* type = reinterpret_cast<unsigned short*>(buffer);
// We should warn the users if they have their profile on a network share only
// if running on a local session.
if (*type == WTS_PROTOCOL_TYPE_CONSOLE) {
bool profile_on_network = false;
if (!profile_folder.empty()) {
base::FilePath temp_file;
// Try to create some non-empty temp file in the profile dir and use
// it to check if there is a reparse-point free path to it.
if (base::CreateTemporaryFileInDir(profile_folder, &temp_file) &&
(base::WriteFile(temp_file, ".", 1) == 1)) {
base::FilePath normalized_temp_file;
if (!base::NormalizeFilePath(temp_file, &normalized_temp_file))
profile_on_network = true;
} else {
RecordUmaEvent(METRIC_CHECK_IO_FAILED);
}
base::DeleteFile(temp_file, false);
}
if (profile_on_network) {
RecordUmaEvent(METRIC_PROFILE_ON_NETWORK);
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::Bind(&NotifyNetworkProfileDetected));
} else {
RecordUmaEvent(METRIC_PROFILE_NOT_ON_NETWORK);
}
} else {
RecordUmaEvent(METRIC_REMOTE_SESSION);
}
::WTSFreeMemory(buffer);
}
// static
void NetworkProfileBubble::SetNotificationShown(bool shown) {
notification_shown_ = shown;
}
// static
void NetworkProfileBubble::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterIntegerPref(
prefs::kNetworkProfileWarningsLeft,
kMaxWarnings,
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
registry->RegisterInt64Pref(
prefs::kNetworkProfileLastWarningTime,
0,
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
}
// static
void NetworkProfileBubble::RecordUmaEvent(MetricNetworkedProfileCheck event) {
UMA_HISTOGRAM_ENUMERATION(kMetricNetworkedProfileCheck,
event,
METRIC_NETWORKED_PROFILE_CHECK_SIZE);
}
// static
void NetworkProfileBubble::NotifyNetworkProfileDetected() {
Browser* browser = chrome::FindLastActiveWithHostDesktopType(
chrome::GetActiveDesktop());
if (browser)
ShowNotification(browser);
else
BrowserList::AddObserver(new BrowserListObserver());
}