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.

480 lines
13 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
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
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
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
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. "encoding/base64"
  7. "errors"
  8. "fmt"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. "github.com/go-martini/martini"
  13. "github.com/gogits/webdav"
  14. "github.com/gogits/gogs/models"
  15. "github.com/gogits/gogs/modules/auth"
  16. "github.com/gogits/gogs/modules/base"
  17. "github.com/gogits/gogs/modules/log"
  18. "github.com/gogits/gogs/modules/middleware"
  19. )
  20. func Create(ctx *middleware.Context, form auth.CreateRepoForm) {
  21. ctx.Data["Title"] = "Create repository"
  22. ctx.Data["PageIsNewRepo"] = true // For navbar arrow.
  23. ctx.Data["LanguageIgns"] = models.LanguageIgns
  24. ctx.Data["Licenses"] = models.Licenses
  25. if ctx.Req.Method == "GET" {
  26. ctx.HTML(200, "repo/create")
  27. return
  28. }
  29. if ctx.HasError() {
  30. ctx.HTML(200, "repo/create")
  31. return
  32. }
  33. _, err := models.CreateRepository(ctx.User, form.RepoName, form.Description,
  34. form.Language, form.License, form.Visibility == "private", form.InitReadme == "on")
  35. if err == nil {
  36. log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName)
  37. ctx.Redirect("/" + ctx.User.Name + "/" + form.RepoName)
  38. return
  39. } else if err == models.ErrRepoAlreadyExist {
  40. ctx.RenderWithErr("Repository name has already been used", "repo/create", &form)
  41. return
  42. } else if err == models.ErrRepoNameIllegal {
  43. ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "repo/create", &form)
  44. return
  45. }
  46. ctx.Handle(200, "repo.Create", err)
  47. }
  48. func Single(ctx *middleware.Context, params martini.Params) {
  49. branchName := ctx.Repo.BranchName
  50. commitId := ctx.Repo.CommitId
  51. userName := ctx.Repo.Owner.Name
  52. repoName := ctx.Repo.Repository.Name
  53. repoLink := ctx.Repo.RepoLink
  54. branchLink := ctx.Repo.RepoLink + "/src/" + branchName
  55. rawLink := ctx.Repo.RepoLink + "/raw/" + branchName
  56. // Get tree path
  57. treename := params["_1"]
  58. if len(treename) > 0 && treename[len(treename)-1] == '/' {
  59. ctx.Redirect(repoLink + "/src/" + branchName + "/" + treename[:len(treename)-1])
  60. return
  61. }
  62. ctx.Data["IsRepoToolbarSource"] = true
  63. // Branches.
  64. brs, err := models.GetBranches(userName, repoName)
  65. if err != nil {
  66. ctx.Handle(404, "repo.Single(GetBranches)", err)
  67. return
  68. }
  69. ctx.Data["Branches"] = brs
  70. isViewBranch := ctx.Repo.IsBranch
  71. ctx.Data["IsViewBranch"] = isViewBranch
  72. repoFile, err := models.GetTargetFile(userName, repoName,
  73. branchName, commitId, treename)
  74. if err != nil && err != models.ErrRepoFileNotExist {
  75. ctx.Handle(404, "repo.Single(GetTargetFile)", err)
  76. return
  77. }
  78. if len(treename) != 0 && repoFile == nil {
  79. ctx.Handle(404, "repo.Single", nil)
  80. return
  81. }
  82. if repoFile != nil && repoFile.IsFile() {
  83. if blob, err := repoFile.LookupBlob(); err != nil {
  84. ctx.Handle(404, "repo.Single(repoFile.LookupBlob)", err)
  85. } else {
  86. ctx.Data["FileSize"] = repoFile.Size
  87. ctx.Data["IsFile"] = true
  88. ctx.Data["FileName"] = repoFile.Name
  89. ext := path.Ext(repoFile.Name)
  90. if len(ext) > 0 {
  91. ext = ext[1:]
  92. }
  93. ctx.Data["FileExt"] = ext
  94. ctx.Data["FileLink"] = rawLink + "/" + treename
  95. data := blob.Contents()
  96. _, isTextFile := base.IsTextFile(data)
  97. _, isImageFile := base.IsImageFile(data)
  98. ctx.Data["FileIsText"] = isTextFile
  99. if isImageFile {
  100. ctx.Data["IsImageFile"] = true
  101. } else {
  102. readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name)
  103. ctx.Data["ReadmeExist"] = readmeExist
  104. if readmeExist {
  105. ctx.Data["FileContent"] = string(base.RenderMarkdown(data, ""))
  106. } else {
  107. if isTextFile {
  108. ctx.Data["FileContent"] = string(data)
  109. }
  110. }
  111. }
  112. }
  113. } else {
  114. // Directory and file list.
  115. files, err := models.GetReposFiles(userName, repoName, ctx.Repo.CommitId, treename)
  116. if err != nil {
  117. ctx.Handle(404, "repo.Single(GetReposFiles)", err)
  118. return
  119. }
  120. ctx.Data["Files"] = files
  121. var readmeFile *models.RepoFile
  122. for _, f := range files {
  123. if !f.IsFile() || !base.IsReadmeFile(f.Name) {
  124. continue
  125. } else {
  126. readmeFile = f
  127. break
  128. }
  129. }
  130. if readmeFile != nil {
  131. ctx.Data["ReadmeInSingle"] = true
  132. ctx.Data["ReadmeExist"] = true
  133. if blob, err := readmeFile.LookupBlob(); err != nil {
  134. ctx.Handle(404, "repo.Single(readmeFile.LookupBlob)", err)
  135. return
  136. } else {
  137. ctx.Data["FileSize"] = readmeFile.Size
  138. ctx.Data["FileLink"] = rawLink + "/" + treename
  139. data := blob.Contents()
  140. _, isTextFile := base.IsTextFile(data)
  141. ctx.Data["FileIsText"] = isTextFile
  142. ctx.Data["FileName"] = readmeFile.Name
  143. if isTextFile {
  144. ctx.Data["FileContent"] = string(base.RenderMarkdown(data, branchLink))
  145. }
  146. }
  147. }
  148. }
  149. ctx.Data["Username"] = userName
  150. ctx.Data["Reponame"] = repoName
  151. var treenames []string
  152. Paths := make([]string, 0)
  153. if len(treename) > 0 {
  154. treenames = strings.Split(treename, "/")
  155. for i, _ := range treenames {
  156. Paths = append(Paths, strings.Join(treenames[0:i+1], "/"))
  157. }
  158. ctx.Data["HasParentPath"] = true
  159. if len(Paths)-2 >= 0 {
  160. ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2]
  161. }
  162. }
  163. ctx.Data["LastCommit"] = ctx.Repo.Commit
  164. ctx.Data["Paths"] = Paths
  165. ctx.Data["Treenames"] = treenames
  166. ctx.Data["BranchLink"] = branchLink
  167. ctx.HTML(200, "repo/single")
  168. }
  169. func SingleDownload(ctx *middleware.Context, params martini.Params) {
  170. // Get tree path
  171. treename := params["_1"]
  172. branchName := params["branchname"]
  173. userName := params["username"]
  174. repoName := params["reponame"]
  175. var commitId string
  176. if !models.IsBranchExist(userName, repoName, branchName) {
  177. commitId = branchName
  178. branchName = ""
  179. }
  180. repoFile, err := models.GetTargetFile(userName, repoName,
  181. branchName, commitId, treename)
  182. if err != nil {
  183. ctx.Handle(404, "repo.SingleDownload(GetTargetFile)", err)
  184. return
  185. }
  186. blob, err := repoFile.LookupBlob()
  187. if err != nil {
  188. ctx.Handle(404, "repo.SingleDownload(LookupBlob)", err)
  189. return
  190. }
  191. data := blob.Contents()
  192. contentType, isTextFile := base.IsTextFile(data)
  193. _, isImageFile := base.IsImageFile(data)
  194. ctx.Res.Header().Set("Content-Type", contentType)
  195. if !isTextFile && !isImageFile {
  196. ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(treename))
  197. ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
  198. }
  199. ctx.Res.Write(data)
  200. }
  201. func basicEncode(username, password string) string {
  202. auth := username + ":" + password
  203. return base64.StdEncoding.EncodeToString([]byte(auth))
  204. }
  205. func basicDecode(encoded string) (user string, name string, err error) {
  206. var s []byte
  207. s, err = base64.StdEncoding.DecodeString(encoded)
  208. if err != nil {
  209. return
  210. }
  211. a := strings.Split(string(s), ":")
  212. if len(a) == 2 {
  213. user, name = a[0], a[1]
  214. } else {
  215. err = errors.New("decode failed")
  216. }
  217. return
  218. }
  219. func authRequired(ctx *middleware.Context) {
  220. ctx.ResponseWriter.Header().Set("WWW-Authenticate", "Basic realm=\".\"")
  221. ctx.Data["ErrorMsg"] = "no basic auth and digit auth"
  222. ctx.HTML(401, fmt.Sprintf("status/401"))
  223. }
  224. func Http(ctx *middleware.Context, params martini.Params) {
  225. username := params["username"]
  226. reponame := params["reponame"]
  227. if strings.HasSuffix(reponame, ".git") {
  228. reponame = reponame[:len(reponame)-4]
  229. }
  230. //fmt.Println("req:", ctx.Req.Header)
  231. repoUser, err := models.GetUserByName(username)
  232. if err != nil {
  233. ctx.Handle(500, "repo.GetUserByName", nil)
  234. return
  235. }
  236. repo, err := models.GetRepositoryByName(repoUser.Id, reponame)
  237. if err != nil {
  238. ctx.Handle(500, "repo.GetRepositoryByName", nil)
  239. return
  240. }
  241. isPull := webdav.IsPullMethod(ctx.Req.Method)
  242. var askAuth = !(!repo.IsPrivate && isPull)
  243. //authRequired(ctx)
  244. //return
  245. // check access
  246. if askAuth {
  247. // check digit auth
  248. // check basic auth
  249. baHead := ctx.Req.Header.Get("Authorization")
  250. if baHead == "" {
  251. authRequired(ctx)
  252. return
  253. }
  254. auths := strings.Fields(baHead)
  255. if len(auths) != 2 || auths[0] != "Basic" {
  256. ctx.Handle(401, "no basic auth and digit auth", nil)
  257. return
  258. }
  259. authUsername, passwd, err := basicDecode(auths[1])
  260. if err != nil {
  261. ctx.Handle(401, "no basic auth and digit auth", nil)
  262. return
  263. }
  264. authUser, err := models.GetUserByName(authUsername)
  265. if err != nil {
  266. ctx.Handle(401, "no basic auth and digit auth", nil)
  267. return
  268. }
  269. newUser := &models.User{Passwd: passwd}
  270. newUser.EncodePasswd()
  271. if authUser.Passwd != newUser.Passwd {
  272. ctx.Handle(401, "no basic auth and digit auth", nil)
  273. return
  274. }
  275. var tp = models.AU_WRITABLE
  276. if isPull {
  277. tp = models.AU_READABLE
  278. }
  279. has, err := models.HasAccess(authUsername, username+"/"+reponame, tp)
  280. if err != nil || !has {
  281. ctx.Handle(401, "no basic auth and digit auth", nil)
  282. return
  283. }
  284. }
  285. dir := models.RepoPath(username, reponame)
  286. prefix := path.Join("/", username, params["reponame"])
  287. server := webdav.NewServer(
  288. dir, prefix, true)
  289. server.ServeHTTP(ctx.ResponseWriter, ctx.Req)
  290. }
  291. func Setting(ctx *middleware.Context, params martini.Params) {
  292. if !ctx.Repo.IsOwner {
  293. ctx.Handle(404, "repo.Setting", nil)
  294. return
  295. }
  296. ctx.Data["IsRepoToolbarSetting"] = true
  297. var title string
  298. if t, ok := ctx.Data["Title"].(string); ok {
  299. title = t
  300. }
  301. ctx.Data["Title"] = title + " - settings"
  302. ctx.HTML(200, "repo/setting")
  303. }
  304. func SettingPost(ctx *middleware.Context) {
  305. if !ctx.Repo.IsOwner {
  306. ctx.Error(404)
  307. return
  308. }
  309. switch ctx.Query("action") {
  310. case "update":
  311. isNameChanged := false
  312. newRepoName := ctx.Query("name")
  313. // Check if repository name has been changed.
  314. if ctx.Repo.Repository.Name != newRepoName {
  315. isExist, err := models.IsRepositoryExist(ctx.Repo.Owner, newRepoName)
  316. if err != nil {
  317. ctx.Handle(404, "repo.SettingPost(update: check existence)", err)
  318. return
  319. } else if isExist {
  320. ctx.RenderWithErr("Repository name has been taken in your repositories.", "repo/setting", nil)
  321. return
  322. } else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil {
  323. ctx.Handle(404, "repo.SettingPost(change repository name)", err)
  324. return
  325. }
  326. log.Trace("%s Repository name changed: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newRepoName)
  327. isNameChanged = true
  328. ctx.Repo.Repository.Name = newRepoName
  329. }
  330. ctx.Repo.Repository.Description = ctx.Query("desc")
  331. ctx.Repo.Repository.Website = ctx.Query("site")
  332. if err := models.UpdateRepository(ctx.Repo.Repository); err != nil {
  333. ctx.Handle(404, "repo.SettingPost(update)", err)
  334. return
  335. }
  336. ctx.Data["IsSuccess"] = true
  337. if isNameChanged {
  338. ctx.Redirect(fmt.Sprintf("/%s/%s/settings", ctx.Repo.Owner.Name, ctx.Repo.Repository.Name))
  339. } else {
  340. ctx.HTML(200, "repo/setting")
  341. }
  342. log.Trace("%s Repository updated: %s/%s", ctx.Req.RequestURI, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
  343. case "transfer":
  344. if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
  345. ctx.RenderWithErr("Please make sure you entered repository name is correct.", "repo/setting", nil)
  346. return
  347. }
  348. newOwner := ctx.Query("owner")
  349. // Check if new owner exists.
  350. isExist, err := models.IsUserExist(newOwner)
  351. if err != nil {
  352. ctx.Handle(404, "repo.SettingPost(transfer: check existence)", err)
  353. return
  354. } else if !isExist {
  355. ctx.RenderWithErr("Please make sure you entered owner name is correct.", "repo/setting", nil)
  356. return
  357. } else if err = models.TransferOwnership(ctx.User, newOwner, ctx.Repo.Repository); err != nil {
  358. ctx.Handle(404, "repo.SettingPost(transfer repository)", err)
  359. return
  360. }
  361. log.Trace("%s Repository transfered: %s/%s -> %s", ctx.Req.RequestURI, ctx.User.Name, ctx.Repo.Repository.Name, newOwner)
  362. ctx.Redirect("/")
  363. return
  364. case "delete":
  365. if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
  366. ctx.RenderWithErr("Please make sure you entered repository name is correct.", "repo/setting", nil)
  367. return
  368. }
  369. if err := models.DeleteRepository(ctx.User.Id, ctx.Repo.Repository.Id, ctx.User.LowerName); err != nil {
  370. ctx.Handle(200, "repo.Delete", err)
  371. return
  372. }
  373. log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName)
  374. ctx.Redirect("/")
  375. }
  376. }
  377. func Action(ctx *middleware.Context, params martini.Params) {
  378. var err error
  379. switch params["action"] {
  380. case "watch":
  381. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
  382. case "unwatch":
  383. err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
  384. case "desc":
  385. if !ctx.Repo.IsOwner {
  386. ctx.Error(404)
  387. return
  388. }
  389. ctx.Repo.Repository.Description = ctx.Query("desc")
  390. ctx.Repo.Repository.Website = ctx.Query("site")
  391. err = models.UpdateRepository(ctx.Repo.Repository)
  392. }
  393. if err != nil {
  394. log.Error("repo.Action(%s): %v", params["action"], err)
  395. ctx.JSON(200, map[string]interface{}{
  396. "ok": false,
  397. "err": err.Error(),
  398. })
  399. return
  400. }
  401. ctx.JSON(200, map[string]interface{}{
  402. "ok": true,
  403. })
  404. }