Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support filtering backup by address #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions cli/bin/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,11 @@ int performBackup(etcpp::Session& session, cxxopts::ParseResult const& argParseR
return EXIT_FAILURE;
}

std::string addressFilter;
if (argParseResult.count("address")) {
addressFilter = argParseResult["address"].as<std::string>();
}

// Telemetry - we'd like to know whether the user overwrote the default export path
session.setUsingDefaultExportPath(!pathCameFromArgs && usingDefaultBackupPath);

Expand All @@ -533,7 +538,7 @@ int performBackup(etcpp::Session& session, cxxopts::ParseResult const& argParseR

std::unique_ptr<BackupTask> backupTask;
try {
backupTask = std::make_unique<BackupTask>(session, backupPath);
backupTask = std::make_unique<BackupTask>(session, backupPath, addressFilter);
} catch (const etcpp::SessionException& e) {
etLogError("Failed to create export task: {}", e.what());
std::cerr << "Failed to create export task: " << e.what() << std::endl;
Expand Down Expand Up @@ -662,6 +667,7 @@ int main(int argc, const char** argv) {
cxxopts::value<std::string>())("t,totp", "User's TOTP 2FA code (can also be set with env var ET_TOTP_CODE)",
cxxopts::value<std::string>())(
"u,user", "User's account/email (can also be set with env var ET_USER_EMAIL", cxxopts::value<std::string>())(
"a,address", "Email address to backup (defaults to all addresses)", cxxopts::value<std::string>())(
"k, telemetry", "Disable anonymous telemetry statistics (can also be set with env var ET_TELEMETRY_OFF)", cxxopts::value<bool>())(
"h,help", "Show help");

Expand Down Expand Up @@ -745,4 +751,4 @@ int main(int argc, const char** argv) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
}
5 changes: 3 additions & 2 deletions cli/bin/tasks/backup_task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
#include <etsession.hpp>
#include <iostream>

BackupTask::BackupTask(etcpp::Session& session, const std::filesystem::path& backupPath) :
mBackup(session.newBackup(backupPath.u8string().c_str())) {}
BackupTask::BackupTask(etcpp::Session& session, const std::filesystem::path& backupPath, const std::string& addressFilter) :
mBackup(session.newBackup(backupPath.u8string().c_str(),
addressFilter.empty() ? nullptr : addressFilter.c_str())) {}

void BackupTask::onProgress(float progress) {
updateProgress(progress);
Expand Down
2 changes: 1 addition & 1 deletion cli/bin/tasks/backup_task.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class BackupTask final : public TaskWithProgress<void>, etcpp::BackupCallback {
CLIProgressBar mProgressBar;

public:
BackupTask(etcpp::Session& session, const std::filesystem::path& backupPath);
BackupTask(etcpp::Session& session, const std::filesystem::path& backupPath, const std::string& addressFilter = "");
~BackupTask() override = default;
BackupTask(const BackupTask&) = delete;
BackupTask(BackupTask&&) = delete;
Expand Down
9 changes: 7 additions & 2 deletions go-lib/cmd/lib/export_backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import (
)

//export etSessionNewBackup
func etSessionNewBackup(sessionPtr *C.etSession, cExportPath *C.cchar_t, outBackup **C.etBackup) C.etSessionStatus {
func etSessionNewBackup(sessionPtr *C.etSession, cExportPath *C.cchar_t, cAddressFilter *C.cchar_t, outBackup **C.etBackup) C.etSessionStatus {
cSession, ok := resolveSession(sessionPtr)
if !ok {
return C.ET_SESSION_STATUS_INVALID
Expand All @@ -56,7 +56,12 @@ func etSessionNewBackup(sessionPtr *C.etSession, cExportPath *C.cchar_t, outBack
exportPath := C.GoString(cExportPath)
exportPath = filepath.Join(exportPath, cSession.s.GetUser().Email)

mailExport := mail.NewExportTask(cSession.ctx, exportPath, cSession.s)
addressFilter := ""
if cAddressFilter != nil {
addressFilter = C.GoString(cAddressFilter)
}

mailExport := mail.NewExportTask(cSession.ctx, exportPath, cSession.s, addressFilter)

h := internal.NewHandle(&cBackup{
csession: cSession,
Expand Down
41 changes: 33 additions & 8 deletions go-lib/internal/mail/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ type ExportTask struct {
session *session.Session
log *logrus.Entry
cancelledByUser bool
addressFilter string
}

func NewExportTask(
ctx context.Context,
exportPath string,
session *session.Session,
addressFilter string,
) *ExportTask {
exportPath = filepath.Join(exportPath, generateUniqueExportDir())

Expand All @@ -78,13 +80,14 @@ func NewExportTask(
ctx, cancel := context.WithCancel(ctx)

return &ExportTask{
ctx: ctx,
ctxCancel: cancel,
group: async.NewGroup(ctx, session.GetPanicHandler()),
tmpDir: tmpDir,
exportDir: exportPath,
session: session,
log: logrus.WithField("export", "mail").WithField("userID", session.GetUser().ID),
ctx: ctx,
ctxCancel: cancel,
group: async.NewGroup(ctx, session.GetPanicHandler()),
tmpDir: tmpDir,
exportDir: exportPath,
session: session,
log: logrus.WithField("export", "mail").WithField("userID", session.GetUser().ID),
addressFilter: addressFilter,
}
}

Expand Down Expand Up @@ -209,8 +212,30 @@ func (e *ExportTask) Run(ctx context.Context, reporter Reporter) error {
downloadMemMb = MinDownloadMemMB
}

// Get the address ID if a filter is specified
var addressID string
if e.addressFilter != "" {
addresses, err := client.GetAddresses(ctx)
if err != nil {
return fmt.Errorf("failed to get addresses: %w", err)
}

for _, addr := range addresses {
if addr.Email == e.addressFilter {
addressID = addr.ID
break
}
}

if addressID == "" {
return fmt.Errorf("specified email address %s not found in account", e.addressFilter)
}

e.log.WithField("address", e.addressFilter).Info("Filtering backup to single address")
}

// Build stages
metaStage := NewMetadataStage(client, e.log, MetadataPageSize, NumParallelDownloads)
metaStage := NewMetadataStage(client, e.log, MetadataPageSize, NumParallelDownloads, addressID)
downloadStage := NewDownloadStage(client, NumParallelDownloads, e.log, downloadMemMb, e.session.GetPanicHandler())
buildStage := NewBuildStage(NumParallelBuilders, e.log, buildMemMB, e.session.GetPanicHandler(), e.session.GetReporter(), user.ID)
writeStage := NewWriteStage(e.tmpDir, e.exportDir, NumParallelWriters, e.log, reporter, e.session.GetPanicHandler())
Expand Down
11 changes: 8 additions & 3 deletions go-lib/internal/mail/export_stage_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,23 @@ type MetadataStage struct {
outputCh chan []proton.MessageMetadata
pageSize int
splitSize int
addressID string
}

func NewMetadataStage(
client apiclient.Client,
entry *logrus.Entry,
pageSize int,
splitSize int,
addressID string,
) *MetadataStage {
return &MetadataStage{
client: client,
log: entry.WithField("stage", "metadata"),
outputCh: make(chan []proton.MessageMetadata),
pageSize: pageSize,
splitSize: splitSize,
addressID: addressID,
}
}

Expand All @@ -76,8 +79,9 @@ func (m *MetadataStage) Run(

if lastMessageID != "" {
meta, err := client.GetMessageMetadataPage(ctx, 0, m.pageSize, proton.MessageFilter{
EndID: lastMessageID,
Desc: true,
EndID: lastMessageID,
AddressID: m.addressID,
Desc: true,
})

if err != nil {
Expand All @@ -93,7 +97,8 @@ func (m *MetadataStage) Run(
metadata = meta
} else {
meta, err := client.GetMessageMetadataPage(ctx, 0, m.pageSize, proton.MessageFilter{
Desc: true,
AddressID: m.addressID,
Desc: true,
})
if err != nil {
errReporter.ReportStageError(err)
Expand Down
2 changes: 1 addition & 1 deletion lib/include/etsession.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class Session final {
[[nodiscard]] std::string getHVSolveURL() const;
[[nodiscard]] LoginState markHVSolved();

[[nodiscard]] Backup newBackup(const char* exportPath) const;
[[nodiscard]] Backup newBackup(const char* exportPath, const char* addressFilter = nullptr) const;
[[nodiscard]] Restore newRestore(const char* backupPath) const;

void setUsingDefaultExportPath(const bool usingDefaultExportPath);
Expand Down
4 changes: 2 additions & 2 deletions lib/lib/etsession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ Session::LoginState Session::getLoginState() const {
return ls;
}

Backup Session::newBackup(const char* exportPath) const {
Backup Session::newBackup(const char* exportPath, const char* addressFilter) const {
etBackup* exportPtr = nullptr;
wrapCCall([&](etSession* ptr) -> etSessionStatus { return etSessionNewBackup(ptr, exportPath, &exportPtr); });
wrapCCall([&](etSession* ptr) -> etSessionStatus { return etSessionNewBackup(ptr, exportPath, addressFilter, &exportPtr); });

return Backup(*this, exportPtr);
}
Expand Down