Merge branch 'sandbox/kwon/transaction' into devel
[platform/upstream/csr-framework.git] / src / framework / db / manager.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        manager.cpp
18  * @author      Dongsun Lee (ds73.lee@samsung.com)
19  * @version     1.0
20  * @brief       DB manager to maintain scanning history
21  */
22 #include "db/manager.h"
23
24 #include <fstream>
25 #include <iostream>
26
27 #include "db/query.h"
28 #include "db/statement.h"
29 #include "common/audit/logger.h"
30 #include "common/exception.h"
31
32 namespace Csr {
33 namespace Db {
34
35 namespace {
36
37 enum SchemaVersion : int {
38         NOT_EXIST = 0,
39         _1        = 1,
40         LATEST    = _1
41 };
42
43 const std::string SCRIPT_CREATE_SCHEMA  = "create_schema";
44 const std::string SCRIPT_DROP_ALL_ITEMS = "drop_all";
45 const std::string SCRIPT_MIGRATE        = "migrate_";
46
47 const std::string DB_VERSION_STR    = "DB_VERSION";
48 const std::string SCHEMA_INFO_TABLE = "SCHEMA_INFO";
49
50 RowShPtr extractRow(Statement &stmt)
51 {
52         RowShPtr row = std::make_shared<Row>();
53
54         row->targetName = stmt.getText(); // name.
55         row->fileInAppPath = stmt.getText(); // file_path
56         row->dataVersion = stmt.getText(); // data_version
57         row->malwareName = stmt.getText(); // malware_name
58         row->detailedUrl = stmt.getText(); // detailed_url
59         row->severity = static_cast<csr_cs_severity_level_e>(stmt.getInt()); // severity
60         row->ts = static_cast<time_t>(stmt.getInt64()); // detected_time
61         row->pkgId = stmt.getText(); // pkg_id
62         row->isApp = !row->pkgId.empty();
63         row->isIgnored = static_cast<bool>(stmt.getInt());
64
65         return row;
66 }
67
68 RowShPtr extractRowCloud(Statement &stmt)
69 {
70         RowShPtr row = std::make_shared<Row>();
71
72         row->targetName = stmt.getText(); // name.
73         row->fileInAppPath.clear();
74         row->dataVersion = stmt.getText(); // data_version
75         row->malwareName = stmt.getText(); // malware_name
76         row->detailedUrl = stmt.getText(); // detailed_url
77         row->severity = static_cast<csr_cs_severity_level_e>(stmt.getInt()); // severity
78         row->ts = static_cast<time_t>(stmt.getInt64()); // detected_time
79         row->pkgId = stmt.getText(); // pkg_id
80         row->isApp = true;
81         row->isIgnored = static_cast<bool>(stmt.getInt());
82
83         return row;
84 }
85
86 } // namespace anonymous
87
88 Manager::Manager(const std::string &dbfile, const std::string &scriptsDir) :
89         m_conn(dbfile, Connection::Create | Connection::ReadWrite |
90                    Connection::Serialized),
91         m_scriptsDir(scriptsDir)
92 {
93         this->m_conn.exec("PRAGMA foreign_keys = ON;");
94
95         // run migration if old database is present
96         auto sv = this->getSchemaVersion();
97
98         switch (sv) {
99         case SchemaVersion::LATEST:
100                 DEBUG("Database version is latest");
101                 break;
102
103         case SchemaVersion::NOT_EXIST:
104                 INFO("Database initializing!");
105                 this->resetDatabase();
106                 break;
107
108         default:
109                 if (sv < SchemaVersion::NOT_EXIST || sv > SchemaVersion::LATEST) {
110                         ERROR("Database corrupted! invalid db version returned! : " << sv);
111                         this->resetDatabase();
112                 } else {
113                         INFO("Database migration! from[" << sv <<
114                                  "] to[" << SchemaVersion::LATEST << "]");
115
116                         for (int vi = sv; vi < SchemaVersion::LATEST; ++vi)
117                                 this->m_conn.exec(this->getMigrationScript(vi).c_str());
118
119                         this->setSchemaVersion(SchemaVersion::LATEST);
120                 }
121                 break;
122         }
123
124         this->m_conn.exec("VACUUM;");
125 }
126
127 Manager::~Manager()
128 {
129 }
130
131 void Manager::resetDatabase()
132 {
133         this->m_conn.exec(this->getScript(SCRIPT_DROP_ALL_ITEMS).c_str());
134         this->m_conn.exec(this->getScript(SCRIPT_CREATE_SCHEMA).c_str());
135         this->setSchemaVersion(SchemaVersion::LATEST);
136 }
137
138 std::string Manager::getMigrationScript(int sv)
139 {
140         return this->getScript(SCRIPT_MIGRATE + std::to_string(sv));
141 }
142
143 std::string Manager::getScript(const std::string &scriptName)
144 {
145         auto scriptPath = this->m_scriptsDir + std::string("/") + scriptName + ".sql";
146         std::ifstream is(scriptPath);
147
148         if (is.fail())
149                 ThrowExc(CSR_ERROR_DB, "Cannot open script: " << scriptPath);
150
151         std::istreambuf_iterator<char> begin(is), end;
152         auto str = std::string(begin, end);
153
154         if (str.empty())
155                 ThrowExc(CSR_ERROR_DB, "Script file empty: " << scriptPath);
156
157         return str;
158 }
159
160 bool Manager::isTableExist(const std::string &name)
161 {
162         Statement stmt(this->m_conn, Query::CHK_TABLE);
163
164         stmt.bind(name);
165
166         return stmt.step();
167 }
168
169 int Manager::getSchemaVersion()
170 {
171         if (!this->isTableExist(SCHEMA_INFO_TABLE)) {
172                 WARN("Schema table doesn't exist. This case would be the first time of "
173                          "db manager instantiated in target");
174                 return SchemaVersion::NOT_EXIST;
175         }
176
177         Statement stmt(this->m_conn, Query::SEL_SCHEMA_INFO);
178
179         stmt.bind(DB_VERSION_STR);
180
181         return stmt.step() ? stmt.getInt() : SchemaVersion::NOT_EXIST;
182 }
183
184 void Manager::setSchemaVersion(int sv)
185 {
186         Statement stmt(this->m_conn, Query::INS_SCHEMA_INFO);
187
188         stmt.bind(DB_VERSION_STR);
189         stmt.bind(sv);
190
191         if (stmt.exec() == 0)
192                 ThrowExc(CSR_ERROR_DB, "Failed to set schema version to: " << sv);
193 }
194
195 //===========================================================================
196 // ENGINE_STATE table
197 //===========================================================================
198 csr_state_e Manager::getEngineState(csr_engine_id_e id)
199 {
200         std::lock_guard<std::mutex> ll(this->m_mutex);
201
202         {
203                 std::lock_guard<std::mutex> l(this->m_stateMutex);
204
205                 if (this->m_stateMap.size() == 0) {
206                         Statement stmt(this->m_conn, Query::SEL_ENGINE_STATE_ALL);
207
208                         while (stmt.step()) {
209                                 auto _id = static_cast<csr_engine_id_e>(stmt.getInt());
210                                 auto _state = static_cast<csr_state_e>(stmt.getInt());
211
212                                 this->m_stateMap[_id] = _state;
213                         }
214                 }
215
216                 return (this->m_stateMap.count(id) == 0) ? CSR_STATE_ENABLE : this->m_stateMap[id];
217         }
218 }
219
220 void Manager::setEngineState(csr_engine_id_e id, csr_state_e state)
221 {
222         std::lock_guard<std::mutex> ll(this->m_mutex);
223
224         {
225                 std::lock_guard<std::mutex> l(this->m_stateMutex);
226
227                 Statement stmt(this->m_conn, Query::INS_ENGINE_STATE);
228
229                 stmt.bind(static_cast<int>(id));
230                 stmt.bind(static_cast<int>(state));
231
232                 stmt.exec();
233
234                 this->m_stateMap[id] = state;
235         }
236 }
237
238 //===========================================================================
239 // SCAN_REQUEST table
240 //===========================================================================
241
242 time_t Manager::getLastScanTime(const std::string &dir, time_t since)
243 {
244         std::lock_guard<std::mutex> l(this->m_mutex);
245
246         time_t latest = -1;
247         std::string current = dir;
248         Statement stmt(this->m_conn, Query::SEL_SCAN_REQUEST);
249
250         while (true) {
251                 stmt.bind(current);
252
253                 if (stmt.step()) {
254                         auto candidate = static_cast<time_t>(stmt.getInt64());
255                         if ((since < 0 || since < candidate) && latest < candidate)
256                                 latest = candidate;
257                 }
258
259                 if (current == "/")
260                         break;
261
262                 auto pos = current.find_last_of('/');
263                 current = (pos == 0) ? "/" : current.substr(0, pos);
264
265                 stmt.reset();
266         }
267
268         return latest;
269 }
270
271 void Manager::insertLastScanTime(const std::string &dir, const std::string &dataVersion,
272                                                                  time_t scanTime)
273 {
274         std::lock_guard<std::mutex> l(this->m_mutex);
275
276         Statement stmt(this->m_conn, Query::INS_SCAN_REQUEST);
277
278         stmt.bind(dir);
279         stmt.bind(static_cast<sqlite3_int64>(scanTime));
280         stmt.bind(dataVersion);
281         stmt.exec();
282 }
283
284 void Manager::deleteLastScanTime(const std::string &dir)
285 {
286         std::lock_guard<std::mutex> l(this->m_mutex);
287
288         Statement stmt(this->m_conn, Query::DEL_SCAN_REQUEST_BY_DIR);
289
290         stmt.bind(dir);
291         stmt.exec();
292 }
293
294 void Manager::cleanLastScanTime()
295 {
296         std::lock_guard<std::mutex> l(this->m_mutex);
297
298         Statement stmt(this->m_conn, Query::DEL_SCAN_REQUEST);
299
300         stmt.exec();
301 }
302
303 //===========================================================================
304 // DETECTED_MALWARE_FILE table
305 //===========================================================================
306 RowShPtr Manager::getDetectedByNameOnPath(const std::string &path, time_t since)
307 {
308         Statement stmt(this->m_conn, Query::SEL_DETECTED_BY_NAME_ON_PATH);
309         stmt.bind(path);
310         stmt.bind(static_cast<sqlite3_int64>(since));
311
312         if (!stmt.step())
313                 return nullptr;
314
315         return extractRow(stmt);
316 }
317
318 RowShPtr Manager::getDetectedCloudByNameOnPath(const std::string &path, time_t since)
319 {
320         Statement stmt(this->m_conn, Query::SEL_DETECTED_CLOUD_BY_NAME_ON_PATH);
321         stmt.bind(path);
322         stmt.bind(static_cast<sqlite3_int64>(since));
323
324         if (!stmt.step())
325                 return nullptr;
326
327         return extractRowCloud(stmt);
328 }
329
330 RowShPtr Manager::getDetectedAllByNameOnPath(const std::string &path, time_t since, bool *isByCloud)
331 {
332         std::lock_guard<std::mutex> l(this->m_mutex);
333         auto row = this->getDetectedByNameOnPath(path, since);
334         if (row) {
335                 if (isByCloud)
336                         *isByCloud = false;
337                 return row;
338         }
339
340         row = this->getDetectedCloudByNameOnPath(path, since);
341         if (row) {
342                 if (isByCloud)
343                         *isByCloud = true;
344                 return row;
345         }
346
347         return nullptr;
348 }
349
350 RowShPtrs Manager::getDetectedByNameOnDir(const std::string &dir, time_t since)
351 {
352         Statement stmt(this->m_conn, Query::SEL_DETECTED_BY_NAME_ON_DIR);
353         stmt.bind(dir);
354         stmt.bind(static_cast<sqlite3_int64>(since));
355
356         RowShPtrs rows;
357
358         while (stmt.step())
359                 rows.emplace_back(extractRow(stmt));
360
361         return rows;
362 }
363
364 RowShPtrs Manager::getDetectedCloudByNameOnDir(const std::string &dir, time_t since)
365 {
366         Statement stmt(this->m_conn, Query::SEL_DETECTED_CLOUD_BY_NAME_ON_DIR);
367         stmt.bind(dir);
368         stmt.bind(static_cast<sqlite3_int64>(since));
369
370         RowShPtrs rows;
371         while (stmt.step())
372                 rows.emplace_back(extractRowCloud(stmt));
373
374         return rows;
375 }
376
377 RowShPtrs Manager::getDetectedAllByNameOnDir(const std::string &dir, time_t since)
378 {
379         RowShPtrs normals;
380         RowShPtrs clouds;
381
382         {
383                 std::lock_guard<std::mutex> l(this->m_mutex);
384                 normals = this->getDetectedByNameOnDir(dir, since);
385                 clouds = this->getDetectedCloudByNameOnDir(dir, since);
386         }
387
388         if (clouds.empty())
389                 return normals;
390
391         RowShPtrs rows;
392
393         for (const auto &cloud : clouds) {
394                 bool found = false;
395                 auto it = normals.begin();
396
397                 while (it != normals.end()) {
398                         if ((*it)->targetName == cloud->targetName) {
399                                 rows.emplace_back(std::move(*it));
400                                 it = normals.erase(it);
401                                 found = true;
402                                 break;
403                         } else {
404                                 ++it;
405                         }
406                 }
407
408                 if (!found)
409                         rows.push_back(cloud);
410         }
411
412         for (auto &&normal : normals)
413                 rows.emplace_back(std::move(normal));
414
415         return rows;
416 }
417
418 RowShPtrs Manager::getDetectedByFilepathOnDir(const std::string &dir, time_t since)
419 {
420         std::lock_guard<std::mutex> l(this->m_mutex);
421
422         Statement stmt(this->m_conn, Query::SEL_DETECTED_BY_FILEPATH_ON_DIR);
423         stmt.bind(dir);
424         stmt.bind(static_cast<sqlite3_int64>(since));
425
426         RowShPtrs rows;
427
428         while (stmt.step())
429                 rows.emplace_back(extractRow(stmt));
430
431         return rows;
432 }
433
434 RowShPtr Manager::getWorstByPkgPath(const std::string &pkgPath, time_t since)
435 {
436         std::lock_guard<std::mutex> l(this->m_mutex);
437
438         Statement stmt(this->m_conn, Query::SEL_WORST_BY_PKGPATH);
439         stmt.bind(pkgPath);
440         stmt.bind(static_cast<sqlite3_int64>(since));
441
442         if (!stmt.step())
443                 return nullptr;
444
445         RowShPtr row = std::make_shared<Row>();
446
447         row->targetName = stmt.getText(); // name
448         row->fileInAppPath = stmt.getText(); // file_path
449         row->dataVersion = stmt.getText(); // data_version
450         row->malwareName = stmt.getText(); // malware_name
451         row->detailedUrl = stmt.getText(); // detailed_url
452         row->severity = static_cast<csr_cs_severity_level_e>(stmt.getInt()); // severity
453         row->ts = static_cast<time_t>(stmt.getInt64()); // detected_time
454         row->pkgId = stmt.getText(); // pkg_id
455         row->isApp = true;
456
457         return row;
458 }
459
460 void Manager::insertDetectedFile(const CsDetected &d, const std::string &dataVersion)
461 {
462         std::lock_guard<std::mutex> l(this->m_mutex);
463
464         this->insertName(d.targetName);
465         this->insertDetected(d, d.targetName, dataVersion);
466 }
467
468 void Manager::insertDetectedAppByCloud(const std::string &name, const std::string &pkgId,
469                                                                            const CsDetected &d, const std::string &dataVersion)
470 {
471         std::lock_guard<std::mutex> l(this->m_mutex);
472
473         this->insertName(name);
474         this->insertDetectedCloud(d, pkgId, name, dataVersion);
475 }
476
477 void Manager::insertCache(const Cache &c)
478 {
479         std::lock_guard<std::mutex> l(this->m_mutex);
480
481         this->insertName(c.pkgPath);
482         for (std::vector<int>::size_type i = 0; i < c.detecteds.size(); ++i)
483                 this->insertDetected(*c.detecteds[i], c.filePaths[i], c.dataVersion);
484 }
485
486 void Manager::insertName(const std::string &name)
487 {
488         Statement stmt(this->m_conn, Query::INS_NAME);
489
490         stmt.bind(name);
491         stmt.bind(name);
492         stmt.exec();
493 }
494
495 void Manager::insertDetected(const CsDetected &d, const std::string &filepath,
496                                                          const std::string &dataVersion)
497 {
498         Statement stmt(this->m_conn, Query::INS_DETECTED);
499
500         stmt.bind(filepath);
501         stmt.bind(d.targetName);
502         stmt.bind(dataVersion);
503         stmt.bind(d.malwareName);
504         stmt.bind(d.detailedUrl);
505         stmt.bind(static_cast<int>(d.severity));
506         stmt.bind(static_cast<sqlite3_int64>(d.ts));
507         stmt.exec();
508 }
509
510 void Manager::insertDetectedCloud(const CsDetected &d, const std::string &pkgId,
511                                                                   const std::string &name, const std::string &dataVersion)
512 {
513         Statement stmt(this->m_conn, Query::INS_DETECTED_CLOUD);
514
515         stmt.bind(name);
516         stmt.bind(pkgId);
517         stmt.bind(dataVersion);
518         stmt.bind(d.malwareName);
519         stmt.bind(d.detailedUrl);
520         stmt.bind(static_cast<int>(d.severity));
521         stmt.bind(static_cast<sqlite3_int64>(d.ts));
522         stmt.exec();
523 }
524
525 void Manager::insertWorst(const std::string &pkgId, const std::string &name,
526                                                   const std::string &filepath)
527 {
528         std::lock_guard<std::mutex> l(this->m_mutex);
529
530         Statement stmt(this->m_conn, Query::INS_WORST);
531
532         stmt.bind(pkgId);
533         stmt.bind(name);
534         stmt.bind(filepath);
535         stmt.exec();
536 }
537
538 void Manager::updateIgnoreFlag(const std::string &name, bool flag)
539 {
540         std::lock_guard<std::mutex> l(this->m_mutex);
541
542         Statement stmt(this->m_conn, Query::UPD_IGNORE);
543
544         stmt.bind((flag ? 1 : 0));
545         stmt.bind(name);
546         stmt.exec();
547 }
548
549 void Manager::deleteDetectedByNameOnPath(const std::string &path)
550 {
551         std::lock_guard<std::mutex> l(this->m_mutex);
552
553         Statement stmt(this->m_conn, Query::DEL_DETECTED_BY_NAME_ON_PATH);
554
555         stmt.bind(path);
556         stmt.exec();
557 }
558
559 void Manager::deleteDetectedByFilepathOnPath(const std::string &path)
560 {
561         std::lock_guard<std::mutex> l(this->m_mutex);
562
563         Statement stmt(this->m_conn, Query::DEL_DETECTED_BY_FILEPATH_ON_PATH);
564
565         stmt.bind(path);
566         stmt.exec();
567 }
568
569 void Manager::deleteDetectedDeprecated(time_t since)
570 {
571         std::lock_guard<std::mutex> l(this->m_mutex);
572
573         Statement stmt(this->m_conn, Query::DEL_DETECTED_DEPRECATED);
574
575         stmt.bind(static_cast<sqlite3_int64>(since));
576         stmt.exec();
577
578         Statement stmt2(this->m_conn, Query::DEL_DETECTED_DEPRECATED_CLOUD);
579
580         stmt2.bind(static_cast<sqlite3_int64>(since));
581         stmt2.exec();
582 }
583
584 void Manager::transactionBegin()
585 {
586         this->m_conn.transactionBegin();
587 }
588
589 void Manager::transactionEnd()
590 {
591         this->m_conn.transactionEnd();
592 }
593
594 } // namespace Db
595 } // namespace Csr