// Copyright (c) 2010 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.

#ifndef CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_
#define CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_
#pragma once

#include <utility>
#include <vector>

#include "base/ref_counted.h"
#include "base/singleton.h"
#include "base/string16.h"
#include "base/timer.h"
#include "chrome/browser/browser_thread.h"
#include "googleurl/src/gurl.h"

class EnumerateModulesModel;
class FilePath;
class ListValue;

// A helper class that implements the enumerate module functionality on the File
// thread.
class ModuleEnumerator : public base::RefCountedThreadSafe<ModuleEnumerator> {
 public:
  // What type of module we are dealing with. Loaded modules are modules we
  // detect as loaded in the process at the time of scanning. The others are
  // modules of interest and may or may not be loaded in the process at the
  // time of scan.
  enum ModuleType {
    LOADED_MODULE,
    WINSOCK_MODULE_REGISTRATION,
  };

  // The blacklist status of the module. Suspected Bad modules have been
  // partially matched (ie. name matches and location, but not description)
  // whereas Confirmed Bad modules have been identified further (ie.
  // AuthentiCode signer matches).
  enum ModuleStatus {
    // This is returned by the matching function when comparing against the
    // blacklist and the module does not match the current entry in the
    // blacklist.
    NOT_MATCHED,
    // The module is not on the blacklist. Assume it is good.
    GOOD,
    // Module is a suspected bad module.
    SUSPECTED_BAD,
    // Module is a bad bad dog.
    CONFIRMED_BAD,
  };

  // A bitmask with the possible resolutions for bad modules.
  enum RecommendedAction {
    NONE          = 0,
    INVESTIGATING = 1 << 0,
    UNINSTALL     = 1 << 1,
    DISABLE       = 1 << 2,
    UPDATE        = 1 << 3,
    SEE_LINK      = 1 << 4,
  };

  // The structure we populate when enumerating modules.
  struct Module {
    // The type of module found
    ModuleType type;
    // The module status (benign/bad/etc).
    ModuleStatus status;
    // The module path, not including filename.
    string16 location;
    // The name of the module (filename).
    string16 name;
    // The name of the product the module belongs to.
    string16 product_name;
    // The module file description.
    string16 description;
    // The module version.
    string16 version;
    // The signer of the digital certificate for the module.
    string16 digital_signer;
    // The help tips bitmask.
    RecommendedAction recommended_action;
    // Whether this module has been normalized (necessary before checking it
    // against blacklist).
    bool normalized;
  };

  // A vector typedef of all modules enumerated.
  typedef std::vector<Module> ModulesVector;

  // A structure we populate with the blacklist entries.
  struct BlacklistEntry {
    const char* filename;
    const char* location;
    const char* desc_or_signer;
    const char* version_from;
    const char* version_to;
    RecommendedAction help_tip;
  };

  // A static function that normalizes the module information in the |module|
  // struct. Module information needs to be normalized before comparing against
  // the blacklist. This is because the same module can be described in many
  // different ways, ie. file paths can be presented in long/short name form,
  // and are not case sensitive on Windows. Also, the version string returned
  // can include appended text, which we don't want to use during comparison
  // against the blacklist.
  static void NormalizeModule(Module* module);

  // A static function that checks whether |module| has been |blacklisted|.
  static ModuleStatus Match(const Module& module,
                            const BlacklistEntry& blacklisted);

  explicit ModuleEnumerator(EnumerateModulesModel* observer);
  virtual ~ModuleEnumerator();

  // Start scanning the loaded module list (if a scan is not already in
  // progress). This function does not block while reading the module list, but
  // will notify when done through the MODULE_LIST_ENUMERATED notification.
  // The process will also send MODULE_INCOMPATIBILITY_DETECTED if an
  // incompatible module was detected.
  void ScanNow(ModulesVector* list);

 private:
  // The (currently) hard coded blacklist of known bad modules.
  static const BlacklistEntry kModuleBlacklist[];

  // This function does the actual file scanning work on the FILE thread. It
  // enumerates all loaded modules in the process and other modules of
  // interest, such as the registered Winsock LSP modules and stores them in
  // |enumerated_modules_|. It then normalizes the module info and matches
  // them against a blacklist of known bad modules. Finally, it calls
  // ReportBack to let the observer know we are done.
  void ScanOnFileThread();

