You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

450 lines
12 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repo
  5. import (
  6. "fmt"
  7. "net/url"
  8. "os"
  9. "path"
  10. "strings"
  11. "github.com/Unknwon/com"
  12. "github.com/gogits/gogs/models"
  13. "github.com/gogits/gogs/modules/auth"
  14. "github.com/gogits/gogs/modules/base"
  15. "github.com/gogits/gogs/modules/git"
  16. "github.com/gogits/gogs/modules/log"
  17. "github.com/gogits/gogs/modules/middleware"
  18. "github.com/gogits/gogs/modules/setting"
  19. )
  20. const (
  21. CREATE base.TplName = "repo/create"
  22. MIGRATE base.TplName = "repo/migrate"
  23. FORK base.TplName = "repo/fork"
  24. )
  25. func checkContextUser(ctx *middleware.Context, uid int64) (*models.User, error) {
  26. ctxUser := ctx.User
  27. if uid > 0 {
  28. org, err := models.GetUserById(uid)
  29. if err != models.ErrUserNotExist {
  30. if err != nil {
  31. return nil, fmt.Errorf("GetUserById: %v", err)
  32. }
  33. ctxUser = org
  34. }
  35. }
  36. return ctxUser, nil
  37. }
  38. func Create(ctx *middleware.Context) {
  39. ctx.Data["Title"] = ctx.Tr("new_repo")
  40. // Give default value for template to render.
  41. ctx.Data["gitignore"] = "0"
  42. ctx.Data["license"] = "0"
  43. ctx.Data["Gitignores"] = models.Gitignores
  44. ctx.Data["Licenses"] = models.Licenses
  45. ctxUser, err := checkContextUser(ctx, ctx.QueryInt64("org"))
  46. if err != nil {
  47. ctx.Handle(500, "checkContextUser", err)
  48. return
  49. }
  50. ctx.Data["ContextUser"] = ctxUser
  51. if err := ctx.User.GetOrganizations(); err != nil {
  52. ctx.Handle(500, "GetOrganizations", err)
  53. return
  54. }
  55. ctx.Data["Orgs"] = ctx.User.Orgs
  56. ctx.HTML(200, CREATE)
  57. }
  58. func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
  59. ctx.Data["Title"] = ctx.Tr("new_repo")
  60. ctx.Data["Gitignores"] = models.Gitignores
  61. ctx.Data["Licenses"] = models.Licenses
  62. ctxUser := ctx.User
  63. // Not equal means current user is an organization.
  64. if form.Uid != ctx.User.Id {
  65. var err error
  66. ctxUser, err = checkContextUser(ctx, form.Uid)
  67. if err != nil {
  68. ctx.Handle(500, "checkContextUser", err)
  69. return
  70. }
  71. }
  72. ctx.Data["ContextUser"] = ctxUser
  73. if err := ctx.User.GetOrganizations(); err != nil {
  74. ctx.Handle(500, "GetOrganizations", err)
  75. return
  76. }
  77. ctx.Data["Orgs"] = ctx.User.Orgs
  78. if ctx.HasError() {
  79. ctx.HTML(200, CREATE)
  80. return
  81. }
  82. if ctxUser.IsOrganization() {
  83. // Check ownership of organization.
  84. if !ctxUser.IsOwnedBy(ctx.User.Id) {
  85. ctx.Error(403)
  86. return
  87. }
  88. }
  89. repo, err := models.CreateRepository(ctxUser, form.RepoName, form.Description,
  90. form.Gitignore, form.License, form.Private, false, form.AutoInit)
  91. if err == nil {
  92. log.Trace("Repository created: %s/%s", ctxUser.Name, repo.Name)
  93. ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
  94. return
  95. } else if err == models.ErrRepoAlreadyExist {
  96. ctx.Data["Err_RepoName"] = true
  97. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), CREATE, &form)
  98. return
  99. } else if err == models.ErrRepoNameIllegal {
  100. ctx.Data["Err_RepoName"] = true
  101. ctx.RenderWithErr(ctx.Tr("form.illegal_repo_name"), CREATE, &form)
  102. return
  103. }
  104. if repo != nil {
  105. if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
  106. log.Error(4, "DeleteRepository: %v", errDelete)
  107. }
  108. }
  109. ctx.Handle(500, "CreatePost", err)
  110. }
  111. func Migrate(ctx *middleware.Context) {
  112. ctx.Data["Title"] = ctx.Tr("new_migrate")
  113. ctxUser, err := checkContextUser(ctx, ctx.QueryInt64("org"))
  114. if err != nil {
  115. ctx.Handle(500, "checkContextUser", err)
  116. return
  117. }
  118. ctx.Data["ContextUser"] = ctxUser
  119. if err := ctx.User.GetOrganizations(); err != nil {
  120. ctx.Handle(500, "GetOrganizations", err)
  121. return
  122. }
  123. ctx.Data["Orgs"] = ctx.User.Orgs
  124. ctx.HTML(200, MIGRATE)
  125. }
  126. func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
  127. ctx.Data["Title"] = ctx.Tr("new_migrate")
  128. ctxUser := ctx.User
  129. // Not equal means current user is an organization.
  130. if form.Uid != ctx.User.Id {
  131. var err error
  132. ctxUser, err = checkContextUser(ctx, form.Uid)
  133. if err != nil {
  134. ctx.Handle(500, "checkContextUser", err)
  135. return
  136. }
  137. }
  138. ctx.Data["ContextUser"] = ctxUser
  139. if err := ctx.User.GetOrganizations(); err != nil {
  140. ctx.Handle(500, "GetOrganizations", err)
  141. return
  142. }
  143. ctx.Data["Orgs"] = ctx.User.Orgs
  144. if ctx.HasError() {
  145. ctx.HTML(200, MIGRATE)
  146. return
  147. }
  148. if ctxUser.IsOrganization() {
  149. // Check ownership of organization.
  150. if !ctxUser.IsOwnedBy(ctx.User.Id) {
  151. ctx.Error(403)
  152. return
  153. }
  154. }
  155. // Remote address can be HTTP/HTTPS/Git URL or local path.
  156. // Note: remember to change api/v1/repo.go: MigrateRepo
  157. // FIXME: merge these two functions with better error handling
  158. remoteAddr := form.CloneAddr
  159. if strings.HasPrefix(form.CloneAddr, "http://") ||
  160. strings.HasPrefix(form.CloneAddr, "https://") ||
  161. strings.HasPrefix(form.CloneAddr, "git://") {
  162. u, err := url.Parse(form.CloneAddr)
  163. if err != nil {
  164. ctx.Data["Err_CloneAddr"] = true
  165. ctx.RenderWithErr(ctx.Tr("form.url_error"), MIGRATE, &form)
  166. return
  167. }
  168. if len(form.AuthUsername) > 0 || len(form.AuthPassword) > 0 {
  169. u.User = url.UserPassword(form.AuthUsername, form.AuthPassword)
  170. }
  171. remoteAddr = u.String()
  172. } else if !com.IsDir(remoteAddr) {
  173. ctx.Data["Err_CloneAddr"] = true
  174. ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), MIGRATE, &form)
  175. return
  176. }
  177. repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr)
  178. if err == nil {
  179. log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
  180. ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + form.RepoName)
  181. return
  182. } else if err == models.ErrRepoAlreadyExist {
  183. ctx.Data["Err_RepoName"] = true
  184. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), MIGRATE, &form)
  185. return
  186. } else if err == models.ErrRepoNameIllegal {
  187. ctx.Data["Err_RepoName"] = true
  188. ctx.RenderWithErr(ctx.Tr("form.illegal_repo_name"), MIGRATE, &form)
  189. return
  190. }
  191. if repo != nil {
  192. if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
  193. log.Error(4, "DeleteRepository: %v", errDelete)
  194. }
  195. }
  196. if strings.Contains(err.Error(), "Authentication failed") {
  197. ctx.Data["Err_Auth"] = true
  198. ctx.RenderWithErr(ctx.Tr("form.auth_failed", err), MIGRATE, &form)
  199. return
  200. }
  201. ctx.Handle(500, "MigratePost", err)
  202. }
  203. func getForkRepository(ctx *middleware.Context) (*models.Repository, error) {
  204. forkId := ctx.QueryInt64("fork_id")
  205. ctx.Data["ForkId"] = forkId
  206. forkRepo, err := models.GetRepositoryById(forkId)
  207. if err != nil {
  208. return nil, fmt.Errorf("GetRepositoryById: %v", err)
  209. }
  210. ctx.Data["repo_name"] = forkRepo.Name
  211. ctx.Data["desc"] = forkRepo.Description
  212. if err = forkRepo.GetOwner(); err != nil {
  213. return nil, fmt.Errorf("GetOwner: %v", err)
  214. }
  215. ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name
  216. return forkRepo, nil
  217. }
  218. func Fork(ctx *middleware.Context) {
  219. ctx.Data["Title"] = ctx.Tr("new_fork")
  220. if _, err := getForkRepository(ctx); err != nil {
  221. if models.IsErrRepoNotExist(err) {
  222. ctx.Redirect(setting.AppSubUrl + "/")
  223. } else {
  224. ctx.Handle(500, "getForkRepository", err)
  225. }
  226. return
  227. }
  228. // FIXME: maybe sometime can directly fork to organization?
  229. ctx.Data["ContextUser"] = ctx.User
  230. if err := ctx.User.GetOrganizations(); err != nil {
  231. ctx.Handle(500, "GetOrganizations", err)
  232. return
  233. }
  234. ctx.Data["Orgs"] = ctx.User.Orgs
  235. ctx.HTML(200, FORK)
  236. }
  237. func ForkPost(ctx *middleware.Context, form auth.CreateRepoForm) {
  238. ctx.Data["Title"] = ctx.Tr("new_fork")
  239. forkRepo, err := getForkRepository(ctx)
  240. if err != nil {
  241. if models.IsErrRepoNotExist(err) {
  242. ctx.Redirect(setting.AppSubUrl + "/")
  243. } else {
  244. ctx.Handle(500, "getForkRepository", err)
  245. }
  246. return
  247. }
  248. ctxUser := ctx.User
  249. // Not equal means current user is an organization.
  250. if form.Uid != ctx.User.Id {
  251. var err error
  252. ctxUser, err = checkContextUser(ctx, form.Uid)
  253. if err != nil {
  254. ctx.Handle(500, "checkContextUser", err)
  255. return
  256. }
  257. }
  258. ctx.Data["ContextUser"] = ctxUser
  259. if err := ctx.User.GetOrganizations(); err != nil {
  260. ctx.Handle(500, "GetOrganizations", err)
  261. return
  262. }
  263. ctx.Data["Orgs"] = ctx.User.Orgs
  264. if ctx.HasError() {
  265. ctx.HTML(200, CREATE)
  266. return
  267. }
  268. if ctxUser.IsOrganization() {
  269. // Check ownership of organization.
  270. if !ctxUser.IsOwnedBy(ctx.User.Id) {
  271. ctx.Error(403)
  272. return
  273. }
  274. }
  275. repo, err := models.ForkRepository(ctxUser, forkRepo, form.RepoName, form.Description)
  276. if err == nil {
  277. log.Trace("Repository forked: %s/%s", ctxUser.Name, repo.Name)
  278. ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
  279. return
  280. } else if err == models.ErrRepoAlreadyExist {
  281. ctx.Data["Err_RepoName"] = true
  282. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), FORK, &form)
  283. return
  284. } else if err == models.ErrRepoNameIllegal {
  285. ctx.Data["Err_RepoName"] = true
  286. ctx.RenderWithErr(ctx.Tr("form.illegal_repo_name"), CREATE, &form)
  287. return
  288. }
  289. if repo != nil {
  290. if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
  291. log.Error(4, "DeleteRepository: %v", errDelete)
  292. }
  293. }
  294. ctx.Handle(500, "ForkPost", err)
  295. }
  296. func Action(ctx *middleware.Context) {
  297. var err error
  298. switch ctx.Params(":action") {
  299. case "watch":
  300. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
  301. case "unwatch":
  302. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
  303. case "star":
  304. err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
  305. case "unstar":
  306. err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
  307. case "desc":
  308. if !ctx.Repo.IsOwner() {
  309. ctx.Error(404)
  310. return
  311. }
  312. ctx.Repo.Repository.Description = ctx.Query("desc")
  313. ctx.Repo.Repository.Website = ctx.Query("site")
  314. err = models.UpdateRepository(ctx.Repo.Repository, false)
  315. }
  316. if err != nil {
  317. log.Error(4, "Action(%s): %v", ctx.Params(":action"), err)
  318. ctx.JSON(200, map[string]interface{}{
  319. "ok": false,
  320. "err": err.Error(),
  321. })
  322. return
  323. }
  324. ctx.Redirect(ctx.Repo.RepoLink)
  325. return
  326. ctx.JSON(200, map[string]interface{}{
  327. "ok": true,
  328. })
  329. }
  330. func Download(ctx *middleware.Context) {
  331. var (
  332. uri = ctx.Params("*")
  333. refName string
  334. ext string
  335. archivePath string
  336. archiveType git.ArchiveType
  337. )
  338. switch {
  339. case strings.HasSuffix(uri, ".zip"):
  340. ext = ".zip"
  341. archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/zip")
  342. archiveType = git.ZIP
  343. case strings.HasSuffix(uri, ".tar.gz"):
  344. ext = ".tar.gz"
  345. archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/targz")
  346. archiveType = git.TARGZ
  347. default:
  348. ctx.Error(404)
  349. return
  350. }
  351. refName = strings.TrimSuffix(uri, ext)
  352. if !com.IsDir(archivePath) {
  353. if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
  354. ctx.Handle(500, "Download -> os.MkdirAll(archivePath)", err)
  355. return
  356. }
  357. }
  358. // Get corresponding commit.
  359. var (
  360. commit *git.Commit
  361. err error
  362. )
  363. gitRepo := ctx.Repo.GitRepo
  364. if gitRepo.IsBranchExist(refName) {
  365. commit, err = gitRepo.GetCommitOfBranch(refName)
  366. if err != nil {
  367. ctx.Handle(500, "Download", err)
  368. return
  369. }
  370. } else if gitRepo.IsTagExist(refName) {
  371. commit, err = gitRepo.GetCommitOfTag(refName)
  372. if err != nil {
  373. ctx.Handle(500, "Download", err)
  374. return
  375. }
  376. } else if len(refName) == 40 {
  377. commit, err = gitRepo.GetCommit(refName)
  378. if err != nil {
  379. ctx.Handle(404, "Download", nil)
  380. return
  381. }
  382. } else {
  383. ctx.Error(404)
  384. return
  385. }
  386. archivePath = path.Join(archivePath, base.ShortSha(commit.Id.String())+ext)
  387. if !com.IsFile(archivePath) {
  388. if err := commit.CreateArchive(archivePath, archiveType); err != nil {
  389. ctx.Handle(500, "Download -> CreateArchive "+archivePath, err)
  390. return
  391. }
  392. }
  393. ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+base.ShortSha(commit.Id.String())+ext)
  394. }