aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common')
-rw-r--r--lib/common/error.go16
-rw-r--r--lib/common/execPipeWs.go151
-rw-r--r--lib/common/filepath.go73
-rw-r--r--lib/common/httpclient.go257
4 files changed, 0 insertions, 497 deletions
diff --git a/lib/common/error.go b/lib/common/error.go
deleted file mode 100644
index 6873d82..0000000
--- a/lib/common/error.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package common
-
-import (
- "fmt"
-
- "github.com/gin-gonic/gin"
-)
-
-// APIError returns an uniform json formatted error
-func APIError(c *gin.Context, format string, args ...interface{}) {
- errMsg := fmt.Sprintf(format, args...)
- c.JSON(500, gin.H{
- "status": "error",
- "error": errMsg,
- })
-}
diff --git a/lib/common/execPipeWs.go b/lib/common/execPipeWs.go
deleted file mode 100644
index 9bb4517..0000000
--- a/lib/common/execPipeWs.go
+++ /dev/null
@@ -1,151 +0,0 @@
-package common
-
-import (
- "bufio"
- "fmt"
- "io"
- "os"
- "strings"
- "time"
-
- "syscall"
-
- "github.com/Sirupsen/logrus"
- "github.com/googollee/go-socket.io"
-)
-
-// EmitOutputCB is the function callback used to emit data
-type EmitOutputCB func(sid string, cmdID int, stdout, stderr string, data *map[string]interface{})
-
-// EmitExitCB is the function callback used to emit exit proc code
-type EmitExitCB func(sid string, cmdID int, code int, err error, data *map[string]interface{})
-
-// Inspired by :
-// https://github.com/gorilla/websocket/blob/master/examples/command/main.go
-
-// ExecPipeWs executes a command and redirect stdout/stderr into a WebSocket
-func ExecPipeWs(cmd []string, env []string, so *socketio.Socket, sid string, cmdID int,
- cmdExecTimeout int, log *logrus.Logger, eoCB EmitOutputCB, eeCB EmitExitCB, data *map[string]interface{}) error {
-
- outr, outw, err := os.Pipe()
- if err != nil {
- return fmt.Errorf("Pipe stdout error: " + err.Error())
- }
-
- // XXX - do we need to pipe stdin one day ?
- inr, inw, err := os.Pipe()
- if err != nil {
- outr.Close()
- outw.Close()
- return fmt.Errorf("Pipe stdin error: " + err.Error())
- }
-
- bashArgs := []string{"/bin/bash", "-c", strings.Join(cmd, " ")}
- proc, err := os.StartProcess("/bin/bash", bashArgs, &os.ProcAttr{
- Files: []*os.File{inr, outw, outw},
- Env: append(os.Environ(), env...),
- })
- if err != nil {
- outr.Close()
- outw.Close()
- inr.Close()
- inw.Close()
- return fmt.Errorf("Process start error: " + err.Error())
- }
-
- go func() {
- defer outr.Close()
- defer outw.Close()
- defer inr.Close()
- defer inw.Close()
-
- stdoutDone := make(chan struct{})
- go cmdPumpStdout(so, outr, stdoutDone, sid, cmdID, log, eoCB, data)
-
- // Blocking function that poll input or wait for end of process
- cmdPumpStdin(so, inw, proc, sid, cmdID, cmdExecTimeout, log, eeCB, data)
-
- // Some commands will exit when stdin is closed.
- inw.Close()
-
- defer outr.Close()
-
- if status, err := proc.Wait(); err == nil {
- // Other commands need a bonk on the head.
- if !status.Exited() {
- if err := proc.Signal(os.Interrupt); err != nil {
- log.Errorln("Proc interrupt:", err)
- }
-
- select {
- case <-stdoutDone:
- case <-time.After(time.Second):
- // A bigger bonk on the head.
- if err := proc.Signal(os.Kill); err != nil {
- log.Errorln("Proc term:", err)
- }
- <-stdoutDone
- }
- }
- }
- }()
-
- return nil
-}
-
-func cmdPumpStdin(so *socketio.Socket, w io.Writer, proc *os.Process,
- sid string, cmdID int, tmo int, log *logrus.Logger, exitFuncCB EmitExitCB,
- data *map[string]interface{}) {
- /* XXX - code to add to support stdin through WS
- for {
- _, message, err := so. ?? ReadMessage()
- if err != nil {
- break
- }
- message = append(message, '\n')
- if _, err := w.Write(message); err != nil {
- break
- }
- }
- */
-
- // Monitor process exit
- type DoneChan struct {
- status int
- err error
- }
- done := make(chan DoneChan, 1)
- go func() {
- status := 0
- sts, err := proc.Wait()
- if !sts.Success() {
- s := sts.Sys().(syscall.WaitStatus)
- status = s.ExitStatus()
- }
- done <- DoneChan{status, err}
- }()
-
- // Wait cmd complete
- select {
- case dC := <-done:
- exitFuncCB(sid, cmdID, dC.status, dC.err, data)
- case <-time.After(time.Duration(tmo) * time.Second):
- exitFuncCB(sid, cmdID, -99,
- fmt.Errorf("Exit Timeout for command ID %v", cmdID), data)
- }
-}
-
-func cmdPumpStdout(so *socketio.Socket, r io.Reader, done chan struct{},
- sid string, cmdID int, log *logrus.Logger, emitFuncCB EmitOutputCB, data *map[string]interface{}) {
- defer func() {
- }()
-
- sc := bufio.NewScanner(r)
- for sc.Scan() {
- emitFuncCB(sid, cmdID, string(sc.Bytes()), "", data)
- }
- if sc.Err() != nil {
- log.Errorln("scan:", sc.Err())
- }
- close(done)
-}
diff --git a/lib/common/filepath.go b/lib/common/filepath.go
deleted file mode 100644
index 8101c5a..0000000
--- a/lib/common/filepath.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package common
-
-import (
- "fmt"
- "os"
- "os/user"
- "path"
- "path/filepath"
- "regexp"
- "strings"
-)
-
-// Exists returns whether the given file or directory exists or not
-func Exists(path string) bool {
- _, err := os.Stat(path)
- if err == nil {
- return true
- }
- if os.IsNotExist(err) {
- return false
- }
- return true
-}
-
-// ResolveEnvVar Resolved environment variable regarding the syntax ${MYVAR}
-// or $MYVAR following by a slash or a backslash
-func ResolveEnvVar(s string) (string, error) {
- if s == "" {
- return s, nil
- }
-
- // Resolved tilde : ~/
- if len(s) > 2 && s[:2] == "~/" {
- if usr, err := user.Current(); err == nil {
- s = filepath.Join(usr.HomeDir, s[2:])
- }
- }
-
- // Resolved ${MYVAR}
- re := regexp.MustCompile("\\${([^}]+)}")
- vars := re.FindAllStringSubmatch(s, -1)
- res := s
- for _, v := range vars {
- val := os.Getenv(v[1])
- if val == "" {
- return res, fmt.Errorf("ERROR: %s env variable not defined", v[1])
- }
-
- rer := regexp.MustCompile("\\${" + v[1] + "}")
- res = rer.ReplaceAllString(res, val)
- }
-
- // Resolved $MYVAR following by a slash (or a backslash for Windows)
- // TODO
- //re := regexp.MustCompile("\\$([^\\/])+/")
-
- return path.Clean(res), nil
-}
-
-// PathNormalize
-func PathNormalize(p string) string {
- sep := string(filepath.Separator)
- if sep != "/" {
- return p
- }
- // Replace drive like C: by C/
- res := p
- if p[1:2] == ":" {
- res = p[0:1] + sep + p[2:]
- }
- res = strings.Replace(res, "\\", "/", -1)
- return filepath.Clean(res)
-}
diff --git a/lib/common/httpclient.go b/lib/common/httpclient.go
deleted file mode 100644
index 72132bf..0000000
--- a/lib/common/httpclient.go
+++ /dev/null
@@ -1,257 +0,0 @@
-package common
-
-import (
- "bytes"
- "crypto/tls"
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "net/http"
- "strings"
-
- "github.com/Sirupsen/logrus"
-)
-
-type HTTPClient struct {
- httpClient http.Client
- endpoint string
- apikey string
- username string
- password string
- id string
- csrf string
- conf HTTPClientConfig
- logger *logrus.Logger
-}
-
-type HTTPClientConfig struct {
- URLPrefix string
- HeaderAPIKeyName string
- Apikey string
- HeaderClientKeyName string
- CsrfDisable bool
-}
-
-const (
- logError = 1
- logWarning = 2
- logInfo = 3
- logDebug = 4
-)
-
-// Inspired by syncthing/cmd/cli
-
-const insecure = false
-
-// HTTPNewClient creates a new HTTP client to deal with Syncthing
-func HTTPNewClient(baseURL string, cfg HTTPClientConfig) (*HTTPClient, error) {
-
- // Create w new Http client
- httpClient := http.Client{
- Transport: &http.Transport{
- TLSClientConfig: &tls.Config{
- InsecureSkipVerify: insecure,
- },
- },
- }
- client := HTTPClient{
- httpClient: httpClient,
- endpoint: baseURL,
- apikey: cfg.Apikey,
- conf: cfg,
- /* TODO - add user + pwd support
- username: c.GlobalString("username"),
- password: c.GlobalString("password"),
- */
- }
-
- if client.apikey == "" {
- if err := client.getCidAndCsrf(); err != nil {
- return nil, err
- }
- }
- return &client, nil
-}
-
-// SetLogger Define the logger to use
-func (c *HTTPClient) SetLogger(log *logrus.Logger) {
- c.logger = log
-}
-
-func (c *HTTPClient) log(level int, format string, args ...interface{}) {
- if c.logger != nil {
- switch level {
- case logError:
- c.logger.Errorf(format, args...)
- break
- case logWarning:
- c.logger.Warningf(format, args...)
- break
- case logInfo:
- c.logger.Infof(format, args...)
- break
- default:
- c.logger.Debugf(format, args...)
- break
- }
- }
-}
-
-// Send request to retrieve Client id and/or CSRF token
-func (c *HTTPClient) getCidAndCsrf() error {
- request, err := http.NewRequest("GET", c.endpoint, nil)
- if err != nil {
- return err
- }
- if _, err := c.handleRequest(request); err != nil {
- return err
- }
- if c.id == "" {
- return errors.New("Failed to get device ID")
- }
- if !c.conf.CsrfDisable && c.csrf == "" {
- return errors.New("Failed to get CSRF token")
- }
- return nil
-}
-
-// GetClientID returns the id
-func (c *HTTPClient) GetClientID() string {
- return c.id
-}
-
-// formatURL Build full url by concatenating all parts
-func (c *HTTPClient) formatURL(endURL string) string {
- url := c.endpoint
- if !strings.HasSuffix(url, "/") {
- url += "/"
- }
- url += strings.TrimLeft(c.conf.URLPrefix, "/")
- if !strings.HasSuffix(url, "/") {
- url += "/"
- }
- return url + strings.TrimLeft(endURL, "/")
-}
-
-// HTTPGet Send a Get request to client and return an error object
-func (c *HTTPClient) HTTPGet(url string, data *[]byte) error {
- _, err := c.HTTPGetWithRes(url, data)
- return err
-}
-
-// HTTPGetWithRes Send a Get request to client and return both response and error
-func (c *HTTPClient) HTTPGetWithRes(url string, data *[]byte) (*http.Response, error) {
- request, err := http.NewRequest("GET", c.formatURL(url), nil)
- if err != nil {
- return nil, err
- }
- res, err := c.handleRequest(request)
- if err != nil {
- return res, err
- }
- if res.StatusCode != 200 {
- return res, errors.New(res.Status)
- }
-
- *data = c.responseToBArray(res)
-
- return res, nil
-}
-
-// HTTPPost Send a POST request to client and return an error object
-func (c *HTTPClient) HTTPPost(url string, body string) error {
- _, err := c.HTTPPostWithRes(url, body)
- return err
-}
-
-// HTTPPostWithRes Send a POST request to client and return both response and error
-func (c *HTTPClient) HTTPPostWithRes(url string, body string) (*http.Response, error) {
- request, err := http.NewRequest("POST", c.formatURL(url), bytes.NewBufferString(body))
- if err != nil {
- return nil, err
- }
- res, err := c.handleRequest(request)
- if err != nil {
- return res, err
- }
- if res.StatusCode != 200 {
- return res, errors.New(res.Status)
- }
- return res, nil
-}
-
-func (c *HTTPClient) responseToBArray(response *http.Response) []byte {
- defer response.Body.Close()
- bytes, err := ioutil.ReadAll(response.Body)
- if err != nil {
- // TODO improved error reporting
- fmt.Println("ERROR: " + err.Error())
- }
- return bytes
-}
-
-func (c *HTTPClient) handleRequest(request *http.Request) (*http.Response, error) {
- if c.conf.HeaderAPIKeyName != "" && c.apikey != "" {
- request.Header.Set(c.conf.HeaderAPIKeyName, c.apikey)
- }
- if c.conf.HeaderClientKeyName != "" && c.id != "" {
- request.Header.Set(c.conf.HeaderClientKeyName, c.id)
- }
- if c.username != "" || c.password != "" {
- request.SetBasicAuth(c.username, c.password)
- }
- if c.csrf != "" {
- request.Header.Set("X-CSRF-Token-"+c.id[:5], c.csrf)
- }
-
- c.log(logDebug, "HTTP %s %v", request.Method, request.URL)
-
- response, err := c.httpClient.Do(request)
- if err != nil {
- return nil, err
- }
-
- // Detect client ID change
- cid := response.Header.Get(c.conf.HeaderClientKeyName)
- if cid != "" && c.id != cid {
- c.id = cid
- }
-
- // Detect CSR token change
- for _, item := range response.Cookies() {
- if item.Name == "CSRF-Token-"+c.id[:5] {
- c.csrf = item.Value
- goto csrffound
- }
- }
- // OK CSRF found
-csrffound:
-
- if response.StatusCode == 404 {
- return nil, errors.New("Invalid endpoint or API call")
- } else if response.StatusCode == 401 {
- return nil, errors.New("Invalid username or password")
- } else if response.StatusCode == 403 {
- if c.apikey == "" {
- // Request a new Csrf for next requests
- c.getCidAndCsrf()
- return nil, errors.New("Invalid CSRF token")
- }
- return nil, errors.New("Invalid API key")
- } else if response.StatusCode != 200 {
- data := make(map[string]interface{})
- // Try to decode error field of APIError struct
- json.Unmarshal(c.responseToBArray(response), &data)
- if err, found := data["error"]; found {
- return nil, fmt.Errorf(err.(string))
- } else {
- body := strings.TrimSpace(string(c.responseToBArray(response)))
- if body != "" {
- return nil, fmt.Errorf(body)
- }
- }
- return nil, errors.New("Unknown HTTP status returned: " + response.Status)
- }
- return response, nil
-}