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