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.

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