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/utility/importer/firefox_importer_unittest_utils.h" 6 7 #include "base/base_switches.h" 8 #include "base/bind.h" 9 #include "base/command_line.h" 10 #include "base/file_util.h" 11 #include "base/files/file_path.h" 12 #include "base/files/scoped_file.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/posix/global_descriptors.h" 15 #include "base/process/kill.h" 16 #include "base/process/launch.h" 17 #include "base/test/test_timeouts.h" 18 #include "chrome/common/importer/firefox_importer_utils.h" 19 #include "ipc/ipc_channel.h" 20 #include "ipc/ipc_descriptors.h" 21 #include "ipc/ipc_listener.h" 22 #include "ipc/ipc_message.h" 23 #include "ipc/ipc_multiprocess_test.h" 24 #include "testing/multiprocess_func_list.h" 25 26 #define IPC_MESSAGE_IMPL 27 #include "chrome/utility/importer/firefox_importer_unittest_messages_internal.h" 28 29 namespace { 30 31 // Name of IPC Channel to use for Server<-> Child Communications. 32 const char kTestChannelID[] = "T1"; 33 34 // Launch the child process: 35 // |nss_path| - path to the NSS directory holding the decryption libraries. 36 // |channel| - IPC Channel to use for communication. 37 // |handle| - On return, the process handle to use to communicate with the 38 // child. 39 bool LaunchNSSDecrypterChildProcess(const base::FilePath& nss_path, 40 IPC::Channel* channel, base::ProcessHandle* handle) { 41 CommandLine cl(*CommandLine::ForCurrentProcess()); 42 cl.AppendSwitchASCII(switches::kTestChildProcess, "NSSDecrypterChildProcess"); 43 44 // Set env variable needed for FF encryption libs to load. 45 // See "chrome/utility/importer/nss_decryptor_mac.mm" for an explanation of 46 // why we need this. 47 base::LaunchOptions options; 48 options.environ["DYLD_FALLBACK_LIBRARY_PATH"] = nss_path.value(); 49 50 base::ScopedFD ipcfd(channel->TakeClientFileDescriptor()); 51 if (!ipcfd.is_valid()) 52 return false; 53 54 base::FileHandleMappingVector fds_to_map; 55 fds_to_map.push_back(std::pair<int,int>(ipcfd.get(), 56 kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); 57 58 options.fds_to_remap = &fds_to_map; 59 return base::LaunchProcess(cl.argv(), options, handle); 60 } 61 62 } // namespace 63 64 //----------------------- Server -------------------- 65 66 // Class to communicate on the server side of the IPC Channel. 67 // Method calls are sent over IPC and replies are read back into class 68 // variables. 69 // This class needs to be called on a single thread. 70 class FFDecryptorServerChannelListener : public IPC::Listener { 71 public: 72 FFDecryptorServerChannelListener() 73 : got_result(false), sender_(NULL) {} 74 75 void SetSender(IPC::Sender* sender) { 76 sender_ = sender; 77 } 78 79 void OnInitDecryptorResponse(bool result) { 80 DCHECK(!got_result); 81 result_bool = result; 82 got_result = true; 83 base::MessageLoop::current()->Quit(); 84 } 85 86 void OnDecryptedTextResponse(const base::string16& decrypted_text) { 87 DCHECK(!got_result); 88 result_string = decrypted_text; 89 got_result = true; 90 base::MessageLoop::current()->Quit(); 91 } 92 93 void OnParseSignonsResponse( 94 const std::vector<autofill::PasswordForm>& parsed_vector) { 95 DCHECK(!got_result); 96 result_vector = parsed_vector; 97 got_result = true; 98 base::MessageLoop::current()->Quit(); 99 } 100 101 void QuitClient() { 102 if (sender_) 103 sender_->Send(new Msg_Decryptor_Quit()); 104 } 105 106 virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE { 107 bool handled = true; 108 IPC_BEGIN_MESSAGE_MAP(FFDecryptorServerChannelListener, msg) 109 IPC_MESSAGE_HANDLER(Msg_Decryptor_InitReturnCode, OnInitDecryptorResponse) 110 IPC_MESSAGE_HANDLER(Msg_Decryptor_Response, OnDecryptedTextResponse) 111 IPC_MESSAGE_HANDLER(Msg_ParseSignons_Response, OnParseSignonsResponse) 112 IPC_MESSAGE_UNHANDLED(handled = false) 113 IPC_END_MESSAGE_MAP() 114 return handled; 115 } 116 117 // If an error occured, just kill the message Loop. 118 virtual void OnChannelError() OVERRIDE { 119 got_result = false; 120 base::MessageLoop::current()->Quit(); 121 } 122 123 // Results of IPC calls. 124 base::string16 result_string; 125 std::vector<autofill::PasswordForm> result_vector; 126 bool result_bool; 127 // True if IPC call succeeded and data in above variables is valid. 128 bool got_result; 129 130 private: 131 IPC::Sender* sender_; // weak 132 }; 133 134 FFUnitTestDecryptorProxy::FFUnitTestDecryptorProxy() 135 : child_process_(0) { 136 } 137 138 bool FFUnitTestDecryptorProxy::Setup(const base::FilePath& nss_path) { 139 // Create a new message loop and spawn the child process. 140 message_loop_.reset(new base::MessageLoopForIO()); 141 142 listener_.reset(new FFDecryptorServerChannelListener()); 143 channel_ = IPC::Channel::CreateServer(kTestChannelID, listener_.get()); 144 CHECK(channel_->Connect()); 145 listener_->SetSender(channel_.get()); 146 147 // Spawn child and set up sync IPC connection. 148 bool ret = LaunchNSSDecrypterChildProcess(nss_path, 149 channel_.get(), 150 &child_process_); 151 return ret && (child_process_ != 0); 152 } 153 154 FFUnitTestDecryptorProxy::~FFUnitTestDecryptorProxy() { 155 listener_->QuitClient(); 156 channel_->Close(); 157 158 if (child_process_) { 159 base::WaitForSingleProcess(child_process_, base::TimeDelta::FromSeconds(5)); 160 base::CloseProcessHandle(child_process_); 161 } 162 } 163 164 // A message_loop task that quits the message loop when invoked, setting cancel 165 // causes the task to do nothing when invoked. 166 class CancellableQuitMsgLoop : public base::RefCounted<CancellableQuitMsgLoop> { 167 public: 168 CancellableQuitMsgLoop() : cancelled_(false) {} 169 void QuitNow() { 170 if (!cancelled_) 171 base::MessageLoop::current()->Quit(); 172 } 173 bool cancelled_; 174 175 private: 176 friend class base::RefCounted<CancellableQuitMsgLoop>; 177 ~CancellableQuitMsgLoop() {} 178 }; 179 180 // Spin until either a client response arrives or a timeout occurs. 181 bool FFUnitTestDecryptorProxy::WaitForClientResponse() { 182 // What we're trying to do here is to wait for an RPC message to go over the 183 // wire and the client to reply. If the client does not reply by a given 184 // timeout we kill the message loop. 185 // The way we do this is to post a CancellableQuitMsgLoop for 3 seconds in 186 // the future and cancel it if an RPC message comes back earlier. 187 // This relies on the IPC listener class to quit the message loop itself when 188 // a message comes in. 189 scoped_refptr<CancellableQuitMsgLoop> quit_task( 190 new CancellableQuitMsgLoop()); 191 base::MessageLoop::current()->PostDelayedTask( 192 FROM_HERE, 193 base::Bind(&CancellableQuitMsgLoop::QuitNow, quit_task.get()), 194 TestTimeouts::action_max_timeout()); 195 196 message_loop_->Run(); 197 bool ret = !quit_task->cancelled_; 198 quit_task->cancelled_ = false; 199 return ret; 200 } 201 202 bool FFUnitTestDecryptorProxy::DecryptorInit(const base::FilePath& dll_path, 203 const base::FilePath& db_path) { 204 channel_->Send(new Msg_Decryptor_Init(dll_path, db_path)); 205 bool ok = WaitForClientResponse(); 206 if (ok && listener_->got_result) { 207 listener_->got_result = false; 208 return listener_->result_bool; 209 } 210 return false; 211 } 212 213 base::string16 FFUnitTestDecryptorProxy::Decrypt(const std::string& crypt) { 214 channel_->Send(new Msg_Decrypt(crypt)); 215 bool ok = WaitForClientResponse(); 216 if (ok && listener_->got_result) { 217 listener_->got_result = false; 218 return listener_->result_string; 219 } 220 return base::string16(); 221 } 222 223 std::vector<autofill::PasswordForm> FFUnitTestDecryptorProxy::ParseSignons( 224 const base::FilePath& signons_path) { 225 channel_->Send(new Msg_ParseSignons(signons_path)); 226 bool ok = WaitForClientResponse(); 227 if (ok && listener_->got_result) { 228 listener_->got_result = false; 229 return listener_->result_vector; 230 } 231 return std::vector<autofill::PasswordForm>(); 232 } 233 234 //---------------------------- Child Process ----------------------- 235 236 // Class to listen on the client side of the ipc channel, it calls through 237 // to the NSSDecryptor and sends back a reply. 238 class FFDecryptorClientChannelListener : public IPC::Listener { 239 public: 240 FFDecryptorClientChannelListener() 241 : sender_(NULL) {} 242 243 void SetSender(IPC::Sender* sender) { 244 sender_ = sender; 245 } 246 247 void OnDecryptor_Init(base::FilePath dll_path, base::FilePath db_path) { 248 bool ret = decryptor_.Init(dll_path, db_path); 249 sender_->Send(new Msg_Decryptor_InitReturnCode(ret)); 250 } 251 252 void OnDecrypt(std::string crypt) { 253 base::string16 unencrypted_str = decryptor_.Decrypt(crypt); 254 sender_->Send(new Msg_Decryptor_Response(unencrypted_str)); 255 } 256 257 void OnParseSignons(base::FilePath signons_path) { 258 std::vector<autofill::PasswordForm> forms; 259 decryptor_.ReadAndParseSignons(signons_path, &forms); 260 sender_->Send(new Msg_ParseSignons_Response(forms)); 261 } 262 263 void OnQuitRequest() { 264 base::MessageLoop::current()->Quit(); 265 } 266 267 virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE { 268 bool handled = true; 269 IPC_BEGIN_MESSAGE_MAP(FFDecryptorClientChannelListener, msg) 270 IPC_MESSAGE_HANDLER(Msg_Decryptor_Init, OnDecryptor_Init) 271 IPC_MESSAGE_HANDLER(Msg_Decrypt, OnDecrypt) 272 IPC_MESSAGE_HANDLER(Msg_ParseSignons, OnParseSignons) 273 IPC_MESSAGE_HANDLER(Msg_Decryptor_Quit, OnQuitRequest) 274 IPC_MESSAGE_UNHANDLED(handled = false) 275 IPC_END_MESSAGE_MAP() 276 return handled; 277 } 278 279 virtual void OnChannelError() OVERRIDE { 280 base::MessageLoop::current()->Quit(); 281 } 282 283 private: 284 NSSDecryptor decryptor_; 285 IPC::Sender* sender_; 286 }; 287 288 // Entry function in child process. 289 MULTIPROCESS_IPC_TEST_MAIN(NSSDecrypterChildProcess) { 290 base::MessageLoopForIO main_message_loop; 291 FFDecryptorClientChannelListener listener; 292 293 scoped_ptr<IPC::Channel> channel = IPC::Channel::CreateClient( 294 kTestChannelID, &listener); 295 CHECK(channel->Connect()); 296 listener.SetSender(channel.get()); 297 298 // run message loop 299 base::MessageLoop::current()->Run(); 300 301 return 0; 302 } 303