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