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