├── LICENSE ├── README.md ├── scp.go └── scp_example_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Travis Cline 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scp 2 | import "github.com/tmc/scp" 3 | 4 | Package scp provides a simple interface to copying files over a 5 | go.crypto/ssh session. 6 | 7 | ## func Copy 8 | ``` go 9 | func Copy(size int64, mode os.FileMode, fileName string, contents io.Reader, destinationPath string, session *ssh.Session) error 10 | ``` 11 | 12 | ## func CopyPath 13 | ``` go 14 | func CopyPath(filePath, destinationPath string, session *ssh.Session) error 15 | ``` 16 | -------------------------------------------------------------------------------- /scp.go: -------------------------------------------------------------------------------- 1 | // Package scp provides a simple interface to copying files over a 2 | // go.crypto/ssh session. 3 | package scp 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | "os" 9 | "path" 10 | 11 | shellquote "github.com/kballard/go-shellquote" 12 | 13 | "golang.org/x/crypto/ssh" 14 | ) 15 | 16 | func Copy(size int64, mode os.FileMode, fileName string, contents io.Reader, destinationPath string, session *ssh.Session) error { 17 | return copy(size, mode, fileName, contents, destinationPath, session) 18 | } 19 | 20 | func CopyPath(filePath, destinationPath string, session *ssh.Session) error { 21 | f, err := os.Open(filePath) 22 | if err != nil { 23 | return err 24 | } 25 | defer f.Close() 26 | s, err := f.Stat() 27 | if err != nil { 28 | return err 29 | } 30 | return copy(s.Size(), s.Mode().Perm(), path.Base(filePath), f, destinationPath, session) 31 | } 32 | 33 | func copy(size int64, mode os.FileMode, fileName string, contents io.Reader, destination string, session *ssh.Session) error { 34 | defer session.Close() 35 | w, err := session.StdinPipe() 36 | 37 | if err != nil { 38 | return err 39 | } 40 | 41 | cmd := shellquote.Join("scp", "-t", destination) 42 | if err := session.Start(cmd); err != nil { 43 | w.Close() 44 | return err 45 | } 46 | 47 | errors := make(chan error) 48 | 49 | go func() { 50 | errors <- session.Wait() 51 | }() 52 | 53 | fmt.Fprintf(w, "C%#o %d %s\n", mode, size, fileName) 54 | io.Copy(w, contents) 55 | fmt.Fprint(w, "\x00") 56 | w.Close() 57 | 58 | return <-errors 59 | } 60 | -------------------------------------------------------------------------------- /scp_example_test.go: -------------------------------------------------------------------------------- 1 | package scp_test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "net" 8 | "os" 9 | 10 | "golang.org/x/crypto/ssh" 11 | "golang.org/x/crypto/ssh/agent" 12 | 13 | "github.com/tmc/scp" 14 | ) 15 | 16 | func getAgent() (agent.Agent, error) { 17 | agentConn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) 18 | return agent.NewClient(agentConn), err 19 | } 20 | 21 | func ExampleCopyPath() { 22 | f, _ := ioutil.TempFile("", "") 23 | fmt.Fprintln(f, "hello world") 24 | f.Close() 25 | defer os.Remove(f.Name()) 26 | defer os.Remove(f.Name() + "-copy") 27 | 28 | agent, err := getAgent() 29 | if err != nil { 30 | log.Fatalln("Failed to connect to SSH_AUTH_SOCK:", err) 31 | } 32 | 33 | client, err := ssh.Dial("tcp", "127.0.0.1:22", &ssh.ClientConfig{ 34 | User: os.Getenv("USER"), 35 | Auth: []ssh.AuthMethod{ 36 | ssh.PublicKeysCallback(agent.Signers), 37 | }, 38 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), // FIXME: please be more secure in checking host keys 39 | }) 40 | if err != nil { 41 | log.Fatalln("Failed to dial:", err) 42 | } 43 | 44 | session, err := client.NewSession() 45 | if err != nil { 46 | log.Fatalln("Failed to create session: " + err.Error()) 47 | } 48 | 49 | dest := f.Name() + "-copy" 50 | err = scp.CopyPath(f.Name(), dest, session) 51 | if _, err := os.Stat(dest); os.IsNotExist(err) { 52 | fmt.Printf("no such file or directory: %s", dest) 53 | } else { 54 | fmt.Println("success") 55 | } 56 | // output: 57 | // success 58 | } 59 | --------------------------------------------------------------------------------