  // Builds up a vector of path values mapping to environment variable,
  // with pairs like [c:\windows\, %systemroot%]. This is later used to
  // collapse paths like c:\windows\system32 into %systemroot%\system32, which
  // we can use for comparison against our blacklist (which uses only env vars).
  // NOTE: The vector will not contain an exhaustive list of environment
  // variables, only the ones currently found on the blacklist or ones that are
  // likely to appear there.
  void PreparePathMappings();

  // For a given |module|, collapse the path from c:\windows to %systemroot%,
  // based on the |path_mapping_| vector.
  void CollapsePath(Module* module);

  // Takes each module in the |enumerated_modules_| vector and matches it
  // against a fixed blacklist of bad and suspected bad modules.
  void MatchAgainstBlacklist();

  // This function executes on the UI thread when the scanning and matching
  // process is done. It notifies the observer.
  void ReportBack();

  // Given a filename, returns the Subject (who signed it) retrieved from
  // the digital signature (Authenticode).
  string16 GetSubjectNameFromDigitalSignature(const FilePath& filename);

  // The typedef for the vector that maps a regular file path to %env_var%.
  typedef std::vector< std::pair<string16, string16> > PathMapping;

  // The vector of paths to %env_var%, used to account for differences in
  // where people keep there files, c:\windows vs. d:\windows, etc.
  PathMapping path_mapping_;

  // The vector containing all the enumerated modules (loaded and modules of
  // interest).
  ModulesVector* enumerated_modules_;

  // The observer, who needs to be notified when we are done.
  EnumerateModulesModel* observer_;

  // The thread that we need to call back on to report that we are done.
  BrowserThread::ID callback_thread_id_;

  DISALLOW_COPY_AND_ASSIGN(ModuleEnumerator);
};

// This is a singleton class that enumerates all modules loaded into Chrome,
// both currently loaded modules (called DLLs on Windows) and modules 'of
// interest', such as WinSock LSP modules. This class also marks each module
// as benign or suspected bad or outright bad, using a supplied blacklist that
// is currently hard-coded.
//
// To use this class, grab the singleton pointer and call ScanNow().
// Then wait to get notified through MODULE_LIST_ENUMERATED when the list is
// ready.
//
// This class can be used on the UI thread as it asynchronously offloads the
// file work over to the FILE thread and reports back to the caller with a
// notification.
class EnumerateModulesModel {
 public:
  static EnumerateModulesModel* GetSingleton() {
    return Singleton<EnumerateModulesModel>::get();
  }

  // Returns the number of suspected bad modules found in the last scan.
  // Returns 0 if no scan has taken place yet.
  int suspected_bad_modules_detected() {
    return suspected_bad_modules_detected_;
  }

  // Returns the number of confirmed bad modules found in the last scan.
  // Returns 0 if no scan has taken place yet.
  int confirmed_bad_modules_detected() {
    return confirmed_bad_modules_detected_;
  }

  // Asynchronously start the scan for the loaded module list.
  // When the list is ready.
  void ScanNow();

  // Gets the whole module list as a ListValue.
  ListValue* GetModuleList();

 private:
  friend struct DefaultSingletonTraits<EnumerateModulesModel>;
  friend class ModuleEnumerator;

  EnumerateModulesModel();
  virtual ~EnumerateModulesModel();

  // Called on the UI thread when the helper class is done scanning.
  void DoneScanning();

  // Constructs a Help Center article URL for help with a particular module.
  // The module must have the SEE_LINK attribute for |recommended_action| set,
  // otherwise this returns a blank string.
  GURL ConstructHelpCenterUrl(const ModuleEnumerator::Module& module);

  // The vector containing all the modules enumerated. Will be normalized and
  // any bad modules will be marked.
  ModuleEnumerator::ModulesVector enumerated_modules_;

  // The object responsible for enumerating the modules on the File thread.
  scoped_refptr<ModuleEnumerator> module_enumerator_;

  // When this singleton object is constructed we go and fire off this timer to
  // start scanning for modules after a certain amount of time has passed.
  base::OneShotTimer<EnumerateModulesModel> check_modules_timer_;

  // True if we are currently scanning for modules.
  bool scanning_;

  // The number of confirmed bad modules (not including suspected bad ones)
  // found during last scan.
  int confirmed_bad_modules_detected_;

  // The number of suspected bad modules (not including confirmed bad ones)
  // found during last scan.
  int suspected_bad_modules_detected_;

  DISALLOW_COPY_AND_ASSIGN(EnumerateModulesModel);
};

#endif  // CHROME_BROWSER_ENUMERATE_MODULES_MODEL_WIN_H_