// 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/extensions/api/messaging/native_process_launcher.h" #include "base/basictypes.h" #include "base/bind.h" #include "base/callback.h" #include "base/command_line.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/strings/string_split.h" #include "base/threading/sequenced_worker_pool.h" #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "content/public/browser/browser_thread.h" #include "googleurl/src/gurl.h" namespace extensions { namespace { const char kNativeHostsDirectoryName[] = "native_hosts"; base::FilePath GetHostManifestPathFromCommandLine( const std::string& native_host_name) { const std::string& value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kNativeMessagingHosts); if (value.empty()) return base::FilePath(); std::vector<std::string> hosts; base::SplitString(value, ',', &hosts); for (size_t i = 0; i < hosts.size(); ++i) { std::vector<std::string> key_and_value; base::SplitString(hosts[i], '=', &key_and_value); if (key_and_value.size() != 2) continue; if (key_and_value[0] == native_host_name) return base::FilePath::FromUTF8Unsafe(key_and_value[1]); } return base::FilePath(); } // Default implementation on NativeProcessLauncher interface. class NativeProcessLauncherImpl : public NativeProcessLauncher { public: NativeProcessLauncherImpl(); virtual ~NativeProcessLauncherImpl(); virtual void Launch(const GURL& origin, const std::string& native_host_name, LaunchedCallback callback) const OVERRIDE; private: class Core : public base::RefCountedThreadSafe<Core> { public: Core(); void Launch(const GURL& origin, const std::string& native_host_name, LaunchedCallback callback); void Detach(); private: friend class base::RefCountedThreadSafe<Core>; virtual ~Core(); void DoLaunchOnThreadPool(const GURL& origin, const std::string& native_host_name, LaunchedCallback callback); void CallCallbackOnIOThread(LaunchedCallback callback, LaunchResult result, base::PlatformFile read_file, base::PlatformFile write_file); bool detached_; DISALLOW_COPY_AND_ASSIGN(Core); }; scoped_refptr<Core> core_; DISALLOW_COPY_AND_ASSIGN(NativeProcessLauncherImpl); }; NativeProcessLauncherImpl::Core::Core() : detached_(false) { } NativeProcessLauncherImpl::Core::~Core() { DCHECK(detached_); } void NativeProcessLauncherImpl::Core::Detach() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); detached_ = true; } void NativeProcessLauncherImpl::Core::Launch( const GURL& origin, const std::string& native_host_name, LaunchedCallback callback) { content::BrowserThread::PostBlockingPoolTask( FROM_HERE, base::Bind(&Core::DoLaunchOnThreadPool, this, origin, native_host_name, callback)); } void NativeProcessLauncherImpl::Core::DoLaunchOnThreadPool( const GURL& origin, const std::string& native_host_name, LaunchedCallback callback) { DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); if (!NativeMessagingHostManifest::IsValidName(native_host_name)) { content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this, callback, RESULT_INVALID_NAME, base::kInvalidPlatformFileValue, base::kInvalidPlatformFileValue)); return; } std::string error_message; scoped_ptr<NativeMessagingHostManifest> manifest; // First check if the manifest location is specified in the command line. base::FilePath path = GetHostManifestPathFromCommandLine(native_host_name); if (!path.empty()) { manifest = NativeMessagingHostManifest::Load(path, &error_message); } else { // Try loading the manifest from the default location. manifest = FindAndLoadManifest(native_host_name, &error_message); } if (manifest && manifest->name() != native_host_name) { error_message = "Name specified in the manifest does not match."; manifest.reset(); } if (!manifest) { LOG(ERROR) << "Failed to load manifest for native messaging host " << native_host_name << ": " << error_message; content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this, callback, RESULT_NOT_FOUND, base::kInvalidPlatformFileValue, base::kInvalidPlatformFileValue)); return; } if (!manifest->allowed_origins().MatchesSecurityOrigin(origin)) { // Not an allowed origin. content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this, callback, RESULT_FORBIDDEN, base::kInvalidPlatformFileValue, base::kInvalidPlatformFileValue)); return; } CommandLine command_line(manifest->path()); command_line.AppendArg(origin.spec()); base::PlatformFile read_file; base::PlatformFile write_file; LaunchResult result = RESULT_FAILED_TO_START; if (NativeProcessLauncher::LaunchNativeProcess( command_line, &read_file, &write_file)) { result = RESULT_SUCCESS; } content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this, callback, result, read_file, write_file)); } void NativeProcessLauncherImpl::Core::CallCallbackOnIOThread( LaunchedCallback callback, LaunchResult result, base::PlatformFile read_file, base::PlatformFile write_file) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); if (detached_) { if (read_file != base::kInvalidPlatformFileValue) base::ClosePlatformFile(read_file); if (write_file != base::kInvalidPlatformFileValue) base::ClosePlatformFile(write_file); return; } callback.Run(result, read_file, write_file); } NativeProcessLauncherImpl::NativeProcessLauncherImpl() : core_(new Core()) { } NativeProcessLauncherImpl::~NativeProcessLauncherImpl() { core_->Detach(); } void NativeProcessLauncherImpl::Launch(const GURL& origin, const std::string& native_host_name, LaunchedCallback callback) const { core_->Launch(origin, native_host_name, callback); } } // namespace // static scoped_ptr<NativeProcessLauncher> NativeProcessLauncher::CreateDefault() { return scoped_ptr<NativeProcessLauncher>(new NativeProcessLauncherImpl()); } } // namespace extensions