gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package rpm
  4. import (
  5. "fmt"
  6. "io"
  7. "strings"
  8. "code.gitea.io/gitea/modules/timeutil"
  9. "code.gitea.io/gitea/modules/validation"
  10. "github.com/sassoftware/go-rpmutils"
  11. )
  12. const (
  13. PropertyMetadata = "rpm.metadata"
  14. PropertyGroup = "rpm.group"
  15. PropertyArchitecture = "rpm.architecture"
  16. SettingKeyPrivate = "rpm.key.private"
  17. SettingKeyPublic = "rpm.key.public"
  18. RepositoryPackage = "_rpm"
  19. RepositoryVersion = "_repository"
  20. )
  21. const (
  22. // Can't use the syscall constants because they are not available for windows build.
  23. sIFMT = 0xf000
  24. sIFDIR = 0x4000
  25. sIXUSR = 0x40
  26. sIXGRP = 0x8
  27. sIXOTH = 0x1
  28. )
  29. // https://rpm-software-management.github.io/rpm/manual/spec.html
  30. // https://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/pkgformat.html
  31. type Package struct {
  32. Name string
  33. Version string
  34. VersionMetadata *VersionMetadata
  35. FileMetadata *FileMetadata
  36. }
  37. type VersionMetadata struct {
  38. License string `json:"license,omitempty"`
  39. ProjectURL string `json:"project_url,omitempty"`
  40. Summary string `json:"summary,omitempty"`
  41. Description string `json:"description,omitempty"`
  42. }
  43. type FileMetadata struct {
  44. Architecture string `json:"architecture,omitempty"`
  45. Epoch string `json:"epoch,omitempty"`
  46. Version string `json:"version,omitempty"`
  47. Release string `json:"release,omitempty"`
  48. Vendor string `json:"vendor,omitempty"`
  49. Group string `json:"group,omitempty"`
  50. Packager string `json:"packager,omitempty"`
  51. SourceRpm string `json:"source_rpm,omitempty"`
  52. BuildHost string `json:"build_host,omitempty"`
  53. BuildTime uint64 `json:"build_time,omitempty"`
  54. FileTime uint64 `json:"file_time,omitempty"`
  55. InstalledSize uint64 `json:"installed_size,omitempty"`
  56. ArchiveSize uint64 `json:"archive_size,omitempty"`
  57. Provides []*Entry `json:"provide,omitempty"`
  58. Requires []*Entry `json:"require,omitempty"`
  59. Conflicts []*Entry `json:"conflict,omitempty"`
  60. Obsoletes []*Entry `json:"obsolete,omitempty"`
  61. Files []*File `json:"files,omitempty"`
  62. Changelogs []*Changelog `json:"changelogs,omitempty"`
  63. }
  64. type Entry struct {
  65. Name string `json:"name" xml:"name,attr"`
  66. Flags string `json:"flags,omitempty" xml:"flags,attr,omitempty"`
  67. Version string `json:"version,omitempty" xml:"ver,attr,omitempty"`
  68. Epoch string `json:"epoch,omitempty" xml:"epoch,attr,omitempty"`
  69. Release string `json:"release,omitempty" xml:"rel,attr,omitempty"`
  70. }
  71. type File struct {
  72. Path string `json:"path" xml:",chardata"`
  73. Type string `json:"type,omitempty" xml:"type,attr,omitempty"`
  74. IsExecutable bool `json:"is_executable" xml:"-"`
  75. }
  76. type Changelog struct {
  77. Author string `json:"author,omitempty" xml:"author,attr"`
  78. Date timeutil.TimeStamp `json:"date,omitempty" xml:"date,attr"`
  79. Text string `json:"text,omitempty" xml:",chardata"`
  80. }
  81. // ParsePackage parses the RPM package file
  82. func ParsePackage(r io.Reader) (*Package, error) {
  83. rpm, err := rpmutils.ReadRpm(r)
  84. if err != nil {
  85. return nil, err
  86. }
  87. nevra, err := rpm.Header.GetNEVRA()
  88. if err != nil {
  89. return nil, err
  90. }
  91. version := fmt.Sprintf("%s-%s", nevra.Version, nevra.Release)
  92. if nevra.Epoch != "" && nevra.Epoch != "0" {
  93. version = fmt.Sprintf("%s-%s", nevra.Epoch, version)
  94. }
  95. p := &Package{
  96. Name: nevra.Name,
  97. Version: version,
  98. VersionMetadata: &VersionMetadata{
  99. Summary: getString(rpm.Header, rpmutils.SUMMARY),
  100. Description: getString(rpm.Header, rpmutils.DESCRIPTION),
  101. License: getString(rpm.Header, rpmutils.LICENSE),
  102. ProjectURL: getString(rpm.Header, rpmutils.URL),
  103. },
  104. FileMetadata: &FileMetadata{
  105. Architecture: nevra.Arch,
  106. Epoch: nevra.Epoch,
  107. Version: nevra.Version,
  108. Release: nevra.Release,
  109. Vendor: getString(rpm.Header, rpmutils.VENDOR),
  110. Group: getString(rpm.Header, rpmutils.GROUP),
  111. Packager: getString(rpm.Header, rpmutils.PACKAGER),
  112. SourceRpm: getString(rpm.Header, rpmutils.SOURCERPM),
  113. BuildHost: getString(rpm.Header, rpmutils.BUILDHOST),
  114. BuildTime: getUInt64(rpm.Header, rpmutils.BUILDTIME),
  115. FileTime: getUInt64(rpm.Header, rpmutils.FILEMTIMES),
  116. InstalledSize: getUInt64(rpm.Header, rpmutils.SIZE),
  117. ArchiveSize: getUInt64(rpm.Header, rpmutils.SIG_PAYLOADSIZE),
  118. Provides: getEntries(rpm.Header, rpmutils.PROVIDENAME, rpmutils.PROVIDEVERSION, rpmutils.PROVIDEFLAGS),
  119. Requires: getEntries(rpm.Header, rpmutils.REQUIRENAME, rpmutils.REQUIREVERSION, rpmutils.REQUIREFLAGS),
  120. Conflicts: getEntries(rpm.Header, rpmutils.CONFLICTNAME, rpmutils.CONFLICTVERSION, rpmutils.CONFLICTFLAGS),
  121. Obsoletes: getEntries(rpm.Header, rpmutils.OBSOLETENAME, rpmutils.OBSOLETEVERSION, rpmutils.OBSOLETEFLAGS),
  122. Files: getFiles(rpm.Header),
  123. Changelogs: getChangelogs(rpm.Header),
  124. },
  125. }
  126. if !validation.IsValidURL(p.VersionMetadata.ProjectURL) {
  127. p.VersionMetadata.ProjectURL = ""
  128. }
  129. return p, nil
  130. }
  131. func getString(h *rpmutils.RpmHeader, tag int) string {
  132. values, err := h.GetStrings(tag)
  133. if err != nil || len(values) < 1 {
  134. return ""
  135. }
  136. return values[0]
  137. }
  138. func getUInt64(h *rpmutils.RpmHeader, tag int) uint64 {
  139. values, err := h.GetUint64s(tag)
  140. if err != nil || len(values) < 1 {
  141. return 0
  142. }
  143. return values[0]
  144. }
  145. func getEntries(h *rpmutils.RpmHeader, namesTag, versionsTag, flagsTag int) []*Entry {
  146. names, err := h.GetStrings(namesTag)
  147. if err != nil || len(names) == 0 {
  148. return nil
  149. }
  150. flags, err := h.GetUint64s(flagsTag)
  151. if err != nil || len(flags) == 0 {
  152. return nil
  153. }
  154. versions, err := h.GetStrings(versionsTag)
  155. if err != nil || len(versions) == 0 {
  156. return nil
  157. }
  158. if len(names) != len(flags) || len(names) != len(versions) {
  159. return nil
  160. }
  161. entries := make([]*Entry, 0, len(names))
  162. for i := range names {
  163. e := &Entry{
  164. Name: names[i],
  165. }
  166. flags := flags[i]
  167. if (flags&rpmutils.RPMSENSE_GREATER) != 0 && (flags&rpmutils.RPMSENSE_EQUAL) != 0 {
  168. e.Flags = "GE"
  169. } else if (flags&rpmutils.RPMSENSE_LESS) != 0 && (flags&rpmutils.RPMSENSE_EQUAL) != 0 {
  170. e.Flags = "LE"
  171. } else if (flags & rpmutils.RPMSENSE_GREATER) != 0 {
  172. e.Flags = "GT"
  173. } else if (flags & rpmutils.RPMSENSE_LESS) != 0 {
  174. e.Flags = "LT"
  175. } else if (flags & rpmutils.RPMSENSE_EQUAL) != 0 {
  176. e.Flags = "EQ"
  177. }
  178. version := versions[i]
  179. if version != "" {
  180. parts := strings.Split(version, "-")
  181. versionParts := strings.Split(parts[0], ":")
  182. if len(versionParts) == 2 {
  183. e.Version = versionParts[1]
  184. e.Epoch = versionParts[0]
  185. } else {
  186. e.Version = versionParts[0]
  187. e.Epoch = "0"
  188. }
  189. if len(parts) > 1 {
  190. e.Release = parts[1]
  191. }
  192. }
  193. entries = append(entries, e)
  194. }
  195. return entries
  196. }
  197. func getFiles(h *rpmutils.RpmHeader) []*File {
  198. baseNames, _ := h.GetStrings(rpmutils.BASENAMES)
  199. dirNames, _ := h.GetStrings(rpmutils.DIRNAMES)
  200. dirIndexes, _ := h.GetUint32s(rpmutils.DIRINDEXES)
  201. fileFlags, _ := h.GetUint32s(rpmutils.FILEFLAGS)
  202. fileModes, _ := h.GetUint32s(rpmutils.FILEMODES)
  203. files := make([]*File, 0, len(baseNames))
  204. for i := range baseNames {
  205. if len(dirIndexes) <= i {
  206. continue
  207. }
  208. dirIndex := dirIndexes[i]
  209. if len(dirNames) <= int(dirIndex) {
  210. continue
  211. }
  212. var fileType string
  213. var isExecutable bool
  214. if i < len(fileFlags) && (fileFlags[i]&rpmutils.RPMFILE_GHOST) != 0 {
  215. fileType = "ghost"
  216. } else if i < len(fileModes) {
  217. if (fileModes[i] & sIFMT) == sIFDIR {
  218. fileType = "dir"
  219. } else {
  220. mode := fileModes[i] & ^uint32(sIFMT)
  221. isExecutable = (mode&sIXUSR) != 0 || (mode&sIXGRP) != 0 || (mode&sIXOTH) != 0
  222. }
  223. }
  224. files = append(files, &File{
  225. Path: dirNames[dirIndex] + baseNames[i],
  226. Type: fileType,
  227. IsExecutable: isExecutable,
  228. })
  229. }
  230. return files
  231. }
  232. func getChangelogs(h *rpmutils.RpmHeader) []*Changelog {
  233. texts, err := h.GetStrings(rpmutils.CHANGELOGTEXT)
  234. if err != nil || len(texts) == 0 {
  235. return nil
  236. }
  237. authors, err := h.GetStrings(rpmutils.CHANGELOGNAME)
  238. if err != nil || len(authors) == 0 {
  239. return nil
  240. }
  241. times, err := h.GetUint32s(rpmutils.CHANGELOGTIME)
  242. if err != nil || len(times) == 0 {
  243. return nil
  244. }
  245. if len(texts) != len(authors) || len(texts) != len(times) {
  246. return nil
  247. }
  248. changelogs := make([]*Changelog, 0, len(texts))
  249. for i := range texts {
  250. changelogs = append(changelogs, &Changelog{
  251. Author: authors[i],
  252. Date: timeutil.TimeStamp(times[i]),
  253. Text: texts[i],
  254. })
  255. }
  256. return changelogs
  257. }