OSDN Git Service

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