OSDN Git Service

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