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.

385 lines
8.8 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
  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 middleware
  5. import (
  6. "crypto/hmac"
  7. "crypto/sha1"
  8. "encoding/base64"
  9. "fmt"
  10. "html/template"
  11. "io"
  12. "net/http"
  13. "net/url"
  14. "path/filepath"
  15. "strconv"
  16. "strings"
  17. "time"
  18. "github.com/go-martini/martini"
  19. "github.com/gogits/cache"
  20. "github.com/gogits/git"
  21. "github.com/gogits/session"
  22. "github.com/gogits/gogs/models"
  23. "github.com/gogits/gogs/modules/auth"
  24. "github.com/gogits/gogs/modules/base"
  25. "github.com/gogits/gogs/modules/log"
  26. "github.com/gogits/gogs/modules/setting"
  27. )
  28. // Context represents context of a request.
  29. type Context struct {
  30. *Render
  31. c martini.Context
  32. p martini.Params
  33. Req *http.Request
  34. Res http.ResponseWriter
  35. Flash *Flash
  36. Session session.SessionStore
  37. Cache cache.Cache
  38. User *models.User
  39. IsSigned bool
  40. csrfToken string
  41. Repo struct {
  42. IsOwner bool
  43. IsTrueOwner bool
  44. IsWatching bool
  45. IsBranch bool
  46. IsTag bool
  47. IsCommit bool
  48. HasAccess bool
  49. Repository *models.Repository
  50. Owner *models.User
  51. Commit *git.Commit
  52. Tag *git.Tag
  53. GitRepo *git.Repository
  54. BranchName string
  55. TagName string
  56. CommitId string
  57. RepoLink string
  58. CloneLink struct {
  59. SSH string
  60. HTTPS string
  61. Git string
  62. }
  63. Mirror *models.Mirror
  64. }
  65. }
  66. // Query querys form parameter.
  67. func (ctx *Context) Query(name string) string {
  68. ctx.Req.ParseForm()
  69. return ctx.Req.Form.Get(name)
  70. }
  71. // func (ctx *Context) Param(name string) string {
  72. // return ctx.p[name]
  73. // }
  74. // HasError returns true if error occurs in form validation.
  75. func (ctx *Context) HasApiError() bool {
  76. hasErr, ok := ctx.Data["HasError"]
  77. if !ok {
  78. return false
  79. }
  80. return hasErr.(bool)
  81. }
  82. func (ctx *Context) GetErrMsg() string {
  83. return ctx.Data["ErrorMsg"].(string)
  84. }
  85. // HasError returns true if error occurs in form validation.
  86. func (ctx *Context) HasError() bool {
  87. hasErr, ok := ctx.Data["HasError"]
  88. if !ok {
  89. return false
  90. }
  91. ctx.Flash.ErrorMsg = ctx.Data["ErrorMsg"].(string)
  92. ctx.Data["Flash"] = ctx.Flash
  93. return hasErr.(bool)
  94. }
  95. // HTML calls render.HTML underlying but reduce one argument.
  96. func (ctx *Context) HTML(status int, name base.TplName, htmlOpt ...HTMLOptions) {
  97. ctx.Render.HTML(status, string(name), ctx.Data, htmlOpt...)
  98. }
  99. // RenderWithErr used for page has form validation but need to prompt error to users.
  100. func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form auth.Form) {
  101. if form != nil {
  102. auth.AssignForm(form, ctx.Data)
  103. }
  104. ctx.Flash.ErrorMsg = msg
  105. ctx.Data["Flash"] = ctx.Flash
  106. ctx.HTML(200, tpl)
  107. }
  108. // Handle handles and logs error by given status.
  109. func (ctx *Context) Handle(status int, title string, err error) {
  110. if err != nil {
  111. log.Error("%s: %v", title, err)
  112. if martini.Dev != martini.Prod {
  113. ctx.Data["ErrorMsg"] = err
  114. }
  115. }
  116. switch status {
  117. case 404:
  118. ctx.Data["Title"] = "Page Not Found"
  119. case 500:
  120. ctx.Data["Title"] = "Internal Server Error"
  121. }
  122. ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
  123. }
  124. func (ctx *Context) Debug(msg string, args ...interface{}) {
  125. log.Debug(msg, args...)
  126. }
  127. func (ctx *Context) GetCookie(name string) string {
  128. cookie, err := ctx.Req.Cookie(name)
  129. if err != nil {
  130. return ""
  131. }
  132. return cookie.Value
  133. }
  134. func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
  135. cookie := http.Cookie{}
  136. cookie.Name = name
  137. cookie.Value = value
  138. if len(others) > 0 {
  139. switch v := others[0].(type) {
  140. case int:
  141. cookie.MaxAge = v
  142. case int64:
  143. cookie.MaxAge = int(v)
  144. case int32:
  145. cookie.MaxAge = int(v)
  146. }
  147. }
  148. // default "/"
  149. if len(others) > 1 {
  150. if v, ok := others[1].(string); ok && len(v) > 0 {
  151. cookie.Path = v
  152. }
  153. } else {
  154. cookie.Path = "/"
  155. }
  156. // default empty
  157. if len(others) > 2 {
  158. if v, ok := others[2].(string); ok && len(v) > 0 {
  159. cookie.Domain = v
  160. }
  161. }
  162. // default empty
  163. if len(others) > 3 {
  164. switch v := others[3].(type) {
  165. case bool:
  166. cookie.Secure = v
  167. default:
  168. if others[3] != nil {
  169. cookie.Secure = true
  170. }
  171. }
  172. }
  173. // default false. for session cookie default true
  174. if len(others) > 4 {
  175. if v, ok := others[4].(bool); ok && v {
  176. cookie.HttpOnly = true
  177. }
  178. }
  179. ctx.Res.Header().Add("Set-Cookie", cookie.String())
  180. }
  181. // Get secure cookie from request by a given key.
  182. func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
  183. val := ctx.GetCookie(key)
  184. if val == "" {
  185. return "", false
  186. }
  187. parts := strings.SplitN(val, "|", 3)
  188. if len(parts) != 3 {
  189. return "", false
  190. }
  191. vs := parts[0]
  192. timestamp := parts[1]
  193. sig := parts[2]
  194. h := hmac.New(sha1.New, []byte(Secret))
  195. fmt.Fprintf(h, "%s%s", vs, timestamp)
  196. if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
  197. return "", false
  198. }
  199. res, _ := base64.URLEncoding.DecodeString(vs)
  200. return string(res), true
  201. }
  202. // Set Secure cookie for response.
  203. func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
  204. vs := base64.URLEncoding.EncodeToString([]byte(value))
  205. timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
  206. h := hmac.New(sha1.New, []byte(Secret))
  207. fmt.Fprintf(h, "%s%s", vs, timestamp)
  208. sig := fmt.Sprintf("%02x", h.Sum(nil))
  209. cookie := strings.Join([]string{vs, timestamp, sig}, "|")
  210. ctx.SetCookie(name, cookie, others...)
  211. }
  212. func (ctx *Context) CsrfToken() string {
  213. if len(ctx.csrfToken) > 0 {
  214. return ctx.csrfToken
  215. }
  216. token := ctx.GetCookie("_csrf")
  217. if len(token) == 0 {
  218. token = base.GetRandomString(30)
  219. ctx.SetCookie("_csrf", token)
  220. }
  221. ctx.csrfToken = token
  222. return token
  223. }
  224. func (ctx *Context) CsrfTokenValid() bool {
  225. token := ctx.Query("_csrf")
  226. if token == "" {
  227. token = ctx.Req.Header.Get("X-Csrf-Token")
  228. }
  229. if token == "" {
  230. return false
  231. } else if ctx.csrfToken != token {
  232. return false
  233. }
  234. return true
  235. }
  236. func (ctx *Context) ServeFile(file string, names ...string) {
  237. var name string
  238. if len(names) > 0 {
  239. name = names[0]
  240. } else {
  241. name = filepath.Base(file)
  242. }
  243. ctx.Res.Header().Set("Content-Description", "File Transfer")
  244. ctx.Res.Header().Set("Content-Type", "application/octet-stream")
  245. ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name)
  246. ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
  247. ctx.Res.Header().Set("Expires", "0")
  248. ctx.Res.Header().Set("Cache-Control", "must-revalidate")
  249. ctx.Res.Header().Set("Pragma", "public")
  250. http.ServeFile(ctx.Res, ctx.Req, file)
  251. }
  252. func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
  253. modtime := time.Now()
  254. for _, p := range params {
  255. switch v := p.(type) {
  256. case time.Time:
  257. modtime = v
  258. }
  259. }
  260. ctx.Res.Header().Set("Content-Description", "File Transfer")
  261. ctx.Res.Header().Set("Content-Type", "application/octet-stream")
  262. ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name)
  263. ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
  264. ctx.Res.Header().Set("Expires", "0")
  265. ctx.Res.Header().Set("Cache-Control", "must-revalidate")
  266. ctx.Res.Header().Set("Pragma", "public")
  267. http.ServeContent(ctx.Res, ctx.Req, name, modtime, r)
  268. }
  269. type Flash struct {
  270. url.Values
  271. ErrorMsg, SuccessMsg string
  272. }
  273. func (f *Flash) Error(msg string) {
  274. f.Set("error", msg)
  275. f.ErrorMsg = msg
  276. }
  277. func (f *Flash) Success(msg string) {
  278. f.Set("success", msg)
  279. f.SuccessMsg = msg
  280. }
  281. // InitContext initializes a classic context for a request.
  282. func InitContext() martini.Handler {
  283. return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) {
  284. ctx := &Context{
  285. c: c,
  286. // p: p,
  287. Req: r,
  288. Res: res,
  289. Cache: setting.Cache,
  290. Render: rd,
  291. }
  292. ctx.Data["PageStartTime"] = time.Now()
  293. // start session
  294. ctx.Session = setting.SessionManager.SessionStart(res, r)
  295. // Get flash.
  296. values, err := url.ParseQuery(ctx.GetCookie("gogs_flash"))
  297. if err != nil {
  298. log.Error("InitContext.ParseQuery(flash): %v", err)
  299. } else if len(values) > 0 {
  300. ctx.Flash = &Flash{Values: values}
  301. ctx.Flash.ErrorMsg = ctx.Flash.Get("error")
  302. ctx.Flash.SuccessMsg = ctx.Flash.Get("success")
  303. ctx.Data["Flash"] = ctx.Flash
  304. ctx.SetCookie("gogs_flash", "", -1)
  305. }
  306. ctx.Flash = &Flash{Values: url.Values{}}
  307. rw := res.(martini.ResponseWriter)
  308. rw.Before(func(martini.ResponseWriter) {
  309. ctx.Session.SessionRelease(res)
  310. if flash := ctx.Flash.Encode(); len(flash) > 0 {
  311. ctx.SetCookie("gogs_flash", ctx.Flash.Encode(), 0)
  312. }
  313. })
  314. // Get user from session if logined.
  315. user := auth.SignedInUser(ctx.req.Header, ctx.Session)
  316. ctx.User = user
  317. ctx.IsSigned = user != nil
  318. ctx.Data["IsSigned"] = ctx.IsSigned
  319. if user != nil {
  320. ctx.Data["SignedUser"] = user
  321. ctx.Data["SignedUserId"] = user.Id
  322. ctx.Data["SignedUserName"] = user.Name
  323. ctx.Data["IsAdmin"] = ctx.User.IsAdmin
  324. }
  325. // get or create csrf token
  326. ctx.Data["CsrfToken"] = ctx.CsrfToken()
  327. ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`)
  328. c.Map(ctx)
  329. c.Next()
  330. }
  331. }