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