// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package nuget import ( "encoding/xml" "strings" "time" packages_model "code.gitea.io/gitea/models/packages" nuget_module "code.gitea.io/gitea/modules/packages/nuget" ) type AtomTitle struct { Type string `xml:"type,attr"` Text string `xml:",chardata"` } type ServiceCollection struct { Href string `xml:"href,attr"` Title AtomTitle `xml:"atom:title"` } type ServiceWorkspace struct { Title AtomTitle `xml:"atom:title"` Collection ServiceCollection `xml:"collection"` } type ServiceIndexResponseV2 struct { XMLName xml.Name `xml:"service"` Base string `xml:"base,attr"` Xmlns string `xml:"xmlns,attr"` XmlnsAtom string `xml:"xmlns:atom,attr"` Workspace ServiceWorkspace `xml:"workspace"` } type EdmxPropertyRef struct { Name string `xml:"Name,attr"` } type EdmxProperty struct { Name string `xml:"Name,attr"` Type string `xml:"Type,attr"` Nullable bool `xml:"Nullable,attr"` } type EdmxEntityType struct { Name string `xml:"Name,attr"` HasStream bool `xml:"m:HasStream,attr"` Keys []EdmxPropertyRef `xml:"Key>PropertyRef"` Properties []EdmxProperty `xml:"Property"` } type EdmxFunctionParameter struct { Name string `xml:"Name,attr"` Type string `xml:"Type,attr"` } type EdmxFunctionImport struct { Name string `xml:"Name,attr"` ReturnType string `xml:"ReturnType,attr"` EntitySet string `xml:"EntitySet,attr"` Parameter []EdmxFunctionParameter `xml:"Parameter"` } type EdmxEntitySet struct { Name string `xml:"Name,attr"` EntityType string `xml:"EntityType,attr"` } type EdmxEntityContainer struct { Name string `xml:"Name,attr"` IsDefaultEntityContainer bool `xml:"m:IsDefaultEntityContainer,attr"` EntitySet EdmxEntitySet `xml:"EntitySet"` FunctionImports []EdmxFunctionImport `xml:"FunctionImport"` } type EdmxSchema struct { Xmlns string `xml:"xmlns,attr"` Namespace string `xml:"Namespace,attr"` EntityType *EdmxEntityType `xml:"EntityType,omitempty"` EntityContainer *EdmxEntityContainer `xml:"EntityContainer,omitempty"` } type EdmxDataServices struct { XmlnsM string `xml:"xmlns:m,attr"` DataServiceVersion string `xml:"m:DataServiceVersion,attr"` MaxDataServiceVersion string `xml:"m:MaxDataServiceVersion,attr"` Schema []EdmxSchema `xml:"Schema"` } type EdmxMetadata struct { XMLName xml.Name `xml:"edmx:Edmx"` XmlnsEdmx string `xml:"xmlns:edmx,attr"` Version string `xml:"Version,attr"` DataServices EdmxDataServices `xml:"edmx:DataServices"` } var Metadata = &EdmxMetadata{ XmlnsEdmx: "http://schemas.microsoft.com/ado/2007/06/edmx", Version: "1.0", DataServices: EdmxDataServices{ XmlnsM: "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", DataServiceVersion: "2.0", MaxDataServiceVersion: "2.0", Schema: []EdmxSchema{ { Xmlns: "http://schemas.microsoft.com/ado/2006/04/edm", Namespace: "NuGetGallery.OData", EntityType: &EdmxEntityType{ Name: "V2FeedPackage", HasStream: true, Keys: []EdmxPropertyRef{ {Name: "Id"}, {Name: "Version"}, }, Properties: []EdmxProperty{ { Name: "Id", Type: "Edm.String", }, { Name: "Version", Type: "Edm.String", }, { Name: "NormalizedVersion", Type: "Edm.String", Nullable: true, }, { Name: "Authors", Type: "Edm.String", Nullable: true, }, { Name: "Created", Type: "Edm.DateTime", }, { Name: "Dependencies", Type: "Edm.String", }, { Name: "Description", Type: "Edm.String", }, { Name: "DownloadCount", Type: "Edm.Int64", }, { Name: "LastUpdated", Type: "Edm.DateTime", }, { Name: "Published", Type: "Edm.DateTime", }, { Name: "PackageSize", Type: "Edm.Int64", }, { Name: "ProjectUrl", Type: "Edm.String", Nullable: true, }, { Name: "ReleaseNotes", Type: "Edm.String", Nullable: true, }, { Name: "RequireLicenseAcceptance", Type: "Edm.Boolean", Nullable: false, }, { Name: "Title", Type: "Edm.String", Nullable: true, }, { Name: "VersionDownloadCount", Type: "Edm.Int64", Nullable: false, }, }, }, }, { Xmlns: "http://schemas.microsoft.com/ado/2006/04/edm", Namespace: "NuGetGallery", EntityContainer: &EdmxEntityContainer{ Name: "V2FeedContext", IsDefaultEntityContainer: true, EntitySet: EdmxEntitySet{ Name: "Packages", EntityType: "NuGetGallery.OData.V2FeedPackage", }, FunctionImports: []EdmxFunctionImport{ { Name: "Search", ReturnType: "Collection(NuGetGallery.OData.V2FeedPackage)", EntitySet: "Packages", Parameter: []EdmxFunctionParameter{ { Name: "searchTerm", Type: "Edm.String", }, }, }, { Name: "FindPackagesById", ReturnType: "Collection(NuGetGallery.OData.V2FeedPackage)", EntitySet: "Packages", Parameter: []EdmxFunctionParameter{ { Name: "id", Type: "Edm.String", }, }, }, }, }, }, }, }, } type FeedEntryCategory struct { Term string `xml:"term,attr"` Scheme string `xml:"scheme,attr"` } type FeedEntryLink struct { Rel string `xml:"rel,attr"` Href string `xml:"href,attr"` } type TypedValue[T any] struct { Type string `xml:"type,attr,omitempty"` Value T `xml:",chardata"` } type FeedEntryProperties struct { Version string `xml:"d:Version"` NormalizedVersion string `xml:"d:NormalizedVersion"` Authors string `xml:"d:Authors"` Dependencies string `xml:"d:Dependencies"` Description string `xml:"d:Description"` VersionDownloadCount TypedValue[int64] `xml:"d:VersionDownloadCount"` DownloadCount TypedValue[int64] `xml:"d:DownloadCount"` PackageSize TypedValue[int64] `xml:"d:PackageSize"` Created TypedValue[time.Time] `xml:"d:Created"` LastUpdated TypedValue[time.Time] `xml:"d:LastUpdated"` Published TypedValue[time.Time] `xml:"d:Published"` ProjectURL string `xml:"d:ProjectUrl,omitempty"` ReleaseNotes string `xml:"d:ReleaseNotes,omitempty"` RequireLicenseAcceptance TypedValue[bool] `xml:"d:RequireLicenseAcceptance"` Title string `xml:"d:Title"` } type FeedEntry struct { XMLName xml.Name `xml:"entry"` Xmlns string `xml:"xmlns,attr,omitempty"` XmlnsD string `xml:"xmlns:d,attr,omitempty"` XmlnsM string `xml:"xmlns:m,attr,omitempty"` Base string `xml:"xml:base,attr,omitempty"` ID string `xml:"id"` Category FeedEntryCategory `xml:"category"` Links []FeedEntryLink `xml:"link"` Title TypedValue[string] `xml:"title"` Updated time.Time `xml:"updated"` Author string `xml:"author>name"` Summary string `xml:"summary"` Properties *FeedEntryProperties `xml:"m:properties"` Content string `xml:",innerxml"` } type FeedResponse struct { XMLName xml.Name `xml:"feed"` Xmlns string `xml:"xmlns,attr,omitempty"` XmlnsD string `xml:"xmlns:d,attr,omitempty"` XmlnsM string `xml:"xmlns:m,attr,omitempty"` Base string `xml:"xml:base,attr,omitempty"` ID string `xml:"id"` Title TypedValue[string] `xml:"title"` Updated time.Time `xml:"updated"` Link FeedEntryLink `xml:"link"` Entries []*FeedEntry `xml:"entry"` Count int64 `xml:"m:count"` } func createFeedResponse(l *linkBuilder, totalEntries int64, pds []*packages_model.PackageDescriptor) *FeedResponse { entries := make([]*FeedEntry, 0, len(pds)) for _, pd := range pds { entries = append(entries, createEntry(l, pd, false)) } return &FeedResponse{ Xmlns: "http://www.w3.org/2005/Atom", Base: l.Base, XmlnsD: "http://schemas.microsoft.com/ado/2007/08/dataservices", XmlnsM: "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata", ID: "http://schemas.datacontract.org/2004/07/", Updated: time.Now(), Link: FeedEntryLink{Rel: "self", Href: l.Base}, Count: totalEntries, Entries: entries, } } func createEntryResponse(l *linkBuilder, pd *packages_model.PackageDescriptor) *FeedEntry { return createEntry(l, pd, true) } func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNamespace bool) *FeedEntry { metadata := pd.Metadata.(*nuget_module.Metadata) id := l.GetPackageMetadataURL(pd.Package.Name, pd.Version.Version) // Workaround to force a self-closing tag to satisfy XmlReader.IsEmptyElement used by the NuGet client. // https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlreader.isemptyelement content := `` createdValue := TypedValue[time.Time]{ Type: "Edm.DateTime", Value: pd.Version.CreatedUnix.AsLocalTime(), } entry := &FeedEntry{ ID: id, Category: FeedEntryCategory{Term: "NuGetGallery.OData.V2FeedPackage", Scheme: "http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"}, Links: []FeedEntryLink{ {Rel: "self", Href: id}, {Rel: "edit", Href: id}, }, Title: TypedValue[string]{Type: "text", Value: pd.Package.Name}, Updated: pd.Version.CreatedUnix.AsLocalTime(), Author: metadata.Authors, Content: content, Properties: &FeedEntryProperties{ Version: pd.Version.Version, NormalizedVersion: pd.Version.Version, Authors: metadata.Authors, Dependencies: buildDependencyString(metadata), Description: metadata.Description, VersionDownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount}, DownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount}, PackageSize: TypedValue[int64]{Type: "Edm.Int64", Value: pd.CalculateBlobSize()}, Created: createdValue, LastUpdated: createdValue, Published: createdValue, ProjectURL: metadata.ProjectURL, ReleaseNotes: metadata.ReleaseNotes, RequireLicenseAcceptance: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.RequireLicenseAcceptance}, Title: pd.Package.Name, }, } if withNamespace { entry.Xmlns = "http://www.w3.org/2005/Atom" entry.Base = l.Base entry.XmlnsD = "http://schemas.microsoft.com/ado/2007/08/dataservices" entry.XmlnsM = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" } return entry } func buildDependencyString(metadata *nuget_module.Metadata) string { var b strings.Builder first := true for group, deps := range metadata.Dependencies { for _, dep := range deps { if !first { b.WriteByte('|') } first = false b.WriteString(dep.ID) b.WriteByte(':') b.WriteString(dep.Version) b.WriteByte(':') b.WriteString(group) } } return b.String() }