b02f2638125251055a79b84e28df9dd250fdd1ee
[platform/upstream/csr-framework.git] / src / framework / service / cs-logic.cpp
1 /*
2  *  Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License
15  */
16 /*
17  * @file        cs-logic.cpp
18  * @author      Kyungwook Tak (k.tak@samsung.com)
19  * @version     1.0
20  * @brief
21  */
22 #include "service/cs-logic.h"
23
24 #include <utility>
25 #include <climits>
26 #include <cerrno>
27 #include <unistd.h>
28
29 #include <csr-error.h>
30
31 #include "common/audit/logger.h"
32 #include "common/exception.h"
33 #include "service/type-converter.h"
34 #include "service/core-usage.h"
35 #include "service/dir-blacklist.h"
36 #include "service/fs-utils.h"
37 #include "ui/askuser.h"
38
39 namespace Csr {
40
41 namespace {
42
43 void setCoreUsage(const csr_cs_core_usage_e &cu)
44 {
45         switch (cu) {
46         case CSR_CS_CORE_USAGE_HALF:
47         case CSR_CS_CORE_USAGE_SINGLE:
48                 CpuUsageManager::set(cu);
49                 break;
50
51         default:
52                 break;
53         }
54 }
55
56 // resolve all of "/." and "/.." in absolute path (which starts with '/')
57 std::string resolvePath(const std::string &_path)
58 {
59         auto path = _path;
60
61         while (path.back() == '/')
62                 path.pop_back();
63
64         size_t from = 0;
65         size_t to = 0;
66
67         while (true) {
68                 if (path.empty()) {
69                         path = "/";
70                         break;
71                 }
72
73                 auto len = path.length();
74
75                 if (from >= len)
76                         break;
77
78                 while (len > from + 1 && path[from + 1] == '/') {
79                         path.erase(from, 1);
80                         --len;
81                 }
82
83                 to = path.find_first_of('/', from + 1);
84
85                 if (to == std::string::npos)
86                         to = len;
87
88                 auto substr = path.substr(from, to - from);
89
90                 if (substr == "/.") {
91                         path.erase(from, 2);
92                 } else if (substr == "/..") {
93                         path.erase(from, 3);
94
95                         if (from == 0)
96                                 continue;
97
98                         auto parent = path.find_last_of('/', from - 1);
99                         path.erase(parent, from - parent);
100
101                         from = parent;
102                 } else {
103                         from = to;
104                 }
105         }
106
107         return path;
108 }
109
110 std::string canonicalizePath(const std::string &path, bool checkAccess)
111 {
112         auto resolved = resolvePath(path);
113
114         auto target = File::getPkgPath(resolved);
115
116         if (checkAccess && !isReadable(path)) {
117                 const int err = errno;
118                 if (err == ENOENT)
119                         ThrowExcWarn(CSR_ERROR_FILE_DO_NOT_EXIST, "File do not exist: " << target);
120                 else if (err == EACCES)
121                         ThrowExc(CSR_ERROR_PERMISSION_DENIED,
122                                          "Perm denied to get real path: " << target);
123                 else
124                         ThrowExc(CSR_ERROR_FILE_SYSTEM, "Failed to get real path: " << target <<
125                                          " with errno: " << err);
126         }
127
128         return target;
129 }
130
131 FilePtr canonicalizePathWithFile(const std::string &path)
132 {
133         auto resolved = resolvePath(path);
134
135         auto fileptr = File::create(path, nullptr);
136
137         if (!isReadable(fileptr->getName()))
138                 ThrowExcWarn(CSR_ERROR_FILE_DO_NOT_EXIST, "File is not readable: " << fileptr->getName());
139
140         return fileptr;
141 }
142
143 } // namespace anonymous
144
145 CsLogic::CsLogic(const std::shared_ptr<CsLoader> &loader,
146                                  const std::shared_ptr<Db::Manager> &db) :
147         m_loader(loader), m_db(db)
148 {
149         if (!this->m_db)
150                 ThrowExc(CSR_ERROR_DB, "Failed to init db");
151
152         if (this->m_loader) {
153                 CsEngineContext csEngineContext(this->m_loader);
154                 this->m_loader->getEngineDataVersion(csEngineContext.get(), this->m_dataVersion);
155                 this->m_db->deleteDetectedDeprecated(this->m_loader->getEngineLatestUpdateTime(csEngineContext.get()));
156         }
157 }
158
159 RawBuffer CsLogic::scanData(const CsContext &context, const RawBuffer &data)
160 {
161         if (this->m_db->getEngineState(CSR_ENGINE_CS) != CSR_STATE_ENABLE)
162                 ThrowExc(CSR_ERROR_ENGINE_DISABLED, "engine is disabled");
163
164         setCoreUsage(context.coreUsage);
165
166         CsEngineContext engineContext(this->m_loader);
167         auto &c = engineContext.get();
168
169         csre_cs_detected_h result = nullptr;
170
171         auto timestamp = ::time(nullptr);
172
173         this->m_loader->scanData(c, data, &result);
174
175         // detected handle is null if it's safe
176         if (result == nullptr)
177                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
178
179         auto d = this->convert(result, std::string(), timestamp);
180
181         return this->handleAskUser(context, d);
182 }
183
184 RawBuffer CsLogic::scanAppOnCloud(const CsContext &context,
185                                                                   const std::string &pkgPath,
186                                                                   const std::string &pkgId)
187 {
188         CsEngineContext engineContext(this->m_loader);
189         auto &c = engineContext.get();
190
191         auto timestamp = ::time(nullptr);
192
193         csre_cs_detected_h result = nullptr;
194         this->m_loader->scanAppOnCloud(c, pkgPath, &result);
195
196         if (!result)
197                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
198
199         auto detected = this->convert(result, pkgPath, timestamp);
200         detected.isApp = true;
201         detected.pkgId = pkgId;
202
203         this->m_db->insertDetectedAppByCloud(pkgPath, pkgId, detected, this->m_dataVersion);
204
205         return this->handleAskUser(context, detected);
206 }
207
208 CsDetectedPtr CsLogic::scanAppDelta(const std::string &pkgPath, const std::string &pkgId,
209                                                                         std::string &riskiestPath)
210 {
211         auto starttime = ::time(nullptr);
212
213         CsEngineContext engineContext(this->m_loader);
214         auto &c = engineContext.get();
215
216         auto t = this->m_loader->getEngineLatestUpdateTime(c);
217         auto lastScanTime = this->m_db->getLastScanTime(pkgPath, t);
218
219         CsDetectedPtr riskiest;
220         // traverse files in app and take which is more danger than riskiest
221         auto visitor = FsVisitor::create([&](const FilePtr &file) {
222                 DEBUG("Scan file by engine: " << file->getPath());
223
224                 auto timestamp = ::time(nullptr);
225
226                 csre_cs_detected_h result = nullptr;
227                 this->m_loader->scanFile(c, file->getPath(), &result);
228
229                 if (!result) {
230                         if (lastScanTime != -1)
231                                 this->m_db->deleteDetectedByFilepathOnPath(file->getPath());
232
233                         return;
234                 }
235
236                 INFO("New malware detected on file: " << file->getPath());
237
238                 auto candidate = this->convert(result, pkgPath, timestamp);
239                 candidate.isApp = true;
240                 candidate.pkgId = pkgId;
241
242                 this->m_db->insertDetectedFileInApp(pkgPath, file->getPath(), candidate,
243                                                                                         this->m_dataVersion);
244
245                 if (!riskiest) {
246                         riskiest.reset(new CsDetected(std::move(candidate)));
247                         riskiestPath = file->getPath();
248                 } else if (*riskiest < candidate) {
249                         *riskiest = std::move(candidate);
250                         riskiestPath = file->getPath();
251                 }
252         }, pkgPath, false, lastScanTime);
253
254         visitor->run();
255
256         this->m_db->insertLastScanTime(pkgPath, this->m_dataVersion, starttime);
257
258         return riskiest;
259 }
260
261 RawBuffer CsLogic::scanApp(const CsContext &context, const FilePtr &pkgPtr)
262 {
263         const auto &pkgPath = pkgPtr->getName();
264         const auto &pkgId = pkgPtr->getAppPkgId();
265
266         if (context.isScanOnCloud && this->m_loader->scanAppOnCloudSupported())
267                 return this->scanAppOnCloud(context, pkgPath, pkgId);
268
269         CsEngineContext engineContext(this->m_loader);
270         auto since = this->m_loader->getEngineLatestUpdateTime(engineContext.get());
271
272         // old history
273         auto history = this->m_db->getWorstByPkgPath(pkgPath, since);
274         // riskiest detected among newly scanned files
275         std::string riskiestPath;
276         auto riskiest = this->scanAppDelta(pkgPath, pkgId, riskiestPath);
277         // history after delta scan. if worst file is changed, it's rescanned in scanAppDelta
278         // and deleted from db if it's cured. if history != nullptr && after == nullptr,
279         // it means worst detected item is cured anyway.
280         auto after = this->m_db->getWorstByPkgPath(pkgPath, since);
281         if (history && after && riskiest) {
282                 if (*history < *riskiest) {
283                         INFO("worst case is remained but the more worst newly detected. on pkg[" <<
284                                  pkgPath << "]");
285                         if (history->isIgnored)
286                                 this->m_db->updateIgnoreFlag(pkgPath, false);
287
288                         this->m_db->insertWorst(pkgId, pkgPath, riskiestPath);
289
290                         return this->handleAskUser(context, *riskiest);
291                 } else {
292                         INFO("worst case is remained and can be re-used on pkg[" << pkgPath << "]");
293                         if (history->isIgnored)
294                                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
295
296                         return this->handleAskUser(context, *history);
297                 }
298         } else if (history && after && !riskiest) {
299                 INFO("worst case is remained and NO new detected. history can be re-used. "
300                          "on pkg[" << pkgPath << "]");
301                 if (history->isIgnored)
302                         return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
303
304                 return this->handleAskUser(context, *history);
305         } else if (history && !after && riskiest) {
306                 INFO("worst case is deleted but new detected. we have to find out "
307                          "worse case in db and compare it with riskiest first. on pkg[" << pkgPath <<
308                          "]");
309                 Db::RowShPtr worse;
310                 since = this->m_loader->getEngineLatestUpdateTime(engineContext.get());
311                 for (auto &row : this->m_db->getDetectedByFilepathOnDir(pkgPath, since))
312                         if (!worse || *worse < *row)
313                                 worse = std::move(row);
314
315                 if (!worse) {
316                         INFO("No detected malware found in db.... Newly detected malware is removed by "
317                                  "other client. Handle it as fully clean case.");
318                         return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
319                 }
320
321                 if (*riskiest < *worse) {
322                         INFO("worse case in db is worse than riskiest. on pkg[" << pkgPath << "]");
323                         riskiestPath = worse->fileInAppPath;
324                         *riskiest = std::move(*worse);
325                 }
326
327                 if (*history < *riskiest) {
328                         INFO("worst case is deleted but the more worst newly detected. on pkg[" <<
329                                  pkgPath << "]");
330                         if (history->isIgnored)
331                                 this->m_db->updateIgnoreFlag(pkgPath, false);
332
333                         this->m_db->insertWorst(pkgId, pkgPath, riskiestPath);
334
335                         return this->handleAskUser(context, *riskiest);
336                 } else {
337                         INFO("worst case is deleted but same or less level newly detected. on pkg[" <<
338                                  pkgPath << "]");
339                         this->m_db->insertWorst(pkgId, pkgPath, riskiestPath);
340
341                         if (history->isIgnored)
342                                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
343
344                         return this->handleAskUser(context, *riskiest);
345                 }
346         } else if (history && !after && !riskiest) {
347                 since = this->m_loader->getEngineLatestUpdateTime(engineContext.get());
348                 auto rows = this->m_db->getDetectedByFilepathOnDir(pkgPath, since);
349
350                 if (!rows.empty()) {
351                         INFO("worst case is deleted cascadingly and NO new detected and "
352                                  "worse case exist on pkg[" << pkgPath << "]. insert it to worst.");
353                         Db::RowShPtr worse;
354                         for (auto &row : rows)
355                                 if (!worse || *worse < *row)
356                                         worse = std::move(row);
357
358                         if (worse) {
359                                 this->m_db->insertWorst(pkgId, pkgPath, worse->fileInAppPath);
360
361                                 if (worse->isIgnored)
362                                         return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
363
364                                 return this->handleAskUser(context, *worse);
365                         }
366                 }
367
368                 INFO("worst case is deleted cascadingly and NO new detected and "
369                          "NO worse case. the pkg[" << pkgPath << "] is clean.");
370
371                 this->m_db->deleteDetectedByNameOnPath(pkgPath);
372                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
373         } else if (!history && riskiest) {
374                 INFO("no history and new detected");
375                 this->m_db->insertWorst(pkgId, pkgPath, riskiestPath);
376
377                 return this->handleAskUser(context, *riskiest);
378         } else {
379                 DEBUG("no history and no new detected");
380                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
381         }
382 }
383
384 RawBuffer CsLogic::scanFileWithoutDelta(const CsContext &context,
385                                                                                 const std::string &filepath, FilePtr &&fileptr)
386 {
387         if (isInBlackList(filepath))
388                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
389
390         CsEngineContext engineContext(this->m_loader);
391         auto &c = engineContext.get();
392
393         auto timestamp = ::time(nullptr);
394
395         csre_cs_detected_h result = nullptr;
396         this->m_loader->scanFile(c, filepath, &result);
397
398         // detected handle is null if it's safe
399         if (result == nullptr)
400                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
401
402         INFO("New malware detected on file: " << filepath);
403
404         auto d = this->convert(result, filepath, timestamp);
405
406         this->m_db->insertDetectedFile(d.targetName, d, this->m_dataVersion);
407
408         return this->handleAskUser(context, d, std::move(fileptr));
409 }
410
411 RawBuffer CsLogic::scanFile(const CsContext &context, const std::string &filepath)
412 {
413         if (this->m_db->getEngineState(CSR_ENGINE_CS) != CSR_STATE_ENABLE)
414                 ThrowExc(CSR_ERROR_ENGINE_DISABLED, "engine is disabled");
415
416         setCoreUsage(context.coreUsage);
417
418         auto target = canonicalizePathWithFile(filepath);
419
420         if (target->isInApp())
421                 return this->scanApp(context, target);
422
423         const auto &name = target->getName();
424
425         if (target->isDir())
426                 ThrowExc(CSR_ERROR_FILE_SYSTEM, "file type shouldn't be directory: " << name);
427
428         DEBUG("Scan request on file: " << name);
429
430         CsEngineContext engineContext(this->m_loader);
431         auto since = this->m_loader->getEngineLatestUpdateTime(engineContext.get());
432         auto history = this->m_db->getDetectedAllByNameOnPath(name, since);
433
434         if (history == nullptr) {
435                 DEBUG("No history exist on target. Newly scan needed: " << name);
436                 return this->scanFileWithoutDelta(context, name, std::move(target));
437         } else if (target->isModifiedSince(history->ts)) {
438                 DEBUG("file[" << name << "] is modified since the detected time. "
439                           "let's remove history and re-scan");
440                 this->m_db->deleteDetectedByNameOnPath(name);
441                 return this->scanFileWithoutDelta(context, name, std::move(target));
442         }
443
444         DEBUG("Usable scan history exist on file: " << name);
445
446         if (history->isIgnored)
447                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
448
449         return this->handleAskUser(context, *history);
450 }
451
452 // Application in input param directory will be treated as one item.
453 // Application base directory path is inserted to file set.
454 // e.g., input param dir : "/opt/usr" (applications in "/opt/usr/apps")
455 //       ls /opt/usr/ :
456 //           /opt/usr/file-not-in-app1
457 //           /opt/usr/file-not-in-app2
458 //           /opt/usr/apps/org.tizen.tutorial
459 //           /opt/usr/apps/org.tizen.tutorial/file-in-app1
460 //           /opt/usr/apps/org.tizen.tutorial/file-in-app2
461 //           /opt/usr/apps/org.tizen.message/file-in-app1
462 //           /opt/usr/apps/org.tizen.message/file-in-app2
463 //           /opt/usr/apps/org.tizen.flash/file-in-app1
464 //           /opt/usr/apps/org.tizen.flash/file-in-app2
465 //
466 //           and detected history exist on...
467 //           /opt/usr/apps/org.tizen.message/file-in-app2
468 //           /opt/usr/apps/org.tizen.flash (If target name is app base directory path,
469 //                                          it's detected by scan on cloud)
470 //
471 //       output scannable file set will be:
472 //           1) /opt/usr/file-not-in-app1
473 //           2) /opt/usr/file-not-in-app2
474 //           3) /opt/usr/apps/org.tizen.tutorial (app base directory path)
475 //           4) /opt/usr/apps/org.tizen.message  (app base directory path)
476 //           5) /opt/usr/apps/org.tizen.flash    (app base directory path)
477 //           % items which has detected history is included in list as well.
478 RawBuffer CsLogic::getScannableFiles(const std::string &dir, const std::function<void()> &isCancelled)
479 {
480         if (this->m_db->getEngineState(CSR_ENGINE_CS) != CSR_STATE_ENABLE)
481                 ThrowExc(CSR_ERROR_ENGINE_DISABLED, "engine is disabled");
482
483         auto targetdir = canonicalizePath(dir, true);
484
485         CsEngineContext csEngineContext(this->m_loader);
486         auto since = this->m_loader->getEngineLatestUpdateTime(csEngineContext.get());
487
488         auto lastScanTime = this->m_db->getLastScanTime(targetdir, since);
489
490         StrSet fileset;
491
492         auto visitor = FsVisitor::create([&](const FilePtr &file) {
493                 isCancelled();
494
495                 DEBUG("Scannable item: " << file->getName());
496                 fileset.insert(file->getName());
497         }, targetdir, true, lastScanTime);
498
499         visitor->run();
500
501         isCancelled();
502
503         if (lastScanTime != -1) {
504                 // for case: scan history exist and not modified.
505                 for (auto &row : this->m_db->getDetectedAllByNameOnDir(targetdir, since)) {
506                         isCancelled();
507
508                         try {
509                                 auto fileptr = File::create(row->targetName, nullptr);
510
511                                 fileset.insert(fileptr->getName());
512                         } catch (const Exception &e) {
513                                 if (e.error() == CSR_ERROR_FILE_DO_NOT_EXIST ||
514                                         e.error() == CSR_ERROR_FILE_SYSTEM)
515                                         this->m_db->deleteDetectedByNameOnPath(row->targetName);
516                                 else
517                                         throw;
518                         }
519                 }
520         }
521
522         return BinaryQueue::Serialize(CSR_ERROR_NONE, fileset).pop();
523 }
524
525 RawBuffer CsLogic::canonicalizePaths(const StrSet &paths)
526 {
527         if (this->m_db->getEngineState(CSR_ENGINE_CS) != CSR_STATE_ENABLE)
528                 ThrowExc(CSR_ERROR_ENGINE_DISABLED, "engine is disabled");
529
530         StrSet canonicalized;
531
532         for (const auto &path : paths) {
533                 auto target = canonicalizePath(path, true);
534
535                 if (canonicalized.find(target) == canonicalized.end()) {
536                         INFO("Insert to canonicalized list: " << target);
537                         canonicalized.emplace(std::move(target));
538                 }
539         }
540
541         return BinaryQueue::Serialize(CSR_ERROR_NONE, canonicalized).pop();
542 }
543
544 RawBuffer CsLogic::setDirTimestamp(const std::string &dir, time_t ts)
545 {
546         this->m_db->insertLastScanTime(dir, this->m_dataVersion, ts);
547
548         return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
549 }
550
551 RawBuffer CsLogic::judgeStatus(const std::string &filepath, csr_cs_action_e action)
552 {
553         // for file existence / status check. exception thrown.
554         FilePtr file;
555         try {
556                 file = File::create(filepath, nullptr);
557         } catch (const Exception &e) {
558                 ERROR("file system related exception occured on file: " << filepath <<
559                           " This case might be file not exist or type invalid,"
560                           " file has changed anyway... Don't refresh detected history to know that"
561                           " it's changed since the time.");
562
563                 throw;
564         }
565
566         const auto &targetName = file->getName();
567
568         CsEngineContext csEngineContext(this->m_loader);
569         auto since = this->m_loader->getEngineLatestUpdateTime(csEngineContext.get());
570
571         bool isCloudHistory = false;
572         auto history = this->m_db->getDetectedAllByNameOnPath(targetName, since, &isCloudHistory);
573
574         if (!history) {
575                 ERROR("Target to be judged doesn't exist in db. name: " << targetName);
576                 return BinaryQueue::Serialize(CSR_ERROR_INVALID_PARAMETER).pop();
577         }
578
579         // file create based on fileInAppPath(for app target, it is worst detected)
580         if (!isCloudHistory && file->isModifiedSince(history->ts))
581                 ThrowExc(CSR_ERROR_FILE_CHANGED,
582                                  "File[" << history->fileInAppPath << "] modified since db delta inserted."
583                                  " Don't refresh detected history to know that it's changed since the"
584                                  " time.");
585
586         switch (action) {
587         case CSR_CS_ACTION_REMOVE:
588                 file->remove();
589
590                 this->m_db->deleteDetectedByNameOnPath(targetName);
591                 break;
592
593         case CSR_CS_ACTION_IGNORE:
594                 this->m_db->updateIgnoreFlag(targetName, true);
595                 break;
596
597         case CSR_CS_ACTION_UNIGNORE:
598                 this->m_db->updateIgnoreFlag(targetName, false);
599                 break;
600
601         default:
602                 ThrowExc(CSR_ERROR_SERVER, "Invalid acation enum val: " <<
603                                  static_cast<csr_cs_action_e>(action));
604         }
605
606         return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
607 }
608
609 RawBuffer CsLogic::getDetected(const std::string &filepath)
610 {
611         std::string target;
612         try {
613                 target = canonicalizePath(filepath, true);
614         } catch (const Exception &e) {
615                 WARN("Ignore exceptions on file canonicalize/existence check for getting"
616                          " history e.g., detected/ignored. filepath: " << filepath);
617                 this->m_db->deleteDetectedByNameOnPath(canonicalizePath(filepath, false));
618                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
619         }
620
621         CsEngineContext csEngineContext(this->m_loader);
622         auto row = this->m_db->getDetectedAllByNameOnPath(target,
623                         this->m_loader->getEngineLatestUpdateTime(csEngineContext.get()));
624
625         if (row && !row->isIgnored)
626                 return BinaryQueue::Serialize(CSR_ERROR_NONE, row).pop();
627         else
628                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
629 }
630
631 RawBuffer CsLogic::getDetectedList(const StrSet &_dirSet)
632 {
633         StrSet dirSet;
634         for (const auto &dir : _dirSet)
635                 dirSet.emplace(canonicalizePath(dir, false));
636
637         CsEngineContext csEngineContext(this->m_loader);
638         auto since = this->m_loader->getEngineLatestUpdateTime(csEngineContext.get());
639         Db::RowShPtrs rows;
640         for (const auto &dir : dirSet) {
641                 for (auto &row : this->m_db->getDetectedAllByNameOnDir(dir, since)) {
642                         if (!row->fileInAppPath.empty() && !isReadable(row->targetName)) {
643                                 WARN("Exclude not-accessable malware detected file from the list: " <<
644                                          row->targetName);
645                                 this->m_db->deleteDetectedByNameOnPath(row->targetName);
646                         } else if (!row->isIgnored) {
647                                 rows.emplace_back(std::move(row));
648                         }
649                 }
650         }
651
652         if (rows.empty())
653                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
654         else
655                 return BinaryQueue::Serialize(CSR_ERROR_NONE, rows).pop();
656 }
657
658 RawBuffer CsLogic::getIgnored(const std::string &filepath)
659 {
660         std::string target;
661         try {
662                 target = canonicalizePath(filepath, true);
663         } catch (const Exception &e) {
664                 WARN("Ignore exceptions on file canonicalize/existence check for getting"
665                          " history e.g., detected/ignored. filepath: " << filepath);
666                 this->m_db->deleteDetectedByNameOnPath(canonicalizePath(filepath, false));
667                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
668         }
669
670         CsEngineContext csEngineContext(this->m_loader);
671         auto row = this->m_db->getDetectedAllByNameOnPath(target,
672                         this->m_loader->getEngineLatestUpdateTime(csEngineContext.get()));
673
674         if (row && row->isIgnored)
675                 return BinaryQueue::Serialize(CSR_ERROR_NONE, row).pop();
676         else
677                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
678 }
679
680 RawBuffer CsLogic::getIgnoredList(const StrSet &_dirSet)
681 {
682         StrSet dirSet;
683         for (const auto &dir : _dirSet)
684                 dirSet.emplace(canonicalizePath(dir, false));
685
686         CsEngineContext csEngineContext(this->m_loader);
687         auto since = this->m_loader->getEngineLatestUpdateTime(csEngineContext.get());
688         Db::RowShPtrs rows;
689         for (const auto &dir : dirSet) {
690                 for (auto &row : this->m_db->getDetectedAllByNameOnDir(dir, since)) {
691                         if (!row->fileInAppPath.empty() && !isReadable(row->targetName)) {
692                                 WARN("Exclude not-accessable malware detected file from the list: " <<
693                                          row->targetName);
694                                 this->m_db->deleteDetectedByNameOnPath(row->targetName);
695                         } else if (row->isIgnored) {
696                                 rows.emplace_back(std::move(row));
697                         }
698                 }
699         }
700
701         if (rows.empty())
702                 return BinaryQueue::Serialize(CSR_ERROR_NONE).pop();
703         else
704                 return BinaryQueue::Serialize(CSR_ERROR_NONE, rows).pop();
705 }
706
707 RawBuffer CsLogic::handleAskUser(const CsContext &c, CsDetected &d, FilePtr &&fileptr)
708 {
709         if (c.askUser == CSR_CS_ASK_USER_NO) {
710                 d.response = CSR_CS_USER_RESPONSE_USER_NOT_ASKED;
711                 return BinaryQueue::Serialize(CSR_ERROR_NONE, d).pop();
712         }
713
714         Ui::CommandId cid;
715
716         switch (d.severity) {
717         case CSR_CS_SEVERITY_LOW:
718         case CSR_CS_SEVERITY_MEDIUM:
719                 if (d.targetName.empty())
720                         cid = Ui::CommandId::CS_PROMPT_DATA;
721                 else if (d.isApp)
722                         cid = Ui::CommandId::CS_PROMPT_APP;
723                 else
724                         cid = Ui::CommandId::CS_PROMPT_FILE;
725
726                 break;
727
728         case CSR_CS_SEVERITY_HIGH:
729                 if (d.targetName.empty())
730                         cid = Ui::CommandId::CS_NOTIFY_DATA;
731                 else if (d.isApp)
732                         cid = Ui::CommandId::CS_NOTIFY_APP;
733                 else
734                         cid = Ui::CommandId::CS_NOTIFY_FILE;
735
736                 break;
737
738         default:
739                 ThrowExc(CSR_ERROR_SERVER, "Invalid severity: " << static_cast<int>(d.severity));
740         }
741
742         Ui::AskUser askUser;
743         auto r = askUser.cs(cid, c.popupMessage, d);
744         if (r == -1) {
745                 ERROR("Failed to get user response by popup service for target: " << d.targetName);
746                 return BinaryQueue::Serialize(CSR_ERROR_USER_RESPONSE_FAILED, d).pop();
747         }
748
749         d.response = r;
750
751         if (d.response == CSR_CS_USER_RESPONSE_REMOVE && !d.targetName.empty()) {
752                 try {
753                         FilePtr _fileptr;
754                         if (fileptr)
755                                 _fileptr = std::move(fileptr);
756                         else
757                                 _fileptr = File::create(d.targetName, nullptr);
758
759                         _fileptr->remove();
760                 } catch (const Exception &e) {
761                         if (e.error() == CSR_ERROR_FILE_DO_NOT_EXIST)
762                                 WARN("File already removed.: " << d.targetName);
763                         else if (e.error() == CSR_ERROR_FILE_SYSTEM)
764                                 WARN("File type is changed, considered as different file: " <<
765                                          d.targetName);
766                         else if (e.error() == CSR_ERROR_REMOVE_FAILED)
767                                 return BinaryQueue::Serialize(CSR_ERROR_REMOVE_FAILED, d).pop();
768                         else
769                                 throw;
770                 }
771
772                 this->m_db->deleteDetectedByNameOnPath(d.targetName);
773         }
774
775         return BinaryQueue::Serialize(CSR_ERROR_NONE, d).pop();
776 }
777
778 CsDetected CsLogic::convert(csre_cs_detected_h &result, const std::string &targetName,
779                                                         time_t timestamp)
780 {
781         DEBUG("convert engine result handle to CsDetected start");
782
783         CsDetected d;
784
785         d.targetName = targetName;
786
787         csre_cs_severity_level_e eseverity = CSRE_CS_SEVERITY_LOW;
788
789         this->m_loader->getSeverity(result, &eseverity);
790         this->m_loader->getMalwareName(result, d.malwareName);
791         this->m_loader->getDetailedUrl(result, d.detailedUrl);
792
793         d.ts = timestamp;
794         d.severity = Csr::convert(eseverity);
795
796         return d;
797 }
798
799 }