From 0c75f9fd36d7bb5a5cab795b8403131bfb19d1d1 Mon Sep 17 00:00:00 2001 From: Wojciech Kwolek Date: Sun, 10 Oct 2021 22:33:20 +0200 Subject: [PATCH] initial commit --- .drone.yml | 21 +++++++ Makefile | 18 ++++++ go.mod | 18 ++++++ go.sum | 25 +++++++++ main.go | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++ main_test.go | 38 +++++++++++++ 6 files changed, 275 insertions(+) create mode 100644 .drone.yml create mode 100644 Makefile create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 main_test.go diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..5975a00 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,21 @@ +kind: pipeline +type: docker +name: default + +steps: + - name: build and test + image: golang + commands: + - make all + - go test + - name: gitea_release + image: plugins/gitea-release + settings: + api_key: + from_secret: gitea_token + base_url: https://git.kwolek.io + files: + - dist/messagessh* + when: + event: + - tag diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4c4939d --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ + + +.PHONY: all linux darwin freebsd clean +all: linux darwin freebsd + +linux: $(addprefix dist/messagessh.linux.,amd64 arm64) +darwin: $(addprefix dist/messagessh.darwin.,arm64 amd64) +freebsd: $(addprefix dist/messagessh.freebsd.,amd64 arm) + +clean: + rm -rf dist + +dist/messagessh.%: *.go go.mod go.sum + $(eval platform := $(subst ., ,$@)) + $(eval os := $(word 2,$(platform))) + $(eval arch := $(word 3,$(platform))) + GOOS=$(os) GOARCH=$(arch) go build -o $@ + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..844f99e --- /dev/null +++ b/go.mod @@ -0,0 +1,18 @@ +module git.kwolek.io/wojciech/messages.sh + +go 1.17 + +require ( + github.com/gliderlabs/ssh v0.3.3 + github.com/stretchr/testify v1.7.0 + golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e +) + +require ( + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..abf76dc --- /dev/null +++ b/go.sum @@ -0,0 +1,25 @@ +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gliderlabs/ssh v0.3.3 h1:mBQ8NiOgDkINJrZtoizkC3nDNYgSaWtxyem6S2XHBtA= +github.com/gliderlabs/ssh v0.3.3/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..deb7b51 --- /dev/null +++ b/main.go @@ -0,0 +1,155 @@ +package main + +import ( + "encoding/hex" + "fmt" + "io" + "log" + "strings" + "time" + + "github.com/gliderlabs/ssh" + terminal "golang.org/x/term" +) + +type Message struct { + Content string + Username string + PK ssh.PublicKey + PKShort string + Timestamp time.Time +} + +type MessageList struct { + list []Message + size int +} + +func NewMessageList(size int) *MessageList { + ml := &MessageList{size: size} + return ml +} + +func (ml *MessageList) Add(m Message) { + if len(ml.list) == ml.size { + ml.list = ml.list[1:] + } + ml.list = append(ml.list, m) +} + +func (ml *MessageList) List() []Message { + return ml.list +} + +func CheckString(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] < 32 || s[i] > 126 { + return false + } + } + return true +} + +func main() { + m := NewMessageList(10) + pkAuth := ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { + return true + }) + ssh.Handle(func(s ssh.Session) { + term := terminal.NewTerminal(s, "> ") + user := s.User() + term.Write(term.Escape.Red) + defer term.Write(term.Escape.Reset) + if !CheckString(user) { + io.WriteString(term, "Sorry, your username needs to be ASCII (without funky characters).\n") + return + } + pk := s.PublicKey() + if pk == nil { + io.WriteString(s, "Hi! Please come back with a public key :)\n") + return + } + term.Write(term.Escape.Reset) + + pkMarshalled := pk.Marshal() + pkHex := hex.EncodeToString(pkMarshalled) + pkHexShort := pkHex[len(pkHex)-7:] + + fmt.Fprintf(s, + "%sHello, %s%s%s (%s)%s\n", + term.Escape.Blue, + term.Escape.Cyan, user, + term.Escape.Blue, pkHexShort, + term.Escape.Reset) + + fmt.Fprintf(s, "%sRun %s`w`%s to write a message, %s`p`%s to print existing messages and %s`q`%s to quit.%s\n\n", + term.Escape.Blue, + term.Escape.Cyan, term.Escape.Blue, + term.Escape.Cyan, term.Escape.Blue, + term.Escape.Cyan, term.Escape.Blue, + term.Escape.Reset) + + for { + term.SetPrompt(fmt.Sprintf("%s>%s ", term.Escape.Blue, term.Escape.Reset)) + cmd, err := term.ReadLine() + if err == io.EOF { + cmd = "q" + err = nil + } + if err != nil { + return + } + + switch strings.TrimSpace(cmd) { + case "w": + term.SetPrompt(fmt.Sprintf("%sMessage:%s ", term.Escape.Blue, term.Escape.Reset)) + msg, err := term.ReadLine() + if err == io.EOF { + continue + } + fmt.Fprintf(term, "%sYour message:%s %s%s\n", term.Escape.Blue, term.Escape.White, msg, term.Escape.Reset) + if !CheckString(msg) { + fmt.Fprintf(term, "Sorry, your message contains non-ASCII or control characters.\n") + continue + } + term.SetPrompt(fmt.Sprintf( + "%sPublish? (%syes%s/%sno%s):%s ", + term.Escape.Blue, + term.Escape.Green, term.Escape.Blue, + term.Escape.Green, term.Escape.Blue, + term.Escape.Reset, + )) + cmd, _ := term.ReadLine() + if strings.ToLower(strings.TrimSpace(cmd)) == "yes" { + m.Add(Message{ + Content: msg, + Username: user, + PK: pk, + PKShort: pkHexShort, + Timestamp: time.Now(), + }) + fmt.Fprintf(term, "%sPublished!%s\n", term.Escape.Green, term.Escape.Reset) + } else { + fmt.Fprintf(term, "%sOkay, not publishing.%s\n", term.Escape.Blue, term.Escape.Reset) + } + case "p": + for _, msg := range m.List() { + fmt.Fprintf(term, + "%s%s <%s%s%s (%s)%s> %s%s%s\n", + term.Escape.Blue, + msg.Timestamp.Format("2006-01-02 15:04:05 MST"), + term.Escape.Cyan, msg.Username, term.Escape.Blue, + msg.PKShort, term.Escape.Blue, + term.Escape.White, msg.Content, term.Escape.Reset) + } + case "q": + fmt.Fprintf(term, "Bye!\n") + return + default: + fmt.Fprintln(term, "Unknown command.") + } + } + }) + + log.Fatal(ssh.ListenAndServe(":2222", nil, pkAuth)) +} diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..233bff8 --- /dev/null +++ b/main_test.go @@ -0,0 +1,38 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAdd(t *testing.T) { + ml := NewMessageList(3) + ml.Add(Message{Content: "1"}) + assert.Len(t, ml.list, 1) + assert.Equal(t, ml.list[0].Content, "1") + ml.Add(Message{Content: "2"}) + assert.Len(t, ml.list, 2) + assert.Equal(t, ml.list[0].Content, "1") + assert.Equal(t, ml.list[1].Content, "2") + ml.Add(Message{Content: "3"}) + assert.Len(t, ml.list, 3) + assert.Equal(t, ml.list[0].Content, "1") + assert.Equal(t, ml.list[1].Content, "2") + assert.Equal(t, ml.list[2].Content, "3") + ml.Add(Message{Content: "4"}) + assert.Len(t, ml.list, 3) + assert.Equal(t, ml.list[0].Content, "2") + assert.Equal(t, ml.list[1].Content, "3") + assert.Equal(t, ml.list[2].Content, "4") + ml.Add(Message{Content: "5"}) + assert.Len(t, ml.list, 3) + assert.Equal(t, ml.list[0].Content, "3") + assert.Equal(t, ml.list[1].Content, "4") + assert.Equal(t, ml.list[2].Content, "5") + ml.Add(Message{Content: "6"}) + assert.Len(t, ml.list, 3) + assert.Equal(t, ml.list[0].Content, "4") + assert.Equal(t, ml.list[1].Content, "5") + assert.Equal(t, ml.list[2].Content, "6") +}