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.

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