summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/network_profile_bubble.cc
blob: a6060e072ffe7c04fa067a7d1b37031b36ee7ff4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// 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());
}