1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package main
6
7import (
8 "fmt"
9 "strings"
10 "unicode"
11)
12
13func validateTrailer(trailer string) error {
14 if trailer == "" {
15 return fmt.Errorf("trailer cannot be empty")
16 }
17
18 lines := strings.Split(trailer, "\n")
19 firstLine := lines[0]
20
21 if len(firstLine) > 0 && unicode.IsSpace(rune(firstLine[0])) {
22 return fmt.Errorf("trailer key cannot start with whitespace: %q", trailer)
23 }
24
25 colonIdx := strings.Index(firstLine, ":")
26 if colonIdx == -1 {
27 return fmt.Errorf("trailer must contain ':' separator: %q", trailer)
28 }
29
30 key := firstLine[:colonIdx]
31
32 if strings.TrimSpace(key) != key {
33 return fmt.Errorf("trailer key cannot have leading or trailing whitespace: %q", key)
34 }
35
36 if strings.ContainsAny(key, " \t") {
37 return fmt.Errorf("trailer key cannot contain whitespace: %q", key)
38 }
39
40 if key == "" {
41 return fmt.Errorf("trailer key cannot be empty: %q", trailer)
42 }
43
44 for i, line := range lines[1:] {
45 if line == "" {
46 continue
47 }
48 if len(line) > 0 && !unicode.IsSpace(rune(line[0])) {
49 return fmt.Errorf("trailer continuation line %d must start with whitespace: %q", i+2, line)
50 }
51 }
52
53 return nil
54}
55
56func buildTrailersBlock(trailers []string) (string, error) {
57 if len(trailers) == 0 {
58 return "", nil
59 }
60
61 for _, trailer := range trailers {
62 if err := validateTrailer(trailer); err != nil {
63 return "", err
64 }
65 }
66
67 return strings.Join(trailers, "\n"), nil
68}