您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

generic.go 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package generic
  4. import (
  5. "errors"
  6. "net/http"
  7. "regexp"
  8. "strings"
  9. "unicode"
  10. packages_model "code.gitea.io/gitea/models/packages"
  11. "code.gitea.io/gitea/modules/log"
  12. packages_module "code.gitea.io/gitea/modules/packages"
  13. "code.gitea.io/gitea/routers/api/packages/helper"
  14. "code.gitea.io/gitea/services/context"
  15. packages_service "code.gitea.io/gitea/services/packages"
  16. )
  17. var (
  18. packageNameRegex = regexp.MustCompile(`\A[-_+.\w]+\z`)
  19. filenameRegex = regexp.MustCompile(`\A[-_+=:;.()\[\]{}~!@#$%^& \w]+\z`)
  20. )
  21. func apiError(ctx *context.Context, status int, obj any) {
  22. helper.LogAndProcessError(ctx, status, obj, func(message string) {
  23. ctx.PlainText(status, message)
  24. })
  25. }
  26. // DownloadPackageFile serves the specific generic package.
  27. func DownloadPackageFile(ctx *context.Context) {
  28. s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
  29. ctx,
  30. &packages_service.PackageInfo{
  31. Owner: ctx.Package.Owner,
  32. PackageType: packages_model.TypeGeneric,
  33. Name: ctx.Params("packagename"),
  34. Version: ctx.Params("packageversion"),
  35. },
  36. &packages_service.PackageFileInfo{
  37. Filename: ctx.Params("filename"),
  38. },
  39. )
  40. if err != nil {
  41. if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
  42. apiError(ctx, http.StatusNotFound, err)
  43. return
  44. }
  45. apiError(ctx, http.StatusInternalServerError, err)
  46. return
  47. }
  48. helper.ServePackageFile(ctx, s, u, pf)
  49. }
  50. func isValidPackageName(packageName string) bool {
  51. if len(packageName) == 1 && !unicode.IsLetter(rune(packageName[0])) && !unicode.IsNumber(rune(packageName[0])) {
  52. return false
  53. }
  54. return packageNameRegex.MatchString(packageName) && packageName != ".."
  55. }
  56. func isValidFileName(filename string) bool {
  57. return filenameRegex.MatchString(filename) &&
  58. strings.TrimSpace(filename) == filename &&
  59. filename != "." && filename != ".."
  60. }
  61. // UploadPackage uploads the specific generic package.
  62. // Duplicated packages get rejected.
  63. func UploadPackage(ctx *context.Context) {
  64. packageName := ctx.Params("packagename")
  65. filename := ctx.Params("filename")
  66. if !isValidPackageName(packageName) {
  67. apiError(ctx, http.StatusBadRequest, errors.New("invalid package name"))
  68. return
  69. }
  70. if !isValidFileName(filename) {
  71. apiError(ctx, http.StatusBadRequest, errors.New("invalid filename"))
  72. return
  73. }
  74. packageVersion := ctx.Params("packageversion")
  75. if packageVersion != strings.TrimSpace(packageVersion) {
  76. apiError(ctx, http.StatusBadRequest, errors.New("invalid package version"))
  77. return
  78. }
  79. upload, needToClose, err := ctx.UploadStream()
  80. if err != nil {
  81. apiError(ctx, http.StatusInternalServerError, err)
  82. return
  83. }
  84. if needToClose {
  85. defer upload.Close()
  86. }
  87. buf, err := packages_module.CreateHashedBufferFromReader(upload)
  88. if err != nil {
  89. log.Error("Error creating hashed buffer: %v", err)
  90. apiError(ctx, http.StatusInternalServerError, err)
  91. return
  92. }
  93. defer buf.Close()
  94. _, _, err = packages_service.CreatePackageOrAddFileToExisting(
  95. ctx,
  96. &packages_service.PackageCreationInfo{
  97. PackageInfo: packages_service.PackageInfo{
  98. Owner: ctx.Package.Owner,
  99. PackageType: packages_model.TypeGeneric,
  100. Name: packageName,
  101. Version: packageVersion,
  102. },
  103. Creator: ctx.Doer,
  104. },
  105. &packages_service.PackageFileCreationInfo{
  106. PackageFileInfo: packages_service.PackageFileInfo{
  107. Filename: filename,
  108. },
  109. Creator: ctx.Doer,
  110. Data: buf,
  111. IsLead: true,
  112. },
  113. )
  114. if err != nil {
  115. switch err {
  116. case packages_model.ErrDuplicatePackageFile:
  117. apiError(ctx, http.StatusConflict, err)
  118. case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
  119. apiError(ctx, http.StatusForbidden, err)
  120. default:
  121. apiError(ctx, http.StatusInternalServerError, err)
  122. }
  123. return
  124. }
  125. ctx.Status(http.StatusCreated)
  126. }
  127. // DeletePackage deletes the specific generic package.
  128. func DeletePackage(ctx *context.Context) {
  129. err := packages_service.RemovePackageVersionByNameAndVersion(
  130. ctx,
  131. ctx.Doer,
  132. &packages_service.PackageInfo{
  133. Owner: ctx.Package.Owner,
  134. PackageType: packages_model.TypeGeneric,
  135. Name: ctx.Params("packagename"),
  136. Version: ctx.Params("packageversion"),
  137. },
  138. )
  139. if err != nil {
  140. if err == packages_model.ErrPackageNotExist {
  141. apiError(ctx, http.StatusNotFound, err)
  142. return
  143. }
  144. apiError(ctx, http.StatusInternalServerError, err)
  145. return
  146. }
  147. ctx.Status(http.StatusNoContent)
  148. }
  149. // DeletePackageFile deletes the specific file of a generic package.
  150. func DeletePackageFile(ctx *context.Context) {
  151. pv, pf, err := func() (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
  152. pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeGeneric, ctx.Params("packagename"), ctx.Params("packageversion"))
  153. if err != nil {
  154. return nil, nil, err
  155. }
  156. pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), packages_model.EmptyFileKey)
  157. if err != nil {
  158. return nil, nil, err
  159. }
  160. return pv, pf, nil
  161. }()
  162. if err != nil {
  163. if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
  164. apiError(ctx, http.StatusNotFound, err)
  165. return
  166. }
  167. apiError(ctx, http.StatusInternalServerError, err)
  168. return
  169. }
  170. pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
  171. if err != nil {
  172. apiError(ctx, http.StatusInternalServerError, err)
  173. return
  174. }
  175. if len(pfs) == 1 {
  176. if err := packages_service.RemovePackageVersion(ctx, ctx.Doer, pv); err != nil {
  177. apiError(ctx, http.StatusInternalServerError, err)
  178. return
  179. }
  180. } else {
  181. if err := packages_service.DeletePackageFile(ctx, pf); err != nil {
  182. apiError(ctx, http.StatusInternalServerError, err)
  183. return
  184. }
  185. }
  186. ctx.Status(http.StatusNoContent)
  187. }