Add RT_MANIFEST arguments to dependencies for Windows resource objects.
[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>
c606c42d 9#include <limits.h>
4c7f0d61 10
78139cd8 11#include "misc.h"
4c7f0d61 12#include "int64.h"
1bc24185 13#include "tree234.h"
4c7f0d61 14#include "sftp.h"
15
4c7f0d61 16#define GET_32BIT(cp) \
17 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
18 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
19 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
20 ((unsigned long)(unsigned char)(cp)[3]))
21
22#define PUT_32BIT(cp, value) { \
23 (cp)[0] = (unsigned char)((value) >> 24); \
24 (cp)[1] = (unsigned char)((value) >> 16); \
25 (cp)[2] = (unsigned char)((value) >> 8); \
26 (cp)[3] = (unsigned char)(value); }
27
28struct sftp_packet {
29 char *data;
46cfeac8 30 unsigned length, maxlen;
31 unsigned savedpos;
4c7f0d61 32 int type;
33};
34
9954aaa3 35static const char *fxp_error_message;
36static int fxp_errtype;
37
1bc24185 38static void fxp_internal_error(char *msg);
39
4c7f0d61 40/* ----------------------------------------------------------------------
41 * SFTP packet construction functions.
42 */
32874aea 43static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
44{
4c7f0d61 45 if (pkt->maxlen < length) {
32874aea 46 pkt->maxlen = length + 256;
3d88e64d 47 pkt->data = sresize(pkt->data, pkt->maxlen, char);
4c7f0d61 48 }
49}
32874aea 50static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
51{
4c7f0d61 52 pkt->length += len;
53 sftp_pkt_ensure(pkt, pkt->length);
32874aea 54 memcpy(pkt->data + pkt->length - len, data, len);
4c7f0d61 55}
32874aea 56static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
57{
4c7f0d61 58 sftp_pkt_adddata(pkt, &byte, 1);
59}
32874aea 60static struct sftp_packet *sftp_pkt_init(int pkt_type)
61{
4c7f0d61 62 struct sftp_packet *pkt;
3d88e64d 63 pkt = snew(struct sftp_packet);
4c7f0d61 64 pkt->data = NULL;
65 pkt->savedpos = -1;
66 pkt->length = 0;
67 pkt->maxlen = 0;
32874aea 68 sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
4c7f0d61 69 return pkt;
70}
b51259f6 71/*
32874aea 72static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
73{
4c7f0d61 74 sftp_pkt_adddata(pkt, &value, 1);
75}
b51259f6 76*/
32874aea 77static void sftp_pkt_adduint32(struct sftp_packet *pkt,
78 unsigned long value)
79{
4c7f0d61 80 unsigned char x[4];
81 PUT_32BIT(x, value);
82 sftp_pkt_adddata(pkt, x, 4);
83}
32874aea 84static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
85{
4c7f0d61 86 unsigned char x[8];
87 PUT_32BIT(x, value.hi);
32874aea 88 PUT_32BIT(x + 4, value.lo);
4c7f0d61 89 sftp_pkt_adddata(pkt, x, 8);
90}
32874aea 91static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
92{
4c7f0d61 93 sftp_pkt_adduint32(pkt, 0);
94 pkt->savedpos = pkt->length;
95}
32874aea 96static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
97{
4c7f0d61 98 sftp_pkt_adddata(pkt, data, strlen(data));
32874aea 99 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
4c7f0d61 100}
101static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
32874aea 102 char *data, int len)
103{
4c7f0d61 104 sftp_pkt_adddata(pkt, data, len);
32874aea 105 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
4c7f0d61 106}
32874aea 107static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
108{
4c7f0d61 109 sftp_pkt_addstring_start(pkt);
110 sftp_pkt_addstring_str(pkt, data);
111}
d92624dc 112static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
113{
114 sftp_pkt_adduint32(pkt, attrs.flags);
115 if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
116 sftp_pkt_adduint32(pkt, attrs.size.hi);
117 sftp_pkt_adduint32(pkt, attrs.size.lo);
118 }
119 if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
120 sftp_pkt_adduint32(pkt, attrs.uid);
121 sftp_pkt_adduint32(pkt, attrs.gid);
122 }
123 if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
124 sftp_pkt_adduint32(pkt, attrs.permissions);
125 }
126 if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
127 sftp_pkt_adduint32(pkt, attrs.atime);
128 sftp_pkt_adduint32(pkt, attrs.mtime);
129 }
130 if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
131 /*
132 * We currently don't support sending any extended
133 * attributes.
134 */
135 }
136}
4c7f0d61 137
138/* ----------------------------------------------------------------------
139 * SFTP packet decode functions.
140 */
141
46cfeac8 142static int sftp_pkt_getbyte(struct sftp_packet *pkt, unsigned char *ret)
32874aea 143{
4c7f0d61 144 if (pkt->length - pkt->savedpos < 1)
46cfeac8 145 return 0;
146 *ret = (unsigned char) pkt->data[pkt->savedpos];
4c7f0d61 147 pkt->savedpos++;
46cfeac8 148 return 1;
4c7f0d61 149}
46cfeac8 150static int sftp_pkt_getuint32(struct sftp_packet *pkt, unsigned long *ret)
32874aea 151{
4c7f0d61 152 if (pkt->length - pkt->savedpos < 4)
46cfeac8 153 return 0;
154 *ret = GET_32BIT(pkt->data + pkt->savedpos);
4c7f0d61 155 pkt->savedpos += 4;
46cfeac8 156 return 1;
4c7f0d61 157}
46cfeac8 158static int sftp_pkt_getstring(struct sftp_packet *pkt,
159 char **p, int *length)
32874aea 160{
4c7f0d61 161 *p = NULL;
162 if (pkt->length - pkt->savedpos < 4)
46cfeac8 163 return 0;
32874aea 164 *length = GET_32BIT(pkt->data + pkt->savedpos);
4c7f0d61 165 pkt->savedpos += 4;
46cfeac8 166 if (pkt->length - pkt->savedpos < *length || *length < 0) {
167 *length = 0;
168 return 0;
169 }
32874aea 170 *p = pkt->data + pkt->savedpos;
4c7f0d61 171 pkt->savedpos += *length;
46cfeac8 172 return 1;
4c7f0d61 173}
46cfeac8 174static int sftp_pkt_getattrs(struct sftp_packet *pkt, struct fxp_attrs *ret)
32874aea 175{
46cfeac8 176 if (!sftp_pkt_getuint32(pkt, &ret->flags))
177 return 0;
178 if (ret->flags & SSH_FILEXFER_ATTR_SIZE) {
4c7f0d61 179 unsigned long hi, lo;
46cfeac8 180 if (!sftp_pkt_getuint32(pkt, &hi) ||
181 !sftp_pkt_getuint32(pkt, &lo))
182 return 0;
183 ret->size = uint64_make(hi, lo);
4c7f0d61 184 }
46cfeac8 185 if (ret->flags & SSH_FILEXFER_ATTR_UIDGID) {
186 if (!sftp_pkt_getuint32(pkt, &ret->uid) ||
187 !sftp_pkt_getuint32(pkt, &ret->gid))
188 return 0;
4c7f0d61 189 }
46cfeac8 190 if (ret->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
191 if (!sftp_pkt_getuint32(pkt, &ret->permissions))
192 return 0;
4c7f0d61 193 }
46cfeac8 194 if (ret->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
195 if (!sftp_pkt_getuint32(pkt, &ret->atime) ||
196 !sftp_pkt_getuint32(pkt, &ret->mtime))
197 return 0;
4c7f0d61 198 }
46cfeac8 199 if (ret->flags & SSH_FILEXFER_ATTR_EXTENDED) {
200 unsigned long count;
201 if (!sftp_pkt_getuint32(pkt, &count))
202 return 0;
4c7f0d61 203 while (count--) {
204 char *str;
205 int len;
206 /*
207 * We should try to analyse these, if we ever find one
208 * we recognise.
209 */
46cfeac8 210 if (!sftp_pkt_getstring(pkt, &str, &len) ||
211 !sftp_pkt_getstring(pkt, &str, &len))
212 return 0;
4c7f0d61 213 }
214 }
46cfeac8 215 return 1;
4c7f0d61 216}
32874aea 217static void sftp_pkt_free(struct sftp_packet *pkt)
218{
219 if (pkt->data)
220 sfree(pkt->data);
4c7f0d61 221 sfree(pkt);
222}
223
224/* ----------------------------------------------------------------------
4a8fc3c4 225 * Send and receive packet functions.
4c7f0d61 226 */
32874aea 227int sftp_send(struct sftp_packet *pkt)
228{
4a8fc3c4 229 int ret;
4c7f0d61 230 char x[4];
231 PUT_32BIT(x, pkt->length);
32874aea 232 ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
4c7f0d61 233 sftp_pkt_free(pkt);
4a8fc3c4 234 return ret;
4c7f0d61 235}
32874aea 236struct sftp_packet *sftp_recv(void)
237{
4c7f0d61 238 struct sftp_packet *pkt;
239 char x[4];
46cfeac8 240 unsigned char uc;
4c7f0d61 241
4a8fc3c4 242 if (!sftp_recvdata(x, 4))
243 return NULL;
4c7f0d61 244
3d88e64d 245 pkt = snew(struct sftp_packet);
4c7f0d61 246 pkt->savedpos = 0;
247 pkt->length = pkt->maxlen = GET_32BIT(x);
3d88e64d 248 pkt->data = snewn(pkt->length, char);
4c7f0d61 249
4a8fc3c4 250 if (!sftp_recvdata(pkt->data, pkt->length)) {
251 sftp_pkt_free(pkt);
252 return NULL;
4c7f0d61 253 }
254
46cfeac8 255 if (!sftp_pkt_getbyte(pkt, &uc)) {
256 sftp_pkt_free(pkt);
257 return NULL;
258 } else {
259 pkt->type = uc;
260 }
4c7f0d61 261
262 return pkt;
263}
264
265/* ----------------------------------------------------------------------
1bc24185 266 * Request ID allocation and temporary dispatch routines.
267 */
268
269#define REQUEST_ID_OFFSET 256
270
271struct sftp_request {
272 unsigned id;
273 int registered;
c606c42d 274 void *userdata;
1bc24185 275};
276
277static int sftp_reqcmp(void *av, void *bv)
278{
279 struct sftp_request *a = (struct sftp_request *)av;
280 struct sftp_request *b = (struct sftp_request *)bv;
281 if (a->id < b->id)
282 return -1;
283 if (a->id > b->id)
284 return +1;
285 return 0;
286}
287static int sftp_reqfind(void *av, void *bv)
288{
289 unsigned *a = (unsigned *) av;
290 struct sftp_request *b = (struct sftp_request *)bv;
291 if (*a < b->id)
292 return -1;
293 if (*a > b->id)
294 return +1;
295 return 0;
296}
297
298static tree234 *sftp_requests;
299
300static struct sftp_request *sftp_alloc_request(void)
301{
1bc24185 302 unsigned low, high, mid;
303 int tsize;
304 struct sftp_request *r;
305
306 if (sftp_requests == NULL)
307 sftp_requests = newtree234(sftp_reqcmp);
308
309 /*
310 * First-fit allocation of request IDs: always pick the lowest
311 * unused one. To do this, binary-search using the counted
312 * B-tree to find the largest ID which is in a contiguous
313 * sequence from the beginning. (Precisely everything in that
314 * sequence must have ID equal to its tree index plus
b51259f6 315 * REQUEST_ID_OFFSET.)
1bc24185 316 */
317 tsize = count234(sftp_requests);
318
319 low = -1;
320 high = tsize;
321 while (high - low > 1) {
322 mid = (high + low) / 2;
323 r = index234(sftp_requests, mid);
324 if (r->id == mid + REQUEST_ID_OFFSET)
325 low = mid; /* this one is fine */
326 else
327 high = mid; /* this one is past it */
328 }
329 /*
330 * Now low points to either -1, or the tree index of the
331 * largest ID in the initial sequence.
332 */
333 {
334 unsigned i = low + 1 + REQUEST_ID_OFFSET;
335 assert(NULL == find234(sftp_requests, &i, sftp_reqfind));
336 }
337
338 /*
339 * So the request ID we need to create is
340 * low + 1 + REQUEST_ID_OFFSET.
341 */
342 r = snew(struct sftp_request);
343 r->id = low + 1 + REQUEST_ID_OFFSET;
344 r->registered = 0;
c606c42d 345 r->userdata = NULL;
1bc24185 346 add234(sftp_requests, r);
347 return r;
348}
349
679539d7 350void sftp_cleanup_request(void)
351{
352 if (sftp_requests == NULL) {
353 freetree234(sftp_requests);
354 sftp_requests = NULL;
355 }
356}
357
1bc24185 358void sftp_register(struct sftp_request *req)
359{
360 req->registered = 1;
361}
362
363struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
364{
365 unsigned long id;
366 struct sftp_request *req;
367
368 if (!pktin) {
369 fxp_internal_error("did not receive a valid SFTP packet\n");
370 return NULL;
371 }
372
46cfeac8 373 if (!sftp_pkt_getuint32(pktin, &id)) {
374 fxp_internal_error("did not receive a valid SFTP packet\n");
375 return NULL;
376 }
1bc24185 377 req = find234(sftp_requests, &id, sftp_reqfind);
378
379 if (!req || !req->registered) {
380 fxp_internal_error("request ID mismatch\n");
381 sftp_pkt_free(pktin);
382 return NULL;
383 }
384
7b7de4f4 385 del234(sftp_requests, req);
386
1bc24185 387 return req;
388}
389
390/* ----------------------------------------------------------------------
4c7f0d61 391 * String handling routines.
392 */
393
32874aea 394static char *mkstr(char *s, int len)
395{
3d88e64d 396 char *p = snewn(len + 1, char);
4c7f0d61 397 memcpy(p, s, len);
398 p[len] = '\0';
399 return p;
400}
401
402/* ----------------------------------------------------------------------
403 * SFTP primitives.
404 */
405
4c7f0d61 406/*
407 * Deal with (and free) an FXP_STATUS packet. Return 1 if
408 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
409 * Also place the status into fxp_errtype.
410 */
32874aea 411static int fxp_got_status(struct sftp_packet *pktin)
412{
4c7f0d61 413 static const char *const messages[] = {
414 /* SSH_FX_OK. The only time we will display a _message_ for this
415 * is if we were expecting something other than FXP_STATUS on
416 * success, so this is actually an error message! */
417 "unexpected OK response",
418 "end of file",
419 "no such file or directory",
420 "permission denied",
421 "failure",
422 "bad message",
423 "no connection",
424 "connection lost",
425 "operation unsupported",
426 };
427
428 if (pktin->type != SSH_FXP_STATUS) {
429 fxp_error_message = "expected FXP_STATUS packet";
430 fxp_errtype = -1;
431 } else {
46cfeac8 432 unsigned long ul;
433 if (!sftp_pkt_getuint32(pktin, &ul)) {
434 fxp_error_message = "malformed FXP_STATUS packet";
435 fxp_errtype = -1;
436 } else {
437 fxp_errtype = ul;
438 if (fxp_errtype < 0 ||
439 fxp_errtype >= sizeof(messages) / sizeof(*messages))
32874aea 440 fxp_error_message = "unknown error code";
46cfeac8 441 else
442 fxp_error_message = messages[fxp_errtype];
443 }
4c7f0d61 444 }
445
446 if (fxp_errtype == SSH_FX_OK)
447 return 1;
448 else if (fxp_errtype == SSH_FX_EOF)
449 return 0;
450 else
451 return -1;
452}
453
32874aea 454static void fxp_internal_error(char *msg)
455{
4c7f0d61 456 fxp_error_message = msg;
457 fxp_errtype = -1;
458}
459
32874aea 460const char *fxp_error(void)
461{
4c7f0d61 462 return fxp_error_message;
463}
464
32874aea 465int fxp_error_type(void)
466{
4c7f0d61 467 return fxp_errtype;
468}
469
470/*
471 * Perform exchange of init/version packets. Return 0 on failure.
472 */
32874aea 473int fxp_init(void)
474{
4c7f0d61 475 struct sftp_packet *pktout, *pktin;
46cfeac8 476 unsigned long remotever;
4c7f0d61 477
478 pktout = sftp_pkt_init(SSH_FXP_INIT);
479 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
480 sftp_send(pktout);
481
482 pktin = sftp_recv();
0d694692 483 if (!pktin) {
484 fxp_internal_error("could not connect");
485 return 0;
486 }
4c7f0d61 487 if (pktin->type != SSH_FXP_VERSION) {
488 fxp_internal_error("did not receive FXP_VERSION");
41d3adbb 489 sftp_pkt_free(pktin);
4c7f0d61 490 return 0;
491 }
46cfeac8 492 if (!sftp_pkt_getuint32(pktin, &remotever)) {
493 fxp_internal_error("malformed FXP_VERSION packet");
494 sftp_pkt_free(pktin);
495 return 0;
496 }
4c7f0d61 497 if (remotever > SFTP_PROTO_VERSION) {
32874aea 498 fxp_internal_error
499 ("remote protocol is more advanced than we support");
41d3adbb 500 sftp_pkt_free(pktin);
4c7f0d61 501 return 0;
502 }
503 /*
504 * In principle, this packet might also contain extension-
505 * string pairs. We should work through them and look for any
506 * we recognise. In practice we don't currently do so because
507 * we know we don't recognise _any_.
508 */
509 sftp_pkt_free(pktin);
510
511 return 1;
512}
513
514/*
f9e162aa 515 * Canonify a pathname.
4c7f0d61 516 */
1bc24185 517struct sftp_request *fxp_realpath_send(char *path)
32874aea 518{
1bc24185 519 struct sftp_request *req = sftp_alloc_request();
520 struct sftp_packet *pktout;
4c7f0d61 521
522 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
1bc24185 523 sftp_pkt_adduint32(pktout, req->id);
4c7f0d61 524 sftp_pkt_addstring_start(pktout);
525 sftp_pkt_addstring_str(pktout, path);
4c7f0d61 526 sftp_send(pktout);
1bc24185 527
528 return req;
529}
530
7b7de4f4 531char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 532{
7b7de4f4 533 sfree(req);
534
4c7f0d61 535 if (pktin->type == SSH_FXP_NAME) {
46cfeac8 536 unsigned long count;
4c7f0d61 537 char *path;
538 int len;
539
46cfeac8 540 if (!sftp_pkt_getuint32(pktin, &count) || count != 1) {
541 fxp_internal_error("REALPATH did not return name count of 1\n");
41d3adbb 542 sftp_pkt_free(pktin);
4c7f0d61 543 return NULL;
544 }
46cfeac8 545 if (!sftp_pkt_getstring(pktin, &path, &len)) {
4c7f0d61 546 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
41d3adbb 547 sftp_pkt_free(pktin);
4c7f0d61 548 return NULL;
549 }
550 path = mkstr(path, len);
551 sftp_pkt_free(pktin);
552 return path;
553 } else {
554 fxp_got_status(pktin);
41d3adbb 555 sftp_pkt_free(pktin);
4c7f0d61 556 return NULL;
557 }
558}
559
560/*
561 * Open a file.
562 */
1bc24185 563struct sftp_request *fxp_open_send(char *path, int type)
32874aea 564{
1bc24185 565 struct sftp_request *req = sftp_alloc_request();
566 struct sftp_packet *pktout;
4c7f0d61 567
568 pktout = sftp_pkt_init(SSH_FXP_OPEN);
1bc24185 569 sftp_pkt_adduint32(pktout, req->id);
4c7f0d61 570 sftp_pkt_addstring(pktout, path);
571 sftp_pkt_adduint32(pktout, type);
572 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
573 sftp_send(pktout);
1bc24185 574
575 return req;
576}
577
7b7de4f4 578struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
579 struct sftp_request *req)
1bc24185 580{
7b7de4f4 581 sfree(req);
582
4c7f0d61 583 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 584 char *hstring;
585 struct fxp_handle *handle;
586 int len;
587
46cfeac8 588 if (!sftp_pkt_getstring(pktin, &hstring, &len)) {
4c7f0d61 589 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
41d3adbb 590 sftp_pkt_free(pktin);
4c7f0d61 591 return NULL;
592 }
3d88e64d 593 handle = snew(struct fxp_handle);
4c7f0d61 594 handle->hstring = mkstr(hstring, len);
f9e162aa 595 handle->hlen = len;
4c7f0d61 596 sftp_pkt_free(pktin);
597 return handle;
598 } else {
599 fxp_got_status(pktin);
41d3adbb 600 sftp_pkt_free(pktin);
4c7f0d61 601 return NULL;
602 }
603}
604
605/*
606 * Open a directory.
607 */
1bc24185 608struct sftp_request *fxp_opendir_send(char *path)
32874aea 609{
1bc24185 610 struct sftp_request *req = sftp_alloc_request();
611 struct sftp_packet *pktout;
4c7f0d61 612
613 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
1bc24185 614 sftp_pkt_adduint32(pktout, req->id);
4c7f0d61 615 sftp_pkt_addstring(pktout, path);
616 sftp_send(pktout);
1bc24185 617
618 return req;
619}
620
7b7de4f4 621struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
622 struct sftp_request *req)
1bc24185 623{
7b7de4f4 624 sfree(req);
4c7f0d61 625 if (pktin->type == SSH_FXP_HANDLE) {
4c7f0d61 626 char *hstring;
627 struct fxp_handle *handle;
628 int len;
629
46cfeac8 630 if (!sftp_pkt_getstring(pktin, &hstring, &len)) {
4c7f0d61 631 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
41d3adbb 632 sftp_pkt_free(pktin);
4c7f0d61 633 return NULL;
634 }
3d88e64d 635 handle = snew(struct fxp_handle);
4c7f0d61 636 handle->hstring = mkstr(hstring, len);
f9e162aa 637 handle->hlen = len;
4c7f0d61 638 sftp_pkt_free(pktin);
639 return handle;
640 } else {
641 fxp_got_status(pktin);
41d3adbb 642 sftp_pkt_free(pktin);
4c7f0d61 643 return NULL;
644 }
645}
646
647/*
648 * Close a file/dir.
649 */
1bc24185 650struct sftp_request *fxp_close_send(struct fxp_handle *handle)
32874aea 651{
1bc24185 652 struct sftp_request *req = sftp_alloc_request();
653 struct sftp_packet *pktout;
4c7f0d61 654
655 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
1bc24185 656 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 657 sftp_pkt_addstring_start(pktout);
658 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 659 sftp_send(pktout);
1bc24185 660
4c7f0d61 661 sfree(handle->hstring);
662 sfree(handle);
1bc24185 663
664 return req;
665}
666
7b7de4f4 667void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 668{
7b7de4f4 669 sfree(req);
1bc24185 670 fxp_got_status(pktin);
671 sftp_pkt_free(pktin);
4c7f0d61 672}
673
1bc24185 674struct sftp_request *fxp_mkdir_send(char *path)
9954aaa3 675{
1bc24185 676 struct sftp_request *req = sftp_alloc_request();
677 struct sftp_packet *pktout;
9954aaa3 678
679 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
1bc24185 680 sftp_pkt_adduint32(pktout, req->id);
d92624dc 681 sftp_pkt_addstring(pktout, path);
9954aaa3 682 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
683 sftp_send(pktout);
1bc24185 684
685 return req;
686}
687
7b7de4f4 688int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 689{
7b7de4f4 690 int id;
691 sfree(req);
692 id = fxp_got_status(pktin);
41d3adbb 693 sftp_pkt_free(pktin);
9954aaa3 694 if (id != 1) {
695 return 0;
696 }
697 return 1;
698}
699
1bc24185 700struct sftp_request *fxp_rmdir_send(char *path)
9954aaa3 701{
1bc24185 702 struct sftp_request *req = sftp_alloc_request();
703 struct sftp_packet *pktout;
9954aaa3 704
705 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
1bc24185 706 sftp_pkt_adduint32(pktout, req->id);
d92624dc 707 sftp_pkt_addstring(pktout, path);
9954aaa3 708 sftp_send(pktout);
1bc24185 709
710 return req;
711}
712
7b7de4f4 713int fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 714{
7b7de4f4 715 int id;
716 sfree(req);
717 id = fxp_got_status(pktin);
41d3adbb 718 sftp_pkt_free(pktin);
9954aaa3 719 if (id != 1) {
720 return 0;
721 }
722 return 1;
723}
724
1bc24185 725struct sftp_request *fxp_remove_send(char *fname)
9954aaa3 726{
1bc24185 727 struct sftp_request *req = sftp_alloc_request();
728 struct sftp_packet *pktout;
9954aaa3 729
730 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
1bc24185 731 sftp_pkt_adduint32(pktout, req->id);
d92624dc 732 sftp_pkt_addstring(pktout, fname);
733 sftp_send(pktout);
1bc24185 734
735 return req;
736}
737
7b7de4f4 738int fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 739{
7b7de4f4 740 int id;
741 sfree(req);
742 id = fxp_got_status(pktin);
41d3adbb 743 sftp_pkt_free(pktin);
d92624dc 744 if (id != 1) {
745 return 0;
746 }
747 return 1;
748}
749
1bc24185 750struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
d92624dc 751{
1bc24185 752 struct sftp_request *req = sftp_alloc_request();
753 struct sftp_packet *pktout;
d92624dc 754
755 pktout = sftp_pkt_init(SSH_FXP_RENAME);
1bc24185 756 sftp_pkt_adduint32(pktout, req->id);
d92624dc 757 sftp_pkt_addstring(pktout, srcfname);
758 sftp_pkt_addstring(pktout, dstfname);
759 sftp_send(pktout);
1bc24185 760
761 return req;
762}
763
7b7de4f4 764int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 765{
7b7de4f4 766 int id;
767 sfree(req);
768 id = fxp_got_status(pktin);
41d3adbb 769 sftp_pkt_free(pktin);
d92624dc 770 if (id != 1) {
771 return 0;
772 }
773 return 1;
774}
775
776/*
777 * Retrieve the attributes of a file. We have fxp_stat which works
778 * on filenames, and fxp_fstat which works on open file handles.
779 */
1bc24185 780struct sftp_request *fxp_stat_send(char *fname)
d92624dc 781{
1bc24185 782 struct sftp_request *req = sftp_alloc_request();
783 struct sftp_packet *pktout;
d92624dc 784
785 pktout = sftp_pkt_init(SSH_FXP_STAT);
1bc24185 786 sftp_pkt_adduint32(pktout, req->id);
d92624dc 787 sftp_pkt_addstring(pktout, fname);
788 sftp_send(pktout);
d92624dc 789
1bc24185 790 return req;
791}
792
7b7de4f4 793int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
794 struct fxp_attrs *attrs)
1bc24185 795{
7b7de4f4 796 sfree(req);
d92624dc 797 if (pktin->type == SSH_FXP_ATTRS) {
46cfeac8 798 if (!sftp_pkt_getattrs(pktin, attrs)) {
799 fxp_internal_error("malformed SSH_FXP_ATTRS packet");
800 sftp_pkt_free(pktin);
801 return 0;
802 }
803 sftp_pkt_free(pktin);
d92624dc 804 return 1;
805 } else {
806 fxp_got_status(pktin);
41d3adbb 807 sftp_pkt_free(pktin);
d92624dc 808 return 0;
809 }
810}
811
1bc24185 812struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
d92624dc 813{
1bc24185 814 struct sftp_request *req = sftp_alloc_request();
815 struct sftp_packet *pktout;
d92624dc 816
817 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
1bc24185 818 sftp_pkt_adduint32(pktout, req->id);
9954aaa3 819 sftp_pkt_addstring_start(pktout);
d92624dc 820 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
821 sftp_send(pktout);
d92624dc 822
1bc24185 823 return req;
824}
825
7b7de4f4 826int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
1bc24185 827 struct fxp_attrs *attrs)
828{
7b7de4f4 829 sfree(req);
d92624dc 830 if (pktin->type == SSH_FXP_ATTRS) {
46cfeac8 831 if (!sftp_pkt_getattrs(pktin, attrs)) {
832 fxp_internal_error("malformed SSH_FXP_ATTRS packet");
833 sftp_pkt_free(pktin);
834 return 0;
835 }
836 sftp_pkt_free(pktin);
d92624dc 837 return 1;
838 } else {
839 fxp_got_status(pktin);
41d3adbb 840 sftp_pkt_free(pktin);
d92624dc 841 return 0;
842 }
843}
844
845/*
846 * Set the attributes of a file.
847 */
1bc24185 848struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
d92624dc 849{
1bc24185 850 struct sftp_request *req = sftp_alloc_request();
851 struct sftp_packet *pktout;
d92624dc 852
853 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
1bc24185 854 sftp_pkt_adduint32(pktout, req->id);
d92624dc 855 sftp_pkt_addstring(pktout, fname);
856 sftp_pkt_addattrs(pktout, attrs);
9954aaa3 857 sftp_send(pktout);
1bc24185 858
859 return req;
860}
861
7b7de4f4 862int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 863{
7b7de4f4 864 int id;
865 sfree(req);
866 id = fxp_got_status(pktin);
41d3adbb 867 sftp_pkt_free(pktin);
9954aaa3 868 if (id != 1) {
869 return 0;
870 }
871 return 1;
872}
1bc24185 873
874struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
875 struct fxp_attrs attrs)
fd5e5847 876{
1bc24185 877 struct sftp_request *req = sftp_alloc_request();
878 struct sftp_packet *pktout;
fd5e5847 879
880 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
1bc24185 881 sftp_pkt_adduint32(pktout, req->id);
fd5e5847 882 sftp_pkt_addstring_start(pktout);
883 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
884 sftp_pkt_addattrs(pktout, attrs);
885 sftp_send(pktout);
1bc24185 886
887 return req;
888}
889
7b7de4f4 890int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 891{
7b7de4f4 892 int id;
893 sfree(req);
894 id = fxp_got_status(pktin);
41d3adbb 895 sftp_pkt_free(pktin);
fd5e5847 896 if (id != 1) {
897 return 0;
898 }
899 return 1;
900}
9954aaa3 901
4c7f0d61 902/*
903 * Read from a file. Returns the number of bytes read, or -1 on an
904 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
905 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
906 * error indicator. It might even depend on the SFTP server.)
907 */
1bc24185 908struct sftp_request *fxp_read_send(struct fxp_handle *handle,
909 uint64 offset, int len)
32874aea 910{
1bc24185 911 struct sftp_request *req = sftp_alloc_request();
912 struct sftp_packet *pktout;
4c7f0d61 913
914 pktout = sftp_pkt_init(SSH_FXP_READ);
1bc24185 915 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 916 sftp_pkt_addstring_start(pktout);
917 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 918 sftp_pkt_adduint64(pktout, offset);
919 sftp_pkt_adduint32(pktout, len);
920 sftp_send(pktout);
1bc24185 921
922 return req;
923}
924
7b7de4f4 925int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
926 char *buffer, int len)
1bc24185 927{
7b7de4f4 928 sfree(req);
4c7f0d61 929 if (pktin->type == SSH_FXP_DATA) {
930 char *str;
931 int rlen;
932
46cfeac8 933 if (!sftp_pkt_getstring(pktin, &str, &rlen)) {
934 fxp_internal_error("READ returned malformed SSH_FXP_DATA packet");
935 sftp_pkt_free(pktin);
936 return -1;
937 }
4c7f0d61 938
939 if (rlen > len || rlen < 0) {
940 fxp_internal_error("READ returned more bytes than requested");
41d3adbb 941 sftp_pkt_free(pktin);
4c7f0d61 942 return -1;
943 }
944
945 memcpy(buffer, str, rlen);
41d3adbb 946 sftp_pkt_free(pktin);
4c7f0d61 947 return rlen;
948 } else {
949 fxp_got_status(pktin);
41d3adbb 950 sftp_pkt_free(pktin);
4c7f0d61 951 return -1;
952 }
953}
954
955/*
956 * Read from a directory.
957 */
1bc24185 958struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
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_READDIR);
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_send(pktout);
1bc24185 968
969 return req;
970}
971
7b7de4f4 972struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
973 struct sftp_request *req)
1bc24185 974{
7b7de4f4 975 sfree(req);
4c7f0d61 976 if (pktin->type == SSH_FXP_NAME) {
977 struct fxp_names *ret;
46cfeac8 978 unsigned long i;
979
980 /*
981 * Sanity-check the number of names. Minimum is obviously
982 * zero. Maximum is the remaining space in the packet
983 * divided by the very minimum length of a name, which is
984 * 12 bytes (4 for an empty filename, 4 for an empty
985 * longname, 4 for a set of attribute flags indicating that
986 * no other attributes are supplied).
987 */
988 if (!sftp_pkt_getuint32(pktin, &i) ||
989 i > (pktin->length-pktin->savedpos)/12) {
990 fxp_internal_error("malformed FXP_NAME packet");
991 sftp_pkt_free(pktin);
992 return NULL;
993 }
994
995 /*
996 * Ensure the implicit multiplication in the snewn() call
997 * doesn't suffer integer overflow and cause us to malloc
998 * too little space.
999 */
1000 if (i > INT_MAX / sizeof(struct fxp_name)) {
1001 fxp_internal_error("unreasonably large FXP_NAME packet");
1002 sftp_pkt_free(pktin);
1003 return NULL;
1004 }
1005
3d88e64d 1006 ret = snew(struct fxp_names);
46cfeac8 1007 ret->nnames = i;
3d88e64d 1008 ret->names = snewn(ret->nnames, struct fxp_name);
4c7f0d61 1009 for (i = 0; i < ret->nnames; i++) {
46cfeac8 1010 char *str1, *str2;
1011 int len1, len2;
1012 if (!sftp_pkt_getstring(pktin, &str1, &len1) ||
1013 !sftp_pkt_getstring(pktin, &str2, &len2) ||
1014 !sftp_pkt_getattrs(pktin, &ret->names[i].attrs)) {
1015 fxp_internal_error("malformed FXP_NAME packet");
1016 while (i--) {
1017 sfree(ret->names[i].filename);
1018 sfree(ret->names[i].longname);
1019 }
1020 sfree(ret->names);
1021 sfree(ret);
1022 sfree(pktin);
1023 return NULL;
1024 }
1025 ret->names[i].filename = mkstr(str1, len1);
1026 ret->names[i].longname = mkstr(str2, len2);
4c7f0d61 1027 }
41d3adbb 1028 sftp_pkt_free(pktin);
4c7f0d61 1029 return ret;
1030 } else {
1031 fxp_got_status(pktin);
41d3adbb 1032 sftp_pkt_free(pktin);
4c7f0d61 1033 return NULL;
1034 }
1035}
1036
1037/*
1038 * Write to a file. Returns 0 on error, 1 on OK.
1039 */
1bc24185 1040struct sftp_request *fxp_write_send(struct fxp_handle *handle,
1041 char *buffer, uint64 offset, int len)
32874aea 1042{
1bc24185 1043 struct sftp_request *req = sftp_alloc_request();
1044 struct sftp_packet *pktout;
4c7f0d61 1045
1046 pktout = sftp_pkt_init(SSH_FXP_WRITE);
1bc24185 1047 sftp_pkt_adduint32(pktout, req->id);
f9e162aa 1048 sftp_pkt_addstring_start(pktout);
1049 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
4c7f0d61 1050 sftp_pkt_adduint64(pktout, offset);
1051 sftp_pkt_addstring_start(pktout);
1052 sftp_pkt_addstring_data(pktout, buffer, len);
1053 sftp_send(pktout);
1bc24185 1054
1055 return req;
1056}
1057
7b7de4f4 1058int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
1bc24185 1059{
7b7de4f4 1060 sfree(req);
4c7f0d61 1061 fxp_got_status(pktin);
41d3adbb 1062 sftp_pkt_free(pktin);
4c7f0d61 1063 return fxp_errtype == SSH_FX_OK;
1064}
1065
1066/*
1067 * Free up an fxp_names structure.
1068 */
32874aea 1069void fxp_free_names(struct fxp_names *names)
1070{
4c7f0d61 1071 int i;
1072
1073 for (i = 0; i < names->nnames; i++) {
1074 sfree(names->names[i].filename);
1075 sfree(names->names[i].longname);
1076 }
1077 sfree(names->names);
1078 sfree(names);
1079}
7d2c1789 1080
1081/*
1082 * Duplicate an fxp_name structure.
1083 */
1084struct fxp_name *fxp_dup_name(struct fxp_name *name)
1085{
1086 struct fxp_name *ret;
3d88e64d 1087 ret = snew(struct fxp_name);
7d2c1789 1088 ret->filename = dupstr(name->filename);
1089 ret->longname = dupstr(name->longname);
1090 ret->attrs = name->attrs; /* structure copy */
1091 return ret;
1092}
1093
1094/*
1095 * Free up an fxp_name structure.
1096 */
1097void fxp_free_name(struct fxp_name *name)
1098{
7d2c1789 1099 sfree(name->filename);
1100 sfree(name->longname);
1101 sfree(name);
1102}
c606c42d 1103
1104/*
1105 * Store user data in an sftp_request structure.
1106 */
1107void *fxp_get_userdata(struct sftp_request *req)
1108{
1109 return req->userdata;
1110}
1111
1112void fxp_set_userdata(struct sftp_request *req, void *data)
1113{
1114 req->userdata = data;
1115}
1116
1117/*
1118 * A wrapper to go round fxp_read_* and fxp_write_*, which manages
1119 * the queueing of multiple read/write requests.
1120 */
1121
1122struct req {
1123 char *buffer;
1124 int len, retlen, complete;
1125 uint64 offset;
1126 struct req *next, *prev;
1127};
1128
1129struct fxp_xfer {
1130 uint64 offset, furthestdata, filesize;
df0870fc 1131 int req_totalsize, req_maxsize, eof, err;
c606c42d 1132 struct fxp_handle *fh;
1133 struct req *head, *tail;
1134};
1135
1136static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64 offset)
1137{
1138 struct fxp_xfer *xfer = snew(struct fxp_xfer);
1139
1140 xfer->fh = fh;
1141 xfer->offset = offset;
1142 xfer->head = xfer->tail = NULL;
df0870fc 1143 xfer->req_totalsize = 0;
1144 xfer->req_maxsize = 16384;
c606c42d 1145 xfer->err = 0;
1146 xfer->filesize = uint64_make(ULONG_MAX, ULONG_MAX);
1147 xfer->furthestdata = uint64_make(0, 0);
1148
1149 return xfer;
1150}
1151
df0870fc 1152int xfer_done(struct fxp_xfer *xfer)
c606c42d 1153{
1154 /*
1155 * We're finished if we've seen EOF _and_ there are no
1156 * outstanding requests.
1157 */
1158 return (xfer->eof || xfer->err) && !xfer->head;
1159}
1160
1161void xfer_download_queue(struct fxp_xfer *xfer)
1162{
479fe1ba 1163 while (xfer->req_totalsize < xfer->req_maxsize &&
1164 !xfer->eof && !xfer->err) {
c606c42d 1165 /*
1166 * Queue a new read request.
1167 */
1168 struct req *rr;
1169 struct sftp_request *req;
1170
1171 rr = snew(struct req);
1172 rr->offset = xfer->offset;
1173 rr->complete = 0;
1174 if (xfer->tail) {
1175 xfer->tail->next = rr;
1176 rr->prev = xfer->tail;
1177 } else {
1178 xfer->head = rr;
1179 rr->prev = NULL;
1180 }
1181 xfer->tail = rr;
1182 rr->next = NULL;
1183
1184 rr->len = 4096;
1185 rr->buffer = snewn(rr->len, char);
1186 sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));
1187 fxp_set_userdata(req, rr);
1188
1189 xfer->offset = uint64_add32(xfer->offset, rr->len);
df0870fc 1190 xfer->req_totalsize += rr->len;
c606c42d 1191
1192#ifdef DEBUG_DOWNLOAD
1193 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }
1194#endif
1195 }
1196}
1197
1198struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)
1199{
1200 struct fxp_xfer *xfer = xfer_init(fh, offset);
1201
1202 xfer->eof = FALSE;
1203 xfer_download_queue(xfer);
1204
1205 return xfer;
1206}
1207
1208int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1209{
1210 struct sftp_request *rreq;
1211 struct req *rr;
1212
1213 rreq = sftp_find_request(pktin);
1214 rr = (struct req *)fxp_get_userdata(rreq);
1215 if (!rr)
1216 return 0; /* this packet isn't ours */
1217 rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);
1218#ifdef DEBUG_DOWNLOAD
1219 printf("read request %p has returned [%d]\n", rr, rr->retlen);
1220#endif
1221
1222 if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {
1223 xfer->eof = TRUE;
1224 rr->complete = -1;
1225#ifdef DEBUG_DOWNLOAD
1226 printf("setting eof\n");
1227#endif
1228 } else if (rr->retlen < 0) {
1229 /* some error other than EOF; signal it back to caller */
479fe1ba 1230 xfer_set_error(xfer);
1231 rr->complete = -1;
c606c42d 1232 return -1;
1233 }
1234
1235 rr->complete = 1;
1236
1237 /*
1238 * Special case: if we have received fewer bytes than we
1239 * actually read, we should do something. For the moment I'll
1240 * just throw an ersatz FXP error to signal this; the SFTP
1241 * draft I've got says that it can't happen except on special
1242 * files, in which case seeking probably has very little
1243 * meaning and so queueing an additional read request to fill
1244 * up the gap sounds like the wrong answer. I'm not sure what I
1245 * should be doing here - if it _was_ a special file, I suspect
1246 * I simply shouldn't have been queueing multiple requests in
1247 * the first place...
1248 */
1249 if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {
1250 xfer->furthestdata = rr->offset;
1251#ifdef DEBUG_DOWNLOAD
1252 { char buf[40];
1253 uint64_decimal(xfer->furthestdata, buf);
1254 printf("setting furthestdata = %s\n", buf); }
1255#endif
1256 }
1257
1258 if (rr->retlen < rr->len) {
1259 uint64 filesize = uint64_add32(rr->offset,
1260 (rr->retlen < 0 ? 0 : rr->retlen));
1261#ifdef DEBUG_DOWNLOAD
1262 { char buf[40];
1263 uint64_decimal(filesize, buf);
1264 printf("short block! trying filesize = %s\n", buf); }
1265#endif
1266 if (uint64_compare(xfer->filesize, filesize) > 0) {
1267 xfer->filesize = filesize;
1268#ifdef DEBUG_DOWNLOAD
1269 printf("actually changing filesize\n");
1270#endif
1271 }
1272 }
1273
1274 if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {
1275 fxp_error_message = "received a short buffer from FXP_READ, but not"
1276 " at EOF";
1277 fxp_errtype = -1;
1278 xfer_set_error(xfer);
1279 return -1;
1280 }
1281
1282 return 1;
1283}
1284
1285void xfer_set_error(struct fxp_xfer *xfer)
1286{
1287 xfer->err = 1;
1288}
1289
1290int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)
1291{
1292 void *retbuf = NULL;
1293 int retlen = 0;
1294
1295 /*
1296 * Discard anything at the head of the rr queue with complete <
1297 * 0; return the first thing with complete > 0.
1298 */
1299 while (xfer->head && xfer->head->complete && !retbuf) {
1300 struct req *rr = xfer->head;
1301
1302 if (rr->complete > 0) {
1303 retbuf = rr->buffer;
1304 retlen = rr->retlen;
1305#ifdef DEBUG_DOWNLOAD
1306 printf("handing back data from read request %p\n", rr);
1307#endif
1308 }
1309#ifdef DEBUG_DOWNLOAD
1310 else
1311 printf("skipping failed read request %p\n", rr);
1312#endif
1313
1314 xfer->head = xfer->head->next;
1315 if (xfer->head)
1316 xfer->head->prev = NULL;
1317 else
1318 xfer->tail = NULL;
df0870fc 1319 xfer->req_totalsize -= rr->len;
c606c42d 1320 sfree(rr);
c606c42d 1321 }
1322
1323 if (retbuf) {
1324 *buf = retbuf;
1325 *len = retlen;
1326 return 1;
1327 } else
1328 return 0;
1329}
1330
df0870fc 1331struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)
1332{
1333 struct fxp_xfer *xfer = xfer_init(fh, offset);
1334
1335 /*
1336 * We set `eof' to 1 because this will cause xfer_done() to
1337 * return true iff there are no outstanding requests. During an
1338 * upload, our caller will be responsible for working out
1339 * whether all the data has been sent, so all it needs to know
1340 * from us is whether the outstanding requests have been
1341 * handled once that's done.
1342 */
1343 xfer->eof = 1;
1344
1345 return xfer;
1346}
1347
1348int xfer_upload_ready(struct fxp_xfer *xfer)
1349{
1350 if (xfer->req_totalsize < xfer->req_maxsize)
1351 return 1;
1352 else
1353 return 0;
1354}
1355
1356void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)
1357{
1358 struct req *rr;
1359 struct sftp_request *req;
1360
1361 rr = snew(struct req);
1362 rr->offset = xfer->offset;
1363 rr->complete = 0;
1364 if (xfer->tail) {
1365 xfer->tail->next = rr;
1366 rr->prev = xfer->tail;
1367 } else {
1368 xfer->head = rr;
1369 rr->prev = NULL;
1370 }
1371 xfer->tail = rr;
1372 rr->next = NULL;
1373
1374 rr->len = len;
1375 rr->buffer = NULL;
1376 sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));
1377 fxp_set_userdata(req, rr);
1378
1379 xfer->offset = uint64_add32(xfer->offset, rr->len);
1380 xfer->req_totalsize += rr->len;
1381
1382#ifdef DEBUG_UPLOAD
1383 { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }
1384#endif
1385}
1386
1387int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1388{
1389 struct sftp_request *rreq;
1390 struct req *rr, *prev, *next;
1391 int ret;
1392
1393 rreq = sftp_find_request(pktin);
1394 rr = (struct req *)fxp_get_userdata(rreq);
1395 if (!rr)
1396 return 0; /* this packet isn't ours */
1397 ret = fxp_write_recv(pktin, rreq);
1398#ifdef DEBUG_UPLOAD
1399 printf("write request %p has returned [%d]\n", rr, ret);
1400#endif
1401
1402 /*
1403 * Remove this one from the queue.
1404 */
1405 prev = rr->prev;
1406 next = rr->next;
1407 if (prev)
1408 prev->next = next;
1409 else
1410 xfer->head = next;
1411 if (next)
1412 next->prev = prev;
1413 else
1414 xfer->tail = prev;
1415 xfer->req_totalsize -= rr->len;
1416 sfree(rr);
1417
1418 if (!ret)
1419 return -1;
1420
1421 return 1;
1422}
1423
c606c42d 1424void xfer_cleanup(struct fxp_xfer *xfer)
1425{
1426 struct req *rr;
1427 while (xfer->head) {
1428 rr = xfer->head;
1429 xfer->head = xfer->head->next;
1430 sfree(rr->buffer);
1431 sfree(rr);
1432 }
1433 sfree(xfer);
1434}