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