diff --git a/.vscode/launch.json b/.vscode/launch.json index 59c9844..c6c9bf9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,19 +1,19 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Package", - "type": "go", - "request": "launch", - "mode": "debug", - "args": [ - "localhost:5903" - ], - "program": "${workspaceRoot}/example/client" - } - - ] +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "debug", + "args": [ + "localhost:5903" + ], + "program": "${workspaceRoot}/example/client" + } + + ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b725b84..cef0593 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,46 +1,46 @@ -{ - "version": "0.1.0", - "command": "go", - "isShellCommand": true, - "echoCommand": true, - "showOutput": "always", - // "showOutput": "silent", - "options": { - // "env": { - // "GOPATH": "/Users/lukeh/dd/go" - // } - }, - "tasks": [ - { - "taskName": "install", - "args": [ - "-v", - "./..." - ], - "osx": { - "options": { - "env": { - //"GOPATH": "${env.HOME}/Dropbox/go" - } - } - }, - "windows": { - "options": { - "env": { - //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" - } - } - }, - "isBuildCommand": true, - "problemMatcher": "$go" - }, - { - "taskName": "test", - "args": [ - "-v", - "./..." - ], - "isTestCommand": true - } - ] +{ + "version": "0.1.0", + "command": "go", + "isShellCommand": true, + "echoCommand": true, + "showOutput": "always", + // "showOutput": "silent", + "options": { + // "env": { + // "GOPATH": "/Users/lukeh/dd/go" + // } + }, + "tasks": [ + { + "taskName": "install", + "args": [ + "-v", + "./..." + ], + "osx": { + "options": { + "env": { + //"GOPATH": "${env.HOME}/Dropbox/go" + } + } + }, + "windows": { + "options": { + "env": { + //"GOPATH": "${env.USERPROFILE}\\Dropbox\\go" + } + } + }, + "isBuildCommand": true, + "problemMatcher": "$go" + }, + { + "taskName": "test", + "args": [ + "-v", + "./..." + ], + "isTestCommand": true + } + ] } \ No newline at end of file diff --git a/LICENSE b/LICENSE index ffe7f04..bfd7cf3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,23 +1,24 @@ -MIT License - -Copyright (c) 2013 Mitchell Hashimoto -Copyright (c) 2016-2017 Kate Ward -Copyright (c) 2017 Vasiliy Tolstov - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2013 Mitchell Hashimoto +Copyright (c) 2016-2017 Kate Ward +Copyright (c) 2017 Vasiliy Tolstov +Copyright (c) 2018 Amit Bezalel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/button_string.go b/button_string.go index 19fb0f7..0dc9ac6 100644 --- a/button_string.go +++ b/button_string.go @@ -1,46 +1,46 @@ -// Code generated by "stringer -type=Button"; DO NOT EDIT. - -package vnc2webm - -import "fmt" - -const ( - _Button_name_0 = "BtnNoneBtnLeftBtnMiddle" - _Button_name_1 = "BtnRight" - _Button_name_2 = "BtnFour" - _Button_name_3 = "BtnFive" - _Button_name_4 = "BtnSix" - _Button_name_5 = "BtnSeven" - _Button_name_6 = "BtnEight" -) - -var ( - _Button_index_0 = [...]uint8{0, 7, 14, 23} - _Button_index_1 = [...]uint8{0, 8} - _Button_index_2 = [...]uint8{0, 7} - _Button_index_3 = [...]uint8{0, 7} - _Button_index_4 = [...]uint8{0, 6} - _Button_index_5 = [...]uint8{0, 8} - _Button_index_6 = [...]uint8{0, 8} -) - -func (i Button) String() string { - switch { - case 0 <= i && i <= 2: - return _Button_name_0[_Button_index_0[i]:_Button_index_0[i+1]] - case i == 4: - return _Button_name_1 - case i == 8: - return _Button_name_2 - case i == 16: - return _Button_name_3 - case i == 32: - return _Button_name_4 - case i == 64: - return _Button_name_5 - case i == 128: - return _Button_name_6 - default: - return fmt.Sprintf("Button(%d)", i) - } -} +// Code generated by "stringer -type=Button"; DO NOT EDIT. + +package vnc2video + +import "fmt" + +const ( + _Button_name_0 = "BtnNoneBtnLeftBtnMiddle" + _Button_name_1 = "BtnRight" + _Button_name_2 = "BtnFour" + _Button_name_3 = "BtnFive" + _Button_name_4 = "BtnSix" + _Button_name_5 = "BtnSeven" + _Button_name_6 = "BtnEight" +) + +var ( + _Button_index_0 = [...]uint8{0, 7, 14, 23} + _Button_index_1 = [...]uint8{0, 8} + _Button_index_2 = [...]uint8{0, 7} + _Button_index_3 = [...]uint8{0, 7} + _Button_index_4 = [...]uint8{0, 6} + _Button_index_5 = [...]uint8{0, 8} + _Button_index_6 = [...]uint8{0, 8} +) + +func (i Button) String() string { + switch { + case 0 <= i && i <= 2: + return _Button_name_0[_Button_index_0[i]:_Button_index_0[i+1]] + case i == 4: + return _Button_name_1 + case i == 8: + return _Button_name_2 + case i == 16: + return _Button_name_3 + case i == 32: + return _Button_name_4 + case i == 64: + return _Button_name_5 + case i == 128: + return _Button_name_6 + default: + return fmt.Sprintf("Button(%d)", i) + } +} diff --git a/buttons.go b/buttons.go index 005a445..f01574c 100644 --- a/buttons.go +++ b/buttons.go @@ -1,24 +1,24 @@ -package vnc2webm - -// Button represents a mask of pointer presses/releases. -type Button uint8 - -//go:generate stringer -type=Button - -// All available button mask components. -const ( - BtnLeft Button = 1 << iota - BtnMiddle - BtnRight - BtnFour - BtnFive - BtnSix - BtnSeven - BtnEight - BtnNone Button = 0 -) - -// Mask returns button mask -func Mask(button Button) uint8 { - return uint8(button) -} +package vnc2video + +// Button represents a mask of pointer presses/releases. +type Button uint8 + +//go:generate stringer -type=Button + +// All available button mask components. +const ( + BtnLeft Button = 1 << iota + BtnMiddle + BtnRight + BtnFour + BtnFive + BtnSix + BtnSeven + BtnEight + BtnNone Button = 0 +) + +// Mask returns button mask +func Mask(button Button) uint8 { + return uint8(button) +} diff --git a/client.go b/client.go index dfd0888..c091f60 100644 --- a/client.go +++ b/client.go @@ -1,347 +1,347 @@ -package vnc2webm - -import ( - "bufio" - "context" - "encoding/binary" - "fmt" - "net" - "sync" - "vnc2webm/logger" -) - -var ( - // DefaultClientHandlers represents default client handlers - DefaultClientHandlers = []Handler{ - &DefaultClientVersionHandler{}, - &DefaultClientSecurityHandler{}, - &DefaultClientClientInitHandler{}, - &DefaultClientServerInitHandler{}, - &DefaultClientMessageHandler{}, - } -) - -// Connect handshake with remote server using underlining net.Conn -func Connect(ctx context.Context, c net.Conn, cfg *ClientConfig) (*ClientConn, error) { - conn, err := NewClientConn(c, cfg) - if err != nil { - conn.Close() - cfg.ErrorCh <- err - return nil, err - } - - if len(cfg.Handlers) == 0 { - cfg.Handlers = DefaultClientHandlers - } - - for _, h := range cfg.Handlers { - if err := h.Handle(conn); err != nil { - conn.Close() - cfg.ErrorCh <- err - return nil, err - } - } - - return conn, nil -} - -var _ Conn = (*ClientConn)(nil) - -// Config returns connection config -func (c *ClientConn) Config() interface{} { - return c.cfg -} - -func (c *ClientConn) GetEncInstance(typ EncodingType) Encoding { - for _, enc := range c.encodings { - if enc.Type() == typ { - return enc - } - } - return nil -} - -// Wait waiting for connection close -func (c *ClientConn) Wait() { - <-c.quit -} - -// Conn return underlining net.Conn -func (c *ClientConn) Conn() net.Conn { - return c.c -} - -// SetProtoVersion sets proto version -func (c *ClientConn) SetProtoVersion(pv string) { - c.protocol = pv -} - -// SetEncodings write SetEncodings message -func (c *ClientConn) SetEncodings(encs []EncodingType) error { - - msg := &SetEncodings{ - EncNum: uint16(len(encs)), - Encodings: encs, - } - - return msg.Write(c) -} - -// Flush flushes data to conn -func (c *ClientConn) Flush() error { - return c.bw.Flush() -} - -// Close closing conn -func (c *ClientConn) Close() error { - if c.quit != nil { - close(c.quit) - c.quit = nil - } - if c.quitCh != nil { - close(c.quitCh) - } - return c.c.Close() -} - -// Read reads data from conn -func (c *ClientConn) Read(buf []byte) (int, error) { - return c.br.Read(buf) -} - -// Write data to conn must be Flushed -func (c *ClientConn) Write(buf []byte) (int, error) { - return c.bw.Write(buf) -} - -// ColorMap returns color map -func (c *ClientConn) ColorMap() ColorMap { - return c.colorMap -} - -// SetColorMap sets color map -func (c *ClientConn) SetColorMap(cm ColorMap) { - c.colorMap = cm -} - -// DesktopName returns connection desktop name -func (c *ClientConn) DesktopName() []byte { - return c.desktopName -} - -// PixelFormat returns connection pixel format -func (c *ClientConn) PixelFormat() PixelFormat { - return c.pixelFormat -} - -// SetDesktopName sets desktop name -func (c *ClientConn) SetDesktopName(name []byte) { - c.desktopName = name -} - -// SetPixelFormat sets pixel format -func (c *ClientConn) SetPixelFormat(pf PixelFormat) error { - c.pixelFormat = pf - return nil -} - -// Encodings returns client encodings -func (c *ClientConn) Encodings() []Encoding { - return c.encodings -} - -// Width returns width -func (c *ClientConn) Width() uint16 { - return c.fbWidth -} - -// Height returns height -func (c *ClientConn) Height() uint16 { - return c.fbHeight -} - -// Protocol returns protocol -func (c *ClientConn) Protocol() string { - return c.protocol -} - -// SetWidth sets width of client conn -func (c *ClientConn) SetWidth(width uint16) { - c.fbWidth = width -} - -// SetHeight sets height of client conn -func (c *ClientConn) SetHeight(height uint16) { - c.fbHeight = height -} - -// SecurityHandler returns security handler -func (c *ClientConn) SecurityHandler() SecurityHandler { - return c.securityHandler -} - -// SetSecurityHandler sets security handler -func (c *ClientConn) SetSecurityHandler(sechandler SecurityHandler) error { - c.securityHandler = sechandler - return nil -} - -// The ClientConn type holds client connection information -type ClientConn struct { - c net.Conn - br *bufio.Reader - bw *bufio.Writer - cfg *ClientConfig - protocol string - // If the pixel format uses a color map, then this is the color - // map that is used. This should not be modified directly, since - // the data comes from the server. - // Definition in §5 - Representation of Pixel Data. - colorMap ColorMap - - // Name associated with the desktop, sent from the server. - desktopName []byte - - // Encodings supported by the client. This should not be modified - // directly. Instead, SetEncodings() should be used. - encodings []Encoding - - securityHandler SecurityHandler - - // Height of the frame buffer in pixels, sent from the server. - fbHeight uint16 - - // Width of the frame buffer in pixels, sent from the server. - fbWidth uint16 - - // The pixel format associated with the connection. This shouldn't - // be modified. If you wish to set a new pixel format, use the - // SetPixelFormat method. - pixelFormat PixelFormat - - quitCh chan struct{} - quit chan struct{} - errorCh chan error -} - -func (cc *ClientConn) ResetAllEncodings() { - for _, enc := range cc.encodings { - enc.Reset() - } -} - -// NewClientConn creates new client conn using config -func NewClientConn(c net.Conn, cfg *ClientConfig) (*ClientConn, error) { - if len(cfg.Encodings) == 0 { - return nil, fmt.Errorf("client can't handle encodings") - } - return &ClientConn{ - c: c, - cfg: cfg, - br: bufio.NewReader(c), - bw: bufio.NewWriter(c), - encodings: cfg.Encodings, - quitCh: cfg.QuitCh, - errorCh: cfg.ErrorCh, - pixelFormat: cfg.PixelFormat, - quit: make(chan struct{}), - }, nil -} - -// DefaultClientMessageHandler represents default client message handler -type DefaultClientMessageHandler struct{} - -// Handle handles server messages. -func (*DefaultClientMessageHandler) Handle(c Conn) error { - logger.Debug("starting DefaultClientMessageHandler") - cfg := c.Config().(*ClientConfig) - var err error - var wg sync.WaitGroup - wg.Add(2) - //defer c.Close() - - serverMessages := make(map[ServerMessageType]ServerMessage) - for _, m := range cfg.Messages { - serverMessages[m.Type()] = m - } - - go func() { - defer wg.Done() - for { - select { - case msg := <-cfg.ClientMessageCh: - if err = msg.Write(c); err != nil { - cfg.ErrorCh <- err - return - } - } - } - }() - - go func() { - defer wg.Done() - for { - select { - default: - var messageType ServerMessageType - if err = binary.Read(c, binary.BigEndian, &messageType); err != nil { - cfg.ErrorCh <- err - return - } - logger.Debugf("got server message, msgType=%d", messageType) - msg, ok := serverMessages[messageType] - if !ok { - err = fmt.Errorf("unknown message-type: %v", messageType) - cfg.ErrorCh <- err - return - } - - parsedMsg, err := msg.Read(c) - logger.Debugf("============== End Message: type=%d ==============", messageType) - - if err != nil { - cfg.ErrorCh <- err - return - } - cfg.ServerMessageCh <- parsedMsg - } - } - }() - //encodings := c.Encodings() - encTypes := make(map[EncodingType]EncodingType) - for _, myEnc := range c.Encodings() { - encTypes[myEnc.Type()] = myEnc.Type() - //encTypes = append(encTypes, myEnc.Type()) - } - v := make([]EncodingType, 0, len(encTypes)) - - for _, value := range encTypes { - v = append(v, value) - } - logger.Debugf("setting encodings: %v", v) - c.SetEncodings(v) - - firstMsg := FramebufferUpdateRequest{Inc: 0, X: 0, Y: 0, Width: c.Width(), Height: c.Height()} - logger.Debugf("sending initial req message: %v", firstMsg) - firstMsg.Write(c) - - //wg.Wait() - return nil -} - -// A ClientConfig structure is used to configure a ClientConn. After -// one has been passed to initialize a connection, it must not be modified. -type ClientConfig struct { - Handlers []Handler - SecurityHandlers []SecurityHandler - Encodings []Encoding - PixelFormat PixelFormat - ColorMap ColorMap - ClientMessageCh chan ClientMessage - ServerMessageCh chan ServerMessage - Exclusive bool - Messages []ServerMessage - QuitCh chan struct{} - ErrorCh chan error - quit chan struct{} -} +package vnc2video + +import ( + "bufio" + "context" + "encoding/binary" + "fmt" + "net" + "sync" + "vnc2video/logger" +) + +var ( + // DefaultClientHandlers represents default client handlers + DefaultClientHandlers = []Handler{ + &DefaultClientVersionHandler{}, + &DefaultClientSecurityHandler{}, + &DefaultClientClientInitHandler{}, + &DefaultClientServerInitHandler{}, + &DefaultClientMessageHandler{}, + } +) + +// Connect handshake with remote server using underlining net.Conn +func Connect(ctx context.Context, c net.Conn, cfg *ClientConfig) (*ClientConn, error) { + conn, err := NewClientConn(c, cfg) + if err != nil { + conn.Close() + cfg.ErrorCh <- err + return nil, err + } + + if len(cfg.Handlers) == 0 { + cfg.Handlers = DefaultClientHandlers + } + + for _, h := range cfg.Handlers { + if err := h.Handle(conn); err != nil { + conn.Close() + cfg.ErrorCh <- err + return nil, err + } + } + + return conn, nil +} + +var _ Conn = (*ClientConn)(nil) + +// Config returns connection config +func (c *ClientConn) Config() interface{} { + return c.cfg +} + +func (c *ClientConn) GetEncInstance(typ EncodingType) Encoding { + for _, enc := range c.encodings { + if enc.Type() == typ { + return enc + } + } + return nil +} + +// Wait waiting for connection close +func (c *ClientConn) Wait() { + <-c.quit +} + +// Conn return underlining net.Conn +func (c *ClientConn) Conn() net.Conn { + return c.c +} + +// SetProtoVersion sets proto version +func (c *ClientConn) SetProtoVersion(pv string) { + c.protocol = pv +} + +// SetEncodings write SetEncodings message +func (c *ClientConn) SetEncodings(encs []EncodingType) error { + + msg := &SetEncodings{ + EncNum: uint16(len(encs)), + Encodings: encs, + } + + return msg.Write(c) +} + +// Flush flushes data to conn +func (c *ClientConn) Flush() error { + return c.bw.Flush() +} + +// Close closing conn +func (c *ClientConn) Close() error { + if c.quit != nil { + close(c.quit) + c.quit = nil + } + if c.quitCh != nil { + close(c.quitCh) + } + return c.c.Close() +} + +// Read reads data from conn +func (c *ClientConn) Read(buf []byte) (int, error) { + return c.br.Read(buf) +} + +// Write data to conn must be Flushed +func (c *ClientConn) Write(buf []byte) (int, error) { + return c.bw.Write(buf) +} + +// ColorMap returns color map +func (c *ClientConn) ColorMap() ColorMap { + return c.colorMap +} + +// SetColorMap sets color map +func (c *ClientConn) SetColorMap(cm ColorMap) { + c.colorMap = cm +} + +// DesktopName returns connection desktop name +func (c *ClientConn) DesktopName() []byte { + return c.desktopName +} + +// PixelFormat returns connection pixel format +func (c *ClientConn) PixelFormat() PixelFormat { + return c.pixelFormat +} + +// SetDesktopName sets desktop name +func (c *ClientConn) SetDesktopName(name []byte) { + c.desktopName = name +} + +// SetPixelFormat sets pixel format +func (c *ClientConn) SetPixelFormat(pf PixelFormat) error { + c.pixelFormat = pf + return nil +} + +// Encodings returns client encodings +func (c *ClientConn) Encodings() []Encoding { + return c.encodings +} + +// Width returns width +func (c *ClientConn) Width() uint16 { + return c.fbWidth +} + +// Height returns height +func (c *ClientConn) Height() uint16 { + return c.fbHeight +} + +// Protocol returns protocol +func (c *ClientConn) Protocol() string { + return c.protocol +} + +// SetWidth sets width of client conn +func (c *ClientConn) SetWidth(width uint16) { + c.fbWidth = width +} + +// SetHeight sets height of client conn +func (c *ClientConn) SetHeight(height uint16) { + c.fbHeight = height +} + +// SecurityHandler returns security handler +func (c *ClientConn) SecurityHandler() SecurityHandler { + return c.securityHandler +} + +// SetSecurityHandler sets security handler +func (c *ClientConn) SetSecurityHandler(sechandler SecurityHandler) error { + c.securityHandler = sechandler + return nil +} + +// The ClientConn type holds client connection information +type ClientConn struct { + c net.Conn + br *bufio.Reader + bw *bufio.Writer + cfg *ClientConfig + protocol string + // If the pixel format uses a color map, then this is the color + // map that is used. This should not be modified directly, since + // the data comes from the server. + // Definition in §5 - Representation of Pixel Data. + colorMap ColorMap + + // Name associated with the desktop, sent from the server. + desktopName []byte + + // Encodings supported by the client. This should not be modified + // directly. Instead, SetEncodings() should be used. + encodings []Encoding + + securityHandler SecurityHandler + + // Height of the frame buffer in pixels, sent from the server. + fbHeight uint16 + + // Width of the frame buffer in pixels, sent from the server. + fbWidth uint16 + + // The pixel format associated with the connection. This shouldn't + // be modified. If you wish to set a new pixel format, use the + // SetPixelFormat method. + pixelFormat PixelFormat + + quitCh chan struct{} + quit chan struct{} + errorCh chan error +} + +func (cc *ClientConn) ResetAllEncodings() { + for _, enc := range cc.encodings { + enc.Reset() + } +} + +// NewClientConn creates new client conn using config +func NewClientConn(c net.Conn, cfg *ClientConfig) (*ClientConn, error) { + if len(cfg.Encodings) == 0 { + return nil, fmt.Errorf("client can't handle encodings") + } + return &ClientConn{ + c: c, + cfg: cfg, + br: bufio.NewReader(c), + bw: bufio.NewWriter(c), + encodings: cfg.Encodings, + quitCh: cfg.QuitCh, + errorCh: cfg.ErrorCh, + pixelFormat: cfg.PixelFormat, + quit: make(chan struct{}), + }, nil +} + +// DefaultClientMessageHandler represents default client message handler +type DefaultClientMessageHandler struct{} + +// Handle handles server messages. +func (*DefaultClientMessageHandler) Handle(c Conn) error { + logger.Debug("starting DefaultClientMessageHandler") + cfg := c.Config().(*ClientConfig) + var err error + var wg sync.WaitGroup + wg.Add(2) + //defer c.Close() + + serverMessages := make(map[ServerMessageType]ServerMessage) + for _, m := range cfg.Messages { + serverMessages[m.Type()] = m + } + + go func() { + defer wg.Done() + for { + select { + case msg := <-cfg.ClientMessageCh: + if err = msg.Write(c); err != nil { + cfg.ErrorCh <- err + return + } + } + } + }() + + go func() { + defer wg.Done() + for { + select { + default: + var messageType ServerMessageType + if err = binary.Read(c, binary.BigEndian, &messageType); err != nil { + cfg.ErrorCh <- err + return + } + logger.Debugf("got server message, msgType=%d", messageType) + msg, ok := serverMessages[messageType] + if !ok { + err = fmt.Errorf("unknown message-type: %v", messageType) + cfg.ErrorCh <- err + return + } + + parsedMsg, err := msg.Read(c) + logger.Debugf("============== End Message: type=%d ==============", messageType) + + if err != nil { + cfg.ErrorCh <- err + return + } + cfg.ServerMessageCh <- parsedMsg + } + } + }() + //encodings := c.Encodings() + encTypes := make(map[EncodingType]EncodingType) + for _, myEnc := range c.Encodings() { + encTypes[myEnc.Type()] = myEnc.Type() + //encTypes = append(encTypes, myEnc.Type()) + } + v := make([]EncodingType, 0, len(encTypes)) + + for _, value := range encTypes { + v = append(v, value) + } + logger.Debugf("setting encodings: %v", v) + c.SetEncodings(v) + + firstMsg := FramebufferUpdateRequest{Inc: 0, X: 0, Y: 0, Width: c.Width(), Height: c.Height()} + logger.Debugf("sending initial req message: %v", firstMsg) + firstMsg.Write(c) + + //wg.Wait() + return nil +} + +// A ClientConfig structure is used to configure a ClientConn. After +// one has been passed to initialize a connection, it must not be modified. +type ClientConfig struct { + Handlers []Handler + SecurityHandlers []SecurityHandler + Encodings []Encoding + PixelFormat PixelFormat + ColorMap ColorMap + ClientMessageCh chan ClientMessage + ServerMessageCh chan ServerMessage + Exclusive bool + Messages []ServerMessage + QuitCh chan struct{} + ErrorCh chan error + quit chan struct{} +} diff --git a/clientmessagetype_string.go b/clientmessagetype_string.go index 27fab0e..2eabf20 100644 --- a/clientmessagetype_string.go +++ b/clientmessagetype_string.go @@ -1,27 +1,27 @@ -// Code generated by "stringer -type=ClientMessageType"; DO NOT EDIT. - -package vnc2webm - -import "fmt" - -const ( - _ClientMessageType_name_0 = "SetPixelFormatMsgType" - _ClientMessageType_name_1 = "SetEncodingsMsgTypeFramebufferUpdateRequestMsgTypeKeyEventMsgTypePointerEventMsgTypeClientCutTextMsgType" -) - -var ( - _ClientMessageType_index_0 = [...]uint8{0, 21} - _ClientMessageType_index_1 = [...]uint8{0, 19, 50, 65, 84, 104} -) - -func (i ClientMessageType) String() string { - switch { - case i == 0: - return _ClientMessageType_name_0 - case 2 <= i && i <= 6: - i -= 2 - return _ClientMessageType_name_1[_ClientMessageType_index_1[i]:_ClientMessageType_index_1[i+1]] - default: - return fmt.Sprintf("ClientMessageType(%d)", i) - } -} +// Code generated by "stringer -type=ClientMessageType"; DO NOT EDIT. + +package vnc2video + +import "fmt" + +const ( + _ClientMessageType_name_0 = "SetPixelFormatMsgType" + _ClientMessageType_name_1 = "SetEncodingsMsgTypeFramebufferUpdateRequestMsgTypeKeyEventMsgTypePointerEventMsgTypeClientCutTextMsgType" +) + +var ( + _ClientMessageType_index_0 = [...]uint8{0, 21} + _ClientMessageType_index_1 = [...]uint8{0, 19, 50, 65, 84, 104} +) + +func (i ClientMessageType) String() string { + switch { + case i == 0: + return _ClientMessageType_name_0 + case 2 <= i && i <= 6: + i -= 2 + return _ClientMessageType_name_1[_ClientMessageType_index_1[i]:_ClientMessageType_index_1[i+1]] + default: + return fmt.Sprintf("ClientMessageType(%d)", i) + } +} diff --git a/conn.go b/conn.go index 371484f..5996098 100644 --- a/conn.go +++ b/conn.go @@ -1,32 +1,32 @@ -package vnc2webm - -import ( - "io" - "net" -) - -// Conn represents vnc conection -type Conn interface { - io.ReadWriteCloser - Conn() net.Conn - Config() interface{} - Protocol() string - PixelFormat() PixelFormat - SetPixelFormat(PixelFormat) error - ColorMap() ColorMap - SetColorMap(ColorMap) - Encodings() []Encoding - SetEncodings([]EncodingType) error - Width() uint16 - Height() uint16 - SetWidth(uint16) - SetHeight(uint16) - DesktopName() []byte - SetDesktopName([]byte) - Flush() error - Wait() - SetProtoVersion(string) - SetSecurityHandler(SecurityHandler) error - SecurityHandler() SecurityHandler - GetEncInstance(EncodingType) Encoding -} +package vnc2video + +import ( + "io" + "net" +) + +// Conn represents vnc conection +type Conn interface { + io.ReadWriteCloser + Conn() net.Conn + Config() interface{} + Protocol() string + PixelFormat() PixelFormat + SetPixelFormat(PixelFormat) error + ColorMap() ColorMap + SetColorMap(ColorMap) + Encodings() []Encoding + SetEncodings([]EncodingType) error + Width() uint16 + Height() uint16 + SetWidth(uint16) + SetHeight(uint16) + DesktopName() []byte + SetDesktopName([]byte) + Flush() error + Wait() + SetProtoVersion(string) + SetSecurityHandler(SecurityHandler) error + SecurityHandler() SecurityHandler + GetEncInstance(EncodingType) Encoding +} diff --git a/encoders/dv8-enc.go b/encoders/dv8-enc.go index 8b1acff..ff36454 100644 --- a/encoders/dv8-enc.go +++ b/encoders/dv8-enc.go @@ -1,91 +1,91 @@ -package encoders - -import ( - "image" - "io" - "os" - "os/exec" - "strings" - "vnc2webm/logger" -) - -type DV8ImageEncoder struct { - cmd *exec.Cmd - binaryPath string - input io.WriteCloser -} - -func (enc *DV8ImageEncoder) Init(videoFileName string) { - fileExt := ".webm" - if !strings.HasSuffix(videoFileName, fileExt) { - videoFileName = videoFileName + fileExt - } - binary := "./ffmpeg" - cmd := exec.Command(binary, - "-f", "image2pipe", - "-vcodec", "ppm", - //"-r", strconv.Itoa(framerate), - "-r", "5", - //"-i", "pipe:0", - "-i", "-", - "-vcodec", "libvpx", //"libvpx",//"libvpx-vp9"//"libx264" - "-b:v", "1M", - "-threads", "8", - //"-speed", "0", - //"-lossless", "1", //for vpx - // "-tile-columns", "6", - //"-frame-parallel", "1", - // "-an", "-f", "webm", - "-cpu-used", "-16", - - "-preset", "ultrafast", - "-deadline", "realtime", - //"-cpu-used", "-5", - "-maxrate", "2.5M", - "-bufsize", "10M", - "-g", "6", - - //"-rc_lookahead", "16", - //"-profile", "0", - "-qmax", "51", - "-qmin", "11", - //"-slices", "4", - //"-vb", "2M", - - videoFileName, - ) - //cmd := exec.Command("/bin/echo") - - //io.Copy(cmd.Stdout, os.Stdout) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - encInput, err := cmd.StdinPipe() - enc.input = encInput - if err != nil { - logger.Error("can't get ffmpeg input pipe") - } - enc.cmd = cmd -} -func (enc *DV8ImageEncoder) Run(encoderFilePath string, videoFileName string) { - if _, err := os.Stat(encoderFilePath); os.IsNotExist(err) { - logger.Error("encoder file doesn't exist in path:", encoderFilePath) - return - } - enc.binaryPath = encoderFilePath - enc.Init(videoFileName) - logger.Infof("launching binary: %v", enc.cmd) - err := enc.cmd.Run() - if err != nil { - logger.Errorf("error while launching ffmpeg: %v\n err: %v", enc.cmd.Args, err) - } -} -func (enc *DV8ImageEncoder) Encode(img image.Image) { - err := encodePPM(enc.input, img) - if err != nil { - logger.Error("error while encoding image:", err) - } -} -func (enc *DV8ImageEncoder) Close() { - -} +package encoders + +import ( + "image" + "io" + "os" + "os/exec" + "strings" + "vnc2video/logger" +) + +type DV8ImageEncoder struct { + cmd *exec.Cmd + binaryPath string + input io.WriteCloser +} + +func (enc *DV8ImageEncoder) Init(videoFileName string) { + fileExt := ".webm" + if !strings.HasSuffix(videoFileName, fileExt) { + videoFileName = videoFileName + fileExt + } + binary := "./ffmpeg" + cmd := exec.Command(binary, + "-f", "image2pipe", + "-vcodec", "ppm", + //"-r", strconv.Itoa(framerate), + "-r", "5", + //"-i", "pipe:0", + "-i", "-", + "-vcodec", "libvpx", //"libvpx",//"libvpx-vp9"//"libx264" + "-b:v", "1M", + "-threads", "8", + //"-speed", "0", + //"-lossless", "1", //for vpx + // "-tile-columns", "6", + //"-frame-parallel", "1", + // "-an", "-f", "webm", + "-cpu-used", "-16", + + "-preset", "ultrafast", + "-deadline", "realtime", + //"-cpu-used", "-5", + "-maxrate", "2.5M", + "-bufsize", "10M", + "-g", "6", + + //"-rc_lookahead", "16", + //"-profile", "0", + "-qmax", "51", + "-qmin", "11", + //"-slices", "4", + //"-vb", "2M", + + videoFileName, + ) + //cmd := exec.Command("/bin/echo") + + //io.Copy(cmd.Stdout, os.Stdout) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + encInput, err := cmd.StdinPipe() + enc.input = encInput + if err != nil { + logger.Error("can't get ffmpeg input pipe") + } + enc.cmd = cmd +} +func (enc *DV8ImageEncoder) Run(encoderFilePath string, videoFileName string) { + if _, err := os.Stat(encoderFilePath); os.IsNotExist(err) { + logger.Error("encoder file doesn't exist in path:", encoderFilePath) + return + } + enc.binaryPath = encoderFilePath + enc.Init(videoFileName) + logger.Infof("launching binary: %v", enc.cmd) + err := enc.cmd.Run() + if err != nil { + logger.Errorf("error while launching ffmpeg: %v\n err: %v", enc.cmd.Args, err) + } +} +func (enc *DV8ImageEncoder) Encode(img image.Image) { + err := encodePPM(enc.input, img) + if err != nil { + logger.Error("error while encoding image:", err) + } +} +func (enc *DV8ImageEncoder) Close() { + +} diff --git a/encoders/dv9-enc.go b/encoders/dv9-enc.go index ba6dc4c..39a92ff 100644 --- a/encoders/dv9-enc.go +++ b/encoders/dv9-enc.go @@ -1,91 +1,91 @@ -package encoders - -import ( - "image" - "io" - "os" - "os/exec" - "strings" - "vnc2webm/logger" -) - -type DV9ImageEncoder struct { - cmd *exec.Cmd - binaryPath string - input io.WriteCloser -} - -func (enc *DV9ImageEncoder) Init(videoFileName string) { - fileExt := ".webm" - if !strings.HasSuffix(videoFileName, fileExt) { - videoFileName = videoFileName + fileExt - } - binary := "./ffmpeg" - cmd := exec.Command(binary, - "-f", "image2pipe", - "-vcodec", "ppm", - //"-r", strconv.Itoa(framerate), - "-r", "5", - //"-i", "pipe:0", - "-i", "-", - "-vcodec", "libvpx-vp9", //"libvpx",//"libvpx-vp9"//"libx264" - "-b:v", "1M", - "-threads", "8", - //"-speed", "0", - //"-lossless", "1", //for vpx - // "-tile-columns", "6", - //"-frame-parallel", "1", - // "-an", "-f", "webm", - "-cpu-used", "-8", - - "-preset", "ultrafast", - "-deadline", "realtime", - //"-cpu-used", "-5", - "-maxrate", "2.5M", - "-bufsize", "10M", - "-g", "6", - - //"-rc_lookahead", "16", - //"-profile", "0", - "-qmax", "51", - "-qmin", "11", - //"-slices", "4", - //"-vb", "2M", - - videoFileName, - ) - //cmd := exec.Command("/bin/echo") - - //io.Copy(cmd.Stdout, os.Stdout) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - encInput, err := cmd.StdinPipe() - enc.input = encInput - if err != nil { - logger.Error("can't get ffmpeg input pipe") - } - enc.cmd = cmd -} -func (enc *DV9ImageEncoder) Run(encoderFilePath string, videoFileName string) { - if _, err := os.Stat(encoderFilePath); os.IsNotExist(err) { - logger.Error("encoder file doesn't exist in path:", encoderFilePath) - return - } - enc.binaryPath = encoderFilePath - enc.Init(videoFileName) - logger.Infof("launching binary: %v", enc.cmd) - err := enc.cmd.Run() - if err != nil { - logger.Errorf("error while launching ffmpeg: %v\n err: %v", enc.cmd.Args, err) - } -} -func (enc *DV9ImageEncoder) Encode(img image.Image) { - err := encodePPM(enc.input, img) - if err != nil { - logger.Error("error while encoding image:", err) - } -} -func (enc *DV9ImageEncoder) Close() { - -} +package encoders + +import ( + "image" + "io" + "os" + "os/exec" + "strings" + "vnc2video/logger" +) + +type DV9ImageEncoder struct { + cmd *exec.Cmd + binaryPath string + input io.WriteCloser +} + +func (enc *DV9ImageEncoder) Init(videoFileName string) { + fileExt := ".webm" + if !strings.HasSuffix(videoFileName, fileExt) { + videoFileName = videoFileName + fileExt + } + binary := "./ffmpeg" + cmd := exec.Command(binary, + "-f", "image2pipe", + "-vcodec", "ppm", + //"-r", strconv.Itoa(framerate), + "-r", "5", + //"-i", "pipe:0", + "-i", "-", + "-vcodec", "libvpx-vp9", //"libvpx",//"libvpx-vp9"//"libx264" + "-b:v", "1M", + "-threads", "8", + //"-speed", "0", + //"-lossless", "1", //for vpx + // "-tile-columns", "6", + //"-frame-parallel", "1", + // "-an", "-f", "webm", + "-cpu-used", "-8", + + "-preset", "ultrafast", + "-deadline", "realtime", + //"-cpu-used", "-5", + "-maxrate", "2.5M", + "-bufsize", "10M", + "-g", "6", + + //"-rc_lookahead", "16", + //"-profile", "0", + "-qmax", "51", + "-qmin", "11", + //"-slices", "4", + //"-vb", "2M", + + videoFileName, + ) + //cmd := exec.Command("/bin/echo") + + //io.Copy(cmd.Stdout, os.Stdout) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + encInput, err := cmd.StdinPipe() + enc.input = encInput + if err != nil { + logger.Error("can't get ffmpeg input pipe") + } + enc.cmd = cmd +} +func (enc *DV9ImageEncoder) Run(encoderFilePath string, videoFileName string) { + if _, err := os.Stat(encoderFilePath); os.IsNotExist(err) { + logger.Error("encoder file doesn't exist in path:", encoderFilePath) + return + } + enc.binaryPath = encoderFilePath + enc.Init(videoFileName) + logger.Infof("launching binary: %v", enc.cmd) + err := enc.cmd.Run() + if err != nil { + logger.Errorf("error while launching ffmpeg: %v\n err: %v", enc.cmd.Args, err) + } +} +func (enc *DV9ImageEncoder) Encode(img image.Image) { + err := encodePPM(enc.input, img) + if err != nil { + logger.Error("error while encoding image:", err) + } +} +func (enc *DV9ImageEncoder) Close() { + +} diff --git a/encoders/image-enc.go b/encoders/image-enc.go index 399f4f8..a80218e 100644 --- a/encoders/image-enc.go +++ b/encoders/image-enc.go @@ -1,43 +1,43 @@ -package encoders - -import ( - "fmt" - "image" - "image/color" - "io" -) - -func encodePPM(w io.Writer, img image.Image) error { - maxvalue := 255 - size := img.Bounds() - // write ppm header - _, err := fmt.Fprintf(w, "P6\n%d %d\n%d\n", size.Dx(), size.Dy(), maxvalue) - if err != nil { - return err - } - - // write the bitmap - colModel := color.RGBAModel - row := make([]uint8, size.Dx()*3) - for y := size.Min.Y; y < size.Max.Y; y++ { - i := 0 - for x := size.Min.X; x < size.Max.X; x++ { - color := colModel.Convert(img.At(x, y)).(color.RGBA) - row[i] = color.R - row[i+1] = color.G - row[i+2] = color.B - i += 3 - } - if _, err := w.Write(row); err != nil { - return err - } - } - return nil -} - -type ImageEncoder interface { - Init(string) - Run() - Encode(image.Image) - Close() -} +package encoders + +import ( + "fmt" + "image" + "image/color" + "io" +) + +func encodePPM(w io.Writer, img image.Image) error { + maxvalue := 255 + size := img.Bounds() + // write ppm header + _, err := fmt.Fprintf(w, "P6\n%d %d\n%d\n", size.Dx(), size.Dy(), maxvalue) + if err != nil { + return err + } + + // write the bitmap + colModel := color.RGBAModel + row := make([]uint8, size.Dx()*3) + for y := size.Min.Y; y < size.Max.Y; y++ { + i := 0 + for x := size.Min.X; x < size.Max.X; x++ { + color := colModel.Convert(img.At(x, y)).(color.RGBA) + row[i] = color.R + row[i+1] = color.G + row[i+2] = color.B + i += 3 + } + if _, err := w.Write(row); err != nil { + return err + } + } + return nil +} + +type ImageEncoder interface { + Init(string) + Run() + Encode(image.Image) + Close() +} diff --git a/encoders/mjpeg-enc.go b/encoders/mjpeg-enc.go index 4a91ae0..0a182b3 100644 --- a/encoders/mjpeg-enc.go +++ b/encoders/mjpeg-enc.go @@ -1,56 +1,56 @@ -package encoders - -import ( - "bytes" - "image" - "image/jpeg" - "strings" - "vnc2webm/logger" - - "github.com/icza/mjpeg" -) - -type MJPegImageEncoder struct { - avWriter mjpeg.AviWriter - Quality int - Framerate int32 -} - -func (enc *MJPegImageEncoder) Init(videoFileName string) { - fileExt := ".avi" - if !strings.HasSuffix(videoFileName, fileExt) { - videoFileName = videoFileName + fileExt - } - if enc.Framerate <= 0 { - enc.Framerate = 5 - } - avWriter, err := mjpeg.New(videoFileName, 1024, 768, enc.Framerate) - if err != nil { - logger.Error("Error during mjpeg init: ", err) - } - enc.avWriter = avWriter -} -func (enc *MJPegImageEncoder) Run() { -} -func (enc *MJPegImageEncoder) Encode(img image.Image) { - buf := &bytes.Buffer{} - jOpts := &jpeg.Options{Quality: enc.Quality} - if enc.Quality <= 0 { - jOpts = nil - } - err := jpeg.Encode(buf, img, jOpts) - if err != nil { - logger.Error("Error while creating jpeg: ", err) - } - err = enc.avWriter.AddFrame(buf.Bytes()) - if err != nil { - logger.Error("Error while adding frame to mjpeg: ", err) - } - -} -func (enc *MJPegImageEncoder) Close() { - err := enc.avWriter.Close() - if err != nil { - logger.Error("Error while closing mjpeg: ", err) - } -} +package encoders + +import ( + "bytes" + "image" + "image/jpeg" + "strings" + "vnc2video/logger" + + "github.com/icza/mjpeg" +) + +type MJPegImageEncoder struct { + avWriter mjpeg.AviWriter + Quality int + Framerate int32 +} + +func (enc *MJPegImageEncoder) Init(videoFileName string) { + fileExt := ".avi" + if !strings.HasSuffix(videoFileName, fileExt) { + videoFileName = videoFileName + fileExt + } + if enc.Framerate <= 0 { + enc.Framerate = 5 + } + avWriter, err := mjpeg.New(videoFileName, 1024, 768, enc.Framerate) + if err != nil { + logger.Error("Error during mjpeg init: ", err) + } + enc.avWriter = avWriter +} +func (enc *MJPegImageEncoder) Run() { +} +func (enc *MJPegImageEncoder) Encode(img image.Image) { + buf := &bytes.Buffer{} + jOpts := &jpeg.Options{Quality: enc.Quality} + if enc.Quality <= 0 { + jOpts = nil + } + err := jpeg.Encode(buf, img, jOpts) + if err != nil { + logger.Error("Error while creating jpeg: ", err) + } + err = enc.avWriter.AddFrame(buf.Bytes()) + if err != nil { + logger.Error("Error while adding frame to mjpeg: ", err) + } + +} +func (enc *MJPegImageEncoder) Close() { + err := enc.avWriter.Close() + if err != nil { + logger.Error("Error while closing mjpeg: ", err) + } +} diff --git a/encoders/x264-enc.go b/encoders/x264-enc.go index 7132ced..768c010 100644 --- a/encoders/x264-enc.go +++ b/encoders/x264-enc.go @@ -1,92 +1,92 @@ -package encoders - -import ( - "image" - "io" - "os" - "os/exec" - "strings" - "vnc2webm/logger" -) - -type X264ImageEncoder struct { - cmd *exec.Cmd - binaryPath string - input io.WriteCloser -} - -func (enc *X264ImageEncoder) Init(videoFileName string) { - fileExt := ".mp4" - if !strings.HasSuffix(videoFileName, fileExt) { - videoFileName = videoFileName + fileExt - } - //binary := "./ffmpeg" - cmd := exec.Command(enc.binaryPath, - "-f", "image2pipe", - "-vcodec", "ppm", - //"-r", strconv.Itoa(framerate), - "-r", "4", - //"-i", "pipe:0", - "-i", "-", - "-vcodec", "libx264", //"libvpx",//"libvpx-vp9"//"libx264" - "-b:v", "1M", - "-threads", "8", - //"-speed", "0", - //"-lossless", "1", //for vpx - // "-tile-columns", "6", - //"-frame-parallel", "1", - // "-an", "-f", "webm", - "-cpu-used", "-16", - - "-preset", "ultrafast", - "-deadline", "realtime", - //"-cpu-used", "-5", - "-maxrate", "2.5M", - "-bufsize", "10M", - "-g", "6", - - //"-rc_lookahead", "16", - //"-profile", "0", - "-qmax", "51", - "-qmin", "11", - //"-slices", "4", - //"-vb", "2M", - - videoFileName, - ) - //cmd := exec.Command("/bin/echo") - - //io.Copy(cmd.Stdout, os.Stdout) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - encInput, err := cmd.StdinPipe() - enc.input = encInput - if err != nil { - logger.Error("can't get ffmpeg input pipe") - } - enc.cmd = cmd -} -func (enc *X264ImageEncoder) Run(encoderFilePath string, videoFileName string) { - if _, err := os.Stat(encoderFilePath); os.IsNotExist(err) { - logger.Error("encoder file doesn't exist in path:", encoderFilePath) - return - } - - enc.binaryPath = encoderFilePath - enc.Init(videoFileName) - logger.Infof("launching binary: %v", enc.cmd) - err := enc.cmd.Run() - if err != nil { - logger.Errorf("error while launching ffmpeg: %v\n err: %v", enc.cmd.Args, err) - } -} -func (enc *X264ImageEncoder) Encode(img image.Image) { - err := encodePPM(enc.input, img) - if err != nil { - logger.Error("error while encoding image:", err) - } -} -func (enc *X264ImageEncoder) Close() { - -} +package encoders + +import ( + "image" + "io" + "os" + "os/exec" + "strings" + "vnc2video/logger" +) + +type X264ImageEncoder struct { + cmd *exec.Cmd + binaryPath string + input io.WriteCloser +} + +func (enc *X264ImageEncoder) Init(videoFileName string) { + fileExt := ".mp4" + if !strings.HasSuffix(videoFileName, fileExt) { + videoFileName = videoFileName + fileExt + } + //binary := "./ffmpeg" + cmd := exec.Command(enc.binaryPath, + "-f", "image2pipe", + "-vcodec", "ppm", + //"-r", strconv.Itoa(framerate), + "-r", "4", + //"-i", "pipe:0", + "-i", "-", + "-vcodec", "libx264", //"libvpx",//"libvpx-vp9"//"libx264" + "-b:v", "1M", + "-threads", "8", + //"-speed", "0", + //"-lossless", "1", //for vpx + // "-tile-columns", "6", + //"-frame-parallel", "1", + // "-an", "-f", "webm", + "-cpu-used", "-16", + + "-preset", "ultrafast", + "-deadline", "realtime", + //"-cpu-used", "-5", + "-maxrate", "2.5M", + "-bufsize", "10M", + "-g", "6", + + //"-rc_lookahead", "16", + //"-profile", "0", + "-qmax", "51", + "-qmin", "11", + //"-slices", "4", + //"-vb", "2M", + + videoFileName, + ) + //cmd := exec.Command("/bin/echo") + + //io.Copy(cmd.Stdout, os.Stdout) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + encInput, err := cmd.StdinPipe() + enc.input = encInput + if err != nil { + logger.Error("can't get ffmpeg input pipe") + } + enc.cmd = cmd +} +func (enc *X264ImageEncoder) Run(encoderFilePath string, videoFileName string) { + if _, err := os.Stat(encoderFilePath); os.IsNotExist(err) { + logger.Error("encoder file doesn't exist in path:", encoderFilePath) + return + } + + enc.binaryPath = encoderFilePath + enc.Init(videoFileName) + logger.Infof("launching binary: %v", enc.cmd) + err := enc.cmd.Run() + if err != nil { + logger.Errorf("error while launching ffmpeg: %v\n err: %v", enc.cmd.Args, err) + } +} +func (enc *X264ImageEncoder) Encode(img image.Image) { + err := encodePPM(enc.input, img) + if err != nil { + logger.Error("error while encoding image:", err) + } +} +func (enc *X264ImageEncoder) Close() { + +} diff --git a/encoding.go b/encoding.go index 2bb2831..0af7546 100644 --- a/encoding.go +++ b/encoding.go @@ -1,121 +1,121 @@ -package vnc2webm - -import ( - "bytes" - "image" - "image/draw" - "sync" -) - -// EncodingType represents a known VNC encoding type. -type EncodingType int32 - -//go:generate stringer -type=EncodingType - -const ( - // EncRaw raw encoding - EncRaw EncodingType = 0 - // EncCopyRect copyrect encoding - EncCopyRect EncodingType = 1 - - EncRRE EncodingType = 2 - EncCoRRE EncodingType = 4 - EncHextile EncodingType = 5 - EncZlib EncodingType = 6 - EncTight EncodingType = 7 - EncZlibHex EncodingType = 8 - EncUltra1 EncodingType = 9 - EncUltra2 EncodingType = 10 - EncJPEG EncodingType = 21 - EncJRLE EncodingType = 22 - EncTRLE EncodingType = 15 - EncZRLE EncodingType = 16 - EncAtenAST2100 EncodingType = 0x57 - EncAtenASTJPEG EncodingType = 0x58 - EncAtenHermon EncodingType = 0x59 - EncAtenYarkon EncodingType = 0x60 - EncAtenPilot3 EncodingType = 0x61 - EncJPEGQualityLevelPseudo10 EncodingType = -23 - EncJPEGQualityLevelPseudo9 EncodingType = -24 - EncJPEGQualityLevelPseudo8 EncodingType = -25 - EncJPEGQualityLevelPseudo7 EncodingType = -26 - EncJPEGQualityLevelPseudo6 EncodingType = -27 - EncJPEGQualityLevelPseudo5 EncodingType = -28 - EncJPEGQualityLevelPseudo4 EncodingType = -29 - EncJPEGQualityLevelPseudo3 EncodingType = -30 - EncJPEGQualityLevelPseudo2 EncodingType = -31 - EncJPEGQualityLevelPseudo1 EncodingType = -32 - EncCursorPseudo EncodingType = -239 - EncXCursorPseudo EncodingType = -240 - EncDesktopSizePseudo EncodingType = -223 - EncLastRectPseudo EncodingType = -224 - EncCompressionLevel10 EncodingType = -247 - EncCompressionLevel9 EncodingType = -248 - EncCompressionLevel8 EncodingType = -249 - EncCompressionLevel7 EncodingType = -250 - EncCompressionLevel6 EncodingType = -251 - EncCompressionLevel5 EncodingType = -252 - EncCompressionLevel4 EncodingType = -253 - EncCompressionLevel3 EncodingType = -254 - EncCompressionLevel2 EncodingType = -255 - EncCompressionLevel1 EncodingType = -256 - EncQEMUPointerMotionChangePseudo EncodingType = -257 - EncQEMUExtendedKeyEventPseudo EncodingType = -258 - EncTightPng EncodingType = -260 - EncDesktopNamePseudo EncodingType = -307 - EncExtendedDesktopSizePseudo EncodingType = -308 - EncXvpPseudo EncodingType = -309 - EncClientRedirect EncodingType = -311 - EncFencePseudo EncodingType = -312 - EncContinuousUpdatesPseudo EncodingType = -313 -) - -var bPool = sync.Pool{ - New: func() interface{} { - // The Pool's New function should generally only return pointer - // types, since a pointer can be put into the return interface - // value without an allocation: - return new(bytes.Buffer) - }, -} - -type Renderer interface { - SetTargetImage(draw.Image) -} - -// Encoding represents interface for vnc encoding -type Encoding interface { - Type() EncodingType - Read(Conn, *Rectangle) error - Write(Conn, *Rectangle) error - Supported(Conn) bool - Reset() error -} - -func setBit(n uint8, pos uint8) uint8 { - n |= (1 << pos) - return n -} - -func clrBit(n uint8, pos uint8) uint8 { - n = n &^ (1 << pos) - return n -} - -func hasBit(n uint8, pos uint8) bool { - v := n & (1 << pos) - return (v > 0) -} - -func getBit(n uint8, pos uint8) uint8 { - n = n & (1 << pos) - return n -} - -func newRGBAImage(rgba []byte, rect *Rectangle) image.Image { - img := &image.RGBA{Stride: 4 * int(rect.Width)} - img.Pix = rgba - img.Rect.Max.X = int(rect.Width) - img.Rect.Max.Y = int(rect.Height) - return img -} +package vnc2video + +import ( + "bytes" + "image" + "image/draw" + "sync" +) + +// EncodingType represents a known VNC encoding type. +type EncodingType int32 + +//go:generate stringer -type=EncodingType + +const ( + // EncRaw raw encoding + EncRaw EncodingType = 0 + // EncCopyRect copyrect encoding + EncCopyRect EncodingType = 1 + + EncRRE EncodingType = 2 + EncCoRRE EncodingType = 4 + EncHextile EncodingType = 5 + EncZlib EncodingType = 6 + EncTight EncodingType = 7 + EncZlibHex EncodingType = 8 + EncUltra1 EncodingType = 9 + EncUltra2 EncodingType = 10 + EncJPEG EncodingType = 21 + EncJRLE EncodingType = 22 + EncTRLE EncodingType = 15 + EncZRLE EncodingType = 16 + EncAtenAST2100 EncodingType = 0x57 + EncAtenASTJPEG EncodingType = 0x58 + EncAtenHermon EncodingType = 0x59 + EncAtenYarkon EncodingType = 0x60 + EncAtenPilot3 EncodingType = 0x61 + EncJPEGQualityLevelPseudo10 EncodingType = -23 + EncJPEGQualityLevelPseudo9 EncodingType = -24 + EncJPEGQualityLevelPseudo8 EncodingType = -25 + EncJPEGQualityLevelPseudo7 EncodingType = -26 + EncJPEGQualityLevelPseudo6 EncodingType = -27 + EncJPEGQualityLevelPseudo5 EncodingType = -28 + EncJPEGQualityLevelPseudo4 EncodingType = -29 + EncJPEGQualityLevelPseudo3 EncodingType = -30 + EncJPEGQualityLevelPseudo2 EncodingType = -31 + EncJPEGQualityLevelPseudo1 EncodingType = -32 + EncCursorPseudo EncodingType = -239 + EncXCursorPseudo EncodingType = -240 + EncDesktopSizePseudo EncodingType = -223 + EncLastRectPseudo EncodingType = -224 + EncCompressionLevel10 EncodingType = -247 + EncCompressionLevel9 EncodingType = -248 + EncCompressionLevel8 EncodingType = -249 + EncCompressionLevel7 EncodingType = -250 + EncCompressionLevel6 EncodingType = -251 + EncCompressionLevel5 EncodingType = -252 + EncCompressionLevel4 EncodingType = -253 + EncCompressionLevel3 EncodingType = -254 + EncCompressionLevel2 EncodingType = -255 + EncCompressionLevel1 EncodingType = -256 + EncQEMUPointerMotionChangePseudo EncodingType = -257 + EncQEMUExtendedKeyEventPseudo EncodingType = -258 + EncTightPng EncodingType = -260 + EncDesktopNamePseudo EncodingType = -307 + EncExtendedDesktopSizePseudo EncodingType = -308 + EncXvpPseudo EncodingType = -309 + EncClientRedirect EncodingType = -311 + EncFencePseudo EncodingType = -312 + EncContinuousUpdatesPseudo EncodingType = -313 +) + +var bPool = sync.Pool{ + New: func() interface{} { + // The Pool's New function should generally only return pointer + // types, since a pointer can be put into the return interface + // value without an allocation: + return new(bytes.Buffer) + }, +} + +type Renderer interface { + SetTargetImage(draw.Image) +} + +// Encoding represents interface for vnc encoding +type Encoding interface { + Type() EncodingType + Read(Conn, *Rectangle) error + Write(Conn, *Rectangle) error + Supported(Conn) bool + Reset() error +} + +func setBit(n uint8, pos uint8) uint8 { + n |= (1 << pos) + return n +} + +func clrBit(n uint8, pos uint8) uint8 { + n = n &^ (1 << pos) + return n +} + +func hasBit(n uint8, pos uint8) bool { + v := n & (1 << pos) + return (v > 0) +} + +func getBit(n uint8, pos uint8) uint8 { + n = n & (1 << pos) + return n +} + +func newRGBAImage(rgba []byte, rect *Rectangle) image.Image { + img := &image.RGBA{Stride: 4 * int(rect.Width)} + img.Pix = rgba + img.Rect.Max.X = int(rect.Width) + img.Rect.Max.Y = int(rect.Height) + return img +} diff --git a/encoding_atenhermon.go b/encoding_atenhermon.go index a30669d..3d3bc72 100644 --- a/encoding_atenhermon.go +++ b/encoding_atenhermon.go @@ -1,216 +1,216 @@ -package vnc2webm - -import ( - "encoding/binary" - "fmt" -) - -const ( - EncAtenHermonSubrect EncodingType = 0 - EncAtenHermonRaw EncodingType = 1 -) - -type AtenHermon struct { - _ [4]byte - AtenLength uint32 - AtenType uint8 - _ [1]byte - AtenSubrects uint32 - AtenRawLength uint32 - Encodings []Encoding -} - -type AtenHermonSubrect struct { - A uint16 - B uint16 - Y uint8 - X uint8 - Data []byte -} - -func (*AtenHermon) Supported(Conn) bool { - return false -} - -func (*AtenHermon) Type() EncodingType { return EncAtenHermon } -func (*AtenHermon) Reset() error { - return nil -} - -func (enc *AtenHermon) Read(c Conn, rect *Rectangle) error { - var pad4 [4]byte - - if err := binary.Read(c, binary.BigEndian, &pad4); err != nil { - return err - } - - var aten_length uint32 - if err := binary.Read(c, binary.BigEndian, &aten_length); err != nil { - return err - } - enc.AtenLength = aten_length - - if rect.Width == 64896 && rect.Height == 65056 { - if aten_length != 10 && aten_length != 0 { - return fmt.Errorf("screen is off and length is invalid") - } - aten_length = 0 - } - - if c.Width() != rect.Width && c.Height() != rect.Height { - c.SetWidth(rect.Width) - c.SetHeight(rect.Height) - } - - var aten_type uint8 - if err := binary.Read(c, binary.BigEndian, &aten_type); err != nil { - return err - } - enc.AtenType = aten_type - - var pad1 [1]byte - if err := binary.Read(c, binary.BigEndian, &pad1); err != nil { - return err - } - - var subrects uint32 - if err := binary.Read(c, binary.BigEndian, &subrects); err != nil { - return err - } - enc.AtenSubrects = subrects - - var raw_length uint32 - if err := binary.Read(c, binary.BigEndian, &raw_length); err != nil { - return err - } - enc.AtenRawLength = raw_length - - if aten_length != raw_length { - return fmt.Errorf("aten_length != raw_length, %d != %d", aten_length, raw_length) - } - - aten_length -= 10 // skip - - for aten_length > 0 { - switch EncodingType(aten_type) { - case EncAtenHermonSubrect: - encSR := &AtenHermonSubrect{} - if err := encSR.Read(c, rect); err != nil { - return err - } - enc.Encodings = append(enc.Encodings, encSR) - aten_length -= 6 + (16 * 16 * uint32(c.PixelFormat().BPP/8)) - case EncAtenHermonRaw: - encRaw := &RawEncoding{} - if err := encRaw.Read(c, rect); err != nil { - return err - } - enc.Encodings = append(enc.Encodings, encRaw) - aten_length -= uint32(rect.Area()) * uint32(c.PixelFormat().BPP/8) - default: - return fmt.Errorf("unknown aten hermon type %d", aten_type) - - } - } - - if aten_length < 0 { - return fmt.Errorf("aten_len dropped below zero") - } - return nil -} - -func (enc *AtenHermon) Write(c Conn, rect *Rectangle) error { - if !enc.Supported(c) { - for _, ew := range enc.Encodings { - if err := ew.Write(c, rect); err != nil { - return err - } - } - return nil - } - var pad4 [4]byte - - if err := binary.Write(c, binary.BigEndian, pad4); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, enc.AtenLength); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, enc.AtenType); err != nil { - return err - } - - var pad1 [1]byte - if err := binary.Write(c, binary.BigEndian, pad1); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, enc.AtenSubrects); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, enc.AtenRawLength); err != nil { - return err - } - - for _, ew := range enc.Encodings { - if err := ew.Write(c, rect); err != nil { - return err - } - } - return nil -} - -func (*AtenHermonSubrect) Supported(Conn) bool { - return false -} - -func (enc *AtenHermonSubrect) Type() EncodingType { - return EncAtenHermonSubrect -} -func (*AtenHermonSubrect) Reset() error { - return nil -} -func (enc *AtenHermonSubrect) Read(c Conn, rect *Rectangle) error { - if err := binary.Read(c, binary.BigEndian, &enc.A); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.B); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.Y); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.X); err != nil { - return err - } - enc.Data = make([]byte, 16*16*uint32(c.PixelFormat().BPP/8)) - if err := binary.Read(c, binary.BigEndian, &enc.Data); err != nil { - return err - } - return nil -} - -func (enc *AtenHermonSubrect) Write(c Conn, rect *Rectangle) error { - if !enc.Supported(c) { - return nil - } - if err := binary.Write(c, binary.BigEndian, enc.A); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.B); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.Y); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.X); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.Data); err != nil { - return err - } - return nil -} +package vnc2video + +import ( + "encoding/binary" + "fmt" +) + +const ( + EncAtenHermonSubrect EncodingType = 0 + EncAtenHermonRaw EncodingType = 1 +) + +type AtenHermon struct { + _ [4]byte + AtenLength uint32 + AtenType uint8 + _ [1]byte + AtenSubrects uint32 + AtenRawLength uint32 + Encodings []Encoding +} + +type AtenHermonSubrect struct { + A uint16 + B uint16 + Y uint8 + X uint8 + Data []byte +} + +func (*AtenHermon) Supported(Conn) bool { + return false +} + +func (*AtenHermon) Type() EncodingType { return EncAtenHermon } +func (*AtenHermon) Reset() error { + return nil +} + +func (enc *AtenHermon) Read(c Conn, rect *Rectangle) error { + var pad4 [4]byte + + if err := binary.Read(c, binary.BigEndian, &pad4); err != nil { + return err + } + + var aten_length uint32 + if err := binary.Read(c, binary.BigEndian, &aten_length); err != nil { + return err + } + enc.AtenLength = aten_length + + if rect.Width == 64896 && rect.Height == 65056 { + if aten_length != 10 && aten_length != 0 { + return fmt.Errorf("screen is off and length is invalid") + } + aten_length = 0 + } + + if c.Width() != rect.Width && c.Height() != rect.Height { + c.SetWidth(rect.Width) + c.SetHeight(rect.Height) + } + + var aten_type uint8 + if err := binary.Read(c, binary.BigEndian, &aten_type); err != nil { + return err + } + enc.AtenType = aten_type + + var pad1 [1]byte + if err := binary.Read(c, binary.BigEndian, &pad1); err != nil { + return err + } + + var subrects uint32 + if err := binary.Read(c, binary.BigEndian, &subrects); err != nil { + return err + } + enc.AtenSubrects = subrects + + var raw_length uint32 + if err := binary.Read(c, binary.BigEndian, &raw_length); err != nil { + return err + } + enc.AtenRawLength = raw_length + + if aten_length != raw_length { + return fmt.Errorf("aten_length != raw_length, %d != %d", aten_length, raw_length) + } + + aten_length -= 10 // skip + + for aten_length > 0 { + switch EncodingType(aten_type) { + case EncAtenHermonSubrect: + encSR := &AtenHermonSubrect{} + if err := encSR.Read(c, rect); err != nil { + return err + } + enc.Encodings = append(enc.Encodings, encSR) + aten_length -= 6 + (16 * 16 * uint32(c.PixelFormat().BPP/8)) + case EncAtenHermonRaw: + encRaw := &RawEncoding{} + if err := encRaw.Read(c, rect); err != nil { + return err + } + enc.Encodings = append(enc.Encodings, encRaw) + aten_length -= uint32(rect.Area()) * uint32(c.PixelFormat().BPP/8) + default: + return fmt.Errorf("unknown aten hermon type %d", aten_type) + + } + } + + if aten_length < 0 { + return fmt.Errorf("aten_len dropped below zero") + } + return nil +} + +func (enc *AtenHermon) Write(c Conn, rect *Rectangle) error { + if !enc.Supported(c) { + for _, ew := range enc.Encodings { + if err := ew.Write(c, rect); err != nil { + return err + } + } + return nil + } + var pad4 [4]byte + + if err := binary.Write(c, binary.BigEndian, pad4); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, enc.AtenLength); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, enc.AtenType); err != nil { + return err + } + + var pad1 [1]byte + if err := binary.Write(c, binary.BigEndian, pad1); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, enc.AtenSubrects); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, enc.AtenRawLength); err != nil { + return err + } + + for _, ew := range enc.Encodings { + if err := ew.Write(c, rect); err != nil { + return err + } + } + return nil +} + +func (*AtenHermonSubrect) Supported(Conn) bool { + return false +} + +func (enc *AtenHermonSubrect) Type() EncodingType { + return EncAtenHermonSubrect +} +func (*AtenHermonSubrect) Reset() error { + return nil +} +func (enc *AtenHermonSubrect) Read(c Conn, rect *Rectangle) error { + if err := binary.Read(c, binary.BigEndian, &enc.A); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.B); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.Y); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.X); err != nil { + return err + } + enc.Data = make([]byte, 16*16*uint32(c.PixelFormat().BPP/8)) + if err := binary.Read(c, binary.BigEndian, &enc.Data); err != nil { + return err + } + return nil +} + +func (enc *AtenHermonSubrect) Write(c Conn, rect *Rectangle) error { + if !enc.Supported(c) { + return nil + } + if err := binary.Write(c, binary.BigEndian, enc.A); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.B); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.Y); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.X); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.Data); err != nil { + return err + } + return nil +} diff --git a/encoding_copyrect.go b/encoding_copyrect.go index 18d25fa..0179611 100644 --- a/encoding_copyrect.go +++ b/encoding_copyrect.go @@ -1,35 +1,35 @@ -package vnc2webm - -import "encoding/binary" - -type CopyRectEncoding struct { - SX, SY uint16 -} - -func (*CopyRectEncoding) Supported(Conn) bool { - return true -} -func (*CopyRectEncoding) Reset() error { - return nil -} -func (*CopyRectEncoding) Type() EncodingType { return EncCopyRect } - -func (enc *CopyRectEncoding) Read(c Conn, rect *Rectangle) error { - if err := binary.Read(c, binary.BigEndian, &enc.SX); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.SY); err != nil { - return err - } - return nil -} - -func (enc *CopyRectEncoding) Write(c Conn, rect *Rectangle) error { - if err := binary.Write(c, binary.BigEndian, enc.SX); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.SY); err != nil { - return err - } - return nil -} +package vnc2video + +import "encoding/binary" + +type CopyRectEncoding struct { + SX, SY uint16 +} + +func (*CopyRectEncoding) Supported(Conn) bool { + return true +} +func (*CopyRectEncoding) Reset() error { + return nil +} +func (*CopyRectEncoding) Type() EncodingType { return EncCopyRect } + +func (enc *CopyRectEncoding) Read(c Conn, rect *Rectangle) error { + if err := binary.Read(c, binary.BigEndian, &enc.SX); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.SY); err != nil { + return err + } + return nil +} + +func (enc *CopyRectEncoding) Write(c Conn, rect *Rectangle) error { + if err := binary.Write(c, binary.BigEndian, enc.SX); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.SY); err != nil { + return err + } + return nil +} diff --git a/encoding_corre.go b/encoding_corre.go new file mode 100644 index 0000000..5a95329 --- /dev/null +++ b/encoding_corre.go @@ -0,0 +1,60 @@ +package vnc2video + +import ( + "encoding/binary" + "io" +) + +type CoRREEncoding struct { + numSubRects uint32 + backgroundColor []byte + subRectData []byte +} + +func (z *CoRREEncoding) Type() int32 { + return 4 +} + +func (z *CoRREEncoding) WriteTo(w io.Writer) (n int, err error) { + binary.Write(w, binary.BigEndian, z.numSubRects) + if err != nil { + return 0, err + } + + w.Write(z.backgroundColor) + if err != nil { + return 0, err + } + + w.Write(z.subRectData) + + if err != nil { + return 0, err + } + b := len(z.backgroundColor) + len(z.subRectData) + 4 + return b, nil +} +func (z *CoRREEncoding) Read(r Conn, rect *Rectangle) error { + //func (z *CoRREEncoding) Read(pixelFmt *PixelFormat, rect *Rectangle, r io.Reader) (Encoding, error) { + bytesPerPixel := int(r.PixelFormat().BPP / 8) + var numOfSubrectangles uint32 + if err := binary.Read(r, binary.BigEndian, &numOfSubrectangles); err != nil { + return err + } + + z.numSubRects = numOfSubrectangles + var err error + //read whole-rect background color + z.backgroundColor, err = ReadBytes(bytesPerPixel, r) + if err != nil { + return err + } + + //read all individual rects (color=BPP + x=16b + y=16b + w=16b + h=16b) + z.subRectData, err = ReadBytes(int(numOfSubrectangles)*(bytesPerPixel+4), r) + if err != nil { + return err + } + + return nil +} diff --git a/encoding_cursor.go b/encoding_cursor.go index 7f5ea9f..fae0281 100644 --- a/encoding_cursor.go +++ b/encoding_cursor.go @@ -1,90 +1,110 @@ -package vnc2webm - -import ( - "encoding/binary" - "image" - "image/draw" -) - -type CursorPseudoEncoding struct { - Colors []Color - BitMask []byte - Image image.Image -} - -func (*CursorPseudoEncoding) Supported(Conn) bool { - return true -} - -func (enc *CursorPseudoEncoding) SetTargetImage(img draw.Image) { - enc.Image = img -} - -func (*CursorPseudoEncoding) Type() EncodingType { return EncCursorPseudo } - -func (enc *CursorPseudoEncoding) Read(c Conn, rect *Rectangle) error { - rgba := make([]byte, int(rect.Height)*int(rect.Width)*int(c.PixelFormat().BPP/8)) - - if err := binary.Read(c, binary.BigEndian, &rgba); err != nil { - return err - } - - bitmask := make([]byte, int((rect.Width+7)/8*rect.Height)) - if err := binary.Read(c, binary.BigEndian, &bitmask); err != nil { - return err - } - - /* - rectStride := 4 * rect.Width - for i := uint16(0); i < rect.Height; i++ { - for j := uint16(0); j < rect.Width; j += 8 { - for idx, k := j/8, 7; k >= 0; k-- { - if (bitmask[idx] & (1 << uint(k))) == 0 { - pIdx := j*4 + i*rectStride - rgbaBuffer[pIdx] = 0 - rgbaBuffer[pIdx+1] = 0 - rgbaBuffer[pIdx+2] = 0 - rgbaBuffer[pIdx+3] = 0 - } - } - } - } - */ - /* - int bytesPerPixel = renderer.getBytesPerPixel(); - int length = rect.width * rect.height * bytesPerPixel; - if (0 == length) - return; - byte[] buffer = ByteBuffer.getInstance().getBuffer(length); - transport.readBytes(buffer, 0, length); - - StringBuilder sb = new StringBuilder(" "); - for (int i=0; i 0 { + enc.Image.Set(x, y, colors[offset]) + } + } + } + + /* + rectStride := 4 * rect.Width + for i := uint16(0); i < rect.Height; i++ { + for j := uint16(0); j < rect.Width; j += 8 { + for idx, k := j/8, 7; k >= 0; k-- { + if (bitmask[idx] & (1 << uint(k))) == 0 { + pIdx := j*4 + i*rectStride + rgba[pIdx] = 0 + rgba[pIdx+1] = 0 + rgba[pIdx+2] = 0 + rgba[pIdx+3] = 0 + } + } + } + } + */ + /* + int bytesPerPixel = renderer.getBytesPerPixel(); + int length = rect.width * rect.height * bytesPerPixel; + if (0 == length) + return; + byte[] buffer = ByteBuffer.getInstance().getBuffer(length); + transport.readBytes(buffer, 0, length); + + StringBuilder sb = new StringBuilder(" "); + for (int i=0; i> pf.RedShift) & uint32(pf.RedMax)), + G: uint8((pixel >> pf.GreenShift) & uint32(pf.GreenMax)), + B: uint8((pixel >> pf.BlueShift) & uint32(pf.BlueMax)), + A: 1, + } + + return &rgb, nil +} + +func (z *HextileEncoding) Type() int32 { + return 5 +} +func (z *HextileEncoding) WriteTo(w io.Writer) (n int, err error) { + return w.Write(z.bytes) +} +func (z *HextileEncoding) Read(r Conn, rect *Rectangle) error { + //func (z *HextileEncoding) Read(pixelFmt *PixelFormat, rect *Rectangle, r io.Reader) (Encoding, error) { + //bytesPerPixel := int(r.PixelFormat().BPP) / 8 + pf := r.PixelFormat() + var bgCol *color.RGBA + var fgCol *color.RGBA + var err error + var dimensions byte + var subencoding byte + + //r.StartByteCollection() + // defer func() { + // z.bytes = r.EndByteCollection() + // }() + + for ty := rect.Y; ty < rect.Y+rect.Height; ty += 16 { + th := 16 + if rect.Y+rect.Height-ty < 16 { + th = int(rect.Y) + int(rect.Height) - int(ty) + } + + for tx := rect.X; tx < rect.X+rect.Width; tx += 16 { + tw := 16 + if rect.X+rect.Width-tx < 16 { + tw = int(rect.X) + int(rect.Width) - int(tx) + } + + //handle Hextile Subrect(tx, ty, tw, th): + subencoding, err = ReadUint8(r) + + if err != nil { + logger.Errorf("HextileEncoding.Read: error in hextile reader: %v", err) + return err + } + + if (subencoding & HextileRaw) != 0 { + rawEnc := r.GetEncInstance(EncRaw) + rawEnc.Read(r, &Rectangle{0, 0, uint16(tw), uint16(th), EncRaw, rawEnc}) + //ReadBytes(tw*th*bytesPerPixel, r) + continue + } + if (subencoding & HextileBackgroundSpecified) != 0 { + //ReadBytes(int(bytesPerPixel), r) + + bgCol, err = ReadColor(r, &pf) + rBounds := image.Rectangle{Min: image.Point{int(tx), int(ty)}, Max: image.Point{int(tw), int(th)}} + FillRect(z.Image, &rBounds, bgCol) + } + if (subencoding & HextileForegroundSpecified) != 0 { + fgCol, err = ReadColor(r, &pf) + } + if (subencoding & HextileAnySubrects) == 0 { + //logger.Debug("hextile reader: no Subrects") + continue + } + + nSubrects, err := ReadUint8(r) + if err != nil { + return err + } + //bufsize := int(nSubrects) * 2 + colorSpecified := ((subencoding & HextileSubrectsColoured) != 0) + for i := 0; i < int(nSubrects); i++ { + var color *color.RGBA + if colorSpecified { + color, err = ReadColor(r, &pf) + if err != nil { + logger.Error("Hextile decoder: problem reading color from connection: ", err) + return err + } + } else { + color = fgCol + } + //int color = colorSpecified ? renderer.readPixelColor(transport) : colors[FG_COLOR_INDEX]; + fgCol = color + dimensions, err = ReadUint8(r) // bits 7-4 for x, bits 3-0 for y + if err != nil { + logger.Error("Hextile decoder: problem reading dimensions from connection: ", err) + return err + } + subtileX := dimensions >> 4 & 0x0f + subtileY := dimensions & 0x0f + dimensions, err = ReadUint8(r) // bits 7-4 for w, bits 3-0 for h + if err != nil { + logger.Error("Hextile decoder: problem reading 2nd dimensions from connection: ", err) + return err + } + subtileWidth := 1 + (dimensions >> 4 & 0x0f) + subtileHeight := 1 + (dimensions & 0x0f) + subrectBounds := image.Rectangle{Min: image.Point{int(tx) + int(subtileX), int(ty) + int(subtileY)}, Max: image.Point{int(subtileWidth), int(subtileHeight)}} + FillRect(z.Image, &subrectBounds, color) + } + } + } + + return nil +} diff --git a/encoding_raw.go b/encoding_raw.go index dd28d3f..a1613d4 100644 --- a/encoding_raw.go +++ b/encoding_raw.go @@ -1,60 +1,60 @@ -package vnc2webm - -import "image" -import "image/draw" -import "image/color" - -type RawEncoding struct { - Image image.Image - //Colors []Color -} - -func (*RawEncoding) Supported(Conn) bool { - return true -} - -func (*RawEncoding) Reset() error { - return nil -} - -func (enc *RawEncoding) Write(c Conn, rect *Rectangle) error { - var err error - // - //for _, clr := range enc.Colors { - // if err = clr.Write(c); err != nil { - // return err - // } - //} - - return err -} -func (enc *RawEncoding) SetTargetImage(img draw.Image) { - enc.Image = img -} - -// Read implements the Encoding interface. -func (enc *RawEncoding) Read(c Conn, rect *Rectangle) error { - pf := c.PixelFormat() - cm := c.ColorMap() - //colors := make([]Color, rect.Area()) - - for y := 0; y < int(rect.Height); y++ { - for x := 0; x < int(rect.Width); x++ { - c1 := NewColor(&pf, &cm) - if err := c1.Read(c); err != nil { - return err - } - - c2:=color.RGBA{R:uint8(c1.R),G:uint8(c1.G),B:uint8(c1.B),A:1} - //c3 := color.RGBAModel.Convert(c2) - - enc.Image.(draw.Image).Set(int(rect.X)+x,int(rect.Y)+y,c2) - //colors[int(y)*int(rect.Width)+int(x)] = *color - } - } - - //enc.Colors = colors - return nil -} - -func (*RawEncoding) Type() EncodingType { return EncRaw } +package vnc2video + +import "image" +import "image/draw" +import "image/color" + +type RawEncoding struct { + Image image.Image + //Colors []Color +} + +func (*RawEncoding) Supported(Conn) bool { + return true +} + +func (*RawEncoding) Reset() error { + return nil +} + +func (enc *RawEncoding) Write(c Conn, rect *Rectangle) error { + var err error + // + //for _, clr := range enc.Colors { + // if err = clr.Write(c); err != nil { + // return err + // } + //} + + return err +} +func (enc *RawEncoding) SetTargetImage(img draw.Image) { + enc.Image = img +} + +// Read implements the Encoding interface. +func (enc *RawEncoding) Read(c Conn, rect *Rectangle) error { + pf := c.PixelFormat() + cm := c.ColorMap() + //colors := make([]Color, rect.Area()) + + for y := 0; y < int(rect.Height); y++ { + for x := 0; x < int(rect.Width); x++ { + c1 := NewColor(&pf, &cm) + if err := c1.Read(c); err != nil { + return err + } + + c2:=color.RGBA{R:uint8(c1.R),G:uint8(c1.G),B:uint8(c1.B),A:1} + //c3 := color.RGBAModel.Convert(c2) + + enc.Image.(draw.Image).Set(int(rect.X)+x,int(rect.Y)+y,c2) + //colors[int(y)*int(rect.Width)+int(x)] = *color + } + } + + //enc.Colors = colors + return nil +} + +func (*RawEncoding) Type() EncodingType { return EncRaw } diff --git a/encoding_rre.go b/encoding_rre.go new file mode 100644 index 0000000..0601288 --- /dev/null +++ b/encoding_rre.go @@ -0,0 +1,64 @@ +package vnc2video + +import ( + "encoding/binary" + "io" + //"image/draw" +) + +type RREEncoding struct { + //Colors []Color + numSubRects uint32 + backgroundColor []byte + subRectData []byte +} + +func (z *RREEncoding) WriteTo(w io.Writer) (n int, err error) { + binary.Write(w, binary.BigEndian, z.numSubRects) + if err != nil { + return 0, err + } + + w.Write(z.backgroundColor) + if err != nil { + return 0, err + } + + w.Write(z.subRectData) + + if err != nil { + return 0, err + } + b := len(z.backgroundColor) + len(z.subRectData) + 4 + return b, nil +} + +func (z *RREEncoding) Type() int32 { + return 2 +} +func (z *RREEncoding) Read(r Conn, rect *Rectangle) error { + //func (z *RREEncoding) Read(pixelFmt *PixelFormat, rect *Rectangle, r io.Reader) (Encoding, error) { + bytesPerPixel := int(r.PixelFormat().BPP / 8) + + var numOfSubrectangles uint32 + if err := binary.Read(r, binary.BigEndian, &numOfSubrectangles); err != nil { + return err + } + + var err error + z.numSubRects = numOfSubrectangles + + //read whole-rect background color + z.backgroundColor, err = ReadBytes(bytesPerPixel, r) + if err != nil { + return err + } + + //read all individual rects (color=bytesPerPixel + x=16b + y=16b + w=16b + h=16b) + z.subRectData, err = ReadBytes(int(numOfSubrectangles)*(bytesPerPixel+8), r) // x+y+w+h=8 bytes + if err != nil { + return err + } + + return nil +} diff --git a/encoding_tight.go b/encoding_tight.go index 9c13de8..defdefa 100644 --- a/encoding_tight.go +++ b/encoding_tight.go @@ -1,744 +1,751 @@ -package vnc2webm - -import ( - "bytes" - "compress/zlib" - "encoding/binary" - "errors" - "fmt" - "image" - "image/color" - "image/draw" - "image/jpeg" - "io" - "math" - "vnc2webm/logger" -) - -//go:generate stringer -type=TightCompression - -type TightCompression uint8 - -const ( - TightCompressionBasic = 0 - TightCompressionFill = 8 - TightCompressionJPEG = 9 - TightCompressionPNG = 10 -) - -//go:generate stringer -type=TightFilter - -type TightFilter uint8 - -const ( - TightFilterCopy = 0 - TightFilterPalette = 1 - TightFilterGradient = 2 -) - -type TightEncoding struct { - Image image.Image - decoders []io.Reader - decoderBuffs []*bytes.Buffer -} - -var instance *TightEncoding -var TightMinToCompress int = 12 - -func (*TightEncoding) Supported(Conn) bool { - return true -} - -func (*TightEncoding) Type() EncodingType { return EncTight } - -func (*TightEncoding) GetInstance() *TightEncoding { - if instance == nil { - instance = &TightEncoding{} - } - return instance -} - -func (enc *TightEncoding) Write(c Conn, rect *Rectangle) error { - return nil -} - -// Read unmarshal color from conn -func getTightColor(c io.Reader, pf *PixelFormat) (*color.RGBA64, error) { - if pf.TrueColor == 0 { - return nil, errors.New("support for non true color formats was not implemented") - } - order := pf.order() - var pixel uint32 - isTightFormat := pf.TrueColor != 0 && pf.Depth == 24 && pf.BPP == 32 && pf.BlueMax <= 255 && pf.RedMax <= 255 && pf.GreenMax <= 255 - if isTightFormat { - //tbytes := make([]byte, 3) - tbytes, err := ReadBytes(3, c) - if err != nil { - return nil, err - } - rgb := color.RGBA64{ - R: uint16(tbytes[0]), - G: uint16(tbytes[1]), - B: uint16(tbytes[2]), - A: uint16(1), - } - return &rgb, nil - } - - switch pf.BPP { - case 8: - var px uint8 - if err := binary.Read(c, order, &px); err != nil { - return nil, err - } - pixel = uint32(px) - case 16: - var px uint16 - if err := binary.Read(c, order, &px); err != nil { - return nil, err - } - pixel = uint32(px) - case 32: - var px uint32 - if err := binary.Read(c, order, &px); err != nil { - return nil, err - } - pixel = uint32(px) - } - - rgb := color.RGBA64{ - R: uint16((pixel >> pf.RedShift) & uint32(pf.RedMax)), - G: uint16((pixel >> pf.GreenShift) & uint32(pf.GreenMax)), - B: uint16((pixel >> pf.BlueShift) & uint32(pf.BlueMax)), - } - - return &rgb, nil -} - -func calcTightBytePerPixel(pf *PixelFormat) int { - bytesPerPixel := int(pf.BPP / 8) - - var bytesPerPixelTight int - if 24 == pf.Depth && 32 == pf.BPP { - bytesPerPixelTight = 3 - } else { - bytesPerPixelTight = bytesPerPixel - } - return bytesPerPixelTight -} - -func (enc *TightEncoding) Reset() error { - //enc.decoders = make([]io.Reader, 4) - //enc.decoderBuffs = make([]*bytes.Buffer, 4) - return nil -} - -func (enc *TightEncoding) resetDecoders(compControl uint8) { - logger.Debugf("###resetDecoders compctl :%d", 0x0F&compControl) - for i := 0; i < 4; i++ { - if (compControl&1) != 0 && enc.decoders[i] != nil { - logger.Debugf("###resetDecoders - resetting decoder #%d", i) - enc.decoders[i] = nil //.(zlib.Resetter).Reset(nil,nil); - } - compControl >>= 1 - } -} - -func (enc *TightEncoding) SetTargetImage(img draw.Image) { - enc.Image = img -} - -var counter int = 0 - -func (enc *TightEncoding) Read(c Conn, rect *Rectangle) error { - - var err error - //////////// - // if counter > 40 { - // os.Exit(1) - // } - //////////// - pixelFmt := c.PixelFormat() - bytesPixel := calcTightBytePerPixel(&pixelFmt) - if enc.Image == nil { - enc.Image = image.NewRGBA(image.Rect(0, 0, int(c.Width()), int(c.Height()))) - } - - //r.StartByteCollection() - - //r.StartByteCollection() - // defer func() { - // t.bytes = r.EndByteCollection() - // }() - - compctl, err := ReadUint8(c) - - ///////////////// - // var out *os.File - // if out == nil { - // out, err = os.Create("./output" + strconv.Itoa(counter) + "-" + strconv.Itoa(int(compctl)) + ".jpg") - // if err != nil { - // fmt.Println(err) - // os.Exit(1) - // } - // } - // defer func() { counter++ }() - // defer jpeg.Encode(out, enc.Image, nil) - ////////////// - logger.Debugf("-----------READ-Tight-encoding compctl=%d -------------", compctl) - - if err != nil { - logger.Errorf("error in handling tight encoding: %v", err) - return err - } - //logger.Debugf("bytesPixel= %d, subencoding= %d", bytesPixel, compctl) - enc.resetDecoders(compctl) - - //move it to position (remove zlib flush commands) - compType := compctl >> 4 & 0x0F - - //logger.Debugf("afterSHL:%d", compType) - switch compType { - case TightCompressionFill: - logger.Debugf("--TIGHT_FILL: reading fill size=%d,counter=%d", bytesPixel, counter) - //read color - pf := c.PixelFormat() - rectColor, err := getTightColor(c, &pf) - if err != nil { - logger.Errorf("error in reading tight encoding: %v", err) - return err - } - - c1 := color.RGBAModel.Convert(rectColor).(color.RGBA) - dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA) - var x, y int - - for y = int(rect.Y); y < int(rect.Height+rect.Y); y++ { - for x = int(rect.X); x < int(rect.Width+rect.X); x++ { - offset := dst.PixOffset(x, y) - dst.Pix[offset+0] = c1.R - dst.Pix[offset+1] = c1.G - dst.Pix[offset+2] = c1.B - dst.Pix[offset+3] = c1.A - } - } - - if bytesPixel != 3 { - return fmt.Errorf("non tight bytesPerPixel format, should be 3 bytes") - } - return nil - case TightCompressionJPEG: - logger.Debugf("--TIGHT_JPEG,counter=%d", counter) - if pixelFmt.BPP == 8 { - return errors.New("Tight encoding: JPEG is not supported in 8 bpp mode") - } - - len, err := readTightLength(c) - - if err != nil { - return err - } - //logger.Debugf("reading jpeg, size=%d\n", len) - jpegBytes, err := ReadBytes(len, c) - if err != nil { - return err - } - //TODO: check if we can read jpeg directly from stream (this is safer for now) - buff := bytes.NewBuffer(jpegBytes) - img, err := jpeg.Decode(buff) - if err != nil { - logger.Error("problem while decoding jpeg:", err) - } - dest := enc.Image.(draw.Image) - draw.Draw(dest, dest.Bounds(), img, image.Point{int(rect.X), int(rect.Y)}, draw.Src) - - - return nil - default: - - if compType > TightCompressionJPEG { - logger.Error("Compression control byte is incorrect!") - } - - enc.handleTightFilters(compctl, &pixelFmt, rect, c) - - return nil - } -} - -func (enc *TightEncoding) handleTightFilters(compCtl uint8, pixelFmt *PixelFormat, rect *Rectangle, r Conn) { - var STREAM_ID_MASK uint8 = 0x30 - var FILTER_ID_MASK uint8 = 0x40 - - var filterid uint8 - var err error - - decoderId := (compCtl & STREAM_ID_MASK) >> 4 - - for len(enc.decoders) < 4 { - enc.decoders = append(enc.decoders, nil) - enc.decoderBuffs = append(enc.decoderBuffs, nil) - } - - if (compCtl & FILTER_ID_MASK) > 0 { - filterid, err = ReadUint8(r) - - if err != nil { - logger.Errorf("error in handling tight encoding, reading filterid: %v", err) - return - } - //logger.Debugf("handleTightFilters: read filter: %d", filterid) - } - - bytesPixel := calcTightBytePerPixel(pixelFmt) - - //logger.Debugf("handleTightFilters: filter: %d", filterid) - - lengthCurrentbpp := int(bytesPixel) * int(rect.Width) * int(rect.Height) - - switch filterid { - case TightFilterPalette: //PALETTE_FILTER - - palette, err := enc.readTightPalette(r, bytesPixel) - if err != nil { - logger.Errorf("handleTightFilters: error in Reading Palette: %v", err) - return - } - logger.Debugf("----PALETTE_FILTER,palette len=%d counter=%d, rect= %v", len(palette), counter, rect) - - //logger.Debugf("got palette: %v", palette) - var dataLength int - if len(palette) == 2 { - dataLength = int(rect.Height) * ((int(rect.Width) + 7) / 8) - } else { - dataLength = int(rect.Width * rect.Height) - } - tightBytes, err := enc.ReadTightData(dataLength, r, int(decoderId)) - //logger.Debugf("got tightBytes: %v", tightBytes) - if err != nil { - logger.Errorf("handleTightFilters: error in handling tight encoding, reading palette filter data: %v", err) - return - } - //logger.Errorf("handleTightFilters: got tight data: %v", tightBytes) - - enc.drawTightPalette(rect, palette, tightBytes) - //enc.Image = myImg - case TightFilterGradient: //GRADIENT_FILTER - logger.Debugf("----GRADIENT_FILTER: bytesPixel=%d, counter=%d", bytesPixel, counter) - //logger.Debugf("usegrad: %d\n", filterid) - data, err := enc.ReadTightData(lengthCurrentbpp, r, int(decoderId)) - if err != nil { - logger.Errorf("handleTightFilters: error in handling tight encoding, Reading GRADIENT_FILTER: %v", err) - return - } - enc.decodeGradData(rect, data) - - case TightFilterCopy: //BASIC_FILTER - //lengthCurrentbpp1 := int(pixelFmt.BPP/8) * int(rect.Width) * int(rect.Height) - logger.Debugf("----BASIC_FILTER: bytesPixel=%d, counter=%d", bytesPixel, counter) - - tightBytes, err := enc.ReadTightData(lengthCurrentbpp, r, int(decoderId)) - if err != nil { - logger.Errorf("handleTightFilters: error in handling tight encoding, Reading BASIC_FILTER: %v", err) - return - } - logger.Debugf("tightBytes len= %d", len(tightBytes)) - enc.drawTightBytes(tightBytes, rect) - default: - logger.Errorf("handleTightFilters: Bad tight filter id: %d", filterid) - return - } - - return -} -func (enc *TightEncoding) drawTightPalette(rect *Rectangle, palette color.Palette, tightBytes []byte) { - myImg := enc.Image.(draw.Image) - bytePos := 0 - bitPos := 0 - var palettePos int - for i := 0; i < int(rect.Height); i++ { - for j := 0; j < int(rect.Width); j++ { - if len(palette) == 2 { - currByte := tightBytes[bytePos] - palettePos = int(currByte&byte(math.Pow(2.0, float64(bitPos)))) >> uint(bitPos) - //logger.Debugf("palletPos=%d, bitpos=%d, bytepos=%d", palettePos, bitPos, bytePos) - bytePos = bytePos + int((bitPos+1.0)/8.0) - bitPos = (bitPos + 1) % 8 - //logger.Debugf("next: bitpos=%d, bytepos=%d", bitPos, bytePos) - } else { - palettePos = int(tightBytes[bytePos]) - bytePos++ - } - myImg.Set(int(rect.X)+j, int(rect.Y)+i, palette[palettePos]) - //logger.Debugf("(%d,%d): pos: %d col:%d", int(rect.X)+j, int(rect.Y)+i, palettePos, palette[palettePos]) - } - } -} -func (enc *TightEncoding) decodeGradData(rect *Rectangle, buffer []byte) { - - logger.Debugf("putting gradient size: %v on image: %v", rect, enc.Image.Bounds()) - - prevRow := make([]byte, rect.Width*3+3) //new byte[w * 3]; - thisRow := make([]byte, rect.Width*3+3) //new byte[w * 3]; - - bIdx := 0 - dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA) - - for i := 0; i < int(rect.Height); i++ { - for j := 3; j < int(rect.Width*3+3); j += 3 { - d := int(0xff&prevRow[j]) + // "upper" pixel (from prev row) - int(0xff&thisRow[j-3]) - // prev pixel - int(0xff&prevRow[j-3]) // "diagonal" prev pixel - if d < 0 { - d = 0 - } - if d > 255 { - d = 255 - } - red := int(buffer[bIdx]) + d - thisRow[j] = byte(red & 255) - - d = int(0xff&prevRow[j+1]) + - int(0xff&thisRow[j+1-3]) - - int(0xff&prevRow[j+1-3]) - if d < 0 { - d = 0 - } - if d > 255 { - d = 255 - } - green := int(buffer[bIdx+1]) + d - thisRow[j+1] = byte(green & 255) - - d = int(0xff&prevRow[j+2]) + - int(0xff&thisRow[j+2-3]) - - int(0xff&prevRow[j+2-3]) - if d < 0 { - d = 0 - } - if d > 255 { - d = 255 - } - blue := int(buffer[bIdx+2]) + d - thisRow[j+2] = byte(blue & 255) - - bIdx += 3 - } - - for idx := 3; idx < (len(thisRow) - 3); idx += 3 { - myColor := color.RGBA{R: (thisRow[idx]), G: (thisRow[idx+1]), B: (thisRow[idx+2]), A: 1} - dst.SetRGBA(idx/3+int(rect.X)-1, int(rect.Y)+i, myColor) - //logger.Debugf("putting pixel: idx=%d, pos=(%d,%d), col=%v", idx, idx/3+int(rect.X), int(rect.Y)+i, myColor) - - } - - // exchange thisRow and prevRow: - tempRow := thisRow - thisRow = prevRow - prevRow = tempRow - } -} - -func (enc *TightEncoding) decodeGradientData(rect *Rectangle, buf []byte) { - logger.Debugf("putting gradient on image: %v", enc.Image.Bounds()) - var dx, dy, c int - prevRow := make([]byte, rect.Width*3) //new byte[w * 3]; - thisRow := make([]byte, rect.Width*3) //new byte[w * 3]; - pix := make([]byte, 3) - est := make([]int, 3) - - dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA) - //offset := int(rect.Y)*dst.Bounds().Max.X + int(rect.X) - - for dy = 0; dy < int(rect.Height); dy++ { - //offset := dst.PixOffset(x, y) - /* First pixel in a row */ - for c = 0; c < 3; c++ { - pix[c] = byte(prevRow[c] + buf[dy*int(rect.Width)*3+c]) - thisRow[c] = pix[c] - } - //logger.Debugf("putting pixel:%d,%d,%d at offset: %d, pixArrayLen= %v, rect=x:%d,y:%d,w:%d,h:%d, Yposition=%d", pix[0], pix[1], pix[2], offset, len(dst.Pix), rect.X, rect.Y, rect.Width, rect.Height, dy) - myColor := color.RGBA{R: (pix[0]), G: (pix[1]), B: (pix[2]), A: 1} - dst.SetRGBA(int(rect.X), dy+int(rect.Y), myColor) - - /* Remaining pixels of a row */ - for dx = 1; dx < int(rect.Width); dx++ { - for c = 0; c < 3; c++ { - est[c] = int((prevRow[dx*3+c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx-1)*3+c] & 0xFF)) - if est[c] > 0xFF { - est[c] = 0xFF - } else if est[c] < 0x00 { - est[c] = 0x00 - } - pix[c] = (byte)(byte(est[c]) + buf[(dy*int(rect.Width)+dx)*3+c]) - thisRow[dx*3+c] = pix[c] - } - //logger.Debugf("putting pixel:%d,%d,%d at offset: %d, pixArrayLen= %v, rect=x:%d,y:%d,w:%d,h:%d, Yposition=%d", pix[0], pix[1], pix[2], offset, len(dst.Pix), x, y, w, h, dy) - myColor := color.RGBA{R: pix[0], G: (pix[1]), B: (pix[2]), A: 1} - dst.SetRGBA(dx+int(rect.X), dy+int(rect.Y), myColor) - - } - - copy(prevRow, thisRow) - } - enc.Image = dst -} - -func ReadBytes(count int, r io.Reader) ([]byte, error) { - buff := make([]byte, count) - - lengthRead, err := io.ReadFull(r, buff) - - //lengthRead, err := r.Read(buff) - if lengthRead != count { - logger.Errorf("RfbReadHelper.ReadBytes unable to read bytes: lengthRead=%d, countExpected=%d", lengthRead, count) - return nil, errors.New("RfbReadHelper.ReadBytes unable to read bytes") - } - - //err := binary.Read(r, binary.BigEndian, &buff) - - if err != nil { - logger.Errorf("RfbReadHelper.ReadBytes error while reading bytes: ", err) - //if err := binary.Read(d.conn, binary.BigEndian, &buff); err != nil { - return nil, err - } - - return buff, nil -} - -func (enc *TightEncoding) readTightPalette(connReader Conn, bytesPixel int) (color.Palette, error) { - - colorCount, err := ReadUint8(connReader) - if err != nil { - logger.Errorf("handleTightFilters: error in handling tight encoding, reading TightFilterPalette: %v", err) - return nil, err - } - - paletteSize := colorCount + 1 // add one more - //logger.Debugf("----PALETTE_FILTER: paletteSize=%d bytesPixel=%d\n", paletteSize, bytesPixel) - //complete palette - paletteColorBytes, err := ReadBytes(int(paletteSize)*bytesPixel, connReader) - if err != nil { - logger.Errorf("handleTightFilters: error in handling tight encoding, reading TightFilterPalette.paletteSize: %v", err) - return nil, err - } - var paletteColors color.Palette = make([]color.Color, 0) - for i := 0; i < int(paletteSize)*bytesPixel; i += 3 { - col := color.RGBA{R: paletteColorBytes[i], G: paletteColorBytes[i+1], B: paletteColorBytes[i+2], A: 1} - paletteColors = append(paletteColors, col) - } - return paletteColors, nil -} - -func ReadUint8(r Conn) (uint8, error) { - var myUint uint8 - if err := binary.Read(r, binary.BigEndian, &myUint); err != nil { - return 0, err - } - - return myUint, nil -} - -func (enc *TightEncoding) ReadTightData(dataSize int, c Conn, decoderId int) ([]byte, error) { - - logger.Debugf(">>> Reading zipped tight data from decoder: %d", decoderId) - if int(dataSize) < TightMinToCompress { - return ReadBytes(int(dataSize), c) - } - zlibDataLen, err := readTightLength(c) - //logger.Debugf("RfbReadHelper.ReadTightData: compactlen=%d", zlibDataLen) - if err != nil { - return nil, err - } - zippedBytes, err := ReadBytes(zlibDataLen, c) - if err != nil { - return nil, err - } - var r io.Reader - if enc.decoders[decoderId] == nil { - b := bytes.NewBuffer(zippedBytes) - r, err = zlib.NewReader(b) - enc.decoders[decoderId] = r - enc.decoderBuffs[decoderId] = b - } else { - b := enc.decoderBuffs[decoderId] - - b.Write(zippedBytes) //set the underlaying buffer to new content (not resetting the decoder zlib stream) - r = enc.decoders[decoderId] - } - - retBytes := make([]byte, dataSize) - count, err := io.ReadFull(r, retBytes) - if err != nil { - return nil, err - } - if count != dataSize { - return nil, errors.New("ReadTightData: reading inflating zip didn't produce expected number of bytes") - } - return retBytes, nil -} - -type TightCC struct { - Compression TightCompression - Filter TightFilter -} - -func readTightCC(c Conn) (*TightCC, error) { - var ccb uint8 // compression control byte - if err := binary.Read(c, binary.BigEndian, &ccb); err != nil { - return nil, err - } - cmp := TightCompression(ccb >> 4) - switch cmp { - case TightCompressionBasic: - return &TightCC{TightCompressionBasic, TightFilterCopy}, nil - case TightCompressionFill: - return &TightCC{TightCompressionFill, TightFilterCopy}, nil - case TightCompressionPNG: - return &TightCC{TightCompressionPNG, TightFilterCopy}, nil - } - return nil, fmt.Errorf("unknown tight compression %d", cmp) -} - -func writeTightCC(c Conn, tcc *TightCC) error { - var ccb uint8 // compression control byte - switch tcc.Compression { - case TightCompressionFill: - ccb = setBit(ccb, 7) - case TightCompressionJPEG: - ccb = setBit(ccb, 7) - ccb = setBit(ccb, 4) - case TightCompressionPNG: - ccb = setBit(ccb, 7) - ccb = setBit(ccb, 5) - } - return binary.Write(c, binary.BigEndian, ccb) -} - -type TightPixel struct { - R uint8 - G uint8 - B uint8 -} - -func writeTightLength(c Conn, l int) error { - var buf []uint8 - - buf = append(buf, uint8(l&0x7F)) - if l > 0x7F { - buf[0] |= 0x80 - buf = append(buf, uint8((l>>7)&0x7F)) - if l > 0x3FFF { - buf[1] |= 0x80 - buf = append(buf, uint8((l>>14)&0xFF)) - } - } - return binary.Write(c, binary.BigEndian, buf) -} - -func readTightLength(c Conn) (int, error) { - var length int - var err error - var b uint8 - - if err = binary.Read(c, binary.BigEndian, &b); err != nil { - return 0, err - } - - length = int(b) & 0x7F - if (b & 0x80) == 0 { - return length, nil - } - - if err = binary.Read(c, binary.BigEndian, &b); err != nil { - return 0, err - } - length |= (int(b) & 0x7F) << 7 - if (b & 0x80) == 0 { - return length, nil - } - - if err = binary.Read(c, binary.BigEndian, &b); err != nil { - return 0, err - } - length |= (int(b) & 0xFF) << 14 - - return length, nil -} - -/** - * Draw byte array bitmap data (for Tight) - */ -func (enc *TightEncoding) drawTightBytes(bytes []byte, rect *Rectangle) { - bytesPos := 0 - myImg := (enc.Image).(draw.Image) - logger.Debugf("drawTightBytes: len(bytes)= %d, %v", len(bytes), rect) - - for ly := rect.Y; ly < rect.Y+rect.Height; ly++ { - for lx := rect.X; lx < rect.X+rect.Width; lx++ { - color := color.RGBA{R: bytes[bytesPos], G: bytes[bytesPos+1], B: bytes[bytesPos+2], A: 1} - //logger.Debugf("drawTightBytes: setting pixel= (%d,%d): %v", int(lx), int(ly), color) - myImg.Set(int(lx), int(ly), color) - - bytesPos += 3 - } - } - //enc.Image = myImg -} - -// /** -// * Draw paletted byte array bitmap data -// * -// * @param buffer bitmap data -// * @param rect bitmap location and dimensions -// * @param palette colour palette -// * @param paletteSize number of colors in palette -// */ -// func (enc *TightPngEncoding) drawBytesWithPalette( buffer []byte, rect *Rectangle, palette []int, int paletteSize) { -// //create palette: -// var imgPalette []color.Color = make([]int, len(palette)) -// for i:=0;len(palette);i++{ -// col := color.RGBA{ -// R:,G:,B:,A:0 -// } -// imgPalette[i]=col -// } - -// //lock.lock(); -// img:=image.Paletted{ - -// } -// // 2 colors -// thisWidth := enc.Image.Bounds().Max.Y -// if paletteSize == 2 { -// var dx, dy, n int; -// i := rect.y * thisWidth + rect.x -// rowBytes := (rect.width + 7) / 8 -// var b byte; - -// for dy = 0; dy < rect.height; dy++ { -// for dx = 0; dx < rect.width / 8; dx++ { -// b = buffer[dy * rowBytes + dx]; -// for n = 7; n >= 0; n-- { -// color := palette[b >> n & 1] -// enc.Image.(draw.Image).Set(0, 0, color.RGBA{R: tpx.R, G: tpx.G, B: tpx.B, A: 1}) -// //pixels[i++] = palette[b >> n & 1]; -// } -// } -// for n = 7; n >= 8 - rect.width % 8; n-- { -// pixels[i++] = palette[buffer[dy * rowBytes + dx] >> n & 1]; -// } -// i += this.width - rect.width; -// } -// } else { -// // 3..255 colors (assuming bytesPixel == 4). -// int i = 0; -// for (int ly = rect.y; ly < rect.y + rect.height; ++ly) { -// for (int lx = rect.x; lx < rect.x + rect.width; ++lx) { -// int pixelsOffset = ly * this.width + lx; -// pixels[pixelsOffset] = palette[buffer[i++] & 0xFF]; -// } -// } -// } -// //lock.unlock(); -// } +package vnc2video + +import ( + "bytes" + "compress/zlib" + "encoding/binary" + "errors" + "fmt" + "image" + "image/color" + "image/draw" + "image/jpeg" + "io" + "math" + "vnc2video/logger" +) + +//go:generate stringer -type=TightCompression + +type TightCompression uint8 + +const ( + TightCompressionBasic = 0 + TightCompressionFill = 8 + TightCompressionJPEG = 9 + TightCompressionPNG = 10 +) + +//go:generate stringer -type=TightFilter + +type TightFilter uint8 + +const ( + TightFilterCopy = 0 + TightFilterPalette = 1 + TightFilterGradient = 2 +) + +type TightEncoding struct { + Image image.Image + decoders []io.Reader + decoderBuffs []*bytes.Buffer +} + +var instance *TightEncoding +var TightMinToCompress int = 12 + +func (*TightEncoding) Supported(Conn) bool { + return true +} + +func (*TightEncoding) Type() EncodingType { return EncTight } + +func (*TightEncoding) GetInstance() *TightEncoding { + if instance == nil { + instance = &TightEncoding{} + } + return instance +} + +func (enc *TightEncoding) Write(c Conn, rect *Rectangle) error { + return nil +} + +// Read unmarshal color from conn +func getTightColor(c io.Reader, pf *PixelFormat) (*color.RGBA64, error) { + if pf.TrueColor == 0 { + return nil, errors.New("support for non true color formats was not implemented") + } + order := pf.order() + var pixel uint32 + isTightFormat := pf.TrueColor != 0 && pf.Depth == 24 && pf.BPP == 32 && pf.BlueMax <= 255 && pf.RedMax <= 255 && pf.GreenMax <= 255 + if isTightFormat { + //tbytes := make([]byte, 3) + tbytes, err := ReadBytes(3, c) + if err != nil { + return nil, err + } + rgb := color.RGBA64{ + R: uint16(tbytes[0]), + G: uint16(tbytes[1]), + B: uint16(tbytes[2]), + A: uint16(1), + } + return &rgb, nil + } + + switch pf.BPP { + case 8: + var px uint8 + if err := binary.Read(c, order, &px); err != nil { + return nil, err + } + pixel = uint32(px) + case 16: + var px uint16 + if err := binary.Read(c, order, &px); err != nil { + return nil, err + } + pixel = uint32(px) + case 32: + var px uint32 + if err := binary.Read(c, order, &px); err != nil { + return nil, err + } + pixel = uint32(px) + } + + rgb := color.RGBA64{ + R: uint16((pixel >> pf.RedShift) & uint32(pf.RedMax)), + G: uint16((pixel >> pf.GreenShift) & uint32(pf.GreenMax)), + B: uint16((pixel >> pf.BlueShift) & uint32(pf.BlueMax)), + } + + return &rgb, nil +} + +func calcTightBytePerPixel(pf *PixelFormat) int { + bytesPerPixel := int(pf.BPP / 8) + + var bytesPerPixelTight int + if 24 == pf.Depth && 32 == pf.BPP { + bytesPerPixelTight = 3 + } else { + bytesPerPixelTight = bytesPerPixel + } + return bytesPerPixelTight +} + +func (enc *TightEncoding) Reset() error { + //enc.decoders = make([]io.Reader, 4) + //enc.decoderBuffs = make([]*bytes.Buffer, 4) + return nil +} + +func (enc *TightEncoding) resetDecoders(compControl uint8) { + logger.Debugf("###resetDecoders compctl :%d", 0x0F&compControl) + for i := 0; i < 4; i++ { + if (compControl&1) != 0 && enc.decoders[i] != nil { + logger.Debugf("###resetDecoders - resetting decoder #%d", i) + enc.decoders[i] = nil //.(zlib.Resetter).Reset(nil,nil); + } + compControl >>= 1 + } +} + +func (enc *TightEncoding) SetTargetImage(img draw.Image) { + enc.Image = img +} + +var counter int = 0 + +func (enc *TightEncoding) Read(c Conn, rect *Rectangle) error { + + var err error + //////////// + // if counter > 40 { + // os.Exit(1) + // } + //////////// + pixelFmt := c.PixelFormat() + bytesPixel := calcTightBytePerPixel(&pixelFmt) + if enc.Image == nil { + enc.Image = image.NewRGBA(image.Rect(0, 0, int(c.Width()), int(c.Height()))) + } + + //r.StartByteCollection() + + //r.StartByteCollection() + // defer func() { + // t.bytes = r.EndByteCollection() + // }() + + compctl, err := ReadUint8(c) + + ///////////////// + // var out *os.File + // if out == nil { + // out, err = os.Create("./output" + strconv.Itoa(counter) + "-" + strconv.Itoa(int(compctl)) + ".jpg") + // if err != nil { + // fmt.Println(err) + // os.Exit(1) + // } + // } + // defer func() { counter++ }() + // defer jpeg.Encode(out, enc.Image, nil) + ////////////// + logger.Debugf("-----------READ-Tight-encoding compctl=%d -------------", compctl) + + if err != nil { + logger.Errorf("error in handling tight encoding: %v", err) + return err + } + //logger.Debugf("bytesPixel= %d, subencoding= %d", bytesPixel, compctl) + enc.resetDecoders(compctl) + + //move it to position (remove zlib flush commands) + compType := compctl >> 4 & 0x0F + + //logger.Debugf("afterSHL:%d", compType) + switch compType { + case TightCompressionFill: + logger.Debugf("--TIGHT_FILL: reading fill size=%d,counter=%d", bytesPixel, counter) + //read color + pf := c.PixelFormat() + rectColor, err := getTightColor(c, &pf) + if err != nil { + logger.Errorf("error in reading tight encoding: %v", err) + return err + } + + c1 := color.RGBAModel.Convert(rectColor).(color.RGBA) + dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA) + var x, y int + + for y = int(rect.Y); y < int(rect.Height+rect.Y); y++ { + for x = int(rect.X); x < int(rect.Width+rect.X); x++ { + offset := dst.PixOffset(x, y) + dst.Pix[offset+0] = c1.R + dst.Pix[offset+1] = c1.G + dst.Pix[offset+2] = c1.B + dst.Pix[offset+3] = c1.A + } + } + + if bytesPixel != 3 { + return fmt.Errorf("non tight bytesPerPixel format, should be 3 bytes") + } + return nil + case TightCompressionJPEG: + logger.Debugf("--TIGHT_JPEG,counter=%d", counter) + if pixelFmt.BPP == 8 { + return errors.New("Tight encoding: JPEG is not supported in 8 bpp mode") + } + + len, err := readTightLength(c) + + if err != nil { + return err + } + //logger.Debugf("reading jpeg, size=%d\n", len) + jpegBytes, err := ReadBytes(len, c) + if err != nil { + return err + } + //TODO: check if we can read jpeg directly from stream (this is safer for now) + buff := bytes.NewBuffer(jpegBytes) + img, err := jpeg.Decode(buff) + if err != nil { + logger.Error("problem while decoding jpeg:", err) + } + dest := enc.Image.(draw.Image) + draw.Draw(dest, dest.Bounds(), img, image.Point{int(rect.X), int(rect.Y)}, draw.Src) + + return nil + default: + + if compType > TightCompressionJPEG { + logger.Error("Compression control byte is incorrect!") + } + + enc.handleTightFilters(compctl, &pixelFmt, rect, c) + + return nil + } +} + +func (enc *TightEncoding) handleTightFilters(compCtl uint8, pixelFmt *PixelFormat, rect *Rectangle, r Conn) { + var STREAM_ID_MASK uint8 = 0x30 + var FILTER_ID_MASK uint8 = 0x40 + + var filterid uint8 + var err error + + decoderId := (compCtl & STREAM_ID_MASK) >> 4 + + for len(enc.decoders) < 4 { + enc.decoders = append(enc.decoders, nil) + enc.decoderBuffs = append(enc.decoderBuffs, nil) + } + + if (compCtl & FILTER_ID_MASK) > 0 { + filterid, err = ReadUint8(r) + + if err != nil { + logger.Errorf("error in handling tight encoding, reading filterid: %v", err) + return + } + //logger.Debugf("handleTightFilters: read filter: %d", filterid) + } + + bytesPixel := calcTightBytePerPixel(pixelFmt) + + //logger.Debugf("handleTightFilters: filter: %d", filterid) + + lengthCurrentbpp := int(bytesPixel) * int(rect.Width) * int(rect.Height) + + switch filterid { + case TightFilterPalette: //PALETTE_FILTER + + palette, err := enc.readTightPalette(r, bytesPixel) + if err != nil { + logger.Errorf("handleTightFilters: error in Reading Palette: %v", err) + return + } + logger.Debugf("----PALETTE_FILTER,palette len=%d counter=%d, rect= %v", len(palette), counter, rect) + + //logger.Debugf("got palette: %v", palette) + var dataLength int + if len(palette) == 2 { + dataLength = int(rect.Height) * ((int(rect.Width) + 7) / 8) + } else { + dataLength = int(rect.Width * rect.Height) + } + tightBytes, err := enc.ReadTightData(dataLength, r, int(decoderId)) + //logger.Debugf("got tightBytes: %v", tightBytes) + if err != nil { + logger.Errorf("handleTightFilters: error in handling tight encoding, reading palette filter data: %v", err) + return + } + //logger.Errorf("handleTightFilters: got tight data: %v", tightBytes) + + enc.drawTightPalette(rect, palette, tightBytes) + //enc.Image = myImg + case TightFilterGradient: //GRADIENT_FILTER + logger.Debugf("----GRADIENT_FILTER: bytesPixel=%d, counter=%d", bytesPixel, counter) + //logger.Debugf("usegrad: %d\n", filterid) + data, err := enc.ReadTightData(lengthCurrentbpp, r, int(decoderId)) + if err != nil { + logger.Errorf("handleTightFilters: error in handling tight encoding, Reading GRADIENT_FILTER: %v", err) + return + } + enc.decodeGradData(rect, data) + + case TightFilterCopy: //BASIC_FILTER + //lengthCurrentbpp1 := int(pixelFmt.BPP/8) * int(rect.Width) * int(rect.Height) + logger.Debugf("----BASIC_FILTER: bytesPixel=%d, counter=%d", bytesPixel, counter) + + tightBytes, err := enc.ReadTightData(lengthCurrentbpp, r, int(decoderId)) + if err != nil { + logger.Errorf("handleTightFilters: error in handling tight encoding, Reading BASIC_FILTER: %v", err) + return + } + logger.Debugf("tightBytes len= %d", len(tightBytes)) + enc.drawTightBytes(tightBytes, rect) + default: + logger.Errorf("handleTightFilters: Bad tight filter id: %d", filterid) + return + } + + return +} +func (enc *TightEncoding) drawTightPalette(rect *Rectangle, palette color.Palette, tightBytes []byte) { + myImg := enc.Image.(draw.Image) + bytePos := 0 + bitPos := 0 + var palettePos int + for i := 0; i < int(rect.Height); i++ { + for j := 0; j < int(rect.Width); j++ { + if len(palette) == 2 { + currByte := tightBytes[bytePos] + palettePos = int(currByte&byte(math.Pow(2.0, float64(bitPos)))) >> uint(bitPos) + //logger.Debugf("palletPos=%d, bitpos=%d, bytepos=%d", palettePos, bitPos, bytePos) + bytePos = bytePos + int((bitPos+1.0)/8.0) + bitPos = (bitPos + 1) % 8 + //logger.Debugf("next: bitpos=%d, bytepos=%d", bitPos, bytePos) + } else { + palettePos = int(tightBytes[bytePos]) + bytePos++ + } + myImg.Set(int(rect.X)+j, int(rect.Y)+i, palette[palettePos]) + //logger.Debugf("(%d,%d): pos: %d col:%d", int(rect.X)+j, int(rect.Y)+i, palettePos, palette[palettePos]) + } + } +} +func (enc *TightEncoding) decodeGradData(rect *Rectangle, buffer []byte) { + + logger.Debugf("putting gradient size: %v on image: %v", rect, enc.Image.Bounds()) + + prevRow := make([]byte, rect.Width*3+3) //new byte[w * 3]; + thisRow := make([]byte, rect.Width*3+3) //new byte[w * 3]; + + bIdx := 0 + dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA) + + for i := 0; i < int(rect.Height); i++ { + for j := 3; j < int(rect.Width*3+3); j += 3 { + d := int(0xff&prevRow[j]) + // "upper" pixel (from prev row) + int(0xff&thisRow[j-3]) - // prev pixel + int(0xff&prevRow[j-3]) // "diagonal" prev pixel + if d < 0 { + d = 0 + } + if d > 255 { + d = 255 + } + red := int(buffer[bIdx]) + d + thisRow[j] = byte(red & 255) + + d = int(0xff&prevRow[j+1]) + + int(0xff&thisRow[j+1-3]) - + int(0xff&prevRow[j+1-3]) + if d < 0 { + d = 0 + } + if d > 255 { + d = 255 + } + green := int(buffer[bIdx+1]) + d + thisRow[j+1] = byte(green & 255) + + d = int(0xff&prevRow[j+2]) + + int(0xff&thisRow[j+2-3]) - + int(0xff&prevRow[j+2-3]) + if d < 0 { + d = 0 + } + if d > 255 { + d = 255 + } + blue := int(buffer[bIdx+2]) + d + thisRow[j+2] = byte(blue & 255) + + bIdx += 3 + } + + for idx := 3; idx < (len(thisRow) - 3); idx += 3 { + myColor := color.RGBA{R: (thisRow[idx]), G: (thisRow[idx+1]), B: (thisRow[idx+2]), A: 1} + dst.SetRGBA(idx/3+int(rect.X)-1, int(rect.Y)+i, myColor) + //logger.Debugf("putting pixel: idx=%d, pos=(%d,%d), col=%v", idx, idx/3+int(rect.X), int(rect.Y)+i, myColor) + + } + + // exchange thisRow and prevRow: + tempRow := thisRow + thisRow = prevRow + prevRow = tempRow + } +} + +func (enc *TightEncoding) decodeGradientData(rect *Rectangle, buf []byte) { + logger.Debugf("putting gradient on image: %v", enc.Image.Bounds()) + var dx, dy, c int + prevRow := make([]byte, rect.Width*3) //new byte[w * 3]; + thisRow := make([]byte, rect.Width*3) //new byte[w * 3]; + pix := make([]byte, 3) + est := make([]int, 3) + + dst := (enc.Image).(*image.RGBA) // enc.Image.(*image.RGBA) + //offset := int(rect.Y)*dst.Bounds().Max.X + int(rect.X) + + for dy = 0; dy < int(rect.Height); dy++ { + //offset := dst.PixOffset(x, y) + /* First pixel in a row */ + for c = 0; c < 3; c++ { + pix[c] = byte(prevRow[c] + buf[dy*int(rect.Width)*3+c]) + thisRow[c] = pix[c] + } + //logger.Debugf("putting pixel:%d,%d,%d at offset: %d, pixArrayLen= %v, rect=x:%d,y:%d,w:%d,h:%d, Yposition=%d", pix[0], pix[1], pix[2], offset, len(dst.Pix), rect.X, rect.Y, rect.Width, rect.Height, dy) + myColor := color.RGBA{R: (pix[0]), G: (pix[1]), B: (pix[2]), A: 1} + dst.SetRGBA(int(rect.X), dy+int(rect.Y), myColor) + + /* Remaining pixels of a row */ + for dx = 1; dx < int(rect.Width); dx++ { + for c = 0; c < 3; c++ { + est[c] = int((prevRow[dx*3+c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx-1)*3+c] & 0xFF)) + if est[c] > 0xFF { + est[c] = 0xFF + } else if est[c] < 0x00 { + est[c] = 0x00 + } + pix[c] = (byte)(byte(est[c]) + buf[(dy*int(rect.Width)+dx)*3+c]) + thisRow[dx*3+c] = pix[c] + } + //logger.Debugf("putting pixel:%d,%d,%d at offset: %d, pixArrayLen= %v, rect=x:%d,y:%d,w:%d,h:%d, Yposition=%d", pix[0], pix[1], pix[2], offset, len(dst.Pix), x, y, w, h, dy) + myColor := color.RGBA{R: pix[0], G: (pix[1]), B: (pix[2]), A: 1} + dst.SetRGBA(dx+int(rect.X), dy+int(rect.Y), myColor) + + } + + copy(prevRow, thisRow) + } + enc.Image = dst +} + +func ReadBytes(count int, r io.Reader) ([]byte, error) { + buff := make([]byte, count) + + lengthRead, err := io.ReadFull(r, buff) + + //lengthRead, err := r.Read(buff) + if lengthRead != count { + logger.Errorf("RfbReadHelper.ReadBytes unable to read bytes: lengthRead=%d, countExpected=%d", lengthRead, count) + return nil, errors.New("RfbReadHelper.ReadBytes unable to read bytes") + } + + //err := binary.Read(r, binary.BigEndian, &buff) + + if err != nil { + logger.Errorf("RfbReadHelper.ReadBytes error while reading bytes: ", err) + //if err := binary.Read(d.conn, binary.BigEndian, &buff); err != nil { + return nil, err + } + + return buff, nil +} + +func (enc *TightEncoding) readTightPalette(connReader Conn, bytesPixel int) (color.Palette, error) { + + colorCount, err := ReadUint8(connReader) + if err != nil { + logger.Errorf("handleTightFilters: error in handling tight encoding, reading TightFilterPalette: %v", err) + return nil, err + } + + paletteSize := colorCount + 1 // add one more + //logger.Debugf("----PALETTE_FILTER: paletteSize=%d bytesPixel=%d\n", paletteSize, bytesPixel) + //complete palette + paletteColorBytes, err := ReadBytes(int(paletteSize)*bytesPixel, connReader) + if err != nil { + logger.Errorf("handleTightFilters: error in handling tight encoding, reading TightFilterPalette.paletteSize: %v", err) + return nil, err + } + var paletteColors color.Palette = make([]color.Color, 0) + for i := 0; i < int(paletteSize)*bytesPixel; i += 3 { + col := color.RGBA{R: paletteColorBytes[i], G: paletteColorBytes[i+1], B: paletteColorBytes[i+2], A: 1} + paletteColors = append(paletteColors, col) + } + return paletteColors, nil +} + +func ReadUint8(r io.Reader) (uint8, error) { + var myUint uint8 + if err := binary.Read(r, binary.BigEndian, &myUint); err != nil { + return 0, err + } + + return myUint, nil +} +func ReadUint32(r io.Reader) (uint32, error) { + var myUint uint32 + if err := binary.Read(r, binary.BigEndian, &myUint); err != nil { + return 0, err + } + + return myUint, nil +} + +func (enc *TightEncoding) ReadTightData(dataSize int, c Conn, decoderId int) ([]byte, error) { + + logger.Debugf(">>> Reading zipped tight data from decoder: %d", decoderId) + if int(dataSize) < TightMinToCompress { + return ReadBytes(int(dataSize), c) + } + zlibDataLen, err := readTightLength(c) + //logger.Debugf("RfbReadHelper.ReadTightData: compactlen=%d", zlibDataLen) + if err != nil { + return nil, err + } + zippedBytes, err := ReadBytes(zlibDataLen, c) + if err != nil { + return nil, err + } + var r io.Reader + if enc.decoders[decoderId] == nil { + b := bytes.NewBuffer(zippedBytes) + r, err = zlib.NewReader(b) + enc.decoders[decoderId] = r + enc.decoderBuffs[decoderId] = b + } else { + b := enc.decoderBuffs[decoderId] + + b.Write(zippedBytes) //set the underlaying buffer to new content (not resetting the decoder zlib stream) + r = enc.decoders[decoderId] + } + + retBytes := make([]byte, dataSize) + count, err := io.ReadFull(r, retBytes) + if err != nil { + return nil, err + } + if count != dataSize { + return nil, errors.New("ReadTightData: reading inflating zip didn't produce expected number of bytes") + } + return retBytes, nil +} + +type TightCC struct { + Compression TightCompression + Filter TightFilter +} + +func readTightCC(c Conn) (*TightCC, error) { + var ccb uint8 // compression control byte + if err := binary.Read(c, binary.BigEndian, &ccb); err != nil { + return nil, err + } + cmp := TightCompression(ccb >> 4) + switch cmp { + case TightCompressionBasic: + return &TightCC{TightCompressionBasic, TightFilterCopy}, nil + case TightCompressionFill: + return &TightCC{TightCompressionFill, TightFilterCopy}, nil + case TightCompressionPNG: + return &TightCC{TightCompressionPNG, TightFilterCopy}, nil + } + return nil, fmt.Errorf("unknown tight compression %d", cmp) +} + +func writeTightCC(c Conn, tcc *TightCC) error { + var ccb uint8 // compression control byte + switch tcc.Compression { + case TightCompressionFill: + ccb = setBit(ccb, 7) + case TightCompressionJPEG: + ccb = setBit(ccb, 7) + ccb = setBit(ccb, 4) + case TightCompressionPNG: + ccb = setBit(ccb, 7) + ccb = setBit(ccb, 5) + } + return binary.Write(c, binary.BigEndian, ccb) +} + +type TightPixel struct { + R uint8 + G uint8 + B uint8 +} + +func writeTightLength(c Conn, l int) error { + var buf []uint8 + + buf = append(buf, uint8(l&0x7F)) + if l > 0x7F { + buf[0] |= 0x80 + buf = append(buf, uint8((l>>7)&0x7F)) + if l > 0x3FFF { + buf[1] |= 0x80 + buf = append(buf, uint8((l>>14)&0xFF)) + } + } + return binary.Write(c, binary.BigEndian, buf) +} + +func readTightLength(c Conn) (int, error) { + var length int + var err error + var b uint8 + + if err = binary.Read(c, binary.BigEndian, &b); err != nil { + return 0, err + } + + length = int(b) & 0x7F + if (b & 0x80) == 0 { + return length, nil + } + + if err = binary.Read(c, binary.BigEndian, &b); err != nil { + return 0, err + } + length |= (int(b) & 0x7F) << 7 + if (b & 0x80) == 0 { + return length, nil + } + + if err = binary.Read(c, binary.BigEndian, &b); err != nil { + return 0, err + } + length |= (int(b) & 0xFF) << 14 + + return length, nil +} + +/** + * Draw byte array bitmap data (for Tight) + */ +func (enc *TightEncoding) drawTightBytes(bytes []byte, rect *Rectangle) { + bytesPos := 0 + myImg := (enc.Image).(draw.Image) + logger.Debugf("drawTightBytes: len(bytes)= %d, %v", len(bytes), rect) + + for ly := rect.Y; ly < rect.Y+rect.Height; ly++ { + for lx := rect.X; lx < rect.X+rect.Width; lx++ { + color := color.RGBA{R: bytes[bytesPos], G: bytes[bytesPos+1], B: bytes[bytesPos+2], A: 1} + //logger.Debugf("drawTightBytes: setting pixel= (%d,%d): %v", int(lx), int(ly), color) + myImg.Set(int(lx), int(ly), color) + + bytesPos += 3 + } + } + //enc.Image = myImg +} + +// /** +// * Draw paletted byte array bitmap data +// * +// * @param buffer bitmap data +// * @param rect bitmap location and dimensions +// * @param palette colour palette +// * @param paletteSize number of colors in palette +// */ +// func (enc *TightPngEncoding) drawBytesWithPalette( buffer []byte, rect *Rectangle, palette []int, int paletteSize) { +// //create palette: +// var imgPalette []color.Color = make([]int, len(palette)) +// for i:=0;len(palette);i++{ +// col := color.RGBA{ +// R:,G:,B:,A:0 +// } +// imgPalette[i]=col +// } + +// //lock.lock(); +// img:=image.Paletted{ + +// } +// // 2 colors +// thisWidth := enc.Image.Bounds().Max.Y +// if paletteSize == 2 { +// var dx, dy, n int; +// i := rect.y * thisWidth + rect.x +// rowBytes := (rect.width + 7) / 8 +// var b byte; + +// for dy = 0; dy < rect.height; dy++ { +// for dx = 0; dx < rect.width / 8; dx++ { +// b = buffer[dy * rowBytes + dx]; +// for n = 7; n >= 0; n-- { +// color := palette[b >> n & 1] +// enc.Image.(draw.Image).Set(0, 0, color.RGBA{R: tpx.R, G: tpx.G, B: tpx.B, A: 1}) +// //pixels[i++] = palette[b >> n & 1]; +// } +// } +// for n = 7; n >= 8 - rect.width % 8; n-- { +// pixels[i++] = palette[buffer[dy * rowBytes + dx] >> n & 1]; +// } +// i += this.width - rect.width; +// } +// } else { +// // 3..255 colors (assuming bytesPixel == 4). +// int i = 0; +// for (int ly = rect.y; ly < rect.y + rect.height; ++ly) { +// for (int lx = rect.x; lx < rect.x + rect.width; ++lx) { +// int pixelsOffset = ly * this.width + lx; +// pixels[pixelsOffset] = palette[buffer[i++] & 0xFF]; +// } +// } +// } +// //lock.unlock(); +// } diff --git a/encoding_tightpng.go b/encoding_tightpng.go index a39e413..c48a311 100644 --- a/encoding_tightpng.go +++ b/encoding_tightpng.go @@ -1,95 +1,95 @@ -package vnc2webm - -import ( - "bytes" - "encoding/binary" - "fmt" - "image" - "image/color" - "image/draw" - "image/png" - "io" - "vnc2webm/logger" -) - -func (*TightPngEncoding) Supported(Conn) bool { - return true -} -func (*TightPngEncoding) Reset() error { - return nil -} - -func (enc *TightPngEncoding) Write(c Conn, rect *Rectangle) error { - if err := writeTightCC(c, enc.TightCC); err != nil { - return err - } - cmp := enc.TightCC.Compression - switch cmp { - case TightCompressionPNG: - buf := bPool.Get().(*bytes.Buffer) - buf.Reset() - defer bPool.Put(buf) - pngEnc := &png.Encoder{CompressionLevel: png.BestSpeed} - //pngEnc := &png.Encoder{CompressionLevel: png.NoCompression} - if err := pngEnc.Encode(buf, enc.Image); err != nil { - return err - } - if err := writeTightLength(c, buf.Len()); err != nil { - return err - } - - if _, err := buf.WriteTo(c); err != nil { - return err - } - case TightCompressionFill: - var tpx TightPixel - r, g, b, _ := enc.Image.At(0, 0).RGBA() - tpx.R = uint8(r) - tpx.G = uint8(g) - tpx.B = uint8(b) - if err := binary.Write(c, binary.BigEndian, tpx); err != nil { - return err - } - default: - return fmt.Errorf("unknown tight compression %d", cmp) - } - return nil -} - -type TightPngEncoding struct { - TightCC *TightCC - Image image.Image -} - -func (*TightPngEncoding) Type() EncodingType { return EncTightPng } - -func (enc *TightPngEncoding) Read(c Conn, rect *Rectangle) error { - tcc, err := readTightCC(c) - logger.Debug("starting to read a tight rect: %v", rect) - if err != nil { - return err - } - enc.TightCC = tcc - cmp := enc.TightCC.Compression - switch cmp { - case TightCompressionPNG: - l, err := readTightLength(c) - if err != nil { - return err - } - enc.Image, err = png.Decode(io.LimitReader(c, int64(l))) - if err != nil { - return err - } - case TightCompressionFill: - var tpx TightPixel - if err := binary.Read(c, binary.BigEndian, &tpx); err != nil { - return err - } - enc.Image = image.NewRGBA(image.Rect(0, 0, 1, 1)) - enc.Image.(draw.Image).Set(0, 0, color.RGBA{R: tpx.R, G: tpx.G, B: tpx.B, A: 1}) - default: - return fmt.Errorf("unknown compression %d", cmp) - } - return nil -} +package vnc2video + +import ( + "bytes" + "encoding/binary" + "fmt" + "image" + "image/color" + "image/draw" + "image/png" + "io" + "vnc2video/logger" +) + +func (*TightPngEncoding) Supported(Conn) bool { + return true +} +func (*TightPngEncoding) Reset() error { + return nil +} + +func (enc *TightPngEncoding) Write(c Conn, rect *Rectangle) error { + if err := writeTightCC(c, enc.TightCC); err != nil { + return err + } + cmp := enc.TightCC.Compression + switch cmp { + case TightCompressionPNG: + buf := bPool.Get().(*bytes.Buffer) + buf.Reset() + defer bPool.Put(buf) + pngEnc := &png.Encoder{CompressionLevel: png.BestSpeed} + //pngEnc := &png.Encoder{CompressionLevel: png.NoCompression} + if err := pngEnc.Encode(buf, enc.Image); err != nil { + return err + } + if err := writeTightLength(c, buf.Len()); err != nil { + return err + } + + if _, err := buf.WriteTo(c); err != nil { + return err + } + case TightCompressionFill: + var tpx TightPixel + r, g, b, _ := enc.Image.At(0, 0).RGBA() + tpx.R = uint8(r) + tpx.G = uint8(g) + tpx.B = uint8(b) + if err := binary.Write(c, binary.BigEndian, tpx); err != nil { + return err + } + default: + return fmt.Errorf("unknown tight compression %d", cmp) + } + return nil +} + +type TightPngEncoding struct { + TightCC *TightCC + Image image.Image +} + +func (*TightPngEncoding) Type() EncodingType { return EncTightPng } + +func (enc *TightPngEncoding) Read(c Conn, rect *Rectangle) error { + tcc, err := readTightCC(c) + logger.Debug("starting to read a tight rect: %v", rect) + if err != nil { + return err + } + enc.TightCC = tcc + cmp := enc.TightCC.Compression + switch cmp { + case TightCompressionPNG: + l, err := readTightLength(c) + if err != nil { + return err + } + enc.Image, err = png.Decode(io.LimitReader(c, int64(l))) + if err != nil { + return err + } + case TightCompressionFill: + var tpx TightPixel + if err := binary.Read(c, binary.BigEndian, &tpx); err != nil { + return err + } + enc.Image = image.NewRGBA(image.Rect(0, 0, 1, 1)) + enc.Image.(draw.Image).Set(0, 0, color.RGBA{R: tpx.R, G: tpx.G, B: tpx.B, A: 1}) + default: + return fmt.Errorf("unknown compression %d", cmp) + } + return nil +} diff --git a/encoding_util.go b/encoding_util.go new file mode 100644 index 0000000..f989f46 --- /dev/null +++ b/encoding_util.go @@ -0,0 +1,19 @@ +package vnc2video + +import ( + "image" + "image/color" + "image/draw" +) + +func FillRect(img draw.Image, rect *image.Rectangle, c color.Color) { + for x := 0; x < rect.Max.X; x++ { + for y := 0; y < rect.Max.Y; y++ { + img.Set(x, y, c) + } + } +} + +func DrawLine(img draw.Image, rect *image.Rectangle, c color.Color) { + +} diff --git a/encoding_xcursor.go b/encoding_xcursor.go index 5a60452..e8f469f 100644 --- a/encoding_xcursor.go +++ b/encoding_xcursor.go @@ -1,92 +1,92 @@ -package vnc2webm - -import ( - "encoding/binary" - "math" -) - -type XCursorPseudoEncoding struct { - PrimaryR uint8 - PrimaryG uint8 - PrimaryB uint8 - SecondaryR uint8 - SecondaryG uint8 - SecondaryB uint8 - Bitmap []byte - Bitmask []byte -} - -func (*XCursorPseudoEncoding) Supported(Conn) bool { - return true -} -func (*XCursorPseudoEncoding) Reset() error { - return nil -} - -func (*XCursorPseudoEncoding) Type() EncodingType { return EncXCursorPseudo } - -// Read implements the Encoding interface. -func (enc *XCursorPseudoEncoding) Read(c Conn, rect *Rectangle) error { - if err := binary.Read(c, binary.BigEndian, &enc.PrimaryR); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.PrimaryG); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.PrimaryB); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.SecondaryR); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.SecondaryG); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.SecondaryB); err != nil { - return err - } - - bitmapsize := int(math.Floor((float64(rect.Width)+7)/8) * float64(rect.Height)) - bitmasksize := int(math.Floor((float64(rect.Width)+7)/8) * float64(rect.Height)) - - enc.Bitmap = make([]byte, bitmapsize) - enc.Bitmask = make([]byte, bitmasksize) - if err := binary.Read(c, binary.BigEndian, &enc.Bitmap); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &enc.Bitmask); err != nil { - return err - } - - return nil -} - -func (enc *XCursorPseudoEncoding) Write(c Conn, rect *Rectangle) error { - if err := binary.Write(c, binary.BigEndian, enc.PrimaryR); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.PrimaryG); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.PrimaryB); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.SecondaryR); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.SecondaryG); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.SecondaryB); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, enc.Bitmap); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, enc.Bitmask); err != nil { - return err - } - - return nil -} +package vnc2video + +import ( + "encoding/binary" + "math" +) + +type XCursorPseudoEncoding struct { + PrimaryR uint8 + PrimaryG uint8 + PrimaryB uint8 + SecondaryR uint8 + SecondaryG uint8 + SecondaryB uint8 + Bitmap []byte + Bitmask []byte +} + +func (*XCursorPseudoEncoding) Supported(Conn) bool { + return true +} +func (*XCursorPseudoEncoding) Reset() error { + return nil +} + +func (*XCursorPseudoEncoding) Type() EncodingType { return EncXCursorPseudo } + +// Read implements the Encoding interface. +func (enc *XCursorPseudoEncoding) Read(c Conn, rect *Rectangle) error { + if err := binary.Read(c, binary.BigEndian, &enc.PrimaryR); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.PrimaryG); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.PrimaryB); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.SecondaryR); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.SecondaryG); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.SecondaryB); err != nil { + return err + } + + bitmapsize := int(math.Floor((float64(rect.Width)+7)/8) * float64(rect.Height)) + bitmasksize := int(math.Floor((float64(rect.Width)+7)/8) * float64(rect.Height)) + + enc.Bitmap = make([]byte, bitmapsize) + enc.Bitmask = make([]byte, bitmasksize) + if err := binary.Read(c, binary.BigEndian, &enc.Bitmap); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &enc.Bitmask); err != nil { + return err + } + + return nil +} + +func (enc *XCursorPseudoEncoding) Write(c Conn, rect *Rectangle) error { + if err := binary.Write(c, binary.BigEndian, enc.PrimaryR); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.PrimaryG); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.PrimaryB); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.SecondaryR); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.SecondaryG); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.SecondaryB); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, enc.Bitmap); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, enc.Bitmask); err != nil { + return err + } + + return nil +} diff --git a/encoding_zlib.go b/encoding_zlib.go new file mode 100644 index 0000000..41f7488 --- /dev/null +++ b/encoding_zlib.go @@ -0,0 +1,38 @@ +package vnc2video + +import ( + "bytes" + "encoding/binary" + "io" +) + +type ZLibEncoding struct { + bytes []byte +} + +func (z *ZLibEncoding) Type() int32 { + return 6 +} +func (z *ZLibEncoding) WriteTo(w io.Writer) (n int, err error) { + return w.Write(z.bytes) +} +func (z *ZLibEncoding) Read(r Conn, rect *Rectangle) error { + //func (z *ZLibEncoding) Read(pixelFmt *PixelFormat, rect *Rectangle, r io.Reader) (Encoding, error) { + //conn := RfbReadHelper{Reader:r} + //conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat} + //bytesPerPixel := c.PixelFormat.BPP / 8 + bytes := &bytes.Buffer{} + len, err := ReadUint32(r) + if err != nil { + return err + } + + binary.Write(bytes, binary.BigEndian, len) + _, err = ReadBytes(int(len), r) + if err != nil { + return err + } + //StoreBytes(bytes, bts) + z.bytes = bytes.Bytes() + return nil +} diff --git a/encoding_zrle.go b/encoding_zrle.go new file mode 100644 index 0000000..4df2a33 --- /dev/null +++ b/encoding_zrle.go @@ -0,0 +1,37 @@ +package vnc2video + +import ( + "bytes" + "encoding/binary" + "io" +) + +type ZRLEEncoding struct { + bytes []byte +} + +func (z *ZRLEEncoding) Type() int32 { + return 16 +} + +func (z *ZRLEEncoding) WriteTo(w io.Writer) (n int, err error) { + return w.Write(z.bytes) +} +func (z *ZRLEEncoding) Read(r Conn, rect *Rectangle) error { + //func (z *ZRLEEncoding) Read(pixelFmt *PixelFormat, rect *Rectangle, r io.Reader) (Encoding, error) { + + bytes := &bytes.Buffer{} + len, err := ReadUint32(r) + if err != nil { + return err + } + + binary.Write(bytes, binary.BigEndian, len) + _, err = ReadBytes(int(len), r) + if err != nil { + return err + } + //StoreBytes(bytes, bts) + z.bytes = bytes.Bytes() + return nil +} diff --git a/encodingtype_string.go b/encodingtype_string.go index 5fea279..b1ffead 100644 --- a/encodingtype_string.go +++ b/encodingtype_string.go @@ -1,69 +1,69 @@ -// Code generated by "stringer -type=EncodingType"; DO NOT EDIT. - -package vnc2webm - -import "fmt" - -const _EncodingType_name = "EncContinuousUpdatesPseudoEncFencePseudoEncClientRedirectEncXvpPseudoEncExtendedDesktopSizePseudoEncDesktopNamePseudoEncTightPngEncQEMUExtendedKeyEventPseudoEncQEMUPointerMotionChangePseudoEncCompressionLevel1EncCompressionLevel2EncCompressionLevel3EncCompressionLevel4EncCompressionLevel5EncCompressionLevel6EncCompressionLevel7EncCompressionLevel8EncCompressionLevel9EncCompressionLevel10EncXCursorPseudoEncCursorPseudoEncLastRectPseudoEncDesktopSizePseudoEncJPEGQualityLevelPseudo1EncJPEGQualityLevelPseudo2EncJPEGQualityLevelPseudo3EncJPEGQualityLevelPseudo4EncJPEGQualityLevelPseudo5EncJPEGQualityLevelPseudo6EncJPEGQualityLevelPseudo7EncJPEGQualityLevelPseudo8EncJPEGQualityLevelPseudo9EncJPEGQualityLevelPseudo10EncRawEncCopyRectEncRREEncCoRREEncHextileEncZlibEncTightEncZlibHexEncUltra1EncUltra2EncTRLEEncZRLEEncJPEGEncJRLEEncAtenAST2100EncAtenASTJPEGEncAtenHermonEncAtenYarkonEncAtenPilot3" - -var _EncodingType_map = map[EncodingType]string{ - -313: _EncodingType_name[0:26], - -312: _EncodingType_name[26:40], - -311: _EncodingType_name[40:57], - -309: _EncodingType_name[57:69], - -308: _EncodingType_name[69:97], - -307: _EncodingType_name[97:117], - -260: _EncodingType_name[117:128], - -258: _EncodingType_name[128:157], - -257: _EncodingType_name[157:189], - -256: _EncodingType_name[189:209], - -255: _EncodingType_name[209:229], - -254: _EncodingType_name[229:249], - -253: _EncodingType_name[249:269], - -252: _EncodingType_name[269:289], - -251: _EncodingType_name[289:309], - -250: _EncodingType_name[309:329], - -249: _EncodingType_name[329:349], - -248: _EncodingType_name[349:369], - -247: _EncodingType_name[369:390], - -240: _EncodingType_name[390:406], - -239: _EncodingType_name[406:421], - -224: _EncodingType_name[421:438], - -223: _EncodingType_name[438:458], - -32: _EncodingType_name[458:484], - -31: _EncodingType_name[484:510], - -30: _EncodingType_name[510:536], - -29: _EncodingType_name[536:562], - -28: _EncodingType_name[562:588], - -27: _EncodingType_name[588:614], - -26: _EncodingType_name[614:640], - -25: _EncodingType_name[640:666], - -24: _EncodingType_name[666:692], - -23: _EncodingType_name[692:719], - 0: _EncodingType_name[719:725], - 1: _EncodingType_name[725:736], - 2: _EncodingType_name[736:742], - 4: _EncodingType_name[742:750], - 5: _EncodingType_name[750:760], - 6: _EncodingType_name[760:767], - 7: _EncodingType_name[767:775], - 8: _EncodingType_name[775:785], - 9: _EncodingType_name[785:794], - 10: _EncodingType_name[794:803], - 15: _EncodingType_name[803:810], - 16: _EncodingType_name[810:817], - 21: _EncodingType_name[817:824], - 22: _EncodingType_name[824:831], - 87: _EncodingType_name[831:845], - 88: _EncodingType_name[845:859], - 89: _EncodingType_name[859:872], - 96: _EncodingType_name[872:885], - 97: _EncodingType_name[885:898], -} - -func (i EncodingType) String() string { - if str, ok := _EncodingType_map[i]; ok { - return str - } - return fmt.Sprintf("EncodingType(%d)", i) -} +// Code generated by "stringer -type=EncodingType"; DO NOT EDIT. + +package vnc2video + +import "fmt" + +const _EncodingType_name = "EncContinuousUpdatesPseudoEncFencePseudoEncClientRedirectEncXvpPseudoEncExtendedDesktopSizePseudoEncDesktopNamePseudoEncTightPngEncQEMUExtendedKeyEventPseudoEncQEMUPointerMotionChangePseudoEncCompressionLevel1EncCompressionLevel2EncCompressionLevel3EncCompressionLevel4EncCompressionLevel5EncCompressionLevel6EncCompressionLevel7EncCompressionLevel8EncCompressionLevel9EncCompressionLevel10EncXCursorPseudoEncCursorPseudoEncLastRectPseudoEncDesktopSizePseudoEncJPEGQualityLevelPseudo1EncJPEGQualityLevelPseudo2EncJPEGQualityLevelPseudo3EncJPEGQualityLevelPseudo4EncJPEGQualityLevelPseudo5EncJPEGQualityLevelPseudo6EncJPEGQualityLevelPseudo7EncJPEGQualityLevelPseudo8EncJPEGQualityLevelPseudo9EncJPEGQualityLevelPseudo10EncRawEncCopyRectEncRREEncCoRREEncHextileEncZlibEncTightEncZlibHexEncUltra1EncUltra2EncTRLEEncZRLEEncJPEGEncJRLEEncAtenAST2100EncAtenASTJPEGEncAtenHermonEncAtenYarkonEncAtenPilot3" + +var _EncodingType_map = map[EncodingType]string{ + -313: _EncodingType_name[0:26], + -312: _EncodingType_name[26:40], + -311: _EncodingType_name[40:57], + -309: _EncodingType_name[57:69], + -308: _EncodingType_name[69:97], + -307: _EncodingType_name[97:117], + -260: _EncodingType_name[117:128], + -258: _EncodingType_name[128:157], + -257: _EncodingType_name[157:189], + -256: _EncodingType_name[189:209], + -255: _EncodingType_name[209:229], + -254: _EncodingType_name[229:249], + -253: _EncodingType_name[249:269], + -252: _EncodingType_name[269:289], + -251: _EncodingType_name[289:309], + -250: _EncodingType_name[309:329], + -249: _EncodingType_name[329:349], + -248: _EncodingType_name[349:369], + -247: _EncodingType_name[369:390], + -240: _EncodingType_name[390:406], + -239: _EncodingType_name[406:421], + -224: _EncodingType_name[421:438], + -223: _EncodingType_name[438:458], + -32: _EncodingType_name[458:484], + -31: _EncodingType_name[484:510], + -30: _EncodingType_name[510:536], + -29: _EncodingType_name[536:562], + -28: _EncodingType_name[562:588], + -27: _EncodingType_name[588:614], + -26: _EncodingType_name[614:640], + -25: _EncodingType_name[640:666], + -24: _EncodingType_name[666:692], + -23: _EncodingType_name[692:719], + 0: _EncodingType_name[719:725], + 1: _EncodingType_name[725:736], + 2: _EncodingType_name[736:742], + 4: _EncodingType_name[742:750], + 5: _EncodingType_name[750:760], + 6: _EncodingType_name[760:767], + 7: _EncodingType_name[767:775], + 8: _EncodingType_name[775:785], + 9: _EncodingType_name[785:794], + 10: _EncodingType_name[794:803], + 15: _EncodingType_name[803:810], + 16: _EncodingType_name[810:817], + 21: _EncodingType_name[817:824], + 22: _EncodingType_name[824:831], + 87: _EncodingType_name[831:845], + 88: _EncodingType_name[845:859], + 89: _EncodingType_name[859:872], + 96: _EncodingType_name[872:885], + 97: _EncodingType_name[885:898], +} + +func (i EncodingType) String() string { + if str, ok := _EncodingType_map[i]; ok { + return str + } + return fmt.Sprintf("EncodingType(%d)", i) +} diff --git a/example/client/main.go b/example/client/main.go index dca9d67..91f0e9f 100644 --- a/example/client/main.go +++ b/example/client/main.go @@ -1,102 +1,102 @@ -package main - -import ( - "context" - "image" - "net" - "os" - "time" - vnc "vnc2webm" - "vnc2webm/encoders" - "vnc2webm/logger" -) - -func main() { - - // Establish TCP connection to VNC server. - nc, err := net.DialTimeout("tcp", os.Args[1], 5*time.Second) - if err != nil { - logger.Fatalf("Error connecting to VNC host. %v", err) - } - - logger.Debugf("starting up the client, connecting to: %s", os.Args[1]) - // Negotiate connection with the server. - cchServer := make(chan vnc.ServerMessage) - cchClient := make(chan vnc.ClientMessage) - errorCh := make(chan error) - - ccfg := &vnc.ClientConfig{ - SecurityHandlers: []vnc.SecurityHandler{ - //&vnc.ClientAuthATEN{Username: []byte(os.Args[2]), Password: []byte(os.Args[3])} - &vnc.ClientAuthVNC{Password: []byte("12345")}, - &vnc.ClientAuthNone{}, - }, - PixelFormat: vnc.PixelFormat32bit, - ClientMessageCh: cchClient, - ServerMessageCh: cchServer, - Messages: vnc.DefaultServerMessages, - Encodings: []vnc.Encoding{&vnc.RawEncoding{}, &vnc.TightEncoding{}}, - ErrorCh: errorCh, - } - - cc, err := vnc.Connect(context.Background(), nc, ccfg) - if err != nil { - logger.Fatalf("Error negotiating connection to VNC host. %v", err) - } - // out, err := os.Create("./output" + strconv.Itoa(counter) + ".jpg") - // if err != nil { - // fmt.Println(err) - // os.Exit(1) - // } - //vcodec := &encoders.MJPegImageEncoder{Quality: 60, Framerate: 6} - vcodec := &encoders.X264ImageEncoder{} - //vcodec := &encoders.DV8ImageEncoder{} - //vcodec := &encoders.DV9ImageEncoder{} - - //counter := 0 - //vcodec.Init("./output" + strconv.Itoa(counter)) - go vcodec.Run("./ffmpeg", "./output.mp4") - - screenImage := image.NewRGBA(image.Rect(0, 0, int(cc.Width()), int(cc.Height()))) - for _, enc := range ccfg.Encodings { - myRenderer, ok := enc.(vnc.Renderer) - - if ok { - myRenderer.SetTargetImage(screenImage) - } - } - // var out *os.File - - logger.Debugf("connected to: %s", os.Args[1]) - defer cc.Close() - - cc.SetEncodings([]vnc.EncodingType{vnc.EncTight}) - //rect := image.Rect(0, 0, int(cc.Width()), int(cc.Height())) - //screenImage := image.NewRGBA64(rect) - // Process messages coming in on the ServerMessage channel. - for { - select { - case err := <-errorCh: - panic(err) - case msg := <-cchClient: - logger.Debugf("Received client message type:%v msg:%v\n", msg.Type(), msg) - case msg := <-cchServer: - logger.Debugf("Received server message type:%v msg:%v\n", msg.Type(), msg) - - // out, err := os.Create("./output" + strconv.Itoa(counter) + ".jpg") - // if err != nil { - // fmt.Println(err) - // os.Exit(1) - // } - if msg.Type() == vnc.FramebufferUpdateMsgType { - //counter++ - //jpeg.Encode(out, screenImage, nil) - vcodec.Encode(screenImage) - reqMsg := vnc.FramebufferUpdateRequest{Inc: 1, X: 0, Y: 0, Width: cc.Width(), Height: cc.Height()} - //cc.ResetAllEncodings() - reqMsg.Write(cc) - } - } - } - //cc.Wait() -} +package main + +import ( + "context" + "image" + "net" + "os" + "time" + vnc "vnc2video" + "vnc2video/encoders" + "vnc2video/logger" +) + +func main() { + + // Establish TCP connection to VNC server. + nc, err := net.DialTimeout("tcp", os.Args[1], 5*time.Second) + if err != nil { + logger.Fatalf("Error connecting to VNC host. %v", err) + } + + logger.Debugf("starting up the client, connecting to: %s", os.Args[1]) + // Negotiate connection with the server. + cchServer := make(chan vnc.ServerMessage) + cchClient := make(chan vnc.ClientMessage) + errorCh := make(chan error) + + ccfg := &vnc.ClientConfig{ + SecurityHandlers: []vnc.SecurityHandler{ + //&vnc.ClientAuthATEN{Username: []byte(os.Args[2]), Password: []byte(os.Args[3])} + &vnc.ClientAuthVNC{Password: []byte("12345")}, + &vnc.ClientAuthNone{}, + }, + PixelFormat: vnc.PixelFormat32bit, + ClientMessageCh: cchClient, + ServerMessageCh: cchServer, + Messages: vnc.DefaultServerMessages, + Encodings: []vnc.Encoding{&vnc.RawEncoding{}, &vnc.TightEncoding{}}, + ErrorCh: errorCh, + } + + cc, err := vnc.Connect(context.Background(), nc, ccfg) + if err != nil { + logger.Fatalf("Error negotiating connection to VNC host. %v", err) + } + // out, err := os.Create("./output" + strconv.Itoa(counter) + ".jpg") + // if err != nil { + // fmt.Println(err) + // os.Exit(1) + // } + //vcodec := &encoders.MJPegImageEncoder{Quality: 60, Framerate: 6} + vcodec := &encoders.X264ImageEncoder{} + //vcodec := &encoders.DV8ImageEncoder{} + //vcodec := &encoders.DV9ImageEncoder{} + + //counter := 0 + //vcodec.Init("./output" + strconv.Itoa(counter)) + go vcodec.Run("./ffmpeg", "./output.mp4") + + screenImage := image.NewRGBA(image.Rect(0, 0, int(cc.Width()), int(cc.Height()))) + for _, enc := range ccfg.Encodings { + myRenderer, ok := enc.(vnc.Renderer) + + if ok { + myRenderer.SetTargetImage(screenImage) + } + } + // var out *os.File + + logger.Debugf("connected to: %s", os.Args[1]) + defer cc.Close() + + cc.SetEncodings([]vnc.EncodingType{vnc.EncTight}) + //rect := image.Rect(0, 0, int(cc.Width()), int(cc.Height())) + //screenImage := image.NewRGBA64(rect) + // Process messages coming in on the ServerMessage channel. + for { + select { + case err := <-errorCh: + panic(err) + case msg := <-cchClient: + logger.Debugf("Received client message type:%v msg:%v\n", msg.Type(), msg) + case msg := <-cchServer: + logger.Debugf("Received server message type:%v msg:%v\n", msg.Type(), msg) + + // out, err := os.Create("./output" + strconv.Itoa(counter) + ".jpg") + // if err != nil { + // fmt.Println(err) + // os.Exit(1) + // } + if msg.Type() == vnc.FramebufferUpdateMsgType { + //counter++ + //jpeg.Encode(out, screenImage, nil) + vcodec.Encode(screenImage) + reqMsg := vnc.FramebufferUpdateRequest{Inc: 1, X: 0, Y: 0, Width: cc.Width(), Height: cc.Height()} + //cc.ResetAllEncodings() + reqMsg.Write(cc) + } + } + } + //cc.Wait() +} diff --git a/example/file-reader/main.go b/example/file-reader/main.go index 56df755..dbdacb8 100644 --- a/example/file-reader/main.go +++ b/example/file-reader/main.go @@ -1,61 +1,61 @@ -package main - -import ( - "image" - "os" - vnc "vnc2webm" - "vnc2webm/encoders" - "vnc2webm/logger" - "path/filepath" -) - -func main() { - - if len(os.Args) <= 1 { - logger.Errorf("please provide a fbs file name") - return - } - if _, err := os.Stat(os.Args[1]); os.IsNotExist(err) { - logger.Errorf("File doesn't exist", err) - return - } - encs := []vnc.Encoding{ - &vnc.RawEncoding{}, - &vnc.TightEncoding{}, - } - - fbs, err := vnc.NewFbsConn( - os.Args[1], - encs, - ) - if err != nil { - logger.Error("failed to open fbs reader:", err) - //return nil, err - } - - //launch video encoding process: - vcodec := &encoders.X264ImageEncoder{} - //vcodec := &encoders.DV8ImageEncoder{} - //vcodec := &encoders.DV9ImageEncoder{} - dir, err := filepath.Abs(filepath.Dir(os.Args[0])) - logger.Debugf("current dir: %s", dir) - go vcodec.Run("./ffmpeg", "./output.mp4") - - screenImage := image.NewRGBA(image.Rect(0, 0, int(fbs.Width()), int(fbs.Height()))) - for _, enc := range encs { - myRenderer, ok := enc.(vnc.Renderer) - - if ok { - myRenderer.SetTargetImage(screenImage) - } - } - - - msgReader := vnc.NewFBSPlayHelper(fbs) - - //loop over all messages, feed images to video codec: - for { - msgReader.ReadFbsMessage() - vcodec.Encode(screenImage) - } -} +package main + +import ( + "image" + "os" + vnc "vnc2video" + "vnc2video/encoders" + "vnc2video/logger" + "path/filepath" +) + +func main() { + + if len(os.Args) <= 1 { + logger.Errorf("please provide a fbs file name") + return + } + if _, err := os.Stat(os.Args[1]); os.IsNotExist(err) { + logger.Errorf("File doesn't exist", err) + return + } + encs := []vnc.Encoding{ + &vnc.RawEncoding{}, + &vnc.TightEncoding{}, + } + + fbs, err := vnc.NewFbsConn( + os.Args[1], + encs, + ) + if err != nil { + logger.Error("failed to open fbs reader:", err) + //return nil, err + } + + //launch video encoding process: + vcodec := &encoders.X264ImageEncoder{} + //vcodec := &encoders.DV8ImageEncoder{} + //vcodec := &encoders.DV9ImageEncoder{} + dir, err := filepath.Abs(filepath.Dir(os.Args[0])) + logger.Debugf("current dir: %s", dir) + go vcodec.Run("./ffmpeg", "./output.mp4") + + screenImage := image.NewRGBA(image.Rect(0, 0, int(fbs.Width()), int(fbs.Height()))) + for _, enc := range encs { + myRenderer, ok := enc.(vnc.Renderer) + + if ok { + myRenderer.SetTargetImage(screenImage) + } + } + + + msgReader := vnc.NewFBSPlayHelper(fbs) + + //loop over all messages, feed images to video codec: + for { + msgReader.ReadFbsMessage() + vcodec.Encode(screenImage) + } +} diff --git a/example/proxy/main.go b/example/proxy/main.go index 81f8dda..edb7468 100644 --- a/example/proxy/main.go +++ b/example/proxy/main.go @@ -1,255 +1,255 @@ -package main - -import ( - "bytes" - "context" - "encoding/base64" - "fmt" - "io" - "net" - "net/http" - _ "net/http/pprof" - "net/url" - "strings" - "sync" - "time" - vnc "vnc2webm" - "vnc2webm/logger" -) - -type Auth struct { - Username []byte - Password []byte -} - -type Proxy struct { - cc vnc.Conn - conns chan vnc.Conn - inp chan vnc.ClientMessage - out chan vnc.ServerMessage -} - -var ( - cliconns = make(map[string]*Proxy) - srvconns = make(map[vnc.Conn]string) - m sync.Mutex -) - -func newConn(hostport string, password []byte) (vnc.Conn, chan vnc.ClientMessage, chan vnc.ServerMessage, chan vnc.Conn, error) { - fmt.Printf("new conn to %s with %s\n", hostport, password) - if cc, ok := cliconns[hostport]; ok { - return cc.cc, cc.inp, cc.out, cc.conns, nil - } - c, err := net.DialTimeout("tcp", hostport, 10*time.Second) - if err != nil { - return nil, nil, nil, nil, err - } - cchServer := make(chan vnc.ServerMessage) - cchClient := make(chan vnc.ClientMessage) - errorCh := make(chan error) - ccfg := &vnc.ClientConfig{ - SecurityHandlers: []vnc.SecurityHandler{&vnc.ClientAuthVNC{Password: password}}, - PixelFormat: vnc.PixelFormat32bit, - ClientMessageCh: cchClient, - ServerMessageCh: cchServer, - //ServerMessages: vnc.DefaultServerMessages, - Encodings: []vnc.Encoding{&vnc.RawEncoding{}}, - ErrorCh: errorCh, - } - csrv := make(chan vnc.Conn) - inp := make(chan vnc.ClientMessage) - out := make(chan vnc.ServerMessage) - fmt.Printf("connect to vnc\n") - cc, err := vnc.Connect(context.Background(), c, ccfg) - if err != nil { - return nil, nil, nil, nil, err - } - fmt.Printf("connected to vnc %#+v\n", cc) - ds := &vnc.DefaultClientMessageHandler{} - go ds.Handle(cc) - go handleIO(cc, inp, out, csrv) - - return cc, inp, out, csrv, nil -} - -func handleIO(cli vnc.Conn, inp chan vnc.ClientMessage, out chan vnc.ServerMessage, csrv chan vnc.Conn) { - fmt.Printf("handle io\n") - ccfg := cli.Config().(*vnc.ClientConfig) - defer cli.Close() - var conns []vnc.Conn - //var prepared bool - - for { - select { - case err := <-ccfg.ErrorCh: - for _, srv := range conns { - srv.Close() - } - fmt.Printf("err %v\n", err) - return - case msg := <-ccfg.ServerMessageCh: - for _, srv := range conns { - scfg := srv.Config().(*vnc.ServerConfig) - scfg.ServerMessageCh <- msg - } - case msg := <-inp: - // messages from real clients - fmt.Printf("3 %#+v\n", msg) - switch msg.Type() { - case vnc.SetPixelFormatMsgType: - - case vnc.SetEncodingsMsgType: - var encTypes []vnc.EncodingType - encs := []vnc.Encoding{ - // &vnc.TightPngEncoding{}, - &vnc.CopyRectEncoding{}, - &vnc.RawEncoding{}, - } - for _, senc := range encs { - for _, cenc := range msg.(*vnc.SetEncodings).Encodings { - if cenc == senc.Type() { - encTypes = append(encTypes, senc.Type()) - } - } - } - ccfg.ClientMessageCh <- &vnc.SetEncodings{Encodings: encTypes} - default: - ccfg.ClientMessageCh <- msg - } - case msg := <-out: - fmt.Printf("4 %#+v\n", msg) - case srv := <-csrv: - conns = append(conns, srv) - } - - } - -} - -type HijackHandler struct{} - -func (*HijackHandler) Handle(c vnc.Conn) error { - m.Lock() - defer m.Unlock() - hostport, ok := srvconns[c] - if !ok { - return fmt.Errorf("client connect in server pool not found") - } - proxy, ok := cliconns[hostport] - if !ok { - return fmt.Errorf("client connect to qemu not found") - } - cfg := c.Config().(*vnc.ServerConfig) - cfg.ClientMessageCh = proxy.inp - cfg.ServerMessageCh = proxy.out - - proxy.conns <- c - ds := &vnc.DefaultServerMessageHandler{} - go ds.Handle(c) - return nil -} - -type AuthVNCHTTP struct { - c *http.Client - vnc.ServerAuthVNC -} - -func (auth *AuthVNCHTTP) Auth(c vnc.Conn) error { - auth.ServerAuthVNC.Challenge = []byte("clodo.ruclodo.ru") - if err := auth.ServerAuthVNC.WriteChallenge(c); err != nil { - return err - } - if err := auth.ServerAuthVNC.ReadChallenge(c); err != nil { - return err - } - - buf := new(bytes.Buffer) - enc := base64.NewEncoder(base64.StdEncoding, buf) - enc.Write(auth.ServerAuthVNC.Crypted) - enc.Close() - - v := url.Values{} - v.Set("hash", buf.String()) - buf.Reset() - src, _, _ := net.SplitHostPort(c.Conn().RemoteAddr().String()) - v.Set("ip", src) - res, err := auth.c.PostForm("https://api.ix.clodo.ru/system/vnc", v) - if err != nil { - return err - } - if res.StatusCode != 200 || res.Body == nil { - if res.Body != nil { - io.Copy(buf, res.Body) - } - fmt.Printf("failed to get auth data: code %d body %s\n", res.StatusCode, buf.String()) - defer buf.Reset() - return fmt.Errorf("failed to get auth data: code %d body %s", res.StatusCode, buf.String()) - } - _, err = io.Copy(buf, res.Body) - if err != nil { - return fmt.Errorf("failed to get auth data: %s", err.Error()) - } - logger.Infof("http auth: %s\n", buf.Bytes()) - res.Body.Close() - data := strings.Split(buf.String(), " ") - if len(data) < 2 { - return fmt.Errorf("failed to get auth data data invalid") - } - buf.Reset() - - hostport := string(data[0]) - password := []byte(data[1]) - - m.Lock() - defer m.Unlock() - cc, inp, out, conns, err := newConn(hostport, password) - if err != nil { - return err - } - cliconns[hostport] = &Proxy{cc, conns, inp, out} - srvconns[c] = hostport - c.SetWidth(cc.Width()) - c.SetHeight(cc.Height()) - return nil -} - -func (*AuthVNCHTTP) Type() vnc.SecurityType { - return vnc.SecTypeVNC -} - -func (*AuthVNCHTTP) SubType() vnc.SecuritySubType { - return vnc.SecSubTypeUnknown -} - -func main() { - go func() { - logger.Info(http.ListenAndServe(":6060", nil)) - }() - - ln, err := net.Listen("tcp", ":6900") - if err != nil { - logger.Fatalf("Error listen. %v", err) - } - - schClient := make(chan vnc.ClientMessage) - schServer := make(chan vnc.ServerMessage) - - scfg := &vnc.ServerConfig{ - SecurityHandlers: []vnc.SecurityHandler{ - &AuthVNCHTTP{c: &http.Client{}}, - }, - Encodings: []vnc.Encoding{ - // &vnc.TightPngEncoding{}, - &vnc.CopyRectEncoding{}, - &vnc.RawEncoding{}, - }, - PixelFormat: vnc.PixelFormat32bit, - ClientMessageCh: schClient, - ServerMessageCh: schServer, - //ClientMessages: vnc.DefaultClientMessages, - DesktopName: []byte("vnc proxy"), - } - scfg.Handlers = append(scfg.Handlers, vnc.DefaultServerHandlers...) - scfg.Handlers = append(scfg.Handlers[:len(scfg.Handlers)-1], &HijackHandler{}) - vnc.Serve(context.Background(), ln, scfg) -} +package main + +import ( + "bytes" + "context" + "encoding/base64" + "fmt" + "io" + "net" + "net/http" + _ "net/http/pprof" + "net/url" + "strings" + "sync" + "time" + vnc "vnc2video" + "vnc2video/logger" +) + +type Auth struct { + Username []byte + Password []byte +} + +type Proxy struct { + cc vnc.Conn + conns chan vnc.Conn + inp chan vnc.ClientMessage + out chan vnc.ServerMessage +} + +var ( + cliconns = make(map[string]*Proxy) + srvconns = make(map[vnc.Conn]string) + m sync.Mutex +) + +func newConn(hostport string, password []byte) (vnc.Conn, chan vnc.ClientMessage, chan vnc.ServerMessage, chan vnc.Conn, error) { + fmt.Printf("new conn to %s with %s\n", hostport, password) + if cc, ok := cliconns[hostport]; ok { + return cc.cc, cc.inp, cc.out, cc.conns, nil + } + c, err := net.DialTimeout("tcp", hostport, 10*time.Second) + if err != nil { + return nil, nil, nil, nil, err + } + cchServer := make(chan vnc.ServerMessage) + cchClient := make(chan vnc.ClientMessage) + errorCh := make(chan error) + ccfg := &vnc.ClientConfig{ + SecurityHandlers: []vnc.SecurityHandler{&vnc.ClientAuthVNC{Password: password}}, + PixelFormat: vnc.PixelFormat32bit, + ClientMessageCh: cchClient, + ServerMessageCh: cchServer, + //ServerMessages: vnc.DefaultServerMessages, + Encodings: []vnc.Encoding{&vnc.RawEncoding{}}, + ErrorCh: errorCh, + } + csrv := make(chan vnc.Conn) + inp := make(chan vnc.ClientMessage) + out := make(chan vnc.ServerMessage) + fmt.Printf("connect to vnc\n") + cc, err := vnc.Connect(context.Background(), c, ccfg) + if err != nil { + return nil, nil, nil, nil, err + } + fmt.Printf("connected to vnc %#+v\n", cc) + ds := &vnc.DefaultClientMessageHandler{} + go ds.Handle(cc) + go handleIO(cc, inp, out, csrv) + + return cc, inp, out, csrv, nil +} + +func handleIO(cli vnc.Conn, inp chan vnc.ClientMessage, out chan vnc.ServerMessage, csrv chan vnc.Conn) { + fmt.Printf("handle io\n") + ccfg := cli.Config().(*vnc.ClientConfig) + defer cli.Close() + var conns []vnc.Conn + //var prepared bool + + for { + select { + case err := <-ccfg.ErrorCh: + for _, srv := range conns { + srv.Close() + } + fmt.Printf("err %v\n", err) + return + case msg := <-ccfg.ServerMessageCh: + for _, srv := range conns { + scfg := srv.Config().(*vnc.ServerConfig) + scfg.ServerMessageCh <- msg + } + case msg := <-inp: + // messages from real clients + fmt.Printf("3 %#+v\n", msg) + switch msg.Type() { + case vnc.SetPixelFormatMsgType: + + case vnc.SetEncodingsMsgType: + var encTypes []vnc.EncodingType + encs := []vnc.Encoding{ + // &vnc.TightPngEncoding{}, + &vnc.CopyRectEncoding{}, + &vnc.RawEncoding{}, + } + for _, senc := range encs { + for _, cenc := range msg.(*vnc.SetEncodings).Encodings { + if cenc == senc.Type() { + encTypes = append(encTypes, senc.Type()) + } + } + } + ccfg.ClientMessageCh <- &vnc.SetEncodings{Encodings: encTypes} + default: + ccfg.ClientMessageCh <- msg + } + case msg := <-out: + fmt.Printf("4 %#+v\n", msg) + case srv := <-csrv: + conns = append(conns, srv) + } + + } + +} + +type HijackHandler struct{} + +func (*HijackHandler) Handle(c vnc.Conn) error { + m.Lock() + defer m.Unlock() + hostport, ok := srvconns[c] + if !ok { + return fmt.Errorf("client connect in server pool not found") + } + proxy, ok := cliconns[hostport] + if !ok { + return fmt.Errorf("client connect to qemu not found") + } + cfg := c.Config().(*vnc.ServerConfig) + cfg.ClientMessageCh = proxy.inp + cfg.ServerMessageCh = proxy.out + + proxy.conns <- c + ds := &vnc.DefaultServerMessageHandler{} + go ds.Handle(c) + return nil +} + +type AuthVNCHTTP struct { + c *http.Client + vnc.ServerAuthVNC +} + +func (auth *AuthVNCHTTP) Auth(c vnc.Conn) error { + auth.ServerAuthVNC.Challenge = []byte("clodo.ruclodo.ru") + if err := auth.ServerAuthVNC.WriteChallenge(c); err != nil { + return err + } + if err := auth.ServerAuthVNC.ReadChallenge(c); err != nil { + return err + } + + buf := new(bytes.Buffer) + enc := base64.NewEncoder(base64.StdEncoding, buf) + enc.Write(auth.ServerAuthVNC.Crypted) + enc.Close() + + v := url.Values{} + v.Set("hash", buf.String()) + buf.Reset() + src, _, _ := net.SplitHostPort(c.Conn().RemoteAddr().String()) + v.Set("ip", src) + res, err := auth.c.PostForm("https://api.ix.clodo.ru/system/vnc", v) + if err != nil { + return err + } + if res.StatusCode != 200 || res.Body == nil { + if res.Body != nil { + io.Copy(buf, res.Body) + } + fmt.Printf("failed to get auth data: code %d body %s\n", res.StatusCode, buf.String()) + defer buf.Reset() + return fmt.Errorf("failed to get auth data: code %d body %s", res.StatusCode, buf.String()) + } + _, err = io.Copy(buf, res.Body) + if err != nil { + return fmt.Errorf("failed to get auth data: %s", err.Error()) + } + logger.Infof("http auth: %s\n", buf.Bytes()) + res.Body.Close() + data := strings.Split(buf.String(), " ") + if len(data) < 2 { + return fmt.Errorf("failed to get auth data data invalid") + } + buf.Reset() + + hostport := string(data[0]) + password := []byte(data[1]) + + m.Lock() + defer m.Unlock() + cc, inp, out, conns, err := newConn(hostport, password) + if err != nil { + return err + } + cliconns[hostport] = &Proxy{cc, conns, inp, out} + srvconns[c] = hostport + c.SetWidth(cc.Width()) + c.SetHeight(cc.Height()) + return nil +} + +func (*AuthVNCHTTP) Type() vnc.SecurityType { + return vnc.SecTypeVNC +} + +func (*AuthVNCHTTP) SubType() vnc.SecuritySubType { + return vnc.SecSubTypeUnknown +} + +func main() { + go func() { + logger.Info(http.ListenAndServe(":6060", nil)) + }() + + ln, err := net.Listen("tcp", ":6900") + if err != nil { + logger.Fatalf("Error listen. %v", err) + } + + schClient := make(chan vnc.ClientMessage) + schServer := make(chan vnc.ServerMessage) + + scfg := &vnc.ServerConfig{ + SecurityHandlers: []vnc.SecurityHandler{ + &AuthVNCHTTP{c: &http.Client{}}, + }, + Encodings: []vnc.Encoding{ + // &vnc.TightPngEncoding{}, + &vnc.CopyRectEncoding{}, + &vnc.RawEncoding{}, + }, + PixelFormat: vnc.PixelFormat32bit, + ClientMessageCh: schClient, + ServerMessageCh: schServer, + //ClientMessages: vnc.DefaultClientMessages, + DesktopName: []byte("vnc proxy"), + } + scfg.Handlers = append(scfg.Handlers, vnc.DefaultServerHandlers...) + scfg.Handlers = append(scfg.Handlers[:len(scfg.Handlers)-1], &HijackHandler{}) + vnc.Serve(context.Background(), ln, scfg) +} diff --git a/example/server/main.go b/example/server/main.go index abf7ed8..7979e72 100644 --- a/example/server/main.go +++ b/example/server/main.go @@ -1,93 +1,93 @@ -package main - -import ( - "context" - "fmt" - "image" - "math" - "net" - "time" - vnc "vnc2webm" - "vnc2webm/logger" -) - -func main() { - ln, err := net.Listen("tcp", ":5900") - if err != nil { - logger.Fatalf("Error listen. %v", err) - } - - chServer := make(chan vnc.ClientMessage) - chClient := make(chan vnc.ServerMessage) - - im := image.NewRGBA(image.Rect(0, 0, width, height)) - tick := time.NewTicker(time.Second / 2) - defer tick.Stop() - - cfg := &vnc.ServerConfig{ - Width: 800, - Height: 600, - //VersionHandler: vnc.ServerVersionHandler, - //SecurityHandler: vnc.ServerSecurityHandler, - SecurityHandlers: []vnc.SecurityHandler{&vnc.ClientAuthNone{}}, - //ClientInitHandler: vnc.ServerClientInitHandler, - //ServerInitHandler: vnc.ServerServerInitHandler, - Encodings: []vnc.Encoding{&vnc.RawEncoding{}}, - PixelFormat: vnc.PixelFormat32bit, - ClientMessageCh: chServer, - ServerMessageCh: chClient, - Messages: vnc.DefaultClientMessages, - } - cfg.Handlers = vnc.DefaultServerHandlers - go vnc.Serve(context.Background(), ln, cfg) - - // Process messages coming in on the ClientMessage channel. - for { - select { - case <-tick.C: - drawImage(im, 0) - fmt.Printf("tick\n") - case msg := <-chClient: - switch msg.Type() { - default: - logger.Debugf("11 Received message type:%v msg:%v\n", msg.Type(), msg) - } - case msg := <-chServer: - switch msg.Type() { - default: - logger.Debugf("22 Received message type:%v msg:%v\n", msg.Type(), msg) - } - } - } -} - -const ( - width = 800 - height = 600 -) - -func drawImage(im *image.RGBA, anim int) { - pos := 0 - const border = 50 - for y := 0; y < height; y++ { - for x := 0; x < width; x++ { - var r, g, b uint8 - switch { - case x < border*2.5 && x < int((1.1+math.Sin(float64(y+anim*2)/40))*border): - r = 255 - case x > width-border*2.5 && x > width-int((1.1+math.Sin(math.Pi+float64(y+anim*2)/40))*border): - g = 255 - case y < border*2.5 && y < int((1.1+math.Sin(float64(x+anim*2)/40))*border): - r, g = 255, 255 - case y > height-border*2.5 && y > height-int((1.1+math.Sin(math.Pi+float64(x+anim*2)/40))*border): - b = 255 - default: - r, g, b = uint8(x+anim), uint8(y+anim), uint8(x+y+anim*3) - } - im.Pix[pos] = r - im.Pix[pos+1] = g - im.Pix[pos+2] = b - pos += 4 // skipping alpha - } - } -} +package main + +import ( + "context" + "fmt" + "image" + "math" + "net" + "time" + vnc "vnc2video" + "vnc2video/logger" +) + +func main() { + ln, err := net.Listen("tcp", ":5900") + if err != nil { + logger.Fatalf("Error listen. %v", err) + } + + chServer := make(chan vnc.ClientMessage) + chClient := make(chan vnc.ServerMessage) + + im := image.NewRGBA(image.Rect(0, 0, width, height)) + tick := time.NewTicker(time.Second / 2) + defer tick.Stop() + + cfg := &vnc.ServerConfig{ + Width: 800, + Height: 600, + //VersionHandler: vnc.ServerVersionHandler, + //SecurityHandler: vnc.ServerSecurityHandler, + SecurityHandlers: []vnc.SecurityHandler{&vnc.ClientAuthNone{}}, + //ClientInitHandler: vnc.ServerClientInitHandler, + //ServerInitHandler: vnc.ServerServerInitHandler, + Encodings: []vnc.Encoding{&vnc.RawEncoding{}}, + PixelFormat: vnc.PixelFormat32bit, + ClientMessageCh: chServer, + ServerMessageCh: chClient, + Messages: vnc.DefaultClientMessages, + } + cfg.Handlers = vnc.DefaultServerHandlers + go vnc.Serve(context.Background(), ln, cfg) + + // Process messages coming in on the ClientMessage channel. + for { + select { + case <-tick.C: + drawImage(im, 0) + fmt.Printf("tick\n") + case msg := <-chClient: + switch msg.Type() { + default: + logger.Debugf("11 Received message type:%v msg:%v\n", msg.Type(), msg) + } + case msg := <-chServer: + switch msg.Type() { + default: + logger.Debugf("22 Received message type:%v msg:%v\n", msg.Type(), msg) + } + } + } +} + +const ( + width = 800 + height = 600 +) + +func drawImage(im *image.RGBA, anim int) { + pos := 0 + const border = 50 + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + var r, g, b uint8 + switch { + case x < border*2.5 && x < int((1.1+math.Sin(float64(y+anim*2)/40))*border): + r = 255 + case x > width-border*2.5 && x > width-int((1.1+math.Sin(math.Pi+float64(y+anim*2)/40))*border): + g = 255 + case y < border*2.5 && y < int((1.1+math.Sin(float64(x+anim*2)/40))*border): + r, g = 255, 255 + case y > height-border*2.5 && y > height-int((1.1+math.Sin(math.Pi+float64(x+anim*2)/40))*border): + b = 255 + default: + r, g, b = uint8(x+anim), uint8(y+anim), uint8(x+y+anim*3) + } + im.Pix[pos] = r + im.Pix[pos+1] = g + im.Pix[pos+2] = b + pos += 4 // skipping alpha + } + } +} diff --git a/fbs-connection.go b/fbs-connection.go index 11e5cd6..3255cf2 100644 --- a/fbs-connection.go +++ b/fbs-connection.go @@ -1,201 +1,201 @@ -package vnc2webm - -import ( - "encoding/binary" - "net" - "vnc2webm/logger" - - "io" - "time" -) - -// Conn represents vnc conection -type FbsConn struct { - FbsReader - - protocol string - //c net.IServerConn - //config *ClientConfig - colorMap ColorMap - - // Encodings supported by the client. This should not be modified - // directly. Instead, SetEncodings should be used. - encodings []Encoding - - // Height of the frame buffer in pixels, sent from the server. - fbHeight uint16 - - // Width of the frame buffer in pixels, sent from the server. - fbWidth uint16 - desktopName string - // The pixel format associated with the connection. This shouldn't - // be modified. If you wish to set a new pixel format, use the - // SetPixelFormat method. - pixelFormat PixelFormat -} - -// func (c *FbsConn) Close() error { -// return c.fbs.Close() -// } - -// // Read reads data from conn -// func (c *FbsConn) Read(buf []byte) (int, error) { -// return c.fbs.Read(buf) -// } - -//dummy, no writing to this conn... -func (c *FbsConn) Write(buf []byte) (int, error) { - return len(buf), nil -} - -func (c *FbsConn) Conn() net.Conn { - return nil -} - -func (c *FbsConn) Config() interface{} { - return nil -} - -func (c *FbsConn) Protocol() string { - return "RFB 003.008" -} -func (c *FbsConn) PixelFormat() PixelFormat { - return c.pixelFormat -} - -func (c *FbsConn) SetPixelFormat(pf PixelFormat) error { - c.pixelFormat = pf - return nil -} - -func (c *FbsConn) ColorMap() ColorMap { return c.colorMap } -func (c *FbsConn) SetColorMap(cm ColorMap) { c.colorMap = cm } -func (c *FbsConn) Encodings() []Encoding { return c.encodings } -func (c *FbsConn) SetEncodings([]EncodingType) error { return nil } -func (c *FbsConn) Width() uint16 { return c.fbWidth } -func (c *FbsConn) Height() uint16 { return c.fbHeight } -func (c *FbsConn) SetWidth(w uint16) { c.fbWidth = w } -func (c *FbsConn) SetHeight(h uint16) { c.fbHeight = h } -func (c *FbsConn) DesktopName() []byte { return []byte(c.desktopName) } -func (c *FbsConn) SetDesktopName(d []byte) { c.desktopName = string(d) } -func (c *FbsConn) Flush() error { return nil } -func (c *FbsConn) Wait() {} -func (c *FbsConn) SetProtoVersion(string) {} -func (c *FbsConn) SetSecurityHandler(SecurityHandler) error { return nil } -func (c *FbsConn) SecurityHandler() SecurityHandler { return nil } -func (c *FbsConn) GetEncInstance(typ EncodingType) Encoding { - for _, enc := range c.encodings { - if enc.Type() == typ { - return enc - } - } - return nil - } - -type VncStreamFileReader interface { - io.Reader - CurrentTimestamp() int - ReadStartSession() (*ServerInit, error) - CurrentPixelFormat() *PixelFormat - Encodings() []Encoding -} - -type FBSPlayHelper struct { - Conn *FbsConn - //Fbs VncStreamFileReader - serverMessageMap map[uint8]ServerMessage - firstSegDone bool - startTime int -} - -func NewFbsConn(filename string, encs []Encoding) (*FbsConn, error) { - - fbs, err := NewFbsReader(filename) - if err != nil { - logger.Error("failed to open fbs reader:", err) - return nil, err - } - - - //NewFbsReader("/Users/amitbet/vncRec/recording.rbs") - initMsg, err := fbs.ReadStartSession() - if err != nil { - logger.Error("failed to open read fbs start session:", err) - return nil, err - } - fbsConn := &FbsConn{FbsReader: *fbs} - fbsConn.encodings = encs - fbsConn.SetPixelFormat(initMsg.PixelFormat) - fbsConn.SetHeight(initMsg.FBHeight) - fbsConn.SetWidth(initMsg.FBWidth) - fbsConn.SetDesktopName([]byte(initMsg.NameText)) - - return fbsConn, nil -} - -func NewFBSPlayHelper(r *FbsConn) *FBSPlayHelper { - h := &FBSPlayHelper{Conn: r} - h.startTime = int(time.Now().UnixNano() / int64(time.Millisecond)) - - h.serverMessageMap = make(map[uint8]ServerMessage) - h.serverMessageMap[0] = &FramebufferUpdate{} - h.serverMessageMap[1] = &SetColorMapEntries{} - h.serverMessageMap[2] = &Bell{} - h.serverMessageMap[3] = &ServerCutText{} - - return h -} - -// func (handler *FBSPlayHelper) Consume(seg *RfbSegment) error { - -// switch seg.SegmentType { -// case SegmentFullyParsedClientMessage: -// clientMsg := seg.Message.(ClientMessage) -// logger.Debugf("ClientUpdater.Consume:(vnc-server-bound) got ClientMessage type=%s", clientMsg.Type()) -// switch clientMsg.Type() { - -// case FramebufferUpdateRequestMsgType: -// if !handler.firstSegDone { -// handler.firstSegDone = true -// handler.startTime = int(time.Now().UnixNano() / int64(time.Millisecond)) -// } -// handler.sendFbsMessage() -// } -// // server.MsgFramebufferUpdateRequest: -// } -// return nil -// } - -func (h *FBSPlayHelper) ReadFbsMessage() ServerMessage { - var messageType uint8 - //messages := make(map[uint8]ServerMessage) - fbs := h.Conn - //conn := h.Conn - err := binary.Read(fbs, binary.BigEndian, &messageType) - if err != nil { - logger.Error("TestServer.NewConnHandler: Error in reading FBS: ", err) - return nil - } - //IClientConn{} - //binary.Write(h.Conn, binary.BigEndian, messageType) - msg := h.serverMessageMap[messageType] - if msg == nil { - logger.Error("TestServer.NewConnHandler: Error unknown message type: ", messageType) - return nil - } - //read the actual message data - //err = binary.Read(fbs, binary.BigEndian, &msg) - parsedMsg, err := msg.Read(fbs) - if err != nil { - logger.Error("TestServer.NewConnHandler: Error in reading FBS message: ", err) - return nil - } - - timeSinceStart := int(time.Now().UnixNano()/int64(time.Millisecond)) - h.startTime - timeToSleep := fbs.CurrentTimestamp() - timeSinceStart - if timeToSleep > 0 { - time.Sleep(time.Duration(timeToSleep) * time.Millisecond) - } - - return parsedMsg -} +package vnc2video + +import ( + "encoding/binary" + "net" + "vnc2video/logger" + + "io" + "time" +) + +// Conn represents vnc conection +type FbsConn struct { + FbsReader + + protocol string + //c net.IServerConn + //config *ClientConfig + colorMap ColorMap + + // Encodings supported by the client. This should not be modified + // directly. Instead, SetEncodings should be used. + encodings []Encoding + + // Height of the frame buffer in pixels, sent from the server. + fbHeight uint16 + + // Width of the frame buffer in pixels, sent from the server. + fbWidth uint16 + desktopName string + // The pixel format associated with the connection. This shouldn't + // be modified. If you wish to set a new pixel format, use the + // SetPixelFormat method. + pixelFormat PixelFormat +} + +// func (c *FbsConn) Close() error { +// return c.fbs.Close() +// } + +// // Read reads data from conn +// func (c *FbsConn) Read(buf []byte) (int, error) { +// return c.fbs.Read(buf) +// } + +//dummy, no writing to this conn... +func (c *FbsConn) Write(buf []byte) (int, error) { + return len(buf), nil +} + +func (c *FbsConn) Conn() net.Conn { + return nil +} + +func (c *FbsConn) Config() interface{} { + return nil +} + +func (c *FbsConn) Protocol() string { + return "RFB 003.008" +} +func (c *FbsConn) PixelFormat() PixelFormat { + return c.pixelFormat +} + +func (c *FbsConn) SetPixelFormat(pf PixelFormat) error { + c.pixelFormat = pf + return nil +} + +func (c *FbsConn) ColorMap() ColorMap { return c.colorMap } +func (c *FbsConn) SetColorMap(cm ColorMap) { c.colorMap = cm } +func (c *FbsConn) Encodings() []Encoding { return c.encodings } +func (c *FbsConn) SetEncodings([]EncodingType) error { return nil } +func (c *FbsConn) Width() uint16 { return c.fbWidth } +func (c *FbsConn) Height() uint16 { return c.fbHeight } +func (c *FbsConn) SetWidth(w uint16) { c.fbWidth = w } +func (c *FbsConn) SetHeight(h uint16) { c.fbHeight = h } +func (c *FbsConn) DesktopName() []byte { return []byte(c.desktopName) } +func (c *FbsConn) SetDesktopName(d []byte) { c.desktopName = string(d) } +func (c *FbsConn) Flush() error { return nil } +func (c *FbsConn) Wait() {} +func (c *FbsConn) SetProtoVersion(string) {} +func (c *FbsConn) SetSecurityHandler(SecurityHandler) error { return nil } +func (c *FbsConn) SecurityHandler() SecurityHandler { return nil } +func (c *FbsConn) GetEncInstance(typ EncodingType) Encoding { + for _, enc := range c.encodings { + if enc.Type() == typ { + return enc + } + } + return nil + } + +type VncStreamFileReader interface { + io.Reader + CurrentTimestamp() int + ReadStartSession() (*ServerInit, error) + CurrentPixelFormat() *PixelFormat + Encodings() []Encoding +} + +type FBSPlayHelper struct { + Conn *FbsConn + //Fbs VncStreamFileReader + serverMessageMap map[uint8]ServerMessage + firstSegDone bool + startTime int +} + +func NewFbsConn(filename string, encs []Encoding) (*FbsConn, error) { + + fbs, err := NewFbsReader(filename) + if err != nil { + logger.Error("failed to open fbs reader:", err) + return nil, err + } + + + //NewFbsReader("/Users/amitbet/vncRec/recording.rbs") + initMsg, err := fbs.ReadStartSession() + if err != nil { + logger.Error("failed to open read fbs start session:", err) + return nil, err + } + fbsConn := &FbsConn{FbsReader: *fbs} + fbsConn.encodings = encs + fbsConn.SetPixelFormat(initMsg.PixelFormat) + fbsConn.SetHeight(initMsg.FBHeight) + fbsConn.SetWidth(initMsg.FBWidth) + fbsConn.SetDesktopName([]byte(initMsg.NameText)) + + return fbsConn, nil +} + +func NewFBSPlayHelper(r *FbsConn) *FBSPlayHelper { + h := &FBSPlayHelper{Conn: r} + h.startTime = int(time.Now().UnixNano() / int64(time.Millisecond)) + + h.serverMessageMap = make(map[uint8]ServerMessage) + h.serverMessageMap[0] = &FramebufferUpdate{} + h.serverMessageMap[1] = &SetColorMapEntries{} + h.serverMessageMap[2] = &Bell{} + h.serverMessageMap[3] = &ServerCutText{} + + return h +} + +// func (handler *FBSPlayHelper) Consume(seg *RfbSegment) error { + +// switch seg.SegmentType { +// case SegmentFullyParsedClientMessage: +// clientMsg := seg.Message.(ClientMessage) +// logger.Debugf("ClientUpdater.Consume:(vnc-server-bound) got ClientMessage type=%s", clientMsg.Type()) +// switch clientMsg.Type() { + +// case FramebufferUpdateRequestMsgType: +// if !handler.firstSegDone { +// handler.firstSegDone = true +// handler.startTime = int(time.Now().UnixNano() / int64(time.Millisecond)) +// } +// handler.sendFbsMessage() +// } +// // server.MsgFramebufferUpdateRequest: +// } +// return nil +// } + +func (h *FBSPlayHelper) ReadFbsMessage() ServerMessage { + var messageType uint8 + //messages := make(map[uint8]ServerMessage) + fbs := h.Conn + //conn := h.Conn + err := binary.Read(fbs, binary.BigEndian, &messageType) + if err != nil { + logger.Error("TestServer.NewConnHandler: Error in reading FBS: ", err) + return nil + } + //IClientConn{} + //binary.Write(h.Conn, binary.BigEndian, messageType) + msg := h.serverMessageMap[messageType] + if msg == nil { + logger.Error("TestServer.NewConnHandler: Error unknown message type: ", messageType) + return nil + } + //read the actual message data + //err = binary.Read(fbs, binary.BigEndian, &msg) + parsedMsg, err := msg.Read(fbs) + if err != nil { + logger.Error("TestServer.NewConnHandler: Error in reading FBS message: ", err) + return nil + } + + timeSinceStart := int(time.Now().UnixNano()/int64(time.Millisecond)) - h.startTime + timeToSleep := fbs.CurrentTimestamp() - timeSinceStart + if timeToSleep > 0 { + time.Sleep(time.Duration(timeToSleep) * time.Millisecond) + } + + return parsedMsg +} diff --git a/fbs-reader.go b/fbs-reader.go index f8fb5ca..387b143 100644 --- a/fbs-reader.go +++ b/fbs-reader.go @@ -1,190 +1,190 @@ -package vnc2webm - -import ( - "bytes" - "encoding/binary" - "io" - "os" - //"vncproxy/common" - //"vncproxy/encodings" - "vnc2webm/logger" - //"vncproxy/encodings" - //"vncproxy/encodings" -) - -type FbsReader struct { - reader io.ReadCloser - buffer bytes.Buffer - currentTimestamp int - //pixelFormat *PixelFormat - //encodings []IEncoding -} - -func (fbs *FbsReader) Close() error { - return fbs.reader.Close() -} - -func (fbs *FbsReader) CurrentTimestamp() int { - return fbs.currentTimestamp -} - -func (fbs *FbsReader) Read(p []byte) (n int, err error) { - if fbs.buffer.Len() < len(p) { - seg, err := fbs.ReadSegment() - - if err != nil { - logger.Error("FBSReader.Read: error reading FBSsegment: ", err) - return 0, err - } - fbs.buffer.Write(seg.bytes) - fbs.currentTimestamp = int(seg.timestamp) - } - return fbs.buffer.Read(p) -} - -//func (fbs *FbsReader) CurrentPixelFormat() *PixelFormat { return fbs.pixelFormat } -//func (fbs *FbsReader) CurrentColorMap() *common.ColorMap { return &common.ColorMap{} } -//func (fbs *FbsReader) Encodings() []IEncoding { return fbs.encodings } - -func NewFbsReader(fbsFile string) (*FbsReader, error) { - - reader, err := os.OpenFile(fbsFile, os.O_RDONLY, 0644) - if err != nil { - logger.Error("NewFbsReader: can't open fbs file: ", fbsFile) - return nil, err - } - return &FbsReader{reader: reader}, //encodings: []IEncoding{ - // //&encodings.CopyRectEncoding{}, - // //&encodings.ZLibEncoding{}, - // //&encodings.ZRLEEncoding{}, - // //&encodings.CoRREEncoding{}, - // //&encodings.HextileEncoding{}, - // &TightEncoding{}, - // //&TightPngEncoding{}, - // //&EncCursorPseudo{}, - // &RawEncoding{}, - // //&encodings.RREEncoding{}, - //}, - nil - -} - -func (fbs *FbsReader) ReadStartSession() (*ServerInit, error) { - - initMsg := ServerInit{} - reader := fbs.reader - - var framebufferWidth uint16 - var framebufferHeight uint16 - var SecTypeNone uint32 - //read rfb header information (the only part done without the [size|data|timestamp] block wrapper) - //.("FBS 001.000\n") - bytes := make([]byte, 12) - _, err := reader.Read(bytes) - if err != nil { - logger.Error("FbsReader.ReadStartSession: error reading rbs init message - FBS file Version:", err) - return nil, err - } - - //read the version message into the buffer, it is written in the first fbs block - //RFB 003.008\n - bytes = make([]byte, 12) - _, err = fbs.Read(bytes) - if err != nil { - logger.Error("FbsReader.ReadStartSession: error reading rbs init - RFB Version: ", err) - return nil, err - } - - //push sec type and fb dimensions - binary.Read(fbs, binary.BigEndian, &SecTypeNone) - if err != nil { - logger.Error("FbsReader.ReadStartSession: error reading rbs init - SecType: ", err) - } - - //read frame buffer width, height - binary.Read(fbs, binary.BigEndian, &framebufferWidth) - if err != nil { - logger.Error("FbsReader.ReadStartSession: error reading rbs init - FBWidth: ", err) - return nil, err - } - initMsg.FBWidth = framebufferWidth - - binary.Read(fbs, binary.BigEndian, &framebufferHeight) - if err != nil { - logger.Error("FbsReader.ReadStartSession: error reading rbs init - FBHeight: ", err) - return nil, err - } - initMsg.FBHeight = framebufferHeight - - //read pixel format - pixelFormat := &PixelFormat{} - binary.Read(fbs, binary.BigEndian, pixelFormat) - if err != nil { - logger.Error("FbsReader.ReadStartSession: error reading rbs init - Pixelformat: ", err) - return nil, err - } - - initMsg.PixelFormat = *pixelFormat - - //read desktop name - var desknameLen uint32 - binary.Read(fbs, binary.BigEndian, &desknameLen) - if err != nil { - logger.Error("FbsReader.ReadStartSession: error reading rbs init - deskname Len: ", err) - return nil, err - } - initMsg.NameLength = desknameLen - - bytes = make([]byte, desknameLen) - fbs.Read(bytes) - if err != nil { - logger.Error("FbsReader.ReadStartSession: error reading rbs init - desktopName: ", err) - return nil, err - } - - initMsg.NameText = bytes - - return &initMsg, nil -} - -func (fbs *FbsReader) ReadSegment() (*FbsSegment, error) { - reader := fbs.reader - var bytesLen uint32 - - //read length - err := binary.Read(reader, binary.BigEndian, &bytesLen) - if err != nil { - logger.Error("FbsReader.ReadStartSession: read len, error reading rbs file: ", err) - return nil, err - } - - paddedSize := (bytesLen + 3) & 0x7FFFFFFC - - //read bytes - bytes := make([]byte, paddedSize) - _, err = reader.Read(bytes) - if err != nil { - logger.Error("FbsReader.ReadSegment: read bytes, error reading rbs file: ", err) - return nil, err - } - - //remove padding - actualBytes := bytes[:bytesLen] - - //read timestamp - var timeSinceStart uint32 - binary.Read(reader, binary.BigEndian, &timeSinceStart) - if err != nil { - logger.Error("FbsReader.ReadSegment: read timestamp, error reading rbs file: ", err) - return nil, err - } - - //timeStamp := time.Unix(timeSinceStart, 0) - seg := &FbsSegment{bytes: actualBytes, timestamp: timeSinceStart} - return seg, nil -} - -type FbsSegment struct { - bytes []byte - timestamp uint32 -} +package vnc2video + +import ( + "bytes" + "encoding/binary" + "io" + "os" + //"vncproxy/common" + //"vncproxy/encodings" + "vnc2video/logger" + //"vncproxy/encodings" + //"vncproxy/encodings" +) + +type FbsReader struct { + reader io.ReadCloser + buffer bytes.Buffer + currentTimestamp int + //pixelFormat *PixelFormat + //encodings []IEncoding +} + +func (fbs *FbsReader) Close() error { + return fbs.reader.Close() +} + +func (fbs *FbsReader) CurrentTimestamp() int { + return fbs.currentTimestamp +} + +func (fbs *FbsReader) Read(p []byte) (n int, err error) { + if fbs.buffer.Len() < len(p) { + seg, err := fbs.ReadSegment() + + if err != nil { + logger.Error("FBSReader.Read: error reading FBSsegment: ", err) + return 0, err + } + fbs.buffer.Write(seg.bytes) + fbs.currentTimestamp = int(seg.timestamp) + } + return fbs.buffer.Read(p) +} + +//func (fbs *FbsReader) CurrentPixelFormat() *PixelFormat { return fbs.pixelFormat } +//func (fbs *FbsReader) CurrentColorMap() *common.ColorMap { return &common.ColorMap{} } +//func (fbs *FbsReader) Encodings() []IEncoding { return fbs.encodings } + +func NewFbsReader(fbsFile string) (*FbsReader, error) { + + reader, err := os.OpenFile(fbsFile, os.O_RDONLY, 0644) + if err != nil { + logger.Error("NewFbsReader: can't open fbs file: ", fbsFile) + return nil, err + } + return &FbsReader{reader: reader}, //encodings: []IEncoding{ + // //&encodings.CopyRectEncoding{}, + // //&encodings.ZLibEncoding{}, + // //&encodings.ZRLEEncoding{}, + // //&encodings.CoRREEncoding{}, + // //&encodings.HextileEncoding{}, + // &TightEncoding{}, + // //&TightPngEncoding{}, + // //&EncCursorPseudo{}, + // &RawEncoding{}, + // //&encodings.RREEncoding{}, + //}, + nil + +} + +func (fbs *FbsReader) ReadStartSession() (*ServerInit, error) { + + initMsg := ServerInit{} + reader := fbs.reader + + var framebufferWidth uint16 + var framebufferHeight uint16 + var SecTypeNone uint32 + //read rfb header information (the only part done without the [size|data|timestamp] block wrapper) + //.("FBS 001.000\n") + bytes := make([]byte, 12) + _, err := reader.Read(bytes) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init message - FBS file Version:", err) + return nil, err + } + + //read the version message into the buffer, it is written in the first fbs block + //RFB 003.008\n + bytes = make([]byte, 12) + _, err = fbs.Read(bytes) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - RFB Version: ", err) + return nil, err + } + + //push sec type and fb dimensions + binary.Read(fbs, binary.BigEndian, &SecTypeNone) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - SecType: ", err) + } + + //read frame buffer width, height + binary.Read(fbs, binary.BigEndian, &framebufferWidth) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - FBWidth: ", err) + return nil, err + } + initMsg.FBWidth = framebufferWidth + + binary.Read(fbs, binary.BigEndian, &framebufferHeight) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - FBHeight: ", err) + return nil, err + } + initMsg.FBHeight = framebufferHeight + + //read pixel format + pixelFormat := &PixelFormat{} + binary.Read(fbs, binary.BigEndian, pixelFormat) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - Pixelformat: ", err) + return nil, err + } + + initMsg.PixelFormat = *pixelFormat + + //read desktop name + var desknameLen uint32 + binary.Read(fbs, binary.BigEndian, &desknameLen) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - deskname Len: ", err) + return nil, err + } + initMsg.NameLength = desknameLen + + bytes = make([]byte, desknameLen) + fbs.Read(bytes) + if err != nil { + logger.Error("FbsReader.ReadStartSession: error reading rbs init - desktopName: ", err) + return nil, err + } + + initMsg.NameText = bytes + + return &initMsg, nil +} + +func (fbs *FbsReader) ReadSegment() (*FbsSegment, error) { + reader := fbs.reader + var bytesLen uint32 + + //read length + err := binary.Read(reader, binary.BigEndian, &bytesLen) + if err != nil { + logger.Error("FbsReader.ReadStartSession: read len, error reading rbs file: ", err) + return nil, err + } + + paddedSize := (bytesLen + 3) & 0x7FFFFFFC + + //read bytes + bytes := make([]byte, paddedSize) + _, err = reader.Read(bytes) + if err != nil { + logger.Error("FbsReader.ReadSegment: read bytes, error reading rbs file: ", err) + return nil, err + } + + //remove padding + actualBytes := bytes[:bytesLen] + + //read timestamp + var timeSinceStart uint32 + binary.Read(reader, binary.BigEndian, &timeSinceStart) + if err != nil { + logger.Error("FbsReader.ReadSegment: read timestamp, error reading rbs file: ", err) + return nil, err + } + + //timeStamp := time.Unix(timeSinceStart, 0) + seg := &FbsSegment{bytes: actualBytes, timestamp: timeSinceStart} + return seg, nil +} + +type FbsSegment struct { + bytes []byte + timestamp uint32 +} diff --git a/handlers.go b/handlers.go index 78bcfeb..387f160 100644 --- a/handlers.go +++ b/handlers.go @@ -1,408 +1,408 @@ -package vnc2webm - -import ( - "encoding/binary" - "fmt" - "vnc2webm/logger" -) - -// Handler represents handler of handshake -type Handler interface { - Handle(Conn) error -} - -// ProtoVersionLength protocol version length -const ProtoVersionLength = 12 - -const ( - // ProtoVersionUnknown unknown version - ProtoVersionUnknown = "" - // ProtoVersion33 sets if proto 003.003 - ProtoVersion33 = "RFB 003.003\n" - // ProtoVersion38 sets if proto 003.008 - ProtoVersion38 = "RFB 003.008\n" - // ProtoVersion37 sets if proto 003.007 - ProtoVersion37 = "RFB 003.007\n" -) - -// ParseProtoVersion parse protocol version -func ParseProtoVersion(pv []byte) (uint, uint, error) { - var major, minor uint - - if len(pv) < ProtoVersionLength { - return 0, 0, fmt.Errorf("ProtocolVersion message too short (%v < %v)", len(pv), ProtoVersionLength) - } - - l, err := fmt.Sscanf(string(pv), "RFB %d.%d\n", &major, &minor) - if l != 2 { - return 0, 0, fmt.Errorf("error parsing protocol version") - } - if err != nil { - return 0, 0, err - } - - return major, minor, nil -} - -// DefaultClientVersionHandler represents default handler -type DefaultClientVersionHandler struct{} - -// Handle provide version handler for client side -func (*DefaultClientVersionHandler) Handle(c Conn) error { - var version [ProtoVersionLength]byte - - if err := binary.Read(c, binary.BigEndian, &version); err != nil { - return err - } - - major, minor, err := ParseProtoVersion(version[:]) - if err != nil { - return err - } - - pv := ProtoVersionUnknown - if major == 3 { - if minor >= 8 { - pv = ProtoVersion38 - } else if minor >= 3 { - pv = ProtoVersion38 - } - } - if pv == ProtoVersionUnknown { - return fmt.Errorf("ProtocolVersion handshake failed; unsupported version '%v'", string(version[:])) - } - c.SetProtoVersion(string(version[:])) - - if err := binary.Write(c, binary.BigEndian, []byte(pv)); err != nil { - return err - } - return c.Flush() -} - -// DefaultServerVersionHandler represents default server handler -type DefaultServerVersionHandler struct{} - -// Handle provide server version handler -func (*DefaultServerVersionHandler) Handle(c Conn) error { - var version [ProtoVersionLength]byte - if err := binary.Write(c, binary.BigEndian, []byte(ProtoVersion38)); err != nil { - return err - } - if err := c.Flush(); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &version); err != nil { - return err - } - major, minor, err := ParseProtoVersion(version[:]) - if err != nil { - return err - } - - pv := ProtoVersionUnknown - if major == 3 { - if minor >= 8 { - pv = ProtoVersion38 - } else if minor >= 3 { - pv = ProtoVersion33 - } - } - if pv == ProtoVersionUnknown { - return fmt.Errorf("ProtocolVersion handshake failed; unsupported version '%v'", string(version[:])) - } - - c.SetProtoVersion(pv) - return nil -} - -// DefaultClientSecurityHandler used for client security handler -type DefaultClientSecurityHandler struct{} - -// Handle provide client side security handler -func (*DefaultClientSecurityHandler) Handle(c Conn) error { - cfg := c.Config().(*ClientConfig) - var numSecurityTypes uint8 - if err := binary.Read(c, binary.BigEndian, &numSecurityTypes); err != nil { - return err - } - secTypes := make([]SecurityType, numSecurityTypes) - if err := binary.Read(c, binary.BigEndian, &secTypes); err != nil { - return err - } - - var secType SecurityHandler - for _, st := range cfg.SecurityHandlers { - for _, sc := range secTypes { - if st.Type() == sc { - secType = st - } - } - } - - if err := binary.Write(c, binary.BigEndian, cfg.SecurityHandlers[0].Type()); err != nil { - return err - } - - if err := c.Flush(); err != nil { - return err - } - - err := secType.Auth(c) - if err != nil { - return err - } - - var authCode uint32 - if err := binary.Read(c, binary.BigEndian, &authCode); err != nil { - return err - } - - logger.Debugf("authenticating, secType: %d, auth code(0=success): %d", secType.Type(), authCode) - if authCode == 1 { - var reasonLength uint32 - if err := binary.Read(c, binary.BigEndian, &reasonLength); err != nil { - return err - } - reasonText := make([]byte, reasonLength) - if err := binary.Read(c, binary.BigEndian, &reasonText); err != nil { - return err - } - return fmt.Errorf("%s", reasonText) - } - c.SetSecurityHandler(secType) - return nil -} - -// DefaultServerSecurityHandler used for server security handler -type DefaultServerSecurityHandler struct{} - -// Handle provide server side security handler -func (*DefaultServerSecurityHandler) Handle(c Conn) error { - cfg := c.Config().(*ServerConfig) - var secType SecurityType - if c.Protocol() == ProtoVersion37 || c.Protocol() == ProtoVersion38 { - if err := binary.Write(c, binary.BigEndian, uint8(len(cfg.SecurityHandlers))); err != nil { - return err - } - - for _, sectype := range cfg.SecurityHandlers { - if err := binary.Write(c, binary.BigEndian, sectype.Type()); err != nil { - return err - } - } - } else { - st := uint32(0) - for _, sectype := range cfg.SecurityHandlers { - if uint32(sectype.Type()) > st { - st = uint32(sectype.Type()) - secType = sectype.Type() - } - } - if err := binary.Write(c, binary.BigEndian, st); err != nil { - return err - } - } - if err := c.Flush(); err != nil { - return err - } - - if c.Protocol() == ProtoVersion38 { - if err := binary.Read(c, binary.BigEndian, &secType); err != nil { - return err - } - } - secTypes := make(map[SecurityType]SecurityHandler) - for _, sType := range cfg.SecurityHandlers { - secTypes[sType.Type()] = sType - } - - sType, ok := secTypes[secType] - if !ok { - return fmt.Errorf("security type %d not implemented", secType) - } - - var authCode uint32 - authErr := sType.Auth(c) - if authErr != nil { - authCode = uint32(1) - } - - if err := binary.Write(c, binary.BigEndian, authCode); err != nil { - return err - } - - if authErr == nil { - if err := c.Flush(); err != nil { - return err - } - c.SetSecurityHandler(sType) - return nil - } - - if c.Protocol() == ProtoVersion38 { - if err := binary.Write(c, binary.BigEndian, uint32(len(authErr.Error()))); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, []byte(authErr.Error())); err != nil { - return err - } - if err := c.Flush(); err != nil { - return err - } - } - return authErr -} - -// DefaultClientServerInitHandler default client server init handler -type DefaultClientServerInitHandler struct{} - -// Handle provide default server init handler -func (*DefaultClientServerInitHandler) Handle(c Conn) error { - logger.Debug("starting DefaultClientServerInitHandler") - var err error - srvInit := ServerInit{} - - if err = binary.Read(c, binary.BigEndian, &srvInit.FBWidth); err != nil { - return err - } - if err = binary.Read(c, binary.BigEndian, &srvInit.FBHeight); err != nil { - return err - } - if err = binary.Read(c, binary.BigEndian, &srvInit.PixelFormat); err != nil { - return err - } - if err = binary.Read(c, binary.BigEndian, &srvInit.NameLength); err != nil { - return err - } - - srvInit.NameText = make([]byte, srvInit.NameLength) - if err = binary.Read(c, binary.BigEndian, &srvInit.NameText); err != nil { - return err - } - logger.Debugf("DefaultClientServerInitHandler got serverInit: %v", srvInit) - c.SetDesktopName(srvInit.NameText) - if c.Protocol() == "aten1" { - c.SetWidth(800) - c.SetHeight(600) - c.SetPixelFormat(NewPixelFormatAten()) - } else { - c.SetWidth(srvInit.FBWidth) - c.SetHeight(srvInit.FBHeight) - - //telling the server to use 32bit pixels (with 24 dept, tight standard format) - pixelMsg:=SetPixelFormat{PF: PixelFormat32bit} - pixelMsg.Write(c) - c.SetPixelFormat(PixelFormat32bit) - //c.SetPixelFormat(srvInit.PixelFormat) - } - if c.Protocol() == "aten1" { - ikvm := struct { - _ [8]byte - IKVMVideoEnable uint8 - IKVMKMEnable uint8 - IKVMKickEnable uint8 - VUSBEnable uint8 - }{} - if err = binary.Read(c, binary.BigEndian, &ikvm); err != nil { - return err - } - } - /* - caps := struct { - ServerMessagesNum uint16 - ClientMessagesNum uint16 - EncodingsNum uint16 - _ [2]byte - }{} - if err := binary.Read(c, binary.BigEndian, &caps); err != nil { - return err - } - - caps.ServerMessagesNum = uint16(1) - var item [16]byte - for i := uint16(0); i < caps.ServerMessagesNum; i++ { - if err := binary.Read(c, binary.BigEndian, &item); err != nil { - return err - } - fmt.Printf("server message cap %s\n", item) - } - - for i := uint16(0); i < caps.ClientMessagesNum; i++ { - if err := binary.Read(c, binary.BigEndian, &item); err != nil { - return err - } - fmt.Printf("client message cap %s\n", item) - } - for i := uint16(0); i < caps.EncodingsNum; i++ { - if err := binary.Read(c, binary.BigEndian, &item); err != nil { - return err - } - fmt.Printf("encoding cap %s\n", item) - } - // var pad [1]byte - // if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - // return err - // } - }*/ - return nil -} - -// DefaultServerServerInitHandler default server server init handler -type DefaultServerServerInitHandler struct{} - -// Handle provide default server server init handler -func (*DefaultServerServerInitHandler) Handle(c Conn) error { - if err := binary.Write(c, binary.BigEndian, c.Width()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, c.Height()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, c.PixelFormat()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, uint32(len(c.DesktopName()))); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, []byte(c.DesktopName())); err != nil { - return err - } - return c.Flush() -} - -// DefaultClientClientInitHandler default client client init handler -type DefaultClientClientInitHandler struct{} - -// Handle provide default client client init handler -func (*DefaultClientClientInitHandler) Handle(c Conn) error { - logger.Debug("starting DefaultClientClientInitHandler") - cfg := c.Config().(*ClientConfig) - var shared uint8 - if cfg.Exclusive { - shared = 0 - } else { - shared = 1 - } - if err := binary.Write(c, binary.BigEndian, shared); err != nil { - return err - } - logger.Debugf("DefaultClientClientInitHandler sending: shared=%d", shared) - return c.Flush() -} - -// DefaultServerClientInitHandler default server client init handler -type DefaultServerClientInitHandler struct{} - -// Handle provide default server client init handler -func (*DefaultServerClientInitHandler) Handle(c Conn) error { - var shared uint8 - if err := binary.Read(c, binary.BigEndian, &shared); err != nil { - return err - } - /* TODO - if shared != 1 { - c.SetShared(false) - } - */ - return nil -} +package vnc2video + +import ( + "encoding/binary" + "fmt" + "vnc2video/logger" +) + +// Handler represents handler of handshake +type Handler interface { + Handle(Conn) error +} + +// ProtoVersionLength protocol version length +const ProtoVersionLength = 12 + +const ( + // ProtoVersionUnknown unknown version + ProtoVersionUnknown = "" + // ProtoVersion33 sets if proto 003.003 + ProtoVersion33 = "RFB 003.003\n" + // ProtoVersion38 sets if proto 003.008 + ProtoVersion38 = "RFB 003.008\n" + // ProtoVersion37 sets if proto 003.007 + ProtoVersion37 = "RFB 003.007\n" +) + +// ParseProtoVersion parse protocol version +func ParseProtoVersion(pv []byte) (uint, uint, error) { + var major, minor uint + + if len(pv) < ProtoVersionLength { + return 0, 0, fmt.Errorf("ProtocolVersion message too short (%v < %v)", len(pv), ProtoVersionLength) + } + + l, err := fmt.Sscanf(string(pv), "RFB %d.%d\n", &major, &minor) + if l != 2 { + return 0, 0, fmt.Errorf("error parsing protocol version") + } + if err != nil { + return 0, 0, err + } + + return major, minor, nil +} + +// DefaultClientVersionHandler represents default handler +type DefaultClientVersionHandler struct{} + +// Handle provide version handler for client side +func (*DefaultClientVersionHandler) Handle(c Conn) error { + var version [ProtoVersionLength]byte + + if err := binary.Read(c, binary.BigEndian, &version); err != nil { + return err + } + + major, minor, err := ParseProtoVersion(version[:]) + if err != nil { + return err + } + + pv := ProtoVersionUnknown + if major == 3 { + if minor >= 8 { + pv = ProtoVersion38 + } else if minor >= 3 { + pv = ProtoVersion38 + } + } + if pv == ProtoVersionUnknown { + return fmt.Errorf("ProtocolVersion handshake failed; unsupported version '%v'", string(version[:])) + } + c.SetProtoVersion(string(version[:])) + + if err := binary.Write(c, binary.BigEndian, []byte(pv)); err != nil { + return err + } + return c.Flush() +} + +// DefaultServerVersionHandler represents default server handler +type DefaultServerVersionHandler struct{} + +// Handle provide server version handler +func (*DefaultServerVersionHandler) Handle(c Conn) error { + var version [ProtoVersionLength]byte + if err := binary.Write(c, binary.BigEndian, []byte(ProtoVersion38)); err != nil { + return err + } + if err := c.Flush(); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &version); err != nil { + return err + } + major, minor, err := ParseProtoVersion(version[:]) + if err != nil { + return err + } + + pv := ProtoVersionUnknown + if major == 3 { + if minor >= 8 { + pv = ProtoVersion38 + } else if minor >= 3 { + pv = ProtoVersion33 + } + } + if pv == ProtoVersionUnknown { + return fmt.Errorf("ProtocolVersion handshake failed; unsupported version '%v'", string(version[:])) + } + + c.SetProtoVersion(pv) + return nil +} + +// DefaultClientSecurityHandler used for client security handler +type DefaultClientSecurityHandler struct{} + +// Handle provide client side security handler +func (*DefaultClientSecurityHandler) Handle(c Conn) error { + cfg := c.Config().(*ClientConfig) + var numSecurityTypes uint8 + if err := binary.Read(c, binary.BigEndian, &numSecurityTypes); err != nil { + return err + } + secTypes := make([]SecurityType, numSecurityTypes) + if err := binary.Read(c, binary.BigEndian, &secTypes); err != nil { + return err + } + + var secType SecurityHandler + for _, st := range cfg.SecurityHandlers { + for _, sc := range secTypes { + if st.Type() == sc { + secType = st + } + } + } + + if err := binary.Write(c, binary.BigEndian, cfg.SecurityHandlers[0].Type()); err != nil { + return err + } + + if err := c.Flush(); err != nil { + return err + } + + err := secType.Auth(c) + if err != nil { + return err + } + + var authCode uint32 + if err := binary.Read(c, binary.BigEndian, &authCode); err != nil { + return err + } + + logger.Debugf("authenticating, secType: %d, auth code(0=success): %d", secType.Type(), authCode) + if authCode == 1 { + var reasonLength uint32 + if err := binary.Read(c, binary.BigEndian, &reasonLength); err != nil { + return err + } + reasonText := make([]byte, reasonLength) + if err := binary.Read(c, binary.BigEndian, &reasonText); err != nil { + return err + } + return fmt.Errorf("%s", reasonText) + } + c.SetSecurityHandler(secType) + return nil +} + +// DefaultServerSecurityHandler used for server security handler +type DefaultServerSecurityHandler struct{} + +// Handle provide server side security handler +func (*DefaultServerSecurityHandler) Handle(c Conn) error { + cfg := c.Config().(*ServerConfig) + var secType SecurityType + if c.Protocol() == ProtoVersion37 || c.Protocol() == ProtoVersion38 { + if err := binary.Write(c, binary.BigEndian, uint8(len(cfg.SecurityHandlers))); err != nil { + return err + } + + for _, sectype := range cfg.SecurityHandlers { + if err := binary.Write(c, binary.BigEndian, sectype.Type()); err != nil { + return err + } + } + } else { + st := uint32(0) + for _, sectype := range cfg.SecurityHandlers { + if uint32(sectype.Type()) > st { + st = uint32(sectype.Type()) + secType = sectype.Type() + } + } + if err := binary.Write(c, binary.BigEndian, st); err != nil { + return err + } + } + if err := c.Flush(); err != nil { + return err + } + + if c.Protocol() == ProtoVersion38 { + if err := binary.Read(c, binary.BigEndian, &secType); err != nil { + return err + } + } + secTypes := make(map[SecurityType]SecurityHandler) + for _, sType := range cfg.SecurityHandlers { + secTypes[sType.Type()] = sType + } + + sType, ok := secTypes[secType] + if !ok { + return fmt.Errorf("security type %d not implemented", secType) + } + + var authCode uint32 + authErr := sType.Auth(c) + if authErr != nil { + authCode = uint32(1) + } + + if err := binary.Write(c, binary.BigEndian, authCode); err != nil { + return err + } + + if authErr == nil { + if err := c.Flush(); err != nil { + return err + } + c.SetSecurityHandler(sType) + return nil + } + + if c.Protocol() == ProtoVersion38 { + if err := binary.Write(c, binary.BigEndian, uint32(len(authErr.Error()))); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, []byte(authErr.Error())); err != nil { + return err + } + if err := c.Flush(); err != nil { + return err + } + } + return authErr +} + +// DefaultClientServerInitHandler default client server init handler +type DefaultClientServerInitHandler struct{} + +// Handle provide default server init handler +func (*DefaultClientServerInitHandler) Handle(c Conn) error { + logger.Debug("starting DefaultClientServerInitHandler") + var err error + srvInit := ServerInit{} + + if err = binary.Read(c, binary.BigEndian, &srvInit.FBWidth); err != nil { + return err + } + if err = binary.Read(c, binary.BigEndian, &srvInit.FBHeight); err != nil { + return err + } + if err = binary.Read(c, binary.BigEndian, &srvInit.PixelFormat); err != nil { + return err + } + if err = binary.Read(c, binary.BigEndian, &srvInit.NameLength); err != nil { + return err + } + + srvInit.NameText = make([]byte, srvInit.NameLength) + if err = binary.Read(c, binary.BigEndian, &srvInit.NameText); err != nil { + return err + } + logger.Debugf("DefaultClientServerInitHandler got serverInit: %v", srvInit) + c.SetDesktopName(srvInit.NameText) + if c.Protocol() == "aten1" { + c.SetWidth(800) + c.SetHeight(600) + c.SetPixelFormat(NewPixelFormatAten()) + } else { + c.SetWidth(srvInit.FBWidth) + c.SetHeight(srvInit.FBHeight) + + //telling the server to use 32bit pixels (with 24 dept, tight standard format) + pixelMsg:=SetPixelFormat{PF: PixelFormat32bit} + pixelMsg.Write(c) + c.SetPixelFormat(PixelFormat32bit) + //c.SetPixelFormat(srvInit.PixelFormat) + } + if c.Protocol() == "aten1" { + ikvm := struct { + _ [8]byte + IKVMVideoEnable uint8 + IKVMKMEnable uint8 + IKVMKickEnable uint8 + VUSBEnable uint8 + }{} + if err = binary.Read(c, binary.BigEndian, &ikvm); err != nil { + return err + } + } + /* + caps := struct { + ServerMessagesNum uint16 + ClientMessagesNum uint16 + EncodingsNum uint16 + _ [2]byte + }{} + if err := binary.Read(c, binary.BigEndian, &caps); err != nil { + return err + } + + caps.ServerMessagesNum = uint16(1) + var item [16]byte + for i := uint16(0); i < caps.ServerMessagesNum; i++ { + if err := binary.Read(c, binary.BigEndian, &item); err != nil { + return err + } + fmt.Printf("server message cap %s\n", item) + } + + for i := uint16(0); i < caps.ClientMessagesNum; i++ { + if err := binary.Read(c, binary.BigEndian, &item); err != nil { + return err + } + fmt.Printf("client message cap %s\n", item) + } + for i := uint16(0); i < caps.EncodingsNum; i++ { + if err := binary.Read(c, binary.BigEndian, &item); err != nil { + return err + } + fmt.Printf("encoding cap %s\n", item) + } + // var pad [1]byte + // if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + // return err + // } + }*/ + return nil +} + +// DefaultServerServerInitHandler default server server init handler +type DefaultServerServerInitHandler struct{} + +// Handle provide default server server init handler +func (*DefaultServerServerInitHandler) Handle(c Conn) error { + if err := binary.Write(c, binary.BigEndian, c.Width()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, c.Height()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, c.PixelFormat()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, uint32(len(c.DesktopName()))); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, []byte(c.DesktopName())); err != nil { + return err + } + return c.Flush() +} + +// DefaultClientClientInitHandler default client client init handler +type DefaultClientClientInitHandler struct{} + +// Handle provide default client client init handler +func (*DefaultClientClientInitHandler) Handle(c Conn) error { + logger.Debug("starting DefaultClientClientInitHandler") + cfg := c.Config().(*ClientConfig) + var shared uint8 + if cfg.Exclusive { + shared = 0 + } else { + shared = 1 + } + if err := binary.Write(c, binary.BigEndian, shared); err != nil { + return err + } + logger.Debugf("DefaultClientClientInitHandler sending: shared=%d", shared) + return c.Flush() +} + +// DefaultServerClientInitHandler default server client init handler +type DefaultServerClientInitHandler struct{} + +// Handle provide default server client init handler +func (*DefaultServerClientInitHandler) Handle(c Conn) error { + var shared uint8 + if err := binary.Read(c, binary.BigEndian, &shared); err != nil { + return err + } + /* TODO + if shared != 1 { + c.SetShared(false) + } + */ + return nil +} diff --git a/image.go b/image.go index 5cd3dd0..5909001 100644 --- a/image.go +++ b/image.go @@ -1,202 +1,202 @@ -package vnc2webm - -import ( - "encoding/binary" - "fmt" - "image" -) - -//var _ draw.Drawer = (*ServerConn)(nil) -//var _ draw.Image = (*ServerConn)(nil) - -// Color represents a single color in a color map. -type Color struct { - pf *PixelFormat - cm *ColorMap - cmIndex uint32 // Only valid if pf.TrueColor is false. - R, G, B uint16 -} - -// ColorMap represent color map -type ColorMap [256]Color - -// NewColor returns a new Color object -func NewColor(pf *PixelFormat, cm *ColorMap) *Color { - return &Color{ - pf: pf, - cm: cm, - } -} - -// Rectangle represents a rectangle of pixel data -type Rectangle struct { - X, Y uint16 - Width, Height uint16 - EncType EncodingType - Enc Encoding -} - -// String return string representation -func (rect *Rectangle) String() string { - return fmt.Sprintf("rect x: %d, y: %d, width: %d, height: %d, enc: %s", rect.X, rect.Y, rect.Width, rect.Height, rect.EncType) -} - -// NewRectangle returns new rectangle -func NewRectangle() *Rectangle { - return &Rectangle{} -} - -// Write marshal color to conn -func (clr *Color) Write(c Conn) error { - var err error - pf := c.PixelFormat() - order := pf.order() - pixel := clr.cmIndex - if clr.pf.TrueColor != 0 { - pixel = uint32(clr.R) << pf.RedShift - pixel |= uint32(clr.G) << pf.GreenShift - pixel |= uint32(clr.B) << pf.BlueShift - } - - switch pf.BPP { - case 8: - err = binary.Write(c, order, byte(pixel)) - case 16: - err = binary.Write(c, order, uint16(pixel)) - case 32: - err = binary.Write(c, order, uint32(pixel)) - } - - return err -} - -// Read unmarshal color from conn -func (clr *Color) Read(c Conn) error { - order := clr.pf.order() - var pixel uint32 - - switch clr.pf.BPP { - case 8: - var px uint8 - if err := binary.Read(c, order, &px); err != nil { - return err - } - pixel = uint32(px) - case 16: - var px uint16 - if err := binary.Read(c, order, &px); err != nil { - return err - } - pixel = uint32(px) - case 32: - var px uint32 - if err := binary.Read(c, order, &px); err != nil { - return err - } - pixel = uint32(px) - } - - if clr.pf.TrueColor != 0 { - clr.R = uint16((pixel >> clr.pf.RedShift) & uint32(clr.pf.RedMax)) - clr.G = uint16((pixel >> clr.pf.GreenShift) & uint32(clr.pf.GreenMax)) - clr.B = uint16((pixel >> clr.pf.BlueShift) & uint32(clr.pf.BlueMax)) - } else { - *clr = clr.cm[pixel] - clr.cmIndex = pixel - } - return nil -} - -func colorsToImage(x, y, width, height uint16, colors []Color) *image.RGBA64 { - rect := image.Rect(int(x), int(y), int(x+width), int(y+height)) - rgba := image.NewRGBA64(rect) - a := uint16(1) - for i, color := range colors { - rgba.Pix[4*i+0] = uint8(color.R >> 8) - rgba.Pix[4*i+1] = uint8(color.R) - rgba.Pix[4*i+2] = uint8(color.G >> 8) - rgba.Pix[4*i+3] = uint8(color.G) - rgba.Pix[4*i+4] = uint8(color.B >> 8) - rgba.Pix[4*i+5] = uint8(color.B) - rgba.Pix[4*i+6] = uint8(a >> 8) - rgba.Pix[4*i+7] = uint8(a) - } - return rgba -} - -// Write marshal rectangle to conn -func (rect *Rectangle) Write(c Conn) error { - var err error - - if err = binary.Write(c, binary.BigEndian, rect.X); err != nil { - return err - } - if err = binary.Write(c, binary.BigEndian, rect.Y); err != nil { - return err - } - if err = binary.Write(c, binary.BigEndian, rect.Width); err != nil { - return err - } - if err = binary.Write(c, binary.BigEndian, rect.Height); err != nil { - return err - } - if err = binary.Write(c, binary.BigEndian, rect.EncType); err != nil { - return err - } - - return rect.Enc.Write(c, rect) -} - -// Read unmarshal rectangle from conn -func (rect *Rectangle) Read(c Conn) error { - var err error - - if err = binary.Read(c, binary.BigEndian, &rect.X); err != nil { - return err - } - if err = binary.Read(c, binary.BigEndian, &rect.Y); err != nil { - return err - } - if err = binary.Read(c, binary.BigEndian, &rect.Width); err != nil { - return err - } - if err = binary.Read(c, binary.BigEndian, &rect.Height); err != nil { - return err - } - if err = binary.Read(c, binary.BigEndian, &rect.EncType); err != nil { - return err - } - - switch rect.EncType { - // case EncCopyRect: - // rect.Enc = &CopyRectEncoding{} - // case EncTight: - // rect.Enc = c.GetEncInstance(rect.EncType) - // case EncTightPng: - // rect.Enc = &TightPngEncoding{} - // case EncRaw: - // if strings.HasPrefix(c.Protocol(), "aten") { - // rect.Enc = &AtenHermon{} - // } else { - // rect.Enc = &RawEncoding{} - // } - case EncDesktopSizePseudo: - rect.Enc = &DesktopSizePseudoEncoding{} - case EncDesktopNamePseudo: - rect.Enc = &DesktopNamePseudoEncoding{} - // case EncXCursorPseudo: - // rect.Enc = &XCursorPseudoEncoding{} - // case EncAtenHermon: - // rect.Enc = &AtenHermon{} - default: - rect.Enc = c.GetEncInstance(rect.EncType) - if rect.Enc == nil { - return fmt.Errorf("unsupported encoding %s", rect.EncType) - } - } - - return rect.Enc.Read(c, rect) -} - -// Area returns the total area in pixels of the Rectangle -func (rect *Rectangle) Area() int { return int(rect.Width) * int(rect.Height) } +package vnc2video + +import ( + "encoding/binary" + "fmt" + "image" +) + +//var _ draw.Drawer = (*ServerConn)(nil) +//var _ draw.Image = (*ServerConn)(nil) + +// Color represents a single color in a color map. +type Color struct { + pf *PixelFormat + cm *ColorMap + cmIndex uint32 // Only valid if pf.TrueColor is false. + R, G, B uint16 +} + +// ColorMap represent color map +type ColorMap [256]Color + +// NewColor returns a new Color object +func NewColor(pf *PixelFormat, cm *ColorMap) *Color { + return &Color{ + pf: pf, + cm: cm, + } +} + +// Rectangle represents a rectangle of pixel data +type Rectangle struct { + X, Y uint16 + Width, Height uint16 + EncType EncodingType + Enc Encoding +} + +// String return string representation +func (rect *Rectangle) String() string { + return fmt.Sprintf("rect x: %d, y: %d, width: %d, height: %d, enc: %s", rect.X, rect.Y, rect.Width, rect.Height, rect.EncType) +} + +// NewRectangle returns new rectangle +func NewRectangle() *Rectangle { + return &Rectangle{} +} + +// Write marshal color to conn +func (clr *Color) Write(c Conn) error { + var err error + pf := c.PixelFormat() + order := pf.order() + pixel := clr.cmIndex + if clr.pf.TrueColor != 0 { + pixel = uint32(clr.R) << pf.RedShift + pixel |= uint32(clr.G) << pf.GreenShift + pixel |= uint32(clr.B) << pf.BlueShift + } + + switch pf.BPP { + case 8: + err = binary.Write(c, order, byte(pixel)) + case 16: + err = binary.Write(c, order, uint16(pixel)) + case 32: + err = binary.Write(c, order, uint32(pixel)) + } + + return err +} + +// Read unmarshal color from conn +func (clr *Color) Read(c Conn) error { + order := clr.pf.order() + var pixel uint32 + + switch clr.pf.BPP { + case 8: + var px uint8 + if err := binary.Read(c, order, &px); err != nil { + return err + } + pixel = uint32(px) + case 16: + var px uint16 + if err := binary.Read(c, order, &px); err != nil { + return err + } + pixel = uint32(px) + case 32: + var px uint32 + if err := binary.Read(c, order, &px); err != nil { + return err + } + pixel = uint32(px) + } + + if clr.pf.TrueColor != 0 { + clr.R = uint16((pixel >> clr.pf.RedShift) & uint32(clr.pf.RedMax)) + clr.G = uint16((pixel >> clr.pf.GreenShift) & uint32(clr.pf.GreenMax)) + clr.B = uint16((pixel >> clr.pf.BlueShift) & uint32(clr.pf.BlueMax)) + } else { + *clr = clr.cm[pixel] + clr.cmIndex = pixel + } + return nil +} + +func colorsToImage(x, y, width, height uint16, colors []Color) *image.RGBA64 { + rect := image.Rect(int(x), int(y), int(x+width), int(y+height)) + rgba := image.NewRGBA64(rect) + a := uint16(1) + for i, color := range colors { + rgba.Pix[4*i+0] = uint8(color.R >> 8) + rgba.Pix[4*i+1] = uint8(color.R) + rgba.Pix[4*i+2] = uint8(color.G >> 8) + rgba.Pix[4*i+3] = uint8(color.G) + rgba.Pix[4*i+4] = uint8(color.B >> 8) + rgba.Pix[4*i+5] = uint8(color.B) + rgba.Pix[4*i+6] = uint8(a >> 8) + rgba.Pix[4*i+7] = uint8(a) + } + return rgba +} + +// Write marshal rectangle to conn +func (rect *Rectangle) Write(c Conn) error { + var err error + + if err = binary.Write(c, binary.BigEndian, rect.X); err != nil { + return err + } + if err = binary.Write(c, binary.BigEndian, rect.Y); err != nil { + return err + } + if err = binary.Write(c, binary.BigEndian, rect.Width); err != nil { + return err + } + if err = binary.Write(c, binary.BigEndian, rect.Height); err != nil { + return err + } + if err = binary.Write(c, binary.BigEndian, rect.EncType); err != nil { + return err + } + + return rect.Enc.Write(c, rect) +} + +// Read unmarshal rectangle from conn +func (rect *Rectangle) Read(c Conn) error { + var err error + + if err = binary.Read(c, binary.BigEndian, &rect.X); err != nil { + return err + } + if err = binary.Read(c, binary.BigEndian, &rect.Y); err != nil { + return err + } + if err = binary.Read(c, binary.BigEndian, &rect.Width); err != nil { + return err + } + if err = binary.Read(c, binary.BigEndian, &rect.Height); err != nil { + return err + } + if err = binary.Read(c, binary.BigEndian, &rect.EncType); err != nil { + return err + } + + switch rect.EncType { + // case EncCopyRect: + // rect.Enc = &CopyRectEncoding{} + // case EncTight: + // rect.Enc = c.GetEncInstance(rect.EncType) + // case EncTightPng: + // rect.Enc = &TightPngEncoding{} + // case EncRaw: + // if strings.HasPrefix(c.Protocol(), "aten") { + // rect.Enc = &AtenHermon{} + // } else { + // rect.Enc = &RawEncoding{} + // } + case EncDesktopSizePseudo: + rect.Enc = &DesktopSizePseudoEncoding{} + case EncDesktopNamePseudo: + rect.Enc = &DesktopNamePseudoEncoding{} + // case EncXCursorPseudo: + // rect.Enc = &XCursorPseudoEncoding{} + // case EncAtenHermon: + // rect.Enc = &AtenHermon{} + default: + rect.Enc = c.GetEncInstance(rect.EncType) + if rect.Enc == nil { + return fmt.Errorf("unsupported encoding %s", rect.EncType) + } + } + + return rect.Enc.Read(c, rect) +} + +// Area returns the total area in pixels of the Rectangle +func (rect *Rectangle) Area() int { return int(rect.Width) * int(rect.Height) } diff --git a/key_string.go b/key_string.go index efaf8a2..c25c7a9 100644 --- a/key_string.go +++ b/key_string.go @@ -1,197 +1,197 @@ -// Code generated by "stringer -type=Key"; DO NOT EDIT. - -package vnc2webm - -import "fmt" - -const _Key_name = "SpaceExclaimQuoteDblNumberSignDollarPercentAmpersandApostropheParenLeftParenRightAsteriskPlusCommaMinusPeriodSlashDigit0Digit1Digit2Digit3Digit4Digit5Digit6Digit7Digit8Digit9ColonSemicolonLessEqualGreaterQuestionAtABCDEFGHIJKLMNOPQRSTUVWXYZBracketLeftBackslashBracketRightAsciiCircumUnderscoreGraveSmallASmallBSmallCSmallDSmallESmallFSmallGSmallHSmallISmallJSmallKSmallLSmallMSmallNSmallOSmallPSmallQSmallRSmallSSmallTSmallUSmallVSmallWSmallXSmallYSmallZBraceLeftBarBraceRightAsciiTildeBackSpaceTabLinefeedClearReturnPauseScrollLockSysReqEscapeHomeLeftUpRightDownPageUpPageDownEndBeginSelectModeSwitchNumLockKeypadSpaceKeypadTabKeypadEnterKeypadF1KeypadF2KeypadF3KeypadF4KeypadHomeKeypadLeftKeypadUpKeypadRightKeypadDownKeypadPriorKeypadPageUpKeypadNextKeypadPageDownKeypadEndKeypadBeginKeypadInsertKeypadDeleteKeypadMultiplyKeypadAddKeypadSeparatorKeypadSubtractKeypadDecimalKeypadDivideKeypad0Keypad1Keypad2Keypad3Keypad4Keypad5Keypad6Keypad7Keypad8Keypad9KeypadEqualF1F2F3F4F5F6F7F8F9F10F11F12ShiftLeftShiftRightControlLeftControlRightCapsLockShiftLockMetaLeftMetaRightAltLeftAltRightSuperLeftSuperRightHyperLeftHyperRightDelete" - -var _Key_map = map[Key]string{ - 32: _Key_name[0:5], - 33: _Key_name[5:12], - 34: _Key_name[12:20], - 35: _Key_name[20:30], - 36: _Key_name[30:36], - 37: _Key_name[36:43], - 38: _Key_name[43:52], - 39: _Key_name[52:62], - 40: _Key_name[62:71], - 41: _Key_name[71:81], - 42: _Key_name[81:89], - 43: _Key_name[89:93], - 44: _Key_name[93:98], - 45: _Key_name[98:103], - 46: _Key_name[103:109], - 47: _Key_name[109:114], - 48: _Key_name[114:120], - 49: _Key_name[120:126], - 50: _Key_name[126:132], - 51: _Key_name[132:138], - 52: _Key_name[138:144], - 53: _Key_name[144:150], - 54: _Key_name[150:156], - 55: _Key_name[156:162], - 56: _Key_name[162:168], - 57: _Key_name[168:174], - 58: _Key_name[174:179], - 59: _Key_name[179:188], - 60: _Key_name[188:192], - 61: _Key_name[192:197], - 62: _Key_name[197:204], - 63: _Key_name[204:212], - 64: _Key_name[212:214], - 65: _Key_name[214:215], - 66: _Key_name[215:216], - 67: _Key_name[216:217], - 68: _Key_name[217:218], - 69: _Key_name[218:219], - 70: _Key_name[219:220], - 71: _Key_name[220:221], - 72: _Key_name[221:222], - 73: _Key_name[222:223], - 74: _Key_name[223:224], - 75: _Key_name[224:225], - 76: _Key_name[225:226], - 77: _Key_name[226:227], - 78: _Key_name[227:228], - 79: _Key_name[228:229], - 80: _Key_name[229:230], - 81: _Key_name[230:231], - 82: _Key_name[231:232], - 83: _Key_name[232:233], - 84: _Key_name[233:234], - 85: _Key_name[234:235], - 86: _Key_name[235:236], - 87: _Key_name[236:237], - 88: _Key_name[237:238], - 89: _Key_name[238:239], - 90: _Key_name[239:240], - 91: _Key_name[240:251], - 92: _Key_name[251:260], - 93: _Key_name[260:272], - 94: _Key_name[272:283], - 95: _Key_name[283:293], - 96: _Key_name[293:298], - 97: _Key_name[298:304], - 98: _Key_name[304:310], - 99: _Key_name[310:316], - 100: _Key_name[316:322], - 101: _Key_name[322:328], - 102: _Key_name[328:334], - 103: _Key_name[334:340], - 104: _Key_name[340:346], - 105: _Key_name[346:352], - 106: _Key_name[352:358], - 107: _Key_name[358:364], - 108: _Key_name[364:370], - 109: _Key_name[370:376], - 110: _Key_name[376:382], - 111: _Key_name[382:388], - 112: _Key_name[388:394], - 113: _Key_name[394:400], - 114: _Key_name[400:406], - 115: _Key_name[406:412], - 116: _Key_name[412:418], - 117: _Key_name[418:424], - 118: _Key_name[424:430], - 119: _Key_name[430:436], - 120: _Key_name[436:442], - 121: _Key_name[442:448], - 122: _Key_name[448:454], - 123: _Key_name[454:463], - 124: _Key_name[463:466], - 125: _Key_name[466:476], - 126: _Key_name[476:486], - 65288: _Key_name[486:495], - 65289: _Key_name[495:498], - 65290: _Key_name[498:506], - 65291: _Key_name[506:511], - 65293: _Key_name[511:517], - 65299: _Key_name[517:522], - 65300: _Key_name[522:532], - 65301: _Key_name[532:538], - 65307: _Key_name[538:544], - 65360: _Key_name[544:548], - 65361: _Key_name[548:552], - 65362: _Key_name[552:554], - 65363: _Key_name[554:559], - 65364: _Key_name[559:563], - 65365: _Key_name[563:569], - 65366: _Key_name[569:577], - 65367: _Key_name[577:580], - 65368: _Key_name[580:585], - 65376: _Key_name[585:591], - 65406: _Key_name[591:601], - 65407: _Key_name[601:608], - 65408: _Key_name[608:619], - 65417: _Key_name[619:628], - 65421: _Key_name[628:639], - 65425: _Key_name[639:647], - 65426: _Key_name[647:655], - 65427: _Key_name[655:663], - 65428: _Key_name[663:671], - 65429: _Key_name[671:681], - 65430: _Key_name[681:691], - 65431: _Key_name[691:699], - 65432: _Key_name[699:710], - 65433: _Key_name[710:720], - 65434: _Key_name[720:731], - 65435: _Key_name[731:743], - 65436: _Key_name[743:753], - 65437: _Key_name[753:767], - 65438: _Key_name[767:776], - 65439: _Key_name[776:787], - 65440: _Key_name[787:799], - 65441: _Key_name[799:811], - 65442: _Key_name[811:825], - 65443: _Key_name[825:834], - 65444: _Key_name[834:849], - 65445: _Key_name[849:863], - 65446: _Key_name[863:876], - 65447: _Key_name[876:888], - 65448: _Key_name[888:895], - 65449: _Key_name[895:902], - 65450: _Key_name[902:909], - 65451: _Key_name[909:916], - 65452: _Key_name[916:923], - 65453: _Key_name[923:930], - 65454: _Key_name[930:937], - 65455: _Key_name[937:944], - 65456: _Key_name[944:951], - 65457: _Key_name[951:958], - 65469: _Key_name[958:969], - 65470: _Key_name[969:971], - 65471: _Key_name[971:973], - 65472: _Key_name[973:975], - 65473: _Key_name[975:977], - 65474: _Key_name[977:979], - 65475: _Key_name[979:981], - 65476: _Key_name[981:983], - 65477: _Key_name[983:985], - 65478: _Key_name[985:987], - 65479: _Key_name[987:990], - 65480: _Key_name[990:993], - 65481: _Key_name[993:996], - 65505: _Key_name[996:1005], - 65506: _Key_name[1005:1015], - 65507: _Key_name[1015:1026], - 65508: _Key_name[1026:1038], - 65509: _Key_name[1038:1046], - 65510: _Key_name[1046:1055], - 65511: _Key_name[1055:1063], - 65512: _Key_name[1063:1072], - 65513: _Key_name[1072:1079], - 65514: _Key_name[1079:1087], - 65515: _Key_name[1087:1096], - 65516: _Key_name[1096:1106], - 65517: _Key_name[1106:1115], - 65518: _Key_name[1115:1125], - 65535: _Key_name[1125:1131], -} - -func (i Key) String() string { - if str, ok := _Key_map[i]; ok { - return str - } - return fmt.Sprintf("Key(%d)", i) -} +// Code generated by "stringer -type=Key"; DO NOT EDIT. + +package vnc2video + +import "fmt" + +const _Key_name = "SpaceExclaimQuoteDblNumberSignDollarPercentAmpersandApostropheParenLeftParenRightAsteriskPlusCommaMinusPeriodSlashDigit0Digit1Digit2Digit3Digit4Digit5Digit6Digit7Digit8Digit9ColonSemicolonLessEqualGreaterQuestionAtABCDEFGHIJKLMNOPQRSTUVWXYZBracketLeftBackslashBracketRightAsciiCircumUnderscoreGraveSmallASmallBSmallCSmallDSmallESmallFSmallGSmallHSmallISmallJSmallKSmallLSmallMSmallNSmallOSmallPSmallQSmallRSmallSSmallTSmallUSmallVSmallWSmallXSmallYSmallZBraceLeftBarBraceRightAsciiTildeBackSpaceTabLinefeedClearReturnPauseScrollLockSysReqEscapeHomeLeftUpRightDownPageUpPageDownEndBeginSelectModeSwitchNumLockKeypadSpaceKeypadTabKeypadEnterKeypadF1KeypadF2KeypadF3KeypadF4KeypadHomeKeypadLeftKeypadUpKeypadRightKeypadDownKeypadPriorKeypadPageUpKeypadNextKeypadPageDownKeypadEndKeypadBeginKeypadInsertKeypadDeleteKeypadMultiplyKeypadAddKeypadSeparatorKeypadSubtractKeypadDecimalKeypadDivideKeypad0Keypad1Keypad2Keypad3Keypad4Keypad5Keypad6Keypad7Keypad8Keypad9KeypadEqualF1F2F3F4F5F6F7F8F9F10F11F12ShiftLeftShiftRightControlLeftControlRightCapsLockShiftLockMetaLeftMetaRightAltLeftAltRightSuperLeftSuperRightHyperLeftHyperRightDelete" + +var _Key_map = map[Key]string{ + 32: _Key_name[0:5], + 33: _Key_name[5:12], + 34: _Key_name[12:20], + 35: _Key_name[20:30], + 36: _Key_name[30:36], + 37: _Key_name[36:43], + 38: _Key_name[43:52], + 39: _Key_name[52:62], + 40: _Key_name[62:71], + 41: _Key_name[71:81], + 42: _Key_name[81:89], + 43: _Key_name[89:93], + 44: _Key_name[93:98], + 45: _Key_name[98:103], + 46: _Key_name[103:109], + 47: _Key_name[109:114], + 48: _Key_name[114:120], + 49: _Key_name[120:126], + 50: _Key_name[126:132], + 51: _Key_name[132:138], + 52: _Key_name[138:144], + 53: _Key_name[144:150], + 54: _Key_name[150:156], + 55: _Key_name[156:162], + 56: _Key_name[162:168], + 57: _Key_name[168:174], + 58: _Key_name[174:179], + 59: _Key_name[179:188], + 60: _Key_name[188:192], + 61: _Key_name[192:197], + 62: _Key_name[197:204], + 63: _Key_name[204:212], + 64: _Key_name[212:214], + 65: _Key_name[214:215], + 66: _Key_name[215:216], + 67: _Key_name[216:217], + 68: _Key_name[217:218], + 69: _Key_name[218:219], + 70: _Key_name[219:220], + 71: _Key_name[220:221], + 72: _Key_name[221:222], + 73: _Key_name[222:223], + 74: _Key_name[223:224], + 75: _Key_name[224:225], + 76: _Key_name[225:226], + 77: _Key_name[226:227], + 78: _Key_name[227:228], + 79: _Key_name[228:229], + 80: _Key_name[229:230], + 81: _Key_name[230:231], + 82: _Key_name[231:232], + 83: _Key_name[232:233], + 84: _Key_name[233:234], + 85: _Key_name[234:235], + 86: _Key_name[235:236], + 87: _Key_name[236:237], + 88: _Key_name[237:238], + 89: _Key_name[238:239], + 90: _Key_name[239:240], + 91: _Key_name[240:251], + 92: _Key_name[251:260], + 93: _Key_name[260:272], + 94: _Key_name[272:283], + 95: _Key_name[283:293], + 96: _Key_name[293:298], + 97: _Key_name[298:304], + 98: _Key_name[304:310], + 99: _Key_name[310:316], + 100: _Key_name[316:322], + 101: _Key_name[322:328], + 102: _Key_name[328:334], + 103: _Key_name[334:340], + 104: _Key_name[340:346], + 105: _Key_name[346:352], + 106: _Key_name[352:358], + 107: _Key_name[358:364], + 108: _Key_name[364:370], + 109: _Key_name[370:376], + 110: _Key_name[376:382], + 111: _Key_name[382:388], + 112: _Key_name[388:394], + 113: _Key_name[394:400], + 114: _Key_name[400:406], + 115: _Key_name[406:412], + 116: _Key_name[412:418], + 117: _Key_name[418:424], + 118: _Key_name[424:430], + 119: _Key_name[430:436], + 120: _Key_name[436:442], + 121: _Key_name[442:448], + 122: _Key_name[448:454], + 123: _Key_name[454:463], + 124: _Key_name[463:466], + 125: _Key_name[466:476], + 126: _Key_name[476:486], + 65288: _Key_name[486:495], + 65289: _Key_name[495:498], + 65290: _Key_name[498:506], + 65291: _Key_name[506:511], + 65293: _Key_name[511:517], + 65299: _Key_name[517:522], + 65300: _Key_name[522:532], + 65301: _Key_name[532:538], + 65307: _Key_name[538:544], + 65360: _Key_name[544:548], + 65361: _Key_name[548:552], + 65362: _Key_name[552:554], + 65363: _Key_name[554:559], + 65364: _Key_name[559:563], + 65365: _Key_name[563:569], + 65366: _Key_name[569:577], + 65367: _Key_name[577:580], + 65368: _Key_name[580:585], + 65376: _Key_name[585:591], + 65406: _Key_name[591:601], + 65407: _Key_name[601:608], + 65408: _Key_name[608:619], + 65417: _Key_name[619:628], + 65421: _Key_name[628:639], + 65425: _Key_name[639:647], + 65426: _Key_name[647:655], + 65427: _Key_name[655:663], + 65428: _Key_name[663:671], + 65429: _Key_name[671:681], + 65430: _Key_name[681:691], + 65431: _Key_name[691:699], + 65432: _Key_name[699:710], + 65433: _Key_name[710:720], + 65434: _Key_name[720:731], + 65435: _Key_name[731:743], + 65436: _Key_name[743:753], + 65437: _Key_name[753:767], + 65438: _Key_name[767:776], + 65439: _Key_name[776:787], + 65440: _Key_name[787:799], + 65441: _Key_name[799:811], + 65442: _Key_name[811:825], + 65443: _Key_name[825:834], + 65444: _Key_name[834:849], + 65445: _Key_name[849:863], + 65446: _Key_name[863:876], + 65447: _Key_name[876:888], + 65448: _Key_name[888:895], + 65449: _Key_name[895:902], + 65450: _Key_name[902:909], + 65451: _Key_name[909:916], + 65452: _Key_name[916:923], + 65453: _Key_name[923:930], + 65454: _Key_name[930:937], + 65455: _Key_name[937:944], + 65456: _Key_name[944:951], + 65457: _Key_name[951:958], + 65469: _Key_name[958:969], + 65470: _Key_name[969:971], + 65471: _Key_name[971:973], + 65472: _Key_name[973:975], + 65473: _Key_name[975:977], + 65474: _Key_name[977:979], + 65475: _Key_name[979:981], + 65476: _Key_name[981:983], + 65477: _Key_name[983:985], + 65478: _Key_name[985:987], + 65479: _Key_name[987:990], + 65480: _Key_name[990:993], + 65481: _Key_name[993:996], + 65505: _Key_name[996:1005], + 65506: _Key_name[1005:1015], + 65507: _Key_name[1015:1026], + 65508: _Key_name[1026:1038], + 65509: _Key_name[1038:1046], + 65510: _Key_name[1046:1055], + 65511: _Key_name[1055:1063], + 65512: _Key_name[1063:1072], + 65513: _Key_name[1072:1079], + 65514: _Key_name[1079:1087], + 65515: _Key_name[1087:1096], + 65516: _Key_name[1096:1106], + 65517: _Key_name[1106:1115], + 65518: _Key_name[1115:1125], + 65535: _Key_name[1125:1131], +} + +func (i Key) String() string { + if str, ok := _Key_map[i]; ok { + return str + } + return fmt.Sprintf("Key(%d)", i) +} diff --git a/keys.go b/keys.go index ba548e1..e706cc7 100644 --- a/keys.go +++ b/keys.go @@ -1,254 +1,254 @@ -package vnc2webm - -import "fmt" - -// Key represents a VNC key press. -type Key uint32 - -//go:generate stringer -type=Key - -// Keys is a slice of Key values. -type Keys []Key - -var keymap = map[rune]Key{ - '-': Minus, - '0': Digit0, - '1': Digit1, - '2': Digit2, - '3': Digit3, - '4': Digit4, - '5': Digit5, - '6': Digit6, - '7': Digit7, - '8': Digit8, - '9': Digit9, -} - -// IntToKeys returns Keys that represent the key presses required to type an int. -func IntToKeys(v int) Keys { - k := Keys{} - for _, c := range fmt.Sprintf("%d", v) { - k = append(k, keymap[c]) - } - return k -} - -// Latin 1 (byte 3 = 0) -// ISO/IEC 8859-1 = Unicode U+0020..U+00FF -const ( - Space Key = iota + 0x0020 - Exclaim // exclamation mark - QuoteDbl - NumberSign - Dollar - Percent - Ampersand - Apostrophe - ParenLeft - ParenRight - Asterisk - Plus - Comma - Minus - Period - Slash - Digit0 - Digit1 - Digit2 - Digit3 - Digit4 - Digit5 - Digit6 - Digit7 - Digit8 - Digit9 - Colon - Semicolon - Less - Equal - Greater - Question - At - A - B - C - D - E - F - G - H - I - J - K - L - M - N - O - P - Q - R - S - T - U - V - W - X - Y - Z - BracketLeft - Backslash - BracketRight - AsciiCircum - Underscore - Grave - SmallA - SmallB - SmallC - SmallD - SmallE - SmallF - SmallG - SmallH - SmallI - SmallJ - SmallK - SmallL - SmallM - SmallN - SmallO - SmallP - SmallQ - SmallR - SmallS - SmallT - SmallU - SmallV - SmallW - SmallX - SmallY - SmallZ - BraceLeft - Bar - BraceRight - AsciiTilde -) - -const ( - BackSpace Key = iota + 0xff08 - Tab - Linefeed - Clear - _ - Return -) - -const ( - Pause Key = iota + 0xff13 - ScrollLock - SysReq - Escape Key = 0xff1b - Delete Key = 0xffff -) - -const ( // Cursor control & motion. - Home Key = iota + 0xff50 - Left - Up - Right - Down - PageUp - PageDown - End - Begin -) - -const ( // Misc functions. - Select Key = 0xff60 - Print - Execute - Insert - Undo - Redo - Menu - Find - Cancel - Help - Break - ModeSwitch Key = 0xff7e - NumLock Key = 0xff7f -) - -const ( // Keypad functions. - KeypadSpace Key = 0xff80 - KeypadTab Key = 0xff89 - KeypadEnter Key = 0xff8d -) - -const ( // Keypad functions cont. - KeypadF1 Key = iota + 0xff91 - KeypadF2 - KeypadF3 - KeypadF4 - KeypadHome - KeypadLeft - KeypadUp - KeypadRight - KeypadDown - KeypadPrior - KeypadPageUp - KeypadNext - KeypadPageDown - KeypadEnd - KeypadBegin - KeypadInsert - KeypadDelete - KeypadMultiply - KeypadAdd - KeypadSeparator - KeypadSubtract - KeypadDecimal - KeypadDivide - Keypad0 - Keypad1 - Keypad2 - Keypad3 - Keypad4 - Keypad5 - Keypad6 - Keypad7 - Keypad8 - Keypad9 - KeypadEqual Key = 0xffbd -) - -const ( - F1 Key = iota + 0xffbe - F2 - F3 - F4 - F5 - F6 - F7 - F8 - F9 - F10 - F11 - F12 -) - -const ( - ShiftLeft Key = iota + 0xffe1 - ShiftRight - ControlLeft - ControlRight - CapsLock - ShiftLock - MetaLeft - MetaRight - AltLeft - AltRight - SuperLeft - SuperRight - HyperLeft - HyperRight -) +package vnc2video + +import "fmt" + +// Key represents a VNC key press. +type Key uint32 + +//go:generate stringer -type=Key + +// Keys is a slice of Key values. +type Keys []Key + +var keymap = map[rune]Key{ + '-': Minus, + '0': Digit0, + '1': Digit1, + '2': Digit2, + '3': Digit3, + '4': Digit4, + '5': Digit5, + '6': Digit6, + '7': Digit7, + '8': Digit8, + '9': Digit9, +} + +// IntToKeys returns Keys that represent the key presses required to type an int. +func IntToKeys(v int) Keys { + k := Keys{} + for _, c := range fmt.Sprintf("%d", v) { + k = append(k, keymap[c]) + } + return k +} + +// Latin 1 (byte 3 = 0) +// ISO/IEC 8859-1 = Unicode U+0020..U+00FF +const ( + Space Key = iota + 0x0020 + Exclaim // exclamation mark + QuoteDbl + NumberSign + Dollar + Percent + Ampersand + Apostrophe + ParenLeft + ParenRight + Asterisk + Plus + Comma + Minus + Period + Slash + Digit0 + Digit1 + Digit2 + Digit3 + Digit4 + Digit5 + Digit6 + Digit7 + Digit8 + Digit9 + Colon + Semicolon + Less + Equal + Greater + Question + At + A + B + C + D + E + F + G + H + I + J + K + L + M + N + O + P + Q + R + S + T + U + V + W + X + Y + Z + BracketLeft + Backslash + BracketRight + AsciiCircum + Underscore + Grave + SmallA + SmallB + SmallC + SmallD + SmallE + SmallF + SmallG + SmallH + SmallI + SmallJ + SmallK + SmallL + SmallM + SmallN + SmallO + SmallP + SmallQ + SmallR + SmallS + SmallT + SmallU + SmallV + SmallW + SmallX + SmallY + SmallZ + BraceLeft + Bar + BraceRight + AsciiTilde +) + +const ( + BackSpace Key = iota + 0xff08 + Tab + Linefeed + Clear + _ + Return +) + +const ( + Pause Key = iota + 0xff13 + ScrollLock + SysReq + Escape Key = 0xff1b + Delete Key = 0xffff +) + +const ( // Cursor control & motion. + Home Key = iota + 0xff50 + Left + Up + Right + Down + PageUp + PageDown + End + Begin +) + +const ( // Misc functions. + Select Key = 0xff60 + Print + Execute + Insert + Undo + Redo + Menu + Find + Cancel + Help + Break + ModeSwitch Key = 0xff7e + NumLock Key = 0xff7f +) + +const ( // Keypad functions. + KeypadSpace Key = 0xff80 + KeypadTab Key = 0xff89 + KeypadEnter Key = 0xff8d +) + +const ( // Keypad functions cont. + KeypadF1 Key = iota + 0xff91 + KeypadF2 + KeypadF3 + KeypadF4 + KeypadHome + KeypadLeft + KeypadUp + KeypadRight + KeypadDown + KeypadPrior + KeypadPageUp + KeypadNext + KeypadPageDown + KeypadEnd + KeypadBegin + KeypadInsert + KeypadDelete + KeypadMultiply + KeypadAdd + KeypadSeparator + KeypadSubtract + KeypadDecimal + KeypadDivide + Keypad0 + Keypad1 + Keypad2 + Keypad3 + Keypad4 + Keypad5 + Keypad6 + Keypad7 + Keypad8 + Keypad9 + KeypadEqual Key = 0xffbd +) + +const ( + F1 Key = iota + 0xffbe + F2 + F3 + F4 + F5 + F6 + F7 + F8 + F9 + F10 + F11 + F12 +) + +const ( + ShiftLeft Key = iota + 0xffe1 + ShiftRight + ControlLeft + ControlRight + CapsLock + ShiftLock + MetaLeft + MetaRight + AltLeft + AltRight + SuperLeft + SuperRight + HyperLeft + HyperRight +) diff --git a/logger/logger.go b/logger/logger.go index 7c4a3a5..2841d61 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -1,156 +1,156 @@ -package logger - -import "fmt" - -var simpleLogger = SimpleLogger{LogLevelInfo} - -type Logger interface { - Debug(v ...interface{}) - Debugf(format string, v ...interface{}) - Info(v ...interface{}) - Infof(format string, v ...interface{}) - Warn(v ...interface{}) - Warnf(format string, v ...interface{}) - Error(v ...interface{}) - Errorf(format string, v ...interface{}) - Fatal(v ...interface{}) - Fatalf(format string, v ...interface{}) -} -type LogLevel int - -const ( - LogLevelTrace LogLevel = iota - LogLevelDebug - LogLevelInfo - LogLevelWarn - LogLevelError - LogLevelFatal -) - -type SimpleLogger struct { - level LogLevel -} - -func (sl *SimpleLogger) Trace(v ...interface{}) { - if sl.level <= LogLevelTrace { - arr := []interface{}{"[Trace]"} - for _, item := range v { - arr = append(arr, item) - } - - fmt.Println(arr...) - } -} -func (sl *SimpleLogger) Tracef(format string, v ...interface{}) { - if sl.level <= LogLevelTrace { - fmt.Printf("[Trace] "+format+"\n", v...) - } -} - -func (sl *SimpleLogger) Debug(v ...interface{}) { - if sl.level <= LogLevelDebug { - arr := []interface{}{"[Debug]"} - for _, item := range v { - arr = append(arr, item) - } - - fmt.Println(arr...) - } -} -func (sl *SimpleLogger) Debugf(format string, v ...interface{}) { - if sl.level <= LogLevelDebug { - fmt.Printf("[Debug] "+format+"\n", v...) - } -} -func (sl *SimpleLogger) Info(v ...interface{}) { - if sl.level <= LogLevelInfo { - arr := []interface{}{"[Info ]"} - for _, item := range v { - arr = append(arr, item) - } - fmt.Println(arr...) - } -} -func (sl *SimpleLogger) Infof(format string, v ...interface{}) { - if sl.level <= LogLevelInfo { - fmt.Printf("[Info ] "+format+"\n", v...) - } -} -func (sl *SimpleLogger) Warn(v ...interface{}) { - if sl.level <= LogLevelWarn { - arr := []interface{}{"[Warn ]"} - for _, item := range v { - arr = append(arr, item) - } - fmt.Println(arr...) - } -} -func (sl *SimpleLogger) Warnf(format string, v ...interface{}) { - if sl.level <= LogLevelWarn { - fmt.Printf("[Warn ] "+format+"\n", v...) - } -} -func (sl *SimpleLogger) Error(v ...interface{}) { - if sl.level <= LogLevelError { - arr := []interface{}{"[Error]"} - for _, item := range v { - arr = append(arr, item) - } - fmt.Println(arr...) - } -} -func (sl *SimpleLogger) Errorf(format string, v ...interface{}) { - if sl.level <= LogLevelError { - fmt.Printf("[Error] "+format+"\n", v...) - } -} -func (sl *SimpleLogger) Fatal(v ...interface{}) { - if sl.level <= LogLevelFatal { - arr := []interface{}{"[Fatal]"} - for _, item := range v { - arr = append(arr, item) - } - fmt.Println(arr...) - - } -} -func (sl *SimpleLogger) Fatalf(format string, v ...interface{}) { - if sl.level <= LogLevelFatal { - fmt.Printf("[Fatal] "+format+"\n", v) - } -} - -func Debug(v ...interface{}) { - simpleLogger.Debug(v...) -} -func Debugf(format string, v ...interface{}) { - simpleLogger.Debugf(format, v...) -} - -func Info(v ...interface{}) { - simpleLogger.Info(v...) -} -func Infof(format string, v ...interface{}) { - simpleLogger.Infof(format, v...) -} - -func Warn(v ...interface{}) { - simpleLogger.Warn(v...) -} -func Warnf(format string, v ...interface{}) { - simpleLogger.Warnf(format, v...) -} - -func Error(v ...interface{}) { - simpleLogger.Error(v...) -} -func Errorf(format string, v ...interface{}) { - simpleLogger.Errorf(format, v...) -} - -func Fatal(v ...interface{}) { - simpleLogger.Fatal(v...) -} -func Fatalf(format string, v ...interface{}) { - simpleLogger.Fatalf(format, v...) -} +package logger + +import "fmt" + +var simpleLogger = SimpleLogger{LogLevelInfo} + +type Logger interface { + Debug(v ...interface{}) + Debugf(format string, v ...interface{}) + Info(v ...interface{}) + Infof(format string, v ...interface{}) + Warn(v ...interface{}) + Warnf(format string, v ...interface{}) + Error(v ...interface{}) + Errorf(format string, v ...interface{}) + Fatal(v ...interface{}) + Fatalf(format string, v ...interface{}) +} +type LogLevel int + +const ( + LogLevelTrace LogLevel = iota + LogLevelDebug + LogLevelInfo + LogLevelWarn + LogLevelError + LogLevelFatal +) + +type SimpleLogger struct { + level LogLevel +} + +func (sl *SimpleLogger) Trace(v ...interface{}) { + if sl.level <= LogLevelTrace { + arr := []interface{}{"[Trace]"} + for _, item := range v { + arr = append(arr, item) + } + + fmt.Println(arr...) + } +} +func (sl *SimpleLogger) Tracef(format string, v ...interface{}) { + if sl.level <= LogLevelTrace { + fmt.Printf("[Trace] "+format+"\n", v...) + } +} + +func (sl *SimpleLogger) Debug(v ...interface{}) { + if sl.level <= LogLevelDebug { + arr := []interface{}{"[Debug]"} + for _, item := range v { + arr = append(arr, item) + } + + fmt.Println(arr...) + } +} +func (sl *SimpleLogger) Debugf(format string, v ...interface{}) { + if sl.level <= LogLevelDebug { + fmt.Printf("[Debug] "+format+"\n", v...) + } +} +func (sl *SimpleLogger) Info(v ...interface{}) { + if sl.level <= LogLevelInfo { + arr := []interface{}{"[Info ]"} + for _, item := range v { + arr = append(arr, item) + } + fmt.Println(arr...) + } +} +func (sl *SimpleLogger) Infof(format string, v ...interface{}) { + if sl.level <= LogLevelInfo { + fmt.Printf("[Info ] "+format+"\n", v...) + } +} +func (sl *SimpleLogger) Warn(v ...interface{}) { + if sl.level <= LogLevelWarn { + arr := []interface{}{"[Warn ]"} + for _, item := range v { + arr = append(arr, item) + } + fmt.Println(arr...) + } +} +func (sl *SimpleLogger) Warnf(format string, v ...interface{}) { + if sl.level <= LogLevelWarn { + fmt.Printf("[Warn ] "+format+"\n", v...) + } +} +func (sl *SimpleLogger) Error(v ...interface{}) { + if sl.level <= LogLevelError { + arr := []interface{}{"[Error]"} + for _, item := range v { + arr = append(arr, item) + } + fmt.Println(arr...) + } +} +func (sl *SimpleLogger) Errorf(format string, v ...interface{}) { + if sl.level <= LogLevelError { + fmt.Printf("[Error] "+format+"\n", v...) + } +} +func (sl *SimpleLogger) Fatal(v ...interface{}) { + if sl.level <= LogLevelFatal { + arr := []interface{}{"[Fatal]"} + for _, item := range v { + arr = append(arr, item) + } + fmt.Println(arr...) + + } +} +func (sl *SimpleLogger) Fatalf(format string, v ...interface{}) { + if sl.level <= LogLevelFatal { + fmt.Printf("[Fatal] "+format+"\n", v) + } +} + +func Debug(v ...interface{}) { + simpleLogger.Debug(v...) +} +func Debugf(format string, v ...interface{}) { + simpleLogger.Debugf(format, v...) +} + +func Info(v ...interface{}) { + simpleLogger.Info(v...) +} +func Infof(format string, v ...interface{}) { + simpleLogger.Infof(format, v...) +} + +func Warn(v ...interface{}) { + simpleLogger.Warn(v...) +} +func Warnf(format string, v ...interface{}) { + simpleLogger.Warnf(format, v...) +} + +func Error(v ...interface{}) { + simpleLogger.Error(v...) +} +func Errorf(format string, v ...interface{}) { + simpleLogger.Errorf(format, v...) +} + +func Fatal(v ...interface{}) { + simpleLogger.Fatal(v...) +} +func Fatalf(format string, v ...interface{}) { + simpleLogger.Fatalf(format, v...) +} diff --git a/messages.go b/messages.go index c1dad96..67508d4 100644 --- a/messages.go +++ b/messages.go @@ -1,633 +1,633 @@ -package vnc2webm - -import ( - "encoding/binary" - "fmt" - - "vnc2webm/logger" -) - -var ( - // DefaultClientMessages slice of default client messages sent to server - DefaultClientMessages = []ClientMessage{ - &SetPixelFormat{}, - &SetEncodings{}, - &FramebufferUpdateRequest{}, - &KeyEvent{}, - &PointerEvent{}, - &ClientCutText{}, - } - - // DefaultServerMessages slice of default server messages sent to client - DefaultServerMessages = []ServerMessage{ - &FramebufferUpdate{}, - &SetColorMapEntries{}, - &Bell{}, - &ServerCutText{}, - } -) - -// ClientMessageType represents RFB message type -type ClientMessageType uint8 - -//go:generate stringer -type=ClientMessageType - -// Client-to-Server message types. -const ( - SetPixelFormatMsgType ClientMessageType = iota - _ - SetEncodingsMsgType - FramebufferUpdateRequestMsgType - KeyEventMsgType - PointerEventMsgType - ClientCutTextMsgType -) - -// ServerMessageType represents RFB message type -type ServerMessageType uint8 - -// Server-to-Client message types -const ( - FramebufferUpdateMsgType ServerMessageType = iota - SetColorMapEntriesMsgType - BellMsgType - ServerCutTextMsgType -) - -// ServerInit struct used in server init handshake -type ServerInit struct { - FBWidth, FBHeight uint16 - PixelFormat PixelFormat - NameLength uint32 - NameText []byte -} - -// String provide stringer -func (srvInit ServerInit) String() string { - return fmt.Sprintf("Width: %d, Height: %d, PixelFormat: %s, NameLength: %d, MameText: %s", srvInit.FBWidth, srvInit.FBHeight, srvInit.PixelFormat, srvInit.NameLength, srvInit.NameText) -} - -type ClientMessage interface { - String() string - Type() ClientMessageType - Read(Conn) (ClientMessage, error) - Write(Conn) error - Supported(Conn) bool -} - -type ServerMessage interface { - String() string - Type() ServerMessageType - Read(Conn) (ServerMessage, error) - Write(Conn) error - Supported(Conn) bool -} - -// FramebufferUpdate holds a FramebufferUpdate wire format message. -type FramebufferUpdate struct { - _ [1]byte // pad - NumRect uint16 // number-of-rectangles - Rects []*Rectangle // rectangles -} - -// String provide stringer -func (msg *FramebufferUpdate) String() string { - return fmt.Sprintf("rects %d rectangle[]: { %v }", msg.NumRect, msg.Rects) -} - -func (msg *FramebufferUpdate) Supported(c Conn) bool { - return true -} - -// Type return MessageType -func (*FramebufferUpdate) Type() ServerMessageType { - return FramebufferUpdateMsgType -} - -// Read unmarshal message from conn -func (*FramebufferUpdate) Read(c Conn) (ServerMessage, error) { - msg := FramebufferUpdate{} - var pad [1]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - - if err := binary.Read(c, binary.BigEndian, &msg.NumRect); err != nil { - return nil, err - } - for i := uint16(0); i < msg.NumRect; i++ { - rect := NewRectangle() - //logger.Debugf("----------RECT %d----------:", i) - - if err := rect.Read(c); err != nil { - return nil, err - } - if rect.EncType == EncDesktopSizePseudo { - c.(*ClientConn).ResetAllEncodings() - } - logger.Debugf("----End RECT #%d Info (%dx%d) encType:%s", i, rect.Width, rect.Height, rect.EncType) - msg.Rects = append(msg.Rects, rect) - } - return &msg, nil -} - -// Write marshals message to conn -func (msg *FramebufferUpdate) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - var pad [1]byte - if err := binary.Write(c, binary.BigEndian, pad); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, msg.NumRect); err != nil { - return err - } - for _, rect := range msg.Rects { - if err := rect.Write(c); err != nil { - return err - } - } - return c.Flush() -} - -// ServerCutText represents server message -type ServerCutText struct { - _ [1]byte - Length uint32 - Text []byte -} - -func (msg *ServerCutText) Supported(c Conn) bool { - return true -} - -// String returns string -func (msg *ServerCutText) String() string { - return fmt.Sprintf("lenght: %d text: %s", msg.Length, msg.Text) -} - -// Type returns MessageType -func (*ServerCutText) Type() ServerMessageType { - return ServerCutTextMsgType -} - -// Read unmarshal message from conn -func (*ServerCutText) Read(c Conn) (ServerMessage, error) { - msg := ServerCutText{} - - var pad [1]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - - if err := binary.Read(c, binary.BigEndian, &msg.Length); err != nil { - return nil, err - } - - msg.Text = make([]byte, msg.Length) - if err := binary.Read(c, binary.BigEndian, &msg.Text); err != nil { - return nil, err - } - return &msg, nil -} - -// Write marshal message to conn -func (msg *ServerCutText) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - var pad [1]byte - if err := binary.Write(c, binary.BigEndian, pad); err != nil { - return err - } - - if msg.Length < uint32(len(msg.Text)) { - msg.Length = uint32(len(msg.Text)) - } - if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil { - return err - } - return c.Flush() -} - -// Bell server message -type Bell struct{} - -func (*Bell) Supported(c Conn) bool { - return true -} - -// String return string -func (*Bell) String() string { - return fmt.Sprintf("bell") -} - -// Type returns MessageType -func (*Bell) Type() ServerMessageType { - return BellMsgType -} - -// Read unmarshal message from conn -func (*Bell) Read(c Conn) (ServerMessage, error) { - return &Bell{}, nil -} - -// Write marshal message to conn -func (msg *Bell) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - return c.Flush() -} - -// SetColorMapEntries server message -type SetColorMapEntries struct { - _ [1]byte - FirstColor uint16 - ColorsNum uint16 - Colors []Color -} - -func (msg *SetColorMapEntries) Supported(c Conn) bool { - return true -} - -// String returns string -func (msg *SetColorMapEntries) String() string { - return fmt.Sprintf("first color: %d, numcolors: %d, colors[]: { %v }", msg.FirstColor, msg.ColorsNum, msg.Colors) -} - -// Type returns MessageType -func (*SetColorMapEntries) Type() ServerMessageType { - return SetColorMapEntriesMsgType -} - -// Read unmrashal message from conn -func (*SetColorMapEntries) Read(c Conn) (ServerMessage, error) { - msg := SetColorMapEntries{} - var pad [1]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - - if err := binary.Read(c, binary.BigEndian, &msg.FirstColor); err != nil { - return nil, err - } - - if err := binary.Read(c, binary.BigEndian, &msg.ColorsNum); err != nil { - return nil, err - } - - msg.Colors = make([]Color, msg.ColorsNum) - colorMap := c.ColorMap() - - for i := uint16(0); i < msg.ColorsNum; i++ { - color := &msg.Colors[i] - if err := binary.Read(c, binary.BigEndian, &color); err != nil { - return nil, err - } - colorMap[msg.FirstColor+i] = *color - } - c.SetColorMap(colorMap) - return &msg, nil -} - -// Write marshal message to conn -func (msg *SetColorMapEntries) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - var pad [1]byte - if err := binary.Write(c, binary.BigEndian, &pad); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, msg.FirstColor); err != nil { - return err - } - - if msg.ColorsNum < uint16(len(msg.Colors)) { - msg.ColorsNum = uint16(len(msg.Colors)) - } - if err := binary.Write(c, binary.BigEndian, msg.ColorsNum); err != nil { - return err - } - - for i := 0; i < len(msg.Colors); i++ { - color := msg.Colors[i] - if err := binary.Write(c, binary.BigEndian, color); err != nil { - return err - } - } - - return c.Flush() -} - -// SetPixelFormat holds the wire format message. -type SetPixelFormat struct { - _ [3]byte // padding - PF PixelFormat // pixel-format -} - -func (msg *SetPixelFormat) Supported(c Conn) bool { - return true -} - -// String returns string -func (msg *SetPixelFormat) String() string { - return fmt.Sprintf("%s", msg.PF) -} - -// Type returns MessageType -func (*SetPixelFormat) Type() ClientMessageType { - return SetPixelFormatMsgType -} - -// Write marshal message to conn -func (msg *SetPixelFormat) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, msg); err != nil { - return err - } - - pf := c.PixelFormat() - // Invalidate the color map. - if pf.TrueColor != 0 { - c.SetColorMap(ColorMap{}) - } - - return c.Flush() -} - -// Read unmarshal message from conn -func (*SetPixelFormat) Read(c Conn) (ClientMessage, error) { - msg := SetPixelFormat{} - if err := binary.Read(c, binary.BigEndian, &msg); err != nil { - return nil, err - } - return &msg, nil -} - -// SetEncodings holds the wire format message, sans encoding-type field. -type SetEncodings struct { - _ [1]byte // padding - EncNum uint16 // number-of-encodings - Encodings []EncodingType -} - -func (msg *SetEncodings) Supported(c Conn) bool { - return true -} - -// String return string -func (msg *SetEncodings) String() string { - return fmt.Sprintf("encnum: %d, encodings[]: { %v }", msg.EncNum, msg.Encodings) -} - -// Type returns MessageType -func (*SetEncodings) Type() ClientMessageType { - return SetEncodingsMsgType -} - -// Read unmarshal message from conn -func (*SetEncodings) Read(c Conn) (ClientMessage, error) { - msg := SetEncodings{} - var pad [1]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - - if err := binary.Read(c, binary.BigEndian, &msg.EncNum); err != nil { - return nil, err - } - var enc EncodingType - for i := uint16(0); i < msg.EncNum; i++ { - if err := binary.Read(c, binary.BigEndian, &enc); err != nil { - return nil, err - } - msg.Encodings = append(msg.Encodings, enc) - } - c.SetEncodings(msg.Encodings) - return &msg, nil -} - -// Write marshal message to conn -func (msg *SetEncodings) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - - var pad [1]byte - if err := binary.Write(c, binary.BigEndian, pad); err != nil { - return err - } - - if uint16(len(msg.Encodings)) > msg.EncNum { - msg.EncNum = uint16(len(msg.Encodings)) - } - if err := binary.Write(c, binary.BigEndian, msg.EncNum); err != nil { - return err - } - for _, enc := range msg.Encodings { - if err := binary.Write(c, binary.BigEndian, enc); err != nil { - return err - } - } - return c.Flush() -} - -// FramebufferUpdateRequest holds the wire format message. -type FramebufferUpdateRequest struct { - Inc uint8 // incremental - X, Y uint16 // x-, y-position - Width, Height uint16 // width, height -} - -func (msg *FramebufferUpdateRequest) Supported(c Conn) bool { - return true -} - -// String returns string -func (msg *FramebufferUpdateRequest) String() string { - return fmt.Sprintf("incremental: %d, x: %d, y: %d, width: %d, height: %d", msg.Inc, msg.X, msg.Y, msg.Width, msg.Height) -} - -// Type returns MessageType -func (*FramebufferUpdateRequest) Type() ClientMessageType { - return FramebufferUpdateRequestMsgType -} - -// Read unmarshal message from conn -func (*FramebufferUpdateRequest) Read(c Conn) (ClientMessage, error) { - msg := FramebufferUpdateRequest{} - if err := binary.Read(c, binary.BigEndian, &msg); err != nil { - return nil, err - } - return &msg, nil -} - -// Write marshal message to conn -func (msg *FramebufferUpdateRequest) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, msg); err != nil { - return err - } - return c.Flush() -} - -// KeyEvent holds the wire format message. -type KeyEvent struct { - Down uint8 // down-flag - _ [2]byte // padding - Key Key // key -} - -func (msg *KeyEvent) Supported(c Conn) bool { - return true -} - -// String returns string -func (msg *KeyEvent) String() string { - return fmt.Sprintf("down: %d, key: %v", msg.Down, msg.Key) -} - -// Type returns MessageType -func (*KeyEvent) Type() ClientMessageType { - return KeyEventMsgType -} - -// Read unmarshal message from conn -func (*KeyEvent) Read(c Conn) (ClientMessage, error) { - msg := KeyEvent{} - if err := binary.Read(c, binary.BigEndian, &msg); err != nil { - return nil, err - } - return &msg, nil -} - -// Write marshal message to conn -func (msg *KeyEvent) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, msg); err != nil { - return err - } - return c.Flush() -} - -// PointerEvent message holds the wire format message -type PointerEvent struct { - Mask uint8 // button-mask - X, Y uint16 // x-, y-position -} - -func (msg *PointerEvent) Supported(c Conn) bool { - return true -} - -// String returns string -func (msg *PointerEvent) String() string { - return fmt.Sprintf("mask %d, x: %d, y: %d", msg.Mask, msg.X, msg.Y) -} - -// Type returns MessageType -func (*PointerEvent) Type() ClientMessageType { - return PointerEventMsgType -} - -// Read unmarshal message from conn -func (*PointerEvent) Read(c Conn) (ClientMessage, error) { - msg := PointerEvent{} - if err := binary.Read(c, binary.BigEndian, &msg); err != nil { - return nil, err - } - return &msg, nil -} - -// Write marshal message to conn -func (msg *PointerEvent) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, msg); err != nil { - return err - } - return c.Flush() -} - -// ClientCutText holds the wire format message, sans the text field. -type ClientCutText struct { - _ [3]byte // padding - Length uint32 // length - Text []byte -} - -func (msg *ClientCutText) Supported(c Conn) bool { - return true -} - -// String returns string -func (msg *ClientCutText) String() string { - return fmt.Sprintf("length: %d, text: %s", msg.Length, msg.Text) -} - -// Type returns MessageType -func (*ClientCutText) Type() ClientMessageType { - return ClientCutTextMsgType -} - -// Read unmarshal message from conn -func (*ClientCutText) Read(c Conn) (ClientMessage, error) { - msg := ClientCutText{} - var pad [3]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - - if err := binary.Read(c, binary.BigEndian, &msg.Length); err != nil { - return nil, err - } - - msg.Text = make([]byte, msg.Length) - if err := binary.Read(c, binary.BigEndian, &msg.Text); err != nil { - return nil, err - } - return &msg, nil -} - -// Write marshal message to conn -func (msg *ClientCutText) Write(c Conn) error { - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - - var pad [3]byte - if err := binary.Write(c, binary.BigEndian, &pad); err != nil { - return err - } - - if uint32(len(msg.Text)) > msg.Length { - msg.Length = uint32(len(msg.Text)) - } - - if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil { - return err - } - - return c.Flush() -} +package vnc2video + +import ( + "encoding/binary" + "fmt" + + "vnc2video/logger" +) + +var ( + // DefaultClientMessages slice of default client messages sent to server + DefaultClientMessages = []ClientMessage{ + &SetPixelFormat{}, + &SetEncodings{}, + &FramebufferUpdateRequest{}, + &KeyEvent{}, + &PointerEvent{}, + &ClientCutText{}, + } + + // DefaultServerMessages slice of default server messages sent to client + DefaultServerMessages = []ServerMessage{ + &FramebufferUpdate{}, + &SetColorMapEntries{}, + &Bell{}, + &ServerCutText{}, + } +) + +// ClientMessageType represents RFB message type +type ClientMessageType uint8 + +//go:generate stringer -type=ClientMessageType + +// Client-to-Server message types. +const ( + SetPixelFormatMsgType ClientMessageType = iota + _ + SetEncodingsMsgType + FramebufferUpdateRequestMsgType + KeyEventMsgType + PointerEventMsgType + ClientCutTextMsgType +) + +// ServerMessageType represents RFB message type +type ServerMessageType uint8 + +// Server-to-Client message types +const ( + FramebufferUpdateMsgType ServerMessageType = iota + SetColorMapEntriesMsgType + BellMsgType + ServerCutTextMsgType +) + +// ServerInit struct used in server init handshake +type ServerInit struct { + FBWidth, FBHeight uint16 + PixelFormat PixelFormat + NameLength uint32 + NameText []byte +} + +// String provide stringer +func (srvInit ServerInit) String() string { + return fmt.Sprintf("Width: %d, Height: %d, PixelFormat: %s, NameLength: %d, MameText: %s", srvInit.FBWidth, srvInit.FBHeight, srvInit.PixelFormat, srvInit.NameLength, srvInit.NameText) +} + +type ClientMessage interface { + String() string + Type() ClientMessageType + Read(Conn) (ClientMessage, error) + Write(Conn) error + Supported(Conn) bool +} + +type ServerMessage interface { + String() string + Type() ServerMessageType + Read(Conn) (ServerMessage, error) + Write(Conn) error + Supported(Conn) bool +} + +// FramebufferUpdate holds a FramebufferUpdate wire format message. +type FramebufferUpdate struct { + _ [1]byte // pad + NumRect uint16 // number-of-rectangles + Rects []*Rectangle // rectangles +} + +// String provide stringer +func (msg *FramebufferUpdate) String() string { + return fmt.Sprintf("rects %d rectangle[]: { %v }", msg.NumRect, msg.Rects) +} + +func (msg *FramebufferUpdate) Supported(c Conn) bool { + return true +} + +// Type return MessageType +func (*FramebufferUpdate) Type() ServerMessageType { + return FramebufferUpdateMsgType +} + +// Read unmarshal message from conn +func (*FramebufferUpdate) Read(c Conn) (ServerMessage, error) { + msg := FramebufferUpdate{} + var pad [1]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + + if err := binary.Read(c, binary.BigEndian, &msg.NumRect); err != nil { + return nil, err + } + for i := uint16(0); i < msg.NumRect; i++ { + rect := NewRectangle() + //logger.Debugf("----------RECT %d----------:", i) + + if err := rect.Read(c); err != nil { + return nil, err + } + if rect.EncType == EncDesktopSizePseudo { + c.(*ClientConn).ResetAllEncodings() + } + logger.Debugf("----End RECT #%d Info (%dx%d) encType:%s", i, rect.Width, rect.Height, rect.EncType) + msg.Rects = append(msg.Rects, rect) + } + return &msg, nil +} + +// Write marshals message to conn +func (msg *FramebufferUpdate) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + var pad [1]byte + if err := binary.Write(c, binary.BigEndian, pad); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, msg.NumRect); err != nil { + return err + } + for _, rect := range msg.Rects { + if err := rect.Write(c); err != nil { + return err + } + } + return c.Flush() +} + +// ServerCutText represents server message +type ServerCutText struct { + _ [1]byte + Length uint32 + Text []byte +} + +func (msg *ServerCutText) Supported(c Conn) bool { + return true +} + +// String returns string +func (msg *ServerCutText) String() string { + return fmt.Sprintf("lenght: %d text: %s", msg.Length, msg.Text) +} + +// Type returns MessageType +func (*ServerCutText) Type() ServerMessageType { + return ServerCutTextMsgType +} + +// Read unmarshal message from conn +func (*ServerCutText) Read(c Conn) (ServerMessage, error) { + msg := ServerCutText{} + + var pad [1]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + + if err := binary.Read(c, binary.BigEndian, &msg.Length); err != nil { + return nil, err + } + + msg.Text = make([]byte, msg.Length) + if err := binary.Read(c, binary.BigEndian, &msg.Text); err != nil { + return nil, err + } + return &msg, nil +} + +// Write marshal message to conn +func (msg *ServerCutText) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + var pad [1]byte + if err := binary.Write(c, binary.BigEndian, pad); err != nil { + return err + } + + if msg.Length < uint32(len(msg.Text)) { + msg.Length = uint32(len(msg.Text)) + } + if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil { + return err + } + return c.Flush() +} + +// Bell server message +type Bell struct{} + +func (*Bell) Supported(c Conn) bool { + return true +} + +// String return string +func (*Bell) String() string { + return fmt.Sprintf("bell") +} + +// Type returns MessageType +func (*Bell) Type() ServerMessageType { + return BellMsgType +} + +// Read unmarshal message from conn +func (*Bell) Read(c Conn) (ServerMessage, error) { + return &Bell{}, nil +} + +// Write marshal message to conn +func (msg *Bell) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + return c.Flush() +} + +// SetColorMapEntries server message +type SetColorMapEntries struct { + _ [1]byte + FirstColor uint16 + ColorsNum uint16 + Colors []Color +} + +func (msg *SetColorMapEntries) Supported(c Conn) bool { + return true +} + +// String returns string +func (msg *SetColorMapEntries) String() string { + return fmt.Sprintf("first color: %d, numcolors: %d, colors[]: { %v }", msg.FirstColor, msg.ColorsNum, msg.Colors) +} + +// Type returns MessageType +func (*SetColorMapEntries) Type() ServerMessageType { + return SetColorMapEntriesMsgType +} + +// Read unmrashal message from conn +func (*SetColorMapEntries) Read(c Conn) (ServerMessage, error) { + msg := SetColorMapEntries{} + var pad [1]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + + if err := binary.Read(c, binary.BigEndian, &msg.FirstColor); err != nil { + return nil, err + } + + if err := binary.Read(c, binary.BigEndian, &msg.ColorsNum); err != nil { + return nil, err + } + + msg.Colors = make([]Color, msg.ColorsNum) + colorMap := c.ColorMap() + + for i := uint16(0); i < msg.ColorsNum; i++ { + color := &msg.Colors[i] + if err := binary.Read(c, binary.BigEndian, &color); err != nil { + return nil, err + } + colorMap[msg.FirstColor+i] = *color + } + c.SetColorMap(colorMap) + return &msg, nil +} + +// Write marshal message to conn +func (msg *SetColorMapEntries) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + var pad [1]byte + if err := binary.Write(c, binary.BigEndian, &pad); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, msg.FirstColor); err != nil { + return err + } + + if msg.ColorsNum < uint16(len(msg.Colors)) { + msg.ColorsNum = uint16(len(msg.Colors)) + } + if err := binary.Write(c, binary.BigEndian, msg.ColorsNum); err != nil { + return err + } + + for i := 0; i < len(msg.Colors); i++ { + color := msg.Colors[i] + if err := binary.Write(c, binary.BigEndian, color); err != nil { + return err + } + } + + return c.Flush() +} + +// SetPixelFormat holds the wire format message. +type SetPixelFormat struct { + _ [3]byte // padding + PF PixelFormat // pixel-format +} + +func (msg *SetPixelFormat) Supported(c Conn) bool { + return true +} + +// String returns string +func (msg *SetPixelFormat) String() string { + return fmt.Sprintf("%s", msg.PF) +} + +// Type returns MessageType +func (*SetPixelFormat) Type() ClientMessageType { + return SetPixelFormatMsgType +} + +// Write marshal message to conn +func (msg *SetPixelFormat) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, msg); err != nil { + return err + } + + pf := c.PixelFormat() + // Invalidate the color map. + if pf.TrueColor != 0 { + c.SetColorMap(ColorMap{}) + } + + return c.Flush() +} + +// Read unmarshal message from conn +func (*SetPixelFormat) Read(c Conn) (ClientMessage, error) { + msg := SetPixelFormat{} + if err := binary.Read(c, binary.BigEndian, &msg); err != nil { + return nil, err + } + return &msg, nil +} + +// SetEncodings holds the wire format message, sans encoding-type field. +type SetEncodings struct { + _ [1]byte // padding + EncNum uint16 // number-of-encodings + Encodings []EncodingType +} + +func (msg *SetEncodings) Supported(c Conn) bool { + return true +} + +// String return string +func (msg *SetEncodings) String() string { + return fmt.Sprintf("encnum: %d, encodings[]: { %v }", msg.EncNum, msg.Encodings) +} + +// Type returns MessageType +func (*SetEncodings) Type() ClientMessageType { + return SetEncodingsMsgType +} + +// Read unmarshal message from conn +func (*SetEncodings) Read(c Conn) (ClientMessage, error) { + msg := SetEncodings{} + var pad [1]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + + if err := binary.Read(c, binary.BigEndian, &msg.EncNum); err != nil { + return nil, err + } + var enc EncodingType + for i := uint16(0); i < msg.EncNum; i++ { + if err := binary.Read(c, binary.BigEndian, &enc); err != nil { + return nil, err + } + msg.Encodings = append(msg.Encodings, enc) + } + c.SetEncodings(msg.Encodings) + return &msg, nil +} + +// Write marshal message to conn +func (msg *SetEncodings) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + + var pad [1]byte + if err := binary.Write(c, binary.BigEndian, pad); err != nil { + return err + } + + if uint16(len(msg.Encodings)) > msg.EncNum { + msg.EncNum = uint16(len(msg.Encodings)) + } + if err := binary.Write(c, binary.BigEndian, msg.EncNum); err != nil { + return err + } + for _, enc := range msg.Encodings { + if err := binary.Write(c, binary.BigEndian, enc); err != nil { + return err + } + } + return c.Flush() +} + +// FramebufferUpdateRequest holds the wire format message. +type FramebufferUpdateRequest struct { + Inc uint8 // incremental + X, Y uint16 // x-, y-position + Width, Height uint16 // width, height +} + +func (msg *FramebufferUpdateRequest) Supported(c Conn) bool { + return true +} + +// String returns string +func (msg *FramebufferUpdateRequest) String() string { + return fmt.Sprintf("incremental: %d, x: %d, y: %d, width: %d, height: %d", msg.Inc, msg.X, msg.Y, msg.Width, msg.Height) +} + +// Type returns MessageType +func (*FramebufferUpdateRequest) Type() ClientMessageType { + return FramebufferUpdateRequestMsgType +} + +// Read unmarshal message from conn +func (*FramebufferUpdateRequest) Read(c Conn) (ClientMessage, error) { + msg := FramebufferUpdateRequest{} + if err := binary.Read(c, binary.BigEndian, &msg); err != nil { + return nil, err + } + return &msg, nil +} + +// Write marshal message to conn +func (msg *FramebufferUpdateRequest) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, msg); err != nil { + return err + } + return c.Flush() +} + +// KeyEvent holds the wire format message. +type KeyEvent struct { + Down uint8 // down-flag + _ [2]byte // padding + Key Key // key +} + +func (msg *KeyEvent) Supported(c Conn) bool { + return true +} + +// String returns string +func (msg *KeyEvent) String() string { + return fmt.Sprintf("down: %d, key: %v", msg.Down, msg.Key) +} + +// Type returns MessageType +func (*KeyEvent) Type() ClientMessageType { + return KeyEventMsgType +} + +// Read unmarshal message from conn +func (*KeyEvent) Read(c Conn) (ClientMessage, error) { + msg := KeyEvent{} + if err := binary.Read(c, binary.BigEndian, &msg); err != nil { + return nil, err + } + return &msg, nil +} + +// Write marshal message to conn +func (msg *KeyEvent) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, msg); err != nil { + return err + } + return c.Flush() +} + +// PointerEvent message holds the wire format message +type PointerEvent struct { + Mask uint8 // button-mask + X, Y uint16 // x-, y-position +} + +func (msg *PointerEvent) Supported(c Conn) bool { + return true +} + +// String returns string +func (msg *PointerEvent) String() string { + return fmt.Sprintf("mask %d, x: %d, y: %d", msg.Mask, msg.X, msg.Y) +} + +// Type returns MessageType +func (*PointerEvent) Type() ClientMessageType { + return PointerEventMsgType +} + +// Read unmarshal message from conn +func (*PointerEvent) Read(c Conn) (ClientMessage, error) { + msg := PointerEvent{} + if err := binary.Read(c, binary.BigEndian, &msg); err != nil { + return nil, err + } + return &msg, nil +} + +// Write marshal message to conn +func (msg *PointerEvent) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, msg); err != nil { + return err + } + return c.Flush() +} + +// ClientCutText holds the wire format message, sans the text field. +type ClientCutText struct { + _ [3]byte // padding + Length uint32 // length + Text []byte +} + +func (msg *ClientCutText) Supported(c Conn) bool { + return true +} + +// String returns string +func (msg *ClientCutText) String() string { + return fmt.Sprintf("length: %d, text: %s", msg.Length, msg.Text) +} + +// Type returns MessageType +func (*ClientCutText) Type() ClientMessageType { + return ClientCutTextMsgType +} + +// Read unmarshal message from conn +func (*ClientCutText) Read(c Conn) (ClientMessage, error) { + msg := ClientCutText{} + var pad [3]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + + if err := binary.Read(c, binary.BigEndian, &msg.Length); err != nil { + return nil, err + } + + msg.Text = make([]byte, msg.Length) + if err := binary.Read(c, binary.BigEndian, &msg.Text); err != nil { + return nil, err + } + return &msg, nil +} + +// Write marshal message to conn +func (msg *ClientCutText) Write(c Conn) error { + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + + var pad [3]byte + if err := binary.Write(c, binary.BigEndian, &pad); err != nil { + return err + } + + if uint32(len(msg.Text)) > msg.Length { + msg.Length = uint32(len(msg.Text)) + } + + if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil { + return err + } + + return c.Flush() +} diff --git a/messages_aten.go b/messages_aten.go index e05535a..e078652 100644 --- a/messages_aten.go +++ b/messages_aten.go @@ -1,370 +1,370 @@ -package vnc2webm - -import ( - "encoding/binary" - "fmt" -) - -// Aten IKVM server message types -const ( - AteniKVMFrontGroundEventMsgType ServerMessageType = 4 - AteniKVMKeepAliveEventMsgType ServerMessageType = 22 - AteniKVMVideoGetInfoMsgType ServerMessageType = 51 - AteniKVMMouseGetInfoMsgType ServerMessageType = 55 - AteniKVMSessionMessageMsgType ServerMessageType = 57 - AteniKVMGetViewerLangMsgType ServerMessageType = 60 -) - -// Aten IKVM client message types -const ( - AteniKVMKeyEventMsgType ClientMessageType = 4 - AteniKVMPointerEventMsgType ClientMessageType = 5 -) - -// AteniKVMKeyEvent holds the wire format message -type AteniKVMKeyEvent struct { - _ [1]byte // padding - Down uint8 // down-flag - _ [2]byte // padding - Key Key // key - _ [9]byte // padding -} - -// AteniKVMPointerEvent holds the wire format message -type AteniKVMPointerEvent struct { - _ [1]byte // padding - Mask uint8 // mask - X uint16 // x - Y uint16 // y - _ [11]byte // padding -} - -func (msg *AteniKVMPointerEvent) Supported(c Conn) bool { - return false -} - -func (msg *AteniKVMPointerEvent) String() string { - return fmt.Sprintf("mask: %d, x:%d, y:%d", msg.Mask, msg.X, msg.Y) -} - -func (msg *AteniKVMPointerEvent) Type() ClientMessageType { - return AteniKVMPointerEventMsgType -} - -func (*AteniKVMPointerEvent) Read(c Conn) (ClientMessage, error) { - msg := AteniKVMPointerEvent{} - if err := binary.Read(c, binary.BigEndian, &msg); err != nil { - return nil, err - } - return &msg, nil -} - -func (msg *AteniKVMPointerEvent) Write(c Conn) error { - if !msg.Supported(c) { - return nil - } - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, msg); err != nil { - return err - } - return c.Flush() -} - -func (msg *AteniKVMKeyEvent) Supported(c Conn) bool { - return false -} - -func (msg *AteniKVMKeyEvent) String() string { - return fmt.Sprintf("down:%d, key:%s", msg.Down, msg.Key) -} - -func (msg *AteniKVMKeyEvent) Type() ClientMessageType { - return AteniKVMKeyEventMsgType -} - -func (*AteniKVMKeyEvent) Read(c Conn) (ClientMessage, error) { - msg := AteniKVMKeyEvent{} - if err := binary.Read(c, binary.BigEndian, &msg); err != nil { - return nil, err - } - return &msg, nil -} - -func (msg *AteniKVMKeyEvent) Write(c Conn) error { - if !msg.Supported(c) { - return nil - } - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, msg); err != nil { - return err - } - return c.Flush() -} - -// AteniKVMFrontGroundEvent unknown aten ikvm message -type AteniKVMFrontGroundEvent struct { - _ [20]byte -} - -func (msg *AteniKVMFrontGroundEvent) Supported(c Conn) bool { - return false -} - -// String return string representation -func (msg *AteniKVMFrontGroundEvent) String() string { - return fmt.Sprintf("%s", msg.Type()) -} - -// Type return ServerMessageType -func (*AteniKVMFrontGroundEvent) Type() ServerMessageType { - return AteniKVMFrontGroundEventMsgType -} - -// Read unmarshal message from conn -func (*AteniKVMFrontGroundEvent) Read(c Conn) (ServerMessage, error) { - msg := &AteniKVMFrontGroundEvent{} - var pad [20]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - return msg, nil -} - -// Write marshal message to conn -func (msg *AteniKVMFrontGroundEvent) Write(c Conn) error { - if !msg.Supported(c) { - return nil - } - var pad [20]byte - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, pad); err != nil { - return err - } - return c.Flush() -} - -// AteniKVMKeepAliveEvent unknown aten ikvm message -type AteniKVMKeepAliveEvent struct { - _ [1]byte -} - -func (msg *AteniKVMKeepAliveEvent) Supported(c Conn) bool { - return false -} - -// String return string representation -func (msg *AteniKVMKeepAliveEvent) String() string { - return fmt.Sprintf("%s", msg.Type()) -} - -// Type return ServerMessageType -func (*AteniKVMKeepAliveEvent) Type() ServerMessageType { - return AteniKVMKeepAliveEventMsgType -} - -// Read unmarshal message from conn -func (*AteniKVMKeepAliveEvent) Read(c Conn) (ServerMessage, error) { - msg := &AteniKVMKeepAliveEvent{} - var pad [1]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - return msg, nil -} - -// Write marshal message to conn -func (msg *AteniKVMKeepAliveEvent) Write(c Conn) error { - if !msg.Supported(c) { - return nil - } - var pad [1]byte - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, pad); err != nil { - return err - } - return c.Flush() -} - -// AteniKVMVideoGetInfo unknown aten ikvm message -type AteniKVMVideoGetInfo struct { - _ [20]byte -} - -func (msg *AteniKVMVideoGetInfo) Supported(c Conn) bool { - return false -} - -// String return string representation -func (msg *AteniKVMVideoGetInfo) String() string { - return fmt.Sprintf("%s", msg.Type()) -} - -// Type return ServerMessageType -func (*AteniKVMVideoGetInfo) Type() ServerMessageType { - return AteniKVMVideoGetInfoMsgType -} - -// Read unmarshal message from conn -func (*AteniKVMVideoGetInfo) Read(c Conn) (ServerMessage, error) { - msg := &AteniKVMVideoGetInfo{} - var pad [40]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - return msg, nil -} - -// Write marshal message to conn -func (msg *AteniKVMVideoGetInfo) Write(c Conn) error { - if !msg.Supported(c) { - return nil - } - var pad [4]byte - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, pad); err != nil { - return err - } - return c.Flush() -} - -// AteniKVMMouseGetInfo unknown aten ikvm message -type AteniKVMMouseGetInfo struct { - _ [2]byte -} - -func (msg *AteniKVMMouseGetInfo) Supported(c Conn) bool { - return false -} - -// String return string representation -func (msg *AteniKVMMouseGetInfo) String() string { - return fmt.Sprintf("%s", msg.Type()) -} - -// Type return ServerMessageType -func (*AteniKVMMouseGetInfo) Type() ServerMessageType { - return AteniKVMMouseGetInfoMsgType -} - -// Read unmarshal message from conn -func (*AteniKVMMouseGetInfo) Read(c Conn) (ServerMessage, error) { - msg := &AteniKVMFrontGroundEvent{} - var pad [2]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - return msg, nil -} - -// Write marshal message to conn -func (msg *AteniKVMMouseGetInfo) Write(c Conn) error { - if !msg.Supported(c) { - return nil - } - var pad [2]byte - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, pad); err != nil { - return err - } - return c.Flush() -} - -// AteniKVMSessionMessage unknown aten ikvm message -type AteniKVMSessionMessage struct { - _ [264]byte -} - -func (msg *AteniKVMSessionMessage) Supported(c Conn) bool { - return false -} - -// String return string representation -func (msg *AteniKVMSessionMessage) String() string { - return fmt.Sprintf("%s", msg.Type()) -} - -// Type return ServerMessageType -func (*AteniKVMSessionMessage) Type() ServerMessageType { - return AteniKVMSessionMessageMsgType -} - -// Read unmarshal message from conn -func (*AteniKVMSessionMessage) Read(c Conn) (ServerMessage, error) { - msg := &AteniKVMSessionMessage{} - var pad [264]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - return msg, nil -} - -// Write marshal message to conn -func (msg *AteniKVMSessionMessage) Write(c Conn) error { - if !msg.Supported(c) { - return nil - } - var pad [264]byte - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, pad); err != nil { - return err - } - return nil -} - -// AteniKVMGetViewerLang unknown aten ikvm message -type AteniKVMGetViewerLang struct { - _ [8]byte -} - -func (msg *AteniKVMGetViewerLang) Supported(c Conn) bool { - return false -} - -// String return string representation -func (msg *AteniKVMGetViewerLang) String() string { - return fmt.Sprintf("%s", msg.Type()) -} - -// Type return ServerMessageType -func (*AteniKVMGetViewerLang) Type() ServerMessageType { - return AteniKVMGetViewerLangMsgType -} - -// Read unmarshal message from conn -func (*AteniKVMGetViewerLang) Read(c Conn) (ServerMessage, error) { - msg := &AteniKVMGetViewerLang{} - var pad [8]byte - if err := binary.Read(c, binary.BigEndian, &pad); err != nil { - return nil, err - } - return msg, nil -} - -// Write marshal message to conn -func (msg *AteniKVMGetViewerLang) Write(c Conn) error { - if !msg.Supported(c) { - return nil - } - var pad [8]byte - if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, pad); err != nil { - return err - } - return c.Flush() -} +package vnc2video + +import ( + "encoding/binary" + "fmt" +) + +// Aten IKVM server message types +const ( + AteniKVMFrontGroundEventMsgType ServerMessageType = 4 + AteniKVMKeepAliveEventMsgType ServerMessageType = 22 + AteniKVMVideoGetInfoMsgType ServerMessageType = 51 + AteniKVMMouseGetInfoMsgType ServerMessageType = 55 + AteniKVMSessionMessageMsgType ServerMessageType = 57 + AteniKVMGetViewerLangMsgType ServerMessageType = 60 +) + +// Aten IKVM client message types +const ( + AteniKVMKeyEventMsgType ClientMessageType = 4 + AteniKVMPointerEventMsgType ClientMessageType = 5 +) + +// AteniKVMKeyEvent holds the wire format message +type AteniKVMKeyEvent struct { + _ [1]byte // padding + Down uint8 // down-flag + _ [2]byte // padding + Key Key // key + _ [9]byte // padding +} + +// AteniKVMPointerEvent holds the wire format message +type AteniKVMPointerEvent struct { + _ [1]byte // padding + Mask uint8 // mask + X uint16 // x + Y uint16 // y + _ [11]byte // padding +} + +func (msg *AteniKVMPointerEvent) Supported(c Conn) bool { + return false +} + +func (msg *AteniKVMPointerEvent) String() string { + return fmt.Sprintf("mask: %d, x:%d, y:%d", msg.Mask, msg.X, msg.Y) +} + +func (msg *AteniKVMPointerEvent) Type() ClientMessageType { + return AteniKVMPointerEventMsgType +} + +func (*AteniKVMPointerEvent) Read(c Conn) (ClientMessage, error) { + msg := AteniKVMPointerEvent{} + if err := binary.Read(c, binary.BigEndian, &msg); err != nil { + return nil, err + } + return &msg, nil +} + +func (msg *AteniKVMPointerEvent) Write(c Conn) error { + if !msg.Supported(c) { + return nil + } + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, msg); err != nil { + return err + } + return c.Flush() +} + +func (msg *AteniKVMKeyEvent) Supported(c Conn) bool { + return false +} + +func (msg *AteniKVMKeyEvent) String() string { + return fmt.Sprintf("down:%d, key:%s", msg.Down, msg.Key) +} + +func (msg *AteniKVMKeyEvent) Type() ClientMessageType { + return AteniKVMKeyEventMsgType +} + +func (*AteniKVMKeyEvent) Read(c Conn) (ClientMessage, error) { + msg := AteniKVMKeyEvent{} + if err := binary.Read(c, binary.BigEndian, &msg); err != nil { + return nil, err + } + return &msg, nil +} + +func (msg *AteniKVMKeyEvent) Write(c Conn) error { + if !msg.Supported(c) { + return nil + } + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, msg); err != nil { + return err + } + return c.Flush() +} + +// AteniKVMFrontGroundEvent unknown aten ikvm message +type AteniKVMFrontGroundEvent struct { + _ [20]byte +} + +func (msg *AteniKVMFrontGroundEvent) Supported(c Conn) bool { + return false +} + +// String return string representation +func (msg *AteniKVMFrontGroundEvent) String() string { + return fmt.Sprintf("%s", msg.Type()) +} + +// Type return ServerMessageType +func (*AteniKVMFrontGroundEvent) Type() ServerMessageType { + return AteniKVMFrontGroundEventMsgType +} + +// Read unmarshal message from conn +func (*AteniKVMFrontGroundEvent) Read(c Conn) (ServerMessage, error) { + msg := &AteniKVMFrontGroundEvent{} + var pad [20]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + return msg, nil +} + +// Write marshal message to conn +func (msg *AteniKVMFrontGroundEvent) Write(c Conn) error { + if !msg.Supported(c) { + return nil + } + var pad [20]byte + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, pad); err != nil { + return err + } + return c.Flush() +} + +// AteniKVMKeepAliveEvent unknown aten ikvm message +type AteniKVMKeepAliveEvent struct { + _ [1]byte +} + +func (msg *AteniKVMKeepAliveEvent) Supported(c Conn) bool { + return false +} + +// String return string representation +func (msg *AteniKVMKeepAliveEvent) String() string { + return fmt.Sprintf("%s", msg.Type()) +} + +// Type return ServerMessageType +func (*AteniKVMKeepAliveEvent) Type() ServerMessageType { + return AteniKVMKeepAliveEventMsgType +} + +// Read unmarshal message from conn +func (*AteniKVMKeepAliveEvent) Read(c Conn) (ServerMessage, error) { + msg := &AteniKVMKeepAliveEvent{} + var pad [1]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + return msg, nil +} + +// Write marshal message to conn +func (msg *AteniKVMKeepAliveEvent) Write(c Conn) error { + if !msg.Supported(c) { + return nil + } + var pad [1]byte + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, pad); err != nil { + return err + } + return c.Flush() +} + +// AteniKVMVideoGetInfo unknown aten ikvm message +type AteniKVMVideoGetInfo struct { + _ [20]byte +} + +func (msg *AteniKVMVideoGetInfo) Supported(c Conn) bool { + return false +} + +// String return string representation +func (msg *AteniKVMVideoGetInfo) String() string { + return fmt.Sprintf("%s", msg.Type()) +} + +// Type return ServerMessageType +func (*AteniKVMVideoGetInfo) Type() ServerMessageType { + return AteniKVMVideoGetInfoMsgType +} + +// Read unmarshal message from conn +func (*AteniKVMVideoGetInfo) Read(c Conn) (ServerMessage, error) { + msg := &AteniKVMVideoGetInfo{} + var pad [40]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + return msg, nil +} + +// Write marshal message to conn +func (msg *AteniKVMVideoGetInfo) Write(c Conn) error { + if !msg.Supported(c) { + return nil + } + var pad [4]byte + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, pad); err != nil { + return err + } + return c.Flush() +} + +// AteniKVMMouseGetInfo unknown aten ikvm message +type AteniKVMMouseGetInfo struct { + _ [2]byte +} + +func (msg *AteniKVMMouseGetInfo) Supported(c Conn) bool { + return false +} + +// String return string representation +func (msg *AteniKVMMouseGetInfo) String() string { + return fmt.Sprintf("%s", msg.Type()) +} + +// Type return ServerMessageType +func (*AteniKVMMouseGetInfo) Type() ServerMessageType { + return AteniKVMMouseGetInfoMsgType +} + +// Read unmarshal message from conn +func (*AteniKVMMouseGetInfo) Read(c Conn) (ServerMessage, error) { + msg := &AteniKVMFrontGroundEvent{} + var pad [2]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + return msg, nil +} + +// Write marshal message to conn +func (msg *AteniKVMMouseGetInfo) Write(c Conn) error { + if !msg.Supported(c) { + return nil + } + var pad [2]byte + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, pad); err != nil { + return err + } + return c.Flush() +} + +// AteniKVMSessionMessage unknown aten ikvm message +type AteniKVMSessionMessage struct { + _ [264]byte +} + +func (msg *AteniKVMSessionMessage) Supported(c Conn) bool { + return false +} + +// String return string representation +func (msg *AteniKVMSessionMessage) String() string { + return fmt.Sprintf("%s", msg.Type()) +} + +// Type return ServerMessageType +func (*AteniKVMSessionMessage) Type() ServerMessageType { + return AteniKVMSessionMessageMsgType +} + +// Read unmarshal message from conn +func (*AteniKVMSessionMessage) Read(c Conn) (ServerMessage, error) { + msg := &AteniKVMSessionMessage{} + var pad [264]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + return msg, nil +} + +// Write marshal message to conn +func (msg *AteniKVMSessionMessage) Write(c Conn) error { + if !msg.Supported(c) { + return nil + } + var pad [264]byte + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, pad); err != nil { + return err + } + return nil +} + +// AteniKVMGetViewerLang unknown aten ikvm message +type AteniKVMGetViewerLang struct { + _ [8]byte +} + +func (msg *AteniKVMGetViewerLang) Supported(c Conn) bool { + return false +} + +// String return string representation +func (msg *AteniKVMGetViewerLang) String() string { + return fmt.Sprintf("%s", msg.Type()) +} + +// Type return ServerMessageType +func (*AteniKVMGetViewerLang) Type() ServerMessageType { + return AteniKVMGetViewerLangMsgType +} + +// Read unmarshal message from conn +func (*AteniKVMGetViewerLang) Read(c Conn) (ServerMessage, error) { + msg := &AteniKVMGetViewerLang{} + var pad [8]byte + if err := binary.Read(c, binary.BigEndian, &pad); err != nil { + return nil, err + } + return msg, nil +} + +// Write marshal message to conn +func (msg *AteniKVMGetViewerLang) Write(c Conn) error { + if !msg.Supported(c) { + return nil + } + var pad [8]byte + if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, pad); err != nil { + return err + } + return c.Flush() +} diff --git a/pixel_format.go b/pixel_format.go index 81ea23f..d12bf3f 100644 --- a/pixel_format.go +++ b/pixel_format.go @@ -1,136 +1,136 @@ -// Implementation of RFC 6143 §7.4 Pixel Format Data Structure. - -package vnc2webm - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" -) - -var ( - // PixelFormat8bit returns 8 bit pixel format - PixelFormat8bit = NewPixelFormat(8) - // PixelFormat16bit returns 16 bit pixel format - PixelFormat16bit = NewPixelFormat(16) - // PixelFormat32bit returns 32 bit pixel format - PixelFormat32bit = NewPixelFormat(32) - // PixelFormatAten returns pixel format used in Aten IKVM - PixelFormatAten = NewPixelFormatAten() -) - -// PixelFormat describes the way a pixel is formatted for a VNC connection -type PixelFormat struct { - BPP uint8 // bits-per-pixel - Depth uint8 // depth - BigEndian uint8 // big-endian-flag - TrueColor uint8 // true-color-flag - RedMax, GreenMax, BlueMax uint16 // red-, green-, blue-max (2^BPP-1) - RedShift, GreenShift, BlueShift uint8 // red-, green-, blue-shift - _ [3]byte // padding -} - -const pixelFormatLen = 16 - -// NewPixelFormat returns a populated PixelFormat structure -func NewPixelFormat(bpp uint8) PixelFormat { - bigEndian := uint8(0) - // rgbMax := uint16(math.Exp2(float64(bpp))) - 1 - rMax := uint16(255) - gMax := uint16(255) - bMax := uint16(255) - var ( - tc = uint8(1) - rs, gs, bs uint8 - depth uint8 - ) - switch bpp { - case 8: - tc = 0 - depth = 8 - rs, gs, bs = 0, 0, 0 - case 16: - depth = 16 - rs, gs, bs = 0, 4, 8 - case 32: - depth = 24 - // rs, gs, bs = 0, 8, 16 - rs, gs, bs = 16, 8, 0 - } - return PixelFormat{bpp, depth, bigEndian, tc, rMax, gMax, bMax, rs, gs, bs, [3]byte{}} -} - -// NewPixelFormatAten returns Aten IKVM pixel format -func NewPixelFormatAten() PixelFormat { - return PixelFormat{16, 15, 0, 1, (1 << 5) - 1, (1 << 5) - 1, (1 << 5) - 1, 10, 5, 0, [3]byte{}} -} - -// Marshal implements the Marshaler interface -func (pf PixelFormat) Marshal() ([]byte, error) { - // Validation checks. - switch pf.BPP { - case 8, 16, 32: - default: - return nil, fmt.Errorf("Invalid BPP value %v; must be 8, 16, or 32", pf.BPP) - } - - if pf.Depth < pf.BPP { - return nil, fmt.Errorf("Invalid Depth value %v; cannot be < BPP", pf.Depth) - } - switch pf.Depth { - case 8, 16, 32: - default: - return nil, fmt.Errorf("Invalid Depth value %v; must be 8, 16, or 32", pf.Depth) - } - - // Create the slice of bytes - buf := bPool.Get().(*bytes.Buffer) - buf.Reset() - defer bPool.Put(buf) - - if err := binary.Write(buf, binary.BigEndian, &pf); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -// Read reads from an io.Reader, and populates the PixelFormat -func (pf PixelFormat) Read(r io.Reader) error { - buf := make([]byte, pixelFormatLen) - if _, err := io.ReadAtLeast(r, buf, pixelFormatLen); err != nil { - return err - } - return pf.Unmarshal(buf) -} - -// Unmarshal implements the Unmarshaler interface -func (pf PixelFormat) Unmarshal(data []byte) error { - buf := bPool.Get().(*bytes.Buffer) - buf.Reset() - defer bPool.Put(buf) - - if _, err := buf.Write(data); err != nil { - return err - } - - if err := binary.Read(buf, binary.BigEndian, &pf); err != nil { - return err - } - - return nil -} - -// String implements the fmt.Stringer interface -func (pf PixelFormat) String() string { - return fmt.Sprintf("{ bpp: %d depth: %d big-endian: %d true-color: %d red-max: %d green-max: %d blue-max: %d red-shift: %d green-shift: %d blue-shift: %d }", - pf.BPP, pf.Depth, pf.BigEndian, pf.TrueColor, pf.RedMax, pf.GreenMax, pf.BlueMax, pf.RedShift, pf.GreenShift, pf.BlueShift) -} - -func (pf PixelFormat) order() binary.ByteOrder { - if pf.BigEndian == 1 { - return binary.BigEndian - } - return binary.LittleEndian -} +// Implementation of RFC 6143 §7.4 Pixel Format Data Structure. + +package vnc2video + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" +) + +var ( + // PixelFormat8bit returns 8 bit pixel format + PixelFormat8bit = NewPixelFormat(8) + // PixelFormat16bit returns 16 bit pixel format + PixelFormat16bit = NewPixelFormat(16) + // PixelFormat32bit returns 32 bit pixel format + PixelFormat32bit = NewPixelFormat(32) + // PixelFormatAten returns pixel format used in Aten IKVM + PixelFormatAten = NewPixelFormatAten() +) + +// PixelFormat describes the way a pixel is formatted for a VNC connection +type PixelFormat struct { + BPP uint8 // bits-per-pixel + Depth uint8 // depth + BigEndian uint8 // big-endian-flag + TrueColor uint8 // true-color-flag + RedMax, GreenMax, BlueMax uint16 // red-, green-, blue-max (2^BPP-1) + RedShift, GreenShift, BlueShift uint8 // red-, green-, blue-shift + _ [3]byte // padding +} + +const pixelFormatLen = 16 + +// NewPixelFormat returns a populated PixelFormat structure +func NewPixelFormat(bpp uint8) PixelFormat { + bigEndian := uint8(0) + // rgbMax := uint16(math.Exp2(float64(bpp))) - 1 + rMax := uint16(255) + gMax := uint16(255) + bMax := uint16(255) + var ( + tc = uint8(1) + rs, gs, bs uint8 + depth uint8 + ) + switch bpp { + case 8: + tc = 0 + depth = 8 + rs, gs, bs = 0, 0, 0 + case 16: + depth = 16 + rs, gs, bs = 0, 4, 8 + case 32: + depth = 24 + // rs, gs, bs = 0, 8, 16 + rs, gs, bs = 16, 8, 0 + } + return PixelFormat{bpp, depth, bigEndian, tc, rMax, gMax, bMax, rs, gs, bs, [3]byte{}} +} + +// NewPixelFormatAten returns Aten IKVM pixel format +func NewPixelFormatAten() PixelFormat { + return PixelFormat{16, 15, 0, 1, (1 << 5) - 1, (1 << 5) - 1, (1 << 5) - 1, 10, 5, 0, [3]byte{}} +} + +// Marshal implements the Marshaler interface +func (pf PixelFormat) Marshal() ([]byte, error) { + // Validation checks. + switch pf.BPP { + case 8, 16, 32: + default: + return nil, fmt.Errorf("Invalid BPP value %v; must be 8, 16, or 32", pf.BPP) + } + + if pf.Depth < pf.BPP { + return nil, fmt.Errorf("Invalid Depth value %v; cannot be < BPP", pf.Depth) + } + switch pf.Depth { + case 8, 16, 32: + default: + return nil, fmt.Errorf("Invalid Depth value %v; must be 8, 16, or 32", pf.Depth) + } + + // Create the slice of bytes + buf := bPool.Get().(*bytes.Buffer) + buf.Reset() + defer bPool.Put(buf) + + if err := binary.Write(buf, binary.BigEndian, &pf); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// Read reads from an io.Reader, and populates the PixelFormat +func (pf PixelFormat) Read(r io.Reader) error { + buf := make([]byte, pixelFormatLen) + if _, err := io.ReadAtLeast(r, buf, pixelFormatLen); err != nil { + return err + } + return pf.Unmarshal(buf) +} + +// Unmarshal implements the Unmarshaler interface +func (pf PixelFormat) Unmarshal(data []byte) error { + buf := bPool.Get().(*bytes.Buffer) + buf.Reset() + defer bPool.Put(buf) + + if _, err := buf.Write(data); err != nil { + return err + } + + if err := binary.Read(buf, binary.BigEndian, &pf); err != nil { + return err + } + + return nil +} + +// String implements the fmt.Stringer interface +func (pf PixelFormat) String() string { + return fmt.Sprintf("{ bpp: %d depth: %d big-endian: %d true-color: %d red-max: %d green-max: %d blue-max: %d red-shift: %d green-shift: %d blue-shift: %d }", + pf.BPP, pf.Depth, pf.BigEndian, pf.TrueColor, pf.RedMax, pf.GreenMax, pf.BlueMax, pf.RedShift, pf.GreenShift, pf.BlueShift) +} + +func (pf PixelFormat) order() binary.ByteOrder { + if pf.BigEndian == 1 { + return binary.BigEndian + } + return binary.LittleEndian +} diff --git a/security.go b/security.go index 35904bc..66a89ad 100644 --- a/security.go +++ b/security.go @@ -1,50 +1,50 @@ -package vnc2webm - -type SecurityType uint8 - -//go:generate stringer -type=SecurityType - -const ( - SecTypeUnknown SecurityType = SecurityType(0) - SecTypeNone SecurityType = SecurityType(1) - SecTypeVNC SecurityType = SecurityType(2) - SecTypeTight SecurityType = SecurityType(16) - SecTypeATEN SecurityType = SecurityType(16) - SecTypeVeNCrypt SecurityType = SecurityType(19) -) - -type SecuritySubType uint32 - -//go:generate stringer -type=SecuritySubType - -const ( - SecSubTypeUnknown SecuritySubType = SecuritySubType(0) -) - -const ( - SecSubTypeVeNCrypt01Unknown SecuritySubType = SecuritySubType(0) - SecSubTypeVeNCrypt01Plain SecuritySubType = SecuritySubType(19) - SecSubTypeVeNCrypt01TLSNone SecuritySubType = SecuritySubType(20) - SecSubTypeVeNCrypt01TLSVNC SecuritySubType = SecuritySubType(21) - SecSubTypeVeNCrypt01TLSPlain SecuritySubType = SecuritySubType(22) - SecSubTypeVeNCrypt01X509None SecuritySubType = SecuritySubType(23) - SecSubTypeVeNCrypt01X509VNC SecuritySubType = SecuritySubType(24) - SecSubTypeVeNCrypt01X509Plain SecuritySubType = SecuritySubType(25) -) - -const ( - SecSubTypeVeNCrypt02Unknown SecuritySubType = SecuritySubType(0) - SecSubTypeVeNCrypt02Plain SecuritySubType = SecuritySubType(256) - SecSubTypeVeNCrypt02TLSNone SecuritySubType = SecuritySubType(257) - SecSubTypeVeNCrypt02TLSVNC SecuritySubType = SecuritySubType(258) - SecSubTypeVeNCrypt02TLSPlain SecuritySubType = SecuritySubType(259) - SecSubTypeVeNCrypt02X509None SecuritySubType = SecuritySubType(260) - SecSubTypeVeNCrypt02X509VNC SecuritySubType = SecuritySubType(261) - SecSubTypeVeNCrypt02X509Plain SecuritySubType = SecuritySubType(262) -) - -type SecurityHandler interface { - Type() SecurityType - SubType() SecuritySubType - Auth(Conn) error -} +package vnc2video + +type SecurityType uint8 + +//go:generate stringer -type=SecurityType + +const ( + SecTypeUnknown SecurityType = SecurityType(0) + SecTypeNone SecurityType = SecurityType(1) + SecTypeVNC SecurityType = SecurityType(2) + SecTypeTight SecurityType = SecurityType(16) + SecTypeATEN SecurityType = SecurityType(16) + SecTypeVeNCrypt SecurityType = SecurityType(19) +) + +type SecuritySubType uint32 + +//go:generate stringer -type=SecuritySubType + +const ( + SecSubTypeUnknown SecuritySubType = SecuritySubType(0) +) + +const ( + SecSubTypeVeNCrypt01Unknown SecuritySubType = SecuritySubType(0) + SecSubTypeVeNCrypt01Plain SecuritySubType = SecuritySubType(19) + SecSubTypeVeNCrypt01TLSNone SecuritySubType = SecuritySubType(20) + SecSubTypeVeNCrypt01TLSVNC SecuritySubType = SecuritySubType(21) + SecSubTypeVeNCrypt01TLSPlain SecuritySubType = SecuritySubType(22) + SecSubTypeVeNCrypt01X509None SecuritySubType = SecuritySubType(23) + SecSubTypeVeNCrypt01X509VNC SecuritySubType = SecuritySubType(24) + SecSubTypeVeNCrypt01X509Plain SecuritySubType = SecuritySubType(25) +) + +const ( + SecSubTypeVeNCrypt02Unknown SecuritySubType = SecuritySubType(0) + SecSubTypeVeNCrypt02Plain SecuritySubType = SecuritySubType(256) + SecSubTypeVeNCrypt02TLSNone SecuritySubType = SecuritySubType(257) + SecSubTypeVeNCrypt02TLSVNC SecuritySubType = SecuritySubType(258) + SecSubTypeVeNCrypt02TLSPlain SecuritySubType = SecuritySubType(259) + SecSubTypeVeNCrypt02X509None SecuritySubType = SecuritySubType(260) + SecSubTypeVeNCrypt02X509VNC SecuritySubType = SecuritySubType(261) + SecSubTypeVeNCrypt02X509Plain SecuritySubType = SecuritySubType(262) +) + +type SecurityHandler interface { + Type() SecurityType + SubType() SecuritySubType + Auth(Conn) error +} diff --git a/security_aten.go b/security_aten.go index b894505..e8eeec6 100644 --- a/security_aten.go +++ b/security_aten.go @@ -1,107 +1,107 @@ -package vnc2webm - -import ( - "bytes" - "encoding/binary" - "fmt" -) - -type ClientAuthATEN struct { - Username []byte - Password []byte -} - -func (*ClientAuthATEN) Type() SecurityType { - return SecTypeATEN -} - -func (*ClientAuthATEN) SubType() SecuritySubType { - return SecSubTypeUnknown -} - -func charCodeAt(s string, n int) rune { - for i, r := range s { - if i == n { - return r - } - } - return 0 -} - -func (auth *ClientAuthATEN) Auth(c Conn) error { - var definedAuthLen = 24 - - if len(auth.Username) > definedAuthLen || len(auth.Password) > definedAuthLen { - return fmt.Errorf("username/password is too long, allowed 0-23") - } - - nt, err := readTightTunnels(c) - if err != nil { - return err - } - /* - fmt.Printf("tunnels %d\n", nt) - for i := uint32(0); i < nt; i++ { - code, vendor, signature, err := readTightCaps(c) - if err != nil { - return err - } - fmt.Printf("code %d vendor %s signature %s\n", code, vendor, signature) - } - */ - if ((nt&0xffff0ff0)>>0 == 0xaff90fb0) || (nt <= 0 || nt > 0x1000000) { - c.SetProtoVersion("aten1") - var skip [20]byte - binary.Read(c, binary.BigEndian, &skip) - //fmt.Printf("skip %v\n", skip) - } - - username := make([]byte, definedAuthLen) - password := make([]byte, definedAuthLen) - copy(username, auth.Username) - copy(password, auth.Password) - challenge := bytes.Join([][]byte{username, password}, []byte("")) - if err := binary.Write(c, binary.BigEndian, challenge); err != nil { - return err - } - - if err := c.Flush(); err != nil { - return err - } - /* - - sendUsername := make([]byte, definedAuthLen) - for i := 0; i < definedAuthLen; i++ { - if i < len(auth.Username) { - sendUsername[i] = byte(charCodeAt(string(auth.Username), i)) - } else { - sendUsername[i] = 0 - } - } - - sendPassword := make([]byte, definedAuthLen) - - for i := 0; i < definedAuthLen; i++ { - if i < len(auth.Password) { - sendPassword[i] = byte(charCodeAt(string(auth.Password), i)) - } else { - sendPassword[i] = 0 - } - } - - if err := binary.Write(c, binary.BigEndian, sendUsername); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, sendPassword); err != nil { - return err - } - - if err := c.Flush(); err != nil { - return err - } - */ - //var pp [10]byte - //binary.Read(c, binary.BigEndian, &pp) - //fmt.Printf("ddd %v\n", pp) - return nil -} +package vnc2video + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +type ClientAuthATEN struct { + Username []byte + Password []byte +} + +func (*ClientAuthATEN) Type() SecurityType { + return SecTypeATEN +} + +func (*ClientAuthATEN) SubType() SecuritySubType { + return SecSubTypeUnknown +} + +func charCodeAt(s string, n int) rune { + for i, r := range s { + if i == n { + return r + } + } + return 0 +} + +func (auth *ClientAuthATEN) Auth(c Conn) error { + var definedAuthLen = 24 + + if len(auth.Username) > definedAuthLen || len(auth.Password) > definedAuthLen { + return fmt.Errorf("username/password is too long, allowed 0-23") + } + + nt, err := readTightTunnels(c) + if err != nil { + return err + } + /* + fmt.Printf("tunnels %d\n", nt) + for i := uint32(0); i < nt; i++ { + code, vendor, signature, err := readTightCaps(c) + if err != nil { + return err + } + fmt.Printf("code %d vendor %s signature %s\n", code, vendor, signature) + } + */ + if ((nt&0xffff0ff0)>>0 == 0xaff90fb0) || (nt <= 0 || nt > 0x1000000) { + c.SetProtoVersion("aten1") + var skip [20]byte + binary.Read(c, binary.BigEndian, &skip) + //fmt.Printf("skip %v\n", skip) + } + + username := make([]byte, definedAuthLen) + password := make([]byte, definedAuthLen) + copy(username, auth.Username) + copy(password, auth.Password) + challenge := bytes.Join([][]byte{username, password}, []byte("")) + if err := binary.Write(c, binary.BigEndian, challenge); err != nil { + return err + } + + if err := c.Flush(); err != nil { + return err + } + /* + + sendUsername := make([]byte, definedAuthLen) + for i := 0; i < definedAuthLen; i++ { + if i < len(auth.Username) { + sendUsername[i] = byte(charCodeAt(string(auth.Username), i)) + } else { + sendUsername[i] = 0 + } + } + + sendPassword := make([]byte, definedAuthLen) + + for i := 0; i < definedAuthLen; i++ { + if i < len(auth.Password) { + sendPassword[i] = byte(charCodeAt(string(auth.Password), i)) + } else { + sendPassword[i] = 0 + } + } + + if err := binary.Write(c, binary.BigEndian, sendUsername); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, sendPassword); err != nil { + return err + } + + if err := c.Flush(); err != nil { + return err + } + */ + //var pp [10]byte + //binary.Read(c, binary.BigEndian, &pp) + //fmt.Printf("ddd %v\n", pp) + return nil +} diff --git a/security_none.go b/security_none.go index a342706..938b819 100644 --- a/security_none.go +++ b/security_none.go @@ -1,30 +1,30 @@ -package vnc2webm - -type ClientAuthNone struct{} - -func (*ClientAuthNone) Type() SecurityType { - return SecTypeNone -} - -func (*ClientAuthNone) SubType() SecuritySubType { - return SecSubTypeUnknown -} - -func (*ClientAuthNone) Auth(conn Conn) error { - return nil -} - -// ServerAuthNone is the "none" authentication. See 7.2.1. -type ServerAuthNone struct{} - -func (*ServerAuthNone) Type() SecurityType { - return SecTypeNone -} - -func (*ServerAuthNone) SubType() SecuritySubType { - return SecSubTypeUnknown -} - -func (*ServerAuthNone) Auth(c Conn) error { - return nil -} +package vnc2video + +type ClientAuthNone struct{} + +func (*ClientAuthNone) Type() SecurityType { + return SecTypeNone +} + +func (*ClientAuthNone) SubType() SecuritySubType { + return SecSubTypeUnknown +} + +func (*ClientAuthNone) Auth(conn Conn) error { + return nil +} + +// ServerAuthNone is the "none" authentication. See 7.2.1. +type ServerAuthNone struct{} + +func (*ServerAuthNone) Type() SecurityType { + return SecTypeNone +} + +func (*ServerAuthNone) SubType() SecuritySubType { + return SecSubTypeUnknown +} + +func (*ServerAuthNone) Auth(c Conn) error { + return nil +} diff --git a/security_tight.go b/security_tight.go index b312d96..600c07b 100644 --- a/security_tight.go +++ b/security_tight.go @@ -1,27 +1,27 @@ -package vnc2webm - -import "encoding/binary" - -func readTightTunnels(c Conn) (uint32, error) { - var n uint32 - if err := binary.Read(c, binary.BigEndian, &n); err != nil { - return 0, err - } - return n, nil -} - -func readTightCaps(c Conn) (int32, []byte, []byte, error) { - var code int32 - var vendor [4]byte - var signature [8]byte - if err := binary.Read(c, binary.BigEndian, &code); err != nil { - return 0, nil, nil, err - } - if err := binary.Read(c, binary.BigEndian, &vendor); err != nil { - return 0, nil, nil, err - } - if err := binary.Read(c, binary.BigEndian, &signature); err != nil { - return 0, nil, nil, err - } - return code, vendor[:], signature[:], nil -} +package vnc2video + +import "encoding/binary" + +func readTightTunnels(c Conn) (uint32, error) { + var n uint32 + if err := binary.Read(c, binary.BigEndian, &n); err != nil { + return 0, err + } + return n, nil +} + +func readTightCaps(c Conn) (int32, []byte, []byte, error) { + var code int32 + var vendor [4]byte + var signature [8]byte + if err := binary.Read(c, binary.BigEndian, &code); err != nil { + return 0, nil, nil, err + } + if err := binary.Read(c, binary.BigEndian, &vendor); err != nil { + return 0, nil, nil, err + } + if err := binary.Read(c, binary.BigEndian, &signature); err != nil { + return 0, nil, nil, err + } + return code, vendor[:], signature[:], nil +} diff --git a/security_vencryptplain.go b/security_vencryptplain.go index 70fea66..29aceb2 100644 --- a/security_vencryptplain.go +++ b/security_vencryptplain.go @@ -1,109 +1,109 @@ -package vnc2webm - -import ( - "bytes" - "encoding/binary" - "fmt" -) - -func (*ClientAuthVeNCrypt02Plain) Type() SecurityType { - return SecTypeVeNCrypt -} - -func (*ClientAuthVeNCrypt02Plain) SubType() SecuritySubType { - return SecSubTypeVeNCrypt02Plain -} - -// ClientAuthVeNCryptPlain see https://www.berrange.com/~dan/vencrypt.txt -type ClientAuthVeNCrypt02Plain struct { - Username []byte - Password []byte -} - -func (auth *ClientAuthVeNCrypt02Plain) Auth(c Conn) error { - if err := binary.Write(c, binary.BigEndian, []uint8{0, 2}); err != nil { - return err - } - if err := c.Flush(); err != nil { - return err - } - var ( - major, minor uint8 - ) - - if err := binary.Read(c, binary.BigEndian, &major); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &minor); err != nil { - return err - } - res := uint8(1) - if major == 0 && minor == 2 { - res = uint8(0) - } - if err := binary.Write(c, binary.BigEndian, res); err != nil { - return err - } - c.Flush() - if err := binary.Write(c, binary.BigEndian, uint8(1)); err != nil { - return err - } - if err := binary.Write(c, binary.BigEndian, auth.SubType()); err != nil { - return err - } - if err := c.Flush(); err != nil { - return err - } - var secType SecuritySubType - if err := binary.Read(c, binary.BigEndian, &secType); err != nil { - return err - } - if secType != auth.SubType() { - binary.Write(c, binary.BigEndian, uint8(1)) - c.Flush() - return fmt.Errorf("invalid sectype") - } - if len(auth.Password) == 0 || len(auth.Username) == 0 { - return fmt.Errorf("Security Handshake failed; no username and/or password provided for VeNCryptAuth.") - } - /* - if err := binary.Write(c, binary.BigEndian, uint32(len(auth.Username))); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, uint32(len(auth.Password))); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, auth.Username); err != nil { - return err - } - - if err := binary.Write(c, binary.BigEndian, auth.Password); err != nil { - return err - } - */ - var ( - uLength, pLength uint32 - ) - if err := binary.Read(c, binary.BigEndian, &uLength); err != nil { - return err - } - if err := binary.Read(c, binary.BigEndian, &pLength); err != nil { - return err - } - - username := make([]byte, uLength) - password := make([]byte, pLength) - if err := binary.Read(c, binary.BigEndian, &username); err != nil { - return err - } - - if err := binary.Read(c, binary.BigEndian, &password); err != nil { - return err - } - if !bytes.Equal(auth.Username, username) || !bytes.Equal(auth.Password, password) { - return fmt.Errorf("invalid username/password") - } - return nil -} +package vnc2video + +import ( + "bytes" + "encoding/binary" + "fmt" +) + +func (*ClientAuthVeNCrypt02Plain) Type() SecurityType { + return SecTypeVeNCrypt +} + +func (*ClientAuthVeNCrypt02Plain) SubType() SecuritySubType { + return SecSubTypeVeNCrypt02Plain +} + +// ClientAuthVeNCryptPlain see https://www.berrange.com/~dan/vencrypt.txt +type ClientAuthVeNCrypt02Plain struct { + Username []byte + Password []byte +} + +func (auth *ClientAuthVeNCrypt02Plain) Auth(c Conn) error { + if err := binary.Write(c, binary.BigEndian, []uint8{0, 2}); err != nil { + return err + } + if err := c.Flush(); err != nil { + return err + } + var ( + major, minor uint8 + ) + + if err := binary.Read(c, binary.BigEndian, &major); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &minor); err != nil { + return err + } + res := uint8(1) + if major == 0 && minor == 2 { + res = uint8(0) + } + if err := binary.Write(c, binary.BigEndian, res); err != nil { + return err + } + c.Flush() + if err := binary.Write(c, binary.BigEndian, uint8(1)); err != nil { + return err + } + if err := binary.Write(c, binary.BigEndian, auth.SubType()); err != nil { + return err + } + if err := c.Flush(); err != nil { + return err + } + var secType SecuritySubType + if err := binary.Read(c, binary.BigEndian, &secType); err != nil { + return err + } + if secType != auth.SubType() { + binary.Write(c, binary.BigEndian, uint8(1)) + c.Flush() + return fmt.Errorf("invalid sectype") + } + if len(auth.Password) == 0 || len(auth.Username) == 0 { + return fmt.Errorf("Security Handshake failed; no username and/or password provided for VeNCryptAuth.") + } + /* + if err := binary.Write(c, binary.BigEndian, uint32(len(auth.Username))); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, uint32(len(auth.Password))); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, auth.Username); err != nil { + return err + } + + if err := binary.Write(c, binary.BigEndian, auth.Password); err != nil { + return err + } + */ + var ( + uLength, pLength uint32 + ) + if err := binary.Read(c, binary.BigEndian, &uLength); err != nil { + return err + } + if err := binary.Read(c, binary.BigEndian, &pLength); err != nil { + return err + } + + username := make([]byte, uLength) + password := make([]byte, pLength) + if err := binary.Read(c, binary.BigEndian, &username); err != nil { + return err + } + + if err := binary.Read(c, binary.BigEndian, &password); err != nil { + return err + } + if !bytes.Equal(auth.Username, username) || !bytes.Equal(auth.Password, password) { + return fmt.Errorf("invalid username/password") + } + return nil +} diff --git a/security_vnc.go b/security_vnc.go index 58a147e..b052861 100644 --- a/security_vnc.go +++ b/security_vnc.go @@ -1,120 +1,120 @@ -package vnc2webm - -import ( - "bytes" - "crypto/des" - "encoding/binary" - "fmt" -) - -// ServerAuthVNC is the standard password authentication. See 7.2.2. -type ServerAuthVNC struct { - Challenge []byte - Password []byte - Crypted []byte -} - -func (*ServerAuthVNC) Type() SecurityType { - return SecTypeVNC -} -func (*ServerAuthVNC) SubType() SecuritySubType { - return SecSubTypeUnknown -} - -func (auth *ServerAuthVNC) WriteChallenge(c Conn) error { - if err := binary.Write(c, binary.BigEndian, auth.Challenge); err != nil { - return err - } - return c.Flush() -} - -func (auth *ServerAuthVNC) ReadChallenge(c Conn) error { - var crypted [16]byte - if err := binary.Read(c, binary.BigEndian, &crypted); err != nil { - return err - } - auth.Crypted = crypted[:] - return nil -} - -func (auth *ServerAuthVNC) Auth(c Conn) error { - if err := auth.WriteChallenge(c); err != nil { - return err - } - - if err := auth.ReadChallenge(c); err != nil { - return err - } - - encrypted, err := AuthVNCEncode(auth.Password, auth.Challenge) - if err != nil { - return err - } - if !bytes.Equal(encrypted, auth.Crypted) { - return fmt.Errorf("password invalid") - } - return nil -} - -// ClientAuthVNC is the standard password authentication. See 7.2.2. -type ClientAuthVNC struct { - Challenge []byte - Password []byte -} - -func (*ClientAuthVNC) Type() SecurityType { - return SecTypeVNC -} -func (*ClientAuthVNC) SubType() SecuritySubType { - return SecSubTypeUnknown -} - -func (auth *ClientAuthVNC) Auth(c Conn) error { - if len(auth.Password) == 0 { - return fmt.Errorf("Security Handshake failed; no password provided for VNCAuth.") - } - - var challenge [16]byte - if err := binary.Read(c, binary.BigEndian, &challenge); err != nil { - return err - } - - encrypted, err := AuthVNCEncode(auth.Password, challenge[:]) - if err != nil { - return err - } - // Send the encrypted challenge back to server - if err := binary.Write(c, binary.BigEndian, encrypted); err != nil { - return err - } - - return c.Flush() -} - -func AuthVNCEncode(password []byte, challenge []byte) ([]byte, error) { - if len(challenge) != 16 { - return nil, fmt.Errorf("challenge size not 16 byte long") - } - // Copy password string to 8 byte 0-padded slice - key := make([]byte, 8) - copy(key, password) - - // Each byte of the password needs to be reversed. This is a - // non RFC-documented behaviour of VNC clients and servers - for i := range key { - key[i] = (key[i]&0x55)<<1 | (key[i]&0xAA)>>1 // Swap adjacent bits - key[i] = (key[i]&0x33)<<2 | (key[i]&0xCC)>>2 // Swap adjacent pairs - key[i] = (key[i]&0x0F)<<4 | (key[i]&0xF0)>>4 // Swap the 2 halves - } - - // Encrypt challenge with key. - cipher, err := des.NewCipher(key) - if err != nil { - return nil, err - } - for i := 0; i < len(challenge); i += cipher.BlockSize() { - cipher.Encrypt(challenge[i:i+cipher.BlockSize()], challenge[i:i+cipher.BlockSize()]) - } - - return challenge, nil -} +package vnc2video + +import ( + "bytes" + "crypto/des" + "encoding/binary" + "fmt" +) + +// ServerAuthVNC is the standard password authentication. See 7.2.2. +type ServerAuthVNC struct { + Challenge []byte + Password []byte + Crypted []byte +} + +func (*ServerAuthVNC) Type() SecurityType { + return SecTypeVNC +} +func (*ServerAuthVNC) SubType() SecuritySubType { + return SecSubTypeUnknown +} + +func (auth *ServerAuthVNC) WriteChallenge(c Conn) error { + if err := binary.Write(c, binary.BigEndian, auth.Challenge); err != nil { + return err + } + return c.Flush() +} + +func (auth *ServerAuthVNC) ReadChallenge(c Conn) error { + var crypted [16]byte + if err := binary.Read(c, binary.BigEndian, &crypted); err != nil { + return err + } + auth.Crypted = crypted[:] + return nil +} + +func (auth *ServerAuthVNC) Auth(c Conn) error { + if err := auth.WriteChallenge(c); err != nil { + return err + } + + if err := auth.ReadChallenge(c); err != nil { + return err + } + + encrypted, err := AuthVNCEncode(auth.Password, auth.Challenge) + if err != nil { + return err + } + if !bytes.Equal(encrypted, auth.Crypted) { + return fmt.Errorf("password invalid") + } + return nil +} + +// ClientAuthVNC is the standard password authentication. See 7.2.2. +type ClientAuthVNC struct { + Challenge []byte + Password []byte +} + +func (*ClientAuthVNC) Type() SecurityType { + return SecTypeVNC +} +func (*ClientAuthVNC) SubType() SecuritySubType { + return SecSubTypeUnknown +} + +func (auth *ClientAuthVNC) Auth(c Conn) error { + if len(auth.Password) == 0 { + return fmt.Errorf("Security Handshake failed; no password provided for VNCAuth.") + } + + var challenge [16]byte + if err := binary.Read(c, binary.BigEndian, &challenge); err != nil { + return err + } + + encrypted, err := AuthVNCEncode(auth.Password, challenge[:]) + if err != nil { + return err + } + // Send the encrypted challenge back to server + if err := binary.Write(c, binary.BigEndian, encrypted); err != nil { + return err + } + + return c.Flush() +} + +func AuthVNCEncode(password []byte, challenge []byte) ([]byte, error) { + if len(challenge) != 16 { + return nil, fmt.Errorf("challenge size not 16 byte long") + } + // Copy password string to 8 byte 0-padded slice + key := make([]byte, 8) + copy(key, password) + + // Each byte of the password needs to be reversed. This is a + // non RFC-documented behaviour of VNC clients and servers + for i := range key { + key[i] = (key[i]&0x55)<<1 | (key[i]&0xAA)>>1 // Swap adjacent bits + key[i] = (key[i]&0x33)<<2 | (key[i]&0xCC)>>2 // Swap adjacent pairs + key[i] = (key[i]&0x0F)<<4 | (key[i]&0xF0)>>4 // Swap the 2 halves + } + + // Encrypt challenge with key. + cipher, err := des.NewCipher(key) + if err != nil { + return nil, err + } + for i := 0; i < len(challenge); i += cipher.BlockSize() { + cipher.Encrypt(challenge[i:i+cipher.BlockSize()], challenge[i:i+cipher.BlockSize()]) + } + + return challenge, nil +} diff --git a/securitysubtype_string.go b/securitysubtype_string.go index 7b90af3..c5850fb 100644 --- a/securitysubtype_string.go +++ b/securitysubtype_string.go @@ -1,32 +1,32 @@ -// Code generated by "stringer -type=SecuritySubType"; DO NOT EDIT. - -package vnc2webm - -import "fmt" - -const ( - _SecuritySubType_name_0 = "SecSubTypeUnknown" - _SecuritySubType_name_1 = "SecSubTypeVeNCrypt01PlainSecSubTypeVeNCrypt01TLSNoneSecSubTypeVeNCrypt01TLSVNCSecSubTypeVeNCrypt01TLSPlainSecSubTypeVeNCrypt01X509NoneSecSubTypeVeNCrypt01X509VNCSecSubTypeVeNCrypt01X509Plain" - _SecuritySubType_name_2 = "SecSubTypeVeNCrypt02PlainSecSubTypeVeNCrypt02TLSNoneSecSubTypeVeNCrypt02TLSVNCSecSubTypeVeNCrypt02TLSPlainSecSubTypeVeNCrypt02X509NoneSecSubTypeVeNCrypt02X509VNCSecSubTypeVeNCrypt02X509Plain" -) - -var ( - _SecuritySubType_index_0 = [...]uint8{0, 17} - _SecuritySubType_index_1 = [...]uint8{0, 25, 52, 78, 106, 134, 161, 190} - _SecuritySubType_index_2 = [...]uint8{0, 25, 52, 78, 106, 134, 161, 190} -) - -func (i SecuritySubType) String() string { - switch { - case i == 0: - return _SecuritySubType_name_0 - case 19 <= i && i <= 25: - i -= 19 - return _SecuritySubType_name_1[_SecuritySubType_index_1[i]:_SecuritySubType_index_1[i+1]] - case 256 <= i && i <= 262: - i -= 256 - return _SecuritySubType_name_2[_SecuritySubType_index_2[i]:_SecuritySubType_index_2[i+1]] - default: - return fmt.Sprintf("SecuritySubType(%d)", i) - } -} +// Code generated by "stringer -type=SecuritySubType"; DO NOT EDIT. + +package vnc2video + +import "fmt" + +const ( + _SecuritySubType_name_0 = "SecSubTypeUnknown" + _SecuritySubType_name_1 = "SecSubTypeVeNCrypt01PlainSecSubTypeVeNCrypt01TLSNoneSecSubTypeVeNCrypt01TLSVNCSecSubTypeVeNCrypt01TLSPlainSecSubTypeVeNCrypt01X509NoneSecSubTypeVeNCrypt01X509VNCSecSubTypeVeNCrypt01X509Plain" + _SecuritySubType_name_2 = "SecSubTypeVeNCrypt02PlainSecSubTypeVeNCrypt02TLSNoneSecSubTypeVeNCrypt02TLSVNCSecSubTypeVeNCrypt02TLSPlainSecSubTypeVeNCrypt02X509NoneSecSubTypeVeNCrypt02X509VNCSecSubTypeVeNCrypt02X509Plain" +) + +var ( + _SecuritySubType_index_0 = [...]uint8{0, 17} + _SecuritySubType_index_1 = [...]uint8{0, 25, 52, 78, 106, 134, 161, 190} + _SecuritySubType_index_2 = [...]uint8{0, 25, 52, 78, 106, 134, 161, 190} +) + +func (i SecuritySubType) String() string { + switch { + case i == 0: + return _SecuritySubType_name_0 + case 19 <= i && i <= 25: + i -= 19 + return _SecuritySubType_name_1[_SecuritySubType_index_1[i]:_SecuritySubType_index_1[i+1]] + case 256 <= i && i <= 262: + i -= 256 + return _SecuritySubType_name_2[_SecuritySubType_index_2[i]:_SecuritySubType_index_2[i+1]] + default: + return fmt.Sprintf("SecuritySubType(%d)", i) + } +} diff --git a/securitytype_string.go b/securitytype_string.go index 7a2a365..5b7cd1f 100644 --- a/securitytype_string.go +++ b/securitytype_string.go @@ -1,30 +1,30 @@ -// Code generated by "stringer -type=SecurityType"; DO NOT EDIT. - -package vnc2webm - -import "fmt" - -const ( - _SecurityType_name_0 = "SecTypeUnknownSecTypeNoneSecTypeVNC" - _SecurityType_name_1 = "SecTypeTight" - _SecurityType_name_2 = "SecTypeVeNCrypt" -) - -var ( - _SecurityType_index_0 = [...]uint8{0, 14, 25, 35} - _SecurityType_index_1 = [...]uint8{0, 12} - _SecurityType_index_2 = [...]uint8{0, 15} -) - -func (i SecurityType) String() string { - switch { - case 0 <= i && i <= 2: - return _SecurityType_name_0[_SecurityType_index_0[i]:_SecurityType_index_0[i+1]] - case i == 16: - return _SecurityType_name_1 - case i == 19: - return _SecurityType_name_2 - default: - return fmt.Sprintf("SecurityType(%d)", i) - } -} +// Code generated by "stringer -type=SecurityType"; DO NOT EDIT. + +package vnc2video + +import "fmt" + +const ( + _SecurityType_name_0 = "SecTypeUnknownSecTypeNoneSecTypeVNC" + _SecurityType_name_1 = "SecTypeTight" + _SecurityType_name_2 = "SecTypeVeNCrypt" +) + +var ( + _SecurityType_index_0 = [...]uint8{0, 14, 25, 35} + _SecurityType_index_1 = [...]uint8{0, 12} + _SecurityType_index_2 = [...]uint8{0, 15} +) + +func (i SecurityType) String() string { + switch { + case 0 <= i && i <= 2: + return _SecurityType_name_0[_SecurityType_index_0[i]:_SecurityType_index_0[i+1]] + case i == 16: + return _SecurityType_name_1 + case i == 19: + return _SecurityType_name_2 + default: + return fmt.Sprintf("SecurityType(%d)", i) + } +} diff --git a/server.go b/server.go index a291c54..37179d9 100644 --- a/server.go +++ b/server.go @@ -1,342 +1,342 @@ -package vnc2webm - -import ( - "bufio" - "context" - "encoding/binary" - "fmt" - "net" - "sync" -) - -var _ Conn = (*ServerConn)(nil) - -// Config returns config for server conn -func (c *ServerConn) Config() interface{} { - return c.cfg -} -func (c *ServerConn) GetEncInstance(typ EncodingType) Encoding { - for _, enc := range c.encodings { - if enc.Type() == typ { - return enc - } - } - return nil -} - -// Conn returns underlining server net.Conn -func (c *ServerConn) Conn() net.Conn { - return c.c -} - -// Wait waits connection to close -func (c *ServerConn) Wait() { - <-c.quit -} - -// SetEncodings ??? sets server connection encodings -func (c *ServerConn) SetEncodings(encs []EncodingType) error { - encodings := make(map[EncodingType]Encoding) - for _, enc := range c.cfg.Encodings { - encodings[enc.Type()] = enc - } - for _, encType := range encs { - if enc, ok := encodings[encType]; ok { - c.encodings = append(c.encodings, enc) - } - } - return nil -} - -// SetProtoVersion ??? sets proto version -func (c *ServerConn) SetProtoVersion(pv string) { - c.protocol = pv -} - -// Flush buffered data to server conn -func (c *ServerConn) Flush() error { - return c.bw.Flush() -} - -// Close closing server conn -func (c *ServerConn) Close() error { - if c.quit != nil { - close(c.quit) - c.quit = nil - } - return c.c.Close() -} - -// Read reads data from net.Conn -func (c *ServerConn) Read(buf []byte) (int, error) { - return c.br.Read(buf) -} - -// Write writes data to net.Conn, must be Flashed -func (c *ServerConn) Write(buf []byte) (int, error) { - return c.bw.Write(buf) -} - -// ColorMap returns server connection color map -func (c *ServerConn) ColorMap() ColorMap { - return c.colorMap -} - -// SetColorMap sets connection color map -func (c *ServerConn) SetColorMap(cm ColorMap) { - c.colorMap = cm -} - -// DesktopName returns connection desktop name -func (c *ServerConn) DesktopName() []byte { - return c.desktopName -} - -// PixelFormat return connection pixel format -func (c *ServerConn) PixelFormat() PixelFormat { - return c.pixelFormat -} - -// SetDesktopName sets connection desktop name -func (c *ServerConn) SetDesktopName(name []byte) { - c.desktopName = name -} - -// SetPixelFormat sets pixel format for server conn -func (c *ServerConn) SetPixelFormat(pf PixelFormat) error { - c.pixelFormat = pf - return nil -} - -// Encodings returns connection encodings -func (c *ServerConn) Encodings() []Encoding { - return c.encodings -} - -// Width returns framebuffer width -func (c *ServerConn) Width() uint16 { - return c.fbWidth -} - -// Height returns framebuffer height -func (c *ServerConn) Height() uint16 { - return c.fbHeight -} - -// Protocol returns protocol -func (c *ServerConn) Protocol() string { - return c.protocol -} - -// SecurityHandler returns security handler -func (c *ServerConn) SecurityHandler() SecurityHandler { - return c.securityHandler -} - -// SetSecurityHandler sets security handler -func (c *ServerConn) SetSecurityHandler(sechandler SecurityHandler) error { - c.securityHandler = sechandler - return nil -} - -// SetWidth sets framebuffer width -func (c *ServerConn) SetWidth(w uint16) { - // TODO send desktopsize pseudo encoding - c.fbWidth = w -} - -// SetHeight sets framebuffer height -func (c *ServerConn) SetHeight(h uint16) { - // TODO send desktopsize pseudo encoding - c.fbHeight = h -} - -// ServerConn underlining server conn -type ServerConn struct { - c net.Conn - cfg *ServerConfig - br *bufio.Reader - bw *bufio.Writer - protocol string - // If the pixel format uses a color map, then this is the color - // map that is used. This should not be modified directly, since - // the data comes from the server. - // Definition in §5 - Representation of Pixel Data. - colorMap ColorMap - - // Name associated with the desktop, sent from the server. - desktopName []byte - - // Encodings supported by the client. This should not be modified - // directly. Instead, SetEncodings() should be used. - encodings []Encoding - - securityHandler SecurityHandler - - // Height of the frame buffer in pixels, sent to the client. - fbHeight uint16 - - // Width of the frame buffer in pixels, sent to the client. - fbWidth uint16 - - // The pixel format associated with the connection. This shouldn't - // be modified. If you wish to set a new pixel format, use the - // SetPixelFormat method. - pixelFormat PixelFormat - - quit chan struct{} -} - -var ( - // DefaultServerHandlers uses default handlers for hanshake - DefaultServerHandlers = []Handler{ - &DefaultServerVersionHandler{}, - &DefaultServerSecurityHandler{}, - &DefaultServerClientInitHandler{}, - &DefaultServerServerInitHandler{}, - &DefaultServerMessageHandler{}, - } -) - -// ServerConfig config struct -type ServerConfig struct { - Handlers []Handler - SecurityHandlers []SecurityHandler - Encodings []Encoding - PixelFormat PixelFormat - ColorMap ColorMap - ClientMessageCh chan ClientMessage - ServerMessageCh chan ServerMessage - Messages []ClientMessage - DesktopName []byte - Height uint16 - Width uint16 - ErrorCh chan error -} - -// NewServerConn returns new Server connection fron net.Conn -func NewServerConn(c net.Conn, cfg *ServerConfig) (*ServerConn, error) { - return &ServerConn{ - c: c, - br: bufio.NewReader(c), - bw: bufio.NewWriter(c), - cfg: cfg, - desktopName: cfg.DesktopName, - encodings: cfg.Encodings, - pixelFormat: cfg.PixelFormat, - fbWidth: cfg.Width, - fbHeight: cfg.Height, - quit: make(chan struct{}), - }, nil -} - -// Serve serves requests from net.Listener using ServerConfig -func Serve(ctx context.Context, ln net.Listener, cfg *ServerConfig) error { - for { - - c, err := ln.Accept() - if err != nil { - continue - } - - conn, err := NewServerConn(c, cfg) - if err != nil { - cfg.ErrorCh <- err - continue - } - - if len(cfg.Handlers) == 0 { - cfg.Handlers = DefaultServerHandlers - } - - handlerLoop: - for _, h := range cfg.Handlers { - if err := h.Handle(conn); err != nil { - if cfg.ErrorCh != nil { - cfg.ErrorCh <- err - } - conn.Close() - break handlerLoop - } - } - } -} - -// DefaultServerMessageHandler default package handler -type DefaultServerMessageHandler struct{} - -// Handle handles messages from clients -func (*DefaultServerMessageHandler) Handle(c Conn) error { - cfg := c.Config().(*ServerConfig) - var err error - var wg sync.WaitGroup - - defer c.Close() - clientMessages := make(map[ClientMessageType]ClientMessage) - for _, m := range cfg.Messages { - clientMessages[m.Type()] = m - } - wg.Add(2) - - quit := make(chan struct{}) - - // server - go func() { - defer wg.Done() - for { - select { - case <-quit: - return - case msg := <-cfg.ServerMessageCh: - if err = msg.Write(c); err != nil { - cfg.ErrorCh <- err - if quit != nil { - close(quit) - quit = nil - } - return - } - } - } - }() - - // client - go func() { - defer wg.Done() - for { - select { - case <-quit: - return - default: - var messageType ClientMessageType - if err := binary.Read(c, binary.BigEndian, &messageType); err != nil { - cfg.ErrorCh <- err - if quit != nil { - close(quit) - quit = nil - } - return - } - msg, ok := clientMessages[messageType] - if !ok { - cfg.ErrorCh <- fmt.Errorf("unsupported message-type: %v", messageType) - close(quit) - return - } - parsedMsg, err := msg.Read(c) - if err != nil { - cfg.ErrorCh <- err - if quit != nil { - close(quit) - quit = nil - } - return - } - cfg.ClientMessageCh <- parsedMsg - } - } - }() - - wg.Wait() - return nil -} +package vnc2video + +import ( + "bufio" + "context" + "encoding/binary" + "fmt" + "net" + "sync" +) + +var _ Conn = (*ServerConn)(nil) + +// Config returns config for server conn +func (c *ServerConn) Config() interface{} { + return c.cfg +} +func (c *ServerConn) GetEncInstance(typ EncodingType) Encoding { + for _, enc := range c.encodings { + if enc.Type() == typ { + return enc + } + } + return nil +} + +// Conn returns underlining server net.Conn +func (c *ServerConn) Conn() net.Conn { + return c.c +} + +// Wait waits connection to close +func (c *ServerConn) Wait() { + <-c.quit +} + +// SetEncodings ??? sets server connection encodings +func (c *ServerConn) SetEncodings(encs []EncodingType) error { + encodings := make(map[EncodingType]Encoding) + for _, enc := range c.cfg.Encodings { + encodings[enc.Type()] = enc + } + for _, encType := range encs { + if enc, ok := encodings[encType]; ok { + c.encodings = append(c.encodings, enc) + } + } + return nil +} + +// SetProtoVersion ??? sets proto version +func (c *ServerConn) SetProtoVersion(pv string) { + c.protocol = pv +} + +// Flush buffered data to server conn +func (c *ServerConn) Flush() error { + return c.bw.Flush() +} + +// Close closing server conn +func (c *ServerConn) Close() error { + if c.quit != nil { + close(c.quit) + c.quit = nil + } + return c.c.Close() +} + +// Read reads data from net.Conn +func (c *ServerConn) Read(buf []byte) (int, error) { + return c.br.Read(buf) +} + +// Write writes data to net.Conn, must be Flashed +func (c *ServerConn) Write(buf []byte) (int, error) { + return c.bw.Write(buf) +} + +// ColorMap returns server connection color map +func (c *ServerConn) ColorMap() ColorMap { + return c.colorMap +} + +// SetColorMap sets connection color map +func (c *ServerConn) SetColorMap(cm ColorMap) { + c.colorMap = cm +} + +// DesktopName returns connection desktop name +func (c *ServerConn) DesktopName() []byte { + return c.desktopName +} + +// PixelFormat return connection pixel format +func (c *ServerConn) PixelFormat() PixelFormat { + return c.pixelFormat +} + +// SetDesktopName sets connection desktop name +func (c *ServerConn) SetDesktopName(name []byte) { + c.desktopName = name +} + +// SetPixelFormat sets pixel format for server conn +func (c *ServerConn) SetPixelFormat(pf PixelFormat) error { + c.pixelFormat = pf + return nil +} + +// Encodings returns connection encodings +func (c *ServerConn) Encodings() []Encoding { + return c.encodings +} + +// Width returns framebuffer width +func (c *ServerConn) Width() uint16 { + return c.fbWidth +} + +// Height returns framebuffer height +func (c *ServerConn) Height() uint16 { + return c.fbHeight +} + +// Protocol returns protocol +func (c *ServerConn) Protocol() string { + return c.protocol +} + +// SecurityHandler returns security handler +func (c *ServerConn) SecurityHandler() SecurityHandler { + return c.securityHandler +} + +// SetSecurityHandler sets security handler +func (c *ServerConn) SetSecurityHandler(sechandler SecurityHandler) error { + c.securityHandler = sechandler + return nil +} + +// SetWidth sets framebuffer width +func (c *ServerConn) SetWidth(w uint16) { + // TODO send desktopsize pseudo encoding + c.fbWidth = w +} + +// SetHeight sets framebuffer height +func (c *ServerConn) SetHeight(h uint16) { + // TODO send desktopsize pseudo encoding + c.fbHeight = h +} + +// ServerConn underlining server conn +type ServerConn struct { + c net.Conn + cfg *ServerConfig + br *bufio.Reader + bw *bufio.Writer + protocol string + // If the pixel format uses a color map, then this is the color + // map that is used. This should not be modified directly, since + // the data comes from the server. + // Definition in §5 - Representation of Pixel Data. + colorMap ColorMap + + // Name associated with the desktop, sent from the server. + desktopName []byte + + // Encodings supported by the client. This should not be modified + // directly. Instead, SetEncodings() should be used. + encodings []Encoding + + securityHandler SecurityHandler + + // Height of the frame buffer in pixels, sent to the client. + fbHeight uint16 + + // Width of the frame buffer in pixels, sent to the client. + fbWidth uint16 + + // The pixel format associated with the connection. This shouldn't + // be modified. If you wish to set a new pixel format, use the + // SetPixelFormat method. + pixelFormat PixelFormat + + quit chan struct{} +} + +var ( + // DefaultServerHandlers uses default handlers for hanshake + DefaultServerHandlers = []Handler{ + &DefaultServerVersionHandler{}, + &DefaultServerSecurityHandler{}, + &DefaultServerClientInitHandler{}, + &DefaultServerServerInitHandler{}, + &DefaultServerMessageHandler{}, + } +) + +// ServerConfig config struct +type ServerConfig struct { + Handlers []Handler + SecurityHandlers []SecurityHandler + Encodings []Encoding + PixelFormat PixelFormat + ColorMap ColorMap + ClientMessageCh chan ClientMessage + ServerMessageCh chan ServerMessage + Messages []ClientMessage + DesktopName []byte + Height uint16 + Width uint16 + ErrorCh chan error +} + +// NewServerConn returns new Server connection fron net.Conn +func NewServerConn(c net.Conn, cfg *ServerConfig) (*ServerConn, error) { + return &ServerConn{ + c: c, + br: bufio.NewReader(c), + bw: bufio.NewWriter(c), + cfg: cfg, + desktopName: cfg.DesktopName, + encodings: cfg.Encodings, + pixelFormat: cfg.PixelFormat, + fbWidth: cfg.Width, + fbHeight: cfg.Height, + quit: make(chan struct{}), + }, nil +} + +// Serve serves requests from net.Listener using ServerConfig +func Serve(ctx context.Context, ln net.Listener, cfg *ServerConfig) error { + for { + + c, err := ln.Accept() + if err != nil { + continue + } + + conn, err := NewServerConn(c, cfg) + if err != nil { + cfg.ErrorCh <- err + continue + } + + if len(cfg.Handlers) == 0 { + cfg.Handlers = DefaultServerHandlers + } + + handlerLoop: + for _, h := range cfg.Handlers { + if err := h.Handle(conn); err != nil { + if cfg.ErrorCh != nil { + cfg.ErrorCh <- err + } + conn.Close() + break handlerLoop + } + } + } +} + +// DefaultServerMessageHandler default package handler +type DefaultServerMessageHandler struct{} + +// Handle handles messages from clients +func (*DefaultServerMessageHandler) Handle(c Conn) error { + cfg := c.Config().(*ServerConfig) + var err error + var wg sync.WaitGroup + + defer c.Close() + clientMessages := make(map[ClientMessageType]ClientMessage) + for _, m := range cfg.Messages { + clientMessages[m.Type()] = m + } + wg.Add(2) + + quit := make(chan struct{}) + + // server + go func() { + defer wg.Done() + for { + select { + case <-quit: + return + case msg := <-cfg.ServerMessageCh: + if err = msg.Write(c); err != nil { + cfg.ErrorCh <- err + if quit != nil { + close(quit) + quit = nil + } + return + } + } + } + }() + + // client + go func() { + defer wg.Done() + for { + select { + case <-quit: + return + default: + var messageType ClientMessageType + if err := binary.Read(c, binary.BigEndian, &messageType); err != nil { + cfg.ErrorCh <- err + if quit != nil { + close(quit) + quit = nil + } + return + } + msg, ok := clientMessages[messageType] + if !ok { + cfg.ErrorCh <- fmt.Errorf("unsupported message-type: %v", messageType) + close(quit) + return + } + parsedMsg, err := msg.Read(c) + if err != nil { + cfg.ErrorCh <- err + if quit != nil { + close(quit) + quit = nil + } + return + } + cfg.ClientMessageCh <- parsedMsg + } + } + }() + + wg.Wait() + return nil +} diff --git a/tightcompression_string.go b/tightcompression_string.go index 473ffa8..22f2e84 100644 --- a/tightcompression_string.go +++ b/tightcompression_string.go @@ -1,27 +1,27 @@ -// Code generated by "stringer -type=TightCompression"; DO NOT EDIT. - -package vnc2webm - -import "fmt" - -const ( - _TightCompression_name_0 = "TightCompressionBasic" - _TightCompression_name_1 = "TightCompressionFillTightCompressionJPEGTightCompressionPNG" -) - -var ( - _TightCompression_index_0 = [...]uint8{0, 21} - _TightCompression_index_1 = [...]uint8{0, 20, 40, 59} -) - -func (i TightCompression) String() string { - switch { - case i == 0: - return _TightCompression_name_0 - case 8 <= i && i <= 10: - i -= 8 - return _TightCompression_name_1[_TightCompression_index_1[i]:_TightCompression_index_1[i+1]] - default: - return fmt.Sprintf("TightCompression(%d)", i) - } -} +// Code generated by "stringer -type=TightCompression"; DO NOT EDIT. + +package vnc2video + +import "fmt" + +const ( + _TightCompression_name_0 = "TightCompressionBasic" + _TightCompression_name_1 = "TightCompressionFillTightCompressionJPEGTightCompressionPNG" +) + +var ( + _TightCompression_index_0 = [...]uint8{0, 21} + _TightCompression_index_1 = [...]uint8{0, 20, 40, 59} +) + +func (i TightCompression) String() string { + switch { + case i == 0: + return _TightCompression_name_0 + case 8 <= i && i <= 10: + i -= 8 + return _TightCompression_name_1[_TightCompression_index_1[i]:_TightCompression_index_1[i+1]] + default: + return fmt.Sprintf("TightCompression(%d)", i) + } +} diff --git a/tightfilter_string.go b/tightfilter_string.go index c61947d..af033de 100644 --- a/tightfilter_string.go +++ b/tightfilter_string.go @@ -1,16 +1,16 @@ -// Code generated by "stringer -type=TightFilter"; DO NOT EDIT. - -package vnc2webm - -import "fmt" - -const _TightFilter_name = "TightFilterCopyTightFilterPaletteTightFilterGradient" - -var _TightFilter_index = [...]uint8{0, 15, 33, 52} - -func (i TightFilter) String() string { - if i >= TightFilter(len(_TightFilter_index)-1) { - return fmt.Sprintf("TightFilter(%d)", i) - } - return _TightFilter_name[_TightFilter_index[i]:_TightFilter_index[i+1]] -} +// Code generated by "stringer -type=TightFilter"; DO NOT EDIT. + +package vnc2video + +import "fmt" + +const _TightFilter_name = "TightFilterCopyTightFilterPaletteTightFilterGradient" + +var _TightFilter_index = [...]uint8{0, 15, 33, 52} + +func (i TightFilter) String() string { + if i >= TightFilter(len(_TightFilter_index)-1) { + return fmt.Sprintf("TightFilter(%d)", i) + } + return _TightFilter_name[_TightFilter_index[i]:_TightFilter_index[i+1]] +}