From 21dc5fa6c6c843fcf263b6483d21bc4b284aad1c Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sat, 3 Feb 2024 04:24:22 -0500 Subject: [PATCH] Form support for transport Ad-Hoc commands with arguments --- Makefile | 2 +- telegabber.go | 2 +- telegram/commands.go | 21 ++++++---- xmpp/handlers.go | 99 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 95 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index c859606..309a27e 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ COMMIT := $(shell git rev-parse --short HEAD) TD_COMMIT := "5bbfc1cf5dab94f82e02f3430ded7241d4653551" -VERSION := "v1.9.1" +VERSION := "v1.10.0-dev" MAKEOPTS := "-j4" all: diff --git a/telegabber.go b/telegabber.go index f42e266..d39820d 100644 --- a/telegabber.go +++ b/telegabber.go @@ -16,7 +16,7 @@ import ( goxmpp "gosrc.io/xmpp" ) -var version string = "1.9.1" +var version string = "1.10.0-dev" var commit string var sm *goxmpp.StreamManager diff --git a/telegram/commands.go b/telegram/commands.go index 47438e0..4dafdf5 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -107,9 +107,9 @@ var transportConfigurationOptions = map[string]configurationOption{ } type command struct { - requiredArgs int - arguments []string - description string + RequiredArgs int + Arguments []string + Description string } type configurationOption struct { arguments string @@ -138,14 +138,21 @@ func GetCommands(typ CommandType) map[string]command { return commandMap } +// GetCommand obtains one command +func GetCommand(typ CommandType, cmd string) (command, bool) { + commands := GetCommands(typ) + command, ok := commands[cmd] + return command, ok +} + // CommandToHelpString builds a text description of a command func CommandToHelpString(name string, cmd command) string { var str strings.Builder str.WriteString("/") str.WriteString(name) - for i, arg := range cmd.arguments { - optional := i >= cmd.requiredArgs + for i, arg := range cmd.Arguments { + optional := i >= cmd.RequiredArgs str.WriteString(" ") if optional { str.WriteString("[") @@ -156,7 +163,7 @@ func CommandToHelpString(name string, cmd command) string { } } str.WriteString(" — ") - str.WriteString(cmd.description) + str.WriteString(cmd.Description) return str.String() } @@ -267,7 +274,7 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) string if !ok { return "Unknown command" } - if len(args) < command.requiredArgs { + if len(args) < command.RequiredArgs { return notEnoughArguments } diff --git a/xmpp/handlers.go b/xmpp/handlers.go index a062f0c..c71fc19 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/pkg/errors" "io" + "sort" "strconv" "strings" @@ -701,31 +702,89 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command log.Debugf("command: %#v", command) + bare, resource, ok := gateway.SplitJID(iq.From) + if !ok { + return + } + + var cmdString string if command.Action == "" || command.Action == stanza.CommandActionExecute { _, ok := toToID(iq.To) if !ok { - bare, resource, ok := gateway.SplitJID(iq.From) - if !ok { - return + cmd, ok := telegram.GetCommand(telegram.CommandTypeTransport, command.Node) + if ok && cmd.RequiredArgs > 0 { + var fields []*stanza.Field + for i, arg := range cmd.Arguments { + fields = append(fields, &stanza.Field{ + Var: strconv.FormatInt(int64(i), 10), + Label: arg, + }) + } + answer.Payload = &stanza.Command{ + SessionId: command.Node, + Node: command.Node, + Status: stanza.CommandStatusExecuting, + CommandElement: &stanza.Form{ + Title: command.Node, + Instructions: []string{cmd.Description}, + Fields: fields, + }, + } + } else { + cmdString = "/" + command.Node } - - session, ok := sessions[bare] - if !ok { - return - } - - response := session.ProcessTransportCommand("/" + command.Node, resource) - - answer.Payload = &stanza.Command{ - Node: command.Node, - Status: stanza.CommandStatusCompleted, - CommandElement: &stanza.Note{ - Text: response, - Type: stanza.CommandNoteTypeInfo, - }, - } - log.Debugf("command response: %#v", answer.Payload) } + } else if command.Action == stanza.CommandActionComplete { + _, ok := toToID(iq.To) + if !ok { + form, ok := command.CommandElement.(*stanza.Form) + if ok { + // just for the case the client messed the order somehow + sort.Slice(form.Fields, func(i int, j int) bool { + iField := form.Fields[i] + jField := form.Fields[j] + if iField != nil && jField != nil { + ii, iErr := strconv.ParseInt(iField.Var, 10, 64) + ji, jErr := strconv.ParseInt(jField.Var, 10, 64) + return iErr == nil && jErr == nil && ii < ji + } + return false + }) + + var cmd strings.Builder + cmd.WriteString("/") + cmd.WriteString(command.Node) + for _, field := range form.Fields { + cmd.WriteString(" ") + if len(field.ValuesList) > 0 { + cmd.WriteString(field.ValuesList[0]) + } + } + + cmdString = cmd.String() + } + } + } + + if cmdString != "" { + session, ok := sessions[bare] + if !ok { + return + } + + response := session.ProcessTransportCommand(cmdString, resource) + + answer.Payload = &stanza.Command{ + SessionId: command.Node, + Node: command.Node, + Status: stanza.CommandStatusCompleted, + CommandElement: &stanza.Note{ + Text: response, + Type: stanza.CommandNoteTypeInfo, + }, + } + + log.Debugf("command response: %#v", answer.Payload) } }