//go:build linux || freebsd // +build linux freebsd package dataurl import ( "context" "encoding/base64" "io" "net/url" "os" "strings" "time" "codeberg.org/petrbalvin/goget/internal/core" "codeberg.org/petrbalvin/goget/internal/protocol" ) // Protocol handles data: URLs (RFC 2397). type Protocol struct { *protocol.BaseProtocol } // NewProtocol creates a new data: URL protocol handler. func NewProtocol() *Protocol { return &Protocol{ BaseProtocol: protocol.NewBaseProtocol(protocol.ProtocolInfo{ Name: "DATA", Scheme: "data", DefaultPort: 0, Features: []string{}, }), } } func (p *Protocol) Download(ctx context.Context, req *core.DownloadRequest) (*core.DownloadResult, error) { startTime := time.Now() // data:[][;base64], data, err := Decode(req.URL.String()) if err != nil { return nil, core.NewProtocolError("failed to decode data URL", err, core.SafeURL(req.URL)) } var writer io.Writer outputPath := req.Output if req.Writer != nil { writer = req.Writer } else if outputPath == "" || outputPath == "-" { writer = os.Stdout outputPath = "-" } else { f, err := os.Create(outputPath) if err != nil { return nil, core.NewFileError("failed to create output file", err) } defer f.Close() writer = f } n, err := writer.Write(data) if err != nil { return nil, core.NewFileError("failed to write data URL content", err) } duration := time.Since(startTime) return &core.DownloadResult{ BytesDownloaded: int64(n), TotalSize: int64(len(data)), Duration: duration, Protocol: "DATA", IPVersion: 0, Speed: float64(n) / duration.Seconds(), OutputPath: outputPath, }, nil } // Decode parses and decodes a data: URL. func Decode(rawURL string) ([]byte, error) { if !strings.HasPrefix(rawURL, "data:") { return nil, nil } content := strings.TrimPrefix(rawURL, "data:") isBase64 := false if idx := strings.Index(content, ";base64,"); idx >= 0 { isBase64 = true content = content[idx+8:] } else if idx := strings.Index(content, ","); idx >= 0 { content = content[idx+1:] } decoded, err := url.PathUnescape(content) if err != nil { decoded = content } if isBase64 { return base64.StdEncoding.DecodeString(decoded) } return []byte(decoded), nil }