2 Syren -- a lightweight downloader for Linux/BSD/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 3 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
;
252 state
->fd
.errCode
= 0;
253 if (url
->proto
== SY_PROTO_FTP
&& (!state
->ftpproxy
|| state
->ftpUseConnect
)) {
256 if (SyTCPConnect(&state
->fd
, purl
->host
, purl
->port
, state
->iface
, state
->ioTimeout
, state
->pfn
) != SY_OK
) break;
259 state
->status
= SY_STATUS_HANDSHAKING
;
260 if (state
->ftpUseConnect
&& state
->ftpproxy
) {
261 proxy
= SyProxyNew(state
->ftpproxy
, SY_PROXY_HTTP_CONNECT
, state
->userAgent
);
263 if (SyProxyConnect(&state
->fd
, proxy
, url
->host
, url
->port
, state
->pfn
) != SY_OK
) break;
266 if (SyFTPStart(&state
->fd
, url
->user
, url
->pass
, state
->pfn
) != SY_OK
) break;
268 if (SyFTPCwd(&state
->fd
, url
->dir
, state
->pfn
) != SY_OK
) break;
270 state
->fileSize
= SyFTPGetSize(&state
->fd
, state
->iface
, url
->file
, rdcnt
, state
->ioTimeout
, state
->pfn
, proxy
);
271 if (state
->firstByte
> state
->fileSize
) break;
273 if (state
->firstByte
> 0) {
275 if (SyLong2Str(tmp
, state
->firstByte
) != SY_OK
) break;
276 if (SyTCPSendLine(state
->pfn
, 1, &state
->fd
, "REST %s", tmp
) != SY_OK
) break;
278 /*if (SyFTPWaitFor2(&state->fd, 3, 2, state->pfn) <= 0) break;*/
279 if ((s
= SyFTPWait(&code
, &state
->fd
, state
->pfn
)) == NULL
) break;
280 if (code
<= 0) break;
282 if (code
!= 3 && code
!= 2) {
283 SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't resume");
284 if (state
->fnnoresume
&& state
->fnnoresume(state
) != SY_OK
) break;
285 state
->firstByte
= 0;
289 if (SyFTPOpenDataPassv(&state
->fd
, &state
->datafd
, state
->iface
, state
->ioTimeout
, state
->pfn
, proxy
) != SY_OK
) break;
291 if (SyTCPSendLine(state
->pfn
, 1, &state
->fd
, "RETR %s", url
->file
) != SY_OK
) break;
293 if (SyFTPWaitFor2(&state
->fd
, 1, -1, state
->pfn
) <= 0) break;
298 int postLen
= strlen(state
->postData
?state
->postData
:"");
299 #ifdef SYOPT_ALLOW_HTTPS
300 if (SyHTTPBuildQuery(hdrs
, postLen
?"POST":"GET", url
,
301 (url
->proto
==SY_PROTO_FTP
)?state
->ftpproxy
:
302 (url
->proto
==SY_PROTO_HTTP
)?state
->httpproxy
:NULL
) != SY_OK
) {
303 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
307 if (SyHTTPBuildQuery(hdrs
, postLen
?"POST":"GET", url
,
308 (url
->proto
==SY_PROTO_FTP
)?state
->ftpproxy
:state
->httpproxy
) != SY_OK
) {
309 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
313 if (state
->userAgent
&& state
->userAgent
[0]) {
314 s
= SySPrintf("User-Agent: %s", state
->userAgent
);
316 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers (out of memory)");
319 if (SyHdrAddLine(hdrs
, s
) != SY_OK
) {
321 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
327 if (SyHdrAddLine(hdrs
, "Content-Type: application/x-www-form-urlencoded") != SY_OK
) {
328 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
331 s
= SySPrintf("Content-Length: %i", postLen
); /* FIXME: change when we'll be able to post files */
332 if (!s
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
333 if (SyHdrAddLine(hdrs
, s
) != SY_OK
) {
335 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
340 if (state
->firstByte
> 0) {
341 if (SyHTTPAddRange(hdrs
, state
->firstByte
, -1) != SY_OK
) {
342 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
346 if (state
->fnprephdrs
&& state
->fnprephdrs(state
, hdrs
) != SY_OK
) break;
349 if (SyTCPConnect(&state
->fd
, purl
->host
, purl
->port
, state
->iface
, state
->ioTimeout
, state
->pfn
) != SY_OK
) break;
352 state
->status
= SY_STATUS_HANDSHAKING
;
353 #ifdef SYOPT_ALLOW_HTTPS
354 if (url
->proto
== SY_PROTO_HTTPS
&& state
->httpproxy
) {
355 proxy
= SyProxyNew(state
->ftpproxy
, SY_PROXY_HTTP_CONNECT
, state
->userAgent
);
357 if (SyProxyConnect(&state
->fd
, proxy
, url
->host
, url
->port
, state
->pfn
) != SY_OK
) break;
360 if (url
->proto
== SY_PROTO_HTTPS
) {
361 if (SyTCPInitSSL(&state
->fd
, state
->ioTimeout
) != SY_OK
) break;
365 if (SyHTTPSendQuery(&state
->fd
, hdrs
, state
->pfn
) != SY_OK
) break;
367 /* send post data, if any */
369 SyMessage(state
->pfn
, SY_MSG_NOTICE
, " sending %i bytes of POST data", postLen
);
370 if (SyTCPSend(&state
->fd
, state
->postData
, postLen
) != SY_OK
) {
371 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't send POST data");
375 /* read reply headers */
376 if (SyHTTPReadHeaders(hdrs
, &state
->fd
, state
->pfn
) != SY_OK
) break;
377 if (state
->fngothdrs
&& state
->fngothdrs(state
, hdrs
) != SY_OK
) break;
379 code
= hdrs
->code
/100;
382 state
->status
= SY_STATUS_REDIRECTING
;
383 SyTCPCloseSocket(&state
->fd
);
384 if (--rdcnt
< 1) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "too many redirects"); break; }
385 s
= SyHdrGetFieldValue(hdrs
, "Location");
386 if (!s
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "redirect to nowhere"); break; }
387 if (!s
[0]) { free(s
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid redirect"); break; }
388 /* check for 'news.php' or 'news.php?dir=/abc' or 'news.php?n=1#/abc */
390 if (!strstr(s
, "://")) {
392 t
= SySPrintf("/%s", s
); free(s
);
393 if (!t
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
397 lurl
= SyURLNew(); if (!lurl
) { free(s
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
398 if (SyURLParse(lurl
, s
) != SY_OK
) {
399 free(s
); SyClearURL(&lurl
);
400 SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid redirect");
406 if (strcmp(lurl
->dir
, "/")) {
407 /* has other dir parts */
408 s
= SySPrintf("%s%s", url
->dir
, (lurl
->dir
)+1);
409 if (!s
) { SyClearURL(&lurl
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
410 SyClearStr(&lurl
->dir
); lurl
->dir
= s
;
413 free(url
->dir
); url
->dir
= lurl
->dir
;
415 SyClearStr(&url
->protostr
); SyClearStr(&url
->host
);
416 SyClearStr(&url
->user
); SyClearStr(&url
->pass
);
417 url
->port
= lurl
->port
;
418 url
->defaultPort
= lurl
->defaultPort
;
419 url
->proto
= lurl
->proto
;
420 url
->protostr
= lurl
->protostr
;
421 url
->host
= lurl
->host
;
422 url
->user
= lurl
->user
;
423 url
->pass
= lurl
->pass
;
424 lurl
->protostr
= lurl
->host
= lurl
->user
= lurl
->pass
= NULL
;
426 SyClearStr(&lurl
->protostr
); SyClearStr(&lurl
->host
);
427 SyClearStr(&lurl
->user
); SyClearStr(&lurl
->pass
);
429 SyClearStr(&url
->file
); url
->file
= lurl
->file
;
430 SyClearStr(&url
->query
); url
->query
= lurl
->query
;
431 SyClearStr(&url
->anchor
); url
->anchor
= lurl
->anchor
;
432 free(lurl
); /* DO NOT CLEAR lurl! */
436 if (code
!= 2) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "%s", hdrs
->firstLine
); break; }
437 if (state
->firstByte
&& hdrs
->code
!= 206) {
438 SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't resume");
439 if (state
->fnnoresume
&& state
->fnnoresume(state
) != SY_OK
) break;
440 state
->firstByte
= 0;
442 state
->fileSize
= SyHTTPGetSize(hdrs
);
443 if (state
->fileSize
>= 0) {
444 state
->fileSize
+= state
->firstByte
;
445 if (state
->firstByte
> state
->fileSize
) break;
447 if ((s
= SyHdrGetFieldValue(hdrs
, "Content-Disposition")) != NULL
) {
448 /* extract file name, if any */
451 t
= strcasestr(s
, "filename");
453 if (t
!= buf
&& isalnum(*(t
-1))) { s
= t
+1; continue; }
454 t
+= 8; if (*t
&& isalnum(*t
)) { s
= t
; continue; }
455 while (*t
&& *t
<= ' ') t
++;
456 if (*t
!= '=') { s
= t
; continue; }
457 t
++; while (*t
&& *t
<= ' ') t
++;
459 if (*t
== '"') { t
++; s
= t
; while (*s
&& *s
!= '"') s
++; }
460 else { s
= t
; while (*s
&& (*s
!= ' ' && *s
!= ';')) s
++; }
462 state
->httpFName
= SyStrNew(t
, -1);
463 SyStrTrim(state
->httpFName
);
464 if (!state
->httpFName
[0]) SyClearStr(&state
->httpFName
);
476 if (state
->lastByte
< 0 && state
->fileSize
>= 0) state
->lastByte
= state
->fileSize
;
477 if ((state
->lastByte
>= 0 && state
->firstByte
> state
->lastByte
) ||
478 (state
->fileSize
>= 0 && state
->lastByte
> state
->fileSize
)) {
479 SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid file range");
482 } else if (state
->fd
.errCode
) {
483 s
= SyTCPGetLastErrorMsg (&state
->fd
);
485 SyMessage(state
->pfn
, SY_MSG_ERROR
, "socket error: %s", s
);
487 } else SyMessage(state
->pfn
, SY_MSG_ERROR
, "socket error: %i", state
->fd
.errCode
);
489 if (res
== SY_OK
) state
->status
= SY_STATUS_CONNECTED
;
490 SyInitStats(state
, &state
->stats
);
495 TSyResult
SyRun (TSyState
*state
) {
496 TSyResult res
= SY_ERROR
;
497 char *buf
, *bufPtr
; int bufSize
, bufLeft
;
498 int64_t total
, done
, left
;
499 double lastprogtime
, tm
, ctime
;
502 if (!state
|| state
->status
!= SY_STATUS_CONNECTED
) return SY_ERROR
;
503 state
->currentByte
= 0; state
->error
= SY_FALSE
;
504 if (state
->fileSize
>= 0) {
505 if ((state
->lastByte
>= 0 && state
->lastByte
== state
->firstByte
) || state
->firstByte
== state
->fileSize
) {
506 state
->status
= SY_STATUS_COMPLETE
;
510 if (!state
->fnopen
) {
511 /* no 'open' function, assume simple check */
512 state
->status
= SY_STATUS_COMPLETE
;
516 bufSize
= state
->initBufferSize
; if (bufSize
< 1) bufSize
= 1;
517 buf
= malloc(bufSize
);
519 SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error");
520 state
->error
= SY_TRUE
;
524 if (state
->fnopen(state
) != SY_OK
) {
526 state
->error
= SY_TRUE
;
530 total
= state
->fileSize
;
532 total
-= state
->firstByte
;
533 if (state
->lastByte
> 0) total
-= (state
->fileSize
-state
->lastByte
);
535 left
= total
; done
= 0;
536 SyInitStats(state
, &state
->stats
);
537 SyUpdateStats(state
, &state
->stats
, 0, total
);
538 SyMessage(state
->pfn
, SY_MSG_MSG
, "starting data transfer");
539 if (state
->fnprog
) state
->fnprog(state
, 0, total
, SY_FALSE
);
540 lastprogtime
= SyGetTimeD();
541 rsock
= (state
->datafd
.fd
>=0)?&state
->datafd
:&state
->fd
;
542 /* start downloading */
543 state
->status
= SY_STATUS_DOWNLOADING
;
544 while (left
< 0 || left
> 0) {
547 /* how many bytes we should receive? */
549 if (left
>= (int64_t)0) {
550 flg
= (int64_t)tord
> left
;
551 if (flg
) tord
= (int)left
;
554 ctime
= tm
= SyGetTimeD();
555 #ifdef SYDL_USE_OLD_ALGO
556 /*rd = SyTCPReceiveEx(rsock, buf, tord, SY_TCP_DONT_ALLOWPARTIAL);
557 bufLeft = bufSize-(rd>0?rd:0);*/
558 bufPtr
= buf
; bufLeft
= tord
; rd
= 0;
560 int xrd
= SyTCPReceiveEx(rsock
, bufPtr
, bufLeft
, SY_TCP_ALLOWPARTIAL
);
561 ctime
= SyGetTimeD();
563 if (xrd
< 0) { rd
= xrd
; break; }
564 rd
+= xrd
; bufPtr
+= xrd
; bufLeft
-= xrd
;
565 } while (!bufLeft
|| rd
>= bufSize
/3);
567 bufPtr
= buf
; bufLeft
= tord
; rd
= 0;
569 int xrd
= SyTCPReceiveEx(rsock
, bufPtr
, bufLeft
, SY_TCP_ALLOWPARTIAL
);
570 ctime
= SyGetTimeD();
572 if (xrd
< 0) { rd
= xrd
; break; }
573 rd
+= xrd
; bufPtr
+= xrd
; bufLeft
-= xrd
;
574 } while (!bufLeft
|| ctime
-tm
>= 4.0);
577 /* write everything %-) */
578 if (state
->fnwrite
&& state
->fnwrite(state
, buf
, rd
) != SY_OK
) break;
579 if (left
> 0) left
-= rd
; done
+= rd
;
580 state
->currentByte
= done
;
582 SyUpdateStats(state
, &state
->stats
, done
, total
);
583 /* and draw progress */
584 if (tm
-lastprogtime
>= 1.0) {
585 if (state
->fnprog
) state
->fnprog(state
, done
, total
, SY_FALSE
);
586 lastprogtime
= ctime
;
588 /* grow buffer if necessary */
589 if (!bufLeft
&& ctime
-tm
< 2.0 && bufSize
< state
->maxBufferSize
) {
591 if (bufLeft
> state
->maxBufferSize
) bufLeft
= state
->maxBufferSize
;
592 bufPtr
= realloc(buf
, bufLeft
);
594 /*fprintf(stderr, "\nbuffer grows from %i to %i\n", bufSize, bufLeft);*/
595 bufSize
= bufLeft
; buf
= bufPtr
;
598 /* shrink buffer if necessary */
599 /*if (bufLeft && ctime-tm > 2.0 && bufSize > 8192) {
604 if (rd
<= 0 || rsock
->errCode
) {
605 /* read error or connection closed */
606 if (rd
>= 0 && left
< 0) res
= SY_OK
; /* size unknown? all done */
607 else if (state
->breakNow
== SY_TRUE
) state
->interrupted
= SY_TRUE
;
611 /* check if downloading ok */
612 if (!left
) res
= SY_OK
;
613 state
->error
= (res
==SY_OK
)?SY_FALSE
:SY_TRUE
;
614 SyUpdateStats(state
, &state
->stats
, done
, total
);
615 if (state
->fnprog
) state
->fnprog(state
, done
, total
, SY_TRUE
);
616 if (state
->fnclose
&& state
->fnclose(state
) != SY_OK
) res
= SY_ERROR
; /* don't set state->error here! */
617 /* free memory, close sockets */
619 SyTCPCloseSocket(&state
->datafd
); SyTCPCloseSocket(&state
->fd
);
620 if (res
== SY_OK
) state
->status
= SY_STATUS_COMPLETE
;
621 if (rsock
->errCode
&& (left
> 0 || res
!= SY_OK
)) {
622 char *s
= SyTCPGetLastErrorMsg(rsock
);
624 SyMessage(state
->pfn
, SY_MSG_ERROR
, "socket error: %s", s
);
626 } else SyMessage(state
->pfn
, SY_MSG_ERROR
, "socket error: %i", rsock
->errCode
);