summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Pointner <equinox@helsinki.at>2015-12-25 21:13:49 (GMT)
committerChristian Pointner <equinox@helsinki.at>2015-12-25 21:13:55 (GMT)
commit9ad2ba0bbeca95224742aaa6860a2615ffb68a27 (patch)
tree3b0f6d41d1512a981ad3ccd2b215d8bc9ee19fa3 /src
parentde51aad67a1a63f1e6e00cc60d2b4e86bf2bbcbe (diff)
telnet control interface now uses spreadspace/telgo
Diffstat (limited to 'src')
-rw-r--r--src/helsinki.at/rhimport/importer.go2
-rw-r--r--src/helsinki.at/rhimport/session_store.go1
-rw-r--r--src/helsinki.at/rhimportd/ctrlTelnet.go650
3 files changed, 182 insertions, 471 deletions
diff --git a/src/helsinki.at/rhimport/importer.go b/src/helsinki.at/rhimport/importer.go
index 3ee94cd..7ce4033 100644
--- a/src/helsinki.at/rhimport/importer.go
+++ b/src/helsinki.at/rhimport/importer.go
@@ -119,7 +119,7 @@ func (ctx *ImportContext) SanityCheck() error {
return err
}
if !ismusic {
- return fmt.Errorf("supplied GroupName is not a music pool")
+ return fmt.Errorf("supplied GroupName '%s' is not a music pool", ctx.GroupName)
}
if ctx.Cart != 0 || ctx.Cut != 0 {
return fmt.Errorf("Cart and Cut must not be supplied when importing into a music group")
diff --git a/src/helsinki.at/rhimport/session_store.go b/src/helsinki.at/rhimport/session_store.go
index 505bd75..2aabc44 100644
--- a/src/helsinki.at/rhimport/session_store.go
+++ b/src/helsinki.at/rhimport/session_store.go
@@ -79,6 +79,7 @@ type SessionStore struct {
}
func (self *SessionStore) new(ctx *ImportContext) (resp newSessionResponse) {
+ // TODO: for untrusted interfaces we need to check Username and PassWord!!!!
b := uuid.NewV4().Bytes()
resp.id = strings.ToLower(strings.TrimRight(base32.StdEncoding.EncodeToString(b), "="))
if _, exists := self.store[ctx.UserName]; !exists {
diff --git a/src/helsinki.at/rhimportd/ctrlTelnet.go b/src/helsinki.at/rhimportd/ctrlTelnet.go
index 55701cd..f578f54 100644
--- a/src/helsinki.at/rhimportd/ctrlTelnet.go
+++ b/src/helsinki.at/rhimportd/ctrlTelnet.go
@@ -25,558 +25,268 @@
package main
import (
- "bufio"
- "bytes"
"fmt"
+ "github.com/spreadspace/telgo"
"helsinki.at/rhimport"
- "net"
"net/http"
"strconv"
"strings"
)
-const (
- prompt = "> "
- EOT = byte(4)
- IP = byte(244)
- WILL = byte(251)
- WONT = byte(252)
- DO = byte(253)
- DONT = byte(254)
- IAC = byte(255)
-)
-
-type TelnetClient struct {
- conn net.Conn
- scanner *bufio.Scanner
- writer *bufio.Writer
- conf *rhimport.Config
- rddb *rhimport.RdDbChan
- ctx *rhimport.ImportContext
- iacout chan []byte
-}
-
-func (c *TelnetClient) write_string(text string) {
- defer c.writer.Flush()
-
- data := []byte(text)
- for {
- idx := bytes.IndexByte(data, IAC)
- if idx >= 0 {
- c.writer.Write(data[:idx+1])
- c.writer.WriteByte(IAC)
- data = data[idx+1:]
- } else {
- c.writer.Write(data)
- return
- }
- }
-}
-
-func (c *TelnetClient) say(format string, a ...interface{}) {
- c.write_string(fmt.Sprintf(format, a...) + "\n")
+func handle_cmd_quit(c *telgo.TelnetClient, args []string, conf *rhimport.Config, rddb *rhimport.RdDbChan) bool {
+ return true
}
-func (c *TelnetClient) handle_cmd_help(args []string) {
+func handle_cmd_help(c *telgo.TelnetClient, args []string, conf *rhimport.Config, rddb *rhimport.RdDbChan) bool {
switch len(args) {
- case 1:
- switch args[0] {
+ case 2:
+ switch args[1] {
case "quit":
- c.say("usage: quit")
- c.say(" terminates the client connection. You may also use Ctrl-D to do this.")
- return
+ c.Sayln("usage: quit")
+ c.Sayln(" terminates the client connection. You may also use Ctrl-D to do this.")
+ return false
case "help":
- c.say("usage: help [ <cmd> ]")
- c.say(" prints command overview or detailed info to <cmd>.")
- return
+ c.Sayln("usage: help [ <cmd> ]")
+ c.Sayln(" prints command overview or detailed info to <cmd>.")
+ return false
case "set":
- c.say("usage: set <param> <value>")
- c.say(" this sets the import parameter <param> to <value>.")
- c.say("")
- c.say(" available parameters:")
- c.say(" UserName string username to use for rdxport interface")
- c.say(" Password string password to use for rdxport interface")
- c.say(" SourceUri string uri to the file to import")
- c.say(" ShowId uint the RHRD show id to import to")
- c.say(" ClearShowCarts bool clear all show-carts before importing?")
- c.say(" GroupName string name of music-pool group to import to")
- c.say(" Cart uint cart to import to")
- c.say(" ClearCart bool remove/add cart before import")
- c.say(" Cut uint cut to import to")
- c.say(" Channels uint number of audio channels (default: %v)", c.conf.ImportParamDefaults.Channels)
- c.say(" NormalizationLevel int normalization level in dB (default: %v)", c.conf.ImportParamDefaults.NormalizationLevel)
- c.say(" AutotrimLevel int autotrim level in dB (default: %v)", c.conf.ImportParamDefaults.AutotrimLevel)
- c.say(" UseMetaData bool extract meta data from file (default: %v)", c.conf.ImportParamDefaults.UseMetaData)
- c.say("")
- c.say(" UserName, Password and SourceUri are mandatory parameters.")
- c.say("")
- c.say(" If ShowId is supplied GroupName, Channels, NomalizationLevel, AutorimLevel,")
- c.say(" UseMetaData and Cut will be ignored. The values from the shows' dropbox will")
- c.say(" be used instead. Cart may be specified but must point to an empty cart within")
- c.say(" that show. If ClearCut is true the specified cart will get deleted before")
- c.say(" importing. If Cart is 0 the next free cart in the show will be used. Show")
- c.say(" carts will always be imported into cut 1.")
- c.say("")
- c.say(" If GroupName is supplied Channels, NomalizationLevel, AutorimLevel,")
- c.say(" UseMetaData, Cut, Cart and ClearCart will be ignored. The values from")
- c.say(" the music pools' dropbox will be used instead. The file will always be")
- c.say(" imported into cut 1 of the first free cart within the music pool.")
- c.say("")
- c.say(" If ShowId and GroupName are omitted a Cart must be specified. Cut may be")
- c.say(" supplied in which case both cart and cut must already exist. The import will")
- c.say(" then replace the contents of the current data stored in Cart/Cut. If only Cart")
- c.say(" and no Cut is supplied and ClearCut is false the file will either get imported")
- c.say(" into the next cut of an existing cart or the cart will be created and the file")
- c.say(" will be imported into cut 1 of this cart.")
- c.say("")
- c.say(" In case of an error carts/cuts which might got created will be removed. Carts")
- c.say(" which got deleted because of ClearShowCarts or ClearCart are however gone for")
- c.say(" good.")
- return
+ c.Sayln("usage: set <param> <value>")
+ c.Sayln(" this sets the import parameter <param> to <value>.")
+ c.Sayln("")
+ c.Sayln(" available parameters:")
+ c.Sayln(" UserName string username to use for rdxport interface")
+ c.Sayln(" Password string password to use for rdxport interface")
+ c.Sayln(" SourceUri string uri to the file to import")
+ c.Sayln(" ShowId uint the RHRD show id to import to")
+ c.Sayln(" ClearShowCarts bool clear all show-carts before importing?")
+ c.Sayln(" GroupName string name of music-pool group to import to")
+ c.Sayln(" Cart uint cart to import to")
+ c.Sayln(" ClearCart bool remove/add cart before import")
+ c.Sayln(" Cut uint cut to import to")
+ c.Sayln(" Channels uint number of audio channels (default: %v)", conf.ImportParamDefaults.Channels)
+ c.Sayln(" NormalizationLevel int normalization level in dB (default: %v)", conf.ImportParamDefaults.NormalizationLevel)
+ c.Sayln(" AutotrimLevel int autotrim level in dB (default: %v)", conf.ImportParamDefaults.AutotrimLevel)
+ c.Sayln(" UseMetaData bool extract meta data from file (default: %v)", conf.ImportParamDefaults.UseMetaData)
+ c.Sayln("")
+ c.Sayln(" UserName, Password and SourceUri are mandatory parameters.")
+ c.Sayln("")
+ c.Sayln(" If ShowId is supplied GroupName, Channels, NomalizationLevel, AutorimLevel,")
+ c.Sayln(" UseMetaData and Cut will be ignored. The values from the shows' dropbox will")
+ c.Sayln(" be used instead. Cart may be specified but must point to an empty cart within")
+ c.Sayln(" that show. If ClearCut is true the specified cart will get deleted before")
+ c.Sayln(" importing. If Cart is 0 the next free cart in the show will be used. Show")
+ c.Sayln(" carts will always be imported into cut 1.")
+ c.Sayln("")
+ c.Sayln(" If GroupName is supplied Channels, NomalizationLevel, AutorimLevel,")
+ c.Sayln(" UseMetaData, Cut, Cart and ClearCart will be ignored. The values from")
+ c.Sayln(" the music pools' dropbox will be used instead. The file will always be")
+ c.Sayln(" imported into cut 1 of the first free cart within the music pool.")
+ c.Sayln("")
+ c.Sayln(" If ShowId and GroupName are omitted a Cart must be specified. Cut may be")
+ c.Sayln(" supplied in which case both cart and cut must already exist. The import will")
+ c.Sayln(" then replace the contents of the current data stored in Cart/Cut. If only Cart")
+ c.Sayln(" and no Cut is supplied and ClearCut is false the file will either get imported")
+ c.Sayln(" into the next cut of an existing cart or the cart will be created and the file")
+ c.Sayln(" will be imported into cut 1 of this cart.")
+ c.Sayln("")
+ c.Sayln(" In case of an error carts/cuts which might got created will be removed. Carts")
+ c.Sayln(" which got deleted because of ClearShowCarts or ClearCart are however gone for")
+ c.Sayln(" good.")
+ return false
case "show":
- c.say("usage: show")
- c.say(" this prints the current values of all import parameters.")
- return
+ c.Sayln("usage: show")
+ c.Sayln(" this prints the current values of all import parameters.")
+ return false
case "reset":
- c.say("usage: reset")
- c.say(" this resets all import parameters to default values.")
- return
+ c.Sayln("usage: reset")
+ c.Sayln(" this resets all import parameters to default values.")
+ return false
case "run":
- c.say("usage: run")
- c.say(" this starts the fetch/import process according to the current")
- c.say(" import parameters.")
- return
+ c.Sayln("usage: run")
+ c.Sayln(" this starts the fetch/import process according to the current")
+ c.Sayln(" import parameters.")
+ return false
}
fallthrough
default:
- c.say("usage: <cmd> [ [ <arg1> ] ... ]")
- c.say(" available commands:")
- c.say(" quit close connection (or use Ctrl-D)")
- c.say(" help [ <cmd> ] print this, or help for specific command")
- c.say(" set <param> <value> sets parameter <param> on current import context")
- c.say(" show shows current import context")
- c.say(" reset resets current import context")
- c.say(" run runs fetch/import using current import context")
- }
+ c.Sayln("usage: <cmd> [ [ <arg1> ] ... ]")
+ c.Sayln(" available commands:")
+ c.Sayln(" quit close connection (or use Ctrl-D)")
+ c.Sayln(" help [ <cmd> ] print this, or help for specific command")
+ c.Sayln(" set <param> <value> sets parameter <param> on current import context")
+ c.Sayln(" show shows current import context")
+ c.Sayln(" reset resets current import context")
+ c.Sayln(" run runs fetch/import using current import context")
+ }
+ return false
}
-func (c *TelnetClient) handle_cmd_set_string(param *string, val string) {
- if val == "\"\"" || val == "''" {
- *param = ""
- } else {
- *param = val
- }
-}
-
-func (c *TelnetClient) handle_cmd_set_int(param *int, val string) {
+func handle_cmd_set_int(c *telgo.TelnetClient, param *int, val string) {
if vint, err := strconv.ParseInt(val, 10, 32); err != nil {
- c.say("invalid value (must be an integer)")
+ c.Sayln("invalid value (must be an integer)")
} else {
*param = int(vint)
}
}
-func (c *TelnetClient) handle_cmd_set_uint(param *uint, val string) {
+func handle_cmd_set_uint(c *telgo.TelnetClient, param *uint, val string) {
if vuint, err := strconv.ParseUint(val, 10, 32); err != nil {
- c.say("invalid value (must be a positive integer)")
+ c.Sayln("invalid value (must be a positive integer)")
} else {
*param = uint(vuint)
}
}
-func (c *TelnetClient) handle_cmd_set_bool(param *bool, val string) {
+func handle_cmd_set_bool(c *telgo.TelnetClient, param *bool, val string) {
if vbool, err := strconv.ParseBool(val); err != nil {
- c.say("invalid value (must be true or false)")
+ c.Sayln("invalid value (must be true or false)")
} else {
*param = vbool
}
}
-func (c *TelnetClient) handle_cmd_set(args []string) {
- if len(args) != 2 {
- c.say("wrong number of arguments")
- return
+func handle_cmd_set(c *telgo.TelnetClient, args []string, conf *rhimport.Config, rddb *rhimport.RdDbChan) bool {
+ if len(args) != 3 {
+ c.Sayln("wrong number of arguments")
+ return false
}
- if c.ctx == nil {
- c.ctx = rhimport.NewImportContext(c.conf, c.rddb, "")
- c.ctx.Trusted = false
+
+ var ctx *rhimport.ImportContext
+ if c.UserData == nil {
+ c.UserData = rhimport.NewImportContext(conf, rddb, "")
+ ctx = c.UserData.(*rhimport.ImportContext)
+ ctx.Trusted = false
+ } else {
+ ctx = c.UserData.(*rhimport.ImportContext)
}
- switch strings.ToLower(args[0]) {
+ switch strings.ToLower(args[1]) {
case "username":
- c.handle_cmd_set_string(&c.ctx.UserName, args[1])
+ ctx.UserName = args[2]
case "password":
- c.handle_cmd_set_string(&c.ctx.Password, args[1])
+ ctx.Password = args[2]
case "sourceuri":
- c.handle_cmd_set_string(&c.ctx.SourceUri, args[1])
+ ctx.SourceUri = args[2]
case "showid":
- c.handle_cmd_set_uint(&c.ctx.ShowId, args[1])
+ handle_cmd_set_uint(c, &ctx.ShowId, args[2])
case "clearshowcarts":
- c.handle_cmd_set_bool(&c.ctx.ClearShowCarts, args[1])
+ handle_cmd_set_bool(c, &ctx.ClearShowCarts, args[2])
case "groupname":
- c.handle_cmd_set_string(&c.ctx.GroupName, args[1])
+ ctx.GroupName = args[2]
case "cart":
- c.handle_cmd_set_uint(&c.ctx.Cart, args[1])
+ handle_cmd_set_uint(c, &ctx.Cart, args[2])
case "clearcart":
- c.handle_cmd_set_bool(&c.ctx.ClearCart, args[1])
+ handle_cmd_set_bool(c, &ctx.ClearCart, args[2])
case "cut":
- c.handle_cmd_set_uint(&c.ctx.Cut, args[1])
+ handle_cmd_set_uint(c, &ctx.Cut, args[2])
case "channels":
- c.handle_cmd_set_uint(&c.ctx.Channels, args[1])
+ handle_cmd_set_uint(c, &ctx.Channels, args[2])
case "normalizationlevel":
- c.handle_cmd_set_int(&c.ctx.NormalizationLevel, args[1])
+ handle_cmd_set_int(c, &ctx.NormalizationLevel, args[2])
case "autotrimlevel":
- c.handle_cmd_set_int(&c.ctx.AutotrimLevel, args[1])
+ handle_cmd_set_int(c, &ctx.AutotrimLevel, args[2])
case "usemetadata":
- c.handle_cmd_set_bool(&c.ctx.UseMetaData, args[1])
+ handle_cmd_set_bool(c, &ctx.UseMetaData, args[2])
default:
- c.say("unknown parameter, use 'help set' for a list of available parameters")
+ c.Sayln("unknown parameter, use 'help set' for a list of available parameters")
}
+ return false
}
-func (c *TelnetClient) handle_cmd_reset(args []string) {
- if len(args) > 0 {
- c.say("too many arguments")
- return
+func handle_cmd_reset(c *telgo.TelnetClient, args []string, conf *rhimport.Config, rddb *rhimport.RdDbChan) bool {
+ if len(args) > 1 {
+ c.Sayln("too many arguments")
+ return false
}
- c.ctx = nil
+
+ c.UserData = nil
+ return false
}
-func (c *TelnetClient) handle_cmd_show(args []string) {
- if len(args) > 0 {
- c.say("too many arguments")
- return
- }
- if c.ctx != nil {
- c.say(" UserName: %v", c.ctx.UserName)
- c.say(" Password: %v", c.ctx.Password)
- c.say(" SourceUri: %v", c.ctx.SourceUri)
- c.say(" ShowId: %v", c.ctx.ShowId)
- c.say(" ClearShowCarts: %v", c.ctx.ClearShowCarts)
- c.say(" GroupName: %v", c.ctx.GroupName)
- c.say(" Cart: %v", c.ctx.Cart)
- c.say(" ClearCart: %v", c.ctx.ClearCart)
- c.say(" Cut: %v", c.ctx.Cut)
- c.say(" Channels: %v", c.ctx.Channels)
- c.say(" NormalizationLevel: %v", c.ctx.NormalizationLevel)
- c.say(" AutotrimLevel: %v", c.ctx.AutotrimLevel)
- c.say(" UseMetaData: %v", c.ctx.UseMetaData)
+func handle_cmd_show(c *telgo.TelnetClient, args []string, conf *rhimport.Config, rddb *rhimport.RdDbChan) bool {
+ if len(args) > 1 {
+ c.Sayln("too many arguments")
+ return false
+ }
+
+ if c.UserData != nil {
+ ctx := c.UserData.(*rhimport.ImportContext)
+ c.Sayln(" UserName: %q", ctx.UserName)
+ c.Sayln(" Password: %q", ctx.Password)
+ c.Sayln(" SourceUri: %q", ctx.SourceUri)
+ c.Sayln(" ShowId: %v", ctx.ShowId)
+ c.Sayln(" ClearShowCarts: %v", ctx.ClearShowCarts)
+ c.Sayln(" GroupName: %q", ctx.GroupName)
+ c.Sayln(" Cart: %v", ctx.Cart)
+ c.Sayln(" ClearCart: %v", ctx.ClearCart)
+ c.Sayln(" Cut: %v", ctx.Cut)
+ c.Sayln(" Channels: %v", ctx.Channels)
+ c.Sayln(" NormalizationLevel: %v", ctx.NormalizationLevel)
+ c.Sayln(" AutotrimLevel: %v", ctx.AutotrimLevel)
+ c.Sayln(" UseMetaData: %v", ctx.UseMetaData)
} else {
- c.say("context is empty")
+ c.Sayln("context is empty")
}
+ return false
}
func telnet_progress_callback(step int, step_name string, progress float64, userdata interface{}) bool {
- out := userdata.(chan<- string)
- out <- fmt.Sprintf("%s: %3.2f%%\r", step_name, progress*100)
+ c := userdata.(*telgo.TelnetClient)
+ c.Say("%s: %3.2f%%\r", step_name, progress*100)
return true
}
-func telnet_cmd_run(ctx rhimport.ImportContext, out chan<- string) {
- defer close(out)
-
- out <- fmt.Sprintf("fetching file from '%s'\n", ctx.SourceUri)
- if res, err := rhimport.FetchFile(&ctx); err != nil {
- out <- fmt.Sprintf("fetch file error: %s\n", err)
- return
- } else if res.ResponseCode != http.StatusOK {
- out <- fmt.Sprintf("fetch file error: %s\n", res.ErrorString)
- return
+func handle_cmd_run(c *telgo.TelnetClient, args []string, conf *rhimport.Config, rddb *rhimport.RdDbChan) bool {
+ if c.UserData == nil {
+ c.Sayln("context is empty please set at least one option")
+ return false
}
-
- out <- fmt.Sprintf("\nimporting file '%s'\n", ctx.SourceFile)
- if res, err := rhimport.ImportFile(&ctx); err != nil {
- out <- fmt.Sprintf("\nimport file error: %s\n", err)
- return
- } else {
- if res.ResponseCode == http.StatusOK {
- out <- fmt.Sprintf("\nFile got succesfully imported into Cart/Cut %d/%d\n", res.Cart, res.Cut)
- } else {
- out <- fmt.Sprintf("\nFileimport has failed (Cart/Cut %d/%d): %s\n", res.Cart, res.Cut, res.ErrorString)
- }
- }
-}
-
-func (c *TelnetClient) handle_cmd_run(args []string, cancel <-chan bool) {
- if c.ctx == nil {
- c.say("context is empty please set at least one option")
- return
- }
-
- if err := c.ctx.SanityCheck(); err != nil {
- c.say("sanity check for import context returned: %s", err)
- return
- }
- select {
- case <-cancel: // consume potentially pending cancel request
- default:
+ ctx := c.UserData.(*rhimport.ImportContext)
+ if err := ctx.SanityCheck(); err != nil {
+ c.Sayln("sanity check for import context returned: %s", err)
+ return false
}
- stdout := make(chan string)
- c.ctx.ProgressCallBack = telnet_progress_callback
- c.ctx.ProgressCallBackData = (chan<- string)(stdout)
- c.ctx.Cancel = cancel
- go telnet_cmd_run(*c.ctx, stdout)
- for str := range stdout {
- c.write_string(str)
- }
-}
+ ctx.ProgressCallBack = telnet_progress_callback
+ ctx.ProgressCallBackData = c
+ ctx.Cancel = c.Cancel
-func (c *TelnetClient) handle_cmd(cmdstr string, done chan<- bool, cancel <-chan bool) {
- cmdslice := strings.Fields(cmdstr)
- if len(cmdslice) == 0 || cmdslice[0] == "" {
- done <- false
- return
+ c.Sayln("fetching file from '%s'", ctx.SourceUri)
+ if res, err := rhimport.FetchFile(ctx); err != nil {
+ c.Sayln("fetch file error: %s", err)
+ return false
+ } else if res.ResponseCode != http.StatusOK {
+ c.Sayln("fetch file error: %s", res.ErrorString)
+ return false
}
- cmd := cmdslice[0]
- args := cmdslice[1:]
- if cmd == "quit" {
- done <- true
- return
- } else if cmd == "help" {
- c.handle_cmd_help(args)
- } else if cmd == "set" {
- c.handle_cmd_set(args)
- } else if cmd == "reset" {
- c.handle_cmd_reset(args)
- } else if cmd == "show" {
- c.handle_cmd_show(args)
- } else if cmd == "run" {
- c.handle_cmd_run(args, cancel)
+ c.Sayln("")
+ c.Sayln("importing file '%s'", ctx.SourceFile)
+ if res, err := rhimport.ImportFile(ctx); err != nil {
+ c.Sayln("")
+ c.Sayln("import file error: %s", err)
} else {
- c.say("unknown command '%s'", cmd)
- }
- done <- false
-}
-
-func handleIac(iac []byte, iacout chan<- []byte) {
- switch iac[1] {
- case WILL, WONT: // Don't accept any proposed options
- iac[1] = DONT
- case DO, DONT:
- iac[1] = WONT
- case IP:
- // pass this through to client.handle which will cancel the process
- default:
- rhdl.Printf("ignoring unimplemented telnet command: %X", iac[1])
- return
- }
- iacout <- iac
-}
-
-func dropCR(data []byte) []byte {
- if len(data) > 0 && data[len(data)-1] == '\r' {
- return data[0 : len(data)-1]
- }
- return data
-}
-
-func dropIAC(data []byte) []byte {
- var token []byte
- iiac := 0
- for {
- niiac := bytes.IndexByte(data[iiac:], IAC)
- if niiac >= 0 {
- token = append(token, data[iiac:iiac+niiac]...)
- iiac += niiac
- if (len(data) - iiac) < 2 {
- return token
- }
- switch data[iiac+1] {
- case DONT, DO, WONT, WILL:
- if (len(data) - iiac) < 3 {
- return token
- }
- iiac += 3
- case IAC:
- token = append(token, IAC)
- fallthrough
- default:
- iiac += 2
- }
- } else {
- token = append(token, data[iiac:]...)
- break
- }
- }
- return token
-}
-
-func compare_idx(a, b int) int {
- if a < 0 {
- a = int(^uint(0) >> 1)
- }
- if b < 0 {
- b = int(^uint(0) >> 1)
- }
- return a - b
-}
-
-func ScanLinesTelnet(data []byte, atEOF bool, iacout chan<- []byte, lastiiac *int) (advance int, token []byte, err error) {
- if atEOF && len(data) == 0 {
- return 0, nil, nil
- }
-
- inl := bytes.IndexByte(data, '\n') // index of first newline character
- ieot := bytes.IndexByte(data, EOT) // index of first End of Transmission
-
- iiac := *lastiiac
- for {
- niiac := bytes.IndexByte(data[iiac:], IAC) // index of first/next telnet IAC
- if niiac >= 0 {
- iiac += niiac
- } else {
- iiac = niiac
- }
-
- if inl >= 0 && compare_idx(inl, ieot) < 0 && compare_idx(inl, iiac) < 0 {
- *lastiiac = 0
- return inl + 1, dropCR(dropIAC(data[0:inl])), nil // found a complete line
- }
- if ieot >= 0 && compare_idx(ieot, iiac) < 0 {
- *lastiiac = 0
- return ieot + 1, data[ieot : ieot+1], nil // found a EOT (aka Ctrl-D)
- }
- if iiac >= 0 {
- l := 2
- if (len(data) - iiac) < 2 {
- return 0, nil, nil // data does not yet contain the telnet command code -> need more data
- }
- switch data[iiac+1] {
- case DONT, DO, WONT, WILL:
- if (len(data) - iiac) < 3 {
- return 0, nil, nil // this is a 3-byte command and data does not yet contain the option code -> need more data
- }
- l = 3
- case IAC:
- iiac += 2
- continue
- }
- handleIac(data[iiac:iiac+l], iacout)
- iiac += l
- *lastiiac = iiac
+ c.Sayln("")
+ if res.ResponseCode == http.StatusOK {
+ c.Sayln("File got succesfully imported into Cart/Cut %d/%d", res.Cart, res.Cut)
} else {
- break
- }
- }
- if atEOF {
- return len(data), dropCR(data), nil // allow last line to have no new line
- }
- return 0, nil, nil // we have found none of the escape codes -> need more data
-}
-
-func (c *TelnetClient) recv(in chan<- string) {
- defer close(in)
-
- for c.scanner.Scan() {
- b := c.scanner.Bytes()
- if len(b) > 0 && b[0] == EOT {
- rhdl.Printf("telnet-ctrl(%s): Ctrl-D received, closing", c.conn.RemoteAddr())
- return
- }
- in <- string(b)
- }
- if err := c.scanner.Err(); err != nil {
- rhdl.Printf("telnet-ctrl(%s): recv() error: %s", c.conn.RemoteAddr(), err)
- } else {
- rhdl.Printf("telnet-ctrl(%s): Connection closed by foreign host", c.conn.RemoteAddr())
- }
-}
-
-func (c *TelnetClient) handle() {
- defer c.conn.Close()
-
- in := make(chan string)
- go c.recv(in)
-
- done := make(chan bool)
- cancel := make(chan bool, 1)
- defer func() { // make sure to cancel possible running job when closing connection
- select {
- case cancel <- true:
- default:
- }
- }()
-
- var cmd_backlog []string
- busy := false
- c.write_string(prompt)
- for {
- select {
- case cmd, ok := <-in:
- if !ok {
- return
- }
- if len(cmd) > 0 {
- if !busy {
- go c.handle_cmd(cmd, done, cancel)
- busy = true
- } else {
- cmd_backlog = append(cmd_backlog, cmd)
- }
- } else {
- c.write_string(prompt)
- }
- case exit := <-done:
- if exit {
- return
- }
- c.write_string(prompt)
- if len(cmd_backlog) > 0 {
- go c.handle_cmd(cmd_backlog[0], done, cancel)
- cmd_backlog = cmd_backlog[1:]
- } else {
- busy = false
- }
- case iac := <-c.iacout:
- if iac[1] == IP {
- select {
- case cancel <- true:
- default: // process got canceled already
- }
- } else {
- c.writer.Write(iac)
- c.writer.Flush()
- }
+ c.Sayln("Fileimport has failed (Cart/Cut %d/%d): %s", res.Cart, res.Cut, res.ErrorString)
}
}
-}
-
-func newTelnetClient(conn net.Conn, conf *rhimport.Config, rddb *rhimport.RdDbChan) (c *TelnetClient) {
- rhl.Println("telnet-ctrl: new client from:", conn.RemoteAddr())
- c = &TelnetClient{}
- c.conn = conn
- c.scanner = bufio.NewScanner(conn)
- c.writer = bufio.NewWriter(conn)
- c.conf = conf
- c.rddb = rddb
- c.ctx = nil
- // the telnet split function needs some closures to handle OOB telnet commands
- c.iacout = make(chan []byte)
- lastiiac := 0
- c.scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
- return ScanLinesTelnet(data, atEOF, c.iacout, &lastiiac)
- })
- return c
+ return false
}
func StartControlTelnet(addr_s string, conf *rhimport.Config, rddb *rhimport.RdDbChan) {
- rhl.Println("telnet-ctrl: listening on", addr_s)
+ cmdlist := make(telgo.TelgoCmdList)
+ cmdlist["quit"] = func(c *telgo.TelnetClient, args []string) bool { return handle_cmd_quit(c, args, conf, rddb) }
+ cmdlist["help"] = func(c *telgo.TelnetClient, args []string) bool { return handle_cmd_help(c, args, conf, rddb) }
+ cmdlist["set"] = func(c *telgo.TelnetClient, args []string) bool { return handle_cmd_set(c, args, conf, rddb) }
+ cmdlist["reset"] = func(c *telgo.TelnetClient, args []string) bool { return handle_cmd_reset(c, args, conf, rddb) }
+ cmdlist["show"] = func(c *telgo.TelnetClient, args []string) bool { return handle_cmd_show(c, args, conf, rddb) }
+ cmdlist["run"] = func(c *telgo.TelnetClient, args []string) bool { return handle_cmd_run(c, args, conf, rddb) }
- server, err := net.Listen("tcp", addr_s)
- if err != nil {
- rhl.Println("telnet-ctrl: Listen() Error:", err)
- return
- }
-
- for {
- conn, err := server.Accept()
- if err != nil {
- rhl.Println("telnet-ctrl: Accept() Error:", err)
- return
- }
-
- c := newTelnetClient(conn, conf, rddb)
- go c.handle()
+ rhl.Println("telnet-ctrl: listening on", addr_s)
+ s := telgo.NewTelnetServer(addr_s, "rhimportd> ", cmdlist, nil)
+ if err := s.Run(); err != nil {
+ fmt.Printf("telnet server returned: %s", err)
}
}