xref: /aosp_15_r20/external/spdx-tools/tvloader/parser2v1/parse_package.go (revision ba677afa8f67bb56cbc794f4d0e378e0da058e16)
1// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2
3package parser2v1
4
5import (
6	"fmt"
7	"strings"
8
9	"github.com/spdx/tools-golang/spdx/common"
10	"github.com/spdx/tools-golang/spdx/v2_1"
11)
12
13func (parser *tvParser2_1) parsePairFromPackage2_1(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 spdxId or not
25			if parser.pkg != nil && parser.pkg.PackageSPDXIdentifier == nullSpdxElementId2_1 {
26				return fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName)
27			}
28			parser.pkg = &v2_1.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_1
37		return parser.parsePairFromFile2_1(tag, value)
38	// tag for going on to other license section
39	case "LicenseID":
40		parser.st = psOtherLicense2_1
41		return parser.parsePairFromOtherLicense2_1(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_1.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		parser.pkg.PackageSupplier = &common.Supplier{}
58		if value == "NOASSERTION" {
59			parser.pkg.PackageSupplier.Supplier = value
60			break
61		}
62		subkey, subvalue, err := extractSubs(value)
63		if err != nil {
64			return err
65		}
66		switch subkey {
67		case "Person", "Organization":
68			parser.pkg.PackageSupplier.Supplier = subvalue
69			parser.pkg.PackageSupplier.SupplierType = subkey
70		default:
71			return fmt.Errorf("unrecognized PackageSupplier type %v", subkey)
72		}
73	case "PackageOriginator":
74		parser.pkg.PackageOriginator = &common.Originator{}
75		if value == "NOASSERTION" {
76			parser.pkg.PackageOriginator.Originator = value
77			break
78		}
79		subkey, subvalue, err := extractSubs(value)
80		if err != nil {
81			return err
82		}
83		switch subkey {
84		case "Person", "Organization":
85			parser.pkg.PackageOriginator.Originator = subvalue
86			parser.pkg.PackageOriginator.OriginatorType = subkey
87		default:
88			return fmt.Errorf("unrecognized PackageOriginator type %v", subkey)
89		}
90	case "PackageDownloadLocation":
91		parser.pkg.PackageDownloadLocation = value
92	case "FilesAnalyzed":
93		parser.pkg.IsFilesAnalyzedTagPresent = true
94		if value == "false" {
95			parser.pkg.FilesAnalyzed = false
96		} else if value == "true" {
97			parser.pkg.FilesAnalyzed = true
98		}
99	case "PackageVerificationCode":
100		parser.pkg.PackageVerificationCode = extractCodeAndExcludes(value)
101	case "PackageChecksum":
102		subkey, subvalue, err := extractSubs(value)
103		if err != nil {
104			return err
105		}
106		if parser.pkg.PackageChecksums == nil {
107			parser.pkg.PackageChecksums = []common.Checksum{}
108		}
109		switch common.ChecksumAlgorithm(subkey) {
110		case common.SHA1, common.SHA256, common.MD5:
111			algorithm := common.ChecksumAlgorithm(subkey)
112			parser.pkg.PackageChecksums = append(parser.pkg.PackageChecksums, common.Checksum{Algorithm: algorithm, Value: subvalue})
113		default:
114			return fmt.Errorf("got unknown checksum type %s", subkey)
115		}
116	case "PackageHomePage":
117		parser.pkg.PackageHomePage = value
118	case "PackageSourceInfo":
119		parser.pkg.PackageSourceInfo = value
120	case "PackageLicenseConcluded":
121		parser.pkg.PackageLicenseConcluded = value
122	case "PackageLicenseInfoFromFiles":
123		parser.pkg.PackageLicenseInfoFromFiles = append(parser.pkg.PackageLicenseInfoFromFiles, value)
124	case "PackageLicenseDeclared":
125		parser.pkg.PackageLicenseDeclared = value
126	case "PackageLicenseComments":
127		parser.pkg.PackageLicenseComments = value
128	case "PackageCopyrightText":
129		parser.pkg.PackageCopyrightText = value
130	case "PackageSummary":
131		parser.pkg.PackageSummary = value
132	case "PackageDescription":
133		parser.pkg.PackageDescription = value
134	case "PackageComment":
135		parser.pkg.PackageComment = value
136	case "ExternalRef":
137		parser.pkgExtRef = &v2_1.PackageExternalReference{}
138		parser.pkg.PackageExternalReferences = append(parser.pkg.PackageExternalReferences, parser.pkgExtRef)
139		category, refType, locator, err := extractPackageExternalReference(value)
140		if err != nil {
141			return err
142		}
143		parser.pkgExtRef.Category = category
144		parser.pkgExtRef.RefType = refType
145		parser.pkgExtRef.Locator = locator
146	case "ExternalRefComment":
147		if parser.pkgExtRef == nil {
148			return fmt.Errorf("no current ExternalRef found")
149		}
150		parser.pkgExtRef.ExternalRefComment = value
151		// now, expire pkgExtRef anyway because it can have at most one comment
152		parser.pkgExtRef = nil
153	// for relationship tags, pass along but don't change state
154	case "Relationship":
155		parser.rln = &v2_1.Relationship{}
156		parser.doc.Relationships = append(parser.doc.Relationships, parser.rln)
157		return parser.parsePairForRelationship2_1(tag, value)
158	case "RelationshipComment":
159		return parser.parsePairForRelationship2_1(tag, value)
160	// for annotation tags, pass along but don't change state
161	case "Annotator":
162		parser.ann = &v2_1.Annotation{}
163		parser.doc.Annotations = append(parser.doc.Annotations, parser.ann)
164		return parser.parsePairForAnnotation2_1(tag, value)
165	case "AnnotationDate":
166		return parser.parsePairForAnnotation2_1(tag, value)
167	case "AnnotationType":
168		return parser.parsePairForAnnotation2_1(tag, value)
169	case "SPDXREF":
170		return parser.parsePairForAnnotation2_1(tag, value)
171	case "AnnotationComment":
172		return parser.parsePairForAnnotation2_1(tag, value)
173	// tag for going on to review section (DEPRECATED)
174	case "Reviewer":
175		parser.st = psReview2_1
176		return parser.parsePairFromReview2_1(tag, value)
177	default:
178		return fmt.Errorf("received unknown tag %v in Package section", tag)
179	}
180
181	return nil
182}
183
184// ===== Helper functions =====
185
186func extractCodeAndExcludes(value string) common.PackageVerificationCode {
187	// FIXME this should probably be done using regular expressions instead
188	// split by paren + word "excludes:"
189	sp := strings.SplitN(value, "(excludes:", 2)
190	if len(sp) < 2 {
191		// not found; return the whole string as just the code
192		return common.PackageVerificationCode{Value: value, ExcludedFiles: []string{}}
193	}
194
195	// if we're here, code is in first part and excludes filename is in
196	// second part, with trailing paren
197	code := strings.TrimSpace(sp[0])
198	parsedSp := strings.SplitN(sp[1], ")", 2)
199	fileName := strings.TrimSpace(parsedSp[0])
200	return common.PackageVerificationCode{Value: code, ExcludedFiles: []string{fileName}}
201}
202
203func extractPackageExternalReference(value string) (string, string, string, error) {
204	sp := strings.Split(value, " ")
205	// remove any that are just whitespace
206	keepSp := []string{}
207	for _, s := range sp {
208		ss := strings.TrimSpace(s)
209		if ss != "" {
210			keepSp = append(keepSp, ss)
211		}
212	}
213	// now, should have 3 items and should be able to map them
214	if len(keepSp) != 3 {
215		return "", "", "", fmt.Errorf("expected 3 elements, got %d", len(keepSp))
216	}
217	return keepSp[0], keepSp[1], keepSp[2], nil
218}
219