xref: /aosp_15_r20/external/spdx-tools/tvloader/parser2v2/parse_package.go (revision ba677afa8f67bb56cbc794f4d0e378e0da058e16)
1// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2
3package parser2v2
4
5import (
6	"fmt"
7	"strings"
8
9	"github.com/spdx/tools-golang/spdx/common"
10	"github.com/spdx/tools-golang/spdx/v2_2"
11)
12
13func (parser *tvParser2_2) parsePairFromPackage2_2(tag string, value string) error {
14	// expire pkgExtRef for anything other than a comment
15	// (we'll actually handle the comment further below)
16	if tag != "ExternalRefComment" {
17		parser.pkgExtRef = nil
18	}
19
20	switch tag {
21	case "PackageName":
22		// if package already has a name, create and go on to a new package
23		if parser.pkg == nil || parser.pkg.PackageName != "" {
24			// check if the previous package contained an spdx Id or not
25			if parser.pkg != nil && parser.pkg.PackageSPDXIdentifier == nullSpdxElementId2_2 {
26				return fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName)
27			}
28			parser.pkg = &v2_2.Package{
29				FilesAnalyzed:             true,
30				IsFilesAnalyzedTagPresent: false,
31			}
32		}
33		parser.pkg.PackageName = value
34	// tag for going on to file section
35	case "FileName":
36		parser.st = psFile2_2
37		return parser.parsePairFromFile2_2(tag, value)
38	// tag for going on to other license section
39	case "LicenseID":
40		parser.st = psOtherLicense2_2
41		return parser.parsePairFromOtherLicense2_2(tag, value)
42	case "SPDXID":
43		eID, err := extractElementID(value)
44		if err != nil {
45			return err
46		}
47		parser.pkg.PackageSPDXIdentifier = eID
48		if parser.doc.Packages == nil {
49			parser.doc.Packages = []*v2_2.Package{}
50		}
51		parser.doc.Packages = append(parser.doc.Packages, parser.pkg)
52	case "PackageVersion":
53		parser.pkg.PackageVersion = value
54	case "PackageFileName":
55		parser.pkg.PackageFileName = value
56	case "PackageSupplier":
57		supplier := &common.Supplier{Supplier: value}
58		if value == "NOASSERTION" {
59			parser.pkg.PackageSupplier = supplier
60			break
61		}
62
63		subkey, subvalue, err := extractSubs(value)
64		if err != nil {
65			return err
66		}
67		switch subkey {
68		case "Person", "Organization":
69			supplier.Supplier = subvalue
70			supplier.SupplierType = subkey
71		default:
72			return fmt.Errorf("unrecognized PackageSupplier type %v", subkey)
73		}
74		parser.pkg.PackageSupplier = supplier
75	case "PackageOriginator":
76		originator := &common.Originator{Originator: value}
77		if value == "NOASSERTION" {
78			parser.pkg.PackageOriginator = originator
79			break
80		}
81
82		subkey, subvalue, err := extractSubs(value)
83		if err != nil {
84			return err
85		}
86		switch subkey {
87		case "Person", "Organization":
88			originator.Originator = subvalue
89			originator.OriginatorType = subkey
90		default:
91			return fmt.Errorf("unrecognized PackageOriginator type %v", subkey)
92		}
93		parser.pkg.PackageOriginator = originator
94	case "PackageDownloadLocation":
95		parser.pkg.PackageDownloadLocation = value
96	case "FilesAnalyzed":
97		parser.pkg.IsFilesAnalyzedTagPresent = true
98		if value == "false" {
99			parser.pkg.FilesAnalyzed = false
100		} else if value == "true" {
101			parser.pkg.FilesAnalyzed = true
102		}
103	case "PackageVerificationCode":
104		parser.pkg.PackageVerificationCode = extractCodeAndExcludes(value)
105	case "PackageChecksum":
106		subkey, subvalue, err := extractSubs(value)
107		if err != nil {
108			return err
109		}
110		if parser.pkg.PackageChecksums == nil {
111			parser.pkg.PackageChecksums = []common.Checksum{}
112		}
113		switch common.ChecksumAlgorithm(subkey) {
114		case common.SHA1,
115			common.SHA224,
116			common.SHA256,
117			common.SHA384,
118			common.SHA512,
119			common.MD2,
120			common.MD4,
121			common.MD5,
122			common.MD6:
123			algorithm := common.ChecksumAlgorithm(subkey)
124			parser.pkg.PackageChecksums = append(parser.pkg.PackageChecksums, common.Checksum{Algorithm: algorithm, Value: subvalue})
125		default:
126			return fmt.Errorf("got unknown checksum type %s", subkey)
127		}
128	case "PackageHomePage":
129		parser.pkg.PackageHomePage = value
130	case "PackageSourceInfo":
131		parser.pkg.PackageSourceInfo = value
132	case "PackageLicenseConcluded":
133		parser.pkg.PackageLicenseConcluded = value
134	case "PackageLicenseInfoFromFiles":
135		parser.pkg.PackageLicenseInfoFromFiles = append(parser.pkg.PackageLicenseInfoFromFiles, value)
136	case "PackageLicenseDeclared":
137		parser.pkg.PackageLicenseDeclared = value
138	case "PackageLicenseComments":
139		parser.pkg.PackageLicenseComments = value
140	case "PackageCopyrightText":
141		parser.pkg.PackageCopyrightText = value
142	case "PackageSummary":
143		parser.pkg.PackageSummary = value
144	case "PackageDescription":
145		parser.pkg.PackageDescription = value
146	case "PackageComment":
147		parser.pkg.PackageComment = value
148	case "PackageAttributionText":
149		parser.pkg.PackageAttributionTexts = append(parser.pkg.PackageAttributionTexts, value)
150	case "ExternalRef":
151		parser.pkgExtRef = &v2_2.PackageExternalReference{}
152		parser.pkg.PackageExternalReferences = append(parser.pkg.PackageExternalReferences, parser.pkgExtRef)
153		category, refType, locator, err := extractPackageExternalReference(value)
154		if err != nil {
155			return err
156		}
157		parser.pkgExtRef.Category = category
158		parser.pkgExtRef.RefType = refType
159		parser.pkgExtRef.Locator = locator
160	case "ExternalRefComment":
161		if parser.pkgExtRef == nil {
162			return fmt.Errorf("no current ExternalRef found")
163		}
164		parser.pkgExtRef.ExternalRefComment = value
165		// now, expire pkgExtRef anyway because it can have at most one comment
166		parser.pkgExtRef = nil
167	// for relationship tags, pass along but don't change state
168	case "Relationship":
169		parser.rln = &v2_2.Relationship{}
170		parser.doc.Relationships = append(parser.doc.Relationships, parser.rln)
171		return parser.parsePairForRelationship2_2(tag, value)
172	case "RelationshipComment":
173		return parser.parsePairForRelationship2_2(tag, value)
174	// for annotation tags, pass along but don't change state
175	case "Annotator":
176		parser.ann = &v2_2.Annotation{}
177		parser.doc.Annotations = append(parser.doc.Annotations, parser.ann)
178		return parser.parsePairForAnnotation2_2(tag, value)
179	case "AnnotationDate":
180		return parser.parsePairForAnnotation2_2(tag, value)
181	case "AnnotationType":
182		return parser.parsePairForAnnotation2_2(tag, value)
183	case "SPDXREF":
184		return parser.parsePairForAnnotation2_2(tag, value)
185	case "AnnotationComment":
186		return parser.parsePairForAnnotation2_2(tag, value)
187	// tag for going on to review section (DEPRECATED)
188	case "Reviewer":
189		parser.st = psReview2_2
190		return parser.parsePairFromReview2_2(tag, value)
191	default:
192		return fmt.Errorf("received unknown tag %v in Package section", tag)
193	}
194
195	return nil
196}
197
198// ===== Helper functions =====
199
200func extractCodeAndExcludes(value string) common.PackageVerificationCode {
201	// FIXME this should probably be done using regular expressions instead
202	// split by paren + word "excludes:"
203	sp := strings.SplitN(value, "(excludes:", 2)
204	if len(sp) < 2 {
205		// not found; return the whole string as just the code
206		return common.PackageVerificationCode{Value: value, ExcludedFiles: []string{}}
207	}
208
209	// if we're here, code is in first part and excludes filename is in
210	// second part, with trailing paren
211	code := strings.TrimSpace(sp[0])
212	parsedSp := strings.SplitN(sp[1], ")", 2)
213	fileName := strings.TrimSpace(parsedSp[0])
214	return common.PackageVerificationCode{Value: code, ExcludedFiles: []string{fileName}}
215}
216
217func extractPackageExternalReference(value string) (string, string, string, error) {
218	sp := strings.Split(value, " ")
219	// remove any that are just whitespace
220	keepSp := []string{}
221	for _, s := range sp {
222		ss := strings.TrimSpace(s)
223		if ss != "" {
224			keepSp = append(keepSp, ss)
225		}
226	}
227	// now, should have 3 items and should be able to map them
228	if len(keepSp) != 3 {
229		return "", "", "", fmt.Errorf("expected 3 elements, got %d", len(keepSp))
230	}
231	return keepSp[0], keepSp[1], keepSp[2], nil
232}
233