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 // Vampire Avalon
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;
44 SyStrFree(*s
); *s
= NULL
;
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 void SyReset (TSyState
*state
) {
220 if (state
->status
== SY_STATUS_UNPREPARED
) return;
221 SyTCPCloseSocket(&state
->datafd
); SyTCPCloseSocket(&state
->fd
);
222 state
->status
= SY_STATUS_PREPARED
;
223 state
->breakNow
= state
->interrupted
= SY_FALSE
;
228 #define SY_CHECK_BREAK0 if (state->breakNow != SY_FALSE) { state->interrupted = SY_TRUE; break; }
231 TSyResult
SyBegin (TSyState
*state
) {
232 TSyResult res
= SY_ERROR
;
233 TSyProxy
*proxy
= NULL
;
234 TSyHdrs
*hdrs
; TSyURL
*url
, *purl
, *lurl
;
235 char *s
, *t
, *buf
, tmp
[32];
238 if (!state
|| (state
->status
!= SY_STATUS_PREPARED
&& state
->status
!= SY_STATUS_DOWNLOADING
)) return SY_ERROR
;
239 rdcnt
= state
->maxRedirects
;
240 state
->breakNow
= state
->interrupted
= state
->error
= SY_FALSE
; state
->currentByte
= 0;
241 state
->chunked
= SY_FALSE
;
242 if (state
->firstByte
< 0) state
->firstByte
= 0;
243 if (state
->lastByte
>= 0 && state
->firstByte
> state
->lastByte
) return SY_ERROR
;
244 hdrs
= SyHdrNew(); if (!hdrs
) return SY_ERROR
;
246 SyProxyFree(proxy
); proxy
= NULL
;
247 SyClearStr(&state
->httpFName
);
248 SyTCPCloseSocket(&state
->datafd
); SyTCPCloseSocket(&state
->fd
);
249 state
->status
= SY_STATUS_CONNECTING
;
252 SyMessage(state
->pfn
, SY_MSG_MSG
, "downloading: %s://%s:%i%s%s%s%s",
253 url
->protostr
, url
->host
, url
->port
, url
->dir
, url
->file
, url
->query
, url
->anchor
);
254 /* check for proxy */
255 if (url
->proto
== SY_PROTO_FTP
&& state
->ftpproxy
) purl
= state
->ftpproxy
;
256 #ifdef SYOPT_ALLOW_HTTPS
257 else if (url
->proto
== SY_PROTO_HTTP
&& state
->httpproxy
) purl
= state
->httpproxy
;
258 else if (url
->proto
== SY_PROTO_HTTPS
&& state
->httpproxy
) purl
= state
->httpproxy
;
260 else if (url
->proto
== SY_PROTO_HTTP
&& state
->httpproxy
) purl
= state
->httpproxy
;
263 state
->fd
.errCode
= 0;
264 if (url
->proto
== SY_PROTO_FTP
&& (!state
->ftpproxy
|| state
->ftpUseConnect
)) {
267 if (SyTCPConnect(&state
->fd
, purl
->host
, purl
->port
, state
->iface
, state
->ioTimeout
, state
->pfn
) != SY_OK
) break;
270 state
->status
= SY_STATUS_HANDSHAKING
;
271 if (state
->ftpUseConnect
&& state
->ftpproxy
) {
272 proxy
= SyProxyNew(state
->ftpproxy
, SY_PROXY_HTTP_CONNECT
, state
->userAgent
);
274 if (SyProxyConnect(&state
->fd
, proxy
, url
->host
, url
->port
, state
->pfn
) != SY_OK
) break;
277 if (SyFTPStart(&state
->fd
, url
->user
, url
->pass
, state
->pfn
) != SY_OK
) break;
279 if (SyFTPCwd(&state
->fd
, url
->dir
, state
->pfn
) != SY_OK
) break;
281 state
->fileSize
= SyFTPGetSize(&state
->fd
, state
->iface
, url
->file
, rdcnt
, state
->ioTimeout
, state
->pfn
, proxy
);
282 if (state
->firstByte
> state
->fileSize
) break;
284 if (state
->firstByte
> 0) {
286 if (SyLong2Str(tmp
, state
->firstByte
) != SY_OK
) break;
287 if (SyTCPSendLine(state
->pfn
, 1, &state
->fd
, "REST %s", tmp
) != SY_OK
) break;
289 /*if (SyFTPWaitFor2(&state->fd, 3, 2, state->pfn) <= 0) break;*/
290 if ((s
= SyFTPWait(&code
, &state
->fd
, state
->pfn
)) == NULL
) break;
291 if (code
<= 0) break;
293 if (code
!= 3 && code
!= 2) {
294 SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't resume");
295 if (state
->fnnoresume
&& state
->fnnoresume(state
) != SY_OK
) break;
296 state
->firstByte
= 0;
300 if (SyFTPOpenDataPassv(&state
->fd
, &state
->datafd
, state
->iface
, state
->ioTimeout
, state
->pfn
, proxy
) != SY_OK
) break;
302 if (SyTCPSendLine(state
->pfn
, 1, &state
->fd
, "RETR %s", url
->file
) != SY_OK
) break;
304 if (SyFTPWaitFor2(&state
->fd
, 1, -1, state
->pfn
) <= 0) break;
309 int postLen
= strlen(state
->postData
?state
->postData
:"");
310 #ifdef SYOPT_ALLOW_HTTPS
311 if (SyHTTPBuildQuery(hdrs
, postLen
?"POST":"GET", url
,
312 (url
->proto
==SY_PROTO_FTP
)?state
->ftpproxy
:
313 (url
->proto
==SY_PROTO_HTTP
)?state
->httpproxy
:NULL
) != SY_OK
) {
314 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
318 if (SyHTTPBuildQuery(hdrs
, postLen
?"POST":"GET", url
,
319 (url
->proto
==SY_PROTO_FTP
)?state
->ftpproxy
:state
->httpproxy
) != SY_OK
) {
320 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
324 if (state
->userAgent
&& state
->userAgent
[0]) {
325 s
= SySPrintf("User-Agent: %s", state
->userAgent
);
327 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers (out of memory)");
330 if (SyHdrAddLine(hdrs
, s
) != SY_OK
) {
332 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
338 if (SyHdrAddLine(hdrs
, "Content-Type: application/x-www-form-urlencoded") != SY_OK
) {
339 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
342 s
= SySPrintf("Content-Length: %i", postLen
); /* FIXME: change when we'll be able to post files */
343 if (!s
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
344 if (SyHdrAddLine(hdrs
, s
) != SY_OK
) {
346 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
351 if (state
->firstByte
> 0) {
352 if (SyHTTPAddRange(hdrs
, state
->firstByte
, -1) != SY_OK
) {
353 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't build request headers");
357 if (state
->fnprephdrs
&& state
->fnprephdrs(state
, hdrs
) != SY_OK
) break;
360 if (SyTCPConnect(&state
->fd
, purl
->host
, purl
->port
, state
->iface
, state
->ioTimeout
, state
->pfn
) != SY_OK
) break;
363 state
->status
= SY_STATUS_HANDSHAKING
;
364 #ifdef SYOPT_ALLOW_HTTPS
365 if (url
->proto
== SY_PROTO_HTTPS
&& state
->httpproxy
) {
366 proxy
= SyProxyNew(state
->ftpproxy
, SY_PROXY_HTTP_CONNECT
, state
->userAgent
);
368 if (SyProxyConnect(&state
->fd
, proxy
, url
->host
, url
->port
, state
->pfn
) != SY_OK
) break;
371 if (url
->proto
== SY_PROTO_HTTPS
) {
372 if (SyTCPInitSSL(&state
->fd
, state
->ioTimeout
) != SY_OK
) break;
376 if (SyHTTPSendQuery(&state
->fd
, hdrs
, state
->pfn
) != SY_OK
) break;
378 /* send post data, if any */
380 SyMessage(state
->pfn
, SY_MSG_NOTICE
, " sending %i bytes of POST data", postLen
);
381 if (SyTCPSend(&state
->fd
, state
->postData
, postLen
) != SY_OK
) {
382 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't send POST data");
386 /* read reply headers */
387 if (SyHTTPReadHeaders(hdrs
, &state
->fd
, state
->pfn
) != SY_OK
) break;
388 if (state
->fngothdrs
&& state
->fngothdrs(state
, hdrs
) != SY_OK
) break;
390 code
= hdrs
->code
/100;
393 state
->status
= SY_STATUS_REDIRECTING
;
394 SyTCPCloseSocket(&state
->fd
);
395 if (--rdcnt
< 1) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "too many redirects"); break; }
396 s
= SyHdrGetFieldValue(hdrs
, "Location");
397 if (!s
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "redirect to nowhere"); break; }
398 if (!s
[0]) { SyStrFree(s
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid redirect"); break; }
399 /* check for 'news.php' or 'news.php?dir=/abc' or 'news.php?n=1#/abc */
400 /*BUG: possible bug! why i'm prepending '/'? */
402 if (!strstr(s
, "://")) {
404 /* seems to be a relative path */
405 char *x
= SySPrintf("%s%s", url
->dir
, s
); SyStrFree(s
);
406 if (!x
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
409 t = SySPrintf("/%s", s); SyStrFree(s);
410 if (!t) { SyMessage(state->pfn, SY_MSG_ERROR, "memory error"); break; }
415 /* check for spaces in url */
416 if (strncmp(s
, "ftp://", 6) && strchr(s
, ' ')) {
417 int cnt
= 0; char *xp
= s
, *tp
;
418 while (*xp
) { if (*xp
== ' ') cnt
++; xp
++; }
419 t
= SyStrAlloc(strlen(s
)+cnt
*4); //actually *3
420 if (!t
) { SyStrFree(s
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
421 for (xp
= s
, tp
= t
; *xp
; xp
++) {
422 if (*xp
!= ' ') *tp
++ = *xp
;
423 else { *tp
++ = '%'; *tp
++ = '2'; *tp
++ = '0'; }
428 lurl
= SyURLNew(); if (!lurl
) { SyStrFree(s
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
429 if (SyURLParse(lurl
, s
) != SY_OK
) {
430 SyStrFree(s
); SyClearURL(&lurl
);
431 SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid redirect");
437 if (strcmp(lurl
->dir
, "/")) {
438 /* has other dir parts */
439 s
= SySPrintf("%s%s", url
->dir
, (lurl
->dir
)+1);
440 if (!s
) { SyClearURL(&lurl
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); break; }
441 SyClearStr(&lurl
->dir
); lurl
->dir
= s
;
444 SyStrFree(url
->dir
); url
->dir
= lurl
->dir
;
446 SyClearStr(&url
->protostr
); SyClearStr(&url
->host
);
447 SyClearStr(&url
->user
); SyClearStr(&url
->pass
);
448 url
->port
= lurl
->port
;
449 url
->defaultPort
= lurl
->defaultPort
;
450 url
->proto
= lurl
->proto
;
451 url
->protostr
= lurl
->protostr
;
452 url
->host
= lurl
->host
;
453 url
->user
= lurl
->user
;
454 url
->pass
= lurl
->pass
;
455 lurl
->protostr
= lurl
->host
= lurl
->user
= lurl
->pass
= NULL
;
457 SyClearStr(&lurl
->protostr
); SyClearStr(&lurl
->host
);
458 SyClearStr(&lurl
->user
); SyClearStr(&lurl
->pass
);
460 SyClearStr(&url
->file
); url
->file
= lurl
->file
;
461 SyClearStr(&url
->query
); url
->query
= lurl
->query
;
462 SyClearStr(&url
->anchor
); url
->anchor
= lurl
->anchor
;
463 free(lurl
); /* DO NOT CLEAR lurl! */
467 if (code
!= 2) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "%s", hdrs
->firstLine
); break; }
468 if (state
->firstByte
&& hdrs
->code
!= 206) {
469 SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't resume");
470 if (state
->fnnoresume
&& state
->fnnoresume(state
) != SY_OK
) break;
471 state
->firstByte
= 0;
473 state
->fileSize
= SyHTTPGetSize(hdrs
);
474 if (state
->fileSize
>= 0) {
475 state
->fileSize
+= state
->firstByte
;
476 if (state
->firstByte
> state
->fileSize
) break;
478 if ((s
= SyHdrGetFieldValue(hdrs
, "Transfer-Encoding")) != NULL
) {
479 if (strstr(s
, "chunked")) {
480 state
->chunked
= SY_TRUE
;
481 state
->fileSize
= -1;
485 if ((s
= SyHdrGetFieldValue(hdrs
, "Content-Disposition")) != NULL
) {
486 /* extract file name, if any */
489 t
= strcasestr(s
, "filename");
491 if (t
!= buf
&& isalnum(*(t
-1))) { s
= t
+1; continue; }
492 t
+= 8; if (*t
&& isalnum(*t
)) { s
= t
; continue; }
493 while (*t
&& *t
<= ' ') t
++;
494 /* strange format: filename*=UTF-8''FurShaderExample.rar */
495 if (*t
== '*' && t
[1] == '=') {
496 t
+= 2; while (*t
&& *t
!= '\x27' && *t
!= ';') t
++;
497 if (*t
== ';') { s
= t
; continue; }
498 while (*t
&& *t
== '\x27') t
++;
500 if (*t
!= '=') { s
= t
; continue; }
501 t
++; while (*t
&& *t
<= ' ') t
++;
504 if (*t
== '"') { t
++; s
= t
; while (*s
&& *s
!= '"') s
++; }
505 else { s
= t
; while (*s
&& (*s
!= ' ' && *s
!= ';')) s
++; }
507 state
->httpFName
= SyStrNew(t
, -1);
508 SyStrTrim(state
->httpFName
);
509 if (!state
->httpFName
[0]) SyClearStr(&state
->httpFName
);
521 if (state
->lastByte
< 0 && state
->fileSize
>= 0) state
->lastByte
= state
->fileSize
;
522 if ((state
->lastByte
>= 0 && state
->firstByte
> state
->lastByte
) ||
523 (state
->fileSize
>= 0 && state
->lastByte
> state
->fileSize
)) {
524 SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid file range");
527 } else if (state
->fd
.errCode
) {
528 s
= SyTCPGetLastErrorMsg (&state
->fd
);
530 SyMessage(state
->pfn
, SY_MSG_ERROR
, "socket error: %s", s
);
532 } else SyMessage(state
->pfn
, SY_MSG_ERROR
, "socket error: %i", state
->fd
.errCode
);
534 if (res
== SY_OK
) state
->status
= SY_STATUS_CONNECTED
;
535 SyInitStats(state
, &state
->stats
);
540 TSyResult
SyRun (TSyState
*state
) {
541 TSyResult res
= SY_ERROR
;
542 char *buf
, *bufPtr
; int bufSize
, bufLeft
;
543 int64_t total
, done
, left
;
544 double lastprogtime
, tm
, ctime
;
547 if (!state
|| state
->status
!= SY_STATUS_CONNECTED
) return SY_ERROR
;
548 state
->currentByte
= 0; state
->error
= SY_FALSE
;
549 if (state
->fileSize
>= 0) {
550 if ((state
->lastByte
>= 0 && state
->lastByte
== state
->firstByte
) || state
->firstByte
== state
->fileSize
) {
551 state
->status
= SY_STATUS_COMPLETE
;
555 if (!state
->fnopen
) {
556 /* no 'open' function, assume simple check */
557 state
->status
= SY_STATUS_COMPLETE
;
561 bufSize
= state
->initBufferSize
; if (bufSize
< 1) bufSize
= 1;
562 buf
= malloc(bufSize
);
564 SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error");
565 state
->error
= SY_TRUE
;
569 if (state
->fnopen(state
) != SY_OK
) {
571 state
->error
= SY_TRUE
;
575 total
= state
->fileSize
;
577 total
-= state
->firstByte
;
578 if (state
->lastByte
> 0) total
-= (state
->fileSize
-state
->lastByte
);
580 left
= total
; done
= 0;
581 SyInitStats(state
, &state
->stats
);
582 SyUpdateStats(state
, &state
->stats
, 0, total
);
583 SyMessage(state
->pfn
, SY_MSG_MSG
, "starting data transfer");
584 if (state
->fnprog
) state
->fnprog(state
, 0, total
, SY_FALSE
);
585 lastprogtime
= SyGetTimeD();
586 rsock
= (state
->datafd
.fd
>=0)?&state
->datafd
:&state
->fd
;
587 /* start downloading */
588 state
->status
= SY_STATUS_DOWNLOADING
;
589 while (state
->chunked
|| left
< 0 || left
> 0) {
592 /*fprintf(stderr, "left: %i\n", (int)left);*/
593 if (state
->chunked
) {
596 /* not the first chunk; should receive chunk end ("\r\n") */
597 if (SyTCPReceive(rsock
, cls
, 2) != 2) { res
= SY_ERROR
; break; }
598 if (cls
[0] != '\r' || cls
[1] != '\n') { res
= SY_ERROR
; break; }
600 /* chunked, get chunk length */
601 if (!SyTCPReceiveStrEx(rsock
, 120, cls
)) { res
= SY_ERROR
; break; }
602 /*fprintf(stderr, " [%s]\n", cls);*/
604 int64_t csz
= SyHexStr2Long(cls
);
605 if (csz
< 1) { res
= SY_ERROR
; break; }
606 if (csz
== 0) { res
= SY_OK
; left
= 0; break; } // done
607 /*fprintf(stderr, "chunk size: %i\n", (int)csz);*/
610 /* how many bytes we should receive? */
612 if (left
>= (int64_t)0) {
613 flg
= (int64_t)tord
> left
;
614 if (flg
) tord
= (int)left
;
617 ctime
= tm
= SyGetTimeD();
618 #ifdef SYDL_USE_OLD_ALGO
619 /*rd = SyTCPReceiveEx(rsock, buf, tord, SY_TCP_DONT_ALLOWPARTIAL);
620 bufLeft = bufSize-(rd>0?rd:0);*/
621 bufPtr
= buf
; bufLeft
= tord
; rd
= 0;
623 int xrd
= SyTCPReceiveEx(rsock
, bufPtr
, bufLeft
, SY_TCP_ALLOWPARTIAL
);
624 ctime
= SyGetTimeD();
626 if (xrd
< 0) { rd
= xrd
; break; }
627 rd
+= xrd
; bufPtr
+= xrd
; bufLeft
-= xrd
;
628 } while (!bufLeft
|| rd
>= bufSize
/3);
630 bufPtr
= buf
; bufLeft
= tord
; rd
= 0;
632 int xrd
= SyTCPReceiveEx(rsock
, bufPtr
, bufLeft
, SY_TCP_ALLOWPARTIAL
);
633 ctime
= SyGetTimeD();
635 if (xrd
< 0) { rd
= xrd
; break; }
636 rd
+= xrd
; bufPtr
+= xrd
; bufLeft
-= xrd
;
637 } while (!bufLeft
|| ctime
-tm
>= 4.0);
640 /* write everything %-) */
641 if (state
->fnwrite
&& state
->fnwrite(state
, buf
, rd
) != SY_OK
) break;
642 if (left
> 0) left
-= rd
; done
+= rd
;
643 state
->currentByte
= done
;
645 SyUpdateStats(state
, &state
->stats
, done
, total
);
646 /* and draw progress */
647 if (tm
-lastprogtime
>= 1.0) {
648 if (state
->fnprog
) state
->fnprog(state
, done
, total
, SY_FALSE
);
649 lastprogtime
= ctime
;
651 /* grow buffer if necessary */
652 if (!bufLeft
&& ctime
-tm
< 2.0 && bufSize
< state
->maxBufferSize
) {
654 if (bufLeft
> state
->maxBufferSize
) bufLeft
= state
->maxBufferSize
;
655 bufPtr
= realloc(buf
, bufLeft
);
657 /*fprintf(stderr, "\nbuffer grows from %i to %i\n", bufSize, bufLeft);*/
658 bufSize
= bufLeft
; buf
= bufPtr
;
661 /* shrink buffer if necessary */
662 /*if (bufLeft && ctime-tm > 2.0 && bufSize > 8192) {
667 if (rd
<= 0 || rsock
->errCode
) {
668 /* read error or connection closed */
669 if (rd
>= 0 && left
< 0) res
= SY_OK
; /* size unknown? all done */
670 else if (state
->breakNow
== SY_TRUE
) state
->interrupted
= SY_TRUE
;
674 /* check if downloading ok */
675 if (!left
) res
= SY_OK
;
676 state
->error
= (res
==SY_OK
)?SY_FALSE
:SY_TRUE
;
677 SyUpdateStats(state
, &state
->stats
, done
, total
);
678 if (state
->fnprog
) state
->fnprog(state
, done
, total
, SY_TRUE
);
679 if (state
->fnclose
&& state
->fnclose(state
) != SY_OK
) res
= SY_ERROR
; /* don't set state->error here! */
680 /* free memory, close sockets */
682 SyTCPCloseSocket(&state
->datafd
); SyTCPCloseSocket(&state
->fd
);
683 if (res
== SY_OK
) state
->status
= SY_STATUS_COMPLETE
;
684 if (rsock
->errCode
&& (left
> 0 || res
!= SY_OK
)) {
685 char *s
= SyTCPGetLastErrorMsg(rsock
);
687 SyMessage(state
->pfn
, SY_MSG_ERROR
, "socket error: %s", s
);
689 } else SyMessage(state
->pfn
, SY_MSG_ERROR
, "socket error: %i", rsock
->errCode
);