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