]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
Joe Yates's memory leak patches.
[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 && !xfer->eof) {
1093         /*
1094          * Queue a new read request.
1095          */
1096         struct req *rr;
1097         struct sftp_request *req;
1098
1099         rr = snew(struct req);
1100         rr->offset = xfer->offset;
1101         rr->complete = 0;
1102         if (xfer->tail) {
1103             xfer->tail->next = rr;
1104             rr->prev = xfer->tail;
1105         } else {
1106             xfer->head = rr;
1107             rr->prev = NULL;
1108         }
1109         xfer->tail = rr;
1110         rr->next = NULL;
1111
1112         rr->len = 4096;
1113         rr->buffer = snewn(rr->len, char);
1114         sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));
1115         fxp_set_userdata(req, rr);
1116
1117         xfer->offset = uint64_add32(xfer->offset, rr->len);
1118         xfer->req_totalsize += rr->len;
1119
1120 #ifdef DEBUG_DOWNLOAD
1121         { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }
1122 #endif
1123     }
1124 }
1125
1126 struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)
1127 {
1128     struct fxp_xfer *xfer = xfer_init(fh, offset);
1129
1130     xfer->eof = FALSE;
1131     xfer_download_queue(xfer);
1132
1133     return xfer;
1134 }
1135
1136 int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1137 {
1138     struct sftp_request *rreq;
1139     struct req *rr;
1140
1141     rreq = sftp_find_request(pktin);
1142     rr = (struct req *)fxp_get_userdata(rreq);
1143     if (!rr)
1144         return 0;                      /* this packet isn't ours */
1145     rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);
1146 #ifdef DEBUG_DOWNLOAD
1147     printf("read request %p has returned [%d]\n", rr, rr->retlen);
1148 #endif
1149
1150     if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {
1151         xfer->eof = TRUE;
1152         rr->complete = -1;
1153 #ifdef DEBUG_DOWNLOAD
1154         printf("setting eof\n");
1155 #endif
1156     } else if (rr->retlen < 0) {
1157         /* some error other than EOF; signal it back to caller */
1158         return -1;
1159     }
1160
1161     rr->complete = 1;
1162
1163     /*
1164      * Special case: if we have received fewer bytes than we
1165      * actually read, we should do something. For the moment I'll
1166      * just throw an ersatz FXP error to signal this; the SFTP
1167      * draft I've got says that it can't happen except on special
1168      * files, in which case seeking probably has very little
1169      * meaning and so queueing an additional read request to fill
1170      * up the gap sounds like the wrong answer. I'm not sure what I
1171      * should be doing here - if it _was_ a special file, I suspect
1172      * I simply shouldn't have been queueing multiple requests in
1173      * the first place...
1174      */
1175     if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {
1176         xfer->furthestdata = rr->offset;
1177 #ifdef DEBUG_DOWNLOAD
1178         { char buf[40];
1179         uint64_decimal(xfer->furthestdata, buf);
1180         printf("setting furthestdata = %s\n", buf); }
1181 #endif
1182     }
1183
1184     if (rr->retlen < rr->len) {
1185         uint64 filesize = uint64_add32(rr->offset,
1186                                        (rr->retlen < 0 ? 0 : rr->retlen));
1187 #ifdef DEBUG_DOWNLOAD
1188         { char buf[40];
1189         uint64_decimal(filesize, buf);
1190         printf("short block! trying filesize = %s\n", buf); }
1191 #endif
1192         if (uint64_compare(xfer->filesize, filesize) > 0) {
1193             xfer->filesize = filesize;
1194 #ifdef DEBUG_DOWNLOAD
1195             printf("actually changing filesize\n");
1196 #endif      
1197         }
1198     }
1199
1200     if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {
1201         fxp_error_message = "received a short buffer from FXP_READ, but not"
1202             " at EOF";
1203         fxp_errtype = -1;
1204         xfer_set_error(xfer);
1205         return -1;
1206     }
1207
1208     return 1;
1209 }
1210
1211 void xfer_set_error(struct fxp_xfer *xfer)
1212 {
1213     xfer->err = 1;
1214 }
1215
1216 int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)
1217 {
1218     void *retbuf = NULL;
1219     int retlen = 0;
1220
1221     /*
1222      * Discard anything at the head of the rr queue with complete <
1223      * 0; return the first thing with complete > 0.
1224      */
1225     while (xfer->head && xfer->head->complete && !retbuf) {
1226         struct req *rr = xfer->head;
1227
1228         if (rr->complete > 0) {
1229             retbuf = rr->buffer;
1230             retlen = rr->retlen;
1231 #ifdef DEBUG_DOWNLOAD
1232             printf("handing back data from read request %p\n", rr);
1233 #endif
1234         }
1235 #ifdef DEBUG_DOWNLOAD
1236         else
1237             printf("skipping failed read request %p\n", rr);
1238 #endif
1239
1240         xfer->head = xfer->head->next;
1241         if (xfer->head)
1242             xfer->head->prev = NULL;
1243         else
1244             xfer->tail = NULL;
1245         xfer->req_totalsize -= rr->len;
1246         sfree(rr);
1247     }
1248
1249     if (retbuf) {
1250         *buf = retbuf;
1251         *len = retlen;
1252         return 1;
1253     } else
1254         return 0;
1255 }
1256
1257 struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)
1258 {
1259     struct fxp_xfer *xfer = xfer_init(fh, offset);
1260
1261     /*
1262      * We set `eof' to 1 because this will cause xfer_done() to
1263      * return true iff there are no outstanding requests. During an
1264      * upload, our caller will be responsible for working out
1265      * whether all the data has been sent, so all it needs to know
1266      * from us is whether the outstanding requests have been
1267      * handled once that's done.
1268      */
1269     xfer->eof = 1;
1270
1271     return xfer;
1272 }
1273
1274 int xfer_upload_ready(struct fxp_xfer *xfer)
1275 {
1276     if (xfer->req_totalsize < xfer->req_maxsize)
1277         return 1;
1278     else
1279         return 0;
1280 }
1281
1282 void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)
1283 {
1284     struct req *rr;
1285     struct sftp_request *req;
1286
1287     rr = snew(struct req);
1288     rr->offset = xfer->offset;
1289     rr->complete = 0;
1290     if (xfer->tail) {
1291         xfer->tail->next = rr;
1292         rr->prev = xfer->tail;
1293     } else {
1294         xfer->head = rr;
1295         rr->prev = NULL;
1296     }
1297     xfer->tail = rr;
1298     rr->next = NULL;
1299
1300     rr->len = len;
1301     rr->buffer = NULL;
1302     sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));
1303     fxp_set_userdata(req, rr);
1304
1305     xfer->offset = uint64_add32(xfer->offset, rr->len);
1306     xfer->req_totalsize += rr->len;
1307
1308 #ifdef DEBUG_UPLOAD
1309     { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }
1310 #endif
1311 }
1312
1313 int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1314 {
1315     struct sftp_request *rreq;
1316     struct req *rr, *prev, *next;
1317     int ret;
1318
1319     rreq = sftp_find_request(pktin);
1320     rr = (struct req *)fxp_get_userdata(rreq);
1321     if (!rr)
1322         return 0;                      /* this packet isn't ours */
1323     ret = fxp_write_recv(pktin, rreq);
1324 #ifdef DEBUG_UPLOAD
1325     printf("write request %p has returned [%d]\n", rr, ret);
1326 #endif
1327
1328     /*
1329      * Remove this one from the queue.
1330      */
1331     prev = rr->prev;
1332     next = rr->next;
1333     if (prev)
1334         prev->next = next;
1335     else
1336         xfer->head = next;
1337     if (next)
1338         next->prev = prev;
1339     else
1340         xfer->tail = prev;
1341     xfer->req_totalsize -= rr->len;
1342     sfree(rr);
1343
1344     if (!ret)
1345         return -1;
1346
1347     return 1;
1348 }
1349
1350 void xfer_cleanup(struct fxp_xfer *xfer)
1351 {
1352     struct req *rr;
1353     while (xfer->head) {
1354         rr = xfer->head;
1355         xfer->head = xfer->head->next;
1356         sfree(rr->buffer);
1357         sfree(rr);
1358     }
1359     sfree(xfer);
1360 }