First phase of SFTP re-engineering. Each base-level fxp_* function
[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
357 return req;
358}
359
360/* ----------------------------------------------------------------------
4c7f0d61 361 * String handling routines.
362 */
363
32874aea 364static char *mkstr(char *s, int len)
365{
3d88e64d 366 char *p = snewn(len + 1, char);
4c7f0d61 367 memcpy(p, s, len);
368 p[len] = '\0';
369 return p;
370}
371
372/* ----------------------------------------------------------------------
373 * SFTP primitives.
374 */
375
4c7f0d61 376/*
377 * Deal with (and free) an FXP_STATUS packet. Return 1 if
378 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
379 * Also place the status into fxp_errtype.
380 */
32874aea 381static int fxp_got_status(struct sftp_packet *pktin)
382{
4c7f0d61 383 static const char *const messages[] = {
384 /* SSH_FX_OK. The only time we will display a _message_ for this
385 * is if we were expecting something other than FXP_STATUS on
386 * success, so this is actually an error message! */
387 "unexpected OK response",
388 "end of file",
389 "no such file or directory",
390 "permission denied",
391 "failure",
392 "bad message",
393 "no connection",
394 "connection lost",
395 "operation unsupported",
396 };
397
398 if (pktin->type != SSH_FXP_STATUS) {
399 fxp_error_message = "expected FXP_STATUS packet";
400 fxp_errtype = -1;
401 } else {
402 fxp_errtype = sftp_pkt_getuint32(pktin);
403 if (fxp_errtype < 0 ||
32874aea 404 fxp_errtype >= sizeof(messages) / sizeof(*messages))
405 fxp_error_message = "unknown error code";
4c7f0d61 406 else
407 fxp_error_message = messages[fxp_errtype];
408 }
409
410 if (fxp_errtype == SSH_FX_OK)
411 return 1;
412 else if (fxp_errtype == SSH_FX_EOF)
413 return 0;
414 else
415 return -1;
416}
417
32874aea 418static void fxp_internal_error(char *msg)
419{
4c7f0d61 420 fxp_error_message = msg;
421 fxp_errtype = -1;
422}
423
32874aea 424const char *fxp_error(void)
425{
4c7f0d61 426 return fxp_error_message;
427}
428
32874aea 429int fxp_error_type(void)
430{
4c7f0d61 431 return fxp_errtype;
432}
433
434/*
435 * Perform exchange of init/version packets. Return 0 on failure.
436 */
32874aea 437int fxp_init(void)
438{
4c7f0d61 439 struct sftp_packet *pktout, *pktin;
440 int remotever;
441
442 pktout = sftp_pkt_init(SSH_FXP_INIT);
443 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
444 sftp_send(pktout);
445
446 pktin = sftp_recv();
0d694692 447 if (!pktin) {
448 fxp_internal_error("could not connect");
449 return 0;
450 }
4c7f0d61 451 if (pktin->type != SSH_FXP_VERSION) {
452 fxp_internal_error("did not receive FXP_VERSION");
41d3adbb 453 sftp_pkt_free(pktin);
4c7f0d61 454 return 0;
455 }
456 remotever = sftp_pkt_getuint32(pktin);
457 if (remotever > SFTP_PROTO_VERSION) {
32874aea 458 fxp_internal_error
459 ("remote protocol is more advanced than we support");
41d3adbb 460 sftp_pkt_free(pktin);
4c7f0d61 461 return 0;
462 }
463 /*
464 * In principle, this packet might also contain extension-
465 * string pairs. We should work through them and look for any
466 * we recognise. In practice we don't currently do so because
467 * we know we don't recognise _any_.
468 */
469 sftp_pkt_free(pktin);
470
471 return 1;
472}
473
474/*
f9e162aa 475 * Canonify a pathname.
4c7f0d61 476 */
1bc24185 477struct sftp_request *fxp_realpath_send(char *path)
32874aea 478{
1bc24185 479 struct sftp_request *req = sftp_alloc_request();
480 struct sftp_packet *pktout;
4c7f0d61 481
482 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
1bc24185 483 sftp_pkt_adduint32(pktout, req->id);
4c7f0d61 484 sftp_pkt_addstring_start(pktout);
485 sftp_pkt_addstring_str(pktout, path);
4c7f0d61 486 sftp_send(pktout);
1bc24185 487
488 return req;
489}
490
491char *fxp_realpath_recv(struct sftp_packet *pktin)
492{
4c7f0d61 493 if (pktin->type == SSH_FXP_NAME) {
494 int count;
495 char *path;
496 int len;
497
498 count = sftp_pkt_getuint32(pktin);
499 if (count != 1) {
500 fxp_internal_error("REALPATH returned name count != 1\n");
41d3adbb 501 sftp_pkt_free(pktin);
4c7f0d61 502 return NULL;
503 }
504 sftp_pkt_getstring(pktin, &path, &len);
505 if (!path) {
506 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
41d3adbb 507 sftp_pkt_free(pktin);
4c7f0d61 508 return NULL;
509 }
510 path = mkstr(path, len);
511 sftp_pkt_free(pktin);
512 return path;
513 } else {
514 fxp_got_status(pktin);
41d3adbb 515 sftp_pkt_free(pktin);
4c7f0d61 516 return NULL;
517 }
518}
519
520/*
521 * Open a file.
522 */
1bc24185 523struct sftp_request *fxp_open_send(char *path, int type)
32874aea 524{
1bc24185 525 struct sftp_request *req = sftp_alloc_request();
526 struct sftp_packet *pktout;
4c7f0d61 527
528 pktout = sftp_pkt_init(SSH_FXP_OPEN);
1bc24185 529 sftp_pkt_adduint32(pktout, req->id);
4c7f0d61 530 sftp_pkt_addstring(pktout, path);
531 sftp_pkt_adduint32(pktout, type);
532 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
533 sftp_send(pktout);
1bc24185 534
535 return req;
536}
537
538struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin)
539{
4c7f0d61 540 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 541 char *hstring;
542 struct fxp_handle *handle;
543 int len;
544
545 sftp_pkt_getstring(pktin, &hstring, &len);
546 if (!hstring) {
547 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
41d3adbb 548 sftp_pkt_free(pktin);
4c7f0d61 549 return NULL;
550 }
3d88e64d 551 handle = snew(struct fxp_handle);
4c7f0d61 552 handle->hstring = mkstr(hstring, len);
f9e162aa 553 handle->hlen = len;
4c7f0d61 554 sftp_pkt_free(pktin);
555 return handle;
556 } else {
557 fxp_got_status(pktin);
41d3adbb 558 sftp_pkt_free(pktin);
4c7f0d61 559 return NULL;
560 }
561}
562
563/*
564 * Open a directory.
565 */
1bc24185 566struct sftp_request *fxp_opendir_send(char *path)
32874aea 567{
1bc24185 568 struct sftp_request *req = sftp_alloc_request();
569 struct sftp_packet *pktout;
4c7f0d61 570
571 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
1bc24185 572 sftp_pkt_adduint32(pktout, req->id);
4c7f0d61 573 sftp_pkt_addstring(pktout, path);
574 sftp_send(pktout);
1bc24185 575
576 return req;
577}
578
579struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin)
580{
4c7f0d61 581 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 582 char *hstring;
583 struct fxp_handle *handle;
584 int len;
585
586 sftp_pkt_getstring(pktin, &hstring, &len);
587 if (!hstring) {
588 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
41d3adbb 589 sftp_pkt_free(pktin);
4c7f0d61 590 return NULL;
591 }
3d88e64d 592 handle = snew(struct fxp_handle);
4c7f0d61 593 handle->hstring = mkstr(hstring, len);
f9e162aa 594 handle->hlen = len;
4c7f0d61 595 sftp_pkt_free(pktin);
596 return handle;
597 } else {
598 fxp_got_status(pktin);
41d3adbb 599 sftp_pkt_free(pktin);
4c7f0d61 600 return NULL;
601 }
602}
603
604/*
605 * Close a file/dir.
606 */
1bc24185 607struct sftp_request *fxp_close_send(struct fxp_handle *handle)
32874aea 608{
1bc24185 609 struct sftp_request *req = sftp_alloc_request();
610 struct sftp_packet *pktout;
4c7f0d61 611
612 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
1bc24185 613 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 614 sftp_pkt_addstring_start(pktout);
615 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 616 sftp_send(pktout);
1bc24185 617
4c7f0d61 618 sfree(handle->hstring);
619 sfree(handle);
1bc24185 620
621 return req;
622}
623
624void fxp_close_recv(struct sftp_packet *pktin)
625{
626 fxp_got_status(pktin);
627 sftp_pkt_free(pktin);
4c7f0d61 628}
629
1bc24185 630struct sftp_request *fxp_mkdir_send(char *path)
9954aaa3 631{
1bc24185 632 struct sftp_request *req = sftp_alloc_request();
633 struct sftp_packet *pktout;
9954aaa3 634
635 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
1bc24185 636 sftp_pkt_adduint32(pktout, req->id);
d92624dc 637 sftp_pkt_addstring(pktout, path);
9954aaa3 638 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
639 sftp_send(pktout);
1bc24185 640
641 return req;
642}
643
644int fxp_mkdir_recv(struct sftp_packet *pktin)
645{
646 int id = fxp_got_status(pktin);
41d3adbb 647 sftp_pkt_free(pktin);
9954aaa3 648 if (id != 1) {
649 return 0;
650 }
651 return 1;
652}
653
1bc24185 654struct sftp_request *fxp_rmdir_send(char *path)
9954aaa3 655{
1bc24185 656 struct sftp_request *req = sftp_alloc_request();
657 struct sftp_packet *pktout;
9954aaa3 658
659 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
1bc24185 660 sftp_pkt_adduint32(pktout, req->id);
d92624dc 661 sftp_pkt_addstring(pktout, path);
9954aaa3 662 sftp_send(pktout);
1bc24185 663
664 return req;
665}
666
667int fxp_rmdir_recv(struct sftp_packet *pktin)
668{
669 int id = fxp_got_status(pktin);
41d3adbb 670 sftp_pkt_free(pktin);
9954aaa3 671 if (id != 1) {
672 return 0;
673 }
674 return 1;
675}
676
1bc24185 677struct sftp_request *fxp_remove_send(char *fname)
9954aaa3 678{
1bc24185 679 struct sftp_request *req = sftp_alloc_request();
680 struct sftp_packet *pktout;
9954aaa3 681
682 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
1bc24185 683 sftp_pkt_adduint32(pktout, req->id);
d92624dc 684 sftp_pkt_addstring(pktout, fname);
685 sftp_send(pktout);
1bc24185 686
687 return req;
688}
689
690int fxp_remove_recv(struct sftp_packet *pktin)
691{
692 int id = fxp_got_status(pktin);
41d3adbb 693 sftp_pkt_free(pktin);
d92624dc 694 if (id != 1) {
695 return 0;
696 }
697 return 1;
698}
699
1bc24185 700struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
d92624dc 701{
1bc24185 702 struct sftp_request *req = sftp_alloc_request();
703 struct sftp_packet *pktout;
d92624dc 704
705 pktout = sftp_pkt_init(SSH_FXP_RENAME);
1bc24185 706 sftp_pkt_adduint32(pktout, req->id);
d92624dc 707 sftp_pkt_addstring(pktout, srcfname);
708 sftp_pkt_addstring(pktout, dstfname);
709 sftp_send(pktout);
1bc24185 710
711 return req;
712}
713
714int fxp_rename_recv(struct sftp_packet *pktin)
715{
716 int id = fxp_got_status(pktin);
41d3adbb 717 sftp_pkt_free(pktin);
d92624dc 718 if (id != 1) {
719 return 0;
720 }
721 return 1;
722}
723
724/*
725 * Retrieve the attributes of a file. We have fxp_stat which works
726 * on filenames, and fxp_fstat which works on open file handles.
727 */
1bc24185 728struct sftp_request *fxp_stat_send(char *fname)
d92624dc 729{
1bc24185 730 struct sftp_request *req = sftp_alloc_request();
731 struct sftp_packet *pktout;
d92624dc 732
733 pktout = sftp_pkt_init(SSH_FXP_STAT);
1bc24185 734 sftp_pkt_adduint32(pktout, req->id);
d92624dc 735 sftp_pkt_addstring(pktout, fname);
736 sftp_send(pktout);
d92624dc 737
1bc24185 738 return req;
739}
740
741int fxp_stat_recv(struct sftp_packet *pktin, struct fxp_attrs *attrs)
742{
d92624dc 743 if (pktin->type == SSH_FXP_ATTRS) {
744 *attrs = sftp_pkt_getattrs(pktin);
41d3adbb 745 sftp_pkt_free(pktin);
d92624dc 746 return 1;
747 } else {
748 fxp_got_status(pktin);
41d3adbb 749 sftp_pkt_free(pktin);
d92624dc 750 return 0;
751 }
752}
753
1bc24185 754struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
d92624dc 755{
1bc24185 756 struct sftp_request *req = sftp_alloc_request();
757 struct sftp_packet *pktout;
d92624dc 758
759 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
1bc24185 760 sftp_pkt_adduint32(pktout, req->id);
9954aaa3 761 sftp_pkt_addstring_start(pktout);
d92624dc 762 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
763 sftp_send(pktout);
d92624dc 764
1bc24185 765 return req;
766}
767
768int fxp_fstat_recv(struct sftp_packet *pktin,
769 struct fxp_attrs *attrs)
770{
d92624dc 771 if (pktin->type == SSH_FXP_ATTRS) {
772 *attrs = sftp_pkt_getattrs(pktin);
41d3adbb 773 sftp_pkt_free(pktin);
d92624dc 774 return 1;
775 } else {
776 fxp_got_status(pktin);
41d3adbb 777 sftp_pkt_free(pktin);
d92624dc 778 return 0;
779 }
780}
781
782/*
783 * Set the attributes of a file.
784 */
1bc24185 785struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
d92624dc 786{
1bc24185 787 struct sftp_request *req = sftp_alloc_request();
788 struct sftp_packet *pktout;
d92624dc 789
790 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
1bc24185 791 sftp_pkt_adduint32(pktout, req->id);
d92624dc 792 sftp_pkt_addstring(pktout, fname);
793 sftp_pkt_addattrs(pktout, attrs);
9954aaa3 794 sftp_send(pktout);
1bc24185 795
796 return req;
797}
798
799int fxp_setstat_recv(struct sftp_packet *pktin)
800{
801 int id = fxp_got_status(pktin);
41d3adbb 802 sftp_pkt_free(pktin);
9954aaa3 803 if (id != 1) {
804 return 0;
805 }
806 return 1;
807}
1bc24185 808
809struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
810 struct fxp_attrs attrs)
fd5e5847 811{
1bc24185 812 struct sftp_request *req = sftp_alloc_request();
813 struct sftp_packet *pktout;
fd5e5847 814
815 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
1bc24185 816 sftp_pkt_adduint32(pktout, req->id);
fd5e5847 817 sftp_pkt_addstring_start(pktout);
818 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
819 sftp_pkt_addattrs(pktout, attrs);
820 sftp_send(pktout);
1bc24185 821
822 return req;
823}
824
825int fxp_fsetstat_recv(struct sftp_packet *pktin)
826{
827 int id = fxp_got_status(pktin);
41d3adbb 828 sftp_pkt_free(pktin);
fd5e5847 829 if (id != 1) {
830 return 0;
831 }
832 return 1;
833}
9954aaa3 834
4c7f0d61 835/*
836 * Read from a file. Returns the number of bytes read, or -1 on an
837 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
838 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
839 * error indicator. It might even depend on the SFTP server.)
840 */
1bc24185 841struct sftp_request *fxp_read_send(struct fxp_handle *handle,
842 uint64 offset, int len)
32874aea 843{
1bc24185 844 struct sftp_request *req = sftp_alloc_request();
845 struct sftp_packet *pktout;
4c7f0d61 846
847 pktout = sftp_pkt_init(SSH_FXP_READ);
1bc24185 848 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 849 sftp_pkt_addstring_start(pktout);
850 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 851 sftp_pkt_adduint64(pktout, offset);
852 sftp_pkt_adduint32(pktout, len);
853 sftp_send(pktout);
1bc24185 854
855 return req;
856}
857
858int fxp_read_recv(struct sftp_packet *pktin, char *buffer, int len)
859{
4c7f0d61 860 if (pktin->type == SSH_FXP_DATA) {
861 char *str;
862 int rlen;
863
864 sftp_pkt_getstring(pktin, &str, &rlen);
865
866 if (rlen > len || rlen < 0) {
867 fxp_internal_error("READ returned more bytes than requested");
41d3adbb 868 sftp_pkt_free(pktin);
4c7f0d61 869 return -1;
870 }
871
872 memcpy(buffer, str, rlen);
41d3adbb 873 sftp_pkt_free(pktin);
4c7f0d61 874 return rlen;
875 } else {
876 fxp_got_status(pktin);
41d3adbb 877 sftp_pkt_free(pktin);
4c7f0d61 878 return -1;
879 }
880}
881
882/*
883 * Read from a directory.
884 */
1bc24185 885struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
32874aea 886{
1bc24185 887 struct sftp_request *req = sftp_alloc_request();
888 struct sftp_packet *pktout;
4c7f0d61 889
890 pktout = sftp_pkt_init(SSH_FXP_READDIR);
1bc24185 891 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 892 sftp_pkt_addstring_start(pktout);
893 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 894 sftp_send(pktout);
1bc24185 895
896 return req;
897}
898
899struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin)
900{
4c7f0d61 901 if (pktin->type == SSH_FXP_NAME) {
902 struct fxp_names *ret;
903 int i;
3d88e64d 904 ret = snew(struct fxp_names);
4c7f0d61 905 ret->nnames = sftp_pkt_getuint32(pktin);
3d88e64d 906 ret->names = snewn(ret->nnames, struct fxp_name);
4c7f0d61 907 for (i = 0; i < ret->nnames; i++) {
908 char *str;
909 int len;
910 sftp_pkt_getstring(pktin, &str, &len);
911 ret->names[i].filename = mkstr(str, len);
912 sftp_pkt_getstring(pktin, &str, &len);
913 ret->names[i].longname = mkstr(str, len);
914 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
915 }
41d3adbb 916 sftp_pkt_free(pktin);
4c7f0d61 917 return ret;
918 } else {
919 fxp_got_status(pktin);
41d3adbb 920 sftp_pkt_free(pktin);
4c7f0d61 921 return NULL;
922 }
923}
924
925/*
926 * Write to a file. Returns 0 on error, 1 on OK.
927 */
1bc24185 928struct sftp_request *fxp_write_send(struct fxp_handle *handle,
929 char *buffer, uint64 offset, int len)
32874aea 930{
1bc24185 931 struct sftp_request *req = sftp_alloc_request();
932 struct sftp_packet *pktout;
4c7f0d61 933
934 pktout = sftp_pkt_init(SSH_FXP_WRITE);
1bc24185 935 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 936 sftp_pkt_addstring_start(pktout);
937 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 938 sftp_pkt_adduint64(pktout, offset);
939 sftp_pkt_addstring_start(pktout);
940 sftp_pkt_addstring_data(pktout, buffer, len);
941 sftp_send(pktout);
1bc24185 942
943 return req;
944}
945
946int fxp_write_recv(struct sftp_packet *pktin)
947{
4c7f0d61 948 fxp_got_status(pktin);
41d3adbb 949 sftp_pkt_free(pktin);
4c7f0d61 950 return fxp_errtype == SSH_FX_OK;
951}
952
953/*
954 * Free up an fxp_names structure.
955 */
32874aea 956void fxp_free_names(struct fxp_names *names)
957{
4c7f0d61 958 int i;
959
960 for (i = 0; i < names->nnames; i++) {
961 sfree(names->names[i].filename);
962 sfree(names->names[i].longname);
963 }
964 sfree(names->names);
965 sfree(names);
966}
7d2c1789 967
968/*
969 * Duplicate an fxp_name structure.
970 */
971struct fxp_name *fxp_dup_name(struct fxp_name *name)
972{
973 struct fxp_name *ret;
3d88e64d 974 ret = snew(struct fxp_name);
7d2c1789 975 ret->filename = dupstr(name->filename);
976 ret->longname = dupstr(name->longname);
977 ret->attrs = name->attrs; /* structure copy */
978 return ret;
979}
980
981/*
982 * Free up an fxp_name structure.
983 */
984void fxp_free_name(struct fxp_name *name)
985{
7d2c1789 986 sfree(name->filename);
987 sfree(name->longname);
988 sfree(name);
989}