xref: /aosp_15_r20/external/spdx-tools/tvloader/parser2v3/parse_package.go (revision ba677afa8f67bb56cbc794f4d0e378e0da058e16)
1// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2
3package parser2v3
4
5import (
6	"fmt"
7	"strings"
8
9	"github.com/spdx/tools-golang/spdx/common"
10	"github.com/spdx/tools-golang/spdx/v2_3"
11)
12
13func (parser *tvParser2_3) parsePairFromPackage2_3(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_3 {
26				return fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName)
27			}
28			parser.pkg = &v2_3.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_3
37		return parser.parsePairFromFile2_3(tag, value)
38	// tag for going on to other license section
39	case "LicenseID":
40		parser.st = psOtherLicense2_3
41		return parser.parsePairFromOtherLicense2_3(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_3.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			common.SHA3_256,
124			common.SHA3_384,
125			common.SHA3_512,
126			common.BLAKE2b_256,
127			common.BLAKE2b_384,
128			common.BLAKE2b_512,
129			common.BLAKE3,
130			common.ADLER32:
131			algorithm := common.ChecksumAlgorithm(subkey)
132			parser.pkg.PackageChecksums = append(parser.pkg.PackageChecksums, common.Checksum{Algorithm: algorithm, Value: subvalue})
133		default:
134			return fmt.Errorf("got unknown checksum type %s", subkey)
135		}
136	case "PackageHomePage":
137		parser.pkg.PackageHomePage = value
138	case "PackageSourceInfo":
139		parser.pkg.PackageSourceInfo = value
140	case "PackageLicenseConcluded":
141		parser.pkg.PackageLicenseConcluded = value
142	case "PackageLicenseInfoFromFiles":
143		parser.pkg.PackageLicenseInfoFromFiles = append(parser.pkg.PackageLicenseInfoFromFiles, value)
144	case "PackageLicenseDeclared":
145		parser.pkg.PackageLicenseDeclared = value
146	case "PackageLicenseComments":
147		parser.pkg.PackageLicenseComments = value
148	case "PackageCopyrightText":
149		parser.pkg.PackageCopyrightText = value
150	case "PackageSummary":
151		parser.pkg.PackageSummary = value
152	case "PackageDescription":
153		parser.pkg.PackageDescription = value
154	case "PackageComment":
155		parser.pkg.PackageComment = value
156	case "PrimaryPackagePurpose":
157		parser.pkg.PrimaryPackagePurpose = value
158	case "ReleaseDate":
159		parser.pkg.ReleaseDate = value
160	case "BuiltDate":
161		parser.pkg.BuiltDate = value
162	case "ValidUntilDate":
163		parser.pkg.ValidUntilDate = value
164	case "PackageAttributionText":
165		parser.pkg.PackageAttributionTexts = append(parser.pkg.PackageAttributionTexts, value)
166	case "ExternalRef":
167		parser.pkgExtRef = &v2_3.PackageExternalReference{}
168		parser.pkg.PackageExternalReferences = append(parser.pkg.PackageExternalReferences, parser.pkgExtRef)
169		category, refType, locator, err := extractPackageExternalReference(value)
170		if err != nil {
171			return err
172		}
173		parser.pkgExtRef.Category = category
174		parser.pkgExtRef.RefType = refType
175		parser.pkgExtRef.Locator = locator
176	case "ExternalRefComment":
177		if parser.pkgExtRef == nil {
178			return fmt.Errorf("no current ExternalRef found")
179		}
180		parser.pkgExtRef.ExternalRefComment = value
181		// now, expire pkgExtRef anyway because it can have at most one comment
182		parser.pkgExtRef = nil
183	// for relationship tags, pass along but don't change state
184	case "Relationship":
185		parser.rln = &v2_3.Relationship{}
186		parser.doc.Relationships = append(parser.doc.Relationships, parser.rln)
187		return parser.parsePairForRelationship2_3(tag, value)
188	case "RelationshipComment":
189		return parser.parsePairForRelationship2_3(tag, value)
190	// for annotation tags, pass along but don't change state
191	case "Annotator":
192		parser.ann = &v2_3.Annotation{}
193		parser.doc.Annotations = append(parser.doc.Annotations, parser.ann)
194		return parser.parsePairForAnnotation2_3(tag, value)
195	case "AnnotationDate":
196		return parser.parsePairForAnnotation2_3(tag, value)
197	case "AnnotationType":
198		return parser.parsePairForAnnotation2_3(tag, value)
199	case "SPDXREF":
200		return parser.parsePairForAnnotation2_3(tag, value)
201	case "AnnotationComment":
202		return parser.parsePairForAnnotation2_3(tag, value)
203	// tag for going on to review section (DEPRECATED)
204	case "Reviewer":
205		parser.st = psReview2_3
206		return parser.parsePairFromReview2_3(tag, value)
207	default:
208		return fmt.Errorf("received unknown tag %v in Package section", tag)
209	}
210
211	return nil
212}
213
214// ===== Helper functions =====
215
216func extractCodeAndExcludes(value string) *common.PackageVerificationCode {
217	// FIXME this should probably be done using regular expressions instead
218	// split by paren + word "excludes:"
219	sp := strings.SplitN(value, "(excludes:", 2)
220	if len(sp) < 2 {
221		// not found; return the whole string as just the code
222		return &common.PackageVerificationCode{Value: value, ExcludedFiles: []string{}}
223	}
224
225	// if we're here, code is in first part and excludes filename is in
226	// second part, with trailing paren
227	code := strings.TrimSpace(sp[0])
228	parsedSp := strings.SplitN(sp[1], ")", 2)
229	fileName := strings.TrimSpace(parsedSp[0])
230	return &common.PackageVerificationCode{Value: code, ExcludedFiles: []string{fileName}}
231}
232
233func extractPackageExternalReference(value string) (string, string, string, error) {
234	sp := strings.Split(value, " ")
235	// remove any that are just whitespace
236	keepSp := []string{}
237	for _, s := range sp {
238		ss := strings.TrimSpace(s)
239		if ss != "" {
240			keepSp = append(keepSp, ss)
241		}
242	}
243	// now, should have 3 items and should be able to map them
244	if len(keepSp) != 3 {
245		return "", "", "", fmt.Errorf("expected 3 elements, got %d", len(keepSp))
246	}
247	return keepSp[0], keepSp[1], keepSp[2], nil
248}
249