2 Syren -- a lightweight downloader for Linux/BSD/Win/MacOSX
3 inspired by Axel Copyright 2001-2002 Wilmer van der Gaast
4 version 0.0.6 (atomic alien)
5 coded by Ketmar // Avalon Group
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License with
18 the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL;
19 if not, write to the Free Software Foundation, Inc., 59 Temple Place,
20 Suite 330, Boston, MA 02111-1307 USA
25 #ifndef _SYREN_DLOADER_C
26 #define _SYREN_DLOADER_C
29 #include "syren_dloader.h"
32 /* old algo seems to be faster on fast networks */
33 #define SYDL_USE_OLD_ALGO
36 static void SyClearURL (TSyURL
**url
) {
37 if (!url
|| !(*url
)) return;
38 SyURLClear(*url
); free(*url
); *url
= NULL
;
42 static void SyClearStr (char **s
) {
43 if (!s
|| !(*s
)) return;
48 static void SyClearInternal (TSyState
*state
, int keepSizesAndPData
) {
50 SyTCPCloseSocket(&state
->datafd
); SyTCPCloseSocket(&state
->fd
);
51 SyClearURL(&state
->url
); SyClearURL(&state
->httpproxy
); SyClearURL(&state
->ftpproxy
);
52 SyClearStr(&state
->iface
); SyClearStr(&state
->httpFName
);
53 SyClearStr(&state
->userAgent
);
54 state
->status
= SY_STATUS_UNPREPARED
;
55 state
->breakNow
= state
->interrupted
= SY_FALSE
;
56 if (!keepSizesAndPData
) {
57 SyClearStr(&state
->postData
);
58 state
->fileSize
= state
->lastByte
= -1;
59 state
->firstByte
= state
->currentByte
= 0;
65 void SyInitStats (TSyState
*state
, TSyStats
*si
) {
68 si
->interval
= 30; /* seconds */
74 void SyUpdateStats (TSyState
*state
, TSyStats
*si
, int64_t bytesDone
, int64_t bytesTotal
) {
76 double ctime
, ptime
, t
;
77 double w0
, w1
; /* weights */
93 i1
->bytesDone
= bytesDone
;
101 /* update "bytes done" */
102 i1
->bytesDone
= bytesDone
-(i1
->bytesFrom
);
104 ptime
= ctime
-(i1
->time
);
105 if (ptime
>= si
->interval
) {
106 /* interval passed, move state */
108 i1
->bytesFrom
= bytesDone
;
111 i0
->time
= ptime
; /* i0->time: time, taken by this stat */
114 /* calculate weights */
115 t
= ctime
-(i1
->time
);
116 w1
= i0
->active
?t
/si
->interval
:1.0;
117 if (w1
> 1.0) w1
= 1.0;
118 if (t
< 0.5) w1
= 0.0;
122 bps0
= i0
->active
?(double)(i0
->bytesDone
)/i0
->time
:0.0;
123 bps1
= t
>=0.5?(double)(i1
->bytesDone
)/t
:0.0;
124 si
->bps
= (bps0
*w0
)+(bps1
*w1
);
127 if (bytesTotal
> 0 && si
->bps
>= 1) {
128 si
->eta
= (double)(bytesTotal
-bytesDone
)/(si
->bps
);
129 } else si
->eta
= 0.0;
134 TSyState
*SyNew (void) {
135 TSyState
*state
= calloc(1, sizeof(TSyState
));
137 if (SyTCPInitSocket(&state
->fd
) != SY_OK
|| SyTCPInitSocket(&state
->datafd
) != SY_OK
) {
141 state
->maxBufferSize
= 1024*1024; /* 1mb */
142 state
->ioTimeout
= 60;
143 state
->maxRedirects
= 6;
144 SyClearInternal(state
, 0);
150 void SyFree (TSyState
*state
) {
152 SyClear(state
); free(state
);
156 void SyClear (TSyState
*state
) {
158 SyClearInternal(state
, 0);
163 TSyResult
SyPrepare (TSyState
*state
, const char *urlstr
, const char *httpproxyurl
, const char *ftpproxyurl
,
165 if (!state
) return SY_ERROR
;
166 if (state
->status
!= SY_STATUS_UNPREPARED
) return SY_ERROR
;
167 SyInitStats(state
, &state
->stats
);
168 SyClearInternal(state
, 1); /* keep sizes and positions */
169 state
->url
= SyURLNew(); if (!state
->url
) return SY_ERROR
;
171 state
->iface
= SyStrDup(iface
);
172 if (!state
->iface
) return SY_ERROR
;
173 SyStrTrim(state
->iface
);
175 if (SyURLParse(state
->url
, urlstr
) != SY_OK
) {
176 SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid URL");
179 if (state
->url
->proto
== SY_PROTO_UNKNOWN
) {
180 SyClearInternal(state
, 1);
181 SyMessage(state
->pfn
, SY_MSG_ERROR
, "unknown protocol");
184 #ifndef SYOPT_ALLOW_HTTPS
185 if (state
->url
->proto
== SY_PROTO_HTTPS
) {
186 SyClearInternal(state
, 1);
187 SyMessage(state
->pfn
, SY_MSG_ERROR
, "HTTPS not supported");
191 if (httpproxyurl
&& *httpproxyurl
) {
192 state
->httpproxy
= SyURLNew(); if (!state
->httpproxy
) return SY_ERROR
;
193 if (SyURLParse(state
->httpproxy
, httpproxyurl
) != SY_OK
) {
194 SyClearInternal(state
, 1);
195 SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid HTTP proxy URL");
199 if (ftpproxyurl
&& *ftpproxyurl
) {
200 state
->ftpproxy
= SyURLNew(); if (!state
->ftpproxy
) return SY_ERROR
;
201 if (SyURLParse(state
->ftpproxy
, ftpproxyurl
) != SY_OK
) {
202 SyClearInternal(state
, 1);
203 SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid FTP proxy URL");
207 state
->status
= SY_STATUS_PREPARED
;
212 TSyResult
SyEnd (TSyState
*state
) {
218 #define SY_CHECK_BREAK0 if (state->breakNow != SY_FALSE) { state->interrupted = SY_TRUE; break; }
221 TSyResult
SyBegin (TSyState
*state
) {
222 TSyResult res
= SY_ERROR
;
223 TSyProxy
*proxy
= NULL
;
224 TSyHdrs
*hdrs
; TSyURL
*url
, *purl
, *lurl
;
225 char *s
, *t
, *buf
, tmp
[32];
228 if (!state
|| (state
->status
!= SY_STATUS_PREPARED
&& state
->status
!= SY_STATUS_DOWNLOADING
)) return SY_ERROR
;
229 rdcnt
= state
->maxRedirects
;
230 state
->breakNow
= state
->interrupted
= state
->error
= SY_FALSE
; state
->currentByte
= 0;
231 if (state
->firstByte
< 0) state
->firstByte
= 0;
232 if (state
->lastByte
>= 0 && state
->firstByte
> state
->lastByte
) return SY_ERROR
;
233 hdrs
= SyHdrNew(); if (!hdrs
) return SY_ERROR
;
235 SyProxyFree(proxy
); proxy
= NULL
;
236 SyClearStr(&state
->httpFName
);
237 SyTCPCloseSocket(&state
->datafd
); SyTCPCloseSocket(&state
->fd
);
238 state
->status
= SY_STATUS_CONNECTING
;
241 SyMessage(state
->pfn
, SY_MSG_MSG
, "downloading: %s://%s:%i%s%s%s%s",
242 url
->protostr
, url
->host
, url
->port
, url
->dir
, url
->file
, url
->query
, url
->anchor
);
243 /* check for proxy */
244 if (url
->proto
== SY_PROTO_FTP
&& state
->ftpproxy
) purl
= state
->ftpproxy
;
245 #ifdef SYOPT_ALLOW_HTTPS
246 else if (url
->proto
== SY_PROTO_HTTP
&& state
->httpproxy
) purl
= state
->httpproxy
;
247 else if (url
->proto
== SY_PROTO_HTTPS
&& state
->httpproxy
) purl
= state
->httpproxy
;
249 else if (url
->proto
== SY_PROTO_HTTP
&& state
->httpproxy
) purl
= state
->httpproxy
;
253 if (SyTCPConnect(&state
->fd
, purl
->host
, purl
->port
, state
->iface
, state
->ioTimeout
, state
->pfn
) != SY_OK
) break;
256 state
->status
= SY_STATUS_HANDSHAKING
;
257 if (url
->proto
== SY_PROTO_FTP
&& (!state
->ftpproxy
|| state
->ftpUseConnect
)) {
259 if (state
->ftpUseConnect
&& state
->ftpproxy
) {
260 proxy
= SyProxyNew(state
->ftpproxy
, SY_PROXY_HTTP_CONNECT
, state
->userAgent
);
262 if (SyProxyConnect(&state
->fd
, proxy
, url
->host
, url
->port
, state
->pfn
) != SY_OK
) break;
265 if (SyFTPStart(&state
->fd
, url
->user
, url
->pass
, state
->pfn
) != SY_OK
) break;
267 if (SyFTPCwd(&state
->fd
, url
->dir
, state
->pfn
) != SY_OK
) break;
269 state
->fileSize
= SyFTPGetSize(&state
->fd
, state
->iface
, url
->file
, rdcnt
, state
->ioTimeout
, state
->pfn
, proxy
);
270 if (state
->firstByte
> state
->fileSize
) break;
272 if (state
->firstByte
> 0) {
274 if (SyLong2Str(tmp
, state
->firstByte
) != SY_OK
) break;
275 if (SyTCPSendLine(state
->pfn
, 1, &state
->fd
, "REST %s", tmp
) != SY_OK
) break;
277 /*if (SyFTPWaitFor2(&state->fd, 3, 2, state->pfn) <= 0) break;*/
278 if ((s
= SyFTPWait(&code
, &state
->fd
, state
->pfn
)) == NULL
) break;
279 if (code
<= 0) break;
281 if (code
!= 3 && code
!= 2) {
282 SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't resume");
283 if (state
->fnnoresume
&& state
->fnnoresume(state
) != SY_OK
) break;
284 state
->firstByte
= 0;
288 if (SyFTPOpenDataPassv(&state
->fd
, &state
->datafd
, state
->iface
, state
->ioTimeout
, state
->pfn
, proxy
) != SY_OK
) break;
290 if (SyTCPSendLine(state
->pfn
, 1, &state
->fd
, "RETR %s", url
->file
) != SY_OK
) break;
292 if (SyFTPWaitFor2(&state
->fd
, 1, -1, state
->pfn
) <= 0) break;
297 int postLen
= strlen(state
->postData
?state
->postData
:"");
298 #ifdef SYOPT_ALLOW_HTTPS
299 if (url
->proto
== SY_PROTO_HTTPS
&& state
->httpproxy
) {
300 proxy
= SyProxyNew(state
->ftpproxy
, SY_PROXY_HTTP_CONNECT
, state
->userAgent
);
302 if (SyProxyConnect(&state
->fd
, proxy
, url
->host
, url
->port
, state
->pfn
) != SY_OK
) break;
305 if (url
->proto
== SY_PROTO_HTTPS
) {
306 if (SyTCPInitSSL(&state
->fd
, state
->ioTimeout
) != SY_OK
) break;
308 if (SyHTTPBuildQuery(hdrs
, postLen
?"POST":"GET", url
,
309 (url
->proto
==SY_PROTO_FTP
)?state
->ftpproxy
:
310 (url
->proto
==SY_PROTO_HTTP
)?state
->httpproxy
:NULL
) != SY_OK
) {
311 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
315 if (SyHTTPBuildQuery(hdrs
, postLen
?"POST":"GET", url
,
316 (url
->proto
==SY_PROTO_FTP
)?state
->ftpproxy
:state
->httpproxy
) != SY_OK
) {
317 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
321 if (state
->userAgent
&& state
->userAgent
[0]) {
322 s
= SySPrintf("User-Agent: %s", state
->userAgent
);
324 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers (out of memory)");
327 if (SyHdrAddLine(hdrs
, s
) != SY_OK
) {
329 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
335 if (SyHdrAddLine(hdrs
, "Content-Type: application/x-www-form-urlencoded") != SY_OK
) {
336 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
339 s
= SySPrintf("Content-Length: %i", postLen
); /* FIXME: change when we'll be able to post files */
340 if (!s
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
341 if (SyHdrAddLine(hdrs
, s
) != SY_OK
) {
343 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
348 if (state
->firstByte
> 0) {
349 if (SyHTTPAddRange(hdrs
, state
->firstByte
, -1) != SY_OK
) {
350 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
354 if (state
->fnprephdrs
&& state
->fnprephdrs(state
, hdrs
) != SY_OK
) break;
355 if (SyHTTPSendQuery(&state
->fd
, hdrs
, state
->pfn
) != SY_OK
) break;
357 /* send post data, if any */
359 SyMessage(state
->pfn
, SY_MSG_NOTICE
, " sending %i bytes of POST data", postLen
);
360 if (SyTCPSend(&state
->fd
, state
->postData
, postLen
) != SY_OK
) {
361 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't send POST data");
365 /* read reply headers */
366 if (SyHTTPReadHeaders(hdrs
, &state
->fd
, state
->pfn
) != SY_OK
) break;
367 if (state
->fngothdrs
&& state
->fngothdrs(state
, hdrs
) != SY_OK
) break;
369 code
= hdrs
->code
/100;
372 state
->status
= SY_STATUS_REDIRECTING
;
373 SyTCPCloseSocket(&state
->fd
);
374 if (--rdcnt
< 1) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "too many redirects"); break; }
375 s
= SyHdrGetFieldValue(hdrs
, "Location");
376 if (!s
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "redirect to nowhere"); break; }
377 if (!s
[0]) { free(s
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid redirect"); break; }
378 /* check for 'news.php' or 'news.php?dir=/abc' or 'news.php?n=1#/abc */
380 if (!strstr(s
, "://")) {
382 t
= SySPrintf("/%s", s
); free(s
);
383 if (!t
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
387 lurl
= SyURLNew(); if (!lurl
) { free(s
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
388 if (SyURLParse(lurl
, s
) != SY_OK
) {
389 free(s
); SyClearURL(&lurl
);
390 SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid redirect");
396 if (strcmp(lurl
->dir
, "/")) {
397 /* has other dir parts */
398 s
= SySPrintf("%s%s", url
->dir
, (lurl
->dir
)+1);
399 if (!s
) { SyClearURL(&lurl
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
400 SyClearStr(&lurl
->dir
); lurl
->dir
= s
;
403 free(url
->dir
); url
->dir
= lurl
->dir
;
405 SyClearStr(&url
->protostr
); SyClearStr(&url
->host
);
406 SyClearStr(&url
->user
); SyClearStr(&url
->pass
);
407 url
->port
= lurl
->port
;
408 url
->defaultPort
= lurl
->defaultPort
;
409 url
->proto
= lurl
->proto
;
410 url
->protostr
= lurl
->protostr
;
411 url
->host
= lurl
->host
;
412 url
->user
= lurl
->user
;
413 url
->pass
= lurl
->pass
;
414 lurl
->protostr
= lurl
->host
= lurl
->user
= lurl
->pass
= NULL
;
416 SyClearStr(&lurl
->protostr
); SyClearStr(&lurl
->host
);
417 SyClearStr(&lurl
->user
); SyClearStr(&lurl
->pass
);
419 SyClearStr(&url
->file
); url
->file
= lurl
->file
;
420 SyClearStr(&url
->query
); url
->query
= lurl
->query
;
421 SyClearStr(&url
->anchor
); url
->anchor
= lurl
->anchor
;
422 free(lurl
); /* DO NOT CLEAR lurl! */
426 if (code
!= 2) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "%s", hdrs
->firstLine
); break; }
427 if (state
->firstByte
&& hdrs
->code
!= 206) {
428 SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't resume");
429 if (state
->fnnoresume
&& state
->fnnoresume(state
) != SY_OK
) break;
430 state
->firstByte
= 0;
432 state
->fileSize
= SyHTTPGetSize(hdrs
);
433 if (state
->fileSize
>= 0) {
434 state
->fileSize
+= state
->firstByte
;
435 if (state
->firstByte
> state
->fileSize
) break;
437 if ((s
= SyHdrGetFieldValue(hdrs
, "Content-Disposition")) != NULL
) {
438 /* extract file name, if any */
441 t
= strcasestr(s
, "filename");
443 t
+= 8; while (*t
&& *t
<= ' ') t
++;
444 if (*t
!= '=') { s
= t
; continue; }
445 t
++; while (*t
&& *t
<= ' ') t
++;
447 if (*t
== '"') { t
++; s
= t
; while (*s
&& *s
!= '"') s
++; }
448 else { s
= t
; while (*s
&& (*s
!= ' ' && *s
!= ';')) s
++; }
450 state
->httpFName
= SyStrNew(t
, -1);
451 SyStrTrim(state
->httpFName
);
452 if (!state
->httpFName
[0]) SyClearStr(&state
->httpFName
);
464 if (state
->lastByte
< 0 && state
->fileSize
>= 0) state
->lastByte
= state
->fileSize
;
465 if ((state
->lastByte
>= 0 && state
->firstByte
> state
->lastByte
) ||
466 (state
->fileSize
>= 0 && state
->lastByte
> state
->fileSize
)) {
467 SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid file range");
471 if (res
== SY_OK
) state
->status
= SY_STATUS_CONNECTED
;
472 SyInitStats(state
, &state
->stats
);
477 TSyResult
SyRun (TSyState
*state
) {
478 TSyResult res
= SY_ERROR
;
479 char *buf
, *bufPtr
; int bufSize
, bufLeft
;
480 int64_t total
, done
, left
;
481 double lastprogtime
, tm
, ctime
;
484 if (!state
|| state
->status
!= SY_STATUS_CONNECTED
) return SY_ERROR
;
485 state
->currentByte
= 0; state
->error
= SY_FALSE
;
486 if (state
->fileSize
>= 0) {
487 if ((state
->lastByte
>= 0 && state
->lastByte
== state
->firstByte
) || state
->firstByte
== state
->fileSize
) {
488 state
->status
= SY_STATUS_COMPLETE
;
492 if (!state
->fnopen
) {
493 /* no 'open' function, assume simple check */
494 state
->status
= SY_STATUS_COMPLETE
;
498 bufSize
= state
->initBufferSize
; if (bufSize
< 1) bufSize
= 1;
499 buf
= malloc(bufSize
);
501 SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error");
502 state
->error
= SY_TRUE
;
506 if (state
->fnopen(state
) != SY_OK
) {
508 state
->error
= SY_TRUE
;
512 total
= state
->fileSize
;
514 total
-= state
->firstByte
;
515 if (state
->lastByte
> 0) total
-= (state
->fileSize
-state
->lastByte
);
517 left
= total
; done
= 0;
518 SyInitStats(state
, &state
->stats
);
519 SyUpdateStats(state
, &state
->stats
, 0, total
);
520 SyMessage(state
->pfn
, SY_MSG_MSG
, "starting data transfer");
521 if (state
->fnprog
) state
->fnprog(state
, 0, total
, SY_FALSE
);
522 lastprogtime
= SyGetTimeD();
523 rsock
= (state
->datafd
.fd
>=0)?&state
->datafd
:&state
->fd
;
524 /* start downloading */
525 state
->status
= SY_STATUS_DOWNLOADING
;
526 while (left
< 0 || left
> 0) {
529 /* how many bytes we should receive? */
531 if (left
>= (int64_t)0) {
532 flg
= (int64_t)tord
> left
;
533 if (flg
) tord
= (int)left
;
535 ctime
= tm
= SyGetTimeD();
536 #ifdef SYDL_USE_OLD_ALGO
537 /*rd = SyTCPReceiveEx(rsock, buf, tord, SY_TCP_DONT_ALLOWPARTIAL);
538 bufLeft = bufSize-(rd>0?rd:0);*/
539 bufPtr
= buf
; bufLeft
= tord
; rd
= 0;
541 int xrd
= SyTCPReceiveEx(rsock
, bufPtr
, bufLeft
, SY_TCP_ALLOWPARTIAL
);
542 ctime
= SyGetTimeD();
544 if (xrd
< 0) { rd
= xrd
; break; }
545 rd
+= xrd
; bufPtr
+= xrd
; bufLeft
-= xrd
;
546 } while (!bufLeft
|| rd
>= bufSize
/3);
548 bufPtr
= buf
; bufLeft
= tord
; rd
= 0;
550 int xrd
= SyTCPReceiveEx(rsock
, bufPtr
, bufLeft
, SY_TCP_ALLOWPARTIAL
);
551 ctime
= SyGetTimeD();
553 if (xrd
< 0) { rd
= xrd
; break; }
554 rd
+= xrd
; bufPtr
+= xrd
; bufLeft
-= xrd
;
555 } while (!bufLeft
|| ctime
-tm
>= 4.0);
558 /* write everything %-) */
559 if (state
->fnwrite
&& state
->fnwrite(state
, buf
, rd
) != SY_OK
) break;
560 if (left
> 0) left
-= rd
; done
+= rd
;
561 state
->currentByte
= done
;
563 SyUpdateStats(state
, &state
->stats
, done
, total
);
564 /* and draw progress */
565 if (tm
-lastprogtime
>= 1.0) {
566 if (state
->fnprog
) state
->fnprog(state
, done
, total
, SY_FALSE
);
567 lastprogtime
= ctime
;
569 /* grow buffer if necessary */
570 if (!bufLeft
&& ctime
-tm
< 2.0 && bufSize
< state
->maxBufferSize
) {
572 if (bufLeft
> state
->maxBufferSize
) bufLeft
= state
->maxBufferSize
;
573 bufPtr
= realloc(buf
, bufLeft
);
575 /*fprintf(stderr, "\nbuffer grows from %i to %i\n", bufSize, bufLeft);*/
576 bufSize
= bufLeft
; buf
= bufPtr
;
579 /* shrink buffer if necessary */
580 /*if (bufLeft && ctime-tm > 2.0 && bufSize > 8192) {
586 /* read error or connection closed */
587 if (rd
>= 0 && left
< 0) res
= SY_OK
; /* size unknown? all done */
588 else if (state
->breakNow
== SY_TRUE
) state
->interrupted
= SY_TRUE
;
592 /* check if downloading ok */
593 if (!left
) res
= SY_OK
;
594 state
->error
= (res
==SY_OK
)?SY_FALSE
:SY_TRUE
;
595 SyUpdateStats(state
, &state
->stats
, done
, total
);
596 if (state
->fnprog
) state
->fnprog(state
, done
, total
, SY_TRUE
);
597 if (state
->fnclose
&& state
->fnclose(state
) != SY_OK
) res
= SY_ERROR
; /* don't set state->error here! */
598 /* free memory, close sockets */
600 SyTCPCloseSocket(&state
->datafd
); SyTCPCloseSocket(&state
->fd
);
601 if (res
== SY_OK
) state
->status
= SY_STATUS_COMPLETE
;