1// Package sftp implements the SSH File Transfer Protocol as described in
2// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
3package sftp
4
5import (
6	"fmt"
7
8	"github.com/pkg/errors"
9)
10
11const (
12	ssh_FXP_INIT           = 1
13	ssh_FXP_VERSION        = 2
14	ssh_FXP_OPEN           = 3
15	ssh_FXP_CLOSE          = 4
16	ssh_FXP_READ           = 5
17	ssh_FXP_WRITE          = 6
18	ssh_FXP_LSTAT          = 7
19	ssh_FXP_FSTAT          = 8
20	ssh_FXP_SETSTAT        = 9
21	ssh_FXP_FSETSTAT       = 10
22	ssh_FXP_OPENDIR        = 11
23	ssh_FXP_READDIR        = 12
24	ssh_FXP_REMOVE         = 13
25	ssh_FXP_MKDIR          = 14
26	ssh_FXP_RMDIR          = 15
27	ssh_FXP_REALPATH       = 16
28	ssh_FXP_STAT           = 17
29	ssh_FXP_RENAME         = 18
30	ssh_FXP_READLINK       = 19
31	ssh_FXP_SYMLINK        = 20
32	ssh_FXP_STATUS         = 101
33	ssh_FXP_HANDLE         = 102
34	ssh_FXP_DATA           = 103
35	ssh_FXP_NAME           = 104
36	ssh_FXP_ATTRS          = 105
37	ssh_FXP_EXTENDED       = 200
38	ssh_FXP_EXTENDED_REPLY = 201
39)
40
41const (
42	ssh_FX_OK                = 0
43	ssh_FX_EOF               = 1
44	ssh_FX_NO_SUCH_FILE      = 2
45	ssh_FX_PERMISSION_DENIED = 3
46	ssh_FX_FAILURE           = 4
47	ssh_FX_BAD_MESSAGE       = 5
48	ssh_FX_NO_CONNECTION     = 6
49	ssh_FX_CONNECTION_LOST   = 7
50	ssh_FX_OP_UNSUPPORTED    = 8
51
52	// see draft-ietf-secsh-filexfer-13
53	// https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.1
54	ssh_FX_INVALID_HANDLE              = 9
55	ssh_FX_NO_SUCH_PATH                = 10
56	ssh_FX_FILE_ALREADY_EXISTS         = 11
57	ssh_FX_WRITE_PROTECT               = 12
58	ssh_FX_NO_MEDIA                    = 13
59	ssh_FX_NO_SPACE_ON_FILESYSTEM      = 14
60	ssh_FX_QUOTA_EXCEEDED              = 15
61	ssh_FX_UNKNOWN_PRINCIPAL           = 16
62	ssh_FX_LOCK_CONFLICT               = 17
63	ssh_FX_DIR_NOT_EMPTY               = 18
64	ssh_FX_NOT_A_DIRECTORY             = 19
65	ssh_FX_INVALID_FILENAME            = 20
66	ssh_FX_LINK_LOOP                   = 21
67	ssh_FX_CANNOT_DELETE               = 22
68	ssh_FX_INVALID_PARAMETER           = 23
69	ssh_FX_FILE_IS_A_DIRECTORY         = 24
70	ssh_FX_BYTE_RANGE_LOCK_CONFLICT    = 25
71	ssh_FX_BYTE_RANGE_LOCK_REFUSED     = 26
72	ssh_FX_DELETE_PENDING              = 27
73	ssh_FX_FILE_CORRUPT                = 28
74	ssh_FX_OWNER_INVALID               = 29
75	ssh_FX_GROUP_INVALID               = 30
76	ssh_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31
77)
78
79const (
80	ssh_FXF_READ   = 0x00000001
81	ssh_FXF_WRITE  = 0x00000002
82	ssh_FXF_APPEND = 0x00000004
83	ssh_FXF_CREAT  = 0x00000008
84	ssh_FXF_TRUNC  = 0x00000010
85	ssh_FXF_EXCL   = 0x00000020
86)
87
88type fxp uint8
89
90func (f fxp) String() string {
91	switch f {
92	case ssh_FXP_INIT:
93		return "SSH_FXP_INIT"
94	case ssh_FXP_VERSION:
95		return "SSH_FXP_VERSION"
96	case ssh_FXP_OPEN:
97		return "SSH_FXP_OPEN"
98	case ssh_FXP_CLOSE:
99		return "SSH_FXP_CLOSE"
100	case ssh_FXP_READ:
101		return "SSH_FXP_READ"
102	case ssh_FXP_WRITE:
103		return "SSH_FXP_WRITE"
104	case ssh_FXP_LSTAT:
105		return "SSH_FXP_LSTAT"
106	case ssh_FXP_FSTAT:
107		return "SSH_FXP_FSTAT"
108	case ssh_FXP_SETSTAT:
109		return "SSH_FXP_SETSTAT"
110	case ssh_FXP_FSETSTAT:
111		return "SSH_FXP_FSETSTAT"
112	case ssh_FXP_OPENDIR:
113		return "SSH_FXP_OPENDIR"
114	case ssh_FXP_READDIR:
115		return "SSH_FXP_READDIR"
116	case ssh_FXP_REMOVE:
117		return "SSH_FXP_REMOVE"
118	case ssh_FXP_MKDIR:
119		return "SSH_FXP_MKDIR"
120	case ssh_FXP_RMDIR:
121		return "SSH_FXP_RMDIR"
122	case ssh_FXP_REALPATH:
123		return "SSH_FXP_REALPATH"
124	case ssh_FXP_STAT:
125		return "SSH_FXP_STAT"
126	case ssh_FXP_RENAME:
127		return "SSH_FXP_RENAME"
128	case ssh_FXP_READLINK:
129		return "SSH_FXP_READLINK"
130	case ssh_FXP_SYMLINK:
131		return "SSH_FXP_SYMLINK"
132	case ssh_FXP_STATUS:
133		return "SSH_FXP_STATUS"
134	case ssh_FXP_HANDLE:
135		return "SSH_FXP_HANDLE"
136	case ssh_FXP_DATA:
137		return "SSH_FXP_DATA"
138	case ssh_FXP_NAME:
139		return "SSH_FXP_NAME"
140	case ssh_FXP_ATTRS:
141		return "SSH_FXP_ATTRS"
142	case ssh_FXP_EXTENDED:
143		return "SSH_FXP_EXTENDED"
144	case ssh_FXP_EXTENDED_REPLY:
145		return "SSH_FXP_EXTENDED_REPLY"
146	default:
147		return "unknown"
148	}
149}
150
151type fx uint8
152
153func (f fx) String() string {
154	switch f {
155	case ssh_FX_OK:
156		return "SSH_FX_OK"
157	case ssh_FX_EOF:
158		return "SSH_FX_EOF"
159	case ssh_FX_NO_SUCH_FILE:
160		return "SSH_FX_NO_SUCH_FILE"
161	case ssh_FX_PERMISSION_DENIED:
162		return "SSH_FX_PERMISSION_DENIED"
163	case ssh_FX_FAILURE:
164		return "SSH_FX_FAILURE"
165	case ssh_FX_BAD_MESSAGE:
166		return "SSH_FX_BAD_MESSAGE"
167	case ssh_FX_NO_CONNECTION:
168		return "SSH_FX_NO_CONNECTION"
169	case ssh_FX_CONNECTION_LOST:
170		return "SSH_FX_CONNECTION_LOST"
171	case ssh_FX_OP_UNSUPPORTED:
172		return "SSH_FX_OP_UNSUPPORTED"
173	default:
174		return "unknown"
175	}
176}
177
178type unexpectedPacketErr struct {
179	want, got uint8
180}
181
182func (u *unexpectedPacketErr) Error() string {
183	return fmt.Sprintf("sftp: unexpected packet: want %v, got %v", fxp(u.want), fxp(u.got))
184}
185
186func unimplementedPacketErr(u uint8) error {
187	return errors.Errorf("sftp: unimplemented packet type: got %v", fxp(u))
188}
189
190type unexpectedIDErr struct{ want, got uint32 }
191
192func (u *unexpectedIDErr) Error() string {
193	return fmt.Sprintf("sftp: unexpected id: want %v, got %v", u.want, u.got)
194}
195
196func unimplementedSeekWhence(whence int) error {
197	return errors.Errorf("sftp: unimplemented seek whence %v", whence)
198}
199
200func unexpectedCount(want, got uint32) error {
201	return errors.Errorf("sftp: unexpected count: want %v, got %v", want, got)
202}
203
204type unexpectedVersionErr struct{ want, got uint32 }
205
206func (u *unexpectedVersionErr) Error() string {
207	return fmt.Sprintf("sftp: unexpected server version: want %v, got %v", u.want, u.got)
208}
209
210// A StatusError is returned when an SFTP operation fails, and provides
211// additional information about the failure.
212type StatusError struct {
213	Code      uint32
214	msg, lang string
215}
216
217func (s *StatusError) Error() string { return fmt.Sprintf("sftp: %q (%v)", s.msg, fx(s.Code)) }
218