//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 } } }