From d04cc64c27d95db11450522a6629709a8f98e6ab Mon Sep 17 00:00:00 2001 From: darmiel <71837281+darmiel@users.noreply.github.com> Date: Sun, 28 Mar 2021 16:21:48 +0200 Subject: [PATCH] Some cleanup --- cmd/watch.go | 186 +++++--------------------------- internal/api/get.go | 6 +- internal/api/type.go | 6 +- internal/client/client.go | 28 +++++ internal/client/watch_client.go | 67 ++++++++++++ internal/client/watch_server.go | 76 +++++++++++++ internal/common/pipe.go | 9 +- 7 files changed, 206 insertions(+), 172 deletions(-) create mode 100644 internal/client/client.go create mode 100644 internal/client/watch_client.go create mode 100644 internal/client/watch_server.go diff --git a/cmd/watch.go b/cmd/watch.go index e726ab7..f0f5e1d 100644 --- a/cmd/watch.go +++ b/cmd/watch.go @@ -16,34 +16,20 @@ limitations under the License. package cmd import ( - "github.com/atotto/clipboard" - "github.com/darmiel/yaxc/internal/api" - "github.com/darmiel/yaxc/internal/common" + "github.com/darmiel/yaxc/internal/client" + "github.com/spf13/cobra" "log" "os" "os/signal" - "strings" - "sync" "syscall" "time" - - "github.com/spf13/cobra" ) var ( watchAnywherePath string watchPassphrase string -) - -var ( - errors int - - lastClipboardData string - lastClipboardHash string - ignoreClipboardHash string - - mu = sync.Mutex{} - errMu = sync.Mutex{} + watchIgnoreServer bool + watchIgnoreClient bool ) // watchCmd represents the watch command @@ -51,161 +37,35 @@ var watchCmd = &cobra.Command{ Use: "watch", Long: `Watch Clipboard`, Run: func(cmd *cobra.Command, args []string) { + check := client.NewCheck(watchAnywherePath, watchPassphrase) + done := make(chan bool) - d := make(chan int, 1) + log.Println("Starting watchers:") - // start watcher - // clipboard - go watchClipboard(watchAnywherePath, watchPassphrase, d) - go watchServer(watchAnywherePath, watchPassphrase, d) + if !watchIgnoreServer { + log.Println(" [~] Starting Server Update Watcher") + go client.WatchServer(check, 1*time.Second, done) + } + + if !watchIgnoreClient { + log.Println(" [~] Starting Client Update Watcher") + go client.WatchClient(check, 100*time.Millisecond, done) + } + + if watchIgnoreServer && watchIgnoreClient { + log.Println("WARN :: Ignoring Client & Server") + } log.Println("Started clipboard-watcher. Press CTRL-C to stop.") sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM) <-sc - // stop watchers - d <- 1 - d <- 1 - - log.Println("Stopped.") + // Stopping server watcher + done <- true }, } -func handleErr(err error, m string) { - errMu.Lock() - errors++ - errMu.Unlock() - log.Println("[err]", "(", m, ") ::", errors, "errors:", err) -} - -func watchClipboard(path, pass string, done chan int) { - a := api.API() - t := time.NewTicker(100 * time.Millisecond) - - for { - select { - case <-t.C: - mu.Lock() - - data, err := clipboard.ReadAll() - if err != nil { - mu.Unlock() - continue - } - errors = 0 - - // strip data - data = strings.TrimSpace(data) - - if lastClipboardData == data { - mu.Unlock() - continue - } - log.Println("[o] Changed!") - - // calculate new hash - lastClipboardData = data - lastClipboardHash = common.MD5Hash(data) - - // check if server has current clipboard - serverHash, err := a.GetHash(path) - if err != nil && err != api.ErrErrResponse { - handleErr(err, "read-server-hash") - mu.Unlock() - continue - } - - if err != api.ErrErrResponse { - if serverHash == lastClipboardHash { - log.Println("[ ~ ] (rea) Server Hash == Local Hash") - mu.Unlock() - continue - } - } - ignoreClipboardHash = serverHash - - // update server hash - if err := a.SetContent(path, pass, data); err != nil { - mu.Unlock() - handleErr(err, "set-server-content") - continue - } - - log.Println("[ ok] Updated contents.") - mu.Unlock() - break - - case <-done: - log.Println("[ ok] Stopping watch clipboard") - return - } - } -} - -func watchServer(path, pass string, done chan int) { - a := api.API() - t := time.NewTicker(1 * time.Second) - - for { - select { - case <-t.C: - - time.Sleep(1 * time.Second) - mu.Lock() - - hash, err := a.GetHash(path) - if err != nil { - if err != api.ErrErrResponse { - handleErr(err, "read-server-hash") - } - mu.Unlock() - continue - } - - if hash == lastClipboardHash || hash == ignoreClipboardHash { - mu.Unlock() - continue - } - - // get data - data, err := a.GetContent(path, pass) - if err != nil { - mu.Unlock() - handleErr(err, "read-server-contents") - continue - } - - // empty - if strings.TrimSpace(data) == "" { - log.Println("[wrn] Received empty data") - mu.Unlock() - continue - } - - log.Println("[ ~ ] Received new data:", data) - - lastClipboardData = data - lastClipboardHash = common.MD5Hash(data) - - if err := clipboard.WriteAll(data); err != nil { - ignoreClipboardHash = hash - handleErr(err, "write-clipboard") - mu.Unlock() - continue - } - - log.Println("[ ok] Wrote client content") - mu.Unlock() - break - - case <-done: - log.Println("[ ok] Stopping watch server") - return - } - } -} - func init() { rootCmd.AddCommand(watchCmd) @@ -213,4 +73,6 @@ func init() { cobra.CheckErr(cobra.MarkFlagRequired(watchCmd.Flags(), "anywhere")) watchCmd.Flags().StringVarP(&watchPassphrase, "passphrase", "s", "", "Encryption Key") + watchCmd.Flags().BoolVar(&watchIgnoreServer, "ignore-server", false, "Ignore Server Updates") + watchCmd.Flags().BoolVar(&watchIgnoreClient, "ignore-client", false, "Ignore Client Updates") } diff --git a/internal/api/get.go b/internal/api/get.go index 7ba2028..53f7b75 100644 --- a/internal/api/get.go +++ b/internal/api/get.go @@ -10,7 +10,7 @@ var ( ErrErrResponse = errors.New("invalid response") ) -func (a *api) GetContent(path, passphrase string) (res string, err error) { +func (a *Api) GetContent(path, passphrase string) (res string, err error) { var resp *req.Resp if resp, err = req.Get(a.ServerURL + "/" + path); err != nil { return @@ -33,7 +33,7 @@ func (a *api) GetContent(path, passphrase string) (res string, err error) { return } -func (a *api) SetContent(path, passphrase, content string) (err error) { +func (a *Api) SetContent(path, passphrase, content string) (err error) { if passphrase != "" { var b []byte if b, err = common.Encrypt(content, passphrase); err != nil { @@ -45,7 +45,7 @@ func (a *api) SetContent(path, passphrase, content string) (err error) { return } -func (a *api) GetHash(path string) (res string, err error) { +func (a *Api) GetHash(path string) (res string, err error) { var resp *req.Resp if resp, err = req.Get(a.ServerURL + "/hash/" + path); err != nil { return diff --git a/internal/api/type.go b/internal/api/type.go index 7e0ba24..9da2045 100644 --- a/internal/api/type.go +++ b/internal/api/type.go @@ -2,13 +2,13 @@ package api import "github.com/spf13/viper" -type api struct { +type Api struct { ServerURL string } -func API() *api { +func API() *Api { server := viper.GetString("server") - return &api{ + return &Api{ ServerURL: server, } } diff --git a/internal/client/client.go b/internal/client/client.go new file mode 100644 index 0000000..37417f8 --- /dev/null +++ b/internal/client/client.go @@ -0,0 +1,28 @@ +package client + +import ( + "errors" + "github.com/darmiel/yaxc/internal/api" + "sync" +) + +var ( + ErrEmptyHash = errors.New("empty hash") + ErrUnsupported = errors.New("clipboard unsupported") +) + +type Check struct { + a *api.Api + mu sync.Mutex + path string + pass string +} + +func NewCheck(path, pass string) *Check { + return &Check{ + a: api.API(), + mu: sync.Mutex{}, + path: path, + pass: pass, + } +} diff --git a/internal/client/watch_client.go b/internal/client/watch_client.go new file mode 100644 index 0000000..3c2602d --- /dev/null +++ b/internal/client/watch_client.go @@ -0,0 +1,67 @@ +package client + +import ( + "github.com/atotto/clipboard" + "github.com/darmiel/yaxc/internal/common" + "log" + "strings" + "time" +) + +func WatchClient(c *Check, d time.Duration, done chan bool) { + t := time.NewTicker(d) + for { + select { + case <-done: + log.Println("[Watch Client] Stopping ...") + return + case <-t.C: + if err := c.CheckClient(); err != nil { + log.Println("[Watch Client] WARN:", err) + } + break + } + } +} + +func (c *Check) CheckClient() (err error) { + // check if clipboard actions are available + if clipboard.Unsupported { + return ErrUnsupported + } + + // lock + c.mu.Lock() + defer c.mu.Unlock() + + // get clipboard + var cb string + cb, _ = clipboard.ReadAll() + + // ignore empty clipboard + if strings.TrimSpace(cb) == "" { + return + } + + // calculate hash + var ch string + if ch = common.MD5Hash(cb); ch == "" { + err = ErrEmptyHash + return + } + + // get hash from server + var sh string + sh, _ = c.a.GetHash(c.path) + if strings.TrimSpace(sh) != "" { + // compare hashes + if ch == sh { + return + } + } + + // upload to server + err = c.a.SetContent(c.path, c.pass, cb) + log.Println("Wrote: '" + cb + "' to server") + return +} diff --git a/internal/client/watch_server.go b/internal/client/watch_server.go new file mode 100644 index 0000000..80a0a8b --- /dev/null +++ b/internal/client/watch_server.go @@ -0,0 +1,76 @@ +package client + +import ( + "github.com/atotto/clipboard" + "github.com/darmiel/yaxc/internal/api" + "github.com/darmiel/yaxc/internal/common" + "log" + "time" +) + +func WatchServer(c *Check, d time.Duration, done chan bool) { + t := time.NewTicker(d) + for { + select { + case <-done: + log.Println("[Watch Server] Stopping ...") + return + case <-t.C: + if err := c.CheckServer(); err != nil { + log.Println("[Watch Server] WARN:", err) + } + break + } + } +} + +func (c *Check) CheckServer() (err error) { + // check if clipboard actions are available + if clipboard.Unsupported { + return ErrUnsupported + } + + // lock + c.mu.Lock() + defer c.mu.Unlock() + + // get hash from server + var sh string + if sh, err = c.a.GetHash(c.path); err != nil { + if err == api.ErrErrResponse { + err = nil + } + return + } + if sh == "" { + return ErrEmptyHash + } + + // get clipboard + var cb string + cb, _ = clipboard.ReadAll() + if cb != "" { + // calculate hash + var ch string + if ch = common.MD5Hash(cb); ch == "" { + err = ErrEmptyHash + return + } + + // compare hashes + if ch == sh { + return + } + } + + // get data from server + var sd string + if sd, err = c.a.GetContent(c.path, c.pass); err != nil { + return + } + + // update contents + err = clipboard.WriteAll(sd) + log.Println("Wrote: '" + sd + "' to client") + return +} diff --git a/internal/common/pipe.go b/internal/common/pipe.go index 84b7535..8342190 100644 --- a/internal/common/pipe.go +++ b/internal/common/pipe.go @@ -5,6 +5,7 @@ import ( "errors" "io" "os" + "strings" ) var NotPiped = errors.New("not piped") @@ -14,15 +15,12 @@ func ReadPipe() (res string, err error) { if info, err = os.Stdin.Stat(); err != nil { return } - if info.Mode()&os.ModeCharDevice != 0 || info.Size() <= 0 { err = NotPiped return } - reader := bufio.NewReader(os.Stdin) var output []rune - for { input, _, err := reader.ReadRune() if err != nil && err == io.EOF { @@ -30,7 +28,10 @@ func ReadPipe() (res string, err error) { } output = append(output, input) } - res = string(output) + // remove new line at the end + if strings.HasSuffix(res, "\n") { + res = res[:len(res)-1] + } return }