dont preview by default, Vault::delete_file

This commit is contained in:
2025-11-02 21:44:10 +01:00
parent 3e33504e22
commit cf1ac6d556
10 changed files with 110 additions and 68 deletions

View File

@@ -1 +1 @@
Checks: '*,clang-analyzer-*,-llvmlibc-*,-fuchsia-*,-altera-*,-abseil-*,-android-*,-modernize-use-trailing-return-type,-readability-identifier-length,-*-readability-todo,-*-magic-numbers,-readability-function-cognitive-complexity,-*-easily-swappable-parameters,-*-pro-type-reinterpret-cast,-*-owning-memory,-concurrency-mt-unsafe,-cert-env33-c'
Checks: '*,clang-analyzer-*,-llvmlibc-*,-fuchsia-*,-altera-*,-abseil-*,-android-*,-modernize-use-trailing-return-type,-readability-identifier-length,-*-readability-todo,-*-magic-numbers,-readability-function-cognitive-complexity,-*-easily-swappable-parameters,-*-pro-type-reinterpret-cast,-*-owning-memory,-concurrency-mt-unsafe,-cert-env33-c,-*-avoid-do-while'

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
/.cache
/build
*.py
*.dull

View File

@@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
find_package(PkgConfig REQUIRED)
pkg_check_modules(BOTAN REQUIRED botan-3)
set(CMAKE_AUTOMOC ON)

View File

@@ -5,11 +5,14 @@
#include <string>
#define ASSERT(cond) \
do { \
if (!(cond)) { \
std::cerr << "ASSERTION FAILED at " << __FILE__ << ":" << __LINE__ << "\n" \
std::cerr << "ASSERTION FAILED at " << __FILE__ << ":" << __LINE__ \
<< "\n" \
<< #cond << std::endl; \
abort(); \
}
} \
} while (0)
using u8 = unsigned char;
using i16 = int16_t;

View File

@@ -15,6 +15,8 @@ encrypt_xchacha20_poly1305(const Botan::secure_vector<u8> &plaintext,
ASSERT(key.size() == 32);
ASSERT(nonce.size() == 24);
// XChaCha20 is selected automatically based on the nonce size
// https://github.com/randombit/botan/blob/master/src/lib/stream/chacha/chacha.cpp#L375
auto cipher = Botan::AEAD_Mode::create_or_throw(
"ChaCha20Poly1305", Botan::Cipher_Dir::Encryption);

View File

@@ -6,9 +6,11 @@
#include <QTemporaryFile>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
: QMainWindow(parent), ui(std::make_unique<Ui::MainWindow>()) {
ui->setupUi(this);
ui->filePreview->setVisible(false);
connect(ui->actionNew, &QAction::triggered, this, [this]() {
QString path = QFileDialog::getSaveFileName(this, "Choose vault location",
QDir::currentPath(),
@@ -33,10 +35,9 @@ MainWindow::MainWindow(QWidget *parent)
reload_fs_tree();
});
connect(ui->fsTreeWidget, &QTreeWidget::itemClicked,
[this](QTreeWidgetItem *item, int column) {
preview_file(item->text(column).toStdString());
});
connect(
ui->fsTreeWidget, &QTreeWidget::itemClicked,
[this](QTreeWidgetItem *, int) { ui->filePreview->setVisible(false); });
connect(ui->fsTreeWidget, &QTreeWidget::customContextMenuRequested, this,
&MainWindow::file_context_menu);
@@ -54,7 +55,7 @@ MainWindow::MainWindow(QWidget *parent)
std::string content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
m_vault->write_file(path_to_filename(path.toStdString()), content);
m_vault->create_file(path_to_filename(path.toStdString()), content);
}
reload_fs_tree();
@@ -62,6 +63,7 @@ MainWindow::MainWindow(QWidget *parent)
}
void MainWindow::reload_fs_tree() {
ui->menuFiles->setEnabled(true);
ui->fsTreeWidget->clear();
auto headers = m_vault->read_file_headers();
@@ -69,11 +71,14 @@ void MainWindow::reload_fs_tree() {
auto *item = new QTreeWidgetItem(ui->fsTreeWidget);
item->setText(0, QString::fromStdString(header.name));
item->setText(1, QString::number(header.size));
item->setText(2, QString::number(header.offset));
}
ui->fsTreeWidget->resizeColumnToContents(0);
}
void MainWindow::preview_file(const std::string &filename) {
ui->filePreview->setVisible(true);
auto content = m_vault->read_file(filename);
if (content) {
ui->filePreview->setText(QString::fromStdString(content.value()));
@@ -102,6 +107,8 @@ void MainWindow::edit_file(const std::string &filename) {
QTextStream in(&temp_file);
m_vault->update_file(filename, in.readAll().toStdString());
reload_fs_tree();
// QTemporaryFile gets deleted when it goes out of scope
} else {
qWarning() << "File to edit not found";
}
@@ -115,6 +122,11 @@ void MainWindow::file_context_menu(const QPoint &pos) {
QMenu menu(this);
QAction *preview_action = menu.addAction(
style()->standardIcon(QStyle::SP_FileDialogContentsView), "Preview");
connect(preview_action, &QAction::triggered, this,
[this, item]() { preview_file(item->text(0).toStdString()); });
QAction *edit_action = menu.addAction(
style()->standardIcon(QStyle::SP_FileDialogDetailedView), "Edit");
connect(edit_action, &QAction::triggered, this,

View File

@@ -12,7 +12,7 @@ public:
explicit MainWindow(QWidget *parent = nullptr);
private:
Ui::MainWindow *ui;
std::unique_ptr<Ui::MainWindow> ui;
std::unique_ptr<Vault> m_vault;

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<width>900</width>
<height>600</height>
</rect>
</property>
@@ -33,6 +33,11 @@
<string>Size</string>
</property>
</column>
<column>
<property name="text">
<string>Offset</string>
</property>
</column>
</widget>
</item>
<item>
@@ -46,10 +51,10 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>30</height>
<height>32</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<widget class="QMenu" name="menuVault">
<property name="title">
<string>Vault</string>
</property>
@@ -57,12 +62,15 @@
<addaction name="actionOpen"/>
</widget>
<widget class="QMenu" name="menuFiles">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title">
<string>Files</string>
</property>
<addaction name="actionAddFiles"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuVault"/>
<addaction name="menuFiles"/>
</widget>
<action name="actionNew">

View File

@@ -19,7 +19,7 @@ Vault::Vault(std::string path) : m_path(std::move(path)) {
ASSERT(static_cast<bool>(m_file.read(header.data(), header.size())));
ASSERT(std::string_view(header.data(), header.size()) == "DULL");
std::int16_t version = 0;
i16 version = 0;
ASSERT(m_file.read(reinterpret_cast<char *>(&version), sizeof(version)));
ASSERT(version == VERSION);
}
@@ -31,19 +31,13 @@ std::vector<FileHeader> Vault::read_file_headers() {
m_file.seekg(AFTER_HEADER_OFFSET, std::ios::beg);
while (true) {
u64 name_size = 0;
if (!m_file.read(reinterpret_cast<char *>(&name_size), sizeof(u64))) {
auto header = read_file_header(m_file);
if (header) {
headers.push_back(header.value());
m_file.seekg(static_cast<i64>(header->size), std::ios::cur);
} else {
break;
}
FileHeader header{};
header.name.resize(name_size);
m_file.read(header.name.data(), static_cast<i64>(name_size));
m_file.read(reinterpret_cast<char *>(&header.size), sizeof(u64));
m_file.seekg(static_cast<i64>(header.size), std::ios::cur);
headers.push_back(header);
}
return headers;
@@ -54,46 +48,42 @@ std::optional<std::string> Vault::read_file(const std::string &filename) {
m_file.seekg(AFTER_HEADER_OFFSET, std::ios::beg);
while (true) {
u64 name_size = 0;
if (!m_file.read(reinterpret_cast<char *>(&name_size), sizeof(u64))) {
auto header = read_file_header(m_file);
if (!header) {
break;
}
std::string name;
name.resize(name_size);
m_file.read(name.data(), static_cast<i64>(name_size));
u64 content_size = 0;
m_file.read(reinterpret_cast<char *>(&content_size), sizeof(u64));
if (name == filename) {
std::string content(content_size, '\0');
m_file.read(content.data(), static_cast<i64>(content_size));
if (header->name == filename) {
std::string content(header->size, '\0');
if (!m_file.read(content.data(), static_cast<i64>(header->size))) {
break;
}
return content;
}
m_file.seekg(static_cast<i64>(content_size), std::ios::cur);
m_file.seekg(static_cast<i64>(header->size), std::ios::cur);
}
return std::nullopt;
}
void Vault::write_file(const std::string &filename,
void Vault::create_file(const std::string &filename,
const std::string &content) {
m_file.clear();
m_file.seekp(0, std::ios::end);
u64 filename_size = filename.size();
u64 content_size = content.size();
m_file.write(reinterpret_cast<const char *>(&filename_size), sizeof(u64));
m_file.write(filename.data(), static_cast<i64>(filename_size));
m_file.write(reinterpret_cast<const char *>(&content_size), sizeof(u64));
m_file.write(content.data(), static_cast<i64>(content_size));
ASSERT(m_file.write(reinterpret_cast<const char *>(&filename_size),
sizeof(u64)));
ASSERT(m_file.write(filename.data(), static_cast<i64>(filename_size)));
ASSERT(
m_file.write(reinterpret_cast<const char *>(&content_size), sizeof(u64)));
ASSERT(m_file.write(content.data(), static_cast<i64>(content_size)));
m_file.flush();
}
void Vault::update_file(const std::string &filename,
const std::string &content) {
void Vault::delete_file(const std::string &filename) {
m_file.clear();
m_file.seekg(AFTER_HEADER_OFFSET, std::ios::beg);
@@ -103,26 +93,20 @@ void Vault::update_file(const std::string &filename,
while (true) {
i64 current_pos = m_file.tellg();
u64 name_size = 0;
if (!m_file.read(reinterpret_cast<char *>(&name_size), sizeof(u64))) {
auto header = read_file_header(m_file);
if (!header) {
break;
}
std::string name;
name.resize(name_size);
m_file.read(name.data(), static_cast<i64>(name_size));
u64 content_size = 0;
m_file.read(reinterpret_cast<char *>(&content_size), sizeof(u64));
if (name == filename) {
if (header->name == filename) {
entry_start = current_pos;
entry_total_size = sizeof(u64) + name_size + sizeof(u64) + content_size;
m_file.seekg(static_cast<i64>(content_size), std::ios::cur);
entry_total_size =
sizeof(u64) + header->name.length() + sizeof(u64) + header->size;
m_file.seekg(static_cast<i64>(header->size), std::ios::cur);
break;
}
m_file.seekg(static_cast<i64>(content_size), std::ios::cur);
m_file.seekg(static_cast<i64>(header->size), std::ios::cur);
}
if (entry_start != -1) {
@@ -136,7 +120,7 @@ void Vault::update_file(const std::string &filename,
m_file.clear();
m_file.seekp(entry_start, std::ios::beg);
m_file.write(remaining.data(), static_cast<i64>(remaining.size()));
ASSERT(m_file.write(remaining.data(), static_cast<i64>(remaining.size())));
i64 new_size = entry_start + static_cast<i64>(remaining.size());
m_file.flush();
@@ -147,6 +131,32 @@ void Vault::update_file(const std::string &filename,
m_file.open(m_path, std::ios::in | std::ios::out | std::ios::binary);
ASSERT(m_file.good());
}
write_file(filename, content);
}
void Vault::update_file(const std::string &filename,
const std::string &content) {
delete_file(filename);
create_file(filename, content);
}
std::optional<FileHeader> Vault::read_file_header(std::fstream &file) {
FileHeader header{};
header.offset = file.tellg();
u64 name_size = 0;
if (!file.read(reinterpret_cast<char *>(&name_size), sizeof(u64))) {
return std::nullopt;
}
ASSERT(name_size < 10000);
header.name.resize(name_size);
if (!file.read(header.name.data(), static_cast<i64>(name_size))) {
return std::nullopt;
}
if (!file.read(reinterpret_cast<char *>(&header.size), sizeof(u64))) {
return std::nullopt;
}
return header;
}

View File

@@ -11,6 +11,7 @@ constexpr u64 AFTER_HEADER_OFFSET = 6;
struct FileHeader {
std::string name;
u64 size;
u64 offset;
};
class Vault {
@@ -19,9 +20,12 @@ public:
std::vector<FileHeader> read_file_headers();
std::optional<std::string> read_file(const std::string &name);
void write_file(const std::string &name, const std::string &content);
void create_file(const std::string &name, const std::string &content);
void delete_file(const std::string &name);
void update_file(const std::string &name, const std::string &content);
static std::optional<FileHeader> read_file_header(std::fstream &file);
private:
std::string m_path;
std::fstream m_file;