]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
And just to prove that psftp.c really is now platform-independent
[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
10 #include "misc.h"
11 #include "int64.h"
12 #include "tree234.h"
13 #include "sftp.h"
14
15 #define GET_32BIT(cp) \
16     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
17     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
18     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
19     ((unsigned long)(unsigned char)(cp)[3]))
20
21 #define PUT_32BIT(cp, value) { \
22     (cp)[0] = (unsigned char)((value) >> 24); \
23     (cp)[1] = (unsigned char)((value) >> 16); \
24     (cp)[2] = (unsigned char)((value) >> 8); \
25     (cp)[3] = (unsigned char)(value); }
26
27 struct sftp_packet {
28     char *data;
29     int length, maxlen;
30     int savedpos;
31     int type;
32 };
33
34 static const char *fxp_error_message;
35 static int fxp_errtype;
36
37 static void fxp_internal_error(char *msg);
38
39 /* ----------------------------------------------------------------------
40  * SFTP packet construction functions.
41  */
42 static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
43 {
44     if (pkt->maxlen < length) {
45         pkt->maxlen = length + 256;
46         pkt->data = sresize(pkt->data, pkt->maxlen, char);
47     }
48 }
49 static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
50 {
51     pkt->length += len;
52     sftp_pkt_ensure(pkt, pkt->length);
53     memcpy(pkt->data + pkt->length - len, data, len);
54 }
55 static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
56 {
57     sftp_pkt_adddata(pkt, &byte, 1);
58 }
59 static struct sftp_packet *sftp_pkt_init(int pkt_type)
60 {
61     struct sftp_packet *pkt;
62     pkt = snew(struct sftp_packet);
63     pkt->data = NULL;
64     pkt->savedpos = -1;
65     pkt->length = 0;
66     pkt->maxlen = 0;
67     sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
68     return pkt;
69 }
70 /*
71 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
72 {
73     sftp_pkt_adddata(pkt, &value, 1);
74 }
75 */
76 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
77                                unsigned long value)
78 {
79     unsigned char x[4];
80     PUT_32BIT(x, value);
81     sftp_pkt_adddata(pkt, x, 4);
82 }
83 static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
84 {
85     unsigned char x[8];
86     PUT_32BIT(x, value.hi);
87     PUT_32BIT(x + 4, value.lo);
88     sftp_pkt_adddata(pkt, x, 8);
89 }
90 static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
91 {
92     sftp_pkt_adduint32(pkt, 0);
93     pkt->savedpos = pkt->length;
94 }
95 static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
96 {
97     sftp_pkt_adddata(pkt, data, strlen(data));
98     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
99 }
100 static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
101                                     char *data, int len)
102 {
103     sftp_pkt_adddata(pkt, data, len);
104     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
105 }
106 static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
107 {
108     sftp_pkt_addstring_start(pkt);
109     sftp_pkt_addstring_str(pkt, data);
110 }
111 static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
112 {
113     sftp_pkt_adduint32(pkt, attrs.flags);
114     if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
115         sftp_pkt_adduint32(pkt, attrs.size.hi);
116         sftp_pkt_adduint32(pkt, attrs.size.lo);
117     }
118     if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
119         sftp_pkt_adduint32(pkt, attrs.uid);
120         sftp_pkt_adduint32(pkt, attrs.gid);
121     }
122     if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
123         sftp_pkt_adduint32(pkt, attrs.permissions);
124     }
125     if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
126         sftp_pkt_adduint32(pkt, attrs.atime);
127         sftp_pkt_adduint32(pkt, attrs.mtime);
128     }
129     if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
130         /*
131          * We currently don't support sending any extended
132          * attributes.
133          */
134     }
135 }
136
137 /* ----------------------------------------------------------------------
138  * SFTP packet decode functions.
139  */
140
141 static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
142 {
143     unsigned char value;
144     if (pkt->length - pkt->savedpos < 1)
145         return 0;                      /* arrgh, no way to decline (FIXME?) */
146     value = (unsigned char) pkt->data[pkt->savedpos];
147     pkt->savedpos++;
148     return value;
149 }
150 static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
151 {
152     unsigned long value;
153     if (pkt->length - pkt->savedpos < 4)
154         return 0;                      /* arrgh, no way to decline (FIXME?) */
155     value = GET_32BIT(pkt->data + pkt->savedpos);
156     pkt->savedpos += 4;
157     return value;
158 }
159 static void sftp_pkt_getstring(struct sftp_packet *pkt,
160                                char **p, int *length)
161 {
162     *p = NULL;
163     if (pkt->length - pkt->savedpos < 4)
164         return;
165     *length = GET_32BIT(pkt->data + pkt->savedpos);
166     pkt->savedpos += 4;
167     if (pkt->length - pkt->savedpos < *length)
168         return;
169     *p = pkt->data + pkt->savedpos;
170     pkt->savedpos += *length;
171 }
172 static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
173 {
174     struct fxp_attrs ret;
175     ret.flags = sftp_pkt_getuint32(pkt);
176     if (ret.flags & SSH_FILEXFER_ATTR_SIZE) {
177         unsigned long hi, lo;
178         hi = sftp_pkt_getuint32(pkt);
179         lo = sftp_pkt_getuint32(pkt);
180         ret.size = uint64_make(hi, lo);
181     }
182     if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) {
183         ret.uid = sftp_pkt_getuint32(pkt);
184         ret.gid = sftp_pkt_getuint32(pkt);
185     }
186     if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
187         ret.permissions = sftp_pkt_getuint32(pkt);
188     }
189     if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
190         ret.atime = sftp_pkt_getuint32(pkt);
191         ret.mtime = sftp_pkt_getuint32(pkt);
192     }
193     if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) {
194         int count;
195         count = sftp_pkt_getuint32(pkt);
196         while (count--) {
197             char *str;
198             int len;
199             /*
200              * We should try to analyse these, if we ever find one
201              * we recognise.
202              */
203             sftp_pkt_getstring(pkt, &str, &len);
204             sftp_pkt_getstring(pkt, &str, &len);
205         }
206     }
207     return ret;
208 }
209 static void sftp_pkt_free(struct sftp_packet *pkt)
210 {
211     if (pkt->data)
212         sfree(pkt->data);
213     sfree(pkt);
214 }
215
216 /* ----------------------------------------------------------------------
217  * Send and receive packet functions.
218  */
219 int sftp_send(struct sftp_packet *pkt)
220 {
221     int ret;
222     char x[4];
223     PUT_32BIT(x, pkt->length);
224     ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
225     sftp_pkt_free(pkt);
226     return ret;
227 }
228 struct sftp_packet *sftp_recv(void)
229 {
230     struct sftp_packet *pkt;
231     char x[4];
232
233     if (!sftp_recvdata(x, 4))
234         return NULL;
235
236     pkt = snew(struct sftp_packet);
237     pkt->savedpos = 0;
238     pkt->length = pkt->maxlen = GET_32BIT(x);
239     pkt->data = snewn(pkt->length, char);
240
241     if (!sftp_recvdata(pkt->data, pkt->length)) {
242         sftp_pkt_free(pkt);
243         return NULL;
244     }
245
246     pkt->type = sftp_pkt_getbyte(pkt);
247
248     return pkt;
249 }
250
251 /* ----------------------------------------------------------------------
252  * Request ID allocation and temporary dispatch routines.
253  */
254
255 #define REQUEST_ID_OFFSET 256
256
257 struct sftp_request {
258     unsigned id;
259     int registered;
260 };
261
262 static int sftp_reqcmp(void *av, void *bv)
263 {
264     struct sftp_request *a = (struct sftp_request *)av;
265     struct sftp_request *b = (struct sftp_request *)bv;
266     if (a->id < b->id)
267         return -1;
268     if (a->id > b->id)
269         return +1;
270     return 0;
271 }
272 static int sftp_reqfind(void *av, void *bv)
273 {
274     unsigned *a = (unsigned *) av;
275     struct sftp_request *b = (struct sftp_request *)bv;
276     if (*a < b->id)
277         return -1;
278     if (*a > b->id)
279         return +1;
280     return 0;
281 }
282
283 static tree234 *sftp_requests;
284
285 static struct sftp_request *sftp_alloc_request(void)
286 {
287     unsigned low, high, mid;
288     int tsize;
289     struct sftp_request *r;
290
291     if (sftp_requests == NULL)
292         sftp_requests = newtree234(sftp_reqcmp);
293
294     /*
295      * First-fit allocation of request IDs: always pick the lowest
296      * unused one. To do this, binary-search using the counted
297      * B-tree to find the largest ID which is in a contiguous
298      * sequence from the beginning. (Precisely everything in that
299      * sequence must have ID equal to its tree index plus
300      * REQUEST_ID_OFFSET.)
301      */
302     tsize = count234(sftp_requests);
303
304     low = -1;
305     high = tsize;
306     while (high - low > 1) {
307         mid = (high + low) / 2;
308         r = index234(sftp_requests, mid);
309         if (r->id == mid + REQUEST_ID_OFFSET)
310             low = mid;                 /* this one is fine */
311         else
312             high = mid;                /* this one is past it */
313     }
314     /*
315      * Now low points to either -1, or the tree index of the
316      * largest ID in the initial sequence.
317      */
318     {
319         unsigned i = low + 1 + REQUEST_ID_OFFSET;
320         assert(NULL == find234(sftp_requests, &i, sftp_reqfind));
321     }
322
323     /*
324      * So the request ID we need to create is
325      * low + 1 + REQUEST_ID_OFFSET.
326      */
327     r = snew(struct sftp_request);
328     r->id = low + 1 + REQUEST_ID_OFFSET;
329     r->registered = 0;
330     add234(sftp_requests, r);
331     return r;
332 }
333
334 void sftp_register(struct sftp_request *req)
335 {
336     req->registered = 1;
337 }
338
339 struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
340 {
341     unsigned long id;
342     struct sftp_request *req;
343
344     if (!pktin) {
345         fxp_internal_error("did not receive a valid SFTP packet\n");
346         return NULL;
347     }
348
349     id = sftp_pkt_getuint32(pktin);
350     req = find234(sftp_requests, &id, sftp_reqfind);
351
352     if (!req || !req->registered) {
353         fxp_internal_error("request ID mismatch\n");
354         sftp_pkt_free(pktin);
355         return NULL;
356     }
357
358     del234(sftp_requests, req);
359
360     return req;
361 }
362
363 /* ----------------------------------------------------------------------
364  * String handling routines.
365  */
366
367 static char *mkstr(char *s, int len)
368 {
369     char *p = snewn(len + 1, char);
370     memcpy(p, s, len);
371     p[len] = '\0';
372     return p;
373 }
374
375 /* ----------------------------------------------------------------------
376  * SFTP primitives.
377  */
378
379 /*
380  * Deal with (and free) an FXP_STATUS packet. Return 1 if
381  * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
382  * Also place the status into fxp_errtype.
383  */
384 static int fxp_got_status(struct sftp_packet *pktin)
385 {
386     static const char *const messages[] = {
387         /* SSH_FX_OK. The only time we will display a _message_ for this
388          * is if we were expecting something other than FXP_STATUS on
389          * success, so this is actually an error message! */
390         "unexpected OK response",
391         "end of file",
392         "no such file or directory",
393         "permission denied",
394         "failure",
395         "bad message",
396         "no connection",
397         "connection lost",
398         "operation unsupported",
399     };
400
401     if (pktin->type != SSH_FXP_STATUS) {
402         fxp_error_message = "expected FXP_STATUS packet";
403         fxp_errtype = -1;
404     } else {
405         fxp_errtype = sftp_pkt_getuint32(pktin);
406         if (fxp_errtype < 0 ||
407             fxp_errtype >= sizeof(messages) / sizeof(*messages))
408                 fxp_error_message = "unknown error code";
409         else
410             fxp_error_message = messages[fxp_errtype];
411     }
412
413     if (fxp_errtype == SSH_FX_OK)
414         return 1;
415     else if (fxp_errtype == SSH_FX_EOF)
416         return 0;
417     else
418         return -1;
419 }
420
421 static void fxp_internal_error(char *msg)
422 {
423     fxp_error_message = msg;
424     fxp_errtype = -1;
425 }
426
427 const char *fxp_error(void)
428 {
429     return fxp_error_message;
430 }
431
432 int fxp_error_type(void)
433 {
434     return fxp_errtype;
435 }
436
437 /*
438  * Perform exchange of init/version packets. Return 0 on failure.
439  */
440 int fxp_init(void)
441 {
442     struct sftp_packet *pktout, *pktin;
443     int remotever;
444
445     pktout = sftp_pkt_init(SSH_FXP_INIT);
446     sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
447     sftp_send(pktout);
448
449     pktin = sftp_recv();
450     if (!pktin) {
451         fxp_internal_error("could not connect");
452         return 0;
453     }
454     if (pktin->type != SSH_FXP_VERSION) {
455         fxp_internal_error("did not receive FXP_VERSION");
456         sftp_pkt_free(pktin);
457         return 0;
458     }
459     remotever = sftp_pkt_getuint32(pktin);
460     if (remotever > SFTP_PROTO_VERSION) {
461         fxp_internal_error
462             ("remote protocol is more advanced than we support");
463         sftp_pkt_free(pktin);
464         return 0;
465     }
466     /*
467      * In principle, this packet might also contain extension-
468      * string pairs. We should work through them and look for any
469      * we recognise. In practice we don't currently do so because
470      * we know we don't recognise _any_.
471      */
472     sftp_pkt_free(pktin);
473
474     return 1;
475 }
476
477 /*
478  * Canonify a pathname.
479  */
480 struct sftp_request *fxp_realpath_send(char *path)
481 {
482     struct sftp_request *req = sftp_alloc_request();
483     struct sftp_packet *pktout;
484
485     pktout = sftp_pkt_init(SSH_FXP_REALPATH);
486     sftp_pkt_adduint32(pktout, req->id);
487     sftp_pkt_addstring_start(pktout);
488     sftp_pkt_addstring_str(pktout, path);
489     sftp_send(pktout);
490
491     return req;
492 }
493
494 char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
495 {
496     sfree(req);
497
498     if (pktin->type == SSH_FXP_NAME) {
499         int count;
500         char *path;
501         int len;
502
503         count = sftp_pkt_getuint32(pktin);
504         if (count != 1) {
505             fxp_internal_error("REALPATH returned name count != 1\n");
506             sftp_pkt_free(pktin);
507             return NULL;
508         }
509         sftp_pkt_getstring(pktin, &path, &len);
510         if (!path) {
511             fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
512             sftp_pkt_free(pktin);
513             return NULL;
514         }
515         path = mkstr(path, len);
516         sftp_pkt_free(pktin);
517         return path;
518     } else {
519         fxp_got_status(pktin);
520         sftp_pkt_free(pktin);
521         return NULL;
522     }
523 }
524
525 /*
526  * Open a file.
527  */
528 struct sftp_request *fxp_open_send(char *path, int type)
529 {
530     struct sftp_request *req = sftp_alloc_request();
531     struct sftp_packet *pktout;
532
533     pktout = sftp_pkt_init(SSH_FXP_OPEN);
534     sftp_pkt_adduint32(pktout, req->id);
535     sftp_pkt_addstring(pktout, path);
536     sftp_pkt_adduint32(pktout, type);
537     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
538     sftp_send(pktout);
539
540     return req;
541 }
542
543 struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
544                                  struct sftp_request *req)
545 {
546     sfree(req);
547
548     if (pktin->type == SSH_FXP_HANDLE) {
549         char *hstring;
550         struct fxp_handle *handle;
551         int len;
552
553         sftp_pkt_getstring(pktin, &hstring, &len);
554         if (!hstring) {
555             fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
556             sftp_pkt_free(pktin);
557             return NULL;
558         }
559         handle = snew(struct fxp_handle);
560         handle->hstring = mkstr(hstring, len);
561         handle->hlen = len;
562         sftp_pkt_free(pktin);
563         return handle;
564     } else {
565         fxp_got_status(pktin);
566         sftp_pkt_free(pktin);
567         return NULL;
568     }
569 }
570
571 /*
572  * Open a directory.
573  */
574 struct sftp_request *fxp_opendir_send(char *path)
575 {
576     struct sftp_request *req = sftp_alloc_request();
577     struct sftp_packet *pktout;
578
579     pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
580     sftp_pkt_adduint32(pktout, req->id);
581     sftp_pkt_addstring(pktout, path);
582     sftp_send(pktout);
583
584     return req;
585 }
586
587 struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
588                                     struct sftp_request *req)
589 {
590     sfree(req);
591     if (pktin->type == SSH_FXP_HANDLE) {
592         char *hstring;
593         struct fxp_handle *handle;
594         int len;
595
596         sftp_pkt_getstring(pktin, &hstring, &len);
597         if (!hstring) {
598             fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
599             sftp_pkt_free(pktin);
600             return NULL;
601         }
602         handle = snew(struct fxp_handle);
603         handle->hstring = mkstr(hstring, len);
604         handle->hlen = len;
605         sftp_pkt_free(pktin);
606         return handle;
607     } else {
608         fxp_got_status(pktin);
609         sftp_pkt_free(pktin);
610         return NULL;
611     }
612 }
613
614 /*
615  * Close a file/dir.
616  */
617 struct sftp_request *fxp_close_send(struct fxp_handle *handle)
618 {
619     struct sftp_request *req = sftp_alloc_request();
620     struct sftp_packet *pktout;
621
622     pktout = sftp_pkt_init(SSH_FXP_CLOSE);
623     sftp_pkt_adduint32(pktout, req->id);
624     sftp_pkt_addstring_start(pktout);
625     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
626     sftp_send(pktout);
627
628     sfree(handle->hstring);
629     sfree(handle);
630
631     return req;
632 }
633
634 void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)
635 {
636     sfree(req);
637     fxp_got_status(pktin);
638     sftp_pkt_free(pktin);
639 }
640
641 struct sftp_request *fxp_mkdir_send(char *path)
642 {
643     struct sftp_request *req = sftp_alloc_request();
644     struct sftp_packet *pktout;
645
646     pktout = sftp_pkt_init(SSH_FXP_MKDIR);
647     sftp_pkt_adduint32(pktout, req->id);
648     sftp_pkt_addstring(pktout, path);
649     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
650     sftp_send(pktout);
651
652     return req;
653 }
654
655 int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
656 {
657     int id;
658     sfree(req);
659     id = fxp_got_status(pktin);
660     sftp_pkt_free(pktin);
661     if (id != 1) {
662         return 0;
663     }
664     return 1;
665 }
666
667 struct sftp_request *fxp_rmdir_send(char *path)
668 {
669     struct sftp_request *req = sftp_alloc_request();
670     struct sftp_packet *pktout;
671
672     pktout = sftp_pkt_init(SSH_FXP_RMDIR);
673     sftp_pkt_adduint32(pktout, req->id);
674     sftp_pkt_addstring(pktout, path);
675     sftp_send(pktout);
676
677     return req;
678 }
679
680 int fxp_rmdir_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_remove_send(char *fname)
693 {
694     struct sftp_request *req = sftp_alloc_request();
695     struct sftp_packet *pktout;
696
697     pktout = sftp_pkt_init(SSH_FXP_REMOVE);
698     sftp_pkt_adduint32(pktout, req->id);
699     sftp_pkt_addstring(pktout, fname);
700     sftp_send(pktout);
701
702     return req;
703 }
704
705 int fxp_remove_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_rename_send(char *srcfname, char *dstfname)
718 {
719     struct sftp_request *req = sftp_alloc_request();
720     struct sftp_packet *pktout;
721
722     pktout = sftp_pkt_init(SSH_FXP_RENAME);
723     sftp_pkt_adduint32(pktout, req->id);
724     sftp_pkt_addstring(pktout, srcfname);
725     sftp_pkt_addstring(pktout, dstfname);
726     sftp_send(pktout);
727
728     return req;
729 }
730
731 int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req)
732 {
733     int id;
734     sfree(req);
735     id = fxp_got_status(pktin);
736     sftp_pkt_free(pktin);
737     if (id != 1) {
738         return 0;
739     }
740     return 1;
741 }
742
743 /*
744  * Retrieve the attributes of a file. We have fxp_stat which works
745  * on filenames, and fxp_fstat which works on open file handles.
746  */
747 struct sftp_request *fxp_stat_send(char *fname)
748 {
749     struct sftp_request *req = sftp_alloc_request();
750     struct sftp_packet *pktout;
751
752     pktout = sftp_pkt_init(SSH_FXP_STAT);
753     sftp_pkt_adduint32(pktout, req->id);
754     sftp_pkt_addstring(pktout, fname);
755     sftp_send(pktout);
756
757     return req;
758 }
759
760 int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
761                   struct fxp_attrs *attrs)
762 {
763     sfree(req);
764     if (pktin->type == SSH_FXP_ATTRS) {
765         *attrs = sftp_pkt_getattrs(pktin);
766         sftp_pkt_free(pktin);
767         return 1;
768     } else {
769         fxp_got_status(pktin);
770         sftp_pkt_free(pktin);
771         return 0;
772     }
773 }
774
775 struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
776 {
777     struct sftp_request *req = sftp_alloc_request();
778     struct sftp_packet *pktout;
779
780     pktout = sftp_pkt_init(SSH_FXP_FSTAT);
781     sftp_pkt_adduint32(pktout, req->id);
782     sftp_pkt_addstring_start(pktout);
783     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
784     sftp_send(pktout);
785
786     return req;
787 }
788
789 int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
790                    struct fxp_attrs *attrs)
791 {
792     sfree(req);
793     if (pktin->type == SSH_FXP_ATTRS) {
794         *attrs = sftp_pkt_getattrs(pktin);
795         sftp_pkt_free(pktin);
796         return 1;
797     } else {
798         fxp_got_status(pktin);
799         sftp_pkt_free(pktin);
800         return 0;
801     }
802 }
803
804 /*
805  * Set the attributes of a file.
806  */
807 struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
808 {
809     struct sftp_request *req = sftp_alloc_request();
810     struct sftp_packet *pktout;
811
812     pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
813     sftp_pkt_adduint32(pktout, req->id);
814     sftp_pkt_addstring(pktout, fname);
815     sftp_pkt_addattrs(pktout, attrs);
816     sftp_send(pktout);
817
818     return req;
819 }
820
821 int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
822 {
823     int id;
824     sfree(req);
825     id = fxp_got_status(pktin);
826     sftp_pkt_free(pktin);
827     if (id != 1) {
828         return 0;
829     }
830     return 1;
831 }
832
833 struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
834                                        struct fxp_attrs attrs)
835 {
836     struct sftp_request *req = sftp_alloc_request();
837     struct sftp_packet *pktout;
838
839     pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
840     sftp_pkt_adduint32(pktout, req->id);
841     sftp_pkt_addstring_start(pktout);
842     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
843     sftp_pkt_addattrs(pktout, attrs);
844     sftp_send(pktout);
845
846     return req;
847 }
848
849 int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
850 {
851     int id;
852     sfree(req);
853     id = fxp_got_status(pktin);
854     sftp_pkt_free(pktin);
855     if (id != 1) {
856         return 0;
857     }
858     return 1;
859 }
860
861 /*
862  * Read from a file. Returns the number of bytes read, or -1 on an
863  * error, or possibly 0 if EOF. (I'm not entirely sure whether it
864  * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
865  * error indicator. It might even depend on the SFTP server.)
866  */
867 struct sftp_request *fxp_read_send(struct fxp_handle *handle,
868                                    uint64 offset, int len)
869 {
870     struct sftp_request *req = sftp_alloc_request();
871     struct sftp_packet *pktout;
872
873     pktout = sftp_pkt_init(SSH_FXP_READ);
874     sftp_pkt_adduint32(pktout, req->id);
875     sftp_pkt_addstring_start(pktout);
876     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
877     sftp_pkt_adduint64(pktout, offset);
878     sftp_pkt_adduint32(pktout, len);
879     sftp_send(pktout);
880
881     return req;
882 }
883
884 int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
885                   char *buffer, int len)
886 {
887     sfree(req);
888     if (pktin->type == SSH_FXP_DATA) {
889         char *str;
890         int rlen;
891
892         sftp_pkt_getstring(pktin, &str, &rlen);
893
894         if (rlen > len || rlen < 0) {
895             fxp_internal_error("READ returned more bytes than requested");
896             sftp_pkt_free(pktin);
897             return -1;
898         }
899
900         memcpy(buffer, str, rlen);
901         sftp_pkt_free(pktin);
902         return rlen;
903     } else {
904         fxp_got_status(pktin);
905         sftp_pkt_free(pktin);
906         return -1;
907     }
908 }
909
910 /*
911  * Read from a directory.
912  */
913 struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
914 {
915     struct sftp_request *req = sftp_alloc_request();
916     struct sftp_packet *pktout;
917
918     pktout = sftp_pkt_init(SSH_FXP_READDIR);
919     sftp_pkt_adduint32(pktout, req->id);
920     sftp_pkt_addstring_start(pktout);
921     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
922     sftp_send(pktout);
923
924     return req;
925 }
926
927 struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
928                                    struct sftp_request *req)
929 {
930     sfree(req);
931     if (pktin->type == SSH_FXP_NAME) {
932         struct fxp_names *ret;
933         int i;
934         ret = snew(struct fxp_names);
935         ret->nnames = sftp_pkt_getuint32(pktin);
936         ret->names = snewn(ret->nnames, struct fxp_name);
937         for (i = 0; i < ret->nnames; i++) {
938             char *str;
939             int len;
940             sftp_pkt_getstring(pktin, &str, &len);
941             ret->names[i].filename = mkstr(str, len);
942             sftp_pkt_getstring(pktin, &str, &len);
943             ret->names[i].longname = mkstr(str, len);
944             ret->names[i].attrs = sftp_pkt_getattrs(pktin);
945         }
946         sftp_pkt_free(pktin);
947         return ret;
948     } else {
949         fxp_got_status(pktin);
950         sftp_pkt_free(pktin);
951         return NULL;
952     }
953 }
954
955 /*
956  * Write to a file. Returns 0 on error, 1 on OK.
957  */
958 struct sftp_request *fxp_write_send(struct fxp_handle *handle,
959                                     char *buffer, uint64 offset, int len)
960 {
961     struct sftp_request *req = sftp_alloc_request();
962     struct sftp_packet *pktout;
963
964     pktout = sftp_pkt_init(SSH_FXP_WRITE);
965     sftp_pkt_adduint32(pktout, req->id);
966     sftp_pkt_addstring_start(pktout);
967     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
968     sftp_pkt_adduint64(pktout, offset);
969     sftp_pkt_addstring_start(pktout);
970     sftp_pkt_addstring_data(pktout, buffer, len);
971     sftp_send(pktout);
972
973     return req;
974 }
975
976 int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
977 {
978     sfree(req);
979     fxp_got_status(pktin);
980     sftp_pkt_free(pktin);
981     return fxp_errtype == SSH_FX_OK;
982 }
983
984 /*
985  * Free up an fxp_names structure.
986  */
987 void fxp_free_names(struct fxp_names *names)
988 {
989     int i;
990
991     for (i = 0; i < names->nnames; i++) {
992         sfree(names->names[i].filename);
993         sfree(names->names[i].longname);
994     }
995     sfree(names->names);
996     sfree(names);
997 }
998
999 /*
1000  * Duplicate an fxp_name structure.
1001  */
1002 struct fxp_name *fxp_dup_name(struct fxp_name *name)
1003 {
1004     struct fxp_name *ret;
1005     ret = snew(struct fxp_name);
1006     ret->filename = dupstr(name->filename);
1007     ret->longname = dupstr(name->longname);
1008     ret->attrs = name->attrs;          /* structure copy */
1009     return ret;
1010 }
1011
1012 /*
1013  * Free up an fxp_name structure.
1014  */
1015 void fxp_free_name(struct fxp_name *name)
1016 {
1017     sfree(name->filename);
1018     sfree(name->longname);
1019     sfree(name);
1020 }