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