forgot to include .jam rules in redist package %-)
[syren.git] / src / syren_dloader.c
blob708ce07173f2aa988ab58130451d4db89e22bb5d
1 /*
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
23 Syren download driver
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 free(*s); *s = NULL;
48 static void SyClearInternal (TSyState *state, int keepSizesAndPData) {
49 if (!state) return;
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) {
66 si->i[0].active = 0;
67 si->i[1].active = 0;
68 si->interval = 30; /* seconds */
69 si->bps = 0.0;
70 si->eta = 0.0;
74 void SyUpdateStats (TSyState *state, TSyStats *si, int64_t bytesDone, int64_t bytesTotal) {
75 TSyStatInfo *i0, *i1;
76 double ctime, ptime, t;
77 double w0, w1; /* weights */
78 double bps0, bps1;
80 i0 = &(si->i[0]);
81 i1 = &(si->i[1]);
83 ctime = SyGetTimeD();
85 if (!i1->active) {
86 /* nothing started */
87 i0->active = 0;
88 i0->bytesFrom = 0;
89 i0->bytesDone = 0;
90 i0->time = 0.0;
91 i1->active = 1;
92 i1->bytesFrom = 0;
93 i1->bytesDone = bytesDone;
94 i1->time = ctime;
95 si->sttime = ctime;
96 si->bps = 0.0;
97 si->eta = 0.0;
98 return;
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 */
107 *i0 = *i1;
108 i1->bytesFrom = bytesDone;
109 i1->bytesDone = 0;
110 i1->time = ctime;
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;
119 w0 = 1.0-w1;
121 /* calculate bps */
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);
126 /* calculate eta */
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));
136 if (state) {
137 if (SyTCPInitSocket(&state->fd) != SY_OK || SyTCPInitSocket(&state->datafd) != SY_OK) {
138 free(state);
139 return NULL;
141 state->maxBufferSize = 1024*1024; /* 1mb */
142 state->ioTimeout = 60;
143 state->maxRedirects = 6;
144 SyClearInternal(state, 0);
146 return state;
150 void SyFree (TSyState *state) {
151 if (!state) return;
152 SyClear(state); free(state);
156 void SyClear (TSyState *state) {
157 if (!state) return;
158 SyClearInternal(state, 0);
163 TSyResult SyPrepare (TSyState *state, const char *urlstr, const char *httpproxyurl, const char *ftpproxyurl,
164 const char *iface) {
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;
170 if (iface) {
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");
177 return SY_ERROR;
179 if (state->url->proto == SY_PROTO_UNKNOWN) {
180 SyClearInternal(state, 1);
181 SyMessage(state->pfn, SY_MSG_ERROR, "unknown protocol");
182 return SY_ERROR;
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");
188 return SY_ERROR;
190 #endif
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");
196 return SY_ERROR;
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");
204 return SY_ERROR;
207 state->status = SY_STATUS_PREPARED;
208 return SY_OK;
212 TSyResult SyEnd (TSyState *state) {
213 SyClear(state);
214 return SY_OK;
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];
226 int rdcnt, code;
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;
234 while (1) {
235 SyProxyFree(proxy); proxy = NULL;
236 SyClearStr(&state->httpFName);
237 SyTCPCloseSocket(&state->datafd); SyTCPCloseSocket(&state->fd);
238 state->status = SY_STATUS_CONNECTING;
239 SY_CHECK_BREAK0
240 url = state->url;
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;
248 #else
249 else if (url->proto == SY_PROTO_HTTP && state->httpproxy) purl = state->httpproxy;
250 #endif
251 else purl = url;
252 /* connecting */
253 if (SyTCPConnect(&state->fd, purl->host, purl->port, state->iface, state->ioTimeout, state->pfn) != SY_OK) break;
254 SY_CHECK_BREAK0
255 /* handshaking */
256 state->status = SY_STATUS_HANDSHAKING;
257 if (url->proto == SY_PROTO_FTP && (!state->ftpproxy || state->ftpUseConnect)) {
258 /* FTP */
259 if (state->ftpUseConnect && state->ftpproxy) {
260 proxy = SyProxyNew(state->ftpproxy, SY_PROXY_HTTP_CONNECT, state->userAgent);
261 if (!proxy) break;
262 if (SyProxyConnect(&state->fd, proxy, url->host, url->port, state->pfn) != SY_OK) break;
263 SY_CHECK_BREAK0
265 if (SyFTPStart(&state->fd, url->user, url->pass, state->pfn) != SY_OK) break;
266 SY_CHECK_BREAK0
267 if (SyFTPCwd(&state->fd, url->dir, state->pfn) != SY_OK) break;
268 SY_CHECK_BREAK0
269 state->fileSize = SyFTPGetSize(&state->fd, state->iface, url->file, rdcnt, state->ioTimeout, state->pfn, proxy);
270 if (state->firstByte > state->fileSize) break;
271 SY_CHECK_BREAK0
272 if (state->firstByte > 0) {
273 /* do REST */
274 if (SyLong2Str(tmp, state->firstByte) != SY_OK) break;
275 if (SyTCPSendLine(state->pfn, 1, &state->fd, "REST %s", tmp) != SY_OK) break;
276 SY_CHECK_BREAK0
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;
280 code /= 100;
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;
286 SY_CHECK_BREAK0
288 if (SyFTPOpenDataPassv(&state->fd, &state->datafd, state->iface, state->ioTimeout, state->pfn, proxy) != SY_OK) break;
289 SY_CHECK_BREAK0
290 if (SyTCPSendLine(state->pfn, 1, &state->fd, "RETR %s", url->file) != SY_OK) break;
291 SY_CHECK_BREAK0
292 if (SyFTPWaitFor2(&state->fd, 1, -1, state->pfn) <= 0) break;
293 res = SY_OK; break;
294 } else {
295 /* HTTP/HTTPS */
296 int nodir;
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);
301 if (!proxy) break;
302 if (SyProxyConnect(&state->fd, proxy, url->host, url->port, state->pfn) != SY_OK) break;
303 SY_CHECK_BREAK0
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");
312 break;
314 #else
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");
318 break;
320 #endif
321 if (state->userAgent && state->userAgent[0]) {
322 s = SySPrintf("User-Agent: %s", state->userAgent);
323 if (!s) {
324 SyMessage(state->pfn, SY_MSG_ERROR, "can't build request headers (out of memory)");
325 break;
327 if (SyHdrAddLine(hdrs, s) != SY_OK) {
328 free(s);
329 SyMessage(state->pfn, SY_MSG_ERROR, "can't build request headers");
330 break;
332 free(s);
334 if (postLen) {
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");
337 break;
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) {
342 free(s);
343 SyMessage(state->pfn, SY_MSG_ERROR, "can't build request headers");
344 break;
346 free(s);
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");
351 break;
354 if (state->fnprephdrs && state->fnprephdrs(state, hdrs) != SY_OK) break;
355 if (SyHTTPSendQuery(&state->fd, hdrs, state->pfn) != SY_OK) break;
356 SY_CHECK_BREAK0
357 /* send post data, if any */
358 if (postLen) {
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");
362 break;
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;
368 SY_CHECK_BREAK0
369 code = hdrs->code/100;
370 if (code == 3) {
371 /* redirect */
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 */
379 nodir = 0;
380 if (!strstr(s, "://")) {
381 if (*s != '/') {
382 t = SySPrintf("/%s", s); free(s);
383 if (!t) { SyMessage(state->pfn, SY_MSG_ERROR, "memory error"); break; }
384 s = t; nodir = 1;
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");
391 break;
393 free(s);
394 if (nodir) {
395 /* relative */
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;
404 if (lurl->host[0]) {
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;
415 } else {
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! */
423 /* again */
424 } else {
425 /* no redirect */
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 */
439 buf = s;
440 while (*s) {
441 t = strcasestr(s, "filename");
442 if (!t) break;
443 t += 8; while (*t && *t <= ' ') t++;
444 if (*t != '=') { s = t; continue; }
445 t++; while (*t && *t <= ' ') t++;
446 if (!(*t)) break;
447 if (*t == '"') { t++; s = t; while (*s && *s != '"') s++; }
448 else { s = t; while (*s && (*s != ' ' && *s != ';')) s++; }
449 *s = '\0';
450 state->httpFName = SyStrNew(t, -1);
451 SyStrTrim(state->httpFName);
452 if (!state->httpFName[0]) SyClearStr(&state->httpFName);
453 break;
455 free(buf);
457 res = SY_OK; break;
461 SyHdrFree(hdrs);
462 SyProxyFree(proxy);
463 if (res == SY_OK) {
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");
468 res = SY_ERROR;
471 if (res == SY_OK) state->status = SY_STATUS_CONNECTED;
472 SyInitStats(state, &state->stats);
473 return res;
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;
482 TSySocket *rsock;
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;
489 return SY_OK;
492 if (!state->fnopen) {
493 /* no 'open' function, assume simple check */
494 state->status = SY_STATUS_COMPLETE;
495 return SY_OK;
497 /* prepare buffer */
498 bufSize = state->initBufferSize; if (bufSize < 1) bufSize = 1;
499 buf = malloc(bufSize);
500 if (!buf) {
501 SyMessage(state->pfn, SY_MSG_ERROR, "memory error");
502 state->error = SY_TRUE;
503 return SY_ERROR;
505 /* open file */
506 if (state->fnopen(state) != SY_OK) {
507 free(buf);
508 state->error = SY_TRUE;
509 return SY_ERROR;
511 /* init vars */
512 total = state->fileSize;
513 if (total > 0) {
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) {
527 int tord, rd, flg;
528 SY_CHECK_BREAK0
529 /* how many bytes we should receive? */
530 tord = bufSize;
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;
540 do {
541 int xrd = SyTCPReceiveEx(rsock, bufPtr, bufLeft, SY_TCP_ALLOWPARTIAL);
542 ctime = SyGetTimeD();
543 if (!xrd) break;
544 if (xrd < 0) { rd = xrd; break; }
545 rd += xrd; bufPtr += xrd; bufLeft -= xrd;
546 } while (!bufLeft || rd >= bufSize/3);
547 #else
548 bufPtr = buf; bufLeft = tord; rd = 0;
549 do {
550 int xrd = SyTCPReceiveEx(rsock, bufPtr, bufLeft, SY_TCP_ALLOWPARTIAL);
551 ctime = SyGetTimeD();
552 if (!xrd) break;
553 if (xrd < 0) { rd = xrd; break; }
554 rd += xrd; bufPtr += xrd; bufLeft -= xrd;
555 } while (!bufLeft || ctime-tm >= 4.0);
556 #endif
557 if (rd > 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;
562 /* update stats */
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) {
571 bufLeft = bufSize*2;
572 if (bufLeft > state->maxBufferSize) bufLeft = state->maxBufferSize;
573 bufPtr = realloc(buf, bufLeft);
574 if (bufPtr) {
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) {
581 bufLeft = bufSize/2;
585 if (rd <= 0) {
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;
589 break;
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 */
599 if (buf) free(buf);
600 SyTCPCloseSocket(&state->datafd); SyTCPCloseSocket(&state->fd);
601 if (res == SY_OK) state->status = SY_STATUS_COMPLETE;
602 return res;
606 #endif