• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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) parsePairFromCreationInfo2_3(tag string, value string) error {
14	// fail if not in Creation Info parser state
15	if parser.st != psCreationInfo2_3 {
16		return fmt.Errorf("got invalid state %v in parsePairFromCreationInfo2_3", parser.st)
17	}
18
19	// create an SPDX Creation Info data struct if we don't have one already
20	if parser.doc.CreationInfo == nil {
21		parser.doc.CreationInfo = &v2_3.CreationInfo{}
22	}
23
24	ci := parser.doc.CreationInfo
25	switch tag {
26	case "LicenseListVersion":
27		ci.LicenseListVersion = value
28	case "Creator":
29		subkey, subvalue, err := extractSubs(value)
30		if err != nil {
31			return err
32		}
33
34		creator := common.Creator{Creator: subvalue}
35		switch subkey {
36		case "Person", "Organization", "Tool":
37			creator.CreatorType = subkey
38		default:
39			return fmt.Errorf("unrecognized Creator type %v", subkey)
40		}
41
42		ci.Creators = append(ci.Creators, creator)
43	case "Created":
44		ci.Created = value
45	case "CreatorComment":
46		ci.CreatorComment = value
47
48	// tag for going on to package section
49	case "PackageName":
50		// error if last file does not have an identifier
51		// this may be a null case: can we ever have a "last file" in
52		// the "creation info" state? should go on to "file" state
53		// even when parsing unpackaged files.
54		if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId2_3 {
55			return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName)
56		}
57		parser.st = psPackage2_3
58		parser.pkg = &v2_3.Package{
59			FilesAnalyzed:             true,
60			IsFilesAnalyzedTagPresent: false,
61		}
62		return parser.parsePairFromPackage2_3(tag, value)
63	// tag for going on to _unpackaged_ file section
64	case "FileName":
65		// leave pkg as nil, so that packages will be placed in Files
66		parser.st = psFile2_3
67		parser.pkg = nil
68		return parser.parsePairFromFile2_3(tag, value)
69	// tag for going on to other license section
70	case "LicenseID":
71		parser.st = psOtherLicense2_3
72		return parser.parsePairFromOtherLicense2_3(tag, value)
73	// tag for going on to review section (DEPRECATED)
74	case "Reviewer":
75		parser.st = psReview2_3
76		return parser.parsePairFromReview2_3(tag, value)
77	// for relationship tags, pass along but don't change state
78	case "Relationship":
79		parser.rln = &v2_3.Relationship{}
80		parser.doc.Relationships = append(parser.doc.Relationships, parser.rln)
81		return parser.parsePairForRelationship2_3(tag, value)
82	case "RelationshipComment":
83		return parser.parsePairForRelationship2_3(tag, value)
84	// for annotation tags, pass along but don't change state
85	case "Annotator":
86		parser.ann = &v2_3.Annotation{}
87		parser.doc.Annotations = append(parser.doc.Annotations, parser.ann)
88		return parser.parsePairForAnnotation2_3(tag, value)
89	case "AnnotationDate":
90		return parser.parsePairForAnnotation2_3(tag, value)
91	case "AnnotationType":
92		return parser.parsePairForAnnotation2_3(tag, value)
93	case "SPDXREF":
94		return parser.parsePairForAnnotation2_3(tag, value)
95	case "AnnotationComment":
96		return parser.parsePairForAnnotation2_3(tag, value)
97	default:
98		return fmt.Errorf("received unknown tag %v in CreationInfo section", tag)
99	}
100
101	return nil
102}
103
104// ===== Helper functions =====
105
106func extractExternalDocumentReference(value string) (string, string, string, string, error) {
107	sp := strings.Split(value, " ")
108	// remove any that are just whitespace
109	keepSp := []string{}
110	for _, s := range sp {
111		ss := strings.TrimSpace(s)
112		if ss != "" {
113			keepSp = append(keepSp, ss)
114		}
115	}
116
117	var documentRefID, uri, alg, checksum string
118
119	// now, should have 4 items (or 3, if Alg and Checksum were joined)
120	// and should be able to map them
121	if len(keepSp) == 4 {
122		documentRefID = keepSp[0]
123		uri = keepSp[1]
124		alg = keepSp[2]
125		// check that colon is present for alg, and remove it
126		if !strings.HasSuffix(alg, ":") {
127			return "", "", "", "", fmt.Errorf("algorithm does not end with colon")
128		}
129		alg = strings.TrimSuffix(alg, ":")
130		checksum = keepSp[3]
131	} else if len(keepSp) == 3 {
132		documentRefID = keepSp[0]
133		uri = keepSp[1]
134		// split on colon into alg and checksum
135		parts := strings.SplitN(keepSp[2], ":", 2)
136		if len(parts) != 2 {
137			return "", "", "", "", fmt.Errorf("missing colon separator between algorithm and checksum")
138		}
139		alg = parts[0]
140		checksum = parts[1]
141	} else {
142		return "", "", "", "", fmt.Errorf("expected 4 elements, got %d", len(keepSp))
143	}
144
145	// additionally, we should be able to parse the first element as a
146	// DocumentRef- ID string, and we should remove that prefix
147	if !strings.HasPrefix(documentRefID, "DocumentRef-") {
148		return "", "", "", "", fmt.Errorf("expected first element to have DocumentRef- prefix")
149	}
150	documentRefID = strings.TrimPrefix(documentRefID, "DocumentRef-")
151	if documentRefID == "" {
152		return "", "", "", "", fmt.Errorf("document identifier has nothing after prefix")
153	}
154
155	return documentRefID, uri, alg, checksum, nil
156}
157