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