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.

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