summaryrefslogtreecommitdiff
path: root/teleirc/matterbridge/vendor/github.com/shazow
diff options
context:
space:
mode:
Diffstat (limited to 'teleirc/matterbridge/vendor/github.com/shazow')
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/rateio/LICENSE21
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/rateio/doc.go29
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/rateio/limiter.go71
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/rateio/reader.go25
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/rateio/writer.go25
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/LICENSE21
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/NOTICE33
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/internal/sanitize/sanitize.go29
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/auth.go74
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/client.go76
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/doc.go34
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/logger.go23
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/net.go75
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/pty.go70
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go71
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal.go246
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/LICENSE27
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go1027
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util.go114
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_aix.go12
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_bsd.go12
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_linux.go10
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_plan9.go58
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_solaris.go125
-rw-r--r--teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_windows.go103
25 files changed, 2411 insertions, 0 deletions
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/rateio/LICENSE b/teleirc/matterbridge/vendor/github.com/shazow/rateio/LICENSE
new file mode 100644
index 0000000..1901179
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/rateio/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Andrey Petrov
+
+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/teleirc/matterbridge/vendor/github.com/shazow/rateio/doc.go b/teleirc/matterbridge/vendor/github.com/shazow/rateio/doc.go
new file mode 100644
index 0000000..1c5e085
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/rateio/doc.go
@@ -0,0 +1,29 @@
+/*
+Package rateio provides an io interfaces for rate-limiting.
+
+This can be used to apply rate limiting to any type that implements an io-style interface.
+
+For example, we can use it to restrict the reading rate of a net.Conn:
+
+ type limitedConn struct {
+ net.Conn
+ io.Reader // Our rate-limited io.Reader for net.Conn
+ }
+
+ func (r *limitedConn) Read(p []byte) (n int, err error) {
+ return r.Reader.Read(p)
+ }
+
+ // ReadLimitConn returns a net.Conn whose io.Reader interface is rate-limited by limiter.
+ func ReadLimitConn(conn net.Conn, limiter rateio.Limiter) net.Conn {
+ return &limitedConn{
+ Conn: conn,
+ Reader: rateio.NewReader(conn, limiter),
+ }
+ }
+
+Then we can use ReadLimitConn to wrap our existing net.Conn and continue using
+the wrapped version in its place.
+
+*/
+package rateio
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/rateio/limiter.go b/teleirc/matterbridge/vendor/github.com/shazow/rateio/limiter.go
new file mode 100644
index 0000000..a18ce3c
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/rateio/limiter.go
@@ -0,0 +1,71 @@
+package rateio
+
+import (
+ "errors"
+ "time"
+)
+
+const minInt = -int(^uint(0)>>1) - 1
+
+// ErrRateExceeded is the error returned when the read rate exceeds our specification.
+var ErrRateExceeded = errors.New("Read rate exceeded.")
+
+// Limiter is an interface for a rate limiter.
+// There are a few example limiters included in the package, but feel free to go wild with your own.
+type Limiter interface {
+ // Apply this many bytes to the limiter, return ErrRateExceeded if the defined rate is exceeded.
+ Count(int) error
+}
+
+// simpleLimiter is a rate limiter that restricts Amount bytes in Frequency duration.
+type simpleLimiter struct {
+ Amount int
+ Frequency time.Duration
+
+ numRead int
+ timeRead time.Time
+}
+
+// NewSimpleLimiter creates a Limiter that restricts a given number of bytes per frequency.
+func NewSimpleLimiter(amount int, frequency time.Duration) *simpleLimiter {
+ return &simpleLimiter{
+ Amount: amount,
+ Frequency: frequency,
+ }
+}
+
+// NewGracefulLimiter returns a Limiter that is the same as a
+// SimpleLimiter but adds a grace period at the start of the rate
+// limiting where it allows unlimited bytes to be read during that
+// period.
+func NewGracefulLimiter(amount int, frequency time.Duration, grace time.Duration) *simpleLimiter {
+ return &simpleLimiter{
+ Amount: amount,
+ Frequency: frequency,
+ numRead: minInt,
+ timeRead: time.Now().Add(grace),
+ }
+}
+
+// Count applies n bytes to the limiter.
+func (limit *simpleLimiter) Count(n int) error {
+ now := time.Now()
+ if now.After(limit.timeRead) {
+ limit.numRead = 0
+ limit.timeRead = now.Add(limit.Frequency)
+ }
+ limit.numRead += n
+ if limit.numRead > limit.Amount {
+ return ErrRateExceeded
+ }
+ return nil
+}
+
+// Delay returns a channel that can be used to block until next window
+func (limit *simpleLimiter) Delay() <-chan time.Time {
+ waitTill := time.Now()
+ if limit.numRead >= limit.Amount {
+ waitTill = waitTill.Add(limit.Frequency)
+ }
+ return time.NewTimer(time.Until(waitTill)).C
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/rateio/reader.go b/teleirc/matterbridge/vendor/github.com/shazow/rateio/reader.go
new file mode 100644
index 0000000..56bd8ff
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/rateio/reader.go
@@ -0,0 +1,25 @@
+package rateio
+
+import "io"
+
+type reader struct {
+ io.Reader
+ Limiter
+}
+
+// Read reads data into p.
+// Returns ErrRateExceeded error if our specified read is exceeded.
+func (r *reader) Read(p []byte) (n int, err error) {
+ n, err = r.Reader.Read(p)
+ if err != nil {
+ return
+ }
+
+ err = r.Limiter.Count(n)
+ return
+}
+
+// NewReader proxies an io.Reader but keeps track of bytes read based on our Limiter.
+func NewReader(r io.Reader, limiter Limiter) io.Reader {
+ return &reader{r, limiter}
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/rateio/writer.go b/teleirc/matterbridge/vendor/github.com/shazow/rateio/writer.go
new file mode 100644
index 0000000..d4feea4
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/rateio/writer.go
@@ -0,0 +1,25 @@
+package rateio
+
+import "io"
+
+type writer struct {
+ io.Writer
+ Limiter
+}
+
+// Write writes the contents of p into the buffer.
+// Returns ErrRateExceeded error if our specified read is exceeded.
+func (w *writer) Write(p []byte) (n int, err error) {
+ n, err = w.Writer.Write(p)
+ if err != nil {
+ return
+ }
+
+ err = w.Limiter.Count(n)
+ return
+}
+
+// NewWriter proxies an io.Writer but keeps track of bytes read based on our Limiter.
+func NewWriter(w io.Writer, limiter Limiter) io.Writer {
+ return &writer{w, limiter}
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/LICENSE b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/LICENSE
new file mode 100644
index 0000000..325b43a
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Andrey Petrov
+
+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/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/NOTICE b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/NOTICE
new file mode 100644
index 0000000..bde874c
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/NOTICE
@@ -0,0 +1,33 @@
+## x/crypto/ssh/terminal
+
+This project contains a fork of https://github.com/golang/crypto/tree/master/ssh/terminal
+under the sshd/terminal directory. The project's original license applies:
+
+
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/internal/sanitize/sanitize.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/internal/sanitize/sanitize.go
new file mode 100644
index 0000000..4f9775f
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/internal/sanitize/sanitize.go
@@ -0,0 +1,29 @@
+package sanitize
+
+import "regexp"
+
+var (
+ reStripName = regexp.MustCompile("[^\\w.-]")
+ reStripData = regexp.MustCompile("[^[:ascii:]]|[[:cntrl:]]")
+)
+
+const maxLength = 16
+
+// Name returns a name with only allowed characters and a reasonable length
+func Name(s string) string {
+ s = reStripName.ReplaceAllString(s, "")
+ nameLength := maxLength
+ if len(s) <= maxLength {
+ nameLength = len(s)
+ }
+ s = s[:nameLength]
+ return s
+}
+
+// Data returns a string with only allowed characters for client-provided metadata inputs.
+func Data(s string, maxlen int) string {
+ if len(s) > maxlen {
+ s = s[:maxlen]
+ }
+ return reStripData.ReplaceAllString(s, "")
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/auth.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/auth.go
new file mode 100644
index 0000000..a140413
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/auth.go
@@ -0,0 +1,74 @@
+package sshd
+
+import (
+ "crypto/sha256"
+ "encoding/base64"
+ "errors"
+ "net"
+
+ "github.com/shazow/ssh-chat/internal/sanitize"
+ "golang.org/x/crypto/ssh"
+)
+
+// Auth is used to authenticate connections based on public keys.
+type Auth interface {
+ // Whether to allow connections without a public key.
+ AllowAnonymous() bool
+ // Given address and public key and client agent string, returns nil if the connection should be allowed.
+ Check(net.Addr, ssh.PublicKey, string) error
+}
+
+// MakeAuth makes an ssh.ServerConfig which performs authentication against an Auth implementation.
+// TODO: Switch to using ssh.AuthMethod instead?
+func MakeAuth(auth Auth) *ssh.ServerConfig {
+ config := ssh.ServerConfig{
+ NoClientAuth: false,
+ // Auth-related things should be constant-time to avoid timing attacks.
+ PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
+ err := auth.Check(conn.RemoteAddr(), key, sanitize.Data(string(conn.ClientVersion()), 64))
+ if err != nil {
+ return nil, err
+ }
+ perm := &ssh.Permissions{Extensions: map[string]string{
+ "pubkey": string(key.Marshal()),
+ }}
+ return perm, nil
+ },
+ KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
+ if !auth.AllowAnonymous() {
+ return nil, errors.New("public key authentication required")
+ }
+ err := auth.Check(conn.RemoteAddr(), nil, sanitize.Data(string(conn.ClientVersion()), 64))
+ return nil, err
+ },
+ }
+
+ return &config
+}
+
+// MakeNoAuth makes a simple ssh.ServerConfig which allows all connections.
+// Primarily used for testing.
+func MakeNoAuth() *ssh.ServerConfig {
+ config := ssh.ServerConfig{
+ NoClientAuth: false,
+ // Auth-related things should be constant-time to avoid timing attacks.
+ PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
+ perm := &ssh.Permissions{Extensions: map[string]string{
+ "pubkey": string(key.Marshal()),
+ }}
+ return perm, nil
+ },
+ KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
+ return nil, nil
+ },
+ }
+
+ return &config
+}
+
+// Fingerprint performs a SHA256 BASE64 fingerprint of the PublicKey, similar to OpenSSH.
+// See: https://anongit.mindrot.org/openssh.git/commit/?id=56d1c83cdd1ac
+func Fingerprint(k ssh.PublicKey) string {
+ hash := sha256.Sum256(k.Marshal())
+ return "SHA256:" + base64.StdEncoding.EncodeToString(hash[:])
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/client.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/client.go
new file mode 100644
index 0000000..004aa47
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/client.go
@@ -0,0 +1,76 @@
+package sshd
+
+import (
+ "crypto/rand"
+ "crypto/rsa"
+ "io"
+
+ "golang.org/x/crypto/ssh"
+)
+
+// NewRandomSigner generates a random key of a desired bit length.
+func NewRandomSigner(bits int) (ssh.Signer, error) {
+ key, err := rsa.GenerateKey(rand.Reader, bits)
+ if err != nil {
+ return nil, err
+ }
+ return ssh.NewSignerFromKey(key)
+}
+
+// NewClientConfig creates a barebones ssh.ClientConfig to be used with ssh.Dial.
+func NewClientConfig(name string) *ssh.ClientConfig {
+ return &ssh.ClientConfig{
+ User: name,
+ Auth: []ssh.AuthMethod{
+ ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
+ return
+ }),
+ },
+ HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+ }
+}
+
+// ConnectShell makes a barebones SSH client session, used for testing.
+func ConnectShell(host string, name string, handler func(r io.Reader, w io.WriteCloser) error) error {
+ config := NewClientConfig(name)
+ conn, err := ssh.Dial("tcp", host, config)
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+
+ session, err := conn.NewSession()
+ if err != nil {
+ return err
+ }
+ defer session.Close()
+
+ in, err := session.StdinPipe()
+ if err != nil {
+ return err
+ }
+
+ out, err := session.StdoutPipe()
+ if err != nil {
+ return err
+ }
+
+ /*
+ err = session.RequestPty("xterm", 80, 40, ssh.TerminalModes{})
+ if err != nil {
+ return err
+ }
+ */
+
+ err = session.Shell()
+ if err != nil {
+ return err
+ }
+
+ _, err = session.SendRequest("ping", true, nil)
+ if err != nil {
+ return err
+ }
+
+ return handler(out, in)
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/doc.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/doc.go
new file mode 100644
index 0000000..21cd914
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/doc.go
@@ -0,0 +1,34 @@
+package sshd
+
+/*
+
+ signer, err := ssh.ParsePrivateKey(privateKey)
+
+ config := MakeNoAuth()
+ config.AddHostKey(signer)
+
+ s, err := ListenSSH("0.0.0.0:2022", config)
+ if err != nil {
+ // Handle opening socket error
+ }
+ defer s.Close()
+
+ terminals := s.ServeTerminal()
+
+ for term := range terminals {
+ go func() {
+ defer term.Close()
+ term.SetPrompt("...")
+ term.AutoCompleteCallback = nil // ...
+
+ for {
+ line, err := term.ReadLine()
+ if err != nil {
+ break
+ }
+ term.Write(...)
+ }
+
+ }()
+ }
+*/
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/logger.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/logger.go
new file mode 100644
index 0000000..c4ebc04
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/logger.go
@@ -0,0 +1,23 @@
+package sshd
+
+import "io"
+import stdlog "log"
+
+var logger *stdlog.Logger
+
+// SetLogger sets the package logging output to use w.
+func SetLogger(w io.Writer) {
+ flags := stdlog.Flags()
+ prefix := "[sshd] "
+ logger = stdlog.New(w, prefix, flags)
+}
+
+type nullWriter struct{}
+
+func (nullWriter) Write(data []byte) (int, error) {
+ return len(data), nil
+}
+
+func init() {
+ SetLogger(nullWriter{})
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/net.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/net.go
new file mode 100644
index 0000000..678454b
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/net.go
@@ -0,0 +1,75 @@
+package sshd
+
+import (
+ "net"
+ "time"
+
+ "github.com/shazow/rateio"
+ "golang.org/x/crypto/ssh"
+)
+
+// SSHListener is the container for the connection and ssh-related configuration
+type SSHListener struct {
+ net.Listener
+ config *ssh.ServerConfig
+
+ RateLimit func() rateio.Limiter
+ HandlerFunc func(term *Terminal)
+}
+
+// ListenSSH makes an SSH listener socket
+func ListenSSH(laddr string, config *ssh.ServerConfig) (*SSHListener, error) {
+ socket, err := net.Listen("tcp", laddr)
+ if err != nil {
+ return nil, err
+ }
+ l := SSHListener{Listener: socket, config: config}
+ return &l, nil
+}
+
+func (l *SSHListener) handleConn(conn net.Conn) (*Terminal, error) {
+ if l.RateLimit != nil {
+ // TODO: Configurable Limiter?
+ conn = ReadLimitConn(conn, l.RateLimit())
+ }
+
+ // If the connection doesn't write anything back for too long before we get
+ // a valid session, it should be dropped.
+ var handleTimeout = 20 * time.Second
+ conn.SetReadDeadline(time.Now().Add(handleTimeout))
+ defer conn.SetReadDeadline(time.Time{})
+
+ // Upgrade TCP connection to SSH connection
+ sshConn, channels, requests, err := ssh.NewServerConn(conn, l.config)
+ if err != nil {
+ return nil, err
+ }
+
+ // FIXME: Disconnect if too many faulty requests? (Avoid DoS.)
+ go ssh.DiscardRequests(requests)
+ return NewSession(sshConn, channels)
+}
+
+// Serve Accepts incoming connections as terminal requests and yield them
+func (l *SSHListener) Serve() {
+ defer l.Close()
+ for {
+ conn, err := l.Accept()
+
+ if err != nil {
+ logger.Printf("Failed to accept connection: %s", err)
+ break
+ }
+
+ // Goroutineify to resume accepting sockets early
+ go func() {
+ term, err := l.handleConn(conn)
+ if err != nil {
+ logger.Printf("[%s] Failed to handshake: %s", conn.RemoteAddr(), err)
+ conn.Close() // Must be closed to avoid a leak
+ return
+ }
+ l.HandlerFunc(term)
+ }()
+ }
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/pty.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/pty.go
new file mode 100644
index 0000000..d3ed9ca
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/pty.go
@@ -0,0 +1,70 @@
+package sshd
+
+import "encoding/binary"
+
+// Helpers below are borrowed from go.crypto circa 2011:
+
+// parsePtyRequest parses the payload of the pty-req message and extracts the
+// dimensions of the terminal. See RFC 4254, section 6.2.
+func parsePtyRequest(s []byte) (term string, width, height int, ok bool) {
+ term, s, ok = parseString(s)
+ if !ok {
+ return
+ }
+ width32, s, ok := parseUint32(s)
+ if !ok {
+ return
+ }
+ height32, _, ok := parseUint32(s)
+ width = int(width32)
+ height = int(height32)
+ if width < 1 {
+ ok = false
+ }
+ if height < 1 {
+ ok = false
+ }
+ return
+}
+
+func parseWinchRequest(s []byte) (width, height int, ok bool) {
+ width32, _, ok := parseUint32(s)
+ if !ok {
+ return
+ }
+ height32, _, ok := parseUint32(s)
+ if !ok {
+ return
+ }
+
+ width = int(width32)
+ height = int(height32)
+ if width < 1 {
+ ok = false
+ }
+ if height < 1 {
+ ok = false
+ }
+ return
+}
+
+func parseString(in []byte) (out string, rest []byte, ok bool) {
+ if len(in) < 4 {
+ return
+ }
+ length := binary.BigEndian.Uint32(in)
+ if uint32(len(in)) < 4+length {
+ return
+ }
+ out = string(in[4 : 4+length])
+ rest = in[4+length:]
+ ok = true
+ return
+}
+
+func parseUint32(in []byte) (uint32, []byte, bool) {
+ if len(in) < 4 {
+ return 0, nil, false
+ }
+ return binary.BigEndian.Uint32(in), in[4:], true
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go
new file mode 100644
index 0000000..b2607e6
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go
@@ -0,0 +1,71 @@
+package sshd
+
+import (
+ "io"
+ "net"
+ "time"
+
+ "github.com/shazow/rateio"
+)
+
+type limitedConn struct {
+ net.Conn
+ io.Reader // Our rate-limited io.Reader for net.Conn
+}
+
+func (r *limitedConn) Read(p []byte) (n int, err error) {
+ return r.Reader.Read(p)
+}
+
+// ReadLimitConn returns a net.Conn whose io.Reader interface is rate-limited by limiter.
+func ReadLimitConn(conn net.Conn, limiter rateio.Limiter) net.Conn {
+ return &limitedConn{
+ Conn: conn,
+ Reader: rateio.NewReader(conn, limiter),
+ }
+}
+
+// Count each read as 1 unless it exceeds some number of bytes.
+type inputLimiter struct {
+ // TODO: Could do all kinds of fancy things here, like be more forgiving of
+ // connections that have been around for a while.
+
+ Amount int
+ Frequency time.Duration
+
+ remaining int
+ readCap int
+ numRead int
+ timeRead time.Time
+}
+
+// NewInputLimiter returns a rateio.Limiter with sensible defaults for
+// differentiating between humans typing and bots spamming.
+func NewInputLimiter() rateio.Limiter {
+ grace := time.Second * 3
+ return &inputLimiter{
+ Amount: 2 << 14, // ~16kb, should be plenty for a high typing rate/copypasta/large key handshakes.
+ Frequency: time.Minute * 1,
+ readCap: 128, // Allow up to 128 bytes per read (anecdotally, 1 character = 52 bytes over ssh)
+ numRead: -1024 * 1024, // Start with a 1mb grace
+ timeRead: time.Now().Add(grace),
+ }
+}
+
+// Count applies 1 if n<readCap, else n
+func (limit *inputLimiter) Count(n int) error {
+ now := time.Now()
+ if now.After(limit.timeRead) {
+ limit.numRead = 0
+ limit.timeRead = now.Add(limit.Frequency)
+ }
+ if n <= limit.readCap {
+ limit.numRead += 1
+ } else {
+ limit.numRead += n
+ }
+ if limit.numRead > limit.Amount {
+ return rateio.ErrRateExceeded
+ }
+ return nil
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal.go
new file mode 100644
index 0000000..e8d9901
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal.go
@@ -0,0 +1,246 @@
+package sshd
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/shazow/ssh-chat/sshd/terminal"
+ "golang.org/x/crypto/ssh"
+)
+
+var keepaliveInterval = time.Second * 30
+var keepaliveRequest = "keepalive@ssh-chat"
+
+// ErrNoSessionChannel is returned when there is no session channel.
+var ErrNoSessionChannel = errors.New("no session channel")
+
+// ErrNotSessionChannel is returned when a channel is not a session channel.
+var ErrNotSessionChannel = errors.New("terminal requires session channel")
+
+// Connection is an interface with fields necessary to operate an sshd host.
+type Connection interface {
+ PublicKey() ssh.PublicKey
+ RemoteAddr() net.Addr
+ Name() string
+ ClientVersion() []byte
+ Close() error
+}
+
+type sshConn struct {
+ *ssh.ServerConn
+}
+
+func (c sshConn) PublicKey() ssh.PublicKey {
+ if c.Permissions == nil {
+ return nil
+ }
+
+ s, ok := c.Permissions.Extensions["pubkey"]
+ if !ok {
+ return nil
+ }
+
+ key, err := ssh.ParsePublicKey([]byte(s))
+ if err != nil {
+ return nil
+ }
+
+ return key
+}
+
+func (c sshConn) Name() string {
+ return c.User()
+}
+
+// EnvVar is an environment variable key-value pair
+type EnvVar struct {
+ Key string
+ Value string
+}
+
+func (v EnvVar) String() string {
+ return v.Key + "=" + v.Value
+}
+
+// Env is a wrapper type around []EnvVar with some helper methods
+type Env []EnvVar
+
+// Get returns the latest value for a given key, or empty string if not found
+func (e Env) Get(key string) string {
+ for i := len(e) - 1; i >= 0; i-- {
+ if e[i].Key == key {
+ return e[i].Value
+ }
+ }
+ return ""
+}
+
+// Terminal extends ssh/terminal to include a close method
+type Terminal struct {
+ terminal.Terminal
+ Conn Connection
+ Channel ssh.Channel
+
+ done chan struct{}
+ closeOnce sync.Once
+
+ mu sync.Mutex
+ env []EnvVar
+ term string
+}
+
+// Make new terminal from a session channel
+// TODO: For v2, make a separate `Serve(ctx context.Context) error` method to activate the Terminal
+func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) {
+ if ch.ChannelType() != "session" {
+ return nil, ErrNotSessionChannel
+ }
+ channel, requests, err := ch.Accept()
+ if err != nil {
+ return nil, err
+ }
+ term := Terminal{
+ Terminal: *terminal.NewTerminal(channel, ""),
+ Conn: sshConn{conn},
+ Channel: channel,
+
+ done: make(chan struct{}),
+ }
+
+ ready := make(chan struct{})
+ go term.listen(requests, ready)
+
+ go func() {
+ // Keep-Alive Ticker
+ ticker := time.Tick(keepaliveInterval)
+ for {
+ select {
+ case <-ticker:
+ _, err := channel.SendRequest(keepaliveRequest, true, nil)
+ if err != nil {
+ // Connection is gone
+ logger.Printf("[%s] Keepalive failed, closing terminal: %s", term.Conn.RemoteAddr(), err)
+ term.Close()
+ return
+ }
+ case <-term.done:
+ return
+ }
+ }
+ }()
+
+ // We need to wait for term.ready to acquire a shell before we return, this
+ // gives the SSH session a chance to populate the env vars and other state.
+ // TODO: Make the timeout configurable
+ // TODO: Use context.Context for abort/timeout in the future, will need to change the API.
+ select {
+ case <-ready: // shell acquired
+ return &term, nil
+ case <-term.done:
+ return nil, errors.New("terminal aborted")
+ case <-time.NewTimer(time.Minute).C:
+ return nil, errors.New("timed out starting terminal")
+ }
+}
+
+// NewSession Finds a session channel and make a Terminal from it
+func NewSession(conn *ssh.ServerConn, channels <-chan ssh.NewChannel) (*Terminal, error) {
+ // Make a terminal from the first session found
+ for ch := range channels {
+ if t := ch.ChannelType(); t != "session" {
+ logger.Printf("[%s] Ignored channel type: %s", conn.RemoteAddr(), t)
+ ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
+ continue
+ }
+
+ return NewTerminal(conn, ch)
+ }
+
+ return nil, ErrNoSessionChannel
+}
+
+// Close terminal and ssh connection
+func (t *Terminal) Close() error {
+ var err error
+ t.closeOnce.Do(func() {
+ close(t.done)
+ t.Channel.Close()
+ err = t.Conn.Close()
+ })
+ return err
+}
+
+// listen negotiates the terminal type and state
+// ready is closed when the terminal is ready.
+func (t *Terminal) listen(requests <-chan *ssh.Request, ready chan<- struct{}) {
+ hasShell := false
+
+ for req := range requests {
+ var width, height int
+ var ok bool
+
+ switch req.Type {
+ case "shell":
+ if !hasShell {
+ ok = true
+ hasShell = true
+ close(ready)
+ }
+ case "pty-req":
+ var term string
+ term, width, height, ok = parsePtyRequest(req.Payload)
+ if ok {
+ // TODO: Hardcode width to 100000?
+ err := t.SetSize(width, height)
+ ok = err == nil
+ // Save the term:
+ t.mu.Lock()
+ t.term = term
+ t.mu.Unlock()
+ }
+ case "window-change":
+ width, height, ok = parseWinchRequest(req.Payload)
+ if ok {
+ // TODO: Hardcode width to 100000?
+ err := t.SetSize(width, height)
+ ok = err == nil
+ }
+ case "env":
+ var v EnvVar
+ if err := ssh.Unmarshal(req.Payload, &v); err == nil {
+ t.mu.Lock()
+ t.env = append(t.env, v)
+ t.mu.Unlock()
+ ok = true
+ }
+ }
+
+ if req.WantReply {
+ req.Reply(ok, nil)
+ }
+ }
+}
+
+// Env returns a list of environment key-values that have been set. They are
+// returned in the order that they have been set, there is no deduplication or
+// other pre-processing applied.
+func (t *Terminal) Env() Env {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ return Env(t.env)
+}
+
+// Term returns the terminal string value as set by the pty.
+// If there was no pty request, it falls back to the TERM value passed in as an
+// Env variable.
+func (t *Terminal) Term() string {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.term != "" {
+ return t.term
+ }
+ return Env(t.env).Get("TERM")
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/LICENSE b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go
new file mode 100644
index 0000000..ec04e8f
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go
@@ -0,0 +1,1027 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package terminal
+
+import (
+ "bytes"
+ "io"
+ "strconv"
+ "sync"
+ "unicode/utf8"
+
+ "golang.org/x/text/width"
+)
+
+// EscapeCodes contains escape sequences that can be written to the terminal in
+// order to achieve different styles of text.
+type EscapeCodes struct {
+ // Foreground colors
+ Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
+
+ // Reset all attributes
+ Reset []byte
+}
+
+var vt100EscapeCodes = EscapeCodes{
+ Black: []byte{keyEscape, '[', '3', '0', 'm'},
+ Red: []byte{keyEscape, '[', '3', '1', 'm'},
+ Green: []byte{keyEscape, '[', '3', '2', 'm'},
+ Yellow: []byte{keyEscape, '[', '3', '3', 'm'},
+ Blue: []byte{keyEscape, '[', '3', '4', 'm'},
+ Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
+ Cyan: []byte{keyEscape, '[', '3', '6', 'm'},
+ White: []byte{keyEscape, '[', '3', '7', 'm'},
+
+ Reset: []byte{keyEscape, '[', '0', 'm'},
+}
+
+// Terminal contains the state for running a VT100 terminal that is capable of
+// reading lines of input.
+type Terminal struct {
+ // AutoCompleteCallback, if non-null, is called for each keypress with
+ // the full input line and the current position of the cursor (in
+ // bytes, as an index into |line|). If it returns ok=false, the key
+ // press is processed normally. Otherwise it returns a replacement line
+ // and the new cursor position.
+ AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
+
+ // Escape contains a pointer to the escape codes for this terminal.
+ // It's always a valid pointer, although the escape codes themselves
+ // may be empty if the terminal doesn't support them.
+ Escape *EscapeCodes
+
+ // lock protects the terminal and the state in this object from
+ // concurrent processing of a key press and a Write() call.
+ lock sync.Mutex
+
+ c io.ReadWriter
+ prompt []rune
+
+ // line is the current line being entered.
+ line []rune
+ // pos is the logical position of the cursor in line
+ pos int
+ // echo is true if local echo is enabled
+ echo bool
+ // pasteActive is true iff there is a bracketed paste operation in
+ // progress.
+ pasteActive bool
+
+ // cursorX contains the current X value of the cursor where the left
+ // edge is 0. cursorY contains the row number where the first row of
+ // the current line is 0.
+ cursorX, cursorY int
+ // maxLine is the greatest value of cursorY so far.
+ maxLine int
+
+ termWidth, termHeight int
+
+ // outBuf contains the terminal data to be sent.
+ outBuf []byte
+ // remainder contains the remainder of any partial key sequences after
+ // a read. It aliases into inBuf.
+ remainder []byte
+ inBuf [256]byte
+
+ // history contains previously entered commands so that they can be
+ // accessed with the up and down keys.
+ history stRingBuffer
+ // historyIndex stores the currently accessed history entry, where zero
+ // means the immediately previous entry.
+ historyIndex int
+ // When navigating up and down the history it's possible to return to
+ // the incomplete, initial line. That value is stored in
+ // historyPending.
+ historyPending string
+
+ // enterClear will clear the input line on enter, instead of printing a
+ // new line after the input. It's useful for replacing the input with
+ // something else without echoing it.
+ enterClear bool
+}
+
+// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
+// a local terminal, that terminal must first have been put into raw mode.
+// prompt is a string that is written at the start of each input line (i.e.
+// "> ").
+func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
+ return &Terminal{
+ Escape: &vt100EscapeCodes,
+ c: c,
+ prompt: []rune(prompt),
+ termWidth: 80,
+ termHeight: 24,
+ echo: true,
+ historyIndex: -1,
+ }
+}
+
+const (
+ keyCtrlD = 4
+ keyCtrlU = 21
+ keyEnter = '\r'
+ keyEscape = 27
+ keyBackspace = 127
+ keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota
+ keyUp
+ keyDown
+ keyLeft
+ keyRight
+ keyAltLeft
+ keyAltRight
+ keyAltF
+ keyAltB
+ keyHome
+ keyEnd
+ keyDeleteWord
+ keyDeleteLine
+ keyClearScreen
+ keyPasteStart
+ keyPasteEnd
+)
+
+var (
+ crlf = []byte{'\r', '\n'}
+ pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
+ pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'}
+)
+
+// bytesToKey tries to parse a key sequence from b. If successful, it returns
+// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
+func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
+ if len(b) == 0 {
+ return utf8.RuneError, nil
+ }
+
+ if !pasteActive {
+ switch b[0] {
+ case 1: // ^A
+ return keyHome, b[1:]
+ case 2: // ^B
+ return keyLeft, b[1:]
+ case 5: // ^E
+ return keyEnd, b[1:]
+ case 6: // ^F
+ return keyRight, b[1:]
+ case 8: // ^H
+ return keyBackspace, b[1:]
+ case 11: // ^K
+ return keyDeleteLine, b[1:]
+ case 12: // ^L
+ return keyClearScreen, b[1:]
+ case 23: // ^W
+ return keyDeleteWord, b[1:]
+ case 14: // ^N
+ return keyDown, b[1:]
+ case 16: // ^P
+ return keyUp, b[1:]
+ }
+ }
+
+ if b[0] != keyEscape {
+ if !utf8.FullRune(b) {
+ return utf8.RuneError, b
+ }
+ r, l := utf8.DecodeRune(b)
+ return r, b[l:]
+ }
+
+ if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
+ switch b[2] {
+ case 'A':
+ return keyUp, b[3:]
+ case 'B':
+ return keyDown, b[3:]
+ case 'C':
+ return keyRight, b[3:]
+ case 'D':
+ return keyLeft, b[3:]
+ case 'H':
+ return keyHome, b[3:]
+ case 'F':
+ return keyEnd, b[3:]
+ }
+ }
+
+ if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
+ switch b[5] {
+ case 'C':
+ return keyAltRight, b[6:]
+ case 'D':
+ return keyAltLeft, b[6:]
+ }
+ }
+
+ if !pasteActive && len(b) >= 2 && b[0] == keyEscape {
+ switch b[1] {
+ case 'f':
+ return keyAltF, b[2:]
+ case 'b':
+ return keyAltB, b[2:]
+ }
+ }
+
+ if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
+ return keyPasteStart, b[6:]
+ }
+
+ if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
+ return keyPasteEnd, b[6:]
+ }
+
+ // If we get here then we have a key that we don't recognise, or a
+ // partial sequence. It's not clear how one should find the end of a
+ // sequence without knowing them all, but it seems that [a-zA-Z~] only
+ // appears at the end of a sequence.
+ for i, c := range b[0:] {
+ if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
+ return keyUnknown, b[i+1:]
+ }
+ }
+
+ return utf8.RuneError, b
+}
+
+// queue appends data to the end of t.outBuf
+func (t *Terminal) queue(data []rune) {
+ t.outBuf = append(t.outBuf, []byte(string(data))...)
+}
+
+var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
+var space = []rune{' '}
+
+func isPrintable(key rune) bool {
+ isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
+ return key >= 32 && !isInSurrogateArea
+}
+
+// moveCursorToPos appends data to t.outBuf which will move the cursor to the
+// given, logical position in the text.
+func (t *Terminal) moveCursorToPos(pos int) {
+ if !t.echo {
+ return
+ }
+
+ x := visualLength(t.prompt) + visualLength(t.line[:pos])
+ y := x / t.termWidth
+ x = x % t.termWidth
+
+ up := 0
+ if y < t.cursorY {
+ up = t.cursorY - y
+ }
+
+ down := 0
+ if y > t.cursorY {
+ down = y - t.cursorY
+ }
+
+ left := 0
+ if x < t.cursorX {
+ left = t.cursorX - x
+ }
+
+ right := 0
+ if x > t.cursorX {
+ right = x - t.cursorX
+ }
+
+ t.cursorX = x
+ t.cursorY = y
+ t.move(up, down, left, right)
+}
+
+func (t *Terminal) move(up, down, left, right int) {
+ m := []rune{}
+
+ // 1 unit up can be expressed as ^[A
+ // 5 units up can be expressed as ^[[5A
+
+ if up == 1 {
+ m = append(m, keyEscape, '[', 'A')
+ } else if up > 1 {
+ m = append(m, keyEscape, '[')
+ m = append(m, []rune(strconv.Itoa(up))...)
+ m = append(m, 'A')
+ }
+
+ if down == 1 {
+ m = append(m, keyEscape, '[', 'B')
+ } else if down > 1 {
+ m = append(m, keyEscape, '[')
+ m = append(m, []rune(strconv.Itoa(down))...)
+ m = append(m, 'B')
+ }
+
+ if right == 1 {
+ m = append(m, keyEscape, '[', 'C')
+ } else if right > 1 {
+ m = append(m, keyEscape, '[')
+ m = append(m, []rune(strconv.Itoa(right))...)
+ m = append(m, 'C')
+ }
+
+ if left == 1 {
+ m = append(m, keyEscape, '[', 'D')
+ } else if left > 1 {
+ m = append(m, keyEscape, '[')
+ m = append(m, []rune(strconv.Itoa(left))...)
+ m = append(m, 'D')
+ }
+
+ t.queue(m)
+}
+
+func (t *Terminal) clearLineToRight() {
+ op := []rune{keyEscape, '[', 'K'}
+ t.queue(op)
+}
+
+func (t *Terminal) clearScreenBelow() {
+ op := []rune{keyEscape, '[', 'J'}
+ t.queue(op)
+}
+
+const maxLineLength = 4096
+
+func (t *Terminal) setLine(newLine []rune, newPos int) {
+ if t.echo {
+ t.moveCursorToPos(0)
+ t.writeLine(newLine)
+ for i := len(newLine); i < len(t.line); i++ {
+ t.writeLine(space)
+ }
+ t.line = newLine
+ t.moveCursorToPos(newPos)
+ }
+ t.line = newLine
+ t.pos = newPos
+}
+
+func (t *Terminal) advanceCursor(places int) {
+ t.cursorX += places
+ t.cursorY += t.cursorX / t.termWidth
+ if t.cursorY > t.maxLine {
+ t.maxLine = t.cursorY
+ }
+ t.cursorX = t.cursorX % t.termWidth
+
+ if places > 0 && t.cursorX == 0 {
+ // Normally terminals will advance the current position
+ // when writing a character. But that doesn't happen
+ // for the last character in a line. However, when
+ // writing a character (except a new line) that causes
+ // a line wrap, the position will be advanced two
+ // places.
+ //
+ // So, if we are stopping at the end of a line, we
+ // need to write a newline so that our cursor can be
+ // advanced to the next line.
+ t.outBuf = append(t.outBuf, '\r', '\n')
+ }
+}
+
+func (t *Terminal) eraseNPreviousChars(n int) {
+ if n == 0 {
+ return
+ }
+
+ if t.pos < n {
+ n = t.pos
+ }
+ t.pos -= n
+ t.moveCursorToPos(t.pos)
+
+ copy(t.line[t.pos:], t.line[n+t.pos:])
+ t.line = t.line[:len(t.line)-n]
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ for i := 0; i < n; i++ {
+ t.queue(space)
+ }
+ t.advanceCursor(n)
+ t.moveCursorToPos(t.pos)
+ }
+}
+
+// countToLeftWord returns then number of characters from the cursor to the
+// start of the previous word.
+func (t *Terminal) countToLeftWord() int {
+ if t.pos == 0 {
+ return 0
+ }
+
+ pos := t.pos - 1
+ for pos > 0 {
+ if t.line[pos] != ' ' {
+ break
+ }
+ pos--
+ }
+ for pos > 0 {
+ if t.line[pos] == ' ' {
+ pos++
+ break
+ }
+ pos--
+ }
+
+ return t.pos - pos
+}
+
+// countToRightWord returns then number of characters from the cursor to the
+// start of the next word.
+func (t *Terminal) countToRightWord() int {
+ pos := t.pos
+ for pos < len(t.line) {
+ if t.line[pos] == ' ' {
+ break
+ }
+ pos++
+ }
+ for pos < len(t.line) {
+ if t.line[pos] != ' ' {
+ break
+ }
+ pos++
+ }
+ return pos - t.pos
+}
+
+// visualLength returns the number of visible glyphs in s.
+func visualLength(runes []rune) int {
+ inEscapeSeq := false
+ length := 0
+
+ for _, r := range runes {
+ switch {
+ case inEscapeSeq:
+ if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
+ inEscapeSeq = false
+ }
+ case r == '\x1b':
+ inEscapeSeq = true
+ default:
+ length++
+ kind := width.LookupRune(r).Kind()
+ if kind == width.EastAsianFullwidth || kind == width.EastAsianWide {
+ length++
+ }
+ }
+ }
+
+ return length
+}
+
+// handleKey processes the given key and, optionally, returns a line of text
+// that the user has entered.
+func (t *Terminal) handleKey(key rune) (line string, ok bool) {
+ if t.pasteActive && key != keyEnter {
+ t.addKeyToLine(key)
+ return
+ }
+
+ switch key {
+ case keyBackspace:
+ if t.pos == 0 {
+ return
+ }
+ t.eraseNPreviousChars(1)
+ case keyAltB:
+ fallthrough
+ case keyAltLeft:
+ // move left by a word.
+ t.pos -= t.countToLeftWord()
+ t.moveCursorToPos(t.pos)
+ case keyAltF:
+ fallthrough
+ case keyAltRight:
+ // move right by a word.
+ t.pos += t.countToRightWord()
+ t.moveCursorToPos(t.pos)
+ case keyLeft:
+ if t.pos == 0 {
+ return
+ }
+ t.pos--
+ t.moveCursorToPos(t.pos)
+ case keyRight:
+ if t.pos == len(t.line) {
+ return
+ }
+ t.pos++
+ t.moveCursorToPos(t.pos)
+ case keyHome:
+ if t.pos == 0 {
+ return
+ }
+ t.pos = 0
+ t.moveCursorToPos(t.pos)
+ case keyEnd:
+ if t.pos == len(t.line) {
+ return
+ }
+ t.pos = len(t.line)
+ t.moveCursorToPos(t.pos)
+ case keyUp:
+ entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
+ if !ok {
+ return "", false
+ }
+ if t.historyIndex == -1 {
+ t.historyPending = string(t.line)
+ }
+ t.historyIndex++
+ runes := []rune(entry)
+ t.setLine(runes, len(runes))
+ case keyDown:
+ switch t.historyIndex {
+ case -1:
+ return
+ case 0:
+ runes := []rune(t.historyPending)
+ t.setLine(runes, len(runes))
+ t.historyIndex--
+ default:
+ entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
+ if ok {
+ t.historyIndex--
+ runes := []rune(entry)
+ t.setLine(runes, len(runes))
+ }
+ }
+ case keyEnter:
+ line = string(t.line)
+ if t.enterClear {
+ // Clear line on enter instead of starting a new line.
+ reset := []rune{
+ // Clear whole line
+ keyEscape, '[', '2', 'K',
+ // Clear screen below (for any wrapped lines)
+ keyEscape, '[', 'J',
+ }
+ t.queue(reset)
+ // FIXME: This causes a slice out of bounds panic, but without it we
+ // get an offset with emojis
+ // t.cursorX += len(reset)
+ } else {
+ // Pushing the line up resets the cursor to 0,0 and we render a
+ // fresh prompt.
+ t.moveCursorToPos(len(t.line))
+ t.queue([]rune("\r\n"))
+ t.cursorX = 0
+ t.cursorY = 0
+ }
+ ok = true
+ t.line = t.line[:0]
+ t.pos = 0
+ t.maxLine = 0
+ case keyDeleteWord:
+ // Delete zero or more spaces and then one or more characters.
+ t.eraseNPreviousChars(t.countToLeftWord())
+ case keyDeleteLine:
+ // Delete everything from the current cursor position to the
+ // end of line.
+ for i := t.pos; i < len(t.line); i++ {
+ t.queue(space)
+ t.advanceCursor(1)
+ }
+ t.line = t.line[:t.pos]
+ t.moveCursorToPos(t.pos)
+ case keyCtrlD:
+ // Erase the character under the current position.
+ // The EOF case when the line is empty is handled in
+ // readLine().
+ if t.pos < len(t.line) {
+ t.pos++
+ t.eraseNPreviousChars(1)
+ }
+ case keyCtrlU:
+ t.eraseNPreviousChars(t.pos)
+ case keyClearScreen:
+ // Erases the screen and moves the cursor to the home position.
+ t.queue([]rune("\x1b[2J\x1b[H"))
+ t.queue(t.prompt)
+ t.cursorX, t.cursorY = 0, 0
+ t.advanceCursor(visualLength(t.prompt))
+ t.setLine(t.line, t.pos)
+ default:
+ if t.AutoCompleteCallback != nil {
+ prefix := string(t.line[:t.pos])
+ suffix := string(t.line[t.pos:])
+
+ t.lock.Unlock()
+ newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
+ t.lock.Lock()
+
+ if completeOk {
+ t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
+ return
+ }
+ }
+ if !isPrintable(key) {
+ return
+ }
+ if len(t.line) == maxLineLength {
+ return
+ }
+ t.addKeyToLine(key)
+ }
+ return
+}
+
+// addKeyToLine inserts the given key at the current position in the current
+// line.
+func (t *Terminal) addKeyToLine(key rune) {
+ if len(t.line) == cap(t.line) {
+ newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
+ copy(newLine, t.line)
+ t.line = newLine
+ }
+ t.line = t.line[:len(t.line)+1]
+ copy(t.line[t.pos+1:], t.line[t.pos:])
+ t.line[t.pos] = key
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ }
+ t.pos++
+ t.moveCursorToPos(t.pos)
+}
+
+func (t *Terminal) writeLine(line []rune) {
+ for len(line) != 0 {
+ remainingOnLine := t.termWidth - t.cursorX
+ todo := len(line)
+ if todo > remainingOnLine {
+ todo = remainingOnLine
+ }
+ t.queue(line[:todo])
+ t.advanceCursor(visualLength(line[:todo]))
+ line = line[todo:]
+ }
+}
+
+// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
+func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
+ for len(buf) > 0 {
+ i := bytes.IndexByte(buf, '\n')
+ todo := len(buf)
+ if i >= 0 {
+ todo = i
+ }
+
+ var nn int
+ nn, err = w.Write(buf[:todo])
+ n += nn
+ if err != nil {
+ return n, err
+ }
+ buf = buf[todo:]
+
+ if i >= 0 {
+ if _, err = w.Write(crlf); err != nil {
+ return n, err
+ }
+ n++
+ buf = buf[1:]
+ }
+ }
+
+ return n, nil
+}
+
+func (t *Terminal) Write(buf []byte) (n int, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if t.cursorX == 0 && t.cursorY == 0 {
+ // This is the easy case: there's nothing on the screen that we
+ // have to move out of the way.
+ return writeWithCRLF(t.c, buf)
+ }
+
+ // We have a prompt and possibly user input on the screen. We
+ // have to clear it first.
+ t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
+ t.cursorX = 0
+ t.clearLineToRight()
+
+ for t.cursorY > 0 {
+ t.move(1 /* up */, 0, 0, 0)
+ t.cursorY--
+ t.clearLineToRight()
+ }
+
+ if _, err = t.c.Write(t.outBuf); err != nil {
+ return
+ }
+ t.outBuf = t.outBuf[:0]
+
+ if n, err = writeWithCRLF(t.c, buf); err != nil {
+ return
+ }
+
+ t.writeLine(t.prompt)
+ if t.echo {
+ t.writeLine(t.line)
+ }
+
+ t.moveCursorToPos(t.pos)
+
+ if _, err = t.c.Write(t.outBuf); err != nil {
+ return
+ }
+ t.outBuf = t.outBuf[:0]
+ return
+}
+
+// ReadPassword temporarily changes the prompt and reads a password, without
+// echo, from the terminal.
+func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ oldPrompt := t.prompt
+ t.prompt = []rune(prompt)
+ t.echo = false
+
+ line, err = t.readLine()
+
+ t.prompt = oldPrompt
+ t.echo = true
+
+ return
+}
+
+// ReadLine returns a line of input from the terminal.
+func (t *Terminal) ReadLine() (line string, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ return t.readLine()
+}
+
+func (t *Terminal) readLine() (line string, err error) {
+ // t.lock must be held at this point
+
+ if t.cursorX == 0 && t.cursorY == 0 {
+ t.writeLine(t.prompt)
+ t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ }
+
+ lineIsPasted := t.pasteActive
+
+ for {
+ rest := t.remainder
+ lineOk := false
+ for !lineOk {
+ var key rune
+ key, rest = bytesToKey(rest, t.pasteActive)
+ if key == utf8.RuneError {
+ break
+ }
+ if !t.pasteActive {
+ if key == keyCtrlD {
+ if len(t.line) == 0 {
+ return "", io.EOF
+ }
+ }
+ if key == keyPasteStart {
+ t.pasteActive = true
+ if len(t.line) == 0 {
+ lineIsPasted = true
+ }
+ continue
+ }
+ } else if key == keyPasteEnd {
+ t.pasteActive = false
+ continue
+ }
+ if !t.pasteActive {
+ lineIsPasted = false
+ }
+ line, lineOk = t.handleKey(key)
+ }
+ if len(rest) > 0 {
+ n := copy(t.inBuf[:], rest)
+ t.remainder = t.inBuf[:n]
+ } else {
+ t.remainder = nil
+ }
+ t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ if lineOk {
+ if t.echo {
+ t.historyIndex = -1
+ t.history.Add(line)
+ }
+ if lineIsPasted {
+ err = ErrPasteIndicator
+ }
+ return
+ }
+
+ // t.remainder is a slice at the beginning of t.inBuf
+ // containing a partial key sequence
+ readBuf := t.inBuf[len(t.remainder):]
+ var n int
+
+ t.lock.Unlock()
+ n, err = t.c.Read(readBuf)
+ t.lock.Lock()
+
+ if err != nil {
+ return
+ }
+
+ t.remainder = t.inBuf[:n+len(t.remainder)]
+ }
+}
+
+// SetPrompt sets the prompt to be used when reading subsequent lines.
+func (t *Terminal) SetPrompt(prompt string) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ t.prompt = []rune(prompt)
+}
+
+func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
+ // Move cursor to column zero at the start of the line.
+ t.move(t.cursorY, 0, t.cursorX, 0)
+ t.cursorX, t.cursorY = 0, 0
+ t.clearLineToRight()
+ for t.cursorY < numPrevLines {
+ // Move down a line
+ t.move(0, 1, 0, 0)
+ t.cursorY++
+ t.clearLineToRight()
+ }
+ // Move back to beginning.
+ t.move(t.cursorY, 0, 0, 0)
+ t.cursorX, t.cursorY = 0, 0
+
+ t.queue(t.prompt)
+ t.advanceCursor(visualLength(t.prompt))
+ t.writeLine(t.line)
+ t.moveCursorToPos(t.pos)
+}
+
+func (t *Terminal) SetSize(width, height int) error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if width == 0 {
+ width = 1
+ }
+
+ oldWidth := t.termWidth
+ t.termWidth, t.termHeight = width, height
+
+ switch {
+ case width == oldWidth:
+ // If the width didn't change then nothing else needs to be
+ // done.
+ return nil
+ case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
+ // If there is nothing on current line and no prompt printed,
+ // just do nothing
+ return nil
+ case width < oldWidth:
+ // Some terminals (e.g. xterm) will truncate lines that were
+ // too long when shinking. Others, (e.g. gnome-terminal) will
+ // attempt to wrap them. For the former, repainting t.maxLine
+ // works great, but that behaviour goes badly wrong in the case
+ // of the latter because they have doubled every full line.
+
+ // We assume that we are working on a terminal that wraps lines
+ // and adjust the cursor position based on every previous line
+ // wrapping and turning into two. This causes the prompt on
+ // xterms to move upwards, which isn't great, but it avoids a
+ // huge mess with gnome-terminal.
+ if t.cursorX >= t.termWidth {
+ t.cursorX = t.termWidth - 1
+ }
+ t.cursorY *= 2
+ t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
+ case width > oldWidth:
+ // If the terminal expands then our position calculations will
+ // be wrong in the future because we think the cursor is
+ // |t.pos| chars into the string, but there will be a gap at
+ // the end of any wrapped line.
+ //
+ // But the position will actually be correct until we move, so
+ // we can move back to the beginning and repaint everything.
+ t.clearAndRepaintLinePlusNPrevious(t.maxLine)
+ }
+
+ _, err := t.c.Write(t.outBuf)
+ t.outBuf = t.outBuf[:0]
+ return err
+}
+
+// SetEnterClear sets whether the input line should be cleared or echoed on
+// enter.
+func (t *Terminal) SetEnterClear(on bool) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ t.enterClear = on
+}
+
+type pasteIndicatorError struct{}
+
+func (pasteIndicatorError) Error() string {
+ return "terminal: ErrPasteIndicator not correctly handled"
+}
+
+// ErrPasteIndicator may be returned from ReadLine as the error, in addition
+// to valid line data. It indicates that bracketed paste mode is enabled and
+// that the returned line consists only of pasted data. Programs may wish to
+// interpret pasted data more literally than typed data.
+var ErrPasteIndicator = pasteIndicatorError{}
+
+// SetBracketedPasteMode requests that the terminal bracket paste operations
+// with markers. Not all terminals support this but, if it is supported, then
+// enabling this mode will stop any autocomplete callback from running due to
+// pastes. Additionally, any lines that are completely pasted will be returned
+// from ReadLine with the error set to ErrPasteIndicator.
+func (t *Terminal) SetBracketedPasteMode(on bool) {
+ if on {
+ io.WriteString(t.c, "\x1b[?2004h")
+ } else {
+ io.WriteString(t.c, "\x1b[?2004l")
+ }
+}
+
+// stRingBuffer is a ring buffer of strings.
+type stRingBuffer struct {
+ // entries contains max elements.
+ entries []string
+ max int
+ // head contains the index of the element most recently added to the ring.
+ head int
+ // size contains the number of elements in the ring.
+ size int
+}
+
+func (s *stRingBuffer) Add(a string) {
+ if s.entries == nil {
+ const defaultNumEntries = 100
+ s.entries = make([]string, defaultNumEntries)
+ s.max = defaultNumEntries
+ }
+
+ s.head = (s.head + 1) % s.max
+ s.entries[s.head] = a
+ if s.size < s.max {
+ s.size++
+ }
+}
+
+// NthPreviousEntry returns the value passed to the nth previous call to Add.
+// If n is zero then the immediately prior value is returned, if one, then the
+// next most recent, and so on. If such an element doesn't exist then ok is
+// false.
+func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
+ if n >= s.size {
+ return "", false
+ }
+ index := s.head - n
+ if index < 0 {
+ index += s.max
+ }
+ return s.entries[index], true
+}
+
+// readPasswordLine reads from reader until it finds \n or io.EOF.
+// The slice returned does not include the \n.
+// readPasswordLine also ignores any \r it finds.
+func readPasswordLine(reader io.Reader) ([]byte, error) {
+ var buf [1]byte
+ var ret []byte
+
+ for {
+ n, err := reader.Read(buf[:])
+ if n > 0 {
+ switch buf[0] {
+ case '\n':
+ return ret, nil
+ case '\r':
+ // remove \r from passwords on Windows
+ default:
+ ret = append(ret, buf[0])
+ }
+ continue
+ }
+ if err != nil {
+ if err == io.EOF && len(ret) > 0 {
+ return ret, nil
+ }
+ return ret, err
+ }
+ }
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util.go
new file mode 100644
index 0000000..e553f7e
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util.go
@@ -0,0 +1,114 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err)
+// }
+// defer terminal.Restore(0, oldState)
+package terminal
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+// State contains the state of a terminal.
+type State struct {
+ termios unix.Termios
+}
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ return err == nil
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ if err != nil {
+ return nil, err
+ }
+
+ oldState := State{termios: *termios}
+
+ // This attempts to replicate the behaviour documented for cfmakeraw in
+ // the termios(3) manpage.
+ termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
+ termios.Oflag &^= unix.OPOST
+ termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
+ termios.Cflag &^= unix.CSIZE | unix.PARENB
+ termios.Cflag |= unix.CS8
+ termios.Cc[unix.VMIN] = 1
+ termios.Cc[unix.VTIME] = 0
+ if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
+ return nil, err
+ }
+
+ return &oldState, nil
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ if err != nil {
+ return nil, err
+ }
+
+ return &State{termios: *termios}, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+ return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
+ if err != nil {
+ return -1, -1, err
+ }
+ return int(ws.Col), int(ws.Row), nil
+}
+
+// passwordReader is an io.Reader that reads from a specific file descriptor.
+type passwordReader int
+
+func (r passwordReader) Read(buf []byte) (int, error) {
+ return unix.Read(int(r), buf)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
+ if err != nil {
+ return nil, err
+ }
+
+ newState := *termios
+ newState.Lflag &^= unix.ECHO
+ newState.Lflag |= unix.ICANON | unix.ISIG
+ newState.Iflag |= unix.ICRNL
+ if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
+ return nil, err
+ }
+
+ defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
+
+ return readPasswordLine(passwordReader(fd))
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_aix.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_aix.go
new file mode 100644
index 0000000..dfcd627
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_aix.go
@@ -0,0 +1,12 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix
+
+package terminal
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
+const ioctlWriteTermios = unix.TCSETS
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_bsd.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_bsd.go
new file mode 100644
index 0000000..cb23a59
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_bsd.go
@@ -0,0 +1,12 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd netbsd openbsd
+
+package terminal
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TIOCGETA
+const ioctlWriteTermios = unix.TIOCSETA
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_linux.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_linux.go
new file mode 100644
index 0000000..5fadfe8
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_linux.go
@@ -0,0 +1,10 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package terminal
+
+import "golang.org/x/sys/unix"
+
+const ioctlReadTermios = unix.TCGETS
+const ioctlWriteTermios = unix.TCSETS
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_plan9.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_plan9.go
new file mode 100644
index 0000000..9317ac7
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_plan9.go
@@ -0,0 +1,58 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err)
+// }
+// defer terminal.Restore(0, oldState)
+package terminal
+
+import (
+ "fmt"
+ "runtime"
+)
+
+type State struct{}
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ return false
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+ return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_solaris.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_solaris.go
new file mode 100644
index 0000000..7b6b6fb
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_solaris.go
@@ -0,0 +1,125 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build solaris
+
+package terminal
+
+import (
+ "io"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+// State contains the state of a terminal.
+type State struct {
+ termios unix.Termios
+}
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ _, err := unix.IoctlGetTermio(fd, unix.TCGETA)
+ return err == nil
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ // see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
+ val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+ if err != nil {
+ return nil, err
+ }
+ oldState := *val
+
+ newState := oldState
+ newState.Lflag &^= syscall.ECHO
+ newState.Lflag |= syscall.ICANON | syscall.ISIG
+ newState.Iflag |= syscall.ICRNL
+ err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
+ if err != nil {
+ return nil, err
+ }
+
+ defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
+
+ var buf [16]byte
+ var ret []byte
+ for {
+ n, err := syscall.Read(fd, buf[:])
+ if err != nil {
+ return nil, err
+ }
+ if n == 0 {
+ if len(ret) == 0 {
+ return nil, io.EOF
+ }
+ break
+ }
+ if buf[n-1] == '\n' {
+ n--
+ }
+ ret = append(ret, buf[:n]...)
+ if n < len(buf) {
+ break
+ }
+ }
+
+ return ret, nil
+}
+
+// MakeRaw puts the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+// see http://cr.illumos.org/~webrev/andy_js/1060/
+func MakeRaw(fd int) (*State, error) {
+ termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+ if err != nil {
+ return nil, err
+ }
+
+ oldState := State{termios: *termios}
+
+ termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
+ termios.Oflag &^= unix.OPOST
+ termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
+ termios.Cflag &^= unix.CSIZE | unix.PARENB
+ termios.Cflag |= unix.CS8
+ termios.Cc[unix.VMIN] = 1
+ termios.Cc[unix.VTIME] = 0
+
+ if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil {
+ return nil, err
+ }
+
+ return &oldState, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, oldState *State) error {
+ return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios)
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
+ if err != nil {
+ return nil, err
+ }
+
+ return &State{termios: *termios}, nil
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
+ if err != nil {
+ return 0, 0, err
+ }
+ return int(ws.Col), int(ws.Row), nil
+}
diff --git a/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_windows.go b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_windows.go
new file mode 100644
index 0000000..6cb8a95
--- /dev/null
+++ b/teleirc/matterbridge/vendor/github.com/shazow/ssh-chat/sshd/terminal/util_windows.go
@@ -0,0 +1,103 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+// Package terminal provides support functions for dealing with terminals, as
+// commonly found on UNIX systems.
+//
+// Putting a terminal into raw mode is the most common requirement:
+//
+// oldState, err := terminal.MakeRaw(0)
+// if err != nil {
+// panic(err)
+// }
+// defer terminal.Restore(0, oldState)
+package terminal
+
+import (
+ "os"
+
+ "golang.org/x/sys/windows"
+)
+
+type State struct {
+ mode uint32
+}
+
+// IsTerminal returns whether the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+ var st uint32
+ err := windows.GetConsoleMode(windows.Handle(fd), &st)
+ return err == nil
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+ var st uint32
+ if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+ return nil, err
+ }
+ raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
+ if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
+ return nil, err
+ }
+ return &State{st}, nil
+}
+
+// GetState returns the current state of a terminal which may be useful to
+// restore the terminal after a signal.
+func GetState(fd int) (*State, error) {
+ var st uint32
+ if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+ return nil, err
+ }
+ return &State{st}, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, state *State) error {
+ return windows.SetConsoleMode(windows.Handle(fd), state.mode)
+}
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ var info windows.ConsoleScreenBufferInfo
+ if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
+ return 0, 0, err
+ }
+ return int(info.Size.X), int(info.Size.Y), nil
+}
+
+// ReadPassword reads a line of input from a terminal without local echo. This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+ var st uint32
+ if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
+ return nil, err
+ }
+ old := st
+
+ st &^= (windows.ENABLE_ECHO_INPUT)
+ st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
+ if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
+ return nil, err
+ }
+
+ defer windows.SetConsoleMode(windows.Handle(fd), old)
+
+ var h windows.Handle
+ p, _ := windows.GetCurrentProcess()
+ if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
+ return nil, err
+ }
+
+ f := os.NewFile(uintptr(h), "stdin")
+ defer f.Close()
+ return readPasswordLine(f)
+}