4c7f0d61 |
1 | /* |
2 | * sftp.c: SFTP generic client code. |
3 | */ |
4 | |
5 | #include <stdio.h> |
6 | #include <stdlib.h> |
4a8fc3c4 |
7 | #include <string.h> |
4c7f0d61 |
8 | #include <assert.h> |
4c7f0d61 |
9 | |
78139cd8 |
10 | #include "misc.h" |
4c7f0d61 |
11 | #include "int64.h" |
12 | #include "sftp.h" |
13 | |
4c7f0d61 |
14 | #define GET_32BIT(cp) \ |
15 | (((unsigned long)(unsigned char)(cp)[0] << 24) | \ |
16 | ((unsigned long)(unsigned char)(cp)[1] << 16) | \ |
17 | ((unsigned long)(unsigned char)(cp)[2] << 8) | \ |
18 | ((unsigned long)(unsigned char)(cp)[3])) |
19 | |
20 | #define PUT_32BIT(cp, value) { \ |
21 | (cp)[0] = (unsigned char)((value) >> 24); \ |
22 | (cp)[1] = (unsigned char)((value) >> 16); \ |
23 | (cp)[2] = (unsigned char)((value) >> 8); \ |
24 | (cp)[3] = (unsigned char)(value); } |
25 | |
26 | struct sftp_packet { |
27 | char *data; |
28 | int length, maxlen; |
29 | int savedpos; |
30 | int type; |
31 | }; |
32 | |
9954aaa3 |
33 | static const char *fxp_error_message; |
34 | static int fxp_errtype; |
35 | |
4c7f0d61 |
36 | /* ---------------------------------------------------------------------- |
37 | * SFTP packet construction functions. |
38 | */ |
32874aea |
39 | static void sftp_pkt_ensure(struct sftp_packet *pkt, int length) |
40 | { |
4c7f0d61 |
41 | if (pkt->maxlen < length) { |
32874aea |
42 | pkt->maxlen = length + 256; |
3d88e64d |
43 | pkt->data = sresize(pkt->data, pkt->maxlen, char); |
4c7f0d61 |
44 | } |
45 | } |
32874aea |
46 | static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len) |
47 | { |
4c7f0d61 |
48 | pkt->length += len; |
49 | sftp_pkt_ensure(pkt, pkt->length); |
32874aea |
50 | memcpy(pkt->data + pkt->length - len, data, len); |
4c7f0d61 |
51 | } |
32874aea |
52 | static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte) |
53 | { |
4c7f0d61 |
54 | sftp_pkt_adddata(pkt, &byte, 1); |
55 | } |
32874aea |
56 | static struct sftp_packet *sftp_pkt_init(int pkt_type) |
57 | { |
4c7f0d61 |
58 | struct sftp_packet *pkt; |
3d88e64d |
59 | pkt = snew(struct sftp_packet); |
4c7f0d61 |
60 | pkt->data = NULL; |
61 | pkt->savedpos = -1; |
62 | pkt->length = 0; |
63 | pkt->maxlen = 0; |
32874aea |
64 | sftp_pkt_addbyte(pkt, (unsigned char) pkt_type); |
4c7f0d61 |
65 | return pkt; |
66 | } |
32874aea |
67 | static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value) |
68 | { |
4c7f0d61 |
69 | sftp_pkt_adddata(pkt, &value, 1); |
70 | } |
32874aea |
71 | static void sftp_pkt_adduint32(struct sftp_packet *pkt, |
72 | unsigned long value) |
73 | { |
4c7f0d61 |
74 | unsigned char x[4]; |
75 | PUT_32BIT(x, value); |
76 | sftp_pkt_adddata(pkt, x, 4); |
77 | } |
32874aea |
78 | static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value) |
79 | { |
4c7f0d61 |
80 | unsigned char x[8]; |
81 | PUT_32BIT(x, value.hi); |
32874aea |
82 | PUT_32BIT(x + 4, value.lo); |
4c7f0d61 |
83 | sftp_pkt_adddata(pkt, x, 8); |
84 | } |
32874aea |
85 | static void sftp_pkt_addstring_start(struct sftp_packet *pkt) |
86 | { |
4c7f0d61 |
87 | sftp_pkt_adduint32(pkt, 0); |
88 | pkt->savedpos = pkt->length; |
89 | } |
32874aea |
90 | static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data) |
91 | { |
4c7f0d61 |
92 | sftp_pkt_adddata(pkt, data, strlen(data)); |
32874aea |
93 | PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); |
4c7f0d61 |
94 | } |
95 | static void sftp_pkt_addstring_data(struct sftp_packet *pkt, |
32874aea |
96 | char *data, int len) |
97 | { |
4c7f0d61 |
98 | sftp_pkt_adddata(pkt, data, len); |
32874aea |
99 | PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos); |
4c7f0d61 |
100 | } |
32874aea |
101 | static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data) |
102 | { |
4c7f0d61 |
103 | sftp_pkt_addstring_start(pkt); |
104 | sftp_pkt_addstring_str(pkt, data); |
105 | } |
d92624dc |
106 | static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs) |
107 | { |
108 | sftp_pkt_adduint32(pkt, attrs.flags); |
109 | if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) { |
110 | sftp_pkt_adduint32(pkt, attrs.size.hi); |
111 | sftp_pkt_adduint32(pkt, attrs.size.lo); |
112 | } |
113 | if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) { |
114 | sftp_pkt_adduint32(pkt, attrs.uid); |
115 | sftp_pkt_adduint32(pkt, attrs.gid); |
116 | } |
117 | if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) { |
118 | sftp_pkt_adduint32(pkt, attrs.permissions); |
119 | } |
120 | if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) { |
121 | sftp_pkt_adduint32(pkt, attrs.atime); |
122 | sftp_pkt_adduint32(pkt, attrs.mtime); |
123 | } |
124 | if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) { |
125 | /* |
126 | * We currently don't support sending any extended |
127 | * attributes. |
128 | */ |
129 | } |
130 | } |
4c7f0d61 |
131 | |
132 | /* ---------------------------------------------------------------------- |
133 | * SFTP packet decode functions. |
134 | */ |
135 | |
32874aea |
136 | static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt) |
137 | { |
d8770b12 |
138 | unsigned char value; |
4c7f0d61 |
139 | if (pkt->length - pkt->savedpos < 1) |
32874aea |
140 | return 0; /* arrgh, no way to decline (FIXME?) */ |
4c7f0d61 |
141 | value = (unsigned char) pkt->data[pkt->savedpos]; |
142 | pkt->savedpos++; |
143 | return value; |
144 | } |
32874aea |
145 | static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt) |
146 | { |
4c7f0d61 |
147 | unsigned long value; |
148 | if (pkt->length - pkt->savedpos < 4) |
32874aea |
149 | return 0; /* arrgh, no way to decline (FIXME?) */ |
150 | value = GET_32BIT(pkt->data + pkt->savedpos); |
4c7f0d61 |
151 | pkt->savedpos += 4; |
152 | return value; |
153 | } |
154 | static void sftp_pkt_getstring(struct sftp_packet *pkt, |
32874aea |
155 | char **p, int *length) |
156 | { |
4c7f0d61 |
157 | *p = NULL; |
158 | if (pkt->length - pkt->savedpos < 4) |
32874aea |
159 | return; |
160 | *length = GET_32BIT(pkt->data + pkt->savedpos); |
4c7f0d61 |
161 | pkt->savedpos += 4; |
162 | if (pkt->length - pkt->savedpos < *length) |
32874aea |
163 | return; |
164 | *p = pkt->data + pkt->savedpos; |
4c7f0d61 |
165 | pkt->savedpos += *length; |
166 | } |
32874aea |
167 | static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt) |
168 | { |
4c7f0d61 |
169 | struct fxp_attrs ret; |
170 | ret.flags = sftp_pkt_getuint32(pkt); |
171 | if (ret.flags & SSH_FILEXFER_ATTR_SIZE) { |
172 | unsigned long hi, lo; |
173 | hi = sftp_pkt_getuint32(pkt); |
174 | lo = sftp_pkt_getuint32(pkt); |
175 | ret.size = uint64_make(hi, lo); |
176 | } |
177 | if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) { |
178 | ret.uid = sftp_pkt_getuint32(pkt); |
179 | ret.gid = sftp_pkt_getuint32(pkt); |
180 | } |
181 | if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) { |
182 | ret.permissions = sftp_pkt_getuint32(pkt); |
183 | } |
184 | if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) { |
185 | ret.atime = sftp_pkt_getuint32(pkt); |
186 | ret.mtime = sftp_pkt_getuint32(pkt); |
187 | } |
188 | if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) { |
189 | int count; |
190 | count = sftp_pkt_getuint32(pkt); |
191 | while (count--) { |
192 | char *str; |
193 | int len; |
194 | /* |
195 | * We should try to analyse these, if we ever find one |
196 | * we recognise. |
197 | */ |
198 | sftp_pkt_getstring(pkt, &str, &len); |
199 | sftp_pkt_getstring(pkt, &str, &len); |
200 | } |
201 | } |
202 | return ret; |
203 | } |
32874aea |
204 | static void sftp_pkt_free(struct sftp_packet *pkt) |
205 | { |
206 | if (pkt->data) |
207 | sfree(pkt->data); |
4c7f0d61 |
208 | sfree(pkt); |
209 | } |
210 | |
211 | /* ---------------------------------------------------------------------- |
4a8fc3c4 |
212 | * Send and receive packet functions. |
4c7f0d61 |
213 | */ |
32874aea |
214 | int sftp_send(struct sftp_packet *pkt) |
215 | { |
4a8fc3c4 |
216 | int ret; |
4c7f0d61 |
217 | char x[4]; |
218 | PUT_32BIT(x, pkt->length); |
32874aea |
219 | ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length)); |
4c7f0d61 |
220 | sftp_pkt_free(pkt); |
4a8fc3c4 |
221 | return ret; |
4c7f0d61 |
222 | } |
32874aea |
223 | struct sftp_packet *sftp_recv(void) |
224 | { |
4c7f0d61 |
225 | struct sftp_packet *pkt; |
226 | char x[4]; |
4c7f0d61 |
227 | |
4a8fc3c4 |
228 | if (!sftp_recvdata(x, 4)) |
229 | return NULL; |
4c7f0d61 |
230 | |
3d88e64d |
231 | pkt = snew(struct sftp_packet); |
4c7f0d61 |
232 | pkt->savedpos = 0; |
233 | pkt->length = pkt->maxlen = GET_32BIT(x); |
3d88e64d |
234 | pkt->data = snewn(pkt->length, char); |
4c7f0d61 |
235 | |
4a8fc3c4 |
236 | if (!sftp_recvdata(pkt->data, pkt->length)) { |
237 | sftp_pkt_free(pkt); |
238 | return NULL; |
4c7f0d61 |
239 | } |
240 | |
241 | pkt->type = sftp_pkt_getbyte(pkt); |
242 | |
243 | return pkt; |
244 | } |
245 | |
246 | /* ---------------------------------------------------------------------- |
247 | * String handling routines. |
248 | */ |
249 | |
32874aea |
250 | static char *mkstr(char *s, int len) |
251 | { |
3d88e64d |
252 | char *p = snewn(len + 1, char); |
4c7f0d61 |
253 | memcpy(p, s, len); |
254 | p[len] = '\0'; |
255 | return p; |
256 | } |
257 | |
258 | /* ---------------------------------------------------------------------- |
259 | * SFTP primitives. |
260 | */ |
261 | |
4c7f0d61 |
262 | /* |
263 | * Deal with (and free) an FXP_STATUS packet. Return 1 if |
264 | * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error). |
265 | * Also place the status into fxp_errtype. |
266 | */ |
32874aea |
267 | static int fxp_got_status(struct sftp_packet *pktin) |
268 | { |
4c7f0d61 |
269 | static const char *const messages[] = { |
270 | /* SSH_FX_OK. The only time we will display a _message_ for this |
271 | * is if we were expecting something other than FXP_STATUS on |
272 | * success, so this is actually an error message! */ |
273 | "unexpected OK response", |
274 | "end of file", |
275 | "no such file or directory", |
276 | "permission denied", |
277 | "failure", |
278 | "bad message", |
279 | "no connection", |
280 | "connection lost", |
281 | "operation unsupported", |
282 | }; |
283 | |
284 | if (pktin->type != SSH_FXP_STATUS) { |
285 | fxp_error_message = "expected FXP_STATUS packet"; |
286 | fxp_errtype = -1; |
287 | } else { |
288 | fxp_errtype = sftp_pkt_getuint32(pktin); |
289 | if (fxp_errtype < 0 || |
32874aea |
290 | fxp_errtype >= sizeof(messages) / sizeof(*messages)) |
291 | fxp_error_message = "unknown error code"; |
4c7f0d61 |
292 | else |
293 | fxp_error_message = messages[fxp_errtype]; |
294 | } |
295 | |
296 | if (fxp_errtype == SSH_FX_OK) |
297 | return 1; |
298 | else if (fxp_errtype == SSH_FX_EOF) |
299 | return 0; |
300 | else |
301 | return -1; |
302 | } |
303 | |
32874aea |
304 | static void fxp_internal_error(char *msg) |
305 | { |
4c7f0d61 |
306 | fxp_error_message = msg; |
307 | fxp_errtype = -1; |
308 | } |
309 | |
32874aea |
310 | const char *fxp_error(void) |
311 | { |
4c7f0d61 |
312 | return fxp_error_message; |
313 | } |
314 | |
32874aea |
315 | int fxp_error_type(void) |
316 | { |
4c7f0d61 |
317 | return fxp_errtype; |
318 | } |
319 | |
320 | /* |
321 | * Perform exchange of init/version packets. Return 0 on failure. |
322 | */ |
32874aea |
323 | int fxp_init(void) |
324 | { |
4c7f0d61 |
325 | struct sftp_packet *pktout, *pktin; |
326 | int remotever; |
327 | |
328 | pktout = sftp_pkt_init(SSH_FXP_INIT); |
329 | sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION); |
330 | sftp_send(pktout); |
331 | |
332 | pktin = sftp_recv(); |
0d694692 |
333 | if (!pktin) { |
334 | fxp_internal_error("could not connect"); |
335 | return 0; |
336 | } |
4c7f0d61 |
337 | if (pktin->type != SSH_FXP_VERSION) { |
338 | fxp_internal_error("did not receive FXP_VERSION"); |
41d3adbb |
339 | sftp_pkt_free(pktin); |
4c7f0d61 |
340 | return 0; |
341 | } |
342 | remotever = sftp_pkt_getuint32(pktin); |
343 | if (remotever > SFTP_PROTO_VERSION) { |
32874aea |
344 | fxp_internal_error |
345 | ("remote protocol is more advanced than we support"); |
41d3adbb |
346 | sftp_pkt_free(pktin); |
4c7f0d61 |
347 | return 0; |
348 | } |
349 | /* |
350 | * In principle, this packet might also contain extension- |
351 | * string pairs. We should work through them and look for any |
352 | * we recognise. In practice we don't currently do so because |
353 | * we know we don't recognise _any_. |
354 | */ |
355 | sftp_pkt_free(pktin); |
356 | |
357 | return 1; |
358 | } |
359 | |
360 | /* |
f9e162aa |
361 | * Canonify a pathname. |
4c7f0d61 |
362 | */ |
32874aea |
363 | char *fxp_realpath(char *path) |
364 | { |
4c7f0d61 |
365 | struct sftp_packet *pktin, *pktout; |
366 | int id; |
367 | |
368 | pktout = sftp_pkt_init(SSH_FXP_REALPATH); |
369 | sftp_pkt_adduint32(pktout, 0x123); /* request id */ |
370 | sftp_pkt_addstring_start(pktout); |
371 | sftp_pkt_addstring_str(pktout, path); |
4c7f0d61 |
372 | sftp_send(pktout); |
373 | pktin = sftp_recv(); |
f65c4643 |
374 | if (!pktin) { |
375 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
376 | return NULL; |
377 | } |
4c7f0d61 |
378 | id = sftp_pkt_getuint32(pktin); |
379 | if (id != 0x123) { |
380 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
381 | sftp_pkt_free(pktin); |
4c7f0d61 |
382 | return NULL; |
383 | } |
384 | if (pktin->type == SSH_FXP_NAME) { |
385 | int count; |
386 | char *path; |
387 | int len; |
388 | |
389 | count = sftp_pkt_getuint32(pktin); |
390 | if (count != 1) { |
391 | fxp_internal_error("REALPATH returned name count != 1\n"); |
41d3adbb |
392 | sftp_pkt_free(pktin); |
4c7f0d61 |
393 | return NULL; |
394 | } |
395 | sftp_pkt_getstring(pktin, &path, &len); |
396 | if (!path) { |
397 | fxp_internal_error("REALPATH returned malformed FXP_NAME\n"); |
41d3adbb |
398 | sftp_pkt_free(pktin); |
4c7f0d61 |
399 | return NULL; |
400 | } |
401 | path = mkstr(path, len); |
402 | sftp_pkt_free(pktin); |
403 | return path; |
404 | } else { |
405 | fxp_got_status(pktin); |
41d3adbb |
406 | sftp_pkt_free(pktin); |
4c7f0d61 |
407 | return NULL; |
408 | } |
409 | } |
410 | |
411 | /* |
412 | * Open a file. |
413 | */ |
32874aea |
414 | struct fxp_handle *fxp_open(char *path, int type) |
415 | { |
4c7f0d61 |
416 | struct sftp_packet *pktin, *pktout; |
417 | int id; |
418 | |
419 | pktout = sftp_pkt_init(SSH_FXP_OPEN); |
420 | sftp_pkt_adduint32(pktout, 0x567); /* request id */ |
421 | sftp_pkt_addstring(pktout, path); |
422 | sftp_pkt_adduint32(pktout, type); |
423 | sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */ |
424 | sftp_send(pktout); |
425 | pktin = sftp_recv(); |
f65c4643 |
426 | if (!pktin) { |
427 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
428 | return NULL; |
429 | } |
4c7f0d61 |
430 | id = sftp_pkt_getuint32(pktin); |
431 | if (id != 0x567) { |
432 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
433 | sftp_pkt_free(pktin); |
4c7f0d61 |
434 | return NULL; |
435 | } |
436 | if (pktin->type == SSH_FXP_HANDLE) { |
4c7f0d61 |
437 | char *hstring; |
438 | struct fxp_handle *handle; |
439 | int len; |
440 | |
441 | sftp_pkt_getstring(pktin, &hstring, &len); |
442 | if (!hstring) { |
443 | fxp_internal_error("OPEN returned malformed FXP_HANDLE\n"); |
41d3adbb |
444 | sftp_pkt_free(pktin); |
4c7f0d61 |
445 | return NULL; |
446 | } |
3d88e64d |
447 | handle = snew(struct fxp_handle); |
4c7f0d61 |
448 | handle->hstring = mkstr(hstring, len); |
f9e162aa |
449 | handle->hlen = len; |
4c7f0d61 |
450 | sftp_pkt_free(pktin); |
451 | return handle; |
452 | } else { |
453 | fxp_got_status(pktin); |
41d3adbb |
454 | sftp_pkt_free(pktin); |
4c7f0d61 |
455 | return NULL; |
456 | } |
457 | } |
458 | |
459 | /* |
460 | * Open a directory. |
461 | */ |
32874aea |
462 | struct fxp_handle *fxp_opendir(char *path) |
463 | { |
4c7f0d61 |
464 | struct sftp_packet *pktin, *pktout; |
465 | int id; |
466 | |
467 | pktout = sftp_pkt_init(SSH_FXP_OPENDIR); |
468 | sftp_pkt_adduint32(pktout, 0x456); /* request id */ |
469 | sftp_pkt_addstring(pktout, path); |
470 | sftp_send(pktout); |
471 | pktin = sftp_recv(); |
f65c4643 |
472 | if (!pktin) { |
473 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
474 | return NULL; |
475 | } |
4c7f0d61 |
476 | id = sftp_pkt_getuint32(pktin); |
477 | if (id != 0x456) { |
478 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
479 | sftp_pkt_free(pktin); |
4c7f0d61 |
480 | return NULL; |
481 | } |
482 | if (pktin->type == SSH_FXP_HANDLE) { |
4c7f0d61 |
483 | char *hstring; |
484 | struct fxp_handle *handle; |
485 | int len; |
486 | |
487 | sftp_pkt_getstring(pktin, &hstring, &len); |
488 | if (!hstring) { |
489 | fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n"); |
41d3adbb |
490 | sftp_pkt_free(pktin); |
4c7f0d61 |
491 | return NULL; |
492 | } |
3d88e64d |
493 | handle = snew(struct fxp_handle); |
4c7f0d61 |
494 | handle->hstring = mkstr(hstring, len); |
f9e162aa |
495 | handle->hlen = len; |
4c7f0d61 |
496 | sftp_pkt_free(pktin); |
497 | return handle; |
498 | } else { |
499 | fxp_got_status(pktin); |
41d3adbb |
500 | sftp_pkt_free(pktin); |
4c7f0d61 |
501 | return NULL; |
502 | } |
503 | } |
504 | |
505 | /* |
506 | * Close a file/dir. |
507 | */ |
32874aea |
508 | void fxp_close(struct fxp_handle *handle) |
509 | { |
4c7f0d61 |
510 | struct sftp_packet *pktin, *pktout; |
511 | int id; |
512 | |
513 | pktout = sftp_pkt_init(SSH_FXP_CLOSE); |
514 | sftp_pkt_adduint32(pktout, 0x789); /* request id */ |
f9e162aa |
515 | sftp_pkt_addstring_start(pktout); |
516 | sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); |
4c7f0d61 |
517 | sftp_send(pktout); |
518 | pktin = sftp_recv(); |
f65c4643 |
519 | if (!pktin) { |
520 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
521 | return; |
522 | } |
4c7f0d61 |
523 | id = sftp_pkt_getuint32(pktin); |
524 | if (id != 0x789) { |
525 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
526 | sftp_pkt_free(pktin); |
4c7f0d61 |
527 | return; |
528 | } |
529 | fxp_got_status(pktin); |
41d3adbb |
530 | sftp_pkt_free(pktin); |
4c7f0d61 |
531 | sfree(handle->hstring); |
532 | sfree(handle); |
533 | } |
534 | |
9954aaa3 |
535 | int fxp_mkdir(char *path) |
536 | { |
537 | struct sftp_packet *pktin, *pktout; |
538 | int id; |
539 | |
540 | pktout = sftp_pkt_init(SSH_FXP_MKDIR); |
541 | sftp_pkt_adduint32(pktout, 0x234); /* request id */ |
d92624dc |
542 | sftp_pkt_addstring(pktout, path); |
9954aaa3 |
543 | sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */ |
544 | sftp_send(pktout); |
545 | pktin = sftp_recv(); |
f65c4643 |
546 | if (!pktin) { |
547 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
548 | return 0; |
549 | } |
9954aaa3 |
550 | id = sftp_pkt_getuint32(pktin); |
551 | if (id != 0x234) { |
552 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
553 | sftp_pkt_free(pktin); |
9954aaa3 |
554 | return 0; |
555 | } |
556 | id = fxp_got_status(pktin); |
41d3adbb |
557 | sftp_pkt_free(pktin); |
9954aaa3 |
558 | if (id != 1) { |
559 | return 0; |
560 | } |
561 | return 1; |
562 | } |
563 | |
564 | int fxp_rmdir(char *path) |
565 | { |
566 | struct sftp_packet *pktin, *pktout; |
567 | int id; |
568 | |
569 | pktout = sftp_pkt_init(SSH_FXP_RMDIR); |
570 | sftp_pkt_adduint32(pktout, 0x345); /* request id */ |
d92624dc |
571 | sftp_pkt_addstring(pktout, path); |
9954aaa3 |
572 | sftp_send(pktout); |
573 | pktin = sftp_recv(); |
f65c4643 |
574 | if (!pktin) { |
575 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
576 | return 0; |
577 | } |
9954aaa3 |
578 | id = sftp_pkt_getuint32(pktin); |
579 | if (id != 0x345) { |
580 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
581 | sftp_pkt_free(pktin); |
9954aaa3 |
582 | return 0; |
583 | } |
584 | id = fxp_got_status(pktin); |
41d3adbb |
585 | sftp_pkt_free(pktin); |
9954aaa3 |
586 | if (id != 1) { |
587 | return 0; |
588 | } |
589 | return 1; |
590 | } |
591 | |
d92624dc |
592 | int fxp_remove(char *fname) |
9954aaa3 |
593 | { |
594 | struct sftp_packet *pktin, *pktout; |
595 | int id; |
596 | |
597 | pktout = sftp_pkt_init(SSH_FXP_REMOVE); |
598 | sftp_pkt_adduint32(pktout, 0x678); /* request id */ |
d92624dc |
599 | sftp_pkt_addstring(pktout, fname); |
600 | sftp_send(pktout); |
601 | pktin = sftp_recv(); |
f65c4643 |
602 | if (!pktin) { |
603 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
604 | return 0; |
605 | } |
d92624dc |
606 | id = sftp_pkt_getuint32(pktin); |
607 | if (id != 0x678) { |
608 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
609 | sftp_pkt_free(pktin); |
d92624dc |
610 | return 0; |
611 | } |
612 | id = fxp_got_status(pktin); |
41d3adbb |
613 | sftp_pkt_free(pktin); |
d92624dc |
614 | if (id != 1) { |
615 | return 0; |
616 | } |
617 | return 1; |
618 | } |
619 | |
620 | int fxp_rename(char *srcfname, char *dstfname) |
621 | { |
622 | struct sftp_packet *pktin, *pktout; |
623 | int id; |
624 | |
625 | pktout = sftp_pkt_init(SSH_FXP_RENAME); |
626 | sftp_pkt_adduint32(pktout, 0x678); /* request id */ |
627 | sftp_pkt_addstring(pktout, srcfname); |
628 | sftp_pkt_addstring(pktout, dstfname); |
629 | sftp_send(pktout); |
630 | pktin = sftp_recv(); |
f65c4643 |
631 | if (!pktin) { |
632 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
633 | return 0; |
634 | } |
d92624dc |
635 | id = sftp_pkt_getuint32(pktin); |
636 | if (id != 0x678) { |
637 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
638 | sftp_pkt_free(pktin); |
d92624dc |
639 | return 0; |
640 | } |
641 | id = fxp_got_status(pktin); |
41d3adbb |
642 | sftp_pkt_free(pktin); |
d92624dc |
643 | if (id != 1) { |
644 | return 0; |
645 | } |
646 | return 1; |
647 | } |
648 | |
649 | /* |
650 | * Retrieve the attributes of a file. We have fxp_stat which works |
651 | * on filenames, and fxp_fstat which works on open file handles. |
652 | */ |
653 | int fxp_stat(char *fname, struct fxp_attrs *attrs) |
654 | { |
655 | struct sftp_packet *pktin, *pktout; |
656 | int id; |
657 | |
658 | pktout = sftp_pkt_init(SSH_FXP_STAT); |
659 | sftp_pkt_adduint32(pktout, 0x678); /* request id */ |
660 | sftp_pkt_addstring(pktout, fname); |
661 | sftp_send(pktout); |
662 | pktin = sftp_recv(); |
f65c4643 |
663 | if (!pktin) { |
664 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
665 | return 0; |
666 | } |
d92624dc |
667 | id = sftp_pkt_getuint32(pktin); |
668 | if (id != 0x678) { |
669 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
670 | sftp_pkt_free(pktin); |
d92624dc |
671 | return 0; |
672 | } |
673 | |
674 | if (pktin->type == SSH_FXP_ATTRS) { |
675 | *attrs = sftp_pkt_getattrs(pktin); |
41d3adbb |
676 | sftp_pkt_free(pktin); |
d92624dc |
677 | return 1; |
678 | } else { |
679 | fxp_got_status(pktin); |
41d3adbb |
680 | sftp_pkt_free(pktin); |
d92624dc |
681 | return 0; |
682 | } |
683 | } |
684 | |
685 | int fxp_fstat(struct fxp_handle *handle, struct fxp_attrs *attrs) |
686 | { |
687 | struct sftp_packet *pktin, *pktout; |
688 | int id; |
689 | |
690 | pktout = sftp_pkt_init(SSH_FXP_FSTAT); |
691 | sftp_pkt_adduint32(pktout, 0x678); /* request id */ |
9954aaa3 |
692 | sftp_pkt_addstring_start(pktout); |
d92624dc |
693 | sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); |
694 | sftp_send(pktout); |
695 | pktin = sftp_recv(); |
f65c4643 |
696 | if (!pktin) { |
697 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
698 | return 0; |
699 | } |
d92624dc |
700 | id = sftp_pkt_getuint32(pktin); |
701 | if (id != 0x678) { |
702 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
703 | sftp_pkt_free(pktin); |
d92624dc |
704 | return 0; |
705 | } |
706 | |
707 | if (pktin->type == SSH_FXP_ATTRS) { |
708 | *attrs = sftp_pkt_getattrs(pktin); |
41d3adbb |
709 | sftp_pkt_free(pktin); |
d92624dc |
710 | return 1; |
711 | } else { |
712 | fxp_got_status(pktin); |
41d3adbb |
713 | sftp_pkt_free(pktin); |
d92624dc |
714 | return 0; |
715 | } |
716 | } |
717 | |
718 | /* |
719 | * Set the attributes of a file. |
720 | */ |
721 | int fxp_setstat(char *fname, struct fxp_attrs attrs) |
722 | { |
723 | struct sftp_packet *pktin, *pktout; |
724 | int id; |
725 | |
726 | pktout = sftp_pkt_init(SSH_FXP_SETSTAT); |
727 | sftp_pkt_adduint32(pktout, 0x678); /* request id */ |
728 | sftp_pkt_addstring(pktout, fname); |
729 | sftp_pkt_addattrs(pktout, attrs); |
9954aaa3 |
730 | sftp_send(pktout); |
731 | pktin = sftp_recv(); |
f65c4643 |
732 | if (!pktin) { |
733 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
734 | return 0; |
735 | } |
9954aaa3 |
736 | id = sftp_pkt_getuint32(pktin); |
737 | if (id != 0x678) { |
738 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
739 | sftp_pkt_free(pktin); |
9954aaa3 |
740 | return 0; |
741 | } |
742 | id = fxp_got_status(pktin); |
41d3adbb |
743 | sftp_pkt_free(pktin); |
9954aaa3 |
744 | if (id != 1) { |
745 | return 0; |
746 | } |
747 | return 1; |
748 | } |
fd5e5847 |
749 | int fxp_fsetstat(struct fxp_handle *handle, struct fxp_attrs attrs) |
750 | { |
751 | struct sftp_packet *pktin, *pktout; |
752 | int id; |
753 | |
754 | pktout = sftp_pkt_init(SSH_FXP_FSETSTAT); |
755 | sftp_pkt_adduint32(pktout, 0x678); /* request id */ |
756 | sftp_pkt_addstring_start(pktout); |
757 | sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); |
758 | sftp_pkt_addattrs(pktout, attrs); |
759 | sftp_send(pktout); |
760 | pktin = sftp_recv(); |
f65c4643 |
761 | if (!pktin) { |
762 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
763 | return 0; |
764 | } |
fd5e5847 |
765 | id = sftp_pkt_getuint32(pktin); |
766 | if (id != 0x678) { |
767 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
768 | sftp_pkt_free(pktin); |
fd5e5847 |
769 | return 0; |
770 | } |
771 | id = fxp_got_status(pktin); |
41d3adbb |
772 | sftp_pkt_free(pktin); |
fd5e5847 |
773 | if (id != 1) { |
774 | return 0; |
775 | } |
776 | return 1; |
777 | } |
9954aaa3 |
778 | |
4c7f0d61 |
779 | /* |
780 | * Read from a file. Returns the number of bytes read, or -1 on an |
781 | * error, or possibly 0 if EOF. (I'm not entirely sure whether it |
782 | * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the |
783 | * error indicator. It might even depend on the SFTP server.) |
784 | */ |
32874aea |
785 | int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset, |
786 | int len) |
787 | { |
4c7f0d61 |
788 | struct sftp_packet *pktin, *pktout; |
789 | int id; |
790 | |
791 | pktout = sftp_pkt_init(SSH_FXP_READ); |
792 | sftp_pkt_adduint32(pktout, 0xBCD); /* request id */ |
f9e162aa |
793 | sftp_pkt_addstring_start(pktout); |
794 | sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); |
4c7f0d61 |
795 | sftp_pkt_adduint64(pktout, offset); |
796 | sftp_pkt_adduint32(pktout, len); |
797 | sftp_send(pktout); |
798 | pktin = sftp_recv(); |
f65c4643 |
799 | if (!pktin) { |
800 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
801 | return -1; |
802 | } |
4c7f0d61 |
803 | id = sftp_pkt_getuint32(pktin); |
804 | if (id != 0xBCD) { |
805 | fxp_internal_error("request ID mismatch"); |
41d3adbb |
806 | sftp_pkt_free(pktin); |
4a8fc3c4 |
807 | return -1; |
4c7f0d61 |
808 | } |
809 | if (pktin->type == SSH_FXP_DATA) { |
810 | char *str; |
811 | int rlen; |
812 | |
813 | sftp_pkt_getstring(pktin, &str, &rlen); |
814 | |
815 | if (rlen > len || rlen < 0) { |
816 | fxp_internal_error("READ returned more bytes than requested"); |
41d3adbb |
817 | sftp_pkt_free(pktin); |
4c7f0d61 |
818 | return -1; |
819 | } |
820 | |
821 | memcpy(buffer, str, rlen); |
41d3adbb |
822 | sftp_pkt_free(pktin); |
4c7f0d61 |
823 | return rlen; |
824 | } else { |
825 | fxp_got_status(pktin); |
41d3adbb |
826 | sftp_pkt_free(pktin); |
4c7f0d61 |
827 | return -1; |
828 | } |
829 | } |
830 | |
831 | /* |
832 | * Read from a directory. |
833 | */ |
32874aea |
834 | struct fxp_names *fxp_readdir(struct fxp_handle *handle) |
835 | { |
4c7f0d61 |
836 | struct sftp_packet *pktin, *pktout; |
837 | int id; |
838 | |
839 | pktout = sftp_pkt_init(SSH_FXP_READDIR); |
840 | sftp_pkt_adduint32(pktout, 0xABC); /* request id */ |
f9e162aa |
841 | sftp_pkt_addstring_start(pktout); |
842 | sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); |
4c7f0d61 |
843 | sftp_send(pktout); |
844 | pktin = sftp_recv(); |
f65c4643 |
845 | if (!pktin) { |
846 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
847 | return NULL; |
848 | } |
4c7f0d61 |
849 | id = sftp_pkt_getuint32(pktin); |
850 | if (id != 0xABC) { |
851 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
852 | sftp_pkt_free(pktin); |
4a8fc3c4 |
853 | return NULL; |
4c7f0d61 |
854 | } |
855 | if (pktin->type == SSH_FXP_NAME) { |
856 | struct fxp_names *ret; |
857 | int i; |
3d88e64d |
858 | ret = snew(struct fxp_names); |
4c7f0d61 |
859 | ret->nnames = sftp_pkt_getuint32(pktin); |
3d88e64d |
860 | ret->names = snewn(ret->nnames, struct fxp_name); |
4c7f0d61 |
861 | for (i = 0; i < ret->nnames; i++) { |
862 | char *str; |
863 | int len; |
864 | sftp_pkt_getstring(pktin, &str, &len); |
865 | ret->names[i].filename = mkstr(str, len); |
866 | sftp_pkt_getstring(pktin, &str, &len); |
867 | ret->names[i].longname = mkstr(str, len); |
868 | ret->names[i].attrs = sftp_pkt_getattrs(pktin); |
869 | } |
41d3adbb |
870 | sftp_pkt_free(pktin); |
4c7f0d61 |
871 | return ret; |
872 | } else { |
873 | fxp_got_status(pktin); |
41d3adbb |
874 | sftp_pkt_free(pktin); |
4c7f0d61 |
875 | return NULL; |
876 | } |
877 | } |
878 | |
879 | /* |
880 | * Write to a file. Returns 0 on error, 1 on OK. |
881 | */ |
32874aea |
882 | int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset, |
883 | int len) |
884 | { |
4c7f0d61 |
885 | struct sftp_packet *pktin, *pktout; |
886 | int id; |
887 | |
888 | pktout = sftp_pkt_init(SSH_FXP_WRITE); |
889 | sftp_pkt_adduint32(pktout, 0xDCB); /* request id */ |
f9e162aa |
890 | sftp_pkt_addstring_start(pktout); |
891 | sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); |
4c7f0d61 |
892 | sftp_pkt_adduint64(pktout, offset); |
893 | sftp_pkt_addstring_start(pktout); |
894 | sftp_pkt_addstring_data(pktout, buffer, len); |
895 | sftp_send(pktout); |
896 | pktin = sftp_recv(); |
f65c4643 |
897 | if (!pktin) { |
898 | fxp_internal_error("did not receive a valid SFTP packet\n"); |
899 | return 0; |
900 | } |
4c7f0d61 |
901 | id = sftp_pkt_getuint32(pktin); |
4a8fc3c4 |
902 | if (id != 0xDCB) { |
903 | fxp_internal_error("request ID mismatch\n"); |
41d3adbb |
904 | sftp_pkt_free(pktin); |
d8770b12 |
905 | return 0; |
4a8fc3c4 |
906 | } |
4c7f0d61 |
907 | fxp_got_status(pktin); |
41d3adbb |
908 | sftp_pkt_free(pktin); |
4c7f0d61 |
909 | return fxp_errtype == SSH_FX_OK; |
910 | } |
911 | |
912 | /* |
913 | * Free up an fxp_names structure. |
914 | */ |
32874aea |
915 | void fxp_free_names(struct fxp_names *names) |
916 | { |
4c7f0d61 |
917 | int i; |
918 | |
919 | for (i = 0; i < names->nnames; i++) { |
920 | sfree(names->names[i].filename); |
921 | sfree(names->names[i].longname); |
922 | } |
923 | sfree(names->names); |
924 | sfree(names); |
925 | } |
7d2c1789 |
926 | |
927 | /* |
928 | * Duplicate an fxp_name structure. |
929 | */ |
930 | struct fxp_name *fxp_dup_name(struct fxp_name *name) |
931 | { |
932 | struct fxp_name *ret; |
3d88e64d |
933 | ret = snew(struct fxp_name); |
7d2c1789 |
934 | ret->filename = dupstr(name->filename); |
935 | ret->longname = dupstr(name->longname); |
936 | ret->attrs = name->attrs; /* structure copy */ |
937 | return ret; |
938 | } |
939 | |
940 | /* |
941 | * Free up an fxp_name structure. |
942 | */ |
943 | void fxp_free_name(struct fxp_name *name) |
944 | { |
7d2c1789 |
945 | sfree(name->filename); |
946 | sfree(name->longname); |
947 | sfree(name); |
948 | } |