]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
Uploads turn out to be much easier than downloads, so here's faster
[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_register(struct sftp_request *req)
338 {
339     req->registered = 1;
340 }
341
342 struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
343 {
344     unsigned long id;
345     struct sftp_request *req;
346
347     if (!pktin) {
348         fxp_internal_error("did not receive a valid SFTP packet\n");
349         return NULL;
350     }
351
352     id = sftp_pkt_getuint32(pktin);
353     req = find234(sftp_requests, &id, sftp_reqfind);
354
355     if (!req || !req->registered) {
356         fxp_internal_error("request ID mismatch\n");
357         sftp_pkt_free(pktin);
358         return NULL;
359     }
360
361     del234(sftp_requests, req);
362
363     return req;
364 }
365
366 /* ----------------------------------------------------------------------
367  * String handling routines.
368  */
369
370 static char *mkstr(char *s, int len)
371 {
372     char *p = snewn(len + 1, char);
373     memcpy(p, s, len);
374     p[len] = '\0';
375     return p;
376 }
377
378 /* ----------------------------------------------------------------------
379  * SFTP primitives.
380  */
381
382 /*
383  * Deal with (and free) an FXP_STATUS packet. Return 1 if
384  * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
385  * Also place the status into fxp_errtype.
386  */
387 static int fxp_got_status(struct sftp_packet *pktin)
388 {
389     static const char *const messages[] = {
390         /* SSH_FX_OK. The only time we will display a _message_ for this
391          * is if we were expecting something other than FXP_STATUS on
392          * success, so this is actually an error message! */
393         "unexpected OK response",
394         "end of file",
395         "no such file or directory",
396         "permission denied",
397         "failure",
398         "bad message",
399         "no connection",
400         "connection lost",
401         "operation unsupported",
402     };
403
404     if (pktin->type != SSH_FXP_STATUS) {
405         fxp_error_message = "expected FXP_STATUS packet";
406         fxp_errtype = -1;
407     } else {
408         fxp_errtype = sftp_pkt_getuint32(pktin);
409         if (fxp_errtype < 0 ||
410             fxp_errtype >= sizeof(messages) / sizeof(*messages))
411                 fxp_error_message = "unknown error code";
412         else
413             fxp_error_message = messages[fxp_errtype];
414     }
415
416     if (fxp_errtype == SSH_FX_OK)
417         return 1;
418     else if (fxp_errtype == SSH_FX_EOF)
419         return 0;
420     else
421         return -1;
422 }
423
424 static void fxp_internal_error(char *msg)
425 {
426     fxp_error_message = msg;
427     fxp_errtype = -1;
428 }
429
430 const char *fxp_error(void)
431 {
432     return fxp_error_message;
433 }
434
435 int fxp_error_type(void)
436 {
437     return fxp_errtype;
438 }
439
440 /*
441  * Perform exchange of init/version packets. Return 0 on failure.
442  */
443 int fxp_init(void)
444 {
445     struct sftp_packet *pktout, *pktin;
446     int remotever;
447
448     pktout = sftp_pkt_init(SSH_FXP_INIT);
449     sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
450     sftp_send(pktout);
451
452     pktin = sftp_recv();
453     if (!pktin) {
454         fxp_internal_error("could not connect");
455         return 0;
456     }
457     if (pktin->type != SSH_FXP_VERSION) {
458         fxp_internal_error("did not receive FXP_VERSION");
459         sftp_pkt_free(pktin);
460         return 0;
461     }
462     remotever = sftp_pkt_getuint32(pktin);
463     if (remotever > SFTP_PROTO_VERSION) {
464         fxp_internal_error
465             ("remote protocol is more advanced than we support");
466         sftp_pkt_free(pktin);
467         return 0;
468     }
469     /*
470      * In principle, this packet might also contain extension-
471      * string pairs. We should work through them and look for any
472      * we recognise. In practice we don't currently do so because
473      * we know we don't recognise _any_.
474      */
475     sftp_pkt_free(pktin);
476
477     return 1;
478 }
479
480 /*
481  * Canonify a pathname.
482  */
483 struct sftp_request *fxp_realpath_send(char *path)
484 {
485     struct sftp_request *req = sftp_alloc_request();
486     struct sftp_packet *pktout;
487
488     pktout = sftp_pkt_init(SSH_FXP_REALPATH);
489     sftp_pkt_adduint32(pktout, req->id);
490     sftp_pkt_addstring_start(pktout);
491     sftp_pkt_addstring_str(pktout, path);
492     sftp_send(pktout);
493
494     return req;
495 }
496
497 char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
498 {
499     sfree(req);
500
501     if (pktin->type == SSH_FXP_NAME) {
502         int count;
503         char *path;
504         int len;
505
506         count = sftp_pkt_getuint32(pktin);
507         if (count != 1) {
508             fxp_internal_error("REALPATH returned name count != 1\n");
509             sftp_pkt_free(pktin);
510             return NULL;
511         }
512         sftp_pkt_getstring(pktin, &path, &len);
513         if (!path) {
514             fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
515             sftp_pkt_free(pktin);
516             return NULL;
517         }
518         path = mkstr(path, len);
519         sftp_pkt_free(pktin);
520         return path;
521     } else {
522         fxp_got_status(pktin);
523         sftp_pkt_free(pktin);
524         return NULL;
525     }
526 }
527
528 /*
529  * Open a file.
530  */
531 struct sftp_request *fxp_open_send(char *path, int type)
532 {
533     struct sftp_request *req = sftp_alloc_request();
534     struct sftp_packet *pktout;
535
536     pktout = sftp_pkt_init(SSH_FXP_OPEN);
537     sftp_pkt_adduint32(pktout, req->id);
538     sftp_pkt_addstring(pktout, path);
539     sftp_pkt_adduint32(pktout, type);
540     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
541     sftp_send(pktout);
542
543     return req;
544 }
545
546 struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
547                                  struct sftp_request *req)
548 {
549     sfree(req);
550
551     if (pktin->type == SSH_FXP_HANDLE) {
552         char *hstring;
553         struct fxp_handle *handle;
554         int len;
555
556         sftp_pkt_getstring(pktin, &hstring, &len);
557         if (!hstring) {
558             fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
559             sftp_pkt_free(pktin);
560             return NULL;
561         }
562         handle = snew(struct fxp_handle);
563         handle->hstring = mkstr(hstring, len);
564         handle->hlen = len;
565         sftp_pkt_free(pktin);
566         return handle;
567     } else {
568         fxp_got_status(pktin);
569         sftp_pkt_free(pktin);
570         return NULL;
571     }
572 }
573
574 /*
575  * Open a directory.
576  */
577 struct sftp_request *fxp_opendir_send(char *path)
578 {
579     struct sftp_request *req = sftp_alloc_request();
580     struct sftp_packet *pktout;
581
582     pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
583     sftp_pkt_adduint32(pktout, req->id);
584     sftp_pkt_addstring(pktout, path);
585     sftp_send(pktout);
586
587     return req;
588 }
589
590 struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
591                                     struct sftp_request *req)
592 {
593     sfree(req);
594     if (pktin->type == SSH_FXP_HANDLE) {
595         char *hstring;
596         struct fxp_handle *handle;
597         int len;
598
599         sftp_pkt_getstring(pktin, &hstring, &len);
600         if (!hstring) {
601             fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
602             sftp_pkt_free(pktin);
603             return NULL;
604         }
605         handle = snew(struct fxp_handle);
606         handle->hstring = mkstr(hstring, len);
607         handle->hlen = len;
608         sftp_pkt_free(pktin);
609         return handle;
610     } else {
611         fxp_got_status(pktin);
612         sftp_pkt_free(pktin);
613         return NULL;
614     }
615 }
616
617 /*
618  * Close a file/dir.
619  */
620 struct sftp_request *fxp_close_send(struct fxp_handle *handle)
621 {
622     struct sftp_request *req = sftp_alloc_request();
623     struct sftp_packet *pktout;
624
625     pktout = sftp_pkt_init(SSH_FXP_CLOSE);
626     sftp_pkt_adduint32(pktout, req->id);
627     sftp_pkt_addstring_start(pktout);
628     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
629     sftp_send(pktout);
630
631     sfree(handle->hstring);
632     sfree(handle);
633
634     return req;
635 }
636
637 void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)
638 {
639     sfree(req);
640     fxp_got_status(pktin);
641     sftp_pkt_free(pktin);
642 }
643
644 struct sftp_request *fxp_mkdir_send(char *path)
645 {
646     struct sftp_request *req = sftp_alloc_request();
647     struct sftp_packet *pktout;
648
649     pktout = sftp_pkt_init(SSH_FXP_MKDIR);
650     sftp_pkt_adduint32(pktout, req->id);
651     sftp_pkt_addstring(pktout, path);
652     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
653     sftp_send(pktout);
654
655     return req;
656 }
657
658 int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
659 {
660     int id;
661     sfree(req);
662     id = fxp_got_status(pktin);
663     sftp_pkt_free(pktin);
664     if (id != 1) {
665         return 0;
666     }
667     return 1;
668 }
669
670 struct sftp_request *fxp_rmdir_send(char *path)
671 {
672     struct sftp_request *req = sftp_alloc_request();
673     struct sftp_packet *pktout;
674
675     pktout = sftp_pkt_init(SSH_FXP_RMDIR);
676     sftp_pkt_adduint32(pktout, req->id);
677     sftp_pkt_addstring(pktout, path);
678     sftp_send(pktout);
679
680     return req;
681 }
682
683 int fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
684 {
685     int id;
686     sfree(req);
687     id = fxp_got_status(pktin);
688     sftp_pkt_free(pktin);
689     if (id != 1) {
690         return 0;
691     }
692     return 1;
693 }
694
695 struct sftp_request *fxp_remove_send(char *fname)
696 {
697     struct sftp_request *req = sftp_alloc_request();
698     struct sftp_packet *pktout;
699
700     pktout = sftp_pkt_init(SSH_FXP_REMOVE);
701     sftp_pkt_adduint32(pktout, req->id);
702     sftp_pkt_addstring(pktout, fname);
703     sftp_send(pktout);
704
705     return req;
706 }
707
708 int fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req)
709 {
710     int id;
711     sfree(req);
712     id = fxp_got_status(pktin);
713     sftp_pkt_free(pktin);
714     if (id != 1) {
715         return 0;
716     }
717     return 1;
718 }
719
720 struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
721 {
722     struct sftp_request *req = sftp_alloc_request();
723     struct sftp_packet *pktout;
724
725     pktout = sftp_pkt_init(SSH_FXP_RENAME);
726     sftp_pkt_adduint32(pktout, req->id);
727     sftp_pkt_addstring(pktout, srcfname);
728     sftp_pkt_addstring(pktout, dstfname);
729     sftp_send(pktout);
730
731     return req;
732 }
733
734 int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req)
735 {
736     int id;
737     sfree(req);
738     id = fxp_got_status(pktin);
739     sftp_pkt_free(pktin);
740     if (id != 1) {
741         return 0;
742     }
743     return 1;
744 }
745
746 /*
747  * Retrieve the attributes of a file. We have fxp_stat which works
748  * on filenames, and fxp_fstat which works on open file handles.
749  */
750 struct sftp_request *fxp_stat_send(char *fname)
751 {
752     struct sftp_request *req = sftp_alloc_request();
753     struct sftp_packet *pktout;
754
755     pktout = sftp_pkt_init(SSH_FXP_STAT);
756     sftp_pkt_adduint32(pktout, req->id);
757     sftp_pkt_addstring(pktout, fname);
758     sftp_send(pktout);
759
760     return req;
761 }
762
763 int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
764                   struct fxp_attrs *attrs)
765 {
766     sfree(req);
767     if (pktin->type == SSH_FXP_ATTRS) {
768         *attrs = sftp_pkt_getattrs(pktin);
769         sftp_pkt_free(pktin);
770         return 1;
771     } else {
772         fxp_got_status(pktin);
773         sftp_pkt_free(pktin);
774         return 0;
775     }
776 }
777
778 struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
779 {
780     struct sftp_request *req = sftp_alloc_request();
781     struct sftp_packet *pktout;
782
783     pktout = sftp_pkt_init(SSH_FXP_FSTAT);
784     sftp_pkt_adduint32(pktout, req->id);
785     sftp_pkt_addstring_start(pktout);
786     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
787     sftp_send(pktout);
788
789     return req;
790 }
791
792 int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
793                    struct fxp_attrs *attrs)
794 {
795     sfree(req);
796     if (pktin->type == SSH_FXP_ATTRS) {
797         *attrs = sftp_pkt_getattrs(pktin);
798         sftp_pkt_free(pktin);
799         return 1;
800     } else {
801         fxp_got_status(pktin);
802         sftp_pkt_free(pktin);
803         return 0;
804     }
805 }
806
807 /*
808  * Set the attributes of a file.
809  */
810 struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
811 {
812     struct sftp_request *req = sftp_alloc_request();
813     struct sftp_packet *pktout;
814
815     pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
816     sftp_pkt_adduint32(pktout, req->id);
817     sftp_pkt_addstring(pktout, fname);
818     sftp_pkt_addattrs(pktout, attrs);
819     sftp_send(pktout);
820
821     return req;
822 }
823
824 int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
825 {
826     int id;
827     sfree(req);
828     id = fxp_got_status(pktin);
829     sftp_pkt_free(pktin);
830     if (id != 1) {
831         return 0;
832     }
833     return 1;
834 }
835
836 struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
837                                        struct fxp_attrs attrs)
838 {
839     struct sftp_request *req = sftp_alloc_request();
840     struct sftp_packet *pktout;
841
842     pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
843     sftp_pkt_adduint32(pktout, req->id);
844     sftp_pkt_addstring_start(pktout);
845     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
846     sftp_pkt_addattrs(pktout, attrs);
847     sftp_send(pktout);
848
849     return req;
850 }
851
852 int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
853 {
854     int id;
855     sfree(req);
856     id = fxp_got_status(pktin);
857     sftp_pkt_free(pktin);
858     if (id != 1) {
859         return 0;
860     }
861     return 1;
862 }
863
864 /*
865  * Read from a file. Returns the number of bytes read, or -1 on an
866  * error, or possibly 0 if EOF. (I'm not entirely sure whether it
867  * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
868  * error indicator. It might even depend on the SFTP server.)
869  */
870 struct sftp_request *fxp_read_send(struct fxp_handle *handle,
871                                    uint64 offset, int len)
872 {
873     struct sftp_request *req = sftp_alloc_request();
874     struct sftp_packet *pktout;
875
876     pktout = sftp_pkt_init(SSH_FXP_READ);
877     sftp_pkt_adduint32(pktout, req->id);
878     sftp_pkt_addstring_start(pktout);
879     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
880     sftp_pkt_adduint64(pktout, offset);
881     sftp_pkt_adduint32(pktout, len);
882     sftp_send(pktout);
883
884     return req;
885 }
886
887 int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
888                   char *buffer, int len)
889 {
890     sfree(req);
891     if (pktin->type == SSH_FXP_DATA) {
892         char *str;
893         int rlen;
894
895         sftp_pkt_getstring(pktin, &str, &rlen);
896
897         if (rlen > len || rlen < 0) {
898             fxp_internal_error("READ returned more bytes than requested");
899             sftp_pkt_free(pktin);
900             return -1;
901         }
902
903         memcpy(buffer, str, rlen);
904         sftp_pkt_free(pktin);
905         return rlen;
906     } else {
907         fxp_got_status(pktin);
908         sftp_pkt_free(pktin);
909         return -1;
910     }
911 }
912
913 /*
914  * Read from a directory.
915  */
916 struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
917 {
918     struct sftp_request *req = sftp_alloc_request();
919     struct sftp_packet *pktout;
920
921     pktout = sftp_pkt_init(SSH_FXP_READDIR);
922     sftp_pkt_adduint32(pktout, req->id);
923     sftp_pkt_addstring_start(pktout);
924     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
925     sftp_send(pktout);
926
927     return req;
928 }
929
930 struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
931                                    struct sftp_request *req)
932 {
933     sfree(req);
934     if (pktin->type == SSH_FXP_NAME) {
935         struct fxp_names *ret;
936         int i;
937         ret = snew(struct fxp_names);
938         ret->nnames = sftp_pkt_getuint32(pktin);
939         ret->names = snewn(ret->nnames, struct fxp_name);
940         for (i = 0; i < ret->nnames; i++) {
941             char *str;
942             int len;
943             sftp_pkt_getstring(pktin, &str, &len);
944             ret->names[i].filename = mkstr(str, len);
945             sftp_pkt_getstring(pktin, &str, &len);
946             ret->names[i].longname = mkstr(str, len);
947             ret->names[i].attrs = sftp_pkt_getattrs(pktin);
948         }
949         sftp_pkt_free(pktin);
950         return ret;
951     } else {
952         fxp_got_status(pktin);
953         sftp_pkt_free(pktin);
954         return NULL;
955     }
956 }
957
958 /*
959  * Write to a file. Returns 0 on error, 1 on OK.
960  */
961 struct sftp_request *fxp_write_send(struct fxp_handle *handle,
962                                     char *buffer, uint64 offset, int len)
963 {
964     struct sftp_request *req = sftp_alloc_request();
965     struct sftp_packet *pktout;
966
967     pktout = sftp_pkt_init(SSH_FXP_WRITE);
968     sftp_pkt_adduint32(pktout, req->id);
969     sftp_pkt_addstring_start(pktout);
970     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
971     sftp_pkt_adduint64(pktout, offset);
972     sftp_pkt_addstring_start(pktout);
973     sftp_pkt_addstring_data(pktout, buffer, len);
974     sftp_send(pktout);
975
976     return req;
977 }
978
979 int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
980 {
981     sfree(req);
982     fxp_got_status(pktin);
983     sftp_pkt_free(pktin);
984     return fxp_errtype == SSH_FX_OK;
985 }
986
987 /*
988  * Free up an fxp_names structure.
989  */
990 void fxp_free_names(struct fxp_names *names)
991 {
992     int i;
993
994     for (i = 0; i < names->nnames; i++) {
995         sfree(names->names[i].filename);
996         sfree(names->names[i].longname);
997     }
998     sfree(names->names);
999     sfree(names);
1000 }
1001
1002 /*
1003  * Duplicate an fxp_name structure.
1004  */
1005 struct fxp_name *fxp_dup_name(struct fxp_name *name)
1006 {
1007     struct fxp_name *ret;
1008     ret = snew(struct fxp_name);
1009     ret->filename = dupstr(name->filename);
1010     ret->longname = dupstr(name->longname);
1011     ret->attrs = name->attrs;          /* structure copy */
1012     return ret;
1013 }
1014
1015 /*
1016  * Free up an fxp_name structure.
1017  */
1018 void fxp_free_name(struct fxp_name *name)
1019 {
1020     sfree(name->filename);
1021     sfree(name->longname);
1022     sfree(name);
1023 }
1024
1025 /*
1026  * Store user data in an sftp_request structure.
1027  */
1028 void *fxp_get_userdata(struct sftp_request *req)
1029 {
1030     return req->userdata;
1031 }
1032
1033 void fxp_set_userdata(struct sftp_request *req, void *data)
1034 {
1035     req->userdata = data;
1036 }
1037
1038 /*
1039  * A wrapper to go round fxp_read_* and fxp_write_*, which manages
1040  * the queueing of multiple read/write requests.
1041  */
1042
1043 struct req {
1044     char *buffer;
1045     int len, retlen, complete;
1046     uint64 offset;
1047     struct req *next, *prev;
1048 };
1049
1050 struct fxp_xfer {
1051     uint64 offset, furthestdata, filesize;
1052     int req_totalsize, req_maxsize, eof, err;
1053     struct fxp_handle *fh;
1054     struct req *head, *tail;
1055 };
1056
1057 static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64 offset)
1058 {
1059     struct fxp_xfer *xfer = snew(struct fxp_xfer);
1060
1061     xfer->fh = fh;
1062     xfer->offset = offset;
1063     xfer->head = xfer->tail = NULL;
1064     xfer->req_totalsize = 0;
1065     xfer->req_maxsize = 16384;
1066     xfer->err = 0;
1067     xfer->filesize = uint64_make(ULONG_MAX, ULONG_MAX);
1068     xfer->furthestdata = uint64_make(0, 0);
1069
1070     return xfer;
1071 }
1072
1073 int xfer_done(struct fxp_xfer *xfer)
1074 {
1075     /*
1076      * We're finished if we've seen EOF _and_ there are no
1077      * outstanding requests.
1078      */
1079     return (xfer->eof || xfer->err) && !xfer->head;
1080 }
1081
1082 void xfer_download_queue(struct fxp_xfer *xfer)
1083 {
1084     while (xfer->req_totalsize < xfer->req_maxsize && !xfer->eof) {
1085         /*
1086          * Queue a new read request.
1087          */
1088         struct req *rr;
1089         struct sftp_request *req;
1090
1091         rr = snew(struct req);
1092         rr->offset = xfer->offset;
1093         rr->complete = 0;
1094         if (xfer->tail) {
1095             xfer->tail->next = rr;
1096             rr->prev = xfer->tail;
1097         } else {
1098             xfer->head = rr;
1099             rr->prev = NULL;
1100         }
1101         xfer->tail = rr;
1102         rr->next = NULL;
1103
1104         rr->len = 4096;
1105         rr->buffer = snewn(rr->len, char);
1106         sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));
1107         fxp_set_userdata(req, rr);
1108
1109         xfer->offset = uint64_add32(xfer->offset, rr->len);
1110         xfer->req_totalsize += rr->len;
1111
1112 #ifdef DEBUG_DOWNLOAD
1113         { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }
1114 #endif
1115     }
1116 }
1117
1118 struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)
1119 {
1120     struct fxp_xfer *xfer = xfer_init(fh, offset);
1121
1122     xfer->eof = FALSE;
1123     xfer_download_queue(xfer);
1124
1125     return xfer;
1126 }
1127
1128 int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1129 {
1130     struct sftp_request *rreq;
1131     struct req *rr;
1132
1133     rreq = sftp_find_request(pktin);
1134     rr = (struct req *)fxp_get_userdata(rreq);
1135     if (!rr)
1136         return 0;                      /* this packet isn't ours */
1137     rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);
1138 #ifdef DEBUG_DOWNLOAD
1139     printf("read request %p has returned [%d]\n", rr, rr->retlen);
1140 #endif
1141
1142     if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {
1143         xfer->eof = TRUE;
1144         rr->complete = -1;
1145 #ifdef DEBUG_DOWNLOAD
1146         printf("setting eof\n");
1147 #endif
1148     } else if (rr->retlen < 0) {
1149         /* some error other than EOF; signal it back to caller */
1150         return -1;
1151     }
1152
1153     rr->complete = 1;
1154
1155     /*
1156      * Special case: if we have received fewer bytes than we
1157      * actually read, we should do something. For the moment I'll
1158      * just throw an ersatz FXP error to signal this; the SFTP
1159      * draft I've got says that it can't happen except on special
1160      * files, in which case seeking probably has very little
1161      * meaning and so queueing an additional read request to fill
1162      * up the gap sounds like the wrong answer. I'm not sure what I
1163      * should be doing here - if it _was_ a special file, I suspect
1164      * I simply shouldn't have been queueing multiple requests in
1165      * the first place...
1166      */
1167     if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {
1168         xfer->furthestdata = rr->offset;
1169 #ifdef DEBUG_DOWNLOAD
1170         { char buf[40];
1171         uint64_decimal(xfer->furthestdata, buf);
1172         printf("setting furthestdata = %s\n", buf); }
1173 #endif
1174     }
1175
1176     if (rr->retlen < rr->len) {
1177         uint64 filesize = uint64_add32(rr->offset,
1178                                        (rr->retlen < 0 ? 0 : rr->retlen));
1179 #ifdef DEBUG_DOWNLOAD
1180         { char buf[40];
1181         uint64_decimal(filesize, buf);
1182         printf("short block! trying filesize = %s\n", buf); }
1183 #endif
1184         if (uint64_compare(xfer->filesize, filesize) > 0) {
1185             xfer->filesize = filesize;
1186 #ifdef DEBUG_DOWNLOAD
1187             printf("actually changing filesize\n");
1188 #endif      
1189         }
1190     }
1191
1192     if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {
1193         fxp_error_message = "received a short buffer from FXP_READ, but not"
1194             " at EOF";
1195         fxp_errtype = -1;
1196         xfer_set_error(xfer);
1197         return -1;
1198     }
1199
1200     return 1;
1201 }
1202
1203 void xfer_set_error(struct fxp_xfer *xfer)
1204 {
1205     xfer->err = 1;
1206 }
1207
1208 int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)
1209 {
1210     void *retbuf = NULL;
1211     int retlen = 0;
1212
1213     /*
1214      * Discard anything at the head of the rr queue with complete <
1215      * 0; return the first thing with complete > 0.
1216      */
1217     while (xfer->head && xfer->head->complete && !retbuf) {
1218         struct req *rr = xfer->head;
1219
1220         if (rr->complete > 0) {
1221             retbuf = rr->buffer;
1222             retlen = rr->retlen;
1223 #ifdef DEBUG_DOWNLOAD
1224             printf("handing back data from read request %p\n", rr);
1225 #endif
1226         }
1227 #ifdef DEBUG_DOWNLOAD
1228         else
1229             printf("skipping failed read request %p\n", rr);
1230 #endif
1231
1232         xfer->head = xfer->head->next;
1233         if (xfer->head)
1234             xfer->head->prev = NULL;
1235         else
1236             xfer->tail = NULL;
1237         xfer->req_totalsize -= rr->len;
1238         sfree(rr);
1239     }
1240
1241     if (retbuf) {
1242         *buf = retbuf;
1243         *len = retlen;
1244         return 1;
1245     } else
1246         return 0;
1247 }
1248
1249 struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)
1250 {
1251     struct fxp_xfer *xfer = xfer_init(fh, offset);
1252
1253     /*
1254      * We set `eof' to 1 because this will cause xfer_done() to
1255      * return true iff there are no outstanding requests. During an
1256      * upload, our caller will be responsible for working out
1257      * whether all the data has been sent, so all it needs to know
1258      * from us is whether the outstanding requests have been
1259      * handled once that's done.
1260      */
1261     xfer->eof = 1;
1262
1263     return xfer;
1264 }
1265
1266 int xfer_upload_ready(struct fxp_xfer *xfer)
1267 {
1268     if (xfer->req_totalsize < xfer->req_maxsize)
1269         return 1;
1270     else
1271         return 0;
1272 }
1273
1274 void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)
1275 {
1276     struct req *rr;
1277     struct sftp_request *req;
1278
1279     rr = snew(struct req);
1280     rr->offset = xfer->offset;
1281     rr->complete = 0;
1282     if (xfer->tail) {
1283         xfer->tail->next = rr;
1284         rr->prev = xfer->tail;
1285     } else {
1286         xfer->head = rr;
1287         rr->prev = NULL;
1288     }
1289     xfer->tail = rr;
1290     rr->next = NULL;
1291
1292     rr->len = len;
1293     rr->buffer = NULL;
1294     sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));
1295     fxp_set_userdata(req, rr);
1296
1297     xfer->offset = uint64_add32(xfer->offset, rr->len);
1298     xfer->req_totalsize += rr->len;
1299
1300 #ifdef DEBUG_UPLOAD
1301     { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }
1302 #endif
1303 }
1304
1305 int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1306 {
1307     struct sftp_request *rreq;
1308     struct req *rr, *prev, *next;
1309     int ret;
1310
1311     rreq = sftp_find_request(pktin);
1312     rr = (struct req *)fxp_get_userdata(rreq);
1313     if (!rr)
1314         return 0;                      /* this packet isn't ours */
1315     ret = fxp_write_recv(pktin, rreq);
1316 #ifdef DEBUG_UPLOAD
1317     printf("write request %p has returned [%d]\n", rr, ret);
1318 #endif
1319
1320     /*
1321      * Remove this one from the queue.
1322      */
1323     prev = rr->prev;
1324     next = rr->next;
1325     if (prev)
1326         prev->next = next;
1327     else
1328         xfer->head = next;
1329     if (next)
1330         next->prev = prev;
1331     else
1332         xfer->tail = prev;
1333     xfer->req_totalsize -= rr->len;
1334     sfree(rr);
1335
1336     if (!ret)
1337         return -1;
1338
1339     return 1;
1340 }
1341
1342 void xfer_cleanup(struct fxp_xfer *xfer)
1343 {
1344     struct req *rr;
1345     while (xfer->head) {
1346         rr = xfer->head;
1347         xfer->head = xfer->head->next;
1348         sfree(rr->buffer);
1349         sfree(rr);
1350     }
1351     sfree(xfer);
1352 }