// 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.

#include "chrome/browser/extensions/external_registry_extension_loader_win.h"

#include "base/file_path.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "base/version.h"
#include "base/win/registry.h"
#include "content/browser/browser_thread.h"
#include "chrome/browser/extensions/external_extension_provider_impl.h"

namespace {

// The Registry hive where to look for external extensions.
const HKEY kRegRoot = HKEY_LOCAL_MACHINE;

// The Registry subkey that contains information about external extensions.
const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";

// Registry value of of that key that defines the path to the .crx file.
const wchar_t kRegistryExtensionPath[] = L"path";

// Registry value of that key that defines the current version of the .crx file.
const wchar_t kRegistryExtensionVersion[] = L"version";

}  // namespace

void ExternalRegistryExtensionLoader::StartLoading() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableMethod(
          this,
          &ExternalRegistryExtensionLoader::LoadOnFileThread));
}

void ExternalRegistryExtensionLoader::LoadOnFileThread() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  scoped_ptr<DictionaryValue> prefs(new DictionaryValue);

  base::win::RegistryKeyIterator iterator(
      kRegRoot, ASCIIToWide(kRegistryExtensions).c_str());
  while (iterator.Valid()) {
    base::win::RegKey key;
    std::wstring key_path = ASCIIToWide(kRegistryExtensions);
    key_path.append(L"\\");
    key_path.append(iterator.Name());
    if (key.Open(kRegRoot, key_path.c_str(), KEY_READ)  == ERROR_SUCCESS) {
      std::wstring extension_path_str;
      if (key.ReadValue(kRegistryExtensionPath, &extension_path_str)
          == ERROR_SUCCESS) {
        FilePath extension_path(extension_path_str);
        if (!extension_path.IsAbsolute()) {
          LOG(ERROR) << "Path " << extension_path_str
                     << " needs to be absolute in key "
                     << key_path;
          ++iterator;
          continue;
        }
        std::wstring extension_version;
        if (key.ReadValue(kRegistryExtensionVersion, &extension_version)
            == ERROR_SUCCESS) {
          std::string id = WideToASCII(iterator.Name());
          StringToLowerASCII(&id);

          if (!Extension::IdIsValid(id)) {
            LOG(ERROR) << "Invalid id value " << id
                       << " for key " << key_path << " .";
            ++iterator;
            continue;
          }

          scoped_ptr<Version> version;
          version.reset(Version::GetVersionFromString(
                            WideToASCII(extension_version)));
          if (!version.get()) {
            LOG(ERROR) << "Invalid version value " << extension_version
                       << " for key " << key_path << " .";
            ++iterator;
            continue;
          }

          prefs->SetString(
              id + "." + ExternalExtensionProviderImpl::kExternalVersion,
              WideToASCII(extension_version));
          prefs->SetString(
              id + "." + ExternalExtensionProviderImpl::kExternalCrx,
              extension_path_str);
        } else {
          // TODO(erikkay): find a way to get this into about:extensions
          LOG(ERROR) << "Missing value " << kRegistryExtensionVersion
                     << " for key " << key_path << " .";
        }
      } else {
        // TODO(erikkay): find a way to get this into about:extensions
        LOG(ERROR) << "Missing value " << kRegistryExtensionPath
                   << " for key " << key_path << " .";
      }
    }
    ++iterator;
  }

  prefs_.reset(prefs.release());
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this,
          &ExternalRegistryExtensionLoader::LoadFinished));
}