1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 // A class handling the IPC connection for the session b/w server and clients.
31
32 #include "client/client.h"
33
34 #ifdef OS_WIN
35 #include <Windows.h>
36 #else
37 #include <unistd.h>
38 #endif // OS_WIN
39
40 #include <cstddef>
41 #include <memory>
42
43 #include "base/const.h"
44 #include "base/file_stream.h"
45 #include "base/file_util.h"
46 #include "base/logging.h"
47 #include "base/process.h"
48 #include "base/run_level.h"
49 #include "base/singleton.h"
50 #include "base/system_util.h"
51 #include "base/util.h"
52 #include "base/version.h"
53 #include "ipc/ipc.h"
54 #include "protocol/commands.pb.h"
55 #include "protocol/config.pb.h"
56
57 #ifdef OS_WIN
58 #include "base/win_util.h"
59 #endif // OS_WIN
60
61 #ifdef OS_MACOSX
62 #include "base/mac_process.h"
63 #endif // OS_MACOSX
64
65 namespace mozc {
66 namespace client {
67
68 namespace {
69 const char kServerAddress[] = "session"; // name for the IPC connection.
70 const int kResultBufferSize = 8192 * 32; // size of IPC buffer
71 const size_t kMaxPlayBackSize = 512; // size of maximum history
72
73 #ifdef DEBUG
74 const int kDefaultTimeout = 100000; // 100 sec for dbg
75 #else
76 const int kDefaultTimeout = 30000; // 30 sec for opt
77 #endif // DEBUG
78
79 // Delete Session is called inside the Destructor of Client class.
80 // To prevent from an application being stalled at the close time,
81 // we change the timeout of DeleteSession shorter.
82 // This timeout is only applied in the DeleteSessions command
83 // called from Destructor. When an application calls DeleteSession
84 // explicitly, the default timeout is used.
85 const int kDeleteSessionOnDestructorTimeout = 1000; // 1 sec
86 } // namespace
87
Client()88 Client::Client()
89 : id_(0),
90 server_launcher_(new ServerLauncher),
91 result_(new char[kResultBufferSize]),
92 timeout_(kDefaultTimeout),
93 server_status_(SERVER_UNKNOWN),
94 server_protocol_version_(0),
95 server_process_id_(0),
96 last_mode_(commands::DIRECT) {
97 client_factory_ = IPCClientFactory::GetIPCClientFactory();
98 }
99
~Client()100 Client::~Client() {
101 set_timeout(kDeleteSessionOnDestructorTimeout);
102 DeleteSession();
103 }
104
SetIPCClientFactory(IPCClientFactoryInterface * client_factory)105 void Client::SetIPCClientFactory(IPCClientFactoryInterface *client_factory) {
106 client_factory_ = client_factory;
107 }
108
SetServerLauncher(ServerLauncherInterface * server_launcher)109 void Client::SetServerLauncher(
110 ServerLauncherInterface *server_launcher) {
111 server_launcher_.reset(server_launcher);
112 }
113
IsValidRunLevel() const114 bool Client::IsValidRunLevel() const {
115 return RunLevel::IsValidClientRunLevel();
116 }
117
EnsureConnection()118 bool Client::EnsureConnection() {
119 switch (server_status_) {
120 case SERVER_OK:
121 case SERVER_INVALID_SESSION:
122 return true;
123 break;
124 case SERVER_FATAL:
125 // once the current status goes into SERVER_FATAL. do nothing.
126 return false;
127 break;
128 case SERVER_TIMEOUT:
129 OnFatal(ServerLauncherInterface::SERVER_TIMEOUT);
130 server_status_ = SERVER_FATAL;
131 return false;
132 break;
133 case SERVER_BROKEN_MESSAGE:
134 OnFatal(ServerLauncherInterface::SERVER_BROKEN_MESSAGE);
135 server_status_ = SERVER_FATAL;
136 return false;
137 break;
138 case SERVER_VERSION_MISMATCH:
139 OnFatal(ServerLauncherInterface::SERVER_VERSION_MISMATCH);
140 server_status_ = SERVER_FATAL;
141 return false;
142 break;
143 case SERVER_SHUTDOWN:
144 #ifdef DEBUG
145 OnFatal(ServerLauncherInterface::SERVER_SHUTDOWN);
146 // don't break here as SERVER_SHUTDOWN and SERVER_UNKNOWN
147 // have basically the same treatment.
148 #endif // DEBUG
149 case SERVER_UNKNOWN:
150 if (StartServer()) {
151 server_status_ = SERVER_INVALID_SESSION;
152 return true;
153 } else {
154 LOG(ERROR) << "Cannot start server";
155 OnFatal(ServerLauncherInterface::SERVER_FATAL);
156 server_status_ = SERVER_FATAL;
157 return false;
158 }
159 break;
160 default:
161 LOG(ERROR) << "Unknown status: " << server_status_;
162 break;
163 }
164
165 return true;
166 }
167
EnsureSession()168 bool Client::EnsureSession() {
169 if (!EnsureConnection()) {
170 return false;
171 }
172
173 if (server_status_ == SERVER_INVALID_SESSION) {
174 if (CreateSession()) {
175 server_status_ = SERVER_OK;
176 return true;
177 } else {
178 LOG(ERROR) << "CreateSession failed";
179 // call EnsureConnection to display error message
180 EnsureConnection();
181 return false;
182 }
183 }
184
185 return true;
186 }
187
DumpQueryOfDeath()188 void Client::DumpQueryOfDeath() {
189 LOG(ERROR) << "The playback history looks like a query of death";
190 const char kFilename[] = "query_of_death.log";
191 const char kLabel[] = "Query of Death";
192 DumpHistorySnapshot(kFilename, kLabel);
193 ResetHistory();
194 }
195
DumpHistorySnapshot(const string & filename,const string & label) const196 void Client::DumpHistorySnapshot(const string &filename,
197 const string &label) const {
198 const string snapshot_file =
199 FileUtil::JoinPath(SystemUtil::GetUserProfileDirectory(), filename);
200 // open with append mode
201 OutputFileStream output(snapshot_file.c_str(), std::ios::app);
202
203 output << "---- Start history snapshot for " << label << std::endl;
204 output << "Created at " << Logging::GetLogMessageHeader() << std::endl;
205 output << "Version " << Version::GetMozcVersion() << std::endl;
206 for (size_t i = 0; i < history_inputs_.size(); ++i) {
207 output << history_inputs_[i].DebugString();
208 }
209 output << "---- End history snapshot for " << label << std::endl;
210 }
211
PlaybackHistory()212 void Client::PlaybackHistory() {
213 if (history_inputs_.size() >= kMaxPlayBackSize) {
214 ResetHistory();
215 return;
216 }
217
218 commands::Output output;
219 VLOG(1) << "Playback history: size=" << history_inputs_.size();
220 for (size_t i = 0; i < history_inputs_.size(); ++i) {
221 history_inputs_[i].set_id(id_);
222 if (!Call(history_inputs_[i], &output)) {
223 LOG(ERROR) << "playback history failed: "
224 << history_inputs_[i].DebugString();
225 break;
226 }
227 }
228 }
229
PushHistory(const commands::Input & input,const commands::Output & output)230 void Client::PushHistory(const commands::Input &input,
231 const commands::Output &output) {
232 if (!output.has_consumed() || !output.consumed()) {
233 // Do not remember unconsumed input.
234 return;
235 }
236
237 // Update mode
238 if (output.has_mode()) {
239 last_mode_ = output.mode();
240 }
241
242 // don't insert a new input when history_inputs_.size()
243 // reaches to the maximum size. This prevents DOS attack.
244 if (history_inputs_.size() < kMaxPlayBackSize) {
245 history_inputs_.push_back(input);
246 }
247
248 // found context boundary.
249 // don't regard the empty output (output without preedit) as the context
250 // boundary, as the IMEOn command make the empty output.
251 if (input.type() == commands::Input::SEND_KEY &&
252 output.has_result()) {
253 ResetHistory();
254 }
255 }
256
257 // Clear the history and push IMEOn command for initialize session.
ResetHistory()258 void Client::ResetHistory() {
259 history_inputs_.clear();
260 #if defined(OS_MACOSX)
261 // On Mac, we should send ON key at the first of each input session
262 // excepting the very first session, because when the session is restored,
263 // its state is direct. On the first session, users should send ON key
264 // by themselves.
265 // On Windows, this is not required because now we can send IME On/Off
266 // state with the key event. See b/8601275
267 // Note that we are assuming that ResetHistory is called only when the
268 // client is ON.
269 // TODO(toshiyuki): Make sure that this assuming is reasonable or not.
270 if (last_mode_ != commands::DIRECT) {
271 commands::Input input;
272 input.set_type(commands::Input::SEND_KEY);
273 input.mutable_key()->set_special_key(commands::KeyEvent::ON);
274 input.mutable_key()->set_mode(last_mode_);
275 history_inputs_.push_back(input);
276 }
277 #endif
278 }
279
GetHistoryInputs(std::vector<commands::Input> * output) const280 void Client::GetHistoryInputs(std::vector<commands::Input> *output) const {
281 output->clear();
282 for (size_t i = 0; i < history_inputs_.size(); ++i) {
283 output->push_back(history_inputs_[i]);
284 }
285 }
286
SendKeyWithContext(const commands::KeyEvent & key,const commands::Context & context,commands::Output * output)287 bool Client::SendKeyWithContext(const commands::KeyEvent &key,
288 const commands::Context &context,
289 commands::Output *output) {
290 commands::Input input;
291 input.set_type(commands::Input::SEND_KEY);
292 input.mutable_key()->CopyFrom(key);
293 // If the pointer of |context| is not the default_instance, update the data.
294 if (&context != &commands::Context::default_instance()) {
295 input.mutable_context()->CopyFrom(context);
296 }
297 return EnsureCallCommand(&input, output);
298 }
299
TestSendKeyWithContext(const commands::KeyEvent & key,const commands::Context & context,commands::Output * output)300 bool Client::TestSendKeyWithContext(const commands::KeyEvent &key,
301 const commands::Context &context,
302 commands::Output *output) {
303 commands::Input input;
304 input.set_type(commands::Input::TEST_SEND_KEY);
305 // If the pointer of |context| is not the default_instance, update the data.
306 if (&context != &commands::Context::default_instance()) {
307 input.mutable_context()->CopyFrom(context);
308 }
309 input.mutable_key()->CopyFrom(key);
310 return EnsureCallCommand(&input, output);
311 }
312
SendCommandWithContext(const commands::SessionCommand & command,const commands::Context & context,commands::Output * output)313 bool Client::SendCommandWithContext(const commands::SessionCommand &command,
314 const commands::Context &context,
315 commands::Output *output) {
316 commands::Input input;
317 input.set_type(commands::Input::SEND_COMMAND);
318 input.mutable_command()->CopyFrom(command);
319 // If the pointer of |context| is not the default_instance, update the data.
320 if (&context != &commands::Context::default_instance()) {
321 input.mutable_context()->CopyFrom(context);
322 }
323 return EnsureCallCommand(&input, output);
324 }
325
CheckVersionOrRestartServer()326 bool Client::CheckVersionOrRestartServer() {
327 commands::Input input;
328 commands::Output output;
329 input.set_type(commands::Input::NO_OPERATION);
330 if (!CheckVersionOrRestartServerInternal(input, &output)) {
331 LOG(ERROR) << "CheckVersionOrRestartServerInternal failed";
332 if (!EnsureConnection()) {
333 LOG(ERROR) << "EnsureConnection failed";
334 return false;
335 }
336 }
337
338 return true;
339 }
340
EnsureCallCommand(commands::Input * input,commands::Output * output)341 bool Client::EnsureCallCommand(commands::Input *input,
342 commands::Output *output) {
343 if (!EnsureSession()) {
344 LOG(ERROR) << "EnsureSession failed";
345 return false;
346 }
347
348 InitInput(input);
349 output->set_id(0);
350
351 if (!CallAndCheckVersion(*input, output)) { // server is not running
352 LOG(ERROR) << "Call command failed";
353 } else if (output->id() != input->id()) { // invalid ID
354 LOG(ERROR) << "Session id is void. re-issue session id";
355 server_status_ = SERVER_INVALID_SESSION;
356 }
357
358 // see the result of Call
359 if (server_status_ >= SERVER_TIMEOUT) {
360 return false;
361 }
362
363 if (server_status_ == SERVER_SHUTDOWN ||
364 server_status_ == SERVER_INVALID_SESSION) {
365 if (EnsureSession()) {
366 // playback the history to restore the previous state.
367 PlaybackHistory();
368 InitInput(input);
369 #ifdef DEBUG
370 // The debug binary dumps query of death at the first trial.
371 history_inputs_.push_back(*input);
372 DumpQueryOfDeath();
373 #endif // DEBUG
374 // second trial
375 if (!CallAndCheckVersion(*input, output)) {
376 #ifndef DEBUG
377 // if second trial failed, record the input
378 history_inputs_.push_back(*input);
379 // Opt or release binaries refrain from dumping query of death
380 // at the first trial, but dumps it at the second trial.
381 //
382 // TODO(komatsu, taku): Should release binary dump query of death?
383 DumpQueryOfDeath();
384 #endif // DEBUG
385 return false;
386 }
387 } else {
388 LOG(ERROR) << "EnsureSession failed: " << server_status_;
389 return false;
390 }
391 }
392
393 PushHistory(*input, *output);
394 return true;
395 }
396
EnableCascadingWindow(const bool enable)397 void Client::EnableCascadingWindow(const bool enable) {
398 if (preferences_.get() == NULL) {
399 preferences_.reset(new config::Config);
400 }
401 preferences_->set_use_cascading_window(enable);
402 }
403
set_timeout(int timeout)404 void Client::set_timeout(int timeout) {
405 timeout_ = timeout;
406 }
407
set_restricted(bool restricted)408 void Client::set_restricted(bool restricted) {
409 server_launcher_->set_restricted(restricted);
410 }
411
set_server_program(const string & program_path)412 void Client::set_server_program(const string &program_path) {
413 server_launcher_->set_server_program(program_path);
414 }
415
set_suppress_error_dialog(bool suppress)416 void Client::set_suppress_error_dialog(bool suppress) {
417 server_launcher_->set_suppress_error_dialog(suppress);
418 }
419
set_client_capability(const commands::Capability & capability)420 void Client::set_client_capability(const commands::Capability &capability) {
421 client_capability_.CopyFrom(capability);
422 }
423
CreateSession()424 bool Client::CreateSession() {
425 id_ = 0;
426 commands::Input input;
427 input.set_type(commands::Input::CREATE_SESSION);
428
429 input.mutable_capability()->CopyFrom(client_capability_);
430
431 commands::ApplicationInfo *info = input.mutable_application_info();
432 DCHECK(info);
433
434 #ifdef OS_WIN
435 info->set_process_id(static_cast<uint32>(::GetCurrentProcessId()));
436 info->set_thread_id(static_cast<uint32>(::GetCurrentThreadId()));
437 #else
438 info->set_process_id(static_cast<uint32>(getpid()));
439 info->set_thread_id(0);
440 #endif
441
442 commands::Output output;
443 if (!CheckVersionOrRestartServerInternal(input, &output)) {
444 LOG(ERROR) << "CheckVersionOrRestartServer() failed";
445 return false;
446 }
447
448 if (output.error_code() != commands::Output::SESSION_SUCCESS) {
449 LOG(ERROR) << "Server returns an error";
450 server_status_ = SERVER_INVALID_SESSION;
451 return false;
452 }
453
454 id_ = output.id();
455 return true;
456 }
457
DeleteSession()458 bool Client::DeleteSession() {
459 // No need to delete session
460 if (id_ == 0) {
461 return true;
462 }
463
464 commands::Input input;
465 InitInput(&input);
466 input.set_type(commands::Input::DELETE_SESSION);
467
468 commands::Output output;
469 if (!Call(input, &output)) {
470 LOG(ERROR) << "DeleteSession failed";
471 return false;
472 }
473 id_ = 0;
474 return true;
475 }
476
GetConfig(config::Config * config)477 bool Client::GetConfig(config::Config *config) {
478 commands::Input input;
479 InitInput(&input);
480 input.set_type(commands::Input::GET_CONFIG);
481
482 commands::Output output;
483 if (!Call(input, &output)) {
484 return false;
485 }
486
487 if (!output.has_config()) {
488 return false;
489 }
490
491 config->Clear();
492 config->CopyFrom(output.config());
493 return true;
494 }
495
SetConfig(const config::Config & config)496 bool Client::SetConfig(const config::Config &config) {
497 commands::Input input;
498 InitInput(&input);
499 input.set_type(commands::Input::SET_CONFIG);
500 input.mutable_config()->CopyFrom(config);
501
502 commands::Output output;
503 if (!Call(input, &output)) {
504 return false;
505 }
506
507 return true;
508 }
509
ClearUserHistory()510 bool Client::ClearUserHistory() {
511 return CallCommand(commands::Input::CLEAR_USER_HISTORY);
512 }
513
ClearUserPrediction()514 bool Client::ClearUserPrediction() {
515 return CallCommand(commands::Input::CLEAR_USER_PREDICTION);
516 }
517
ClearUnusedUserPrediction()518 bool Client::ClearUnusedUserPrediction() {
519 return CallCommand(commands::Input::CLEAR_UNUSED_USER_PREDICTION);
520 }
521
Shutdown()522 bool Client::Shutdown() {
523 CallCommand(commands::Input::SHUTDOWN);
524 if (!server_launcher_->WaitServer(server_process_id_)) {
525 LOG(ERROR) << "Cannot shutdown the server";
526 return false;
527 }
528 return true;
529 }
530
SyncData()531 bool Client::SyncData() {
532 return CallCommand(commands::Input::SYNC_DATA);
533 }
534
Reload()535 bool Client::Reload() {
536 return CallCommand(commands::Input::RELOAD);
537 }
538
Cleanup()539 bool Client::Cleanup() {
540 return CallCommand(commands::Input::CLEANUP);
541 }
542
NoOperation()543 bool Client::NoOperation() {
544 return CallCommand(commands::Input::NO_OPERATION);
545 }
546
547 // PingServer ignores all server status
PingServer() const548 bool Client::PingServer() const {
549 if (client_factory_ == NULL) {
550 return false;
551 }
552
553 commands::Input input;
554 commands::Output output;
555
556 InitInput(&input);
557 input.set_type(commands::Input::NO_OPERATION);
558
559 // Call IPC
560 std::unique_ptr<IPCClientInterface> client(
561 client_factory_->NewClient(kServerAddress,
562 server_launcher_->server_program()));
563
564 if (client.get() == NULL) {
565 LOG(ERROR) << "Cannot make client object";
566 return false;
567 }
568
569 if (!client->Connected()) {
570 LOG(ERROR) << "Connection failure to " << kServerAddress;
571 return false;
572 }
573
574 // Serialize
575 string request;
576 input.SerializeToString(&request);
577
578 size_t size = kResultBufferSize;
579 if (!client->Call(request.data(), request.size(),
580 result_.get(), &size, timeout_)) {
581 LOG(ERROR) << "IPCClient::Call failed: " << client->GetLastIPCError();
582 return false;
583 }
584
585 return true;
586 }
587
CallCommand(commands::Input::CommandType type)588 bool Client::CallCommand(commands::Input::CommandType type) {
589 commands::Input input;
590 InitInput(&input);
591 input.set_type(type);
592 commands::Output output;
593 return Call(input, &output);
594 }
595
CallAndCheckVersion(const commands::Input & input,commands::Output * output)596 bool Client::CallAndCheckVersion(const commands::Input &input,
597 commands::Output *output) {
598 if (!Call(input, output)) {
599 if (server_protocol_version_ != IPC_PROTOCOL_VERSION) {
600 LOG(ERROR) << "version mismatch: "
601 << server_protocol_version_ << " "
602 << static_cast<int>(IPC_PROTOCOL_VERSION);
603 server_status_ = SERVER_VERSION_MISMATCH;
604 }
605 return false;
606 }
607
608 return true;
609 }
610
Call(const commands::Input & input,commands::Output * output)611 bool Client::Call(const commands::Input &input,
612 commands::Output *output) {
613 VLOG(2) << "commands::Input: " << std::endl
614 << input.DebugString();
615
616 // don't repeat Call() if the status is either
617 // SERVER_FATAL, SERVER_TIMEOUT, or SERVER_BROKEN_MESSAGE
618 if (server_status_ >= SERVER_TIMEOUT) {
619 LOG(ERROR) << "Don't repat the same status: " << server_status_;
620 return false;
621 }
622
623 if (client_factory_ == NULL) {
624 return false;
625 }
626
627 // Serialize
628 string request;
629 input.SerializeToString(&request);
630
631 // Call IPC
632 std::unique_ptr<IPCClientInterface> client(
633 client_factory_->NewClient(kServerAddress,
634 server_launcher_->server_program()));
635
636 // set client protocol version.
637 // When an error occurs inside Connected() function,
638 // the server_protocol_version_ may be set to
639 // the default value defined in .proto file.
640 // This caused an mis-version-detection.
641 // To avoid such situation, we set the client protocol version
642 // before calling IPC request.
643 server_protocol_version_ = IPC_PROTOCOL_VERSION;
644 server_product_version_ = Version::GetMozcVersion();
645 server_process_id_ = 0;
646
647 if (client.get() == NULL) {
648 LOG(ERROR) << "Cannot make client object";
649 server_status_ = SERVER_FATAL;
650 return false;
651 }
652
653 if (!client->Connected()) {
654 LOG(ERROR) << "Connection failure to " << kServerAddress;
655 // if the status is not SERVER_UNKNOWN, it means that
656 // the server WAS working as correctly.
657 if (server_status_ != SERVER_UNKNOWN) {
658 server_status_ = SERVER_SHUTDOWN;
659 }
660 return false;
661 }
662
663 server_protocol_version_ = client->GetServerProtocolVersion();
664 server_product_version_ = client->GetServerProductVersion();
665 server_process_id_ = client->GetServerProcessId();
666
667 if (server_protocol_version_ != IPC_PROTOCOL_VERSION) {
668 LOG(ERROR) << "Server version mismatch. skipped to update the status here";
669 return false;
670 }
671
672 // Drop DebugString() as it raises segmentation fault.
673 // http://b/2126375
674 // TODO(taku): Investigate the error in detail.
675 size_t size = kResultBufferSize;
676 if (!client->Call(request.data(), request.size(),
677 result_.get(), &size, timeout_)) {
678 LOG(ERROR) << "Call failure";
679 // << input.DebugString();
680 if (client->GetLastIPCError() == IPC_TIMEOUT_ERROR) {
681 server_status_ = SERVER_TIMEOUT;
682 } else {
683 // server crash
684 server_status_ = SERVER_SHUTDOWN;
685 }
686 return false;
687 }
688
689 if (!output->ParseFromArray(result_.get(), size)) {
690 LOG(ERROR) << "Parse failure of the result of the request:";
691 // << input.DebugString();
692 server_status_ = SERVER_BROKEN_MESSAGE;
693 return false;
694 }
695
696 DCHECK(server_status_ == SERVER_OK ||
697 server_status_ == SERVER_INVALID_SESSION ||
698 server_status_ == SERVER_SHUTDOWN ||
699 server_status_ == SERVER_UNKNOWN /* during StartServer() */)
700 << " " << server_status_;
701
702 VLOG(2) << "commands::Output: " << std::endl
703 << output->DebugString();
704
705 return true;
706 }
707
StartServer()708 bool Client::StartServer() {
709 if (server_launcher_.get() != NULL) {
710 return server_launcher_->StartServer(this);
711 }
712 return true;
713 }
714
715
OnFatal(ServerLauncherInterface::ServerErrorType type)716 void Client::OnFatal(ServerLauncherInterface::ServerErrorType type) {
717 if (server_launcher_.get() != NULL) {
718 server_launcher_->OnFatal(type);
719 }
720 }
721
InitInput(commands::Input * input) const722 void Client::InitInput(commands::Input *input) const {
723 input->set_id(id_);
724 if (preferences_.get() != NULL) {
725 input->mutable_config()->CopyFrom(*preferences_);
726 }
727 }
728
CheckVersionOrRestartServerInternal(const commands::Input & input,commands::Output * output)729 bool Client::CheckVersionOrRestartServerInternal(
730 const commands::Input &input, commands::Output *output) {
731 for (int trial = 0; trial < 2; ++trial) {
732 const bool call_result = Call(input, output);
733
734 if (!call_result && server_protocol_version_ > IPC_PROTOCOL_VERSION) {
735 LOG(ERROR) << "Server version is newer than client version.";
736 server_status_ = SERVER_VERSION_MISMATCH;
737 return false;
738 }
739
740 const bool version_upgraded =
741 Version::CompareVersion(server_product_version_,
742 Version::GetMozcVersion());
743
744 // if the server version is older than client version or
745 // protocol version is updated, force to reboot the server.
746 // if the version is even unchanged after the reboot, goes to
747 // SERVER_VERSION_MISMATCH state, which brings the client into
748 // SERVER_FATAL state finally.
749 if ((call_result && version_upgraded) ||
750 (!call_result && server_protocol_version_ < IPC_PROTOCOL_VERSION)) {
751 LOG(WARNING) << "Version Mismatch: "
752 << server_product_version_ << " "
753 << Version::GetMozcVersion()
754 << " "
755 << server_protocol_version_ << " "
756 << static_cast<int>(IPC_PROTOCOL_VERSION)
757 << " " << trial;
758 if (trial > 0) {
759 LOG(ERROR) << "Server version mismatch even after server reboot";
760 server_status_ = SERVER_BROKEN_MESSAGE;
761 return false;
762 }
763
764 bool shutdown_result = true;
765 if (call_result && version_upgraded) {
766 // use shutdown command if the protocol version is compatible
767 shutdown_result = Shutdown();
768 if (!shutdown_result) {
769 LOG(ERROR) << "Shutdown command failed";
770 }
771 }
772
773 // force to terminate the process if protocol version is not compatible
774 if (!shutdown_result ||
775 (!call_result && server_protocol_version_ < IPC_PROTOCOL_VERSION)) {
776 if (!server_launcher_->ForceTerminateServer(kServerAddress)) {
777 LOG(ERROR) << "ForceTerminateProcess failed";
778 server_status_ = SERVER_BROKEN_MESSAGE;
779 return false;
780 }
781
782 if (!server_launcher_->WaitServer(server_process_id_)) {
783 LOG(ERROR) << "Cannot terminate server process";
784 }
785 }
786
787 server_status_ = SERVER_UNKNOWN;
788 if (!EnsureConnection()) {
789 server_status_ = SERVER_VERSION_MISMATCH;
790 LOG(ERROR) << "Ensure Connecion failed";
791 return false;
792 }
793
794 continue;
795 }
796
797 if (!call_result) {
798 LOG(ERROR) << "Call() failed";
799 return false;
800 }
801
802 return true;
803 }
804
805 return false;
806 }
807
Reset()808 void Client::Reset() {
809 server_status_ = SERVER_UNKNOWN;
810 server_protocol_version_ = 0;
811 server_process_id_ = 0;
812 }
813
TranslateProtoBufToMozcToolArg(const commands::Output & output,string * mode)814 bool Client::TranslateProtoBufToMozcToolArg(const commands::Output &output,
815 string *mode) {
816 if (!output.has_launch_tool_mode() || mode == NULL) {
817 return false;
818 }
819
820 switch (output.launch_tool_mode()) {
821 case commands::Output::CONFIG_DIALOG:
822 mode->assign("config_dialog");
823 break;
824 case commands::Output::DICTIONARY_TOOL:
825 mode->assign("dictionary_tool");
826 break;
827 case commands::Output::WORD_REGISTER_DIALOG:
828 mode->assign("word_register_dialog");
829 break;
830 case commands::Output::NO_TOOL:
831 default:
832 // do nothing
833 return false;
834 break;
835 }
836
837 return true;
838 }
839
LaunchToolWithProtoBuf(const commands::Output & output)840 bool Client::LaunchToolWithProtoBuf(const commands::Output &output) {
841 string mode;
842 if (!TranslateProtoBufToMozcToolArg(output, &mode)) {
843 return false;
844 }
845
846 // TODO(nona): extends output message to support extra argument.
847 return LaunchTool(mode, "");
848 }
849
LaunchTool(const string & mode,const string & extra_arg)850 bool Client::LaunchTool(const string &mode, const string &extra_arg) {
851 // Don't execute any child process if the parent process is not
852 // in proper runlevel.
853 if (!IsValidRunLevel()) {
854 return false;
855 }
856
857 // Validate |mode|.
858 // TODO(taku): better to validate the parameter more carefully.
859 const size_t kModeMaxSize = 32;
860 if (mode.empty() || mode.size() >= kModeMaxSize) {
861 LOG(ERROR) << "Invalid mode: " << mode;
862 return false;
863 }
864
865 if (mode == "administration_dialog") {
866 #ifdef OS_WIN
867 const string &path = mozc::SystemUtil::GetToolPath();
868 std::wstring wpath;
869 Util::UTF8ToWide(path, &wpath);
870 wpath = L"\"" + wpath + L"\"";
871 // Run administration dialog with UAC.
872 // AFAIK, ShellExecute is only the way to launch process with
873 // UAC protection.
874 // No COM operations are executed as ShellExecute is only
875 // used for launching an UAC-process.
876 //
877 // In Windows XP, cannot use "runas", instead, administration
878 // dialog is launched with normal process with "open"
879 // http://b/2415191
880 return WinUtil::ShellExecuteInSystemDir(
881 L"runas", wpath.c_str(), L"--mode=administration_dialog");
882 #endif // OS_WIN
883
884 return false;
885 }
886
887 #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)\
888 || defined(OS_NACL)
889 string arg = "--mode=" + mode;
890 if (!extra_arg.empty()) {
891 arg += " ";
892 arg += extra_arg;
893 }
894 if (!mozc::Process::SpawnMozcProcess(kMozcTool, arg)) {
895 LOG(ERROR) << "Cannot execute: " << kMozcTool << " " << arg;
896 return false;
897 }
898 #endif // OS_WIN || OS_LINUX || OS_ANDROID || OS_NACL
899
900 // TODO(taku): move MacProcess inside SpawnMozcProcess.
901 // TODO(taku): support extra_arg.
902 #ifdef OS_MACOSX
903 if (!MacProcess::LaunchMozcTool(mode)) {
904 LOG(ERROR) << "Cannot execute: " << mode;
905 return false;
906 }
907 #endif // OS_MACOSX
908
909 return true;
910 }
911
OpenBrowser(const string & url)912 bool Client::OpenBrowser(const string &url) {
913 if (!IsValidRunLevel()) {
914 return false;
915 }
916
917 if (!Process::OpenBrowser(url)) {
918 LOG(ERROR) << "Process::OpenBrowser failed.";
919 return false;
920 }
921
922 return true;
923 }
924
925 namespace {
926 class DefaultClientFactory : public ClientFactoryInterface {
927 public:
NewClient()928 virtual ClientInterface *NewClient() {
929 return new Client;
930 }
931 };
932
933 ClientFactoryInterface *g_client_factory = NULL;
934 } // namespace
935
NewClient()936 ClientInterface *ClientFactory::NewClient() {
937 if (g_client_factory == NULL) {
938 return Singleton<DefaultClientFactory>::get()->NewClient();
939 } else {
940 return g_client_factory->NewClient();
941 }
942 }
943
SetClientFactory(ClientFactoryInterface * client_factory)944 void ClientFactory::SetClientFactory(
945 ClientFactoryInterface *client_factory) {
946 g_client_factory = client_factory;
947 }
948 } // namespace client
949 } // namespace mozc
950