1*ba677afaSXin Li// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 2*ba677afaSXin Li 3*ba677afaSXin Lipackage parser2v1 4*ba677afaSXin Li 5*ba677afaSXin Liimport ( 6*ba677afaSXin Li "fmt" 7*ba677afaSXin Li "strings" 8*ba677afaSXin Li 9*ba677afaSXin Li "github.com/spdx/tools-golang/spdx/common" 10*ba677afaSXin Li "github.com/spdx/tools-golang/spdx/v2_1" 11*ba677afaSXin Li) 12*ba677afaSXin Li 13*ba677afaSXin Lifunc (parser *tvParser2_1) parsePairFromPackage2_1(tag string, value string) error { 14*ba677afaSXin Li // expire pkgExtRef for anything other than a comment 15*ba677afaSXin Li // (we'll actually handle the comment further below) 16*ba677afaSXin Li if tag != "ExternalRefComment" { 17*ba677afaSXin Li parser.pkgExtRef = nil 18*ba677afaSXin Li } 19*ba677afaSXin Li 20*ba677afaSXin Li switch tag { 21*ba677afaSXin Li case "PackageName": 22*ba677afaSXin Li // if package already has a name, create and go on to a new package 23*ba677afaSXin Li if parser.pkg == nil || parser.pkg.PackageName != "" { 24*ba677afaSXin Li // check if the previous package contained an spdxId or not 25*ba677afaSXin Li if parser.pkg != nil && parser.pkg.PackageSPDXIdentifier == nullSpdxElementId2_1 { 26*ba677afaSXin Li return fmt.Errorf("package with PackageName %s does not have SPDX identifier", parser.pkg.PackageName) 27*ba677afaSXin Li } 28*ba677afaSXin Li parser.pkg = &v2_1.Package{ 29*ba677afaSXin Li FilesAnalyzed: true, 30*ba677afaSXin Li IsFilesAnalyzedTagPresent: false, 31*ba677afaSXin Li } 32*ba677afaSXin Li } 33*ba677afaSXin Li parser.pkg.PackageName = value 34*ba677afaSXin Li // tag for going on to file section 35*ba677afaSXin Li case "FileName": 36*ba677afaSXin Li parser.st = psFile2_1 37*ba677afaSXin Li return parser.parsePairFromFile2_1(tag, value) 38*ba677afaSXin Li // tag for going on to other license section 39*ba677afaSXin Li case "LicenseID": 40*ba677afaSXin Li parser.st = psOtherLicense2_1 41*ba677afaSXin Li return parser.parsePairFromOtherLicense2_1(tag, value) 42*ba677afaSXin Li case "SPDXID": 43*ba677afaSXin Li eID, err := extractElementID(value) 44*ba677afaSXin Li if err != nil { 45*ba677afaSXin Li return err 46*ba677afaSXin Li } 47*ba677afaSXin Li parser.pkg.PackageSPDXIdentifier = eID 48*ba677afaSXin Li if parser.doc.Packages == nil { 49*ba677afaSXin Li parser.doc.Packages = []*v2_1.Package{} 50*ba677afaSXin Li } 51*ba677afaSXin Li parser.doc.Packages = append(parser.doc.Packages, parser.pkg) 52*ba677afaSXin Li case "PackageVersion": 53*ba677afaSXin Li parser.pkg.PackageVersion = value 54*ba677afaSXin Li case "PackageFileName": 55*ba677afaSXin Li parser.pkg.PackageFileName = value 56*ba677afaSXin Li case "PackageSupplier": 57*ba677afaSXin Li parser.pkg.PackageSupplier = &common.Supplier{} 58*ba677afaSXin Li if value == "NOASSERTION" { 59*ba677afaSXin Li parser.pkg.PackageSupplier.Supplier = value 60*ba677afaSXin Li break 61*ba677afaSXin Li } 62*ba677afaSXin Li subkey, subvalue, err := extractSubs(value) 63*ba677afaSXin Li if err != nil { 64*ba677afaSXin Li return err 65*ba677afaSXin Li } 66*ba677afaSXin Li switch subkey { 67*ba677afaSXin Li case "Person", "Organization": 68*ba677afaSXin Li parser.pkg.PackageSupplier.Supplier = subvalue 69*ba677afaSXin Li parser.pkg.PackageSupplier.SupplierType = subkey 70*ba677afaSXin Li default: 71*ba677afaSXin Li return fmt.Errorf("unrecognized PackageSupplier type %v", subkey) 72*ba677afaSXin Li } 73*ba677afaSXin Li case "PackageOriginator": 74*ba677afaSXin Li parser.pkg.PackageOriginator = &common.Originator{} 75*ba677afaSXin Li if value == "NOASSERTION" { 76*ba677afaSXin Li parser.pkg.PackageOriginator.Originator = value 77*ba677afaSXin Li break 78*ba677afaSXin Li } 79*ba677afaSXin Li subkey, subvalue, err := extractSubs(value) 80*ba677afaSXin Li if err != nil { 81*ba677afaSXin Li return err 82*ba677afaSXin Li } 83*ba677afaSXin Li switch subkey { 84*ba677afaSXin Li case "Person", "Organization": 85*ba677afaSXin Li parser.pkg.PackageOriginator.Originator = subvalue 86*ba677afaSXin Li parser.pkg.PackageOriginator.OriginatorType = subkey 87*ba677afaSXin Li default: 88*ba677afaSXin Li return fmt.Errorf("unrecognized PackageOriginator type %v", subkey) 89*ba677afaSXin Li } 90*ba677afaSXin Li case "PackageDownloadLocation": 91*ba677afaSXin Li parser.pkg.PackageDownloadLocation = value 92*ba677afaSXin Li case "FilesAnalyzed": 93*ba677afaSXin Li parser.pkg.IsFilesAnalyzedTagPresent = true 94*ba677afaSXin Li if value == "false" { 95*ba677afaSXin Li parser.pkg.FilesAnalyzed = false 96*ba677afaSXin Li } else if value == "true" { 97*ba677afaSXin Li parser.pkg.FilesAnalyzed = true 98*ba677afaSXin Li } 99*ba677afaSXin Li case "PackageVerificationCode": 100*ba677afaSXin Li parser.pkg.PackageVerificationCode = extractCodeAndExcludes(value) 101*ba677afaSXin Li case "PackageChecksum": 102*ba677afaSXin Li subkey, subvalue, err := extractSubs(value) 103*ba677afaSXin Li if err != nil { 104*ba677afaSXin Li return err 105*ba677afaSXin Li } 106*ba677afaSXin Li if parser.pkg.PackageChecksums == nil { 107*ba677afaSXin Li parser.pkg.PackageChecksums = []common.Checksum{} 108*ba677afaSXin Li } 109*ba677afaSXin Li switch common.ChecksumAlgorithm(subkey) { 110*ba677afaSXin Li case common.SHA1, common.SHA256, common.MD5: 111*ba677afaSXin Li algorithm := common.ChecksumAlgorithm(subkey) 112*ba677afaSXin Li parser.pkg.PackageChecksums = append(parser.pkg.PackageChecksums, common.Checksum{Algorithm: algorithm, Value: subvalue}) 113*ba677afaSXin Li default: 114*ba677afaSXin Li return fmt.Errorf("got unknown checksum type %s", subkey) 115*ba677afaSXin Li } 116*ba677afaSXin Li case "PackageHomePage": 117*ba677afaSXin Li parser.pkg.PackageHomePage = value 118*ba677afaSXin Li case "PackageSourceInfo": 119*ba677afaSXin Li parser.pkg.PackageSourceInfo = value 120*ba677afaSXin Li case "PackageLicenseConcluded": 121*ba677afaSXin Li parser.pkg.PackageLicenseConcluded = value 122*ba677afaSXin Li case "PackageLicenseInfoFromFiles": 123*ba677afaSXin Li parser.pkg.PackageLicenseInfoFromFiles = append(parser.pkg.PackageLicenseInfoFromFiles, value) 124*ba677afaSXin Li case "PackageLicenseDeclared": 125*ba677afaSXin Li parser.pkg.PackageLicenseDeclared = value 126*ba677afaSXin Li case "PackageLicenseComments": 127*ba677afaSXin Li parser.pkg.PackageLicenseComments = value 128*ba677afaSXin Li case "PackageCopyrightText": 129*ba677afaSXin Li parser.pkg.PackageCopyrightText = value 130*ba677afaSXin Li case "PackageSummary": 131*ba677afaSXin Li parser.pkg.PackageSummary = value 132*ba677afaSXin Li case "PackageDescription": 133*ba677afaSXin Li parser.pkg.PackageDescription = value 134*ba677afaSXin Li case "PackageComment": 135*ba677afaSXin Li parser.pkg.PackageComment = value 136*ba677afaSXin Li case "ExternalRef": 137*ba677afaSXin Li parser.pkgExtRef = &v2_1.PackageExternalReference{} 138*ba677afaSXin Li parser.pkg.PackageExternalReferences = append(parser.pkg.PackageExternalReferences, parser.pkgExtRef) 139*ba677afaSXin Li category, refType, locator, err := extractPackageExternalReference(value) 140*ba677afaSXin Li if err != nil { 141*ba677afaSXin Li return err 142*ba677afaSXin Li } 143*ba677afaSXin Li parser.pkgExtRef.Category = category 144*ba677afaSXin Li parser.pkgExtRef.RefType = refType 145*ba677afaSXin Li parser.pkgExtRef.Locator = locator 146*ba677afaSXin Li case "ExternalRefComment": 147*ba677afaSXin Li if parser.pkgExtRef == nil { 148*ba677afaSXin Li return fmt.Errorf("no current ExternalRef found") 149*ba677afaSXin Li } 150*ba677afaSXin Li parser.pkgExtRef.ExternalRefComment = value 151*ba677afaSXin Li // now, expire pkgExtRef anyway because it can have at most one comment 152*ba677afaSXin Li parser.pkgExtRef = nil 153*ba677afaSXin Li // for relationship tags, pass along but don't change state 154*ba677afaSXin Li case "Relationship": 155*ba677afaSXin Li parser.rln = &v2_1.Relationship{} 156*ba677afaSXin Li parser.doc.Relationships = append(parser.doc.Relationships, parser.rln) 157*ba677afaSXin Li return parser.parsePairForRelationship2_1(tag, value) 158*ba677afaSXin Li case "RelationshipComment": 159*ba677afaSXin Li return parser.parsePairForRelationship2_1(tag, value) 160*ba677afaSXin Li // for annotation tags, pass along but don't change state 161*ba677afaSXin Li case "Annotator": 162*ba677afaSXin Li parser.ann = &v2_1.Annotation{} 163*ba677afaSXin Li parser.doc.Annotations = append(parser.doc.Annotations, parser.ann) 164*ba677afaSXin Li return parser.parsePairForAnnotation2_1(tag, value) 165*ba677afaSXin Li case "AnnotationDate": 166*ba677afaSXin Li return parser.parsePairForAnnotation2_1(tag, value) 167*ba677afaSXin Li case "AnnotationType": 168*ba677afaSXin Li return parser.parsePairForAnnotation2_1(tag, value) 169*ba677afaSXin Li case "SPDXREF": 170*ba677afaSXin Li return parser.parsePairForAnnotation2_1(tag, value) 171*ba677afaSXin Li case "AnnotationComment": 172*ba677afaSXin Li return parser.parsePairForAnnotation2_1(tag, value) 173*ba677afaSXin Li // tag for going on to review section (DEPRECATED) 174*ba677afaSXin Li case "Reviewer": 175*ba677afaSXin Li parser.st = psReview2_1 176*ba677afaSXin Li return parser.parsePairFromReview2_1(tag, value) 177*ba677afaSXin Li default: 178*ba677afaSXin Li return fmt.Errorf("received unknown tag %v in Package section", tag) 179*ba677afaSXin Li } 180*ba677afaSXin Li 181*ba677afaSXin Li return nil 182*ba677afaSXin Li} 183*ba677afaSXin Li 184*ba677afaSXin Li// ===== Helper functions ===== 185*ba677afaSXin Li 186*ba677afaSXin Lifunc extractCodeAndExcludes(value string) common.PackageVerificationCode { 187*ba677afaSXin Li // FIXME this should probably be done using regular expressions instead 188*ba677afaSXin Li // split by paren + word "excludes:" 189*ba677afaSXin Li sp := strings.SplitN(value, "(excludes:", 2) 190*ba677afaSXin Li if len(sp) < 2 { 191*ba677afaSXin Li // not found; return the whole string as just the code 192*ba677afaSXin Li return common.PackageVerificationCode{Value: value, ExcludedFiles: []string{}} 193*ba677afaSXin Li } 194*ba677afaSXin Li 195*ba677afaSXin Li // if we're here, code is in first part and excludes filename is in 196*ba677afaSXin Li // second part, with trailing paren 197*ba677afaSXin Li code := strings.TrimSpace(sp[0]) 198*ba677afaSXin Li parsedSp := strings.SplitN(sp[1], ")", 2) 199*ba677afaSXin Li fileName := strings.TrimSpace(parsedSp[0]) 200*ba677afaSXin Li return common.PackageVerificationCode{Value: code, ExcludedFiles: []string{fileName}} 201*ba677afaSXin Li} 202*ba677afaSXin Li 203*ba677afaSXin Lifunc extractPackageExternalReference(value string) (string, string, string, error) { 204*ba677afaSXin Li sp := strings.Split(value, " ") 205*ba677afaSXin Li // remove any that are just whitespace 206*ba677afaSXin Li keepSp := []string{} 207*ba677afaSXin Li for _, s := range sp { 208*ba677afaSXin Li ss := strings.TrimSpace(s) 209*ba677afaSXin Li if ss != "" { 210*ba677afaSXin Li keepSp = append(keepSp, ss) 211*ba677afaSXin Li } 212*ba677afaSXin Li } 213*ba677afaSXin Li // now, should have 3 items and should be able to map them 214*ba677afaSXin Li if len(keepSp) != 3 { 215*ba677afaSXin Li return "", "", "", fmt.Errorf("expected 3 elements, got %d", len(keepSp)) 216*ba677afaSXin Li } 217*ba677afaSXin Li return keepSp[0], keepSp[1], keepSp[2], nil 218*ba677afaSXin Li} 219