Home | History | Annotate | Download | only in ui
      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