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
32 #define USE_NEW_PREALLOC 0
35 #include "syren_common.h"
37 #include "syren_str.h"
38 #include "syren_msg.h"
39 #include "syren_cfg.h"
40 #include "syren_hdrs.h"
41 #include "syren_tcp.h"
42 #include "syren_http.h"
43 #include "syren_proxy.h"
44 #include "syren_ftp.h"
45 #include "syren_dloader.h"
47 #include "syren_script.h"
56 # include <linux/fallocate.h>
60 /* uncomment this to use Linux TTY ioctls */
67 /* uncomment this to use ANSI/VT100 TTY codes */
70 /* uncomment this to use "DEC private" TTY codes (ESC [ ? n)*/
76 cfgNoOutput
= SY_FALSE
,
78 cfgNoIndicator
= SY_FALSE
,
79 cfgUseProxyHTTP
= SY_TRUE
,
80 cfgUseProxyFTP
= SY_TRUE
,
81 cfgDecodeFTPURL
= SY_TRUE
,
82 cfgFTPUseConnect
= SY_FALSE
,
83 cfgProgressWriteCR
= SY_FALSE
,
84 optAnyCode
= SY_FALSE
;
87 cfgMaxBufferSize
= 1024*1024,
89 cfgReconnectDelay
= 10,
90 cfgReconnectAttempts
= 666,
95 *cfgDefaultName
= NULL
,
99 *cfgRefererStr
= NULL
,
103 *oOutFileName
= NULL
, /* -o/-O */
104 *oStateFileName
= NULL
, /* -s */
107 *oSendCookies
= NULL
,
109 *oAddonHeaders
= NULL
;
112 oCanRename
= SY_TRUE
,
113 oCanRemoveState
= SY_TRUE
,
114 oIgnoreStatePos
= SY_FALSE
,
115 oNoStateFile
= SY_FALSE
,
116 oForceRetry
= SY_FALSE
,
118 dumpState
= SY_FALSE
,
119 replyOnly
= SY_FALSE
,
120 oPreallocSpace
= SY_TRUE
;
121 int uEncodeDecode
= 0;
122 TSyBool doRXVT
= SY_FALSE
;
126 TSyKVList
*oURLList
= NULL
;
128 double lastSSave
= 0;
130 TSyState
*state
= NULL
; /* dl state */
131 #ifdef SYREN_USE_SCRIPT
132 TSyBool scAbort
= SY_FALSE
;
136 static int GetTTYWidth (void) {
138 if (!isatty(fileno(stderr
))) return -1;
139 if (ioctl(fileno(stderr
), TIOCGWINSZ
, &ws
)) return -1;
145 static void SetTermTitle (const char *tit
) {
146 if (!tit
|| !isatty(fileno(stdout
)) || doRXVT
!= SY_TRUE
) return;
147 fprintf(stdout
, "\x1b]2;%s\7", tit
);
152 static TSyBool ctrlCPressed
= SY_FALSE
;
154 static void DoWait (int seconds
) {
155 while (seconds
> 0) {
156 char *tit
= SySPrintf("waiting: %i seconds left", seconds
);
161 if (ctrlCPressed
!= SY_FALSE
) return;
166 static void PrintProgress (TSyState
*state
, int64_t bytesDone
, int64_t bytesTotal
, TSyBool done
) {
167 char tmp0
[128], tmp1
[128], tmp2
[128], tmp3
[128], tmp4
[128], fmt
[128], str
[1024];
168 int len
; int64_t fb
= state
->firstByte
;
169 TSyStats
*stats
= &state
->stats
;
171 if (cfgNoIndicator
) return;
172 fprintf(stderr
, "\r");
173 if (isatty(fileno(stderr
))) {
175 fputs("\x1b[?7l", stderr
);
178 if (bytesTotal
> 0) {
179 len
= SyLong2StrComma(tmp1
, (bytesTotal
+fb
));
180 SyLong2StrComma(tmp0
, (bytesDone
+fb
));
181 sprintf(fmt
, "[%%%is/%%s] ", len
);
182 sprintf(tmp2
, fmt
, tmp0
, tmp1
); /*!*/
183 sprintf(tmp3
, "[%3i%%] ", (int)((double)100*(bytesDone
+fb
)/(bytesTotal
+fb
))); /*!*/
185 SySize2Str(tmp0
, (int64_t)stats
->bps
);
186 sprintf(tmp4
, "[SPD:%s] ", tmp0
); /*!*/
187 SyTime2Str(tmp0
, (int)(SyGetTimeD()-stats
->sttime
));
188 SyTime2Str(tmp1
, (int)stats
->eta
);
189 sprintf(fmt
, "TIME:%s ETA:%s", tmp0
, tmp1
); /*!*/
190 fprintf(stderr
, "%s%s%s%s", tmp2
, tmp3
, tmp4
, fmt
);
191 sprintf(str
, "%s%s%s/%s", tmp2
, tmp3
, tmp0
, tmp1
);
193 SyLong2StrComma(tmp0
, bytesDone
+fb
);
194 SyTime2Str(tmp1
, (int)(SyGetTimeD()-stats
->sttime
));
195 SySize2Str(fmt
, (int64_t)stats
->bps
);
196 sprintf(str
, "[%s] [SPD:%s] TIME:%s", tmp0
, fmt
, tmp1
);
197 fprintf(stderr
, "%s", str
);
199 if (isatty(fileno(stdout
)) && doRXVT
== SY_TRUE
) {
200 fprintf(stdout
, "\x1b]2;%s\7", str
);
203 if (isatty(fileno(stderr
))) {
205 fprintf(stderr
, "\x1b[K");
208 fputs("\x1b[?7h", stderr
);
211 if (done
== SY_TRUE
) fputs("\n", stderr
);
212 else if (cfgProgressWriteCR
== SY_TRUE
) fprintf(stderr
, "\n");
217 static void SyPrintStr (void *udata
, TSyMsgType msgtype
, const char *msg
) {
221 static const char *ltr
= "NMWE?";
222 if (cfgNoOutput
) return;
223 if (cfgQuiet
&& msgtype
< SY_MSG_MSG
) return;
225 if (isatty(fileno(stderr
))) {
228 fputs("\x1b[?7l", stderr
);
232 if (wdt
< 0) wdt
= strlen(msg
)+16;
236 if (msgtype
< SY_MSG_NOTICE
|| msgtype
> SY_MSG_ERROR
) msgtype
= SY_MSG_ERROR
+1;
237 fputc(ltr
[msgtype
], stderr
);
242 if (wdt
>= strlen(msg
)) fputs(msg
, stderr
);
243 else while (wdt
--) fputc(*(msg
++), stderr
);
249 if (isatty(fileno(stderr
))) {
251 fputs("\x1b[K", stderr
);
255 fputs("\x1b[?7h", stderr
);
261 if (isatty(fileno(stdout
)) && doRXVT
== SY_TRUE
&& msgtype
> SY_MSG_NOTICE
) {
262 fprintf(stdout
, "\x1b]2;%s\7", msg
);
268 static void SyDefaultCfg (TSyCfg
*cfg
) {
269 SyCfgAddIntKey(cfg
, "reconnect_delay", "number of seconds to wait before reconnecting to server", &cfgReconnectDelay
);
270 SyCfgAddIntKey(cfg
, "reconnect_attempts", "number of reconnection attempts (0: do not reconnect)", &cfgReconnectAttempts
);
271 SyCfgAddIntKey(cfg
, "io_timeout", "i/o timeout in seconds", &cfgTimeout
);
272 SyCfgAddIntKey(cfg
, "save_state_interval", "save state every x seconds (0: disable)", &cfgSaveInterval
);
273 cfgDefaultName
= SyStrNew("index.html", -1);
274 SyCfgAddStrKey(cfg
, "default_file_name", "default file name for http", &cfgDefaultName
);
275 SyCfgAddIntKey(cfg
, "max_redirects", "maximum number of redirects/symlinks", &cfgMaxRedirects
);
276 SyCfgAddIntKey(cfg
, "init_buffer_size", "initial buffer size", &cfgBufferSize
);
277 SyCfgAddIntKey(cfg
, "max_buffer_size", "maximum amount of bytes read buffer can grow to", &cfgMaxBufferSize
);
278 SyCfgAddBoolKey(cfg
, "quiet", "tan: do not show notices", &cfgQuiet
);
279 SyCfgAddBoolKey(cfg
, "no_output", "tan: disable any output", &cfgNoOutput
);
280 SyCfgAddBoolKey(cfg
, "no_indicator", "tan: hide download indicator", &cfgNoIndicator
);
282 cfgIFace
= SyStrNew("", -1);
283 SyCfgAddStrKey(cfg
, "interface", "net interface to use for connection (name or IP)", &cfgIFace
);
285 cfgProxyHTTP
= SyStrNew(getenv("http_proxy"), -1);
286 SyCfgAddStrKey(cfg
, "http_proxy", "proxy for http connections", &cfgProxyHTTP
);
287 cfgProxyFTP
= SyStrNew(getenv("ftp_proxy"), -1);
288 SyCfgAddStrKey(cfg
, "ftp_proxy", "proxy for ftp connections", &cfgProxyFTP
);
289 SyCfgAddBoolKey(cfg
, "use_http_proxy", "use http proxy (if specified)?", &cfgUseProxyHTTP
);
290 SyCfgAddBoolKey(cfg
, "use_ftp_proxy", "use ftp proxy (if specified)?", &cfgUseProxyFTP
);
291 SyCfgAddBoolKey(cfg
, "ftp_proxy_use_connect", "use CONNECT method for proxied ftp connections", &cfgFTPUseConnect
);
293 SyCfgAddIntKey(cfg
, "referer_type", "set referer type (0:none; 1:orig URL; 2:follow URL; 3:user)", &cfgRefererType
);
294 cfgRefererStr
= SyStrNew(NULL
, -1);
295 SyCfgAddStrKey(cfg
, "referer_str", "referer for mode 3", &cfgRefererStr
);
296 SyCfgAddBoolKey(cfg
, "decode_ftp_url", "decode URLs for FTP", &cfgDecodeFTPURL
);
298 cfgUAStr
= SyStrNew(SYREN_DEFAULT_USER_AGENT
, -1);
299 SyCfgAddStrKey(cfg
, "ua_str", "user agent (set to empty string if no UA should be sent)", &cfgUAStr
);
304 TSyResult
SySaveInt (int fd
, int64_t v
, int size
) {
307 if (fd
< 0 || size
< 1 || size
> 8) return SY_ERROR
;
309 b
= (v
>>(size
*8))&0xff;
310 if (SyWriteFile(fd
, &b
, 1) != SY_OK
) return SY_ERROR
;
317 TSyResult
SyLoadInt (int fd
, int64_t *v
, int size
) {
318 uint8_t b
; int64_t res
= 0;
321 if (!fd
|| size
< 1 || size
> 8) return SY_ERROR
;
323 if (SyReadFile(fd
, &b
, 1) != SY_OK
) return SY_ERROR
;
332 TSyResult
SySaveStr (int fd
, const char *v
) {
335 if (!fd
) return SY_ERROR
;
336 if (v
) len
= strlen(v
);
337 if (len
> 65535) return SY_ERROR
;
338 if (SySaveInt(fd
, len
, 2) != SY_OK
) return SY_ERROR
;
339 if (len
) { if (SyWriteFile(fd
, v
, len
) != SY_OK
) return SY_ERROR
; }
345 char *SyLoadStr (int fd
) {
349 if (!fd
) return NULL
;
350 if (SyLoadInt(fd
, &len
, 2) != SY_OK
) return NULL
;
351 if (!len
) return SyStrNewEmpty();
352 v
= calloc(1, len
+8); if (!v
) return NULL
;
353 if (SyReadFile(fd
, v
, len
) != SY_OK
) { free(v
); return NULL
; }
359 static const char *SF_STR
= "SYREN STATE FILE v6\r\n\x1a";
360 TSyResult
SySaveState (const char *destfile
, TSyState
*state
) {
361 TSyResult res
= SY_ERROR
;
366 if (!state
) return SY_ERROR
;
368 s
= SySPrintf("%s://%s:%s@%s:%i%s%s%s%s",
369 url
->protostr
, url
->user
, url
->pass
, url
->host
, url
->port
,
370 url
->dir
, url
->file
, url
->query
, url
->anchor
);
371 if (!s
) return SY_ERROR
;
372 fd
= SyOpenFile(destfile
, SY_FMODE_WRITE
, SY_TRUE
);
375 /*if (SySaveStr(fd, "SYREN STATE FILE v5") != SY_OK) break;*/
376 if (SyWriteFile(fd
, SF_STR
, strlen(SF_STR
)) != SY_OK
) break;
377 if (SySaveInt(fd
, state
->fileSize
, 8) != SY_OK
) break;
378 if (SySaveInt(fd
, state
->currentByte
+state
->firstByte
, 8) != SY_OK
) break;
379 if (SySaveInt(fd
, state
->lastByte
, 8) != SY_OK
) break;
380 if (SySaveStr(fd
, s
) != SY_OK
) break;
381 t
= strrchr(oOutFileName
, '/'); if (!t
) t
= oOutFileName
; else t
++;
382 if (SySaveStr(fd
, t
) != SY_OK
) break;
383 if (SySaveInt(fd
, cfgRefererType
, 1) != SY_OK
) break;
384 if (SySaveStr(fd
, cfgRefererStr
) != SY_OK
) break;
385 if (SySaveStr(fd
, oPostData
) != SY_OK
) break;
390 if (res
!= SY_OK
) SyDeleteFile(destfile
);
397 char *SyLoadStateV5 (int fd
, TSyState
*state
) {
398 char *destfile
= NULL
, *s
= NULL
;
402 s
= SyLoadStr(fd
); if (!s
) break;
403 if (strcmp(s
, "SYREN STATE FILE v5")) break;
404 if (SyLoadInt(fd
, &state
->fileSize
, 8) != SY_OK
) break;
405 if (SyLoadInt(fd
, &state
->firstByte
, 8) != SY_OK
) break;
406 if (oIgnoreStatePos
!= SY_FALSE
) state
->firstByte
= 0;
407 if (state
->firstByte
< 0 || (state
->fileSize
>= 0 && state
->firstByte
> state
->fileSize
)) break;
408 if (SyLoadInt(fd
, &state
->lastByte
, 8) != SY_OK
) break;
409 if (state
->lastByte
< -1 || (state
->lastByte
>= 0 && state
->firstByte
> state
->lastByte
)) break;
410 free(s
); s
= SyLoadStr(fd
); if (!s
) break;
411 if (oURL
) free(oURL
); oURL
= s
;
412 s
= SyLoadStr(fd
); if (!s
) break;
413 destfile
= s
; s
= NULL
;
414 if (SyLoadInt(fd
, &i
, 1) != SY_OK
) break;
415 cfgRefererType
= (int)i
;
416 s
= SyLoadStr(fd
); if (!s
) break;
417 if (cfgRefererStr
) free(cfgRefererStr
);
419 s
= SyLoadStr(fd
); if (!s
) { free(destfile
); destfile
= NULL
; break; }
420 if (state
->postData
) free(state
->postData
);
421 state
->postData
= s
; s
= NULL
;
430 char *SyLoadStateV6 (int fd
, TSyState
*state
) {
431 char *destfile
= NULL
, *s
= NULL
, buf
[128];
433 int l
= strlen(SF_STR
);
436 memset(buf
, 0, sizeof(buf
));
437 if (SyReadFile(fd
, buf
, l
) != SY_OK
) break;
438 if (strcmp(buf
, SF_STR
)) break;
439 if (SyLoadInt(fd
, &state
->fileSize
, 8) != SY_OK
) break;
440 if (SyLoadInt(fd
, &state
->firstByte
, 8) != SY_OK
) break;
441 if (oIgnoreStatePos
!= SY_FALSE
) state
->firstByte
= 0;
442 if (state
->firstByte
< 0 || (state
->fileSize
>= 0 && state
->firstByte
> state
->fileSize
)) break;
443 if (SyLoadInt(fd
, &state
->lastByte
, 8) != SY_OK
) break;
444 if (state
->lastByte
< -1 || (state
->lastByte
>= 0 && state
->firstByte
> state
->lastByte
)) break;
445 free(s
); s
= SyLoadStr(fd
); if (!s
) break;
446 if (oURL
) free(oURL
); oURL
= s
;
447 s
= SyLoadStr(fd
); if (!s
) break;
448 destfile
= s
; s
= NULL
;
449 if (SyLoadInt(fd
, &i
, 1) != SY_OK
) break;
450 cfgRefererType
= (int)i
;
451 s
= SyLoadStr(fd
); if (!s
) break;
452 if (cfgRefererStr
) free(cfgRefererStr
);
454 s
= SyLoadStr(fd
); if (!s
) { free(destfile
); destfile
= NULL
; break; }
455 if (state
->postData
) free(state
->postData
);
456 state
->postData
= s
; s
= NULL
;
465 /* return destfile */
466 char *SyLoadState (const char *statefile
, TSyState
*state
) {
467 char *destfile
= NULL
;
471 fd
= SyOpenFile(statefile
, SY_FMODE_READ
, SY_FALSE
);
472 if (fd
< 0) return NULL
;
473 if (SyReadFile(fd
, &b
, 1) == SY_OK
) {
474 if (SySeekFile(fd
, 0) == SY_OK
) {
475 /*fprintf(stderr, "state v%i\n", b?6:5);*/
476 if (!b
) destfile
= SyLoadStateV5(fd
, state
);
477 else destfile
= SyLoadStateV6(fd
, state
);
486 void DumpState (TSyState
*state
) {
487 char t0
[32], t1
[32], t2
[32];
488 SyLong2StrComma(t0
, state
->fileSize
);
489 SyLong2StrComma(t1
, state
->firstByte
);
490 SyLong2StrComma(t2
, state
->lastByte
);
491 printf("state dump\n==========\nURL: %s\nfile name: %s\n"
492 "file size: %s\nfirst byte: %s\nlast byte: %s\n",
493 oURL
, oOutFileName
, t0
, t1
, t2
498 static void ShowHelp (void) {
499 fprintf(stderr
, "%s",
500 "usage: syren [options] url [+url] [+url] [@urllistfile]\n"
503 " -h all show 'long' configuration options\n"
504 " -h <longopt> show 'long' configuration option description\n"
505 " -l filename same as @urllistfile\n"
506 " -o filename output to specified file\n"
507 " -O filename output to specified file (same as '-o')\n"
508 " -r filename resume download (do not use saved state, use file)\n"
509 " -s filename restore state\n"
510 " -S filename redownload file using state (ignore starting position, recreate)\n"
511 " -D filename dump state file and exit\n"
512 " -k keep state file\n"
513 " -P <str|@fn> make POST request\n"
514 " -c <str|@fn> send cookies\n"
515 " -C filename dump cookies\n"
516 " -H <str|@fn> additional headers\n"
517 " -i download server reply and stop\n"
518 " -I name_or_ip use specified net interface (\"-\" any)\n"
519 " -X do not use proxies\n"
520 " -A don't preallocate disk space\n"
521 " -T turn off terminal window title changing\n"
522 " -Z write CR after progress string\n"
523 " -w generate .syrenrc file with the current config\n"
524 " -N don't write state file\n"
525 " -F force retry on any download error\n"
529 " -Q be VERY quiet\n"
530 #ifdef SYREN_USE_SCRIPT
531 " -L disable scripting\n"
534 " -W accept any HTTP reply code\n"
540 static char *GetStrOpt (TSyPrintStr
*prs
, int argc
, char *argv
[], int *f
, const char *optName
,
541 char **value
, TSyBool allowEmpty
, TSyBool exclusive
) {
545 if (*f
>= argc
|| !argv
[*f
]) { SyMessage(prs
, SY_MSG_ERROR
, "no value for '%s'", optName
); return NULL
; }
546 if (exclusive
== SY_TRUE
&& (value
&& *value
)) { SyMessage(prs
, SY_MSG_ERROR
, "duplicate '%s'", optName
); return NULL
; }
547 res
= SyStrNew(argv
[*f
], -1); (*f
)++;
548 if (!res
) { SyMessage(prs
, SY_MSG_ERROR
, "memory error"); return NULL
; }
549 if (allowEmpty
!= SY_TRUE
&& !res
[0]) {
551 SyMessage(prs
, SY_MSG_ERROR
, "empty value for '%s'", optName
);
555 if (*value
) free(*value
);
563 static TSyResult
GetStrOptWithAt (TSyPrintStr
*prs
, int argc
, char *argv
[], int *f
, const char *optName
,
564 char **value
, TSyBool allowEmpty
, TSyBool exclusive
, TSyBool ishdr
) {
566 //printf("[%s]: %p\n", optName, ishdr?NULL:value);
567 char *res
= GetStrOpt(prs
, argc
, argv
, f
, optName
, ishdr
?NULL
:value
, allowEmpty
, exclusive
);
568 if (!res
) return SY_ERROR
;
570 res
= SyLoadWholeFile(res
+1);
571 if (!res
) { SyMessage(prs
, SY_MSG_ERROR
, "can't load data from '%s'", (*value
)+1); return SY_ERROR
; }
572 //free(*value); *value = res;
578 char *t
= SySPrintf("%s\n%s", *value
, res
);
589 if (value
!= NULL
) *value
= res
;
596 static void SyAddURLFromFile (const char *fname
) {
599 fl
= SyLoadWholeFile(fname
);
602 e
= s
; while (*e
&& *e
!= '\n') e
++;
605 if (*s
&& s
[0] != '#' && s
[0] != ';') {
606 SyKVListDelete(oURLList
, s
);
607 SyKVListSet(oURLList
, s
, "", NULL
);
615 static TSyResult
ParseCmdLine (TSyPrintStr
*prs
, TSyCfg
*cfg
, int argc
, char *argv
[]) {
616 int f
, nomoreopt
= 0;
617 char *s
, *t
, *sopt
, soptN
[2];
620 f
= 1; while (f
< argc
) {
622 if (!s
) break; if (!(*s
)) continue;
623 if (!nomoreopt
&& *s
== '-') {
625 /* long option (config) */
626 if (!s
[3]) nomoreopt
= 1;
629 if (SyCfgSet(cfg
, s
) != SY_OK
) { SyMessage(prs
, SY_MSG_ERROR
, "invalid option: %s", (s
-2)); return SY_ERROR
; }
634 soptN
[0] = *sopt
; soptN
[1] = '\0';
637 printf("%s", SYREN_VDHEADER_STRING
);
639 case '9': optWOS
= 15; break;
640 case 'W': optAnyCode
= SY_TRUE
; break;
641 case 'e': uEncodeDecode
= 1; break;
642 case 'E': uEncodeDecode
= -1; break;
643 case 'w': writeCfg
= SY_TRUE
; break;
644 case 'X': cfgUseProxyHTTP
= cfgUseProxyFTP
= SY_FALSE
; break;
645 case 'T': doRXVT
= SY_FALSE
; break;
646 case 'q': cfgQuiet
= SY_TRUE
; break;
647 case 'Q': cfgQuiet
= cfgNoOutput
= SY_TRUE
; break;
648 case 'i': replyOnly
= SY_TRUE
; break;
649 case 'N': oNoStateFile
= SY_TRUE
; break;
650 case 'F': oForceRetry
= SY_TRUE
; break;
651 case 'k': oCanRemoveState
= SY_FALSE
; break;
652 case 'Z': cfgProgressWriteCR
= SY_TRUE
; break;
653 case 'A': oPreallocSpace
= SY_FALSE
; break;
655 #ifdef SYREN_USE_SCRIPT
656 syScriptEnabled
= SY_FALSE
;
661 SyMessage(prs
, SY_MSG_ERROR
, "no argument for -I");
665 cfgIFace
= SyStrDup(argv
[f
++]);
666 /*SyStrTrim(cfgIFace);*/
667 /*if (!cfgIFace[0] || !strcmp(cfgIFace, "-")) { SyStrFree(cfgIFace); cfgIFace = NULL; }*/
669 case 'o': case 'O': case 'r':
670 if (*sopt
== 'r') oResume
= SY_TRUE
;
671 s
= GetStrOpt(prs
, argc
, argv
, &f
, soptN
, &oOutFileName
, SY_FALSE
, SY_TRUE
);
672 if (!s
) return SY_ERROR
;
673 oCanRename
= SY_FALSE
;
675 case 's': case 'S': case 'D':
676 if (*sopt
== 'S') oIgnoreStatePos
= SY_TRUE
;
677 else if (*sopt
== 'D') dumpState
= SY_TRUE
;
678 s
= GetStrOpt(prs
, argc
, argv
, &f
, soptN
, &oStateFileName
, SY_FALSE
, SY_TRUE
);
679 if (!s
) return SY_ERROR
;
680 oCanRename
= SY_FALSE
;
683 if (GetStrOptWithAt(prs
, argc
, argv
, &f
, soptN
, &oPostData
, SY_FALSE
, SY_TRUE
, SY_FALSE
) != SY_OK
) return SY_ERROR
;
686 if (GetStrOptWithAt(prs
, argc
, argv
, &f
, soptN
, &oSendCookies
, SY_FALSE
, SY_TRUE
, SY_FALSE
) != SY_OK
) return SY_ERROR
;
689 if (!GetStrOpt(prs
, argc
, argv
, &f
, soptN
, &oCookieDump
, SY_FALSE
, SY_TRUE
)) return SY_ERROR
;
692 if (GetStrOptWithAt(prs
, argc
, argv
, &f
, soptN
, &oAddonHeaders
, SY_FALSE
, SY_TRUE
, SY_TRUE
)) return SY_ERROR
;
696 fprintf(stderr
, "E: \"-l\" without file name!\n");
699 SyAddURLFromFile(argv
[f
++]);
702 fprintf(stderr
, "%s\n", SYREN_VERSION_DATETIME_STRING
);
703 key
= cfg
->opts
->first
;
704 if (f
< argc
&& !strcmp(argv
[f
], "all")) {
705 fprintf(stderr
, "long options:\n");
706 while (key
) { fprintf(stderr
, " --%s\n", key
->key
); key
= key
->next
; }
708 if (f
< argc
&& argv
[f
]) {
710 if (!strcasecmp(key
->key
, argv
[f
])) {
711 fprintf(stderr
, "--%s %s\n %s\n", key
->key
,
712 key
->uidata
==SY_CI_STRING
?"string":(key
->uidata
==SY_CI_INT
?"integer":(key
->uidata
==SY_CI_BOOL
?"boolean":"?")),
719 if (!key
) ShowHelp();
723 SyMessage(prs
, SY_MSG_ERROR
, "unknown option: %s", soptN
);
731 if (!t
) { SyMessage(prs
, SY_MSG_ERROR
, "memory error"); return SY_ERROR
; }
733 if (*t
== '@') SyAddURLFromFile(t
+1);
735 if (t
[0] == '+') { strcpy(t
, &(t
[1])); SyStrTrim(t
); }
737 SyKVListDelete(oURLList
, t
);
738 SyKVListSet(oURLList
, t
, "", NULL
);
744 if (oResume
&& oStateFileName
) { SyMessage(prs
, SY_MSG_ERROR
, "'-r'/'-s' conflict"); return SY_ERROR
; }
750 #ifdef SYREN_USE_SCRIPT
751 TSyHdrs
*scHdrs
= NULL
;
754 TSyResult
SyDrvAddHeaders (TSyState
*state
, TSyHdrs
*hdrs
) {
755 char *s
= NULL
, *t
, *cs
;
756 TSyURL
*url
= state
->url
;
759 // fuck you, sourceshit
760 if (strstr(url
->host
, "sourceforge.net")) {
761 if (SyHdrAddLine(hdrs
, "User-Agent: HereToPissOffSourceCrapSniffer") != SY_OK
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "hdr error"); return SY_ERROR
; }
764 if (cfgRefererType
) {
765 switch (cfgRefererType
) {
766 case 1: s
= SyStrNew(oURL
, -1); break;
768 if (url
->proto
== SY_PROTO_FTP
&& url
->port
!= 80) sprintf(port
, ":%i", url
->port
); else port
[0] = '\0';
769 s
= SySPrintf("%s://%s%s%s%s%s%s", url
->protostr
, url
->host
, port
,
770 url
->dir
, url
->file
, url
->query
, url
->anchor
);
772 default: s
= SyStrNew(cfgRefererStr
, -1); break;
774 if (!s
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); return SY_ERROR
; }
775 t
= SySPrintf("Referer: %s", s
); free(s
);
776 if (!t
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); return SY_ERROR
; }
777 if (SyHdrAddLine(hdrs
, t
) != SY_OK
) { free(t
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "hdr error"); return SY_ERROR
; }
780 if (oAddonHeaders
&& *oAddonHeaders
) {
783 t
= s
; while (*t
&& *t
!= '\n') t
++;
784 cs
= SyStrNew(s
, t
-s
); if (!cs
) return SY_ERROR
;
787 if (SyHdrAddLine(hdrs
, cs
) != SY_OK
) { free(cs
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "hdr error"); return SY_ERROR
; }
789 free(cs
); if (!s
[0]) break;
790 s
= t
; if (*s
) s
++; /* don't skip latest 0 */
793 if (oSendCookies
&& *oSendCookies
) {
794 char *newstr
= calloc(strlen(oSendCookies
)*3, 1); /* this should be enough */
797 t
= s
; while (*t
&& *t
!= '\n') t
++;
798 cs
= SyStrNew(s
, t
-s
); if (!cs
) return SY_ERROR
;
801 if (newstr
[0]) strcat(newstr
, "; ");
802 //if (SyHdrSetCookie(hdrs, cs) != SY_OK) { free(cs); return SY_ERROR; }
805 free(cs
); if (!s
[0]) break;
806 s
= t
; if (*s
) s
++; /* don't skip latest 0 */
809 SyHdrSetCookie(hdrs
, newstr
);
810 SyHdrAddLine(hdrs
, "Cookie2: $Version=1");
814 #ifdef SYREN_USE_SCRIPT
816 SyScriptCallback("Event.AddHeaders", state
->pfn
);
818 if (scAbort
== SY_TRUE
) state
->breakNow
= SY_TRUE
;
825 TSyResult
SyDrvGotHeaders (TSyState
*state
, TSyHdrs
*hdrs
) {
826 TSyResult res
= SY_OK
;
827 TSyKVList
*cc
; TSyKVListItem
*item
;
829 static char *crlf
= "\r\n";
831 #ifdef SYREN_USE_SCRIPT
833 SyScriptCallback("Event.GotHeaders", state
->pfn
);
834 /*fprintf(stderr, "\rGotHeaders: return\n");*/
836 if (scAbort
== SY_TRUE
) state
->breakNow
= SY_TRUE
;
838 if (oCookieDump
&& *oCookieDump
) {
839 cc
= SyHdrFindCookieList(hdrs
);
840 if (cc
&& cc
->count
) {
841 if (strcmp(oCookieDump
, "-")) {
842 fd
= SyOpenFile(oCookieDump
, SY_FMODE_WRITE
, SY_TRUE
);
843 if (fd
< 0) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't dump cookies to '%s'", oCookieDump
); return SY_ERROR
; }
847 if (SyWriteFile(fd
, item
->value
, strlen(item
->value
)) != SY_OK
) { res
= SY_ERROR
; break; }
848 if (SyWriteFile(fd
, crlf
, strlen(crlf
)) != SY_OK
) { res
= SY_ERROR
; break; }
851 if (fd
) SyCloseFile(fd
);
859 static TSyState
*curState
= NULL
;
861 static void BreakSignal (int signo
) {
863 if (curState
) curState
->breakNow
= SY_TRUE
;
864 ctrlCPressed
= SY_TRUE
;
868 static void SaveStateSignal (int signo
) {
869 /*fprintf(stderr, "\nsave state\n");*/
874 TSyResult
SyXPrealloc (int fd
, int64_t fileSize
) {
875 #if !USE_NEW_PREALLOC
879 double sttime
= SyGetTimeD(), ctime
;
882 if (fileSize
< 1) return SY_ERROR
;
885 if (bufSz
> fileSize
) bufSz
= fileSize
;
886 buf
= calloc(1, bufSz
);
887 if (!buf
) return SY_ERROR
;
889 while (fileSize
> 0) {
890 if (cfgQuiet
!= SY_TRUE
|| cfgNoOutput
!= SY_TRUE
) {
891 ctime
= SyGetTimeD()-sttime
;
892 if (ctime
>= 0.666) {
893 SyLong2StrComma(tmp0
, fileSize
);
894 fprintf(stderr
, "\rN: preallocating: [%s] bytes left\x1b[K", tmp0
);
897 sttime
= SyGetTimeD();
900 wr
= fileSize
>(int64_t)bufSz
?bufSz
:fileSize
;
901 if (SyWriteFile(fd
, buf
, wr
) != SY_OK
) {
903 if (isatty(fileno(stderr
))) {
904 if (wasoutput
) { fprintf(stderr
, "\r\x1b[K"); fflush(stderr
); }
908 fileSize
-= (int64_t)wr
;
909 if (ctrlCPressed
== SY_TRUE
) {
911 if (isatty(fileno(stderr
))) {
912 if (wasoutput
) { fprintf(stderr
, "\r\x1b[Kaborted!\n"); fflush(stderr
); }
918 if (isatty(fileno(stderr
))) {
919 if (wasoutput
) { fprintf(stderr
, "\r\x1b[K"); fflush(stderr
); }
924 /*if (fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, fileSize)) return SY_ERROR;*/
925 /*if (fallocate(fd, 0, 0, fileSize)) return SY_ERROR;*/
926 if (posix_fallocate(fd
, 0, fileSize
)) return SY_ERROR
;
928 if (posix_fallocate(fd
, 0, fileSize
)) return SY_ERROR
;
933 return SySeekFile(fd
, 0);
937 TSyResult
SyDrvOpenFile (TSyState
*state
) {
938 int fd
, sto
= 0; int64_t len
= -1;
941 if (replyOnly
!= SY_FALSE
) return SY_OK
;
944 if (!oOutFileName
|| !oOutFileName
[0]) {
945 if (oOutFileName
) free(oOutFileName
);
946 if (state
->httpFName
&& *state
->httpFName
) {
949 if ((t
= strrchr(state
->httpFName
, '/')) != NULL
) {
950 oOutFileName
= SyStrDup(t
[1]?t
+1:"unnamed_file.bin");
952 oOutFileName
= SyStrDup(state
->httpFName
);
955 oOutFileName
= SyStrDup(state
->url
->file
);
957 #ifdef SYREN_USE_SCRIPT
958 SyScriptCallback("Event.CheckFileName", state
->pfn
);
960 /*if (scAbort == SY_TRUE) goto done;*/
961 if (!oOutFileName
|| !oOutFileName
[0]) {
962 if (oOutFileName
) free(oOutFileName
);
963 oOutFileName
= SyStrNew((cfgDefaultName
&&*cfgDefaultName
)?cfgDefaultName
:"index.html", -1);
965 if (!oOutFileName
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); return SY_ERROR
; }
967 /* check for duplicates */
968 fd
= SyOpenFile(oOutFileName
, SY_FMODE_READ
, SY_FALSE
);
970 int f
= -1; s
= NULL
;
972 SyCloseFile(fd
); if (s
) free(s
);
973 s
= SySPrintf("%s.%i", oOutFileName
, f
);
974 if (!s
) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "memory error"); return SY_ERROR
; }
975 fd
= SyOpenFile(s
, SY_FMODE_READ
, SY_FALSE
);
977 free(oOutFileName
); oOutFileName
= s
;
980 /* open/create file */
981 if (!oOutFileName
|| !oOutFileName
[0]) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "invalid file name"); return SY_ERROR
; }
982 if (state
->firstByte
> 0) {
983 if (!strcmp(oOutFileName
, "-")) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't resume stdout"); return SY_ERROR
; }
984 fd
= SyOpenFile(oOutFileName
, SY_FMODE_WRITE
, SY_FALSE
);
986 len
= SyFileSize(fd
);
987 if (len
< 0) { SyCloseFile(fd
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't resume: file error"); return SY_ERROR
; }
988 if (len
< state
->firstByte
) { SyCloseFile(fd
); SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't resume: corrupted file"); return SY_ERROR
; }
989 if (SySeekFile(fd
, state
->firstByte
) != SY_OK
) {
991 SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't resume: file error");
994 SyLong2StrComma(tmp
, state
->firstByte
);
995 SyMessage(state
->pfn
, SY_MSG_MSG
, "resuming %s at %s", oOutFileName
, tmp
);
998 if (!strcmp(oOutFileName
, "-")) { fd
= 0; sto
= 1; oNoStateFile
= SY_TRUE
; }
1000 SyDeleteFile(oOutFileName
);
1001 fd
= SyOpenFile(oOutFileName
, SY_FMODE_WRITE
, SY_TRUE
);
1002 /* preallocating w/o state file means no resumes, so disable it */
1003 if (fd
> 0 && oPreallocSpace
!= SY_FALSE
&& state
->fileSize
> 0 && oNoStateFile
!= SY_TRUE
) {
1004 SyLong2StrComma(tmp
, state
->fileSize
);
1005 SyMessage(state
->pfn
, SY_MSG_MSG
, "preallocating %s bytes", tmp
);
1006 if (SyXPrealloc(fd
, state
->fileSize
) != SY_OK
) {
1007 SyMessage(state
->pfn
, SY_MSG_ERROR
, "preallocating failed");
1009 SyDeleteFile(oOutFileName
);
1015 if (fd
< 0) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't open file %s", oOutFileName
); return SY_ERROR
; }
1016 if (state
->firstByte
<= 0 || sto
) SyMessage(state
->pfn
, SY_MSG_MSG
, "writing to %s", sto
?"stdout":oOutFileName
);
1017 state
->udatai
= sto
?-2:fd
;
1018 if (!oStateFileName
|| !oStateFileName
[0]) {
1019 if (oStateFileName
) free(oStateFileName
);
1020 oStateFileName
= SySPrintf("%s.syren", oOutFileName
);
1021 if (!oStateFileName
) SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't create state file name");
1022 else if (oNoStateFile
!= SY_TRUE
) SySaveState(oStateFileName
, state
);
1029 TSyResult
SyDrvCloseFile (TSyState
*state
) {
1030 if (replyOnly
!= SY_FALSE
) return SY_OK
;
1031 if (state
->udatai
>= 0) {
1032 SyMessage(state
->pfn
, SY_MSG_MSG
, "closing %s%s", oOutFileName
,
1033 (state
->error
==SY_FALSE
?"":" (incomplete file)"));
1034 SyCloseFile(state
->udatai
);
1036 if (oStateFileName
&& *oStateFileName
&& oNoStateFile
!= SY_TRUE
) SySaveState(oStateFileName
, state
);
1043 TSyResult
SyDrvWriteFile (TSyState
*state
, void *buf
, int bufSize
) {
1046 if (replyOnly
!= SY_FALSE
) return SY_OK
;
1047 fd
= state
->udatai
; if (fd
== -2) fd
= 1; /* stdout */
1049 if (SyWriteFile(fd
, buf
, bufSize
) != SY_OK
) return SY_ERROR
;
1051 if (oStateFileName
&& *oStateFileName
&& oNoStateFile
!= SY_TRUE
&& state
->udatai
>= 0) {
1052 if (cfgSaveInterval
>= 1.0 && SyGetTimeD()-lastSSave
>= cfgSaveInterval
) {
1053 SySaveState(oStateFileName
, state
);
1054 lastSSave
= SyGetTimeD();
1055 fsync(state
->udatai
);
1063 TSyResult
SyDrvNoResume (TSyState
*state
) {
1064 if (replyOnly
!= SY_FALSE
) {
1065 SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't resume");
1068 if (state
->udatai
>= 0) {
1069 if (state
->udatai
) SyCloseFile(state
->udatai
);
1070 SyDeleteFile(oOutFileName
);
1071 state
->udatai
= SyOpenFile(oOutFileName
, SY_FMODE_WRITE
, SY_TRUE
);
1072 if (state
->udatai
< 0) {
1073 SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't create file %s", oOutFileName
);
1077 SyMessage(state
->pfn
, SY_MSG_WARNING
, "can't resume file %s, file truncated", oOutFileName
);
1083 TSyResult
CheckResume (TSyState
*state
) {
1084 int fd
; int64_t len
;
1086 SyMessage(state
->pfn
, SY_MSG_NOTICE
, "checking file for resuming");
1087 if (!strcmp(oOutFileName
, "-")) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't resume stdout"); return SY_ERROR
; }
1088 fd
= SyOpenFile(oOutFileName
, SY_FMODE_WRITE
, SY_FALSE
);
1089 if (fd
< 0) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "resume: can't open file %s", oOutFileName
); return SY_ERROR
; }
1090 len
= SyFileSize(fd
);
1092 if (len
< 0) { SyMessage(state
->pfn
, SY_MSG_ERROR
, "can't determine file size for %s", oOutFileName
); return SY_ERROR
; }
1093 state
->firstByte
= len
;
1094 SyMessage(state
->pfn
, SY_MSG_NOTICE
, "resume check passed");
1100 static TSyCfg
*curcfg
= NULL
;
1101 static char *cfgSaveBuf
= NULL
;
1102 static size_t cfgSaveBufSize
= 0;
1103 static TSyPrintStr prs
;
1106 static void loadDefaultConfig (void) {
1111 SyDefaultCfg(curcfg
);
1112 if ((home
= getenv("HOME")) != NULL
) {
1113 s
= SySPrintf("%s/.syren/.syrenrc", home
);
1114 if (access(s
, R_OK
) == 0) {
1115 SyCfgLoad(curcfg
, s
, &prs
);
1119 s
= SySPrintf("%s/.syrenrc", home
);
1120 if (access(s
, R_OK
) == 0) {
1121 SyCfgLoad(curcfg
, s
, &prs
);
1125 if (access(".syrenrc", R_OK
) == 0) SyCfgLoad(curcfg
, ".syrenrc", &prs
);
1126 else if (access("/etc/syren/.syrenrc", R_OK
) == 0) SyCfgLoad(curcfg
, "/etc/syren/.syrenrc", &prs
);
1128 if (s
!= NULL
) free(s
);
1132 static void storeConfig (void) {
1133 FILE *fl
= open_memstream(&cfgSaveBuf
, &cfgSaveBufSize
);
1134 SyCfgSaveFile(curcfg
, fl
, &prs
);
1139 static void restoreConfig (void) {
1140 if (cfgSaveBuf
!= NULL
) {
1143 fl
= fmemopen(cfgSaveBuf
, cfgSaveBufSize
, "r");
1144 SyCfgLoadFile(curcfg
, fl
, &prs
);
1150 static char *getPerSiteConfigFileName (const char *host
) {
1151 const char *t
= getenv("HOME");
1154 char *cfgname
= SySPrintf("%s/%s/%s.rc", (t
!= NULL
? t
: "/etc"), (t
!= NULL
? ".syren" : "syren"), host
), *dot
;
1156 if (access(cfgname
, R_OK
) == 0) return cfgname
;
1158 if ((dot
= strchr(host
, '.')) == NULL
) break;
1166 static void loadPerSiteConfig (void) {
1167 TSyURL
*url
= SyURLNew();
1169 if (SyURLParse(url
, oURL
) == SY_OK
) {
1170 char *cfgname
= getPerSiteConfigFileName(url
->host
);
1172 if (cfgname
!= NULL
) {
1173 SyCfgLoad(curcfg
, cfgname
, &prs
);
1182 int main (int argc
, char *argv
[]) {
1189 prs
.print
= &SyPrintStr
; prs
.udata
= NULL
;
1192 if (t
&& (!strcmp(t
, "rxvt") || !strcmp(t
, "mrxvt") || !strcmp(t
, "xterm") || !strcmp(t
, "eterm"))) doRXVT
= SY_TRUE
;
1194 curcfg
= SyCfgNew();
1195 if (!curcfg
) { SyMessage(&prs
, SY_MSG_ERROR
, "memory error"); }
1198 if (!state
) { SyMessage(&prs
, SY_MSG_ERROR
, "memory error"); }
1199 prs
.udata
= (void *)state
;
1201 oURLList
= SyKVListNew();
1202 if (!oURLList
) goto done
;
1203 oURLList
->casesens
= 1;
1205 loadDefaultConfig();
1207 if (ParseCmdLine(&prs
, curcfg
, argc
, argv
) != SY_OK
) goto done
;
1209 SyMessage(&prs
, SY_MSG_MSG
, "%s", SYREN_VERSION_DATETIME_STRING
);
1211 #ifdef SYREN_USE_SCRIPT
1213 SyScriptLoadInit(argv
[0], &prs
);
1216 if (!oStateFileName
) {
1217 if (!oURLList
->first
) { SyMessage(&prs
, SY_MSG_ERROR
, "URL?"); goto done
; }
1220 if (cfgMaxBufferSize
< 1) { SyMessage(&prs
, SY_MSG_ERROR
, "buffer size too small"); goto done
; }
1221 if (cfgTimeout
< 1) { SyMessage(&prs
, SY_MSG_ERROR
, "timeout too small"); goto done
; }
1223 if (writeCfg
== SY_TRUE
) {
1224 SyMessage(&prs
, SY_MSG_MSG
, "generating sample config");
1225 SyCfgSave(curcfg
, ".syrenrc", &prs
);
1230 if (SySocketInit() != SY_OK
) goto done
;
1236 state
->ftpUseConnect
= cfgFTPUseConnect
;
1239 state
->fnprog
= PrintProgress
;
1240 state
->fnprephdrs
= SyDrvAddHeaders
;
1241 state
->fngothdrs
= SyDrvGotHeaders
;
1243 state
->fnopen
= SyDrvOpenFile
;
1244 state
->fnclose
= SyDrvCloseFile
;
1245 state
->fnwrite
= SyDrvWriteFile
;
1246 state
->fnnoresume
= SyDrvNoResume
;
1248 state
->postData
= oPostData
; oPostData
= NULL
;
1251 static int wosDo
= 0;
1257 if (!oStateFileName
) {
1258 if (!oURLList
->first
) break;
1259 oURL
= SyStrDup(oURLList
->first
->key
);
1260 SyKVListDelete(oURLList
, oURLList
->first
->key
);
1262 SyMessage(&prs
, SY_MSG_ERROR
, "memory error");
1265 /*if (!oURL) { SyMessage(&prs, SY_MSG_ERROR, "URL?"); goto done; }*/
1267 SyKVListClear(oURLList
);
1268 SyMessage(&prs
, SY_MSG_MSG
, "restoring state from file %s", oStateFileName
);
1269 if (oStateFileName
&& *oStateFileName
) {
1270 s
= SyLoadState(oStateFileName
, state
);
1273 SyMessage(&prs
, SY_MSG_ERROR
, "can't restore state");
1276 if (oOutFileName
) free(oOutFileName
);
1279 if (dumpState
== SY_TRUE
) { DumpState(state
); goto done
; }
1282 loadPerSiteConfig();
1284 if (replyOnly
== SY_FALSE
&& oResume
== SY_TRUE
&& CheckResume(state
) != SY_OK
) goto done
;
1286 //if (strcasestr(oURL, "worldofspectrum.org")) optWOS = 15; // hehe
1287 if (wosDo
&& optWOS
> 0) { SyMessage(&prs
, SY_MSG_MSG
, "WOS waiting: %d", optWOS
); sleep(optWOS
); }
1290 #ifdef SYREN_USE_SCRIPT
1292 if (!(state->url = SyURLNew())) {
1293 SyMessage(&prs, SY_MSG_ERROR, "memory error!");
1296 if (SyURLParse(state->url, oURL) != SY_OK) {
1297 SyMessage(state->pfn, SY_MSG_ERROR, "invalid URL");
1300 SyScriptCallback("Event.BeforePrepare", &prs);
1301 s = SyURL2StrEx(state->url, SY_TRUE, SY_FALSE);
1303 SyMessage(&prs, SY_MSG_ERROR, "memory error!");
1306 SyStrFree(oURL); oURL = s; s = NULL;
1307 SyURLFree(state->url); state->url = NULL;
1308 fprintf(stderr, "URL: <%s>\n", oURL);
1311 if (SyPrepare(state
, oURL
,
1312 cfgUseProxyHTTP
?cfgProxyHTTP
:NULL
,
1313 cfgUseProxyFTP
?cfgProxyFTP
:NULL
, cfgIFace
) != SY_OK
) { SyMessage(&prs
, SY_MSG_ERROR
, "SyPrepare() failed"); goto done
; }
1314 #ifdef SYREN_USE_SCRIPT
1315 SyScriptCallback("Event.AfterPrepare", &prs
);
1316 if (scAbort
== SY_TRUE
) goto done
;
1319 if (uEncodeDecode
< 0 ||
1320 (cfgDecodeFTPURL
== SY_TRUE
&& state
->url
->proto
== SY_PROTO_FTP
)) {
1321 s
= SyURLDecode(state
->url
->dir
);
1322 if (!s
) { SyMessage(&prs
, SY_MSG_ERROR
, "SyURLDecode() failed"); goto done
; }
1323 SyStrFree(state
->url
->dir
); state
->url
->dir
= s
;
1324 s
= SyURLDecode(state
->url
->file
);
1325 if (!s
) { SyMessage(&prs
, SY_MSG_ERROR
, "SyURLDecode() failed"); goto done
; }
1326 SyStrFree(state
->url
->file
); state
->url
->file
= s
;
1327 } else if (uEncodeDecode
> 0) {
1328 s
= SyURLEncode(state
->url
->dir
);
1329 if (!s
) { SyMessage(&prs
, SY_MSG_ERROR
, "SyURLEncode() failed"); goto done
; }
1330 SyStrFree(state
->url
->dir
); state
->url
->dir
= s
;
1331 s
= SyURLEncode(state
->url
->file
);
1332 if (!s
) { SyMessage(&prs
, SY_MSG_ERROR
, "SyURLEncode() failed"); goto done
; }
1333 SyStrFree(state
->url
->file
); state
->url
->file
= s
;
1336 signal(SIGHUP
, SaveStateSignal
);
1337 signal(SIGUSR1
, SaveStateSignal
);
1339 signal(SIGINT
, BreakSignal
);
1340 signal(SIGQUIT
, BreakSignal
);
1341 signal(SIGTERM
, BreakSignal
);
1343 retCnt
= cfgReconnectAttempts
;
1345 state
->initBufferSize
= cfgBufferSize
;
1346 state
->maxBufferSize
= cfgMaxBufferSize
;
1347 state
->ioTimeout
= cfgTimeout
;
1348 state
->maxRedirects
= cfgMaxRedirects
;
1349 if (cfgUAStr
&& *cfgUAStr
) {
1350 SyStrFree(state
->userAgent
);
1351 state
->userAgent
= SyStrNew(cfgUAStr
, -1);
1354 ctrlCPressed
= SY_FALSE
;
1356 state
->allowOnly2XX
= optAnyCode
=SY_TRUE
? SY_FALSE
: SY_TRUE
;
1357 res
= SyBegin(state
);
1358 if (replyOnly
!= SY_FALSE
) break;
1359 if (res
== SY_OK
) res
= SyRun(state
);
1361 if (res
== SY_OK
) break;
1362 if (state
->interrupted
== SY_TRUE
) {
1364 if (oStateFileName
&& *oStateFileName
&& state
->status
== SY_STATUS_DOWNLOADING
&& oNoStateFile
!= SY_TRUE
) {
1365 SyMessage(&prs
, SY_MSG_WARNING
, "user break, saving state");
1366 SySaveState(oStateFileName
, state
);
1367 } else SyMessage(&prs
, SY_MSG_WARNING
, "user break");
1370 if (retCnt
< 1) break;
1371 if (state
->status
!= SY_STATUS_DOWNLOADING
&& oForceRetry
!= SY_TRUE
) break;
1372 SyMessage(&prs
, SY_MSG_ERROR
, "download failed");
1373 if (cfgReconnectDelay
> 0) {
1374 SyMessage(&prs
, SY_MSG_MSG
, "waiting %i seconds", cfgReconnectDelay
);
1375 //SySleep(cfgReconnectDelay);
1376 DoWait(cfgReconnectDelay
);
1377 if (ctrlCPressed
!= SY_FALSE
) {
1378 SyMessage(&prs
, SY_MSG_WARNING
, "user break");
1384 SyMessage(&prs
, SY_MSG_MSG
, "restarting");
1385 state
->firstByte
+= state
->currentByte
; /* start from here */
1386 oCanRename
= SY_FALSE
;
1389 #ifdef SYREN_USE_SCRIPT
1390 if (res
== SY_OK
) SyScriptCallback("Event.DownloadComplete", &prs
);
1391 else SyScriptCallback("Event.DownloadFailed", &prs
);
1392 if (scAbort
== SY_TRUE
) goto done
;
1396 SyMessage(&prs
, SY_MSG_MSG
, "download complete");
1397 if (oCanRemoveState
&& oStateFileName
&& *oStateFileName
) {
1398 if (SyDeleteFile(oStateFileName
) == SY_OK
) SyMessage(&prs
, SY_MSG_NOTICE
, "state file removed");
1400 } else SyMessage(&prs
, SY_MSG_ERROR
, "download failed");
1402 mainres
= (res
==SY_OK
)?0:1;
1405 SyStrFree(oURL
); oURL
= NULL
;
1406 SyStrFree(oOutFileName
); oOutFileName
= NULL
;
1407 SyStrFree(oStateFileName
); oStateFileName
= NULL
;
1408 oCanRename
= SY_TRUE
;
1409 } while (!userBreak
);
1411 if (cfgSaveBuf
!= NULL
) free(cfgSaveBuf
);
1414 if (oURLList
) SyKVListFree(oURLList
);
1415 if (cfgDefaultName
) SyStrFree(cfgDefaultName
);
1416 if (cfgIFace
) SyStrFree(cfgIFace
);
1417 if (cfgProxyHTTP
) SyStrFree(cfgProxyHTTP
);
1418 if (cfgProxyFTP
) SyStrFree(cfgProxyFTP
);
1419 if (cfgRefererStr
) SyStrFree(cfgRefererStr
);
1420 if (cfgUAStr
) SyStrFree(cfgUAStr
);
1422 SyStrFree(oOutFileName
);
1423 SyStrFree(oStateFileName
);
1424 if (oPostData
) SyStrFree(oPostData
);
1425 if (oSendCookies
) SyStrFree(oSendCookies
);
1426 if (oCookieDump
) SyStrFree(oCookieDump
);
1427 if (oAddonHeaders
) SyStrFree(oAddonHeaders
);
1429 #ifdef SYREN_USE_SCRIPT