321 lines
18 KiB
Go
321 lines
18 KiB
Go
//go:build linux || freebsd
|
|
// +build linux freebsd
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"strings"
|
|
"time"
|
|
|
|
"codeberg.org/petrbalvin/goget/internal/config"
|
|
)
|
|
|
|
// headerFlags accumulates multiple --header values.
|
|
type headerFlags []string
|
|
|
|
func (h *headerFlags) String() string { return strings.Join(*h, ", ") }
|
|
func (h *headerFlags) Set(v string) error {
|
|
*h = append(*h, v)
|
|
return nil
|
|
}
|
|
|
|
// formFlags accumulates multiple --form values.
|
|
type formFlags []string
|
|
|
|
func (f *formFlags) String() string { return strings.Join(*f, ", ") }
|
|
func (f *formFlags) Set(v string) error {
|
|
*f = append(*f, v)
|
|
return nil
|
|
}
|
|
|
|
// Flags holds all CLI flag values.
|
|
type Flags struct {
|
|
URL *string
|
|
Output *string
|
|
Resume *bool
|
|
Overwrite *bool
|
|
Restart *bool
|
|
Verbose *bool
|
|
Quiet *bool
|
|
NoProgress *bool
|
|
JSON *bool
|
|
Debug *bool
|
|
Timeout *time.Duration
|
|
Proxy *string
|
|
NoIPv4 *bool
|
|
NoRedirect *bool
|
|
MaxRetries *int
|
|
DNSServers *string
|
|
Checksum *string
|
|
ChecksumAlgo *string
|
|
ChecksumFile *string
|
|
NoDecompress *bool
|
|
Parallel *int
|
|
MaxSpeed *string
|
|
Username *string
|
|
Password *string
|
|
PasswordFile *string
|
|
AuthType *string
|
|
CookieJar *string
|
|
ContentDisp *bool
|
|
ShowHeaders *bool
|
|
KeepTimestamps *bool
|
|
Recursive *bool
|
|
RecursiveParallel *int
|
|
MaxDepth *int
|
|
FollowExternal *bool
|
|
Mirror *bool
|
|
NoConvertLinks *bool
|
|
NoMirrorAssets *bool
|
|
NoRobots *bool
|
|
RateLimit *string
|
|
ExcludePattern *string
|
|
Extract *bool
|
|
ExtractDir *string
|
|
StripComponents *int
|
|
Upload *bool
|
|
UploadMethod *string
|
|
UploadFile *string
|
|
UploadFormData *string
|
|
UploadFileField *string
|
|
PGPDecrypt *bool
|
|
PGPVerify *bool
|
|
PGPSignature *string
|
|
PGPKey *string
|
|
PGPPassphrase *string
|
|
PGPPassphraseFile *string
|
|
OAuthClientID *string
|
|
OAuthClientSecret *string
|
|
OAuthTokenURL *string
|
|
OAuthAuthURL *string
|
|
OAuthRedirectURI *string
|
|
OAuthScopes *string
|
|
OAuthGrantType *string
|
|
OAuthAccessToken *string
|
|
OAuthRefreshToken *string
|
|
Metalink *bool
|
|
MetalinkFile *string
|
|
Queue *bool
|
|
QueueList *bool
|
|
QueueProcess *bool
|
|
QueueClear *bool
|
|
QueueFile *string
|
|
QueuePriority *int
|
|
BatchFile *string
|
|
InitConfig *bool
|
|
ConfigGet *string
|
|
ConfigSet *string
|
|
ConfigList *bool
|
|
ConfigUnset *string
|
|
Completion *string
|
|
Info *string
|
|
Benchmark *string
|
|
Schedule *string
|
|
BindInterface *string
|
|
KnownHosts *string
|
|
SSHInsecure *bool
|
|
DryRun *bool
|
|
OnComplete *string
|
|
GenManPage *bool
|
|
Help *bool
|
|
Version *bool
|
|
// curl/wget compatible flags
|
|
Headers headerFlags
|
|
Cookies headerFlags // --cookie, repeatable
|
|
Insecure *bool
|
|
CertFile *string
|
|
KeyFile *string
|
|
SSLKeyLogFile *string
|
|
Data *string
|
|
MaxTime *time.Duration
|
|
MaxFileSize *string
|
|
Accept *string
|
|
NoParent *bool
|
|
Spider *bool
|
|
WriteOut *string
|
|
ProgressStyle *string
|
|
WarcFile *string
|
|
TraceTime *bool
|
|
Form formFlags
|
|
PageRequisites *bool
|
|
SpanHosts *bool
|
|
AdjustExt *bool
|
|
NoPrivate *bool // deprecated, use AllowPrivate inversely
|
|
Wait *string
|
|
RandomWait *bool
|
|
ConvertLinks *bool
|
|
Fail *bool
|
|
Compressed *bool
|
|
Referer *string
|
|
RetryDelay *time.Duration
|
|
PinnedCert *string
|
|
PinnedHostKey *string
|
|
AllowPrivate *bool
|
|
Range *string
|
|
ConnectTimeout *time.Duration
|
|
Domains *string
|
|
Reject *string
|
|
CutDirs *int
|
|
PostFile *string
|
|
CACertificate *string
|
|
NoClobber *bool
|
|
RetryHTTPError *string
|
|
RetryAllErrors *bool
|
|
Glob *bool
|
|
InputFile *string
|
|
Timestamping *bool
|
|
BackupConverted *bool
|
|
Continue *bool
|
|
ProtoDirs *bool
|
|
HSTSFile *string
|
|
CreateDirs *bool
|
|
ShowMetadata *string
|
|
}
|
|
|
|
// defineFlags registers all CLI flags on the given FlagSet and returns them.
|
|
func defineFlags(fs *flag.FlagSet, cfg *config.Config) *Flags {
|
|
f := &Flags{
|
|
URL: fs.String("url", "", "URL to download (can be specified multiple times)"),
|
|
Output: fs.String("output", "", "Output file path"),
|
|
Resume: fs.Bool("resume", cfg.AutoResume, "Resume interrupted download"),
|
|
Overwrite: fs.Bool("overwrite", false, "Overwrite existing file"),
|
|
Restart: fs.Bool("restart", false, "Delete existing file and restart download from scratch"),
|
|
Verbose: fs.Bool("verbose", cfg.Verbose, "Verbose output"),
|
|
Quiet: fs.Bool("quiet", false, "Suppress all output except errors"),
|
|
NoProgress: fs.Bool("no-progress", false, "Hide progress bar (keep other output)"),
|
|
JSON: fs.Bool("json", false, "Output machine-readable progress as JSON lines"),
|
|
Debug: fs.Bool("debug", cfg.Debug, "Debug mode"),
|
|
Timeout: fs.Duration("timeout", 0, "Timeout (0 = auto)"),
|
|
Proxy: fs.String("proxy", cfg.Proxy, "Proxy server URL (http://, https://, socks5://, socks5h://)"),
|
|
NoIPv4: fs.Bool("no-ipv4", !cfg.IPv4Fallback, "Disable IPv4 fallback"),
|
|
NoRedirect: fs.Bool("no-redirect", false, "Disable following HTTP redirects"),
|
|
MaxRetries: fs.Int("max-retries", 0, "Maximum number of retries on failure (0 = default)"),
|
|
DNSServers: fs.String("dns-servers", "", "Custom DNS servers (comma-separated, e.g. 1.1.1.1,8.8.8.8)"),
|
|
Checksum: fs.String("checksum", "", "Expected checksum value"),
|
|
ChecksumAlgo: fs.String("checksum-algo", cfg.ChecksumAlgo, "Checksum algorithm (sha256, sha512, blake2b, sha3-256, sha3-512, md5)"),
|
|
ChecksumFile: fs.String("checksum-file", "", "Read checksum from file (SHA256SUMS format)"),
|
|
NoDecompress: fs.Bool("no-decompress", !cfg.AutoDecompress, "Disable automatic decompression"),
|
|
Parallel: fs.Int("parallel", cfg.Parallel, "Number of parallel connections (0 = auto)"),
|
|
MaxSpeed: fs.String("max-speed", "", "Max speed (e.g., 10MB/s, 0 = unlimited)"),
|
|
Username: fs.String("username", cfg.Auth.Username, "Username for HTTP Basic/Digest Auth"),
|
|
Password: fs.String("password", "", "Password for HTTP Basic/Digest Auth"),
|
|
PasswordFile: fs.String("password-file", "", "Read password from file"),
|
|
AuthType: fs.String("auth-type", cfg.Auth.AuthType, "Auth type: basic, digest, auto"),
|
|
CookieJar: fs.String("cookie-jar", cfg.CookieJar, "File for loading/saving cookies"),
|
|
ContentDisp: fs.Bool("content-disposition", false, "Use server-provided filename from Content-Disposition header"),
|
|
ShowHeaders: fs.Bool("show-headers", false, "Print HTTP response headers during download"),
|
|
KeepTimestamps: fs.Bool("keep-timestamps", false, "Set file modification time from Last-Modified header"),
|
|
Recursive: fs.Bool("recursive", cfg.Recursive.Enabled, "Recursive downloading"),
|
|
RecursiveParallel: fs.Int("recursive-parallel", cfg.Recursive.Parallel, "Number of concurrent file downloads in recursive mode (0 = sequential)"),
|
|
MaxDepth: fs.Int("max-depth", cfg.Recursive.MaxDepth, "Maximum recursion depth"),
|
|
FollowExternal: fs.Bool("follow-external", cfg.Recursive.FollowExternal, "Follow external domains"),
|
|
Mirror: fs.Bool("mirror", false, "Mirror entire website (infinite depth, convert links)"),
|
|
NoConvertLinks: fs.Bool("no-convert-links", false, "Don't convert links for local viewing in mirror mode"),
|
|
NoMirrorAssets: fs.Bool("no-mirror-assets", false, "Don't download assets (CSS, JS, images) in mirror mode"),
|
|
NoRobots: fs.Bool("no-robots", false, "Don't respect robots.txt in mirror mode"),
|
|
RateLimit: fs.String("rate-limit", "", "Rate limit (e.g., 100KB/s, 1MB/s, 0 = unlimited)"),
|
|
ExcludePattern: fs.String("exclude-pattern", strings.Join(cfg.Recursive.ExcludePatterns, ","), "Exclude URL patterns (comma-separated)"),
|
|
Extract: fs.Bool("extract", cfg.Extract.Enabled, "Auto-extract archive after download"),
|
|
ExtractDir: fs.String("extract-dir", cfg.Extract.OutputDir, "Directory for extracted files"),
|
|
StripComponents: fs.Int("strip-components", cfg.Extract.StripComponents, "Strip N path components during extraction"),
|
|
Upload: fs.Bool("upload", false, "Upload file instead of downloading"),
|
|
UploadMethod: fs.String("upload-method", "PUT", "Upload method: PUT or POST"),
|
|
UploadFile: fs.String("upload-file", "", "File to upload"),
|
|
UploadFormData: fs.String("form-data", "", "Form data (key=value, comma-separated)"),
|
|
UploadFileField: fs.String("file-field", "file", "Form field name for file upload"),
|
|
PGPDecrypt: fs.Bool("pgp-decrypt", false, "Decrypt PGP encrypted file after download"),
|
|
PGPVerify: fs.Bool("pgp-verify", false, "Verify PGP signature after download"),
|
|
PGPSignature: fs.String("pgp-sig", "", "Path to PGP signature file (.asc, .sig)"),
|
|
PGPKey: fs.String("pgp-key", "", "Path to PGP key file (public or private)"),
|
|
PGPPassphrase: fs.String("pgp-passphrase", "", "Passphrase for PGP private key"),
|
|
PGPPassphraseFile: fs.String("pgp-passphrase-file", "", "Read PGP passphrase from file"),
|
|
OAuthClientID: fs.String("oauth-client-id", cfg.Auth.OAuth.ClientID, "OAuth 2.0 Client ID"),
|
|
OAuthClientSecret: fs.String("oauth-client-secret", cfg.Auth.OAuth.ClientSecret, "OAuth 2.0 Client Secret"),
|
|
OAuthTokenURL: fs.String("oauth-token-url", cfg.Auth.OAuth.TokenURL, "OAuth 2.0 Token URL"),
|
|
OAuthAuthURL: fs.String("oauth-auth-url", cfg.Auth.OAuth.AuthURL, "OAuth 2.0 Authorization URL"),
|
|
OAuthRedirectURI: fs.String("oauth-redirect-uri", cfg.Auth.OAuth.RedirectURI, "OAuth 2.0 Redirect URI"),
|
|
OAuthScopes: fs.String("oauth-scopes", strings.Join(cfg.Auth.OAuth.Scopes, ","), "OAuth 2.0 Scopes (comma-separated)"),
|
|
OAuthGrantType: fs.String("oauth-grant-type", cfg.Auth.OAuth.GrantType, "OAuth 2.0 Grant Type (client_credentials, authorization_code, password, refresh_token)"),
|
|
OAuthAccessToken: fs.String("oauth-access-token", cfg.Auth.OAuth.AccessToken, "OAuth 2.0 Access Token (direct)"),
|
|
OAuthRefreshToken: fs.String("oauth-refresh-token", cfg.Auth.OAuth.RefreshToken, "OAuth 2.0 Refresh Token"),
|
|
Metalink: fs.Bool("metalink", false, "Treat URL as Metalink (multi-source download)"),
|
|
MetalinkFile: fs.String("metalink-file", "", "Path to local Metalink file"),
|
|
Queue: fs.Bool("queue", false, "Add to download queue instead of downloading immediately"),
|
|
QueueList: fs.Bool("queue-list", false, "List items in download queue"),
|
|
QueueProcess: fs.Bool("queue-process", false, "Process download queue"),
|
|
QueueClear: fs.Bool("queue-clear", false, "Clear completed items from queue"),
|
|
QueueFile: fs.String("queue-file", "", "Custom queue file path"),
|
|
QueuePriority: fs.Int("queue-priority", 5, "Priority for queue item (1-10)"),
|
|
BatchFile: fs.String("batch-file", "", "Download multiple URLs from a file (one per line)"),
|
|
InitConfig: fs.Bool("init-config", false, "Generate default config file"),
|
|
ConfigGet: fs.String("config-get", "", "Get a config value (e.g. --config-get timeout)"),
|
|
ConfigSet: fs.String("config-set", "", "Set a config value (e.g. --config-set timeout 5m)"),
|
|
ConfigList: fs.Bool("config-list", false, "List all config values"),
|
|
ConfigUnset: fs.String("config-unset", "", "Unset a config value (e.g. --config-unset timeout)"),
|
|
Completion: fs.String("completion", "", "Generate shell completion script (bash, zsh, fish)"),
|
|
Info: fs.String("info", "", "Show file information without downloading"),
|
|
Benchmark: fs.String("benchmark", "", "Benchmark download speed from URL (downloads first 10MB)"),
|
|
Schedule: fs.String("schedule", "", "Schedule download at specified time (e.g. '2026-06-01 02:00' or cron '0 2 * * *')"),
|
|
BindInterface: fs.String("bind-interface", "", "Bind to specific network interface"),
|
|
KnownHosts: fs.String("known-hosts", "", "Path to SSH known_hosts file for SFTP (default: ~/.ssh/known_hosts)"),
|
|
SSHInsecure: fs.Bool("ssh-insecure", false, "Skip SSH host key verification for SFTP"),
|
|
DryRun: fs.Bool("dry-run", false, "List URLs without saving files in recursive/mirror mode"),
|
|
OnComplete: fs.String("on-complete", "", "Run command after successful download"),
|
|
GenManPage: fs.Bool("generate-man-page", false, "Generate man page and exit"),
|
|
Help: fs.Bool("help", false, "Show help"),
|
|
Version: fs.Bool("version", false, "Show version"),
|
|
// curl/wget compatible flags
|
|
Insecure: fs.Bool("insecure", false, "Skip TLS certificate verification"),
|
|
CertFile: fs.String("cert", "", "Client certificate file (PEM, for mTLS)"),
|
|
KeyFile: fs.String("key", "", "Client private key file (PEM)"),
|
|
SSLKeyLogFile: fs.String("ssl-key-log", "", "File to log TLS session keys to (SSLKEYLOGFILE for Wireshark)"),
|
|
Data: fs.String("data", "", "Send POST data (e.g., --data 'key=value')"),
|
|
MaxTime: fs.Duration("max-time", 0, "Maximum total transfer time (0 = unlimited)"),
|
|
MaxFileSize: fs.String("max-filesize", "", "Maximum file size to download (e.g., 100MB)"),
|
|
Accept: fs.String("accept", "", "Accept patterns for recursive download (e.g., '*.pdf,*.html')"),
|
|
NoParent: fs.Bool("no-parent", false, "Do not ascend to parent directory in recursive download"),
|
|
Spider: fs.Bool("spider", false, "Check URL existence only (HEAD request, no download)"),
|
|
WriteOut: fs.String("write-out", "", "Output metadata after download (curl format: %{http_code}, %{size_download}, %{time_total})"),
|
|
ProgressStyle: fs.String("progress", "", "Progress display style: bar (default) or dot"),
|
|
WarcFile: fs.String("warc-file", "", "Write WARC output file for web archiving"),
|
|
TraceTime: fs.Bool("trace-time", false, "Print HTTP timing breakdown after download"),
|
|
PageRequisites: fs.Bool("page-requisites", false, "Download all assets (CSS, JS, images) needed to display the page"),
|
|
SpanHosts: fs.Bool("span-hosts", false, "Follow links to external domains in recursive download"),
|
|
AdjustExt: fs.Bool("adjust-extension", false, "Append .html extension to downloaded HTML files"),
|
|
Wait: fs.String("wait", "", "Wait between requests in recursive/mirror mode (e.g., 1s, 500ms)"),
|
|
RandomWait: fs.Bool("random-wait", false, "Randomize wait time (0.5x to 1.5x) in recursive/mirror mode"),
|
|
ConvertLinks: fs.Bool("convert-links", false, "Convert links for local viewing in recursive mode"),
|
|
Fail: fs.Bool("fail", false, "Exit with error on HTTP 4xx/5xx status codes"),
|
|
Compressed: fs.Bool("compressed", false, "Send Accept-Encoding header for auto-decompression"),
|
|
Referer: fs.String("referer", "", "Send Referer header"),
|
|
RetryDelay: fs.Duration("retry-delay", 0, "Delay between retry attempts"),
|
|
PinnedCert: fs.String("pinned-cert", "", "SHA-256 hash of server certificate for pinning (TLS protocols: HTTPS, FTPS, Gemini, WebDAVS)"),
|
|
PinnedHostKey: fs.String("pinned-host-key", "", "SSH host key fingerprint for pinning (SFTP). Accepts 'SHA256:<base64>' (ssh-keygen -lf) or raw hex SHA-256."),
|
|
AllowPrivate: fs.Bool("allow-private", false, "Allow connections to private/internal IPs (disables SSRF protection)"),
|
|
Range: fs.String("range", "", "Download only a range of bytes (e.g., 0-1000)"),
|
|
ConnectTimeout: fs.Duration("connect-timeout", 0, "Timeout for connection establishment"),
|
|
Domains: fs.String("domains", "", "Only follow these domains in recursive mode (comma-separated)"),
|
|
Reject: fs.String("reject", "", "Reject URL patterns in recursive mode (glob, comma-separated)"),
|
|
CutDirs: fs.Int("cut-dirs", 0, "Strip N directory levels when saving files"),
|
|
PostFile: fs.String("post-file", "", "Send file contents as POST body"),
|
|
CACertificate: fs.String("ca-certificate", "", "Path to custom CA certificate"),
|
|
NoClobber: fs.Bool("no-clobber", false, "Skip download if output file already exists"),
|
|
RetryHTTPError: fs.String("retry-on-http-error", "", "Retry on these HTTP status codes (comma-separated, e.g. 503,429)"),
|
|
RetryAllErrors: fs.Bool("retry-all-errors", false, "Retry on any HTTP error (4xx/5xx) instead of just selected codes"),
|
|
Glob: fs.Bool("glob", false, "Expand [1-3], {a,b} patterns in URL"),
|
|
Timestamping: fs.Bool("timestamping", false, "Download only if remote file is newer"),
|
|
BackupConverted: fs.Bool("backup-converted", false, "Backup original file before link conversion"),
|
|
Continue: fs.Bool("continue", false, "Auto-resume download if output file exists"),
|
|
ProtoDirs: fs.Bool("protocol-directories", false, "Create .http/, .ftp/ subdirectories in recursive mode"),
|
|
InputFile: fs.String("input-file", "", "Read URLs from file (use - for stdin)"),
|
|
HSTSFile: fs.String("hsts-file", "", "HSTS cache file (default: ~/.config/goget/hsts)"),
|
|
CreateDirs: fs.Bool("create-dirs", true, "Create missing parent directories of --output (curl --create-dirs)"),
|
|
ShowMetadata: fs.String("show-metadata", "", "Display .goget.meta contents for debugging interrupted/resumed downloads (path to output file, or to the .goget.meta sidecar itself)"),
|
|
}
|
|
fs.Var(&f.Headers, "header", "Custom HTTP header (repeatable, e.g. --header 'Key: Value')")
|
|
fs.Var(&f.Cookies, "cookie", "Set cookie (repeatable, e.g. --cookie 'name=value')")
|
|
fs.Var(&f.Form, "form", "Multipart form data (repeatable, e.g. --form 'field=value' or --form 'field=@file')")
|
|
return f
|
|
}
|