diff --git a/native/cpp/CommonCpp/DatabaseManagers/NativeConnectionManager.h b/native/cpp/CommonCpp/DatabaseManagers/NativeConnectionManager.h --- a/native/cpp/CommonCpp/DatabaseManagers/NativeConnectionManager.h +++ b/native/cpp/CommonCpp/DatabaseManagers/NativeConnectionManager.h @@ -14,6 +14,8 @@ std::string logID, std::uint8_t *changesetPtr, int changesetSize); + std::vector + getAttachmentsFromLog(std::uint8_t *changesetPtr, int changesetSize); public: NativeConnectionManager(); diff --git a/native/cpp/CommonCpp/DatabaseManagers/NativeConnectionManager.cpp b/native/cpp/CommonCpp/DatabaseManagers/NativeConnectionManager.cpp --- a/native/cpp/CommonCpp/DatabaseManagers/NativeConnectionManager.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/NativeConnectionManager.cpp @@ -9,6 +9,8 @@ namespace comm { +const std::string BLOB_SERVICE_PREFIX = "comm-blob-service://"; + void NativeConnectionManager::attachSession() { int sessionCreationResult = sqlite3session_create(dbConnection, "main", &backupLogsSession); @@ -58,6 +60,112 @@ "Failed to rename complete log file from temporary path to target " "path."); } + + std::vector attachments = + getAttachmentsFromLog(changesetPtr, changesetSize); + if (!attachments.size()) { + return; + } + + std::string finalAttachmentsPath = + PlatformSpecificTools::getBackupLogFilePath(backupID, logID, true); + std::string tempAttachmentsPath = finalAttachmentsPath + "_tmp"; + + std::ofstream tempAttachmentsFile( + tempAttachmentsPath, std::ofstream::out | std::ofstream::trunc); + + if (!tempAttachmentsFile.is_open()) { + throw std::runtime_error("Failed to open temporary log attachments file."); + } + + for (const auto &attachment : attachments) { + tempAttachmentsFile << attachment << std::endl; + } + tempAttachmentsFile.close(); + + if (std::rename(tempAttachmentsPath.c_str(), finalAttachmentsPath.c_str())) { + throw std::runtime_error( + "Failed to rename complete log attachments file from temporary path to " + "target " + "path."); + } +} + +std::vector NativeConnectionManager::getAttachmentsFromLog( + std::uint8_t *changesetPtr, + int changesetSize) { + std::vector attachments; + sqlite3_changeset_iter *changesetIter; + int startIterResult = + sqlite3changeset_start(&changesetIter, changesetSize, changesetPtr); + + if (startIterResult != SQLITE_OK) { + throw std::runtime_error( + "Failed to initialize log iterator. Details: " + + std::string(sqlite3_errstr(startIterResult))); + } + + int nextResult; + for (nextResult = sqlite3changeset_next(changesetIter); + nextResult == SQLITE_ROW; + nextResult = sqlite3changeset_next(changesetIter)) { + const char *tableName; + int columnsNumber; + int operationType; + + int getOperationResult = sqlite3changeset_op( + changesetIter, &tableName, &columnsNumber, &operationType, 0); + + if (getOperationResult != SQLITE_OK) { + throw std::runtime_error( + "Failed to extract operation from log iterator. Details: " + + std::string(sqlite3_errstr(getOperationResult))); + } + + if (std::string(tableName) != "media") { + continue; + } + + if (operationType != SQLITE_UPDATE && operationType != SQLITE_INSERT) { + continue; + } + + sqlite3_value *uriFromMediaRow; + // In "media" table "uri" column has index 3 (starting from 0) + int getURIResult = sqlite3changeset_new(changesetIter, 3, &uriFromMediaRow); + if (getURIResult != SQLITE_OK) { + throw std::runtime_error( + "Failed to extract uri value of media row from " + "log iterator. Details: " + + std::string(sqlite3_errstr(getURIResult))); + } + + if (!uriFromMediaRow) { + continue; + } + + std::string uri = std::string( + reinterpret_cast(sqlite3_value_text(uriFromMediaRow))); + if (uri.substr(0, BLOB_SERVICE_PREFIX.size()) != BLOB_SERVICE_PREFIX) { + continue; + } + attachments.push_back(uri.substr(BLOB_SERVICE_PREFIX.size())); + } + + if (nextResult != SQLITE_DONE) { + throw std::runtime_error( + "Error while iterating over a log. Details: " + + std::string(sqlite3_errstr(nextResult))); + } + + int finalizeIterResult = sqlite3changeset_finalize(changesetIter); + if (finalizeIterResult != SQLITE_OK) { + throw std::runtime_error( + "Failed to finalize log iterator. Details: " + + std::string(sqlite3_errstr(finalizeIterResult))); + } + + return attachments; } NativeConnectionManager::NativeConnectionManager()