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 // Session Handler of Mozc server.
31 // Migrated from ipc/interpreter and session/session_manager
32 
33 #include "session/session_handler.h"
34 
35 #include <algorithm>
36 #include <string>
37 #include <utility>
38 #include <vector>
39 
40 #include "base/clock.h"
41 #include "base/flags.h"
42 #include "base/logging.h"
43 #include "base/port.h"
44 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
45 #include "base/process.h"
46 #endif  // MOZC_DISABLE_SESSION_WATCHDOG
47 #include "base/singleton.h"
48 #include "base/stopwatch.h"
49 #include "base/util.h"
50 #include "composer/table.h"
51 #include "config/character_form_manager.h"
52 #include "config/config_handler.h"
53 #include "dictionary/user_dictionary_session_handler.h"
54 #include "engine/engine_interface.h"
55 #include "engine/user_data_manager_interface.h"
56 #include "protocol/commands.pb.h"
57 #include "protocol/config.pb.h"
58 #include "protocol/user_dictionary_storage.pb.h"
59 #include "session/generic_storage_manager.h"
60 #include "session/session.h"
61 #include "session/session_observer_handler.h"
62 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
63 #include "session/session_watch_dog.h"
64 #else  // MOZC_DISABLE_SESSION_WATCHDOG
65 // Session watch dog is not aviable from android mozc and nacl mozc for now.
66 // TODO(kkojima): Remove this guard after
67 // enabling session watch dog for android.
68 #endif  // MOZC_DISABLE_SESSION_WATCHDOG
69 #include "usage_stats/usage_stats.h"
70 
71 using mozc::usage_stats::UsageStats;
72 
73 DEFINE_int32(timeout, -1,
74              "server timeout. "
75              "if sessions get empty for \"timeout\", "
76              "shutdown message is automatically emitted");
77 
78 DEFINE_int32(max_session_size, 64,
79              "maximum sessions size. "
80              "if size of sessions reaches to \"max_session_size\", "
81              "oldest session is removed");
82 
83 DEFINE_int32(create_session_min_interval, 0,
84              "minimum interval (sec) for create session");
85 
86 DEFINE_int32(watch_dog_interval, 180,
87              "watch dog timer intaval (sec)");
88 
89 DEFINE_int32(last_command_timeout, 3600,
90              "remove session if it is not accessed for "
91              "\"last_command_timeout\" sec");
92 
93 DEFINE_int32(last_create_session_timeout, 300,
94              "remove session if it is not accessed for "
95              "\"last_create_session_timeout\" sec "
96              "after create session command");
97 
98 DEFINE_bool(restricted, false,
99             "Launch server with restricted setting");
100 
101 namespace mozc {
102 
103 namespace {
IsApplicationAlive(const session::SessionInterface * session)104 bool IsApplicationAlive(const session::SessionInterface *session) {
105 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
106   const commands::ApplicationInfo &info = session->application_info();
107   // When the thread/process's current status is unknown, i.e.,
108   // if IsThreadAlive/IsProcessAlive functions failed to know the
109   // status of the thread/process, return true just in case.
110   // Here, we want to kill the session only when the target thread/process
111   // are terminated with 100% probability. Otherwise, it's better to do
112   // nothing to prevent any side effects.
113 #ifdef OS_WIN
114   if (info.has_thread_id()) {
115     return Process::IsThreadAlive(
116         static_cast<size_t>(info.thread_id()), true);
117   }
118 #else   // OS_WIN
119   if (info.has_process_id()) {
120     return Process::IsProcessAlive(
121         static_cast<size_t>(info.process_id()), true);
122   }
123 #endif  // OS_WIN
124 #else  // MOZC_DISABLE_SESSION_WATCHDOG
125   // Currently the process is not available through android mozc and nacl mozc.
126   // TODO(kkojima): remove this guard after
127   // android version supports base/process.cc
128 #endif  // MOZC_DISABLE_SESSION_WATCHDOG
129   return true;
130 }
131 
IsCarrierEmoji(const string & utf8_str)132 bool IsCarrierEmoji(const string &utf8_str) {
133   if (Util::CharsLen(utf8_str) != 1) {
134     return false;
135   }
136   const char *utf8_begin = utf8_str.c_str();
137   size_t mblen = 0;
138   const uint32 ucs4_val = static_cast<uint32>(
139       Util::UTF8ToUCS4(utf8_begin, utf8_begin + utf8_str.size(), &mblen));
140   const uint32 kMinEmojiPuaCodePoint = 0xFE000;
141   const uint32 kMaxEmojiPuaCodePoint = 0xFEEA0;
142   return kMinEmojiPuaCodePoint <= ucs4_val && ucs4_val <= kMaxEmojiPuaCodePoint;
143 }
144 }  // namespace
145 
SessionHandler(std::unique_ptr<EngineInterface> engine)146 SessionHandler::SessionHandler(std::unique_ptr<EngineInterface> engine) {
147   Init(std::move(engine), std::unique_ptr<EngineBuilderInterface>());
148 }
149 
SessionHandler(std::unique_ptr<EngineInterface> engine,std::unique_ptr<EngineBuilderInterface> engine_builder)150 SessionHandler::SessionHandler(
151     std::unique_ptr<EngineInterface> engine,
152     std::unique_ptr<EngineBuilderInterface> engine_builder) {
153   Init(std::move(engine), std::move(engine_builder));
154 }
155 
Init(std::unique_ptr<EngineInterface> engine,std::unique_ptr<EngineBuilderInterface> engine_builder)156 void SessionHandler::Init(
157     std::unique_ptr<EngineInterface> engine,
158     std::unique_ptr<EngineBuilderInterface> engine_builder) {
159   is_available_ = false;
160   max_session_size_ = 0;
161   last_session_empty_time_ = Clock::GetTime();
162   last_cleanup_time_ = 0;
163   last_create_session_time_ = 0;
164   engine_ = std::move(engine);
165   engine_builder_ = std::move(engine_builder);
166   observer_handler_.reset(new session::SessionObserverHandler());
167   stopwatch_.reset(new Stopwatch);
168   user_dictionary_session_handler_.reset(
169       new user_dictionary::UserDictionarySessionHandler);
170   table_manager_.reset(new composer::TableManager);
171   request_.reset(new commands::Request);
172   config_.reset(new config::Config);
173 
174   if (FLAGS_restricted) {
175     VLOG(1) << "Server starts with restricted mode";
176     // --restricted is almost always specified when mozc_client is inside Job.
177     // The typical case is Startup processes on Vista.
178     // On Vista, StartUp processes are in Job for 60 seconds. In order
179     // to launch new mozc_server inside sandbox, we set the timeout
180     // to be 60sec. Client application hopefully re-launch mozc_server.
181     FLAGS_timeout = 60;
182     FLAGS_max_session_size = 8;
183     FLAGS_watch_dog_interval = 15;
184     FLAGS_last_create_session_timeout = 60;
185     FLAGS_last_command_timeout = 60;
186   }
187 
188 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
189   session_watch_dog_.reset(new SessionWatchDog(FLAGS_watch_dog_interval));
190 #else  // MOZC_DISABLE_SESSION_WATCHDOG
191   // Session watch dog is not aviable from android mozc and nacl mozc for now.
192   // TODO(kkojima): Remove this guard after
193   // enabling session watch dog for android.
194 #endif  // MOZC_DISABLE_SESSION_WATCHDOG
195 
196   config::ConfigHandler::GetConfig(config_.get());
197 
198   // allow [2..128] sessions
199   max_session_size_ = std::max(2, std::min(FLAGS_max_session_size, 128));
200   session_map_.reset(new SessionMap(max_session_size_));
201 
202   if (!engine_) {
203     return;
204   }
205 
206   // everything is OK
207   is_available_ = true;
208 }
209 
~SessionHandler()210 SessionHandler::~SessionHandler() {
211   for (SessionElement *element =
212            const_cast<SessionElement *>(session_map_->Head());
213        element != nullptr; element = element->next) {
214     delete element->value;
215     element->value = nullptr;
216   }
217   session_map_->Clear();
218 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
219   if (session_watch_dog_->IsRunning()) {
220     session_watch_dog_->Terminate();
221   }
222 #else  // MOZC_DISABLE_SESSION_WATCHDOG
223   // Session watch dog is not aviable from android mozc and nacl mozc for now.
224   // TODO(kkojima): Remove this guard after
225   // enabling session watch dog for android.
226 #endif  // MOZC_DISABLE_SESSION_WATCHDOG
227 }
228 
IsAvailable() const229 bool SessionHandler::IsAvailable() const {
230   return is_available_;
231 }
232 
StartWatchDog()233 bool SessionHandler::StartWatchDog() {
234 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
235   if (!session_watch_dog_->IsRunning()) {
236     session_watch_dog_->Start("WatchDog");
237   }
238   return session_watch_dog_->IsRunning();
239 #else  // MOZC_DISABLE_SESSION_WATCHDOG
240   // Session watch dog is not aviable from android mozc and nacl mozc for now.
241   // TODO(kkojima): Remove this guard after
242   // enabling session watch dog for android.
243   return false;
244 #endif  // MOZC_DISABLE_SESSION_WATCHDOG
245 }
246 
SetConfig(const config::Config & config)247 void SessionHandler::SetConfig(const config::Config &config) {
248   *config_ = config;
249   const composer::Table *table = table_manager_->GetTable(
250       *request_, *config_, *engine_->GetDataManager());
251   for (SessionElement *element =
252            const_cast<SessionElement *>(session_map_->Head());
253        element != NULL; element = element->next) {
254     if (element->value != NULL) {
255       element->value->SetConfig(config_.get());
256       element->value->SetRequest(request_.get());
257       element->value->SetTable(table);
258     }
259   }
260   config::CharacterFormManager::GetCharacterFormManager()->ReloadConfig(config);
261 }
262 
SyncData(commands::Command * command)263 bool SessionHandler::SyncData(commands::Command *command) {
264   VLOG(1) << "Syncing user data";
265   engine_->GetUserDataManager()->Sync();
266   return true;
267 }
268 
Shutdown(commands::Command * command)269 bool SessionHandler::Shutdown(commands::Command *command) {
270   VLOG(1) << "Shutdown server";
271   SyncData(command);
272   is_available_ = false;
273   UsageStats::IncrementCount("ShutDown");
274   return true;
275 }
276 
Reload(commands::Command * command)277 bool SessionHandler::Reload(commands::Command *command) {
278   VLOG(1) << "Reloading server";
279   {
280     config::Config config;
281     config::ConfigHandler::GetConfig(&config);
282     SetConfig(config);
283   }
284   engine_->Reload();
285   return true;
286 }
287 
ClearUserHistory(commands::Command * command)288 bool SessionHandler::ClearUserHistory(commands::Command *command) {
289   VLOG(1) << "Clearing user history";
290   engine_->GetUserDataManager()->ClearUserHistory();
291   UsageStats::IncrementCount("ClearUserHistory");
292   return true;
293 }
294 
ClearUserPrediction(commands::Command * command)295 bool SessionHandler::ClearUserPrediction(commands::Command *command) {
296   VLOG(1) << "Clearing user prediction";
297   engine_->GetUserDataManager()->ClearUserPrediction();
298   UsageStats::IncrementCount("ClearUserPrediction");
299   return true;
300 }
301 
ClearUnusedUserPrediction(commands::Command * command)302 bool SessionHandler::ClearUnusedUserPrediction(commands::Command *command) {
303   VLOG(1) << "Clearing unused user prediction";
304   engine_->GetUserDataManager()->ClearUnusedUserPrediction();
305   UsageStats::IncrementCount("ClearUnusedUserPrediction");
306   return true;
307 }
308 
GetStoredConfig(commands::Command * command)309 bool SessionHandler::GetStoredConfig(commands::Command *command) {
310   VLOG(1) << "Getting stored config";
311   // Use GetStoredConfig instead of GetConfig because GET_CONFIG
312   // command should return raw stored config, which is not
313   // affected by imposed config.
314   if (!config::ConfigHandler::GetStoredConfig(
315           command->mutable_output()->mutable_config())) {
316     LOG(WARNING) << "cannot get config";
317     return false;
318   }
319 
320   // Ensure the onmemory config is same as the locally stored one
321   // because the local data could be changed by sync.
322   SetConfig(command->output().config());
323 
324   return true;
325 }
326 
SetStoredConfig(commands::Command * command)327 bool SessionHandler::SetStoredConfig(commands::Command *command) {
328   VLOG(1) << "Setting user config";
329   if (!command->input().has_config()) {
330     LOG(WARNING) << "config is empty";
331     return false;
332   }
333 
334   *command->mutable_output()->mutable_config() = command->input().config();
335   MaybeUpdateStoredConfig(command);
336 
337   UsageStats::IncrementCount("SetConfig");
338   return true;
339 }
340 
SetImposedConfig(commands::Command * command)341 bool SessionHandler::SetImposedConfig(commands::Command *command) {
342   VLOG(1) << "Setting imposed config";
343   if (!command->input().has_config()) {
344     LOG(WARNING) << "config is empty";
345     return false;
346   }
347 
348   const mozc::config::Config &config = command->input().config();
349   config::ConfigHandler::SetImposedConfig(config);
350 
351   Reload(command);
352 
353   return true;
354 }
355 
SetRequest(commands::Command * command)356 bool SessionHandler::SetRequest(commands::Command *command) {
357   VLOG(1) << "Setting client's request";
358   if (!command->input().has_request()) {
359     LOG(WARNING) << "request is empty";
360     return false;
361   }
362 
363   request_->CopyFrom(command->input().request());
364 
365   Reload(command);
366 
367   return true;
368 }
369 
InsertToStorage(commands::Command * command)370 bool SessionHandler::InsertToStorage(commands::Command *command) {
371   VLOG(1) << "Insert to generic storage";
372   if (!command->input().has_storage_entry()) {
373     LOG(WARNING) << "No storage_entry";
374     return false;
375   }
376   const commands::GenericStorageEntry &storage_entry =
377       command->input().storage_entry();
378   if (!storage_entry.has_type() ||
379       !storage_entry.has_key() ||
380       storage_entry.value().size() == 0) {
381     LOG(WARNING) << "storage_entry lacks some fields.";
382     return false;
383   }
384 
385   GenericStorageInterface *storage =
386       GenericStorageManagerFactory::GetStorage(storage_entry.type());
387   if (!storage) {
388     LOG(WARNING) << "No storage found";
389     return false;
390   }
391 
392   for (int i = 0; i < storage_entry.value_size(); ++i) {
393     const string &value = storage_entry.value(i);
394     storage->Insert(value, value.c_str());
395   }
396 
397   if (storage_entry.type() == commands::GenericStorageEntry::EMOJI_HISTORY) {
398     for (int i = 0; i < storage_entry.value_size(); ++i) {
399       if (IsCarrierEmoji(storage_entry.value(i))) {
400         UsageStats::IncrementCount("CommitCarrierEmoji");
401       } else {
402         UsageStats::IncrementCount("CommitUnicodeEmoji");
403       }
404     }
405   }
406 
407   return true;
408 }
409 
ReadAllFromStorage(commands::Command * command)410 bool SessionHandler::ReadAllFromStorage(commands::Command *command) {
411   VLOG(1) << "Read all from storage";
412   commands::Output *output = command->mutable_output();
413   if (!command->input().has_storage_entry()) {
414     LOG(WARNING) << "No storage_entry";
415     return false;
416   }
417   if (!command->input().storage_entry().has_type()) {
418     LOG(WARNING) << "storage_entry lacks type fields.";
419     return false;
420   }
421 
422   commands::GenericStorageEntry::StorageType storage_type =
423     command->input().storage_entry().type();
424   GenericStorageInterface *storage =
425       GenericStorageManagerFactory::GetStorage(storage_type);
426   if (!storage) {
427     LOG(WARNING) << "No storage found";
428     return false;
429   }
430 
431   std::vector<string> result;
432   storage->GetAllValues(&result);
433   output->mutable_storage_entry()->set_type(storage_type);
434   for (size_t i = 0; i < result.size(); ++i) {
435     output->mutable_storage_entry()->add_value(result[i]);
436   }
437   return true;
438 }
439 
ClearStorage(commands::Command * command)440 bool SessionHandler::ClearStorage(commands::Command *command) {
441   VLOG(1) << "Clear storage";
442   commands::Output *output = command->mutable_output();
443   if (!command->input().has_storage_entry()) {
444     LOG(WARNING) << "No storage_entry";
445     return false;
446   }
447   if (!command->input().storage_entry().has_type()) {
448     LOG(WARNING) << "storage_entry lacks type fields.";
449     return false;
450   }
451 
452   commands::GenericStorageEntry::StorageType storage_type =
453     command->input().storage_entry().type();
454   GenericStorageInterface *storage =
455       GenericStorageManagerFactory::GetStorage(storage_type);
456   if (!storage) {
457     LOG(WARNING) << "No storage found";
458     return false;
459   }
460   output->mutable_storage_entry()->set_type(storage_type);
461   return storage->Clear();
462 }
463 
EvalCommand(commands::Command * command)464 bool SessionHandler::EvalCommand(commands::Command *command) {
465   if (!is_available_) {
466     LOG(ERROR) << "SessionHandler is not available.";
467     return false;
468   }
469 
470   bool eval_succeeded = false;
471   stopwatch_->Reset();
472   stopwatch_->Start();
473 
474   switch (command->input().type()) {
475     case commands::Input::CREATE_SESSION:
476       eval_succeeded = CreateSession(command);
477       break;
478     case commands::Input::DELETE_SESSION:
479       eval_succeeded = DeleteSession(command);
480       break;
481     case commands::Input::SEND_KEY:
482       eval_succeeded = SendKey(command);
483       break;
484     case commands::Input::TEST_SEND_KEY:
485       eval_succeeded = TestSendKey(command);
486       break;
487     case commands::Input::SEND_COMMAND:
488       eval_succeeded = SendCommand(command);
489       break;
490     case commands::Input::SYNC_DATA:
491       eval_succeeded = SyncData(command);
492       break;
493     case commands::Input::CLEAR_USER_HISTORY:
494       eval_succeeded = ClearUserHistory(command);
495       break;
496     case commands::Input::CLEAR_USER_PREDICTION:
497       eval_succeeded = ClearUserPrediction(command);
498       break;
499     case commands::Input::CLEAR_UNUSED_USER_PREDICTION:
500       eval_succeeded = ClearUnusedUserPrediction(command);
501       break;
502     case commands::Input::GET_CONFIG:
503       eval_succeeded = GetStoredConfig(command);
504       break;
505     case commands::Input::SET_CONFIG:
506       eval_succeeded = SetStoredConfig(command);
507       break;
508     case commands::Input::SET_IMPOSED_CONFIG:
509       eval_succeeded = SetImposedConfig(command);
510       break;
511     case commands::Input::SET_REQUEST:
512       eval_succeeded = SetRequest(command);
513       break;
514     case commands::Input::SHUTDOWN:
515       eval_succeeded = Shutdown(command);
516       break;
517     case commands::Input::RELOAD:
518       eval_succeeded = Reload(command);
519       break;
520     case commands::Input::CLEANUP:
521       eval_succeeded = Cleanup(command);
522       break;
523     case commands::Input::INSERT_TO_STORAGE:
524       eval_succeeded = InsertToStorage(command);
525       break;
526     case commands::Input::READ_ALL_FROM_STORAGE:
527       eval_succeeded = ReadAllFromStorage(command);
528       break;
529     case commands::Input::CLEAR_STORAGE:
530       eval_succeeded = ClearStorage(command);
531       break;
532     case commands::Input::SEND_USER_DICTIONARY_COMMAND:
533       eval_succeeded = SendUserDictionaryCommand(command);
534       break;
535     case commands::Input::SEND_ENGINE_RELOAD_REQUEST:
536       eval_succeeded = SendEngineReloadRequest(command);
537       break;
538     case commands::Input::NO_OPERATION:
539       eval_succeeded = NoOperation(command);
540       break;
541     default:
542       eval_succeeded = false;
543   }
544 
545   if (eval_succeeded) {
546     UsageStats::IncrementCount("SessionAllEvent");
547     if (command->input().type() != commands::Input::CREATE_SESSION) {
548       // Fill a session ID even if command->input() doesn't have a id to ensure
549       // that response size should not be 0, which causes disconnection of IPC.
550       command->mutable_output()->set_id(command->input().id());
551     }
552   } else {
553     command->mutable_output()->set_id(0);
554     command->mutable_output()->set_error_code(
555         commands::Output::SESSION_FAILURE);
556   }
557 
558   if (eval_succeeded) {
559     // TODO(komatsu): Make sre if checking eval_succeeded is necessary or not.
560     observer_handler_->EvalCommandHandler(*command);
561   }
562 
563   stopwatch_->Stop();
564   UsageStats::UpdateTiming("ElapsedTimeUSec",
565                            stopwatch_->GetElapsedMicroseconds());
566 
567   return is_available_;
568 }
569 
NewSession()570 session::SessionInterface *SessionHandler::NewSession() {
571   // Session doesn't take the ownership of engine.
572   return new session::Session(engine_.get());
573 }
574 
AddObserver(session::SessionObserverInterface * observer)575 void SessionHandler::AddObserver(session::SessionObserverInterface *observer) {
576   observer_handler_->AddObserver(observer);
577 }
578 
MaybeUpdateStoredConfig(commands::Command * command)579 void SessionHandler::MaybeUpdateStoredConfig(commands::Command *command) {
580   if (!command->output().has_config()) {
581     return;
582   }
583 
584   *config_ = command->output().config();
585   config::ConfigHandler::SetConfig(*config_);
586   Reload(command);
587 }
588 
SendKey(commands::Command * command)589 bool SessionHandler::SendKey(commands::Command *command) {
590   const SessionID id = command->input().id();
591   session::SessionInterface **session = session_map_->MutableLookup(id);
592   if (session == NULL || *session == NULL) {
593     LOG(WARNING) << "SessionID " << id << " is not available";
594     return false;
595   }
596   (*session)->SendKey(command);
597   MaybeUpdateStoredConfig(command);
598   return true;
599 }
600 
TestSendKey(commands::Command * command)601 bool SessionHandler::TestSendKey(commands::Command *command) {
602   const SessionID id = command->input().id();
603   session::SessionInterface **session = session_map_->MutableLookup(id);
604   if (session == NULL || *session == NULL) {
605     LOG(WARNING) << "SessionID " << id << " is not available";
606     return false;
607   }
608   (*session)->TestSendKey(command);
609   return true;
610 }
611 
SendCommand(commands::Command * command)612 bool SessionHandler::SendCommand(commands::Command *command) {
613   const SessionID id = command->input().id();
614   session::SessionInterface **session =
615     const_cast<session::SessionInterface **>(session_map_->Lookup(id));
616   if (session == NULL || *session == NULL) {
617     LOG(WARNING) << "SessionID " << id << " is not available";
618     return false;
619   }
620   (*session)->SendCommand(command);
621   MaybeUpdateStoredConfig(command);
622   return true;
623 }
624 
CreateSession(commands::Command * command)625 bool SessionHandler::CreateSession(commands::Command *command) {
626   // prevent DOS attack
627   // don't allow CreateSession in very short period.
628   const int create_session_minimum_interval =
629       std::max(0, std::min(FLAGS_create_session_min_interval, 10));
630 
631   uint64 current_time = Clock::GetTime();
632   if (last_create_session_time_ != 0 &&
633       (current_time - last_create_session_time_) <
634       create_session_minimum_interval) {
635     return false;
636   }
637 
638   last_create_session_time_ = current_time;
639 
640   // if session map is FULL, remove the oldest item from the LRU
641   SessionElement *oldest_element = NULL;
642   if (session_map_->Size() >= max_session_size_) {
643     oldest_element = const_cast<SessionElement *>(session_map_->Tail());
644     if (oldest_element == NULL) {
645       LOG(ERROR) << "oldest SessionElement is NULL";
646       return false;
647     }
648     delete oldest_element->value;
649     oldest_element->value = NULL;
650     session_map_->Erase(oldest_element->key);
651     VLOG(1) << "Session is FULL, oldest SessionID "
652             << oldest_element->key << " is removed";
653   }
654 
655   if (engine_builder_ &&
656       session_map_->Size() == 0 &&
657       engine_builder_->HasResponse()) {
658     auto *response =
659         command->mutable_output()->mutable_engine_reload_response();
660     engine_builder_->GetResponse(response);
661     if (response->status() == EngineReloadResponse::RELOAD_READY) {
662       if (engine_->GetUserDataManager()) {
663         engine_->GetUserDataManager()->Wait();
664       }
665       engine_.reset();
666       engine_ = engine_builder_->BuildFromPreparedData();
667       LOG_IF(FATAL, !engine_) << "Critical failure in engine replace";
668       table_manager_->ClearCaches();
669       response->set_status(EngineReloadResponse::RELOADED);
670     }
671     engine_builder_->Clear();
672   }
673 
674   session::SessionInterface *session = NewSession();
675   if (session == NULL) {
676     LOG(ERROR) << "Cannot allocate new Session";
677     return false;
678   }
679 
680   const SessionID new_id = CreateNewSessionID();
681   SessionElement *element = session_map_->Insert(new_id);
682   element->value = session;
683   command->mutable_output()->set_id(new_id);
684 
685   // The oldes item should be reused
686   DCHECK(oldest_element == NULL || oldest_element == element);
687 
688   if (command->input().has_capability()) {
689     session->set_client_capability(command->input().capability());
690   }
691 
692   if (command->input().has_application_info()) {
693     session->set_application_info(command->input().application_info());
694 #ifdef OS_NACL
695     if (command->input().application_info().has_timezone_offset()) {
696       Clock::SetTimezoneOffset(
697           command->input().application_info().timezone_offset());
698     }
699 #endif  // OS_NACL
700   }
701 
702   // Ensure the onmemory config is same as the locally stored one
703   // because the local data could be changed by sync.
704   {
705     config::Config config;
706     config::ConfigHandler::GetConfig(&config);
707     SetConfig(config);
708   }
709 
710   // session is not empty.
711   last_session_empty_time_ = 0;
712 
713   UsageStats::IncrementCount("SessionCreated");
714 
715   return true;
716 }
717 
DeleteSession(commands::Command * command)718 bool SessionHandler::DeleteSession(commands::Command *command) {
719   DeleteSessionID(command->input().id());
720   if (engine_->GetUserDataManager()) {
721     engine_->GetUserDataManager()->Sync();
722   }
723   return true;
724 }
725 
726 // Scan all sessions and find and delete session which is either
727 // (a) The session is not activated for 60min
728 // (b) The session is created but not accessed for 5min
729 // (c) application is already terminated.
730 // Also, if timeout is enabled, shutdown server if there is
731 // no active session and client doesn't send any conversion
732 // request to the server for FLAGS_timeout sec.
Cleanup(commands::Command * command)733 bool SessionHandler::Cleanup(commands::Command *command) {
734   const uint64 current_time = Clock::GetTime();
735 
736   // suspend/hibernation may happen
737   uint64 suspend_time = 0;
738 #ifndef MOZC_DISABLE_SESSION_WATCHDOG
739   if (last_cleanup_time_ != 0 &&
740       session_watch_dog_->IsRunning() &&
741       (current_time - last_cleanup_time_) >
742       2 * session_watch_dog_->interval()) {
743     suspend_time = current_time - last_cleanup_time_ -
744         session_watch_dog_->interval();
745     LOG(WARNING) << "server went to suspend mode for "
746                  << suspend_time << " sec";
747   }
748 #else  // MOZC_DISABLE_SESSION_WATCHDOG
749   // Session watch dog is not aviable from android mozc and nacl mozc for now.
750   // TODO(kkojima): Remove this guard after
751   // enabling session watch dog for android.
752 #endif  // MOZC_DISABLE_SESSION_WATCHDOG
753 
754   // allow [1..600] sec. default: 300
755   const uint64 create_session_timeout =
756       suspend_time +
757       std::max(1, std::min(FLAGS_last_create_session_timeout, 600));
758 
759   // allow [10..7200] sec. default 3600
760   const uint64 last_command_timeout =
761       suspend_time + std::max(10, std::min(FLAGS_last_command_timeout, 7200));
762 
763   std::vector<SessionID> remove_ids;
764   for (SessionElement *element =
765            const_cast<SessionElement *>(session_map_->Head());
766        element != NULL; element = element->next) {
767     session::SessionInterface *session = element->value;
768     if (!IsApplicationAlive(session)) {
769       VLOG(2) << "Application is not alive. Removing: " << element->key;
770       remove_ids.push_back(element->key);
771     } else if (session->last_command_time() == 0) {
772       // no command is exectuted
773       if ((current_time - session->create_session_time()) >=
774           create_session_timeout) {
775         remove_ids.push_back(element->key);
776       }
777     } else {  // some commands are executed already
778       if ((current_time - session->last_command_time()) >=
779           last_command_timeout) {
780         remove_ids.push_back(element->key);
781       }
782     }
783   }
784 
785   for (size_t i = 0; i < remove_ids.size(); ++i) {
786     DeleteSessionID(remove_ids[i]);
787     VLOG(1) << "Session ID " << remove_ids[i] << " is removed by server";
788   }
789 
790   // Sync all data. This is a regression bug fix http://b/3033708
791   engine_->GetUserDataManager()->Sync();
792 
793   // timeout is enabled.
794   if (FLAGS_timeout > 0 &&
795       last_session_empty_time_ != 0 &&
796       (current_time - last_session_empty_time_)
797       >= suspend_time + FLAGS_timeout) {
798     Shutdown(command);
799   }
800 
801   last_cleanup_time_ = current_time;
802 
803   return true;
804 }
805 
SendUserDictionaryCommand(commands::Command * command)806 bool SessionHandler::SendUserDictionaryCommand(commands::Command *command) {
807   if (!command->input().has_user_dictionary_command()) {
808     return false;
809   }
810   user_dictionary::UserDictionaryCommandStatus status;
811   const bool result = user_dictionary_session_handler_->Evaluate(
812       command->input().user_dictionary_command(), &status);
813   if (result) {
814     status.Swap(
815         command->mutable_output()->mutable_user_dictionary_command_status());
816   }
817   return result;
818 }
819 
SendEngineReloadRequest(commands::Command * command)820 bool SessionHandler::SendEngineReloadRequest(commands::Command *command) {
821   if (!engine_builder_ || !command->input().has_engine_reload_request()) {
822     return false;
823   }
824   engine_builder_->PrepareAsync(
825       command->input().engine_reload_request(),
826       command->mutable_output()->mutable_engine_reload_response());
827   return true;
828 }
829 
NoOperation(commands::Command * command)830 bool SessionHandler::NoOperation(commands::Command *command) {
831   return true;
832 }
833 
834 // Create Random Session ID in order to make the session id unpredicable
CreateNewSessionID()835 SessionID SessionHandler::CreateNewSessionID() {
836   SessionID id = 0;
837   while (true) {
838     Util::GetRandomSequence(reinterpret_cast<char *>(&id), sizeof(id));
839     // don't allow id == 0, as it is reserved for
840     // "invalid id"
841     if (id != 0 && !session_map_->HasKey(id)) {
842       break;
843     }
844 
845     LOG(WARNING) << "Session ID " << id << " is already used. retry";
846   }
847 
848   return id;
849 }
850 
DeleteSessionID(SessionID id)851 bool SessionHandler::DeleteSessionID(SessionID id) {
852   session::SessionInterface **session = session_map_->MutableLookup(id);
853   if (session == NULL || *session == NULL) {
854     LOG_IF(WARNING, id != 0) << "cannot find SessionID " << id;
855     return false;
856   }
857   delete *session;
858 
859   session_map_->Erase(id);   // remove from LRU
860 
861   // if session gets empty, save the timestamp
862   if (last_session_empty_time_ == 0 &&
863       session_map_->Size() == 0) {
864     last_session_empty_time_ = Clock::GetTime();
865   }
866 
867   return true;
868 }
869 }  // namespace mozc
870