package obsws import ( "crypto/sha256" "encoding/base64" "errors" "fmt" "github.com/gorilla/websocket" ) // Connect opens a WebSocket connection and authenticates if necessary. func (c *Client) Connect() error { c.handlers = make(map[string]func(Event)) c.respQ = make(chan map[string]interface{}, bufferSize) conn, err := connectWS(c.Host, c.Port) if err != nil { return err } c.conn = conn // We can't use SendReceive yet because we haven't started polling. reqGAR := NewGetAuthRequiredRequest() if err = c.conn.WriteJSON(reqGAR); err != nil { return err } respGAR := &GetAuthRequiredResponse{} if err = c.conn.ReadJSON(respGAR); err != nil { return err } if !respGAR.AuthRequired { Logger.Println("logged in (no authentication required)") c.connected = true go c.poll() return nil } auth := getAuth(c.Password, respGAR.Salt, respGAR.Challenge) Logger.Println("auth:", auth) reqA := NewAuthenticateRequest(auth) if err = c.conn.WriteJSON(reqA); err != nil { return err } respA := &AuthenticateResponse{} if err = c.conn.ReadJSON(respA); err != nil { return err } if respA.Status() != "ok" { return errors.New(respA.Error()) } Logger.Println("logged in (authentication successful)") c.connected = true go c.poll() return nil } // Disconnect closes the WebSocket connection. func (c *Client) Disconnect() error { c.connected = false if err := c.conn.Close(); err != nil { return err } return nil } // connectWS opens the WebSocket connection. func connectWS(host string, port int) (*websocket.Conn, error) { url := fmt.Sprintf("ws://%s:%d", host, port) Logger.Println("connecting to", url) conn, _, err := websocket.DefaultDialer.Dial(url, nil) if err != nil { return nil, err } return conn, nil } // getAuth computes the auth challenge response. func getAuth(password, salt, challenge string) string { sha := sha256.Sum256([]byte(password + salt)) b64 := base64.StdEncoding.EncodeToString([]byte(sha[:])) sha = sha256.Sum256([]byte(b64 + challenge)) b64 = base64.StdEncoding.EncodeToString([]byte(sha[:])) return b64 }