| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 | // Copyright 2014 The Gogs Authors. All rights reserved.// Use of this source code is governed by a MIT-style// license that can be found in the LICENSE file.package contextimport (	"fmt"	"io"	"net/http"	"strings"	"time"	"github.com/go-macaron/cache"	"github.com/go-macaron/csrf"	"github.com/go-macaron/i18n"	"github.com/go-macaron/session"	"gopkg.in/macaron.v1"	log "unknwon.dev/clog/v2"	"gogs.io/gogs/internal/conf"	"gogs.io/gogs/internal/db"	"gogs.io/gogs/internal/errutil"	"gogs.io/gogs/internal/form"	"gogs.io/gogs/internal/template")// Context represents context of a request.type Context struct {	*macaron.Context	Cache   cache.Cache	csrf    csrf.CSRF	Flash   *session.Flash	Session session.Store	Link        string // Current request URL	User        *db.User	IsLogged    bool	IsBasicAuth bool	IsTokenAuth bool	Repo *Repository	Org  *Organization}// RawTitle sets the "Title" field in template data.func (c *Context) RawTitle(title string) {	c.Data["Title"] = title}// Title localizes the "Title" field in template data.func (c *Context) Title(locale string) {	c.RawTitle(c.Tr(locale))}// PageIs sets "PageIsxxx" field in template data.func (c *Context) PageIs(name string) {	c.Data["PageIs"+name] = true}// Require sets "Requirexxx" field in template data.func (c *Context) Require(name string) {	c.Data["Require"+name] = true}func (c *Context) RequireHighlightJS() {	c.Require("HighlightJS")}func (c *Context) RequireSimpleMDE() {	c.Require("SimpleMDE")}func (c *Context) RequireAutosize() {	c.Require("Autosize")}func (c *Context) RequireDropzone() {	c.Require("Dropzone")}// FormErr sets "Err_xxx" field in template data.func (c *Context) FormErr(names ...string) {	for i := range names {		c.Data["Err_"+names[i]] = true	}}// UserID returns ID of current logged in user.// It returns 0 if visitor is anonymous.func (c *Context) UserID() int64 {	if !c.IsLogged {		return 0	}	return c.User.ID}// HasError returns true if error occurs in form validation.func (c *Context) HasApiError() bool {	hasErr, ok := c.Data["HasError"]	if !ok {		return false	}	return hasErr.(bool)}func (c *Context) GetErrMsg() string {	return c.Data["ErrorMsg"].(string)}// HasError returns true if error occurs in form validation.func (c *Context) HasError() bool {	hasErr, ok := c.Data["HasError"]	if !ok {		return false	}	c.Flash.ErrorMsg = c.Data["ErrorMsg"].(string)	c.Data["Flash"] = c.Flash	return hasErr.(bool)}// HasValue returns true if value of given name exists.func (c *Context) HasValue(name string) bool {	_, ok := c.Data[name]	return ok}// HTML responses template with given status.func (c *Context) HTML(status int, name string) {	log.Trace("Template: %s", name)	c.Context.HTML(status, name)}// Success responses template with status http.StatusOK.func (c *Context) Success(name string) {	c.HTML(http.StatusOK, name)}// JSONSuccess responses JSON with status http.StatusOK.func (c *Context) JSONSuccess(data interface{}) {	c.JSON(http.StatusOK, data)}// RawRedirect simply calls underlying Redirect method with no escape.func (c *Context) RawRedirect(location string, status ...int) {	c.Context.Redirect(location, status...)}// Redirect responses redirection with given location and status.// It escapes special characters in the location string.func (c *Context) Redirect(location string, status ...int) {	c.Context.Redirect(template.EscapePound(location), status...)}// RedirectSubpath responses redirection with given location and status.// It prepends setting.Server.Subpath to the location string.func (c *Context) RedirectSubpath(location string, status ...int) {	c.Redirect(conf.Server.Subpath+location, status...)}// RenderWithErr used for page has form validation but need to prompt error to users.func (c *Context) RenderWithErr(msg, tpl string, f interface{}) {	if f != nil {		form.Assign(f, c.Data)	}	c.Flash.ErrorMsg = msg	c.Data["Flash"] = c.Flash	c.HTML(http.StatusOK, tpl)}// NotFound renders the 404 page.func (c *Context) NotFound() {	c.Title("status.page_not_found")	c.HTML(http.StatusNotFound, fmt.Sprintf("status/%d", http.StatusNotFound))}// Error renders the 500 page.func (c *Context) Error(err error, msg string) {	log.ErrorDepth(4, "%s: %v", msg, err)	c.Title("status.internal_server_error")	// Only in non-production mode or admin can see the actual error message.	if !conf.IsProdMode() || (c.IsLogged && c.User.IsAdmin) {		c.Data["ErrorMsg"] = err	}	c.HTML(http.StatusInternalServerError, fmt.Sprintf("status/%d", http.StatusInternalServerError))}// Errorf renders the 500 response with formatted message.func (c *Context) Errorf(err error, format string, args ...interface{}) {	c.Error(err, fmt.Sprintf(format, args...))}// NotFoundOrError responses with 404 page for not found error and 500 page otherwise.func (c *Context) NotFoundOrError(err error, msg string) {	if errutil.IsNotFound(err) {		c.NotFound()		return	}	c.Error(err, msg)}// NotFoundOrErrorf is same as NotFoundOrError but with formatted message.func (c *Context) NotFoundOrErrorf(err error, format string, args ...interface{}) {	c.NotFoundOrError(err, fmt.Sprintf(format, args...))}func (c *Context) PlainText(status int, msg string) {	c.Render.PlainText(status, []byte(msg))}func (c *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {	modtime := time.Now()	for _, p := range params {		switch v := p.(type) {		case time.Time:			modtime = v		}	}	c.Resp.Header().Set("Content-Description", "File Transfer")	c.Resp.Header().Set("Content-Type", "application/octet-stream")	c.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)	c.Resp.Header().Set("Content-Transfer-Encoding", "binary")	c.Resp.Header().Set("Expires", "0")	c.Resp.Header().Set("Cache-Control", "must-revalidate")	c.Resp.Header().Set("Pragma", "public")	http.ServeContent(c.Resp, c.Req.Request, name, modtime, r)}// Contexter initializes a classic context for a request.func Contexter() macaron.Handler {	return func(ctx *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) {		c := &Context{			Context: ctx,			Cache:   cache,			csrf:    x,			Flash:   f,			Session: sess,			Link:    conf.Server.Subpath + strings.TrimSuffix(ctx.Req.URL.Path, "/"),			Repo: &Repository{				PullRequest: &PullRequest{},			},			Org: &Organization{},		}		c.Data["Link"] = template.EscapePound(c.Link)		c.Data["PageStartTime"] = time.Now()		if len(conf.HTTP.AccessControlAllowOrigin) > 0 {			c.Header().Set("Access-Control-Allow-Origin", conf.HTTP.AccessControlAllowOrigin)			c.Header().Set("Access-Control-Allow-Credentials", "true")			c.Header().Set("Access-Control-Max-Age", "3600")			c.Header().Set("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")		}		// Get user from session or header when possible		c.User, c.IsBasicAuth, c.IsTokenAuth = authenticatedUser(c.Context, c.Session)		if c.User != nil {			c.IsLogged = true			c.Data["IsLogged"] = c.IsLogged			c.Data["LoggedUser"] = c.User			c.Data["LoggedUserID"] = c.User.ID			c.Data["LoggedUserName"] = c.User.Name			c.Data["IsAdmin"] = c.User.IsAdmin		} else {			c.Data["LoggedUserID"] = 0			c.Data["LoggedUserName"] = ""		}		// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.		if c.Req.Method == "POST" && strings.Contains(c.Req.Header.Get("Content-Type"), "multipart/form-data") {			if err := c.Req.ParseMultipartForm(conf.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size				c.Error(err, "parse multipart form")				return			}		}		c.Data["CSRFToken"] = x.GetToken()		c.Data["CSRFTokenHTML"] = template.Safe(`<input type="hidden" name="_csrf" value="` + x.GetToken() + `">`)		log.Trace("Session ID: %s", sess.ID())		log.Trace("CSRF Token: %v", c.Data["CSRFToken"])		c.Data["ShowRegistrationButton"] = !conf.Auth.DisableRegistration		c.Data["ShowFooterBranding"] = conf.Other.ShowFooterBranding		c.renderNoticeBanner()		// 🚨 SECURITY: Prevent MIME type sniffing in some browsers,		// see https://github.com/gogs/gogs/issues/5397 for details.		c.Header().Set("X-Content-Type-Options", "nosniff")		c.Header().Set("X-Frame-Options", "deny")		ctx.Map(c)	}}
 |