#define RAPIDJSON_HAS_STDSTRING 1 #include #include #include #include "data_internal.hxx" #define FIND_MEMBER(name, ty) m = doc.FindMember(#name); \ if (m == doc.MemberEnd() || !m->value.Is##ty()) { data_logger->error("Missing or invalid "#name); throw std::exception{}; } #define ASSIGN_MEMBER(lhs, name, ty) FIND_MEMBER(name, ty) lhs = m->value.Get##ty() SaveNode load_node(const rapidjson::Value &doc) { rapidjson::Value::ConstMemberIterator m; auto node = std::make_unique(); std::uint64_t id, parent; node->parent = nullptr; ASSIGN_MEMBER(node->id, id, Uint64); id = node->id; data_logger->debug("Loading node {}", id); ASSIGN_MEMBER(node->name, name, String); ASSIGN_MEMBER(node->file, file, Bool); ASSIGN_MEMBER(node->preview, preview, Bool); ASSIGN_MEMBER(node->size, size, Uint64); ASSIGN_MEMBER(parent, parent, Uint64); std::list children; FIND_MEMBER(children, Array) for (const auto &v : m->value.GetArray()) if (!v.IsUint64()) { data_logger->error("Invalid child id"); throw std::exception{}; } else children.push_back(v.GetUint64()); return { .node = std::move(node), .id = id, .parent_id = parent, .children_ids = children }; } std::unique_ptr load_user(const rapidjson::Value &doc) { rapidjson::Value::ConstMemberIterator m; auto user = std::make_unique(); ASSIGN_MEMBER(user->id, id, Uint64); data_logger->debug("Loading user {}", user->id); ASSIGN_MEMBER(user->name, name, String); ASSIGN_MEMBER(user->password, password, String); ASSIGN_MEMBER(user->tfa_secret, tfa_secret, String); ASSIGN_MEMBER(user->enabled, enabled, Bool); ASSIGN_MEMBER(user->admin, admin, Bool); ASSIGN_MEMBER(user->tfa_enabled, tfa_enabled, Bool); ASSIGN_MEMBER(user->tfa_mail, tfa_mail, Bool); ASSIGN_MEMBER(user->next_node_id, next_node_id, Uint64); std::list nodes; FIND_MEMBER(nodes, Array) for (const auto &v : m->value.GetArray()) nodes.push_back(load_node(v)); for (auto &node : nodes) user->nodes.emplace(node.id, node.node.release()); for (const auto &snode : nodes) { auto &node = user->nodes.at(snode.id); if (node->id != 0) node->parent = user->nodes.at(snode.parent_id); for (const auto &child : snode.children_ids) node->children.push_back(user->nodes.at(child)); } user->user_dir = files_dir / std::to_string(user->id); return user; } bool load_from_file(Data &data, const std::string &file) { char buf[65536]; bool success = false; data_logger->info("Loading data from {}", file); data.users.clear(); auto start = std::chrono::high_resolution_clock::now(); try { FileWrapper f{file.c_str(), "r"}; rapidjson::FileReadStream fstream{f.f, buf, sizeof(buf)}; rapidjson::Document doc; rapidjson::Document::MemberIterator m; doc.ParseStream(fstream); if (doc.HasParseError()) { data_logger->error("Failed to parse data"); auto e = rapidjson::GetParseError_En(doc.GetParseError()); data_logger->error("{}", e); throw std::exception{}; } ASSIGN_MEMBER(data.version, version, Uint64); if (data.version != Data::current_version) { data_logger->error("Version is {}, current version is {}. Refusing to load!", data.version, Data::current_version); throw std::exception{}; } ASSIGN_MEMBER(data.next_user_id, next_user_id, Uint64); FIND_MEMBER(users, Array) for (const auto &v : m->value.GetArray()) { auto user = load_user(v); data.users.emplace(user->id, std::move(user)); } success = true; } catch (std::exception &_) {} auto dur = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); data_logger->info("Data loading took {} ms", dur.count()); return success; } void Data::load() { if (std::filesystem::exists(data_cur_file)) { bool ok = load_from_file(*this, data_cur_file); if (!ok) { data_logger->warn("Retrying from old file"); if (!std::filesystem::exists(data_old_file)) { data_logger->critical("Old file missing"); crash(); } ok = load_from_file(*this, data_old_file); if (!ok) { data_logger->critical("Failed to load data"); crash(); } } } else if (std::filesystem::exists(data_old_file)) { data_logger->warn("Data file missing, loading from old file"); bool ok = load_from_file(*this, data_old_file); if (!ok) { data_logger->critical("Failed to load data"); crash(); } } else data_logger->info("No data file found for loading"); }