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