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

repo.go 12 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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. }
  96. if repo != nil {
  97. if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
  98. log.Error(4, "DeleteRepository: %v", errDelete)
  99. }
  100. }
  101. switch {
  102. case err == models.ErrRepoAlreadyExist:
  103. ctx.Data["Err_RepoName"] = true
  104. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), CREATE, &form)
  105. case models.IsErrNameReserved(err):
  106. ctx.Data["Err_RepoName"] = true
  107. ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), CREATE, &form)
  108. case models.IsErrNamePatternNotAllowed(err):
  109. ctx.Data["Err_RepoName"] = true
  110. ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), CREATE, &form)
  111. default:
  112. ctx.Handle(500, "CreatePost", err)
  113. }
  114. }
  115. func Migrate(ctx *middleware.Context) {
  116. ctx.Data["Title"] = ctx.Tr("new_migrate")
  117. ctxUser, err := checkContextUser(ctx, ctx.QueryInt64("org"))
  118. if err != nil {
  119. ctx.Handle(500, "checkContextUser", err)
  120. return
  121. }
  122. ctx.Data["ContextUser"] = ctxUser
  123. if err := ctx.User.GetOrganizations(); err != nil {
  124. ctx.Handle(500, "GetOrganizations", err)
  125. return
  126. }
  127. ctx.Data["Orgs"] = ctx.User.Orgs
  128. ctx.HTML(200, MIGRATE)
  129. }
  130. func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
  131. ctx.Data["Title"] = ctx.Tr("new_migrate")
  132. ctxUser := ctx.User
  133. // Not equal means current user is an organization.
  134. if form.Uid != ctx.User.Id {
  135. var err error
  136. ctxUser, err = checkContextUser(ctx, form.Uid)
  137. if err != nil {
  138. ctx.Handle(500, "checkContextUser", err)
  139. return
  140. }
  141. }
  142. ctx.Data["ContextUser"] = ctxUser
  143. if err := ctx.User.GetOrganizations(); err != nil {
  144. ctx.Handle(500, "GetOrganizations", err)
  145. return
  146. }
  147. ctx.Data["Orgs"] = ctx.User.Orgs
  148. if ctx.HasError() {
  149. ctx.HTML(200, MIGRATE)
  150. return
  151. }
  152. if ctxUser.IsOrganization() {
  153. // Check ownership of organization.
  154. if !ctxUser.IsOwnedBy(ctx.User.Id) {
  155. ctx.Error(403)
  156. return
  157. }
  158. }
  159. // Remote address can be HTTP/HTTPS/Git URL or local path.
  160. // Note: remember to change api/v1/repo.go: MigrateRepo
  161. // FIXME: merge these two functions with better error handling
  162. remoteAddr := form.CloneAddr
  163. if strings.HasPrefix(form.CloneAddr, "http://") ||
  164. strings.HasPrefix(form.CloneAddr, "https://") ||
  165. strings.HasPrefix(form.CloneAddr, "git://") {
  166. u, err := url.Parse(form.CloneAddr)
  167. if err != nil {
  168. ctx.Data["Err_CloneAddr"] = true
  169. ctx.RenderWithErr(ctx.Tr("form.url_error"), MIGRATE, &form)
  170. return
  171. }
  172. if len(form.AuthUsername) > 0 || len(form.AuthPassword) > 0 {
  173. u.User = url.UserPassword(form.AuthUsername, form.AuthPassword)
  174. }
  175. remoteAddr = u.String()
  176. } else if !com.IsDir(remoteAddr) {
  177. ctx.Data["Err_CloneAddr"] = true
  178. ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), MIGRATE, &form)
  179. return
  180. }
  181. repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr)
  182. if err == nil {
  183. log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
  184. ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + form.RepoName)
  185. return
  186. }
  187. if repo != nil {
  188. if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
  189. log.Error(4, "DeleteRepository: %v", errDelete)
  190. }
  191. }
  192. if strings.Contains(err.Error(), "Authentication failed") {
  193. ctx.Data["Err_Auth"] = true
  194. ctx.RenderWithErr(ctx.Tr("form.auth_failed", err), MIGRATE, &form)
  195. return
  196. }
  197. switch {
  198. case err == models.ErrRepoAlreadyExist:
  199. ctx.Data["Err_RepoName"] = true
  200. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), MIGRATE, &form)
  201. case models.IsErrNameReserved(err):
  202. ctx.Data["Err_RepoName"] = true
  203. ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), MIGRATE, &form)
  204. case models.IsErrNamePatternNotAllowed(err):
  205. ctx.Data["Err_RepoName"] = true
  206. ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), MIGRATE, &form)
  207. default:
  208. ctx.Handle(500, "MigratePost", err)
  209. }
  210. }
  211. func getForkRepository(ctx *middleware.Context) (*models.Repository, error) {
  212. forkId := ctx.QueryInt64("fork_id")
  213. ctx.Data["ForkId"] = forkId
  214. forkRepo, err := models.GetRepositoryById(forkId)
  215. if err != nil {
  216. return nil, fmt.Errorf("GetRepositoryById: %v", err)
  217. }
  218. ctx.Data["repo_name"] = forkRepo.Name
  219. ctx.Data["desc"] = forkRepo.Description
  220. if err = forkRepo.GetOwner(); err != nil {
  221. return nil, fmt.Errorf("GetOwner: %v", err)
  222. }
  223. ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name
  224. return forkRepo, nil
  225. }
  226. func Fork(ctx *middleware.Context) {
  227. ctx.Data["Title"] = ctx.Tr("new_fork")
  228. if _, err := getForkRepository(ctx); err != nil {
  229. if models.IsErrRepoNotExist(err) {
  230. ctx.Redirect(setting.AppSubUrl + "/")
  231. } else {
  232. ctx.Handle(500, "getForkRepository", err)
  233. }
  234. return
  235. }
  236. // FIXME: maybe sometime can directly fork to organization?
  237. ctx.Data["ContextUser"] = ctx.User
  238. if err := ctx.User.GetOrganizations(); err != nil {
  239. ctx.Handle(500, "GetOrganizations", err)
  240. return
  241. }
  242. ctx.Data["Orgs"] = ctx.User.Orgs
  243. ctx.HTML(200, FORK)
  244. }
  245. func ForkPost(ctx *middleware.Context, form auth.CreateRepoForm) {
  246. ctx.Data["Title"] = ctx.Tr("new_fork")
  247. forkRepo, err := getForkRepository(ctx)
  248. if err != nil {
  249. if models.IsErrRepoNotExist(err) {
  250. ctx.Redirect(setting.AppSubUrl + "/")
  251. } else {
  252. ctx.Handle(500, "getForkRepository", err)
  253. }
  254. return
  255. }
  256. ctxUser := ctx.User
  257. // Not equal means current user is an organization.
  258. if form.Uid != ctx.User.Id {
  259. var err error
  260. ctxUser, err = checkContextUser(ctx, form.Uid)
  261. if err != nil {
  262. ctx.Handle(500, "checkContextUser", err)
  263. return
  264. }
  265. }
  266. ctx.Data["ContextUser"] = ctxUser
  267. if err := ctx.User.GetOrganizations(); err != nil {
  268. ctx.Handle(500, "GetOrganizations", err)
  269. return
  270. }
  271. ctx.Data["Orgs"] = ctx.User.Orgs
  272. if ctx.HasError() {
  273. ctx.HTML(200, CREATE)
  274. return
  275. }
  276. if ctxUser.IsOrganization() {
  277. // Check ownership of organization.
  278. if !ctxUser.IsOwnedBy(ctx.User.Id) {
  279. ctx.Error(403)
  280. return
  281. }
  282. }
  283. repo, err := models.ForkRepository(ctxUser, forkRepo, form.RepoName, form.Description)
  284. if err == nil {
  285. log.Trace("Repository forked: %s/%s", ctxUser.Name, repo.Name)
  286. ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
  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. // FIXME: merge this with other 2 error handling in to one.
  295. switch {
  296. case err == models.ErrRepoAlreadyExist:
  297. ctx.Data["Err_RepoName"] = true
  298. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), FORK, &form)
  299. case models.IsErrNameReserved(err):
  300. ctx.Data["Err_RepoName"] = true
  301. ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), FORK, &form)
  302. case models.IsErrNamePatternNotAllowed(err):
  303. ctx.Data["Err_RepoName"] = true
  304. ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), FORK, &form)
  305. default:
  306. ctx.Handle(500, "ForkPost", err)
  307. }
  308. }
  309. func Action(ctx *middleware.Context) {
  310. var err error
  311. switch ctx.Params(":action") {
  312. case "watch":
  313. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
  314. case "unwatch":
  315. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
  316. case "star":
  317. err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
  318. case "unstar":
  319. err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
  320. case "desc":
  321. if !ctx.Repo.IsOwner() {
  322. ctx.Error(404)
  323. return
  324. }
  325. ctx.Repo.Repository.Description = ctx.Query("desc")
  326. ctx.Repo.Repository.Website = ctx.Query("site")
  327. err = models.UpdateRepository(ctx.Repo.Repository, false)
  328. }
  329. if err != nil {
  330. log.Error(4, "Action(%s): %v", ctx.Params(":action"), err)
  331. ctx.JSON(200, map[string]interface{}{
  332. "ok": false,
  333. "err": err.Error(),
  334. })
  335. return
  336. }
  337. ctx.Redirect(ctx.Repo.RepoLink)
  338. return
  339. ctx.JSON(200, map[string]interface{}{
  340. "ok": true,
  341. })
  342. }
  343. func Download(ctx *middleware.Context) {
  344. var (
  345. uri = ctx.Params("*")
  346. refName string
  347. ext string
  348. archivePath string
  349. archiveType git.ArchiveType
  350. )
  351. switch {
  352. case strings.HasSuffix(uri, ".zip"):
  353. ext = ".zip"
  354. archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/zip")
  355. archiveType = git.ZIP
  356. case strings.HasSuffix(uri, ".tar.gz"):
  357. ext = ".tar.gz"
  358. archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/targz")
  359. archiveType = git.TARGZ
  360. default:
  361. ctx.Error(404)
  362. return
  363. }
  364. refName = strings.TrimSuffix(uri, ext)
  365. if !com.IsDir(archivePath) {
  366. if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
  367. ctx.Handle(500, "Download -> os.MkdirAll(archivePath)", err)
  368. return
  369. }
  370. }
  371. // Get corresponding commit.
  372. var (
  373. commit *git.Commit
  374. err error
  375. )
  376. gitRepo := ctx.Repo.GitRepo
  377. if gitRepo.IsBranchExist(refName) {
  378. commit, err = gitRepo.GetCommitOfBranch(refName)
  379. if err != nil {
  380. ctx.Handle(500, "Download", err)
  381. return
  382. }
  383. } else if gitRepo.IsTagExist(refName) {
  384. commit, err = gitRepo.GetCommitOfTag(refName)
  385. if err != nil {
  386. ctx.Handle(500, "Download", err)
  387. return
  388. }
  389. } else if len(refName) == 40 {
  390. commit, err = gitRepo.GetCommit(refName)
  391. if err != nil {
  392. ctx.Handle(404, "Download", nil)
  393. return
  394. }
  395. } else {
  396. ctx.Error(404)
  397. return
  398. }
  399. archivePath = path.Join(archivePath, base.ShortSha(commit.Id.String())+ext)
  400. if !com.IsFile(archivePath) {
  401. if err := commit.CreateArchive(archivePath, archiveType); err != nil {
  402. ctx.Handle(500, "Download -> CreateArchive "+archivePath, err)
  403. return
  404. }
  405. }
  406. ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+base.ShortSha(commit.Id.String())+ext)
  407. }