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.4 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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. log.Error("%s: %v", title, err)
  96. if martini.Dev != martini.Prod {
  97. ctx.Data["ErrorMsg"] = err
  98. }
  99. ctx.HTML(status, fmt.Sprintf("status/%d", status))
  100. }
  101. func (ctx *Context) Debug(msg string, args ...interface{}) {
  102. log.Debug(msg, args...)
  103. }
  104. func (ctx *Context) GetCookie(name string) string {
  105. cookie, err := ctx.Req.Cookie(name)
  106. if err != nil {
  107. return ""
  108. }
  109. return cookie.Value
  110. }
  111. func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
  112. cookie := http.Cookie{}
  113. cookie.Name = name
  114. cookie.Value = value
  115. if len(others) > 0 {
  116. switch v := others[0].(type) {
  117. case int:
  118. cookie.MaxAge = v
  119. case int64:
  120. cookie.MaxAge = int(v)
  121. case int32:
  122. cookie.MaxAge = int(v)
  123. }
  124. }
  125. // default "/"
  126. if len(others) > 1 {
  127. if v, ok := others[1].(string); ok && len(v) > 0 {
  128. cookie.Path = v
  129. }
  130. } else {
  131. cookie.Path = "/"
  132. }
  133. // default empty
  134. if len(others) > 2 {
  135. if v, ok := others[2].(string); ok && len(v) > 0 {
  136. cookie.Domain = v
  137. }
  138. }
  139. // default empty
  140. if len(others) > 3 {
  141. switch v := others[3].(type) {
  142. case bool:
  143. cookie.Secure = v
  144. default:
  145. if others[3] != nil {
  146. cookie.Secure = true
  147. }
  148. }
  149. }
  150. // default false. for session cookie default true
  151. if len(others) > 4 {
  152. if v, ok := others[4].(bool); ok && v {
  153. cookie.HttpOnly = true
  154. }
  155. }
  156. ctx.Res.Header().Add("Set-Cookie", cookie.String())
  157. }
  158. // Get secure cookie from request by a given key.
  159. func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
  160. val := ctx.GetCookie(key)
  161. if val == "" {
  162. return "", false
  163. }
  164. parts := strings.SplitN(val, "|", 3)
  165. if len(parts) != 3 {
  166. return "", false
  167. }
  168. vs := parts[0]
  169. timestamp := parts[1]
  170. sig := parts[2]
  171. h := hmac.New(sha1.New, []byte(Secret))
  172. fmt.Fprintf(h, "%s%s", vs, timestamp)
  173. if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
  174. return "", false
  175. }
  176. res, _ := base64.URLEncoding.DecodeString(vs)
  177. return string(res), true
  178. }
  179. // Set Secure cookie for response.
  180. func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
  181. vs := base64.URLEncoding.EncodeToString([]byte(value))
  182. timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
  183. h := hmac.New(sha1.New, []byte(Secret))
  184. fmt.Fprintf(h, "%s%s", vs, timestamp)
  185. sig := fmt.Sprintf("%02x", h.Sum(nil))
  186. cookie := strings.Join([]string{vs, timestamp, sig}, "|")
  187. ctx.SetCookie(name, cookie, others...)
  188. }
  189. func (ctx *Context) CsrfToken() string {
  190. if len(ctx.csrfToken) > 0 {
  191. return ctx.csrfToken
  192. }
  193. token := ctx.GetCookie("_csrf")
  194. if len(token) == 0 {
  195. token = base.GetRandomString(30)
  196. ctx.SetCookie("_csrf", token)
  197. }
  198. ctx.csrfToken = token
  199. return token
  200. }
  201. func (ctx *Context) CsrfTokenValid() bool {
  202. token := ctx.Query("_csrf")
  203. if token == "" {
  204. token = ctx.Req.Header.Get("X-Csrf-Token")
  205. }
  206. if token == "" {
  207. return false
  208. } else if ctx.csrfToken != token {
  209. return false
  210. }
  211. return true
  212. }
  213. func (ctx *Context) ServeFile(file string, names ...string) {
  214. var name string
  215. if len(names) > 0 {
  216. name = names[0]
  217. } else {
  218. name = filepath.Base(file)
  219. }
  220. ctx.Res.Header().Set("Content-Description", "File Transfer")
  221. ctx.Res.Header().Set("Content-Type", "application/octet-stream")
  222. ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name)
  223. ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
  224. ctx.Res.Header().Set("Expires", "0")
  225. ctx.Res.Header().Set("Cache-Control", "must-revalidate")
  226. ctx.Res.Header().Set("Pragma", "public")
  227. http.ServeFile(ctx.Res, ctx.Req, file)
  228. }
  229. func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
  230. modtime := time.Now()
  231. for _, p := range params {
  232. switch v := p.(type) {
  233. case time.Time:
  234. modtime = v
  235. }
  236. }
  237. ctx.Res.Header().Set("Content-Description", "File Transfer")
  238. ctx.Res.Header().Set("Content-Type", "application/octet-stream")
  239. ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name)
  240. ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
  241. ctx.Res.Header().Set("Expires", "0")
  242. ctx.Res.Header().Set("Cache-Control", "must-revalidate")
  243. ctx.Res.Header().Set("Pragma", "public")
  244. http.ServeContent(ctx.Res, ctx.Req, name, modtime, r)
  245. }
  246. type Flash struct {
  247. url.Values
  248. ErrorMsg, SuccessMsg string
  249. }
  250. func (f *Flash) Error(msg string) {
  251. f.Set("error", msg)
  252. f.ErrorMsg = msg
  253. }
  254. func (f *Flash) Success(msg string) {
  255. f.Set("success", msg)
  256. f.SuccessMsg = msg
  257. }
  258. // InitContext initializes a classic context for a request.
  259. func InitContext() martini.Handler {
  260. return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) {
  261. ctx := &Context{
  262. c: c,
  263. // p: p,
  264. Req: r,
  265. Res: res,
  266. Cache: base.Cache,
  267. Render: rd,
  268. }
  269. ctx.Data["PageStartTime"] = time.Now()
  270. // start session
  271. ctx.Session = base.SessionManager.SessionStart(res, r)
  272. // Get flash.
  273. values, err := url.ParseQuery(ctx.GetCookie("gogs_flash"))
  274. if err != nil {
  275. log.Error("InitContext.ParseQuery(flash): %v", err)
  276. } else if len(values) > 0 {
  277. ctx.Flash = &Flash{Values: values}
  278. ctx.Flash.ErrorMsg = ctx.Flash.Get("error")
  279. ctx.Flash.SuccessMsg = ctx.Flash.Get("success")
  280. ctx.Data["Flash"] = ctx.Flash
  281. ctx.SetCookie("gogs_flash", "", -1)
  282. }
  283. ctx.Flash = &Flash{Values: url.Values{}}
  284. rw := res.(martini.ResponseWriter)
  285. rw.Before(func(martini.ResponseWriter) {
  286. ctx.Session.SessionRelease(res)
  287. if flash := ctx.Flash.Encode(); len(flash) > 0 {
  288. ctx.SetCookie("gogs_flash", ctx.Flash.Encode(), 0)
  289. }
  290. })
  291. // Get user from session if logined.
  292. user := auth.SignedInUser(ctx.Session)
  293. ctx.User = user
  294. ctx.IsSigned = user != nil
  295. ctx.Data["IsSigned"] = ctx.IsSigned
  296. if user != nil {
  297. ctx.Data["SignedUser"] = user
  298. ctx.Data["SignedUserId"] = user.Id
  299. ctx.Data["SignedUserName"] = user.Name
  300. ctx.Data["IsAdmin"] = ctx.User.IsAdmin
  301. }
  302. // get or create csrf token
  303. ctx.Data["CsrfToken"] = ctx.CsrfToken()
  304. ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`)
  305. c.Map(ctx)
  306. c.Next()
  307. }
  308. }