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