forked from bramvdbogaerde/go-scp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
protocol.go
137 lines (118 loc) · 3.16 KB
/
protocol.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* Copyright (c) 2020 Bram Vandenbogaerde
* You may use, distribute or modify this code under the
* terms of the Mozilla Public License 2.0, which is distributed
* along with the source code.
*/
package scp
import (
"bufio"
"errors"
"io"
"strconv"
"strings"
)
type ResponseType = uint8
const (
Ok ResponseType = 0
Warning ResponseType = 1
Error ResponseType = 2
)
const buffSize = 1024 * 256
// There are tree types of responses that the remote can send back:
// ok, warning and error
//
// The difference between warning and error is that the connection is not closed by the remote,
// however, a warning can indicate a file transfer failure (such as invalid destination directory)
// and such be handled as such.
//
// All responses except for the `Ok` type always have a message (although these can be empty)
//
// The remote sends a confirmation after every SCP command, because a failure can occur after every
// command, the response should be read and checked after sending them.
type Response struct {
Type ResponseType
Message string
}
// Reads from the given reader (assuming it is the output of the remote) and parses it into a Response structure
func ParseResponse(reader io.Reader) (Response, error) {
buffer := make([]uint8, 1)
_, err := reader.Read(buffer)
if err != nil {
return Response{}, err
}
response_type := buffer[0]
message := ""
if response_type > 0 {
buffered_reader := bufio.NewReader(reader)
message, err = buffered_reader.ReadString('\n')
if err != nil {
return Response{}, err
}
}
return Response{response_type, message}, nil
}
func (r *Response) IsOk() bool {
return r.Type == Ok
}
func (r *Response) IsWarning() bool {
return r.Type == Warning
}
// Returns true when the remote responded with an error
func (r *Response) IsError() bool {
return r.Type == Error
}
// Returns true when the remote answered with a warning or an error
func (r *Response) IsFailure() bool {
return r.Type > 0
}
// Returns the message the remote sent back
func (r *Response) GetMessage() string {
return r.Message
}
type FileInfos struct {
Message string
Filename string
Permissions string
Size int64
}
func (r *Response) ParseFileInfos() (*FileInfos, error) {
message := strings.ReplaceAll(r.Message, "\n", "")
parts := strings.Split(message, " ")
if len(parts) < 3 {
return nil, errors.New("Unable to parse message as file infos")
}
size, err := strconv.Atoi(parts[1])
if err != nil {
return nil, err
}
return &FileInfos{
Message: r.Message,
Permissions: parts[0],
Size: int64(size),
Filename: parts[2],
}, nil
}
// from https://github.com/dtylman/scp/blob/f3000a34aef49d94fcecd8f636244fb65e799133/scp.go
func CopyN(writer io.Writer, src io.Reader, size int64) (int64, error) {
reader := io.LimitReader(src, size)
var total int64
for total < size {
n, err := io.CopyBuffer(writer, reader, make([]byte, buffSize))
if err != nil {
return 0, err
}
total += n
}
return total, nil
}
func Ack(writer io.Writer) error {
var msg = []byte{0}
n, err := writer.Write(msg)
if err != nil {
return err
}
if n < len(msg) {
return errors.New("Failed to write ack buffer")
}
return nil
}