266 lines
6.7 KiB
Go
266 lines
6.7 KiB
Go
//go:build linux || freebsd
|
|
// +build linux freebsd
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
stdhttp "net/http"
|
|
"net/url"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"codeberg.org/petrbalvin/goget/internal/auth"
|
|
"codeberg.org/petrbalvin/goget/internal/cli"
|
|
"codeberg.org/petrbalvin/goget/internal/config"
|
|
"codeberg.org/petrbalvin/goget/internal/cookie"
|
|
"codeberg.org/petrbalvin/goget/internal/core"
|
|
"codeberg.org/petrbalvin/goget/internal/protocol/http"
|
|
"codeberg.org/petrbalvin/goget/internal/transport"
|
|
)
|
|
|
|
// ApplyFlags populates the Config from CLI flags, global config, and cookie jar.
|
|
// It is the single function that maps all CLI flags to config fields — adding a
|
|
// new flag only requires adding one block here instead of hunting through
|
|
// scattered setup code.
|
|
func ApplyFlags(c *http.Config, f *Flags, cfg *config.Config, cookieJar *cookie.Jar, parsedURL *url.URL) {
|
|
// ── Transport defaults from global config ──
|
|
c.Transport.Timeout = cfg.GetEffectiveTimeout(-1, *f.Timeout)
|
|
c.Transport.Dialer.IPv4Fallback = cfg.IPv4Fallback
|
|
c.Transport.Dialer.Debug = cfg.Debug
|
|
c.UserAgent = cfg.UserAgent
|
|
c.Output.Verbose = cfg.Verbose
|
|
c.Parallel = &core.ParallelConfig{
|
|
Connections: cfg.GetParallelConnections(-1),
|
|
MinSize: 100 * 1024 * 1024,
|
|
MinChunkSize: 5 * 1024 * 1024,
|
|
MaxChunkSize: 50 * 1024 * 1024,
|
|
}
|
|
if cfg.MaxSpeed > 0 {
|
|
c.RateLimiter = transport.NewTokenBucket(cfg.MaxSpeed, cfg.MaxSpeed/10)
|
|
}
|
|
|
|
// ── DNS / interface ──
|
|
if len(cfg.DNSServers) > 0 {
|
|
c.Transport.DNSServers = cfg.DNSServers
|
|
}
|
|
if *f.BindInterface != "" {
|
|
c.Transport.BindInterface = *f.BindInterface
|
|
}
|
|
c.Transport.BlockPrivateIPs = !*f.AllowPrivate
|
|
|
|
// ── Authentication ──
|
|
if cfg.Auth.Username == "" && cfg.Auth.OAuth.AccessToken == "" {
|
|
if user, pass, ok := auth.LookupNetrc(parsedURL.Hostname()); ok {
|
|
cfg.Auth.Username = user
|
|
cfg.Auth.Password = pass
|
|
}
|
|
}
|
|
if cfg.Auth.Username != "" || cfg.Auth.OAuth.AccessToken != "" {
|
|
c.Auth.Username = cfg.Auth.Username
|
|
c.Auth.Password = cfg.Auth.Password
|
|
c.Auth.AuthType = cfg.Auth.AuthType
|
|
c.Auth.OAuth = &cfg.Auth.OAuth
|
|
}
|
|
if cookieJar != nil {
|
|
c.CookieJar = cookieJar
|
|
}
|
|
|
|
// ── TLS and certificates ──
|
|
if *f.Insecure && c.Transport.TLS != nil {
|
|
c.Transport.TLS.InsecureSkipVerify = true
|
|
}
|
|
if *f.CertFile != "" && c.Transport.TLS != nil {
|
|
c.Transport.TLS.CertFile = *f.CertFile
|
|
}
|
|
if *f.KeyFile != "" && c.Transport.TLS != nil {
|
|
c.Transport.TLS.KeyFile = *f.KeyFile
|
|
}
|
|
if *f.PinnedCert != "" && c.Transport.TLS != nil {
|
|
c.Transport.TLS.PinnedCertHash = *f.PinnedCert
|
|
}
|
|
if *f.CACertificate != "" && c.Transport.TLS != nil {
|
|
c.Transport.TLS.CACertFile = *f.CACertificate
|
|
}
|
|
if *f.SSLKeyLogFile != "" && c.Transport.TLS != nil {
|
|
c.Transport.TLS.KeyLogFile = *f.SSLKeyLogFile
|
|
}
|
|
if *f.ConnectTimeout > 0 && c.Transport.Dialer != nil {
|
|
c.Transport.Dialer.Timeout = *f.ConnectTimeout
|
|
}
|
|
|
|
// ── Retry and redirects ──
|
|
if *f.NoRedirect {
|
|
c.Transport.FollowRedirects = false
|
|
}
|
|
if *f.MaxRetries > 0 {
|
|
c.Transport.MaxRetries = *f.MaxRetries
|
|
if c.Transport.Retry != nil {
|
|
c.Transport.Retry.MaxRetries = *f.MaxRetries
|
|
}
|
|
}
|
|
if *f.RetryDelay > 0 {
|
|
c.Transport.RetryDelay = *f.RetryDelay
|
|
}
|
|
if *f.RetryHTTPError != "" {
|
|
for _, code := range strings.Split(*f.RetryHTTPError, ",") {
|
|
if code, err := strconv.Atoi(strings.TrimSpace(code)); err == nil {
|
|
c.Transport.RetryHTTPCodes = append(c.Transport.RetryHTTPCodes, code)
|
|
}
|
|
}
|
|
}
|
|
if *f.RetryAllErrors {
|
|
c.Transport.RetryAllErrors = true
|
|
}
|
|
|
|
// ── Timeouts and limits ──
|
|
if *f.ConnectTimeout > 0 {
|
|
c.Transport.ConnectTimeout = *f.ConnectTimeout
|
|
}
|
|
if *f.MaxTime > 0 {
|
|
c.Transport.MaxTime = *f.MaxTime
|
|
}
|
|
if *f.MaxFileSize != "" {
|
|
size, perr := parseRateLimit(*f.MaxFileSize)
|
|
if perr != nil {
|
|
cli.PrintWarning(os.Stderr, fmt.Sprintf("ignoring --max-filesize: %v; size limit disabled", perr))
|
|
}
|
|
c.Transport.MaxFileSize = size
|
|
}
|
|
|
|
// ── Output flags ──
|
|
if *f.ContentDisp {
|
|
c.Output.ContentDisposition = true
|
|
}
|
|
if *f.ShowHeaders {
|
|
c.Output.ShowHeaders = true
|
|
}
|
|
if *f.KeepTimestamps {
|
|
c.Output.KeepTimestamps = true
|
|
}
|
|
if *f.JSON {
|
|
c.Output.JSONOutput = true
|
|
}
|
|
if *f.OnComplete != "" {
|
|
c.Output.OnComplete = *f.OnComplete
|
|
}
|
|
if *f.WarcFile != "" {
|
|
c.Output.WarcFile = *f.WarcFile
|
|
}
|
|
if *f.TraceTime {
|
|
c.Output.TraceTime = true
|
|
}
|
|
if *f.WriteOut != "" {
|
|
c.Output.WriteOut = *f.WriteOut
|
|
}
|
|
|
|
// ── Request flags ──
|
|
if len(f.Headers) > 0 {
|
|
if c.CustomHeaders == nil {
|
|
c.CustomHeaders = make(map[string]string)
|
|
}
|
|
for _, h := range f.Headers {
|
|
parts := strings.SplitN(h, ":", 2)
|
|
key := strings.TrimSpace(parts[0])
|
|
val := ""
|
|
if len(parts) > 1 {
|
|
val = strings.TrimSpace(parts[1])
|
|
}
|
|
if strings.ContainsAny(key, "\r\n") || strings.ContainsAny(val, "\r\n") {
|
|
cli.PrintWarning(os.Stderr, fmt.Sprintf("Skipping header with CRLF: %s", key))
|
|
continue
|
|
}
|
|
c.CustomHeaders[key] = val
|
|
}
|
|
}
|
|
if *f.Data != "" {
|
|
c.PostData = *f.Data
|
|
}
|
|
if *f.PostFile != "" {
|
|
c.PostFile = *f.PostFile
|
|
}
|
|
if *f.Range != "" {
|
|
c.Range = *f.Range
|
|
}
|
|
if *f.Compressed {
|
|
c.Compressed = true
|
|
}
|
|
if *f.Referer != "" {
|
|
c.Referer = *f.Referer
|
|
}
|
|
if *f.Fail {
|
|
c.FailOnHTTPError = true
|
|
}
|
|
if *f.NoClobber {
|
|
c.Transport.NoClobber = true
|
|
}
|
|
if len(f.Form) > 0 {
|
|
c.FormData = f.Form
|
|
}
|
|
|
|
// ── Recursive flags ──
|
|
if *f.RecursiveParallel > 0 {
|
|
c.Recursive.Parallel = *f.RecursiveParallel
|
|
}
|
|
if *f.Wait != "" {
|
|
if d, err := time.ParseDuration(*f.Wait); err == nil {
|
|
c.Recursive.Wait = d
|
|
}
|
|
}
|
|
if *f.RandomWait {
|
|
c.Recursive.RandomWait = true
|
|
}
|
|
if *f.ConvertLinks {
|
|
c.Recursive.ConvertLinks = true
|
|
}
|
|
if *f.Accept != "" {
|
|
c.Recursive.Accept = *f.Accept
|
|
}
|
|
if *f.Domains != "" {
|
|
c.Recursive.Domains = strings.Split(*f.Domains, ",")
|
|
}
|
|
if *f.Reject != "" {
|
|
c.Recursive.Reject = strings.Split(*f.Reject, ",")
|
|
}
|
|
if *f.CutDirs > 0 {
|
|
c.Recursive.CutDirs = *f.CutDirs
|
|
}
|
|
if *f.PageRequisites {
|
|
c.Recursive.PageRequisites = true
|
|
}
|
|
if *f.SpanHosts {
|
|
c.Recursive.SpanHosts = true
|
|
}
|
|
if *f.AdjustExt {
|
|
c.Recursive.AdjustExt = true
|
|
}
|
|
if *f.BackupConverted {
|
|
c.Recursive.BackupConverted = true
|
|
}
|
|
|
|
// ── Cookies from CLI flags ──
|
|
if len(f.Cookies) > 0 {
|
|
if cookieJar == nil {
|
|
var err error
|
|
cookieJar, err = cookie.NewJar()
|
|
if err != nil {
|
|
cli.PrintWarning(os.Stderr, fmt.Sprintf("failed to create cookie jar: %v", err))
|
|
}
|
|
}
|
|
if cookieJar != nil {
|
|
for _, cstr := range f.Cookies {
|
|
parts := strings.SplitN(cstr, "=", 2)
|
|
if len(parts) < 2 {
|
|
cli.PrintWarning(os.Stderr, fmt.Sprintf("invalid cookie format (use name=value): %s", cstr))
|
|
continue
|
|
}
|
|
ck := &stdhttp.Cookie{Name: parts[0], Value: parts[1]}
|
|
cookieJar.SetCookies(parsedURL, []*stdhttp.Cookie{ck})
|
|
}
|
|
c.CookieJar = cookieJar
|
|
}
|
|
}
|
|
}
|