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