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