mirror of
https://github.com/Mikescher/kpsync.git
synced 2025-08-25 16:38:06 +02:00
wip
This commit is contained in:
parent
47080e14db
commit
1f6fb583ee
5
Makefile
5
Makefile
@ -32,11 +32,6 @@ package:
|
|||||||
GOARCH=386 GOOS=linux go build -o _out/kpsync_linux-386 ./cmd/cli # Linux - 32 bit
|
GOARCH=386 GOOS=linux go build -o _out/kpsync_linux-386 ./cmd/cli # Linux - 32 bit
|
||||||
GOARCH=amd64 GOOS=linux go build -o _out/kpsync_linux-amd64 ./cmd/cli # Linux - 64 bit
|
GOARCH=amd64 GOOS=linux go build -o _out/kpsync_linux-amd64 ./cmd/cli # Linux - 64 bit
|
||||||
GOARCH=arm64 GOOS=linux go build -o _out/kpsync_linux-arm64 ./cmd/cli # Linux - ARM
|
GOARCH=arm64 GOOS=linux go build -o _out/kpsync_linux-arm64 ./cmd/cli # Linux - ARM
|
||||||
GOARCH=386 GOOS=windows go build -o _out/kpsync_win-386.exe -tags timetzdata -ldflags "-w -s" ./cmd/cli # Windows - 32 bit
|
|
||||||
GOARCH=amd64 GOOS=windows go build -o _out/kpsync_win-amd64.exe -tags timetzdata -ldflags "-w -s" ./cmd/cli # Windows - 64 bit
|
|
||||||
GOARCH=arm64 GOOS=windows go build -o _out/kpsync_win-arm64.exe -tags timetzdata -ldflags "-w -s" ./cmd/cli # Windows - ARM
|
|
||||||
GOARCH=amd64 GOOS=darwin go build -o _out/kpsync_macos-amd64 ./cmd/cli # macOS - 32 bit
|
|
||||||
GOARCH=amd64 GOOS=darwin go build -o _out/kpsync_macos-amd64 ./cmd/cli # macOS - 64 bit
|
|
||||||
GOARCH=amd64 GOOS=openbsd go build -o _out/kpsync_openbsd-amd64 ./cmd/cli # OpenBSD - 64 bit
|
GOARCH=amd64 GOOS=openbsd go build -o _out/kpsync_openbsd-amd64 ./cmd/cli # OpenBSD - 64 bit
|
||||||
GOARCH=arm64 GOOS=openbsd go build -o _out/kpsync_openbsd-arm64 ./cmd/cli # OpenBSD - ARM
|
GOARCH=arm64 GOOS=openbsd go build -o _out/kpsync_openbsd-arm64 ./cmd/cli # OpenBSD - ARM
|
||||||
GOARCH=amd64 GOOS=freebsd go build -o _out/kpsync_freebsd-amd64 ./cmd/cli # FreeBSD - 64 bit
|
GOARCH=amd64 GOOS=freebsd go build -o _out/kpsync_freebsd-amd64 ./cmd/cli # FreeBSD - 64 bit
|
||||||
|
@ -24,8 +24,9 @@ type Application struct {
|
|||||||
syncLoopRunning *syncext.AtomicBool
|
syncLoopRunning *syncext.AtomicBool
|
||||||
keepassRunning *syncext.AtomicBool
|
keepassRunning *syncext.AtomicBool
|
||||||
|
|
||||||
sigStopChan chan bool // keepass exited
|
sigKPExitChan chan bool // keepass exited
|
||||||
sigErrChan chan error // fatal error
|
sigManualStopChan chan bool // manual stop
|
||||||
|
sigErrChan chan error // fatal error
|
||||||
|
|
||||||
sigSyncLoopStopChan chan bool // stop sync loop
|
sigSyncLoopStopChan chan bool // stop sync loop
|
||||||
sigTermKeepassChan chan bool // stop keepass
|
sigTermKeepassChan chan bool // stop keepass
|
||||||
@ -42,7 +43,8 @@ func NewApplication() *Application {
|
|||||||
trayReady: syncext.NewAtomicBool(false),
|
trayReady: syncext.NewAtomicBool(false),
|
||||||
syncLoopRunning: syncext.NewAtomicBool(false),
|
syncLoopRunning: syncext.NewAtomicBool(false),
|
||||||
keepassRunning: syncext.NewAtomicBool(false),
|
keepassRunning: syncext.NewAtomicBool(false),
|
||||||
sigStopChan: make(chan bool, 128),
|
sigKPExitChan: make(chan bool, 128),
|
||||||
|
sigManualStopChan: make(chan bool, 128),
|
||||||
sigErrChan: make(chan error, 128),
|
sigErrChan: make(chan error, 128),
|
||||||
sigSyncLoopStopChan: make(chan bool, 128),
|
sigSyncLoopStopChan: make(chan bool, 128),
|
||||||
sigTermKeepassChan: make(chan bool, 128),
|
sigTermKeepassChan: make(chan bool, 128),
|
||||||
@ -77,28 +79,54 @@ func (app *Application) Run() {
|
|||||||
app.syncLoopRunning = syncext.NewAtomicBool(true)
|
app.syncLoopRunning = syncext.NewAtomicBool(true)
|
||||||
defer app.syncLoopRunning.Set(false)
|
defer app.syncLoopRunning.Set(false)
|
||||||
|
|
||||||
err := app.initSync()
|
isr, err := app.initSync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.sigErrChan <- err
|
app.sigErrChan <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
if isr == InitSyncResponseAbort {
|
||||||
app.keepassRunning = syncext.NewAtomicBool(true)
|
app.sigManualStopChan <- true
|
||||||
defer app.keepassRunning.Set(false)
|
return
|
||||||
|
} else if isr == InitSyncResponseOkay {
|
||||||
|
|
||||||
app.runKeepass()
|
go func() {
|
||||||
}()
|
app.keepassRunning = syncext.NewAtomicBool(true)
|
||||||
|
defer app.keepassRunning.Set(false)
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
app.runKeepass(false)
|
||||||
|
}()
|
||||||
|
|
||||||
app.setTrayStateDirect("Sleeping...", assets.IconDefault)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
err = app.runSyncLoop()
|
app.setTrayStateDirect("Sleeping...", assets.IconDefault)
|
||||||
if err != nil {
|
|
||||||
app.sigErrChan <- err
|
err = app.runSyncLoop()
|
||||||
|
if err != nil {
|
||||||
|
app.sigErrChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if isr == InitSyncResponseFallback {
|
||||||
|
|
||||||
|
app.LogInfo(fmt.Sprintf("Starting KeepassXC with local fallback database (without sync loop!)"))
|
||||||
|
app.LogDebug(fmt.Sprintf("DB-Path := '%s'", app.config.LocalFallback))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
app.keepassRunning = syncext.NewAtomicBool(true)
|
||||||
|
defer app.keepassRunning.Set(false)
|
||||||
|
|
||||||
|
app.runKeepass(true)
|
||||||
|
}()
|
||||||
|
|
||||||
|
app.setTrayStateDirect("Sleeping...", assets.IconDefault)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
app.LogError("Unknown InitSyncResponse: "+string(isr), nil)
|
||||||
|
app.sigErrChan <- fmt.Errorf("unknown InitSyncResponse: %s", isr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
sigTerm := make(chan os.Signal, 1)
|
sigTerm := make(chan os.Signal, 1)
|
||||||
@ -111,7 +139,7 @@ func (app *Application) Run() {
|
|||||||
|
|
||||||
app.stopBackgroundRoutines()
|
app.stopBackgroundRoutines()
|
||||||
|
|
||||||
// TODO try final sync
|
// TODO try final sync (?)
|
||||||
|
|
||||||
case err := <-app.sigErrChan: // fatal error
|
case err := <-app.sigErrChan: // fatal error
|
||||||
|
|
||||||
@ -121,9 +149,17 @@ func (app *Application) Run() {
|
|||||||
|
|
||||||
app.LogError("Stopped due to error: "+err.Error(), nil)
|
app.LogError("Stopped due to error: "+err.Error(), nil)
|
||||||
|
|
||||||
// TODO stop?
|
return
|
||||||
|
|
||||||
case _ = <-app.sigStopChan: // keepass exited
|
case <-app.sigManualStopChan: // manual
|
||||||
|
|
||||||
|
app.LogInfo("Stopping application (manual)")
|
||||||
|
|
||||||
|
app.stopBackgroundRoutines()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
case _ = <-app.sigKPExitChan: // keepass exited
|
||||||
|
|
||||||
app.LogInfo("Stopping application (received STOP)")
|
app.LogInfo("Stopping application (received STOP)")
|
||||||
|
|
||||||
|
@ -1,9 +1,90 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
func (app *Application) showErrorNotification(msg string) {
|
import (
|
||||||
//TODO
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.blackforestbytes.com/BlackForestBytes/goext/cmdext"
|
||||||
|
"git.blackforestbytes.com/BlackForestBytes/goext/exerr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (app *Application) showErrorNotification(msg string, body string) {
|
||||||
|
app.LogDebug("{notify-send} " + msg)
|
||||||
|
|
||||||
|
res, err := cmdext.
|
||||||
|
Runner("notify-send").
|
||||||
|
Arg("--urgency=critical").
|
||||||
|
Arg("--app-name=kpsync").
|
||||||
|
Arg("--print-id").
|
||||||
|
Arg(msg).
|
||||||
|
Arg(body).
|
||||||
|
Run()
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to show notification", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.ExitCode != 0 {
|
||||||
|
app.LogError("Failed to show notification", nil)
|
||||||
|
app.LogDebug(fmt.Sprintf("ExitCode: %d", res.ExitCode))
|
||||||
|
app.LogDebug(fmt.Sprintf("Stdout: %s", res.StdOut))
|
||||||
|
app.LogDebug(fmt.Sprintf("Stderr: %s", res.StdErr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.LogDebug(fmt.Sprintf("Displayed notification with id %s", res.StdOut))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *Application) showSuccessNotification(msg string) {
|
func (app *Application) showSuccessNotification(msg string, body string) {
|
||||||
//TODO
|
app.LogDebug("{notify-send} " + msg)
|
||||||
|
|
||||||
|
res, err := cmdext.
|
||||||
|
Runner("notify-send").
|
||||||
|
Arg("--urgency=normal").
|
||||||
|
Arg("--app-name=kpsync").
|
||||||
|
Arg("--print-id").
|
||||||
|
Arg(msg).
|
||||||
|
Arg(body).
|
||||||
|
Run()
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to show notification", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.ExitCode != 0 {
|
||||||
|
app.LogError("Failed to show notification", nil)
|
||||||
|
app.LogDebug(fmt.Sprintf("ExitCode: %d", res.ExitCode))
|
||||||
|
app.LogDebug(fmt.Sprintf("Stdout: %s", res.StdOut))
|
||||||
|
app.LogDebug(fmt.Sprintf("Stderr: %s", res.StdErr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.LogDebug(fmt.Sprintf("Displayed notification with id %s", res.StdOut))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *Application) showChoiceNotification(msg string, body string, options map[string]string) (string, error) {
|
||||||
|
app.LogDebug(fmt.Sprintf("{notify-send} %s {%d choices}", msg, len(options)))
|
||||||
|
|
||||||
|
bldr := cmdext.
|
||||||
|
Runner("notify-send").
|
||||||
|
Arg("--wait").
|
||||||
|
Arg("--app-name=kpsync")
|
||||||
|
|
||||||
|
for kOpt, vOpt := range options {
|
||||||
|
bldr = bldr.Arg("--action=" + kOpt + "=" + vOpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
bldr = bldr.Arg(msg)
|
||||||
|
|
||||||
|
res, err := bldr.Run()
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to show choice-notification", err)
|
||||||
|
return "", exerr.Wrap(err, "").Build()
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.ExitCode != 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(res.StdOut), nil
|
||||||
}
|
}
|
||||||
|
249
app/sync.go
249
app/sync.go
@ -16,11 +16,19 @@ import (
|
|||||||
"mikescher.com/kpsync/assets"
|
"mikescher.com/kpsync/assets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *Application) initSync() error {
|
type InitSyncResponse string //@enum:type
|
||||||
|
|
||||||
|
const (
|
||||||
|
InitSyncResponseOkay InitSyncResponse = "OKAY"
|
||||||
|
InitSyncResponseFallback InitSyncResponse = "FALLBACK"
|
||||||
|
InitSyncResponseAbort InitSyncResponse = "ABORT"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (app *Application) initSync() (InitSyncResponse, error) {
|
||||||
|
|
||||||
err := os.MkdirAll(app.config.WorkDir, os.ModePerm)
|
err := os.MkdirAll(app.config.WorkDir, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return exerr.Wrap(err, "").Build()
|
return "", exerr.Wrap(err, "").Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
app.dbFile = path.Join(app.config.WorkDir, path.Base(app.config.LocalFallback))
|
app.dbFile = path.Join(app.config.WorkDir, path.Base(app.config.LocalFallback))
|
||||||
@ -28,7 +36,7 @@ func (app *Application) initSync() error {
|
|||||||
|
|
||||||
if app.isKeepassRunning() {
|
if app.isKeepassRunning() {
|
||||||
app.LogError("keepassxc is already running!", nil)
|
app.LogError("keepassxc is already running!", nil)
|
||||||
return exerr.New(exerr.TypeInternal, "keepassxc is already running").Build()
|
return "", exerr.New(exerr.TypeInternal, "keepassxc is already running").Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
state := app.readState()
|
state := app.readState()
|
||||||
@ -89,20 +97,42 @@ func (app *Application) initSync() error {
|
|||||||
return nil
|
return nil
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return exerr.Wrap(err, "").Build()
|
|
||||||
|
r, err := app.showChoiceNotification("Failed to download remote database.\nUse local fallback?", map[string]string{"y": "Yes", "n": "Abort"})
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to show choice notification", err)
|
||||||
|
return "", exerr.Wrap(err, "Failed to show choice notification").Build()
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == "y" {
|
||||||
|
return InitSyncResponseFallback, nil
|
||||||
|
} else if r == "n" {
|
||||||
|
return InitSyncResponseAbort, nil
|
||||||
|
} else {
|
||||||
|
return "", exerr.Wrap(err, "").Build()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return InitSyncResponseOkay, nil
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
app.LogInfo(fmt.Sprintf("Skip download - use existing local database %s", app.dbFile))
|
app.LogInfo(fmt.Sprintf("Skip download - use existing local database %s", app.dbFile))
|
||||||
app.LogLine()
|
app.LogLine()
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return InitSyncResponseOkay, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *Application) runKeepass() {
|
func (app *Application) runKeepass(fallback bool) {
|
||||||
app.LogInfo("Starting keepassxc...")
|
app.LogInfo("Starting keepassxc...")
|
||||||
|
|
||||||
cmd := exec.Command("keepassxc", app.dbFile)
|
filePath := app.dbFile
|
||||||
|
if fallback {
|
||||||
|
filePath = app.config.LocalFallback
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("keepassxc", filePath)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
@ -138,7 +168,7 @@ func (app *Application) runKeepass() {
|
|||||||
if errors.As(err, &exitErr) {
|
if errors.As(err, &exitErr) {
|
||||||
|
|
||||||
app.LogInfo(fmt.Sprintf("keepass exited with code %d", exitErr.ExitCode()))
|
app.LogInfo(fmt.Sprintf("keepass exited with code %d", exitErr.ExitCode()))
|
||||||
app.sigStopChan <- true
|
app.sigKPExitChan <- true
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -152,7 +182,7 @@ func (app *Application) runKeepass() {
|
|||||||
app.LogInfo("keepassxc exited successfully")
|
app.LogInfo("keepassxc exited successfully")
|
||||||
app.LogLine()
|
app.LogLine()
|
||||||
|
|
||||||
app.sigStopChan <- true
|
app.sigKPExitChan <- true
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -190,65 +220,152 @@ func (app *Application) runSyncLoop() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
func() {
|
app.onDBFileChanged()
|
||||||
app.masterLock.Lock()
|
|
||||||
app.uploadRunning.Wait(false)
|
|
||||||
app.uploadRunning.Set(true)
|
|
||||||
app.masterLock.Unlock()
|
|
||||||
|
|
||||||
defer app.uploadRunning.Set(false)
|
|
||||||
|
|
||||||
app.LogInfo("Database file was modified")
|
|
||||||
app.LogInfo(fmt.Sprintf("Sleeping for %d seconds", app.config.Debounce))
|
|
||||||
|
|
||||||
time.Sleep(timeext.FromSeconds(app.config.Debounce))
|
|
||||||
|
|
||||||
state := app.readState()
|
|
||||||
localCS, err := app.calcLocalChecksum()
|
|
||||||
if err != nil {
|
|
||||||
app.LogError("Failed to calculate local database checksum", err)
|
|
||||||
app.showErrorNotification("Failed to calculate local database checksum")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if localCS == state.Checksum {
|
|
||||||
app.LogInfo("Local database still matches remote (via checksum) - no need to upload")
|
|
||||||
app.LogInfo(fmt.Sprintf("Checksum (remote/cached) := %s", state.Checksum))
|
|
||||||
app.LogInfo(fmt.Sprintf("Checksum (local) := %s", localCS))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
etag, lm, sha, sz, err := app.uploadDatabase(langext.Ptr(state.ETag))
|
|
||||||
if errors.Is(err, ETagConflictError) {
|
|
||||||
|
|
||||||
//TODO - choice notification
|
|
||||||
|
|
||||||
} else if err != nil {
|
|
||||||
app.LogError("Failed to upload remote database", err)
|
|
||||||
app.showErrorNotification("Failed to upload remote database")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
app.LogInfo(fmt.Sprintf("Uploaded database to remote"))
|
|
||||||
app.LogDebug(fmt.Sprintf("Checksum := %s", sha))
|
|
||||||
app.LogDebug(fmt.Sprintf("ETag := %s", etag))
|
|
||||||
app.LogDebug(fmt.Sprintf("Size := %s (%d)", langext.FormatBytes(sz), sz))
|
|
||||||
app.LogDebug(fmt.Sprintf("LastModified := %s", lm.Format(time.RFC3339)))
|
|
||||||
|
|
||||||
err = app.saveState(etag, lm, sha, sz)
|
|
||||||
if err != nil {
|
|
||||||
app.LogError("Failed to save state", err)
|
|
||||||
app.showErrorNotification("Failed to save state")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
app.showSuccessNotification("Uploaded database successfully")
|
|
||||||
|
|
||||||
app.LogLine()
|
|
||||||
}()
|
|
||||||
|
|
||||||
case err := <-watcher.Errors:
|
case err := <-watcher.Errors:
|
||||||
app.LogError("Filewatcher reported an error", err)
|
app.LogError("Filewatcher reported an error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *Application) onDBFileChanged() {
|
||||||
|
app.masterLock.Lock()
|
||||||
|
app.uploadRunning.Wait(false)
|
||||||
|
app.uploadRunning.Set(true)
|
||||||
|
app.masterLock.Unlock()
|
||||||
|
|
||||||
|
defer app.uploadRunning.Set(false)
|
||||||
|
|
||||||
|
fin1 := app.setTrayState("Uploading database", assets.IconUpload)
|
||||||
|
defer fin1()
|
||||||
|
|
||||||
|
app.LogInfo("Database file was modified")
|
||||||
|
app.LogInfo(fmt.Sprintf("Sleeping for %d seconds", app.config.Debounce))
|
||||||
|
|
||||||
|
time.Sleep(timeext.FromSeconds(app.config.Debounce))
|
||||||
|
|
||||||
|
state := app.readState()
|
||||||
|
localCS, err := app.calcLocalChecksum()
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to calculate local database checksum", err)
|
||||||
|
app.showErrorNotification("KeePassSync: Error", "Failed to calculate local database checksum")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if state != nil && localCS == state.Checksum {
|
||||||
|
app.LogInfo("Local database still matches remote (via checksum) - no need to upload")
|
||||||
|
app.LogInfo(fmt.Sprintf("Checksum (remote/cached) := %s", state.Checksum))
|
||||||
|
app.LogInfo(fmt.Sprintf("Checksum (local) := %s", localCS))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.LogInfo("Uploading database to remote")
|
||||||
|
|
||||||
|
var eTagPtr *string = nil
|
||||||
|
if state != nil {
|
||||||
|
eTagPtr = langext.Ptr(state.ETag)
|
||||||
|
}
|
||||||
|
|
||||||
|
etag, lm, sha, sz, err := app.uploadDatabase(eTagPtr)
|
||||||
|
if errors.Is(err, ETagConflictError) {
|
||||||
|
|
||||||
|
fin1()
|
||||||
|
fin2 := app.setTrayState("Uploading database (conflict", assets.IconUploadConflict)
|
||||||
|
defer fin2()
|
||||||
|
|
||||||
|
r, err := app.showChoiceNotification("KeePassSync: Upload failed", "Conflict with remote file.\n[1] Overwrite remote file\n[2] Download remote and sync manually", map[string]string{"o": "Overwrite", "d": "Download", "a": "Abort"})
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to show choice notification", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == "o" {
|
||||||
|
|
||||||
|
app.LogInfo("Uploading database to remote (unchecked)")
|
||||||
|
|
||||||
|
etag, lm, sha, sz, err := app.uploadDatabase(nil) // unchecked upload
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to upload remote database", err)
|
||||||
|
app.showErrorNotification("KeePassSync: Error", "Failed to upload remote database")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.LogInfo(fmt.Sprintf("Uploaded database to remote"))
|
||||||
|
app.LogDebug(fmt.Sprintf("Checksum := %s", sha))
|
||||||
|
app.LogDebug(fmt.Sprintf("ETag := %s", etag))
|
||||||
|
app.LogDebug(fmt.Sprintf("Size := %s (%d)", langext.FormatBytes(sz), sz))
|
||||||
|
app.LogDebug(fmt.Sprintf("LastModified := %s", lm.Format(time.RFC3339)))
|
||||||
|
|
||||||
|
err = app.saveState(etag, lm, sha, sz)
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to save state", err)
|
||||||
|
app.showErrorNotification("KeePassSync: Error", "Failed to save state")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.showSuccessNotification("KeePassSync", "Uploaded database successfully (overwrite remote)")
|
||||||
|
|
||||||
|
app.LogLine()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
} else if r == "d" {
|
||||||
|
|
||||||
|
app.LogInfo(fmt.Sprintf("Re-Downloading remote database to %s", app.dbFile))
|
||||||
|
|
||||||
|
etag, lm, sha, sz, err := app.downloadDatabase()
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to download remote database", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.LogInfo(fmt.Sprintf("Downloaded remote database to %s", app.dbFile))
|
||||||
|
app.LogInfo(fmt.Sprintf("Checksum := %s", sha))
|
||||||
|
app.LogInfo(fmt.Sprintf("ETag := %s", etag))
|
||||||
|
app.LogInfo(fmt.Sprintf("Size := %s (%d)", langext.FormatBytes(sz), sz))
|
||||||
|
app.LogInfo(fmt.Sprintf("LastModified := %s", lm.Format(time.RFC3339)))
|
||||||
|
|
||||||
|
err = app.saveState(etag, lm, sha, sz)
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to save state", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.showSuccessNotification("KeePassSync", "Re-Downloaded database successfully")
|
||||||
|
|
||||||
|
app.LogLine()
|
||||||
|
|
||||||
|
} else if r == "a" {
|
||||||
|
|
||||||
|
app.sigManualStopChan <- true
|
||||||
|
return
|
||||||
|
|
||||||
|
} else {
|
||||||
|
app.LogError("Unknown choice in notification: '"+r+"'", nil)
|
||||||
|
app.showErrorNotification("KeePassSync: Error", "Unknown choice in notification: '"+r+"'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if err != nil {
|
||||||
|
app.LogError("Failed to upload remote database", err)
|
||||||
|
app.showErrorNotification("KeePassSync: Error", "Failed to upload remote database")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.LogInfo(fmt.Sprintf("Uploaded database to remote"))
|
||||||
|
app.LogDebug(fmt.Sprintf("Checksum := %s", sha))
|
||||||
|
app.LogDebug(fmt.Sprintf("ETag := %s", etag))
|
||||||
|
app.LogDebug(fmt.Sprintf("Size := %s (%d)", langext.FormatBytes(sz), sz))
|
||||||
|
app.LogDebug(fmt.Sprintf("LastModified := %s", lm.Format(time.RFC3339)))
|
||||||
|
|
||||||
|
err = app.saveState(etag, lm, sha, sz)
|
||||||
|
if err != nil {
|
||||||
|
app.LogError("Failed to save state", err)
|
||||||
|
app.showErrorNotification("KeePassSync: Error", "Failed to save state")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.showSuccessNotification("KeePassSync: Error", "Uploaded database successfully")
|
||||||
|
|
||||||
|
app.LogLine()
|
||||||
|
}
|
||||||
|
@ -38,16 +38,24 @@ func (app *Application) setTrayState(txt string, icon []byte) func() {
|
|||||||
systray.SetIcon(icon)
|
systray.SetIcon(icon)
|
||||||
systray.SetTooltip(txt)
|
systray.SetTooltip(txt)
|
||||||
|
|
||||||
|
var finDone = false
|
||||||
|
|
||||||
fin := func() {
|
fin := func() {
|
||||||
app.masterLock.Lock()
|
app.masterLock.Lock()
|
||||||
defer app.masterLock.Unlock()
|
defer app.masterLock.Unlock()
|
||||||
|
|
||||||
|
if finDone {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !app.trayReady.Get() {
|
if !app.trayReady.Get() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
systray.SetIcon(assets.IconDefault)
|
systray.SetIcon(assets.IconDefault)
|
||||||
systray.SetTooltip("Sleeping...")
|
systray.SetTooltip("Sleeping...")
|
||||||
|
|
||||||
|
finDone = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return fin
|
return fin
|
||||||
|
@ -12,3 +12,9 @@ var IconDefault []byte
|
|||||||
|
|
||||||
//go:embed iconDownload.png
|
//go:embed iconDownload.png
|
||||||
var IconDownload []byte
|
var IconDownload []byte
|
||||||
|
|
||||||
|
//go:embed iconUpload.png
|
||||||
|
var IconUpload []byte
|
||||||
|
|
||||||
|
//go:embed iconUploadConflict.png
|
||||||
|
var IconUploadConflict []byte
|
||||||
|
BIN
assets/iconUpload.png
Normal file
BIN
assets/iconUpload.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 358 B |
BIN
assets/iconUploadConflict.png
Normal file
BIN
assets/iconUploadConflict.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 448 B |
Loading…
x
Reference in New Issue
Block a user