build system changed
[syren.git] / src / syren.c
blobd85d55a57a4a6fe3c2b1358f4ca5b33ba806a096
1 /*
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
23 Syren main program
25 #include "syren_common.h"
26 #include "syren_os.h"
27 #include "syren_str.h"
28 #include "syren_msg.h"
29 #include "syren_cfg.h"
30 #include "syren_hdrs.h"
31 #include "syren_tcp.h"
32 #include "syren_http.h"
33 #include "syren_proxy.h"
34 #include "syren_ftp.h"
35 #include "syren_dloader.h"
37 #include "syren_script.h"
40 #include <signal.h>
42 /* uncomment this to use Linux TTY ioctls */
44 #ifdef __linux
45 #define LINUX_TTY
46 #endif
49 /* uncomment this to use ANSI/VT100 TTY codes */
50 #define ANSI_TTY
52 /* uncomment this to use "DEC private" TTY codes (ESC [ ? n)*/
53 #define DEC_TTY
57 TSyBool
58 cfgNoOutput = SY_FALSE,
59 cfgQuiet = SY_FALSE,
60 cfgNoIndicator = SY_FALSE,
61 cfgUseProxyHTTP = SY_TRUE,
62 cfgUseProxyFTP = SY_TRUE,
63 cfgDecodeFTPURL = SY_TRUE,
64 cfgFTPUseConnect = SY_FALSE,
65 cfgProgressWriteCR = SY_FALSE;
66 int
67 cfgBufferSize = 8192,
68 cfgMaxBufferSize = 1024*1024,
69 cfgSaveInterval = 10,
70 cfgReconnectDelay = 10,
71 cfgReconnectAttempts = 666,
72 cfgTimeout = 60,
73 cfgMaxRedirects = 10,
74 cfgRefererType = 0;
75 char
76 *cfgDefaultName = NULL,
77 *cfgIFace = NULL,
78 *cfgProxyHTTP = NULL,
79 *cfgProxyFTP = NULL,
80 *cfgRefererStr = NULL,
81 *cfgUAStr = NULL;
83 char
84 *oOutFileName = NULL, /* -o/-O */
85 *oStateFileName = NULL, /* -s */
86 *oURL = NULL,
87 *oPostData = NULL,
88 *oSendCookies = NULL,
89 *oCookieDump = NULL,
90 *oAddonHeaders = NULL;
91 TSyBool
92 oResume = SY_FALSE,
93 oCanRename = SY_TRUE,
94 oCanRemoveState = SY_TRUE,
95 oIgnoreStatePos = SY_FALSE,
96 oNoStateFile = SY_FALSE,
97 oForceRetry = SY_FALSE,
98 writeCfg = SY_FALSE,
99 dumpState = SY_FALSE,
100 replyOnly = SY_FALSE,
101 oPreallocSpace = SY_TRUE;
102 int uEncodeDecode = 0;
103 TSyBool doRXVT = SY_FALSE;
106 TSyKVList *oURLList = NULL;
108 double lastSSave = 0;
110 TSyState *state = NULL; /* dl state */
111 #ifdef SYREN_USE_SCRIPT
112 TSyBool scAbort = SY_FALSE;
113 #endif
115 #ifdef LINUX_TTY
116 static int GetTTYWidth (void) {
117 struct winsize ws;
118 if (!isatty(fileno(stderr))) return -1;
119 if (ioctl(fileno(stderr), TIOCGWINSZ, &ws)) return -1;
120 return ws.ws_col;
122 #endif
125 static void PrintProgress (TSyState *state, int64_t bytesDone, int64_t bytesTotal, TSyBool done) {
126 char tmp0[128], tmp1[128], tmp2[128], tmp3[128], tmp4[128], fmt[128], str[1024];
127 int len; int64_t fb = state->firstByte;
128 TSyStats *stats = &state->stats;
130 if (cfgNoIndicator) return;
131 fprintf(stderr, "\r");
132 #ifdef DEC_TTY
133 fputs("\x1b[?7l", stderr);
134 #endif
135 if (bytesTotal > 0) {
136 len = SyLong2StrComma(tmp1, (bytesTotal+fb));
137 SyLong2StrComma(tmp0, (bytesDone+fb));
138 sprintf(fmt, "[%%%is/%%s] ", len);
139 sprintf(tmp2, fmt, tmp0, tmp1); /*!*/
140 sprintf(tmp3, "[%3i%%] ", (int)((double)100*(bytesDone+fb)/(bytesTotal+fb))); /*!*/
141 /* tmp3: res */
142 SySize2Str(tmp0, (int64_t)stats->bps);
143 sprintf(tmp4, "[SPD:%s] ", tmp0); /*!*/
144 SyTime2Str(tmp0, (int)(SyGetTimeD()-stats->sttime));
145 SyTime2Str(tmp1, (int)stats->eta);
146 sprintf(fmt, "TIME:%s ETA:%s", tmp0, tmp1); /*!*/
147 fprintf(stderr, "%s%s%s%s", tmp2, tmp3, tmp4, fmt);
148 sprintf(str, "%s%s%s/%s", tmp2, tmp3, tmp0, tmp1);
149 } else {
150 SyLong2StrComma(tmp0, bytesDone+fb);
151 SyTime2Str(tmp1, (int)(SyGetTimeD()-stats->sttime));
152 SySize2Str(fmt, (int64_t)stats->bps);
153 sprintf(str, "[%s] [SPD:%s] TIME:%s", tmp0, fmt, tmp1);
154 fprintf(stderr, "%s", str);
156 if (isatty(fileno(stdout)) && doRXVT == SY_TRUE) {
157 fprintf(stdout, "\x1b]2;%s\7", str);
158 fflush(stdout);
160 #ifdef ANSI_TTY
161 fprintf(stderr, "\x1b[K");
162 #endif
163 #ifdef DEC_TTY
164 fputs("\x1b[?7h", stderr);
165 #endif
166 if (done == SY_TRUE) fputs("\n", stderr);
167 else if (cfgProgressWriteCR == SY_TRUE) fprintf(stderr, "\n");
168 fflush(stderr);
172 static void SyPrintStr (void *udata, TSyMsgType msgtype, const char *msg) {
173 #ifdef LINUX_TTY
174 int wdt;
175 #endif
176 static const char *ltr = "NMWE?";
177 if (cfgNoOutput) return;
178 if (cfgQuiet && msgtype < SY_MSG_MSG) return;
179 fflush(stderr);
180 #ifndef LINUX_TTY
181 #ifdef DEC_TTY
182 fputs("\x1b[?7l", stderr);
183 #endif
184 #else
185 wdt = GetTTYWidth();
186 if (wdt < 0) wdt = strlen(msg)+16;
187 if (wdt < 2) return;
188 #endif
189 if (msgtype < SY_MSG_NOTICE || msgtype > SY_MSG_ERROR) msgtype = SY_MSG_ERROR+1;
190 fputc(ltr[msgtype], stderr);
191 #ifdef LINUX_TTY
192 if (wdt > 4) {
193 fputs(": ", stderr);
194 wdt -= 4;
195 if (wdt >= strlen(msg)) fputs(msg, stderr);
196 else while (wdt--) fputc(*(msg++), stderr);
198 #else
199 fputs(": ", stderr);
200 fputs(msg, stderr);
201 #endif
202 #ifdef ANSI_TTY
203 fputs("\x1b[K", stderr);
204 #endif
205 #ifndef LINUX_TTY
206 #ifdef DEC_TTY
207 fputs("\x1b[?7h", stderr);
208 #endif
209 #endif
210 fputs("\n", stderr);
211 fflush(stderr);
212 if (isatty(fileno(stdout)) && doRXVT == SY_TRUE && msgtype > SY_MSG_NOTICE) {
213 fprintf(stdout, "\x1b]2;%s\7", msg);
214 fflush(stdout);
219 static void SyDefaultCfg (TSyCfg *cfg) {
220 SyCfgAddIntKey(cfg, "reconnect_delay", "number of seconds to wait before reconnecting to server", &cfgReconnectDelay);
221 SyCfgAddIntKey(cfg, "reconnect_attempts", "number of reconnection attempts (0: do not reconnect)", &cfgReconnectAttempts);
222 SyCfgAddIntKey(cfg, "io_timeout", "i/o timeout in seconds", &cfgTimeout);
223 SyCfgAddIntKey(cfg, "save_state_interval", "save state every x seconds (0: disable)", &cfgSaveInterval);
224 cfgDefaultName = SyStrNew("index.html", -1);
225 SyCfgAddStrKey(cfg, "default_file_name", "default file name for http", &cfgDefaultName);
226 SyCfgAddIntKey(cfg, "max_redirects", "maximum number of redirects/symlinks", &cfgMaxRedirects);
227 SyCfgAddIntKey(cfg, "init_buffer_size", "initial buffer size", &cfgBufferSize);
228 SyCfgAddIntKey(cfg, "max_buffer_size", "maximum amount of bytes read buffer can grow to", &cfgMaxBufferSize);
229 SyCfgAddBoolKey(cfg, "quiet", "tan: do not show notices", &cfgQuiet);
230 SyCfgAddBoolKey(cfg, "no_output", "tan: disable any output", &cfgNoOutput);
231 SyCfgAddBoolKey(cfg, "no_indicator", "tan: hide download indicator", &cfgNoIndicator);
233 cfgIFace = SyStrNew("", -1);
234 SyCfgAddStrKey(cfg, "interface", "net interface to use for connection (name or IP)", &cfgIFace);
236 cfgProxyHTTP = SyStrNew(getenv("http_proxy"), -1);
237 SyCfgAddStrKey(cfg, "http_proxy", "proxy for http connections", &cfgProxyHTTP);
238 cfgProxyFTP = SyStrNew(getenv("ftp_proxy"), -1);
239 SyCfgAddStrKey(cfg, "ftp_proxy", "proxy for ftp connections", &cfgProxyFTP);
240 SyCfgAddBoolKey(cfg, "use_http_proxy", "use http proxy (if specified)?", &cfgUseProxyHTTP);
241 SyCfgAddBoolKey(cfg, "use_ftp_proxy", "use ftp proxy (if specified)?", &cfgUseProxyFTP);
242 SyCfgAddBoolKey(cfg, "ftp_proxy_use_connect", "use CONNECT method for proxied ftp connections", &cfgFTPUseConnect);
244 SyCfgAddIntKey(cfg, "referer_type", "set referer type (0:none; 1:orig URL; 2:follow URL; 3:user)", &cfgRefererType);
245 cfgRefererStr = SyStrNew(NULL, -1);
246 SyCfgAddStrKey(cfg, "referer_str", "referer for mode 3", &cfgRefererStr);
247 SyCfgAddBoolKey(cfg, "decode_ftp_url", "decode URLs for FTP", &cfgDecodeFTPURL);
249 cfgUAStr = SyStrNew(SYREN_DEFAULT_USER_AGENT, -1);
250 SyCfgAddStrKey(cfg, "ua_str", "user agent (set to empty string if no UA should be sent)", &cfgUAStr);
255 TSyResult SySaveInt (int fd, int64_t v, int size) {
256 uint8_t b;
258 if (fd < 0 || size < 1 || size > 8) return SY_ERROR;
259 while (size--) {
260 b = (v>>(size*8))&0xff;
261 if (SyWriteFile(fd, &b, 1) != SY_OK) return SY_ERROR;
264 return SY_OK;
268 TSyResult SyLoadInt (int fd, int64_t *v, int size) {
269 uint8_t b; int64_t res = 0;
271 if (v) *v = 0;
272 if (!fd || size < 1 || size > 8) return SY_ERROR;
273 while (size--) {
274 if (SyReadFile(fd, &b, 1) != SY_OK) return SY_ERROR;
275 res = (res<<8)+b;
277 if (v) *v = res;
279 return SY_OK;
283 TSyResult SySaveStr (int fd, const char *v) {
284 int len = 0;
286 if (!fd) return SY_ERROR;
287 if (v) len = strlen(v);
288 if (len > 65535) return SY_ERROR;
289 if (SySaveInt(fd, len, 2) != SY_OK) return SY_ERROR;
290 if (len) { if (SyWriteFile(fd, v, len) != SY_OK) return SY_ERROR; }
292 return SY_OK;
296 char *SyLoadStr (int fd) {
297 char *v;
298 int64_t len = 0;
300 if (!fd) return NULL;
301 if (SyLoadInt(fd, &len, 2) != SY_OK) return NULL;
302 if (!len) return SyStrNewEmpty();
303 v = calloc(1, len+8); if (!v) return NULL;
304 if (SyReadFile(fd, v, len) != SY_OK) { free(v); return NULL; }
306 return v;
310 static const char *SF_STR = "SYREN STATE FILE v6\r\n\x1a";
311 TSyResult SySaveState (const char *destfile, TSyState *state) {
312 TSyResult res = SY_ERROR;
313 TSyURL *url;
314 char *s, *t;
315 int fd;
317 if (!state) return SY_ERROR;
318 url = state->url;
319 s = SySPrintf("%s://%s:%s@%s:%i%s%s%s%s",
320 url->protostr, url->user, url->pass, url->host, url->port,
321 url->dir, url->file, url->query, url->anchor);
322 if (!s) return SY_ERROR;
323 fd = SyOpenFile(destfile, SY_FMODE_WRITE, SY_TRUE);
324 if (fd >= 0) {
325 while (1) {
326 /*if (SySaveStr(fd, "SYREN STATE FILE v5") != SY_OK) break;*/
327 if (SyWriteFile(fd, SF_STR, strlen(SF_STR)) != SY_OK) break;
328 if (SySaveInt(fd, state->fileSize, 8) != SY_OK) break;
329 if (SySaveInt(fd, state->currentByte+state->firstByte, 8) != SY_OK) break;
330 if (SySaveInt(fd, state->lastByte, 8) != SY_OK) break;
331 if (SySaveStr(fd, s) != SY_OK) break;
332 t = strrchr(oOutFileName, '/'); if (!t) t = oOutFileName; else t++;
333 if (SySaveStr(fd, t) != SY_OK) break;
334 if (SySaveInt(fd, cfgRefererType, 1) != SY_OK) break;
335 if (SySaveStr(fd, cfgRefererStr) != SY_OK) break;
336 if (SySaveStr(fd, oPostData) != SY_OK) break;
337 res = SY_OK; break;
339 SyCloseFile(fd);
341 if (res != SY_OK) SyDeleteFile(destfile);
342 free(s);
344 return res;
348 char *SyLoadStateV5 (int fd, TSyState *state) {
349 char *destfile = NULL, *s = NULL;
350 int64_t i;
352 while (1) {
353 s = SyLoadStr(fd); if (!s) break;
354 if (strcmp(s, "SYREN STATE FILE v5")) break;
355 if (SyLoadInt(fd, &state->fileSize, 8) != SY_OK) break;
356 if (SyLoadInt(fd, &state->firstByte, 8) != SY_OK) break;
357 if (oIgnoreStatePos != SY_FALSE) state->firstByte = 0;
358 if (state->firstByte < 0 || (state->fileSize >= 0 && state->firstByte > state->fileSize)) break;
359 if (SyLoadInt(fd, &state->lastByte, 8) != SY_OK) break;
360 if (state->lastByte < -1 || (state->lastByte >= 0 && state->firstByte > state->lastByte)) break;
361 free(s); s = SyLoadStr(fd); if (!s) break;
362 if (oURL) free(oURL); oURL = s;
363 s = SyLoadStr(fd); if (!s) break;
364 destfile = s; s = NULL;
365 if (SyLoadInt(fd, &i, 1) != SY_OK) break;
366 cfgRefererType = (int)i;
367 s = SyLoadStr(fd); if (!s) break;
368 if (cfgRefererStr) free(cfgRefererStr);
369 cfgRefererStr = s;
370 s = SyLoadStr(fd); if (!s) { free(destfile); destfile = NULL; break; }
371 if (state->postData) free(state->postData);
372 state->postData = s; s = NULL;
373 break;
375 if (s) free(s);
377 return destfile;
381 char *SyLoadStateV6 (int fd, TSyState *state) {
382 char *destfile = NULL, *s = NULL, buf[128];
383 int64_t i;
384 int l = strlen(SF_STR);
386 while (1) {
387 memset(buf, 0, sizeof(buf));
388 if (SyReadFile(fd, buf, l) != SY_OK) break;
389 if (strcmp(buf, SF_STR)) break;
390 if (SyLoadInt(fd, &state->fileSize, 8) != SY_OK) break;
391 if (SyLoadInt(fd, &state->firstByte, 8) != SY_OK) break;
392 if (oIgnoreStatePos != SY_FALSE) state->firstByte = 0;
393 if (state->firstByte < 0 || (state->fileSize >= 0 && state->firstByte > state->fileSize)) break;
394 if (SyLoadInt(fd, &state->lastByte, 8) != SY_OK) break;
395 if (state->lastByte < -1 || (state->lastByte >= 0 && state->firstByte > state->lastByte)) break;
396 free(s); s = SyLoadStr(fd); if (!s) break;
397 if (oURL) free(oURL); oURL = s;
398 s = SyLoadStr(fd); if (!s) break;
399 destfile = s; s = NULL;
400 if (SyLoadInt(fd, &i, 1) != SY_OK) break;
401 cfgRefererType = (int)i;
402 s = SyLoadStr(fd); if (!s) break;
403 if (cfgRefererStr) free(cfgRefererStr);
404 cfgRefererStr = s;
405 s = SyLoadStr(fd); if (!s) { free(destfile); destfile = NULL; break; }
406 if (state->postData) free(state->postData);
407 state->postData = s; s = NULL;
408 break;
410 if (s) free(s);
412 return destfile;
416 /* return destfile */
417 char *SyLoadState (const char *statefile, TSyState *state) {
418 char *destfile = NULL;
419 int fd;
420 uint8_t b;
422 fd = SyOpenFile(statefile, SY_FMODE_READ, SY_FALSE);
423 if (fd < 0) return NULL;
424 if (SyReadFile(fd, &b, 1) == SY_OK) {
425 if (SySeekFile(fd, 0) == SY_OK) {
426 /*fprintf(stderr, "state v%i\n", b?6:5);*/
427 if (!b) destfile = SyLoadStateV5(fd, state);
428 else destfile = SyLoadStateV6(fd, state);
431 SyCloseFile(fd);
433 return destfile;
437 void DumpState (TSyState *state) {
438 char t0[32], t1[32], t2[32];
439 SyLong2StrComma(t0, state->fileSize);
440 SyLong2StrComma(t1, state->firstByte);
441 SyLong2StrComma(t2, state->lastByte);
442 printf("state dump\n==========\nURL: %s\nfile name: %s\n"
443 "file size: %s\nfirst byte: %s\nlast byte: %s\n",
444 oURL, oOutFileName, t0, t1, t2
449 static void ShowHelp (void) {
450 fprintf(stderr, "%s",
451 "usage: syren [options] url [+url] [+url] [@urllistfile]\n"
452 "options:\n" \
453 " -h this help\n"
454 " -h all show 'long' configuration options\n"
455 " -h <longopt> show 'long' configuration option description\n"
456 " -o filename output to specified file\n" \
457 " -O filename output to specified file (same as '-o')\n" \
458 " -r filename resume download (do not use saved state, use file)\n" \
459 " -s filename restore state\n"
460 " -S filename redownload file using state (ignore starting position, recreate)\n"
461 " -D filename dump state file and exit\n"
462 " -k keep state file\n"
463 " -P <str|@fn> make POST request\n"
464 " -c <str|@fn> send cookies\n"
465 " -C filename dump cookies\n"
466 " -H <str|@fn> additional headers\n"
467 " -i download server reply and stop\n"
468 " -I name_or_ip use specified net interface (\"-\" any)\n"
469 " -X do not use proxies\n"
470 " -A don't preallocate disk space\n"
471 " -T turn off terminal window title changing\n"
472 " -Z write CR after progress string\n"
473 " -w generate .syrenrc file with the current config\n"
474 " -N don't write state file\n"
475 " -F force retry on any download error\n"
476 " -e encode URL\n"
477 " -E decode URL\n"
478 " -q be quiet\n"
479 " -Q be VERY quiet\n"
480 " -V version\n"
485 static char *GetStrOpt (TSyPrintStr *prs, int argc, char *argv[], int *f, const char *optName,
486 char **value, TSyBool allowEmpty, TSyBool exclusive) {
487 char *res/*, *s*/;
488 if (*f >= argc || !argv[*f]) { SyMessage(prs, SY_MSG_ERROR, "no value for '%s'", optName); return NULL; }
489 if (exclusive == SY_TRUE && *value) { SyMessage(prs, SY_MSG_ERROR, "duplicate '%s'", optName); return NULL; }
490 res = SyStrNew(argv[*f], -1); (*f)++;
491 if (!res) { SyMessage(prs, SY_MSG_ERROR, "memory error"); return NULL; }
492 if (allowEmpty != SY_TRUE && !res[0]) {
493 free(res);
494 SyMessage(prs, SY_MSG_ERROR, "empty value for '%s'", optName);
495 return NULL;
497 if (*value) free(*value);
498 *value = res;
500 return res;
504 static TSyResult GetStrOptWithAt (TSyPrintStr *prs, int argc, char *argv[], int *f, const char *optName,
505 char **value, TSyBool allowEmpty, TSyBool exclusive) {
506 char *res = GetStrOpt(prs, argc, argv, f, optName, value, allowEmpty, exclusive);
507 if (!res) return SY_ERROR;
508 if (*res == '@') {
509 res = SyLoadWholeFile(res+1);
510 if (!res) { SyMessage(prs, SY_MSG_ERROR, "can't load data from '%s'", (*value)+1); return SY_ERROR; }
511 free(*value); *value = res;
514 return SY_OK;
518 static void SyAddURLFromFile (const char *fname) {
519 char *fl, *s, *e;
521 fl = SyLoadWholeFile(fname);
522 if (!fl) return;
523 s = fl; while (*s) {
524 e = s; while (*e && *e != '\n') e++;
525 if (*e) *e++ = '\0';
526 SyStrTrim(s);
527 if (*s) {
528 SyKVListDelete(oURLList, s);
529 SyKVListSet(oURLList, s, "", NULL);
531 s = e;
533 free(fl);
537 static TSyResult ParseCmdLine (TSyPrintStr *prs, TSyCfg *cfg, int argc, char *argv[]) {
538 int f, nomoreopt = 0;
539 char *s, *t, *sopt, soptN[2];
540 TSyKVListItem *key;
542 f = 1; while (f < argc) {
543 s = argv[f++];
544 if (!s) break; if (!(*s)) continue;
545 if (!nomoreopt && *s == '-') {
546 if (s[1] == '-') {
547 /* long option (config) */
548 if (!s[3]) nomoreopt = 1;
549 else {
550 s += 2;
551 if (SyCfgSet(cfg, s) != SY_OK) { SyMessage(prs, SY_MSG_ERROR, "invalid option: %s", (s-2)); return SY_ERROR; }
553 } else {
554 sopt = s;
555 while (*(++sopt)) {
556 soptN[0] = *sopt; soptN[1] = '\0';
557 switch (*sopt) {
558 case 'V':
559 printf("%s", SYREN_VDHEADER_STRING);
560 return SY_ERROR;
561 case 'e': uEncodeDecode = 1; break;
562 case 'E': uEncodeDecode = -1; break;
563 case 'w': writeCfg = SY_TRUE; break;
564 case 'X': cfgUseProxyHTTP = cfgUseProxyFTP = SY_FALSE; break;
565 case 'T': doRXVT = SY_FALSE; break;
566 case 'q': cfgQuiet = SY_TRUE; break;
567 case 'Q': cfgQuiet = cfgNoOutput = SY_TRUE; break;
568 case 'i': replyOnly = SY_TRUE; break;
569 case 'N': oNoStateFile = SY_TRUE; break;
570 case 'F': oForceRetry = SY_TRUE; break;
571 case 'k': oCanRemoveState = SY_FALSE; break;
572 case 'Z': cfgProgressWriteCR = SY_TRUE; break;
573 case 'A': oPreallocSpace = SY_FALSE; break;
574 case 'I':
575 if (!argv[f]) {
576 SyMessage(prs, SY_MSG_ERROR, "no argument for -I");
577 return SY_ERROR;
579 SyStrFree(cfgIFace);
580 cfgIFace = SyStrDup(argv[f++]);
581 /*SyStrTrim(cfgIFace);*/
582 /*if (!cfgIFace[0] || !strcmp(cfgIFace, "-")) { SyStrFree(cfgIFace); cfgIFace = NULL; }*/
583 break;
584 case 'o': case 'O': case 'r':
585 if (*sopt == 'r') oResume = SY_TRUE;
586 s = GetStrOpt(prs, argc, argv, &f, soptN, &oOutFileName, SY_FALSE, SY_TRUE);
587 if (!s) return SY_ERROR;
588 oCanRename = SY_FALSE;
589 break;
590 case 's': case 'S': case 'D':
591 if (*sopt == 'S') oIgnoreStatePos = SY_TRUE;
592 else if (*sopt == 'D') dumpState = SY_TRUE;
593 s = GetStrOpt(prs, argc, argv, &f, soptN, &oStateFileName, SY_FALSE, SY_TRUE);
594 if (!s) return SY_ERROR;
595 oCanRename = SY_FALSE;
596 break;
597 case 'P':
598 if (GetStrOptWithAt(prs, argc, argv, &f, soptN, &oPostData, SY_FALSE, SY_TRUE) != SY_OK) return SY_ERROR;
599 break;
600 case 'c':
601 if (GetStrOptWithAt(prs, argc, argv, &f, soptN, &oSendCookies, SY_FALSE, SY_TRUE) != SY_OK) return SY_ERROR;
602 break;
603 case 'C':
604 if (!GetStrOpt(prs, argc, argv, &f, soptN, &oCookieDump, SY_FALSE, SY_TRUE)) return SY_ERROR;
605 break;
606 case 'H':
607 if (GetStrOptWithAt(prs, argc, argv, &f, soptN, &oAddonHeaders, SY_FALSE, SY_TRUE)) return SY_ERROR;
608 break;
609 case 'h':
610 fprintf(stderr, "%s\n", SYREN_VERSION_DATETIME_STRING);
611 key = cfg->opts->first;
612 if (f < argc && !strcmp(argv[f], "all")) {
613 fprintf(stderr, "long options:\n");
614 while (key) { fprintf(stderr, " --%s\n", key->key); key = key->next; }
615 } else {
616 if (f < argc && argv[f]) {
617 while (key) {
618 if (!strcasecmp(key->key, argv[f])) {
619 fprintf(stderr, "--%s %s\n %s\n", key->key,
620 key->uidata==SY_CI_STRING?"string":(key->uidata==SY_CI_INT?"integer":(key->uidata==SY_CI_BOOL?"boolean":"?")),
621 key->ustr);
622 break;
624 key = key->next;
626 } else key = NULL;
627 if (!key) ShowHelp();
629 return SY_ERROR;
630 default:
631 SyMessage(prs, SY_MSG_ERROR, "unknown option: %s", soptN);
632 return SY_ERROR;
633 } /* switch */
634 } /* while */
635 } /* if */
636 } else {
637 /* new URL */
638 t = SyStrNew(s, -1);
639 if (!t) { SyMessage(prs, SY_MSG_ERROR, "memory error"); return SY_ERROR; }
640 SyStrTrim(t);
641 if (*t == '@') SyAddURLFromFile(t+1);
642 else if (*t) {
643 if (t[0] == '+') { strcpy(t, &(t[1])); SyStrTrim(t); }
644 if (*t) {
645 SyKVListDelete(oURLList, t);
646 SyKVListSet(oURLList, t, "", NULL);
649 SyStrFree(t);
652 if (oResume && oStateFileName) { SyMessage(prs, SY_MSG_ERROR, "'-r'/'-s' conflict"); return SY_ERROR; }
654 return SY_OK;
658 #ifdef SYREN_USE_SCRIPT
659 TSyHdrs *scHdrs = NULL;
660 #endif
662 TSyResult SyDrvAddHeaders (TSyState *state, TSyHdrs *hdrs) {
663 char *s = NULL, *t, *cs;
664 TSyURL *url = state->url;
665 char port[32];
667 if (cfgRefererType) {
668 switch (cfgRefererType) {
669 case 1: s = SyStrNew(oURL, -1); break;
670 case 2:
671 if (url->proto == SY_PROTO_FTP && url->port != 80) sprintf(port, ":%i", url->port); else port[0] = '\0';
672 s = SySPrintf("%s://%s%s%s%s%s%s", url->protostr, url->host, port,
673 url->dir, url->file, url->query, url->anchor);
674 break;
675 default: s = SyStrNew(cfgRefererStr, -1); break;
677 if (!s) { SyMessage(state->pfn, SY_MSG_ERROR, "memory error"); return SY_ERROR; }
678 t = SySPrintf("Referer: %s", s); free(s);
679 if (!t) { SyMessage(state->pfn, SY_MSG_ERROR, "memory error"); return SY_ERROR; }
680 if (SyHdrAddLine(hdrs, t) != SY_OK) { free(t); SyMessage(state->pfn, SY_MSG_ERROR, "hdr error"); return SY_ERROR; }
681 free(t);
683 if (oAddonHeaders && *oAddonHeaders) {
684 s = oAddonHeaders;
685 while (*s) {
686 t = s; while (*t && *t != '\n') t++;
687 cs = SyStrNew(s, t-s); if (!cs) return SY_ERROR;
688 SyStrTrim(cs);
689 if (*cs) {
690 if (SyHdrAddLine(hdrs, cs) != SY_OK) { free(cs); SyMessage(state->pfn, SY_MSG_ERROR, "hdr error"); return SY_ERROR; }
692 free(cs); if (!s[0]) break;
693 s = t; if (*s) s++; /* don't skip latest 0 */
696 if (oSendCookies && *oSendCookies) {
697 s = oSendCookies;
698 while (*s) {
699 t = s; while (*t && *t != '\n') t++;
700 cs = SyStrNew(s, t-s); if (!cs) return SY_ERROR;
701 SyStrTrim(cs);
702 if (*cs) {
703 if (SyHdrSetCookie(hdrs, cs) != SY_OK) { free(cs); return SY_ERROR; }
705 free(cs); if (!s[0]) break;
706 s = t; if (*s) s++; /* don't skip latest 0 */
709 #ifdef SYREN_USE_SCRIPT
710 scHdrs = hdrs;
711 SyScriptCallback("Event.AddHeaders", state->pfn);
712 scHdrs = NULL;
713 if (scAbort == SY_TRUE) state->breakNow = SY_TRUE;
714 #endif
716 return SY_OK;
720 TSyResult SyDrvGotHeaders (TSyState *state, TSyHdrs *hdrs) {
721 TSyResult res = SY_OK;
722 TSyKVList *cc; TSyKVListItem *item;
723 int fd;
724 static char *crlf = "\r\n";
726 #ifdef SYREN_USE_SCRIPT
727 scHdrs = hdrs;
728 SyScriptCallback("Event.GotHeaders", state->pfn);
729 scHdrs = NULL;
730 if (scAbort == SY_TRUE) state->breakNow = SY_TRUE;
731 #endif
732 if (oCookieDump && *oCookieDump) {
733 cc = SyHdrFindCookieList(hdrs);
734 if (cc && cc->count) {
735 if (strcmp(oCookieDump, "-")) {
736 fd = SyOpenFile(oCookieDump, SY_FMODE_WRITE, SY_TRUE);
737 if (fd < 0) { SyMessage(state->pfn, SY_MSG_ERROR, "can't dump cookies to '%s'", oCookieDump); return SY_ERROR; }
738 } else fd = 0;
739 item = cc->first;
740 while (item) {
741 if (SyWriteFile(fd, item->value, strlen(item->value)) != SY_OK) { res = SY_ERROR; break; }
742 if (SyWriteFile(fd, crlf, strlen(crlf)) != SY_OK) { res = SY_ERROR; break; }
743 item = item->next;
745 if (fd) SyCloseFile(fd);
749 return res;
753 TSyResult SyXPrealloc (int fd, int64_t fileSize) {
754 char *buf;
755 int bufSz, wr;
757 if (fileSize < 1) return SY_ERROR;
759 bufSz = 256*1024;
760 if (bufSz > fileSize) bufSz = fileSize;
761 buf = calloc(1, bufSz);
762 if (!buf) return SY_ERROR;
764 while (fileSize > 0) {
765 wr = fileSize>bufSz?bufSz:fileSize;
766 if (SyWriteFile(fd, buf, wr) != SY_OK) return SY_ERROR;
767 fileSize -= wr;
770 return SySeekFile(fd, 0);
774 TSyResult SyDrvOpenFile (TSyState *state) {
775 int fd, sto = 0; int64_t len = -1;
776 char *s, tmp[32];
778 if (replyOnly != SY_FALSE) return SY_OK;
780 if (oCanRename) {
781 if (!oOutFileName || !oOutFileName[0]) {
782 if (oOutFileName) free(oOutFileName);
783 if (state->httpFName && *state->httpFName) oOutFileName = SyStrDup(state->httpFName);
784 else oOutFileName = SyStrDup(state->url->file);
785 #ifdef SYREN_USE_SCRIPT
786 SyScriptCallback("Event.CheckFileName", state->pfn);
787 #endif
788 /*if (scAbort == SY_TRUE) goto done;*/
789 if (!oOutFileName || !oOutFileName[0]) {
790 if (oOutFileName) free(oOutFileName);
791 oOutFileName = SyStrNew((cfgDefaultName&&*cfgDefaultName)?cfgDefaultName:"index.html", -1);
793 if (!oOutFileName) { SyMessage(state->pfn, SY_MSG_ERROR, "memory error"); return SY_ERROR; }
795 /* check for duplicates */
796 fd = SyOpenFile(oOutFileName, SY_FMODE_READ, SY_FALSE);
797 if (fd >= 0) {
798 int f = -1; s = NULL;
799 do { f++;
800 SyCloseFile(fd); if (s) free(s);
801 s = SySPrintf("%s.%i", oOutFileName, f);
802 if (!s) { SyMessage(state->pfn, SY_MSG_ERROR, "memory error"); return SY_ERROR; }
803 fd = SyOpenFile(s, SY_FMODE_READ, SY_FALSE);
804 } while (fd >= 0);
805 free(oOutFileName); oOutFileName = s;
808 /* open/create file */
809 if (!oOutFileName || !oOutFileName[0]) { SyMessage(state->pfn, SY_MSG_ERROR, "invalid file name"); return SY_ERROR; }
810 if (state->firstByte > 0) {
811 if (!strcmp(oOutFileName, "-")) { SyMessage(state->pfn, SY_MSG_ERROR, "can't resume stdout"); return SY_ERROR; }
812 fd = SyOpenFile(oOutFileName, SY_FMODE_WRITE, SY_FALSE);
813 if (fd >= 0) {
814 len = SyFileSize(fd);
815 if (len < 0) { SyCloseFile(fd); SyMessage(state->pfn, SY_MSG_ERROR, "can't resume: file error"); return SY_ERROR; }
816 if (len < state->firstByte) { SyCloseFile(fd); SyMessage(state->pfn, SY_MSG_ERROR, "can't resume: corrupted file"); return SY_ERROR; }
817 if (SySeekFile(fd, state->firstByte) != SY_OK) {
818 SyCloseFile(fd);
819 SyMessage(state->pfn, SY_MSG_ERROR, "can't resume: file error");
820 return SY_ERROR;
822 SyLong2StrComma(tmp, state->firstByte);
823 SyMessage(state->pfn, SY_MSG_MSG, "resuming %s at %s", oOutFileName, tmp);
825 } else {
826 if (!strcmp(oOutFileName, "-")) { fd = 0; sto = 1; oNoStateFile = SY_TRUE; }
827 else {
828 SyDeleteFile(oOutFileName);
829 fd = SyOpenFile(oOutFileName, SY_FMODE_WRITE, SY_TRUE);
830 /* preallocating w/o state file means no resumes, so disable it */
831 if (fd > 0 && oPreallocSpace != SY_FALSE && state->fileSize > 0 && oNoStateFile != SY_TRUE) {
832 SyLong2StrComma(tmp, state->fileSize);
833 SyMessage(state->pfn, SY_MSG_MSG, "preallocating %s bytes", tmp);
834 if (SyXPrealloc(fd, state->fileSize) != SY_OK) {
835 SyMessage(state->pfn, SY_MSG_ERROR, "preallocating failed");
836 SyCloseFile(fd);
837 SyDeleteFile(oOutFileName);
842 if (fd < 0) { SyMessage(state->pfn, SY_MSG_ERROR, "can't open file %s", oOutFileName); return SY_ERROR; }
843 if (state->firstByte <= 0 || sto) SyMessage(state->pfn, SY_MSG_MSG, "writing to %s", sto?"stdout":oOutFileName);
844 state->udatai = sto?-2:fd;
845 if (!oStateFileName || !oStateFileName[0]) {
846 if (oStateFileName) free(oStateFileName);
847 oStateFileName = SySPrintf("%s.syren", oOutFileName);
848 if (!oStateFileName) SyMessage(state->pfn, SY_MSG_WARNING, "can't create state file name");
849 else if (oNoStateFile != SY_TRUE) SySaveState(oStateFileName, state);
852 return SY_OK;
856 TSyResult SyDrvCloseFile (TSyState *state) {
857 if (replyOnly != SY_FALSE) return SY_OK;
858 if (state->udatai >= 0) {
859 SyMessage(state->pfn, SY_MSG_MSG, "closing %s%s", oOutFileName,
860 (state->error==SY_FALSE?"":" (incomplete file)"));
861 SyCloseFile(state->udatai);
862 state->udatai = -1;
863 if (oStateFileName && *oStateFileName && oNoStateFile != SY_TRUE) SySaveState(oStateFileName, state);
866 return SY_OK;
870 TSyResult SyDrvWriteFile (TSyState *state, void *buf, int bufSize) {
871 int fd;
873 if (replyOnly != SY_FALSE) return SY_OK;
874 fd = state->udatai; if (fd == -2) fd = 1; /* stdout */
875 if (fd >= 0) {
876 if (SyWriteFile(fd, buf, bufSize) != SY_OK) return SY_ERROR;
878 if (oStateFileName && *oStateFileName && oNoStateFile != SY_TRUE && state->udatai >= 0) {
879 if (cfgSaveInterval >= 1.0 && SyGetTimeD()-lastSSave >= cfgSaveInterval) {
880 SySaveState(oStateFileName, state);
881 lastSSave = SyGetTimeD();
882 fsync(state->udatai);
886 return SY_OK;
890 TSyResult SyDrvNoResume (TSyState *state) {
891 if (replyOnly != SY_FALSE) {
892 SyMessage(state->pfn, SY_MSG_WARNING, "can't resume");
893 return SY_OK;
895 if (state->udatai >= 0) {
896 if (state->udatai) SyCloseFile(state->udatai);
897 SyDeleteFile(oOutFileName);
898 state->udatai = SyOpenFile(oOutFileName, SY_FMODE_WRITE, SY_TRUE);
899 if (state->udatai < 0) {
900 SyMessage(state->pfn, SY_MSG_WARNING, "can't create file %s", oOutFileName);
901 return SY_ERROR;
904 SyMessage(state->pfn, SY_MSG_WARNING, "can't resume file %s, file truncated", oOutFileName);
906 return SY_OK;
910 TSyResult CheckResume (TSyState *state) {
911 int fd; int64_t len;
913 SyMessage(state->pfn, SY_MSG_NOTICE, "checking file for resuming");
914 if (!strcmp(oOutFileName, "-")) { SyMessage(state->pfn, SY_MSG_ERROR, "can't resume stdout"); return SY_ERROR; }
915 fd = SyOpenFile(oOutFileName, SY_FMODE_WRITE, SY_FALSE);
916 if (fd < 0) { SyMessage(state->pfn, SY_MSG_ERROR, "resume: can't open file %s", oOutFileName); return SY_ERROR; }
917 len = SyFileSize(fd);
918 SyCloseFile(fd);
919 if (len < 0) { SyMessage(state->pfn, SY_MSG_ERROR, "can't determine file size for %s", oOutFileName); return SY_ERROR; }
920 state->firstByte = len;
921 SyMessage(state->pfn, SY_MSG_NOTICE, "resume check passed");
923 return SY_OK;
927 TSyState *curState = NULL;
929 void BreakSignal (int signo) {
930 lastSSave = 0;
931 if (curState) curState->breakNow = SY_TRUE;
935 void SaveStateSignal (int signo) {
936 /*fprintf(stderr, "\nsave state\n");*/
937 lastSSave = 0;
941 int main (int argc, char *argv[]) {
942 int mainres = 1;
943 TSyPrintStr prs;
944 TSyCfg *cfg;
945 char *s, *t;
946 TSyResult res;
947 int retCnt;
948 int userBreak = 0;
950 prs.print = &SyPrintStr; prs.udata = NULL;
952 t = getenv("TERM");
953 if (t && (!strcmp(t, "rxvt") || !strcmp(t, "mrxvt") || !strcmp(t, "xterm") || !strcmp(t, "eterm"))) doRXVT = SY_TRUE;
955 state = SyNew();
956 if (!state) { SyMessage(&prs, SY_MSG_ERROR, "memory error"); }
957 prs.udata = (void *)state;
959 cfg = SyCfgNew();
960 if (!cfg) { SyMessage(&prs, SY_MSG_ERROR, "memory error"); }
961 SyDefaultCfg(cfg);
962 SyCfgLoad(cfg, "/etc/syren/.syrenrc", &prs);
963 t = getenv("HOME");
964 if (t != NULL) {
965 s = SySPrintf("%s/.syren/.syrenrc", t);
966 SyCfgLoad(cfg, s, &prs);
967 free(s);
968 s = SySPrintf("%s/.syrenrc", t);
969 SyCfgLoad(cfg, s, &prs);
970 free(s);
972 SyCfgLoad(cfg, ".syrenrc", &prs);
974 oURLList = SyKVListNew();
975 if (!oURLList) goto done;
976 oURLList->casesens = 1;
978 if (ParseCmdLine(&prs, cfg, argc, argv) != SY_OK) goto done;
980 SyMessage(&prs, SY_MSG_MSG, "%s", SYREN_VERSION_DATETIME_STRING);
982 #ifdef SYREN_USE_SCRIPT
983 SyScriptInit(&prs);
984 SyScriptLoadInit(argv[0], &prs);
985 #endif
987 if (!oStateFileName) {
988 if (!oURLList->first) { SyMessage(&prs, SY_MSG_ERROR, "URL?"); goto done; }
991 if (cfgMaxBufferSize < 1) { SyMessage(&prs, SY_MSG_ERROR, "buffer size too small"); goto done; }
992 if (cfgTimeout < 1) { SyMessage(&prs, SY_MSG_ERROR, "timeout too small"); goto done; }
994 if (writeCfg == SY_TRUE) {
995 SyMessage(&prs, SY_MSG_MSG, "generating sample config");
996 SyCfgSave(cfg, ".syrenrc", &prs);
997 mainres = 0;
998 goto done;
1001 if (SySocketInit() != SY_OK) goto done;
1003 state->udatai = -1;
1005 state->ftpUseConnect = cfgFTPUseConnect;
1007 state->pfn = &prs;
1008 state->fnprog = PrintProgress;
1009 state->fnprephdrs = SyDrvAddHeaders;
1010 state->fngothdrs = SyDrvGotHeaders;
1012 state->fnopen = SyDrvOpenFile;
1013 state->fnclose = SyDrvCloseFile;
1014 state->fnwrite = SyDrvWriteFile;
1015 state->fnnoresume = SyDrvNoResume;
1017 state->postData = oPostData; oPostData = NULL;
1019 do {
1020 res = SY_ERROR;
1021 userBreak = 0;
1023 if (!oStateFileName) {
1024 if (!oURLList->first) break;
1025 oURL = SyStrDup(oURLList->first->key);
1026 SyKVListDelete(oURLList, oURLList->first->key);
1027 if (!oURL) {
1028 SyMessage(&prs, SY_MSG_ERROR, "memory error");
1029 goto done;
1031 /*if (!oURL) { SyMessage(&prs, SY_MSG_ERROR, "URL?"); goto done; }*/
1032 } else {
1033 SyKVListClear(oURLList);
1034 SyMessage(&prs, SY_MSG_MSG, "restoring state from file %s", oStateFileName);
1035 if (oStateFileName && *oStateFileName) {
1036 s = SyLoadState(oStateFileName, state);
1037 if (!s || !s[0]) {
1038 if (s) free(s);
1039 SyMessage(&prs, SY_MSG_ERROR, "can't restore state");
1040 goto done;
1042 if (oOutFileName) free(oOutFileName);
1043 oOutFileName = s;
1045 if (dumpState == SY_TRUE) { DumpState(state); goto done; }
1048 if (replyOnly == SY_FALSE && oResume == SY_TRUE && CheckResume(state) != SY_OK) goto done;
1050 #ifdef SYREN_USE_SCRIPT
1052 if (!(state->url = SyURLNew())) {
1053 SyMessage(&prs, SY_MSG_ERROR, "memory error!");
1054 goto done;
1056 if (SyURLParse(state->url, oURL) != SY_OK) {
1057 SyMessage(state->pfn, SY_MSG_ERROR, "invalid URL");
1058 return SY_ERROR;
1060 SyScriptCallback("Event.BeforePrepare", &prs);
1061 s = SyURL2StrEx(state->url, SY_TRUE, SY_FALSE);
1062 if (!s) {
1063 SyMessage(&prs, SY_MSG_ERROR, "memory error!");
1064 goto done;
1066 SyStrFree(oURL); oURL = s; s = NULL;
1067 SyURLFree(state->url); state->url = NULL;
1068 fprintf(stderr, "URL: <%s>\n", oURL);
1070 #endif
1071 if (SyPrepare(state, oURL,
1072 cfgUseProxyHTTP?cfgProxyHTTP:NULL,
1073 cfgUseProxyFTP?cfgProxyFTP:NULL, cfgIFace) != SY_OK) { SyMessage(&prs, SY_MSG_ERROR, "SyPrepare() failed"); goto done; }
1074 #ifdef SYREN_USE_SCRIPT
1075 SyScriptCallback("Event.AfterPrepare", &prs);
1076 if (scAbort == SY_TRUE) goto done;
1077 #endif
1079 if (uEncodeDecode < 0 ||
1080 (cfgDecodeFTPURL == SY_TRUE && state->url->proto == SY_PROTO_FTP)) {
1081 s = SyURLDecode(state->url->dir);
1082 if (!s) { SyMessage(&prs, SY_MSG_ERROR, "SyURLDecode() failed"); goto done; }
1083 SyStrFree(state->url->dir); state->url->dir = s;
1084 s = SyURLDecode(state->url->file);
1085 if (!s) { SyMessage(&prs, SY_MSG_ERROR, "SyURLDecode() failed"); goto done; }
1086 SyStrFree(state->url->file); state->url->file = s;
1087 } else if (uEncodeDecode > 0) {
1088 s = SyURLEncode(state->url->dir);
1089 if (!s) { SyMessage(&prs, SY_MSG_ERROR, "SyURLEncode() failed"); goto done; }
1090 SyStrFree(state->url->dir); state->url->dir = s;
1091 s = SyURLEncode(state->url->file);
1092 if (!s) { SyMessage(&prs, SY_MSG_ERROR, "SyURLEncode() failed"); goto done; }
1093 SyStrFree(state->url->file); state->url->file = s;
1096 signal(SIGHUP, SaveStateSignal);
1097 signal(SIGUSR1, SaveStateSignal);
1099 signal(SIGINT, BreakSignal);
1100 signal(SIGQUIT, BreakSignal);
1101 signal(SIGTERM, BreakSignal);
1103 retCnt = cfgReconnectAttempts;
1105 state->initBufferSize = cfgBufferSize;
1106 state->maxBufferSize = cfgMaxBufferSize;
1107 state->ioTimeout = cfgTimeout;
1108 state->maxRedirects = cfgMaxRedirects;
1109 if (cfgUAStr && *cfgUAStr) {
1110 SyStrFree(state->userAgent);
1111 state->userAgent = SyStrNew(cfgUAStr, -1);
1113 while (1) {
1114 curState = state;
1115 res = SyBegin(state);
1116 if (replyOnly != SY_FALSE) break;
1117 if (res == SY_OK) res = SyRun(state);
1118 curState = NULL;
1119 if (res == SY_OK) break;
1120 if (state->interrupted == SY_TRUE) {
1121 userBreak = 1;
1122 if (oStateFileName && *oStateFileName && state->status == SY_STATUS_DOWNLOADING && oNoStateFile != SY_TRUE) {
1123 SyMessage(&prs, SY_MSG_WARNING, "user break, saving state");
1124 SySaveState(oStateFileName, state);
1125 } else SyMessage(&prs, SY_MSG_WARNING, "user break");
1126 break;
1128 if (retCnt < 1) break;
1129 if (state->status != SY_STATUS_DOWNLOADING && oForceRetry != SY_TRUE) break;
1130 SyMessage(&prs, SY_MSG_ERROR, "download failed");
1131 if (cfgReconnectDelay > 0) {
1132 SyMessage(&prs, SY_MSG_MSG, "waiting %i seconds", cfgReconnectDelay);
1133 SySleep(cfgReconnectDelay);
1135 SyMessage(&prs, SY_MSG_MSG, "restarting");
1136 state->firstByte += state->currentByte; /* start from here */
1137 oCanRename = SY_FALSE;
1138 retCnt--;
1140 #ifdef SYREN_USE_SCRIPT
1141 if (res == SY_OK) SyScriptCallback("Event.DownloadComplete", &prs);
1142 else SyScriptCallback("Event.DownloadFailed", &prs);
1143 if (scAbort == SY_TRUE) goto done;
1144 #endif
1145 SyEnd(state);
1146 if (res == SY_OK) {
1147 SyMessage(&prs, SY_MSG_MSG, "download complete");
1148 if (oCanRemoveState && oStateFileName && *oStateFileName) {
1149 if (SyDeleteFile(oStateFileName) == SY_OK) SyMessage(&prs, SY_MSG_NOTICE, "state file removed");
1151 } else SyMessage(&prs, SY_MSG_ERROR, "download failed");
1153 mainres = (res==SY_OK)?0:1;
1155 done:
1156 SyStrFree(oURL); oURL = NULL;
1157 SyStrFree(oOutFileName); oOutFileName = NULL;
1158 SyStrFree(oStateFileName); oStateFileName = NULL;
1159 oCanRename = SY_TRUE;
1160 } while (!userBreak);
1162 SyFree(state); SyCfgFree(cfg);
1163 if (oURLList) SyKVListFree(oURLList);
1164 if (cfgDefaultName) SyStrFree(cfgDefaultName);
1165 if (cfgIFace) SyStrFree(cfgIFace);
1166 if (cfgProxyHTTP) SyStrFree(cfgProxyHTTP);
1167 if (cfgProxyFTP) SyStrFree(cfgProxyFTP);
1168 if (cfgRefererStr) SyStrFree(cfgRefererStr);
1169 if (cfgUAStr) SyStrFree(cfgUAStr);
1170 SyStrFree(oURL);
1171 SyStrFree(oOutFileName);
1172 SyStrFree(oStateFileName);
1173 if (oPostData) SyStrFree(oPostData);
1174 if (oSendCookies) SyStrFree(oSendCookies);
1175 if (oCookieDump) SyStrFree(oCookieDump);
1176 if (oAddonHeaders) SyStrFree(oAddonHeaders);
1178 #ifdef SYREN_USE_SCRIPT
1179 SyScriptDeinit();
1180 #endif
1182 SySocketShutdown();
1183 return mainres;