diff --git a/.gitignore b/.gitignore index 49fb1f2..b556bae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,6 @@ .envrc result example.json -go.sum -go.mod ./gokill output.md thoughts.md diff --git a/actions/actions.go b/actions/actions.go index 5e9939c..616d4fd 100644 --- a/actions/actions.go +++ b/actions/actions.go @@ -119,11 +119,12 @@ func NewAction(config []internal.ActionConfig) (Action, error) { func GetAllActions() []DocumentedAction { return []DocumentedAction{ - Printer{}, - TimeOut{}, Command{}, + Printer{}, ShellScript{}, Shutdown{}, + SendMatrix{}, + TimeOut{}, } } diff --git a/actions/send_matrix.go b/actions/send_matrix.go new file mode 100644 index 0000000..ac9848b --- /dev/null +++ b/actions/send_matrix.go @@ -0,0 +1,226 @@ +package actions + +import ( + "fmt" + "encoding/json" + + "context" + "errors" + "sync" + "time" + + _ "github.com/mattn/go-sqlite3" + + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/id" + "maunium.net/go/mautrix/crypto/cryptohelper" + + "unknown.com/gokill/internal" +) + +type SendMatrix struct { + Homeserver string `json:"homeserver"` + Username string `json:"username"` + Password string `json:"password"` + Database string `json:"database"` + RoomId string `json:"roomId"` + Message string `json:"message"` + TestMessage string `json:"testMessage"` + ActionChan ActionResultChan +} + +func (s SendMatrix) sendMessage(message string) error { + client, err := mautrix.NewClient(s.Homeserver, "", "") + if err != nil { + return err + } + + cryptoHelper, err := cryptohelper.NewCryptoHelper(client, []byte("meow"), s.Database) + if err != nil { + return err + } + + cryptoHelper.LoginAs = &mautrix.ReqLogin{ + Type: mautrix.AuthTypePassword, + Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: s.Username}, + Password: s.Password, + } + + err = cryptoHelper.Init() + if err != nil { + return err + } + + client.Crypto = cryptoHelper + + fmt.Println("Matrix Client Now running") + syncCtx, cancelSync := context.WithCancel(context.Background()) + var syncStopWait sync.WaitGroup + syncStopWait.Add(1) + + go func() { + err = client.SyncWithContext(syncCtx) + defer syncStopWait.Done() + if err != nil && !errors.Is(err, context.Canceled) { + return + } + }() + + time.Sleep(5 * time.Second) + resp, err := client.SendText(id.RoomID(s.RoomId), message) + + if err != nil { + return fmt.Errorf("Failed to send event") + } else { + fmt.Println("Matrix Client: Message sent") + fmt.Printf("Matrix Client: event_id: %s", resp.EventID.String()) + } + + cancelSync() + syncStopWait.Wait() + err = cryptoHelper.Close() + if err != nil { + return fmt.Errorf("Error closing database") + } + + return nil +} + +func (s SendMatrix) DryExecute() { + fmt.Println("SendMatrix: Trying to send test message") + err := s.sendMessage(s.TestMessage) + + if err != nil { + fmt.Println("SendMatrix: failed to send test message") + } + + s.ActionChan <- err +} + +func (s SendMatrix) Execute() { + fmt.Println("SendMatrix: Trying to send test message") + err := s.sendMessage(s.Message) + + if err != nil { + fmt.Println("SendMatrix: failed to send test message") + } + + s.ActionChan <- err +} + +func CreateSendMatrix(config internal.ActionConfig, c ActionResultChan) (SendMatrix, error) { + result := SendMatrix{} + + err := json.Unmarshal(config.Options, &result) + + if err != nil { + return SendMatrix{}, err + } + + if result.Homeserver == "" { + return SendMatrix{}, internal.OptionMissingError{"homeserver"} + } + + if result.Username == "" { + return SendMatrix{}, internal.OptionMissingError{"username"} + } + + if result.Password == "" { + return SendMatrix{}, internal.OptionMissingError{"password"} + } + + if result.Database == "" { + return SendMatrix{}, internal.OptionMissingError{"database"} + } + + if result.RoomId == "" { + return SendMatrix{}, internal.OptionMissingError{"roomId"} + } + + if result.Message == "" { + return SendMatrix{}, internal.OptionMissingError{"message"} + } + + if result.TestMessage == "" { + return SendMatrix{}, internal.OptionMissingError{"testMessage"} + } + + result.ActionChan = c + return result, nil +} + +func (s SendMatrix) Create(config internal.ActionConfig, c ActionResultChan) (Action, error) { + return CreateSendMatrix(config, c) +} + +func (p SendMatrix) GetName() string { + return "SendMatrix" +} + +func (p SendMatrix) GetDescription() string { + return "Sends a message to a given room. The user needs to be part of that room already." +} + +func (p SendMatrix) GetExample() string { + return ` + { + "type": "SendMatrix", + "options": { + "homeserver": "matrix.org", + "username": "testuser", + "password": "super-secret", + "database": "/etc/gokill/matrix.db", + "roomId": "!Balrthajskensaw:matrix.org", + "message": "attention, intruders got my device!", + "testMessage": "this is just a test, no worries" + } + } + ` +} + +func (p SendMatrix) GetOptions() []internal.ConfigOption { + return []internal.ConfigOption{ + { + Name: "homeserver", + Type: "string", + Description: "homeserver address.", + Default: "", + }, + { + Name: "username", + Type: "string", + Description: "username (localpart, wihout homeserver address)", + Default: "", + }, + { + Name: "password", + Type: "string", + Description: "password in clear text", + Default: "", + }, + { + Name: "database", + Type: "string", + Description: "path to database file, will be created if not existing. this is necessary to sync keys for encryption.", + Default: "", + }, + { + Name: "roomId", + Type: "string", + Description: "", + Default: "", + }, + { + Name: "message", + Type: "string", + Description: "actual message that should be sent", + Default: "", + }, + { + Name: "testMessage", + Type: "string", + Description: "message sent during test run", + Default: "", + }, + } +} diff --git a/flake.nix b/flake.nix index 5ce8e9f..f5a34c0 100644 --- a/flake.nix +++ b/flake.nix @@ -15,15 +15,20 @@ go gotools mdbook + olm ]; }; packages.x86_64-linux.gokill = nixpkgs.legacyPackages.x86_64-linux.buildGoModule rec { pname = "gokill"; version = "1.0"; - vendorHash = "sha256-aKEOMeW9QVSLsSuHV4b1khqM0rRrMjJ6Eu5RjY+6V8k="; + vendorHash = "sha256-MVIvXxASUO33Ca34ruIz4S0QDJcW2unaG4+Zo73g/9o="; src = ./.; + buildInputs = [ + pkgs.olm + ]; + postInstall = '' ''; }; @@ -31,10 +36,14 @@ packages.x86_64-linux.gokill-docbuilder = nixpkgs.legacyPackages.x86_64-linux.buildGoModule rec { pname = "docbuilder"; version = "1.0"; - vendorHash = null; + vendorHash = "sha256-MVIvXxASUO33Ca34ruIz4S0QDJcW2unaG4+Zo73g/9o="; buildFLags = "-o . $dest/cmd/gokill/docbuilder"; src = ./.; + buildInputs = [ + pkgs.olm + ]; + postInstall = '' ''; }; diff --git a/go.mod b/go.mod index aa59491..ff69295 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,24 @@ module unknown.com/gokill go 1.21.3 + +require ( + github.com/mattn/go-sqlite3 v1.14.17 + maunium.net/go/mautrix v0.16.1 +) + +require ( + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/rs/zerolog v1.30.0 // indirect + github.com/tidwall/gjson v1.16.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/sjson v1.2.5 // indirect + go.mau.fi/util v0.1.0 // indirect + golang.org/x/crypto v0.13.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/sys v0.12.0 // indirect + maunium.net/go/maulogger/v2 v2.4.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4816867 --- /dev/null +++ b/go.sum @@ -0,0 +1,47 @@ +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= +github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +go.mau.fi/util v0.1.0 h1:BwIFWIOEeO7lsiI2eWKFkWTfc5yQmoe+0FYyOFVyaoE= +go.mau.fi/util v0.1.0/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8= +maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho= +maunium.net/go/mautrix v0.16.1 h1:Wb3CvOCe8A/NLsFeZYxKrgXKiqeZUQEBD1zqm7n/kWk= +maunium.net/go/mautrix v0.16.1/go.mod h1:2Jf15tulVtr6LxoiRL4smRXwpkGWUNfBFhwh/aXDBuk= diff --git a/triggers/triggers.go b/triggers/triggers.go index be9e56f..9fe7247 100644 --- a/triggers/triggers.go +++ b/triggers/triggers.go @@ -28,8 +28,8 @@ func NewTrigger(config internal.KillSwitchConfig) (Trigger, error) { func GetAllTriggers() []DocumentedTrigger { return []DocumentedTrigger{ - TimeOut{}, EthernetDisconnect{}, + TimeOut{}, UsbDisconnect{}, } }