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