- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / messaging / native_process_launcher_win.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
6
7 #include <windows.h>
8
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/process/kill.h"
12 #include "base/process/launch.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/win/registry.h"
18 #include "base/win/scoped_handle.h"
19 #include "crypto/random.h"
20
21 namespace extensions {
22
23 const wchar_t kNativeMessagingRegistryKey[] =
24     L"SOFTWARE\\Google\\Chrome\\NativeMessagingHosts";
25
26 namespace {
27
28 // Reads path to the native messaging host manifest from the registry. Returns
29 // empty string if the path isn't found.
30 string16 GetManifestPath(const string16& native_host_name, DWORD flags) {
31   base::win::RegKey key;
32   string16 result;
33
34   if (key.Open(HKEY_LOCAL_MACHINE, kNativeMessagingRegistryKey,
35                KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
36       key.OpenKey(native_host_name.c_str(),
37                   KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
38       key.ReadValue(NULL, &result) != ERROR_SUCCESS) {
39     return string16();
40   }
41
42   return result;
43 }
44
45 }  // namespace
46
47 // static
48 base::FilePath NativeProcessLauncher::FindManifest(
49     const std::string& native_host_name,
50     std::string* error_message) {
51   string16 native_host_name_wide = UTF8ToUTF16(native_host_name);
52
53   // First check 32-bit registry and then try 64-bit.
54   string16 manifest_path_str =
55       GetManifestPath(native_host_name_wide, KEY_WOW64_32KEY);
56   if (manifest_path_str.empty())
57     manifest_path_str = GetManifestPath(native_host_name_wide, KEY_WOW64_64KEY);
58
59   if (manifest_path_str.empty()) {
60     *error_message = "Native messaging host " + native_host_name +
61         " is not registered";
62     return base::FilePath();
63   }
64
65   base::FilePath manifest_path(manifest_path_str);
66   if (!manifest_path.IsAbsolute()) {
67     *error_message = "Path to native messaging host manifest must be absolute.";
68     return base::FilePath();
69   }
70
71   return manifest_path;
72 }
73
74 // static
75 bool NativeProcessLauncher::LaunchNativeProcess(
76     const CommandLine& command_line,
77     base::ProcessHandle* process_handle,
78     base::PlatformFile* read_file,
79     base::PlatformFile* write_file) {
80   // Timeout for the IO pipes.
81   const DWORD kTimeoutMs = 5000;
82
83   // Windows will use default buffer size when 0 is passed to
84   // CreateNamedPipeW().
85   const DWORD kBufferSize = 0;
86
87   if (!command_line.GetProgram().IsAbsolute()) {
88     LOG(ERROR) << "Native Messaging host path must be absolute.";
89     return false;
90   }
91
92   uint64 pipe_name_token;
93   crypto::RandBytes(&pipe_name_token, sizeof(pipe_name_token));
94   string16 out_pipe_name = base::StringPrintf(
95       L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", pipe_name_token);
96   string16 in_pipe_name = base::StringPrintf(
97       L"\\\\.\\pipe\\chrome.nativeMessaging.in.%llx", pipe_name_token);
98
99   // Create the pipes to read and write from.
100   base::win::ScopedHandle stdout_pipe(
101       CreateNamedPipeW(out_pipe_name.c_str(),
102                        PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED |
103                            FILE_FLAG_FIRST_PIPE_INSTANCE,
104                        PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
105                        kTimeoutMs, NULL));
106   if (!stdout_pipe.IsValid()) {
107     LOG(ERROR) << "Failed to create pipe " << out_pipe_name;
108     return false;
109   }
110
111   base::win::ScopedHandle stdin_pipe(
112       CreateNamedPipeW(in_pipe_name.c_str(),
113                        PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED |
114                            FILE_FLAG_FIRST_PIPE_INSTANCE,
115                        PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
116                        kTimeoutMs, NULL));
117   if (!stdin_pipe.IsValid()) {
118     LOG(ERROR) << "Failed to create pipe " << in_pipe_name;
119     return false;
120   }
121
122   DWORD comspec_length = ::GetEnvironmentVariable(L"COMSPEC", NULL, 0);
123   if (comspec_length == 0) {
124     LOG(ERROR) << "COMSPEC is not set";
125     return false;
126   }
127   scoped_ptr<wchar_t[]> comspec(new wchar_t[comspec_length]);
128   ::GetEnvironmentVariable(L"COMSPEC", comspec.get(), comspec_length);
129
130   string16 command_line_string = command_line.GetCommandLineString();
131
132   string16 command = base::StringPrintf(
133       L"%ls /c %ls < %ls > %ls",
134       comspec.get(), command_line_string.c_str(),
135       in_pipe_name.c_str(), out_pipe_name.c_str());
136
137   base::LaunchOptions options;
138   options.start_hidden = true;
139   base::ProcessHandle cmd_handle;
140   if (!base::LaunchProcess(command.c_str(), options, &cmd_handle)) {
141     LOG(ERROR) << "Error launching process "
142                << command_line.GetProgram().MaybeAsASCII();
143     return false;
144   }
145
146   bool stdout_connected = ConnectNamedPipe(stdout_pipe.Get(), NULL) ?
147       TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
148   bool stdin_connected = ConnectNamedPipe(stdin_pipe.Get(), NULL) ?
149       TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
150   if (!stdout_connected || !stdin_connected) {
151     base::KillProcess(cmd_handle, 0, false);
152     base::CloseProcessHandle(cmd_handle);
153     LOG(ERROR) << "Failed to connect IO pipes when starting "
154                << command_line.GetProgram().MaybeAsASCII();
155     return false;
156   }
157
158   *process_handle = cmd_handle;
159   *read_file = stdout_pipe.Take();
160   *write_file = stdin_pipe.Take();
161
162   return true;
163 }
164
165 }  // namespace extensions