2014-03-15 19:01:50 +08:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2020-01-09 21:34:25 +00:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2014-03-15 19:01:50 +08:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2016-03-11 11:56:52 -05:00
package context
2014-03-15 19:01:50 +08:00
import (
2016-11-29 22:49:06 +01:00
"html"
2014-03-23 01:44:02 +08:00
"html/template"
2014-04-16 00:27:29 +08:00
"io"
2014-03-15 19:01:50 +08:00
"net/http"
2018-03-15 23:13:34 +02:00
"net/url"
2017-06-26 03:06:40 +02:00
"path"
2014-03-22 16:40:09 -04:00
"strings"
2014-03-19 21:57:55 +08:00
"time"
2014-03-15 19:01:50 +08:00
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
2021-01-05 21:05:40 +08:00
"code.gitea.io/gitea/modules/auth/sso"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
2019-03-18 10:00:23 -04:00
"code.gitea.io/gitea/modules/util"
2019-08-23 09:40:30 -07:00
"gitea.com/macaron/cache"
"gitea.com/macaron/csrf"
"gitea.com/macaron/i18n"
"gitea.com/macaron/macaron"
"gitea.com/macaron/session"
"github.com/unknwon/com"
2014-03-15 19:01:50 +08:00
)
2014-03-15 09:17:16 -04:00
// Context represents context of a request.
2014-03-15 19:01:50 +08:00
type Context struct {
2014-07-26 00:24:27 -04:00
* macaron . Context
2014-07-31 17:25:34 -04:00
Cache cache . Cache
csrf csrf . CSRF
2014-07-26 00:24:27 -04:00
Flash * session . Flash
Session session . Store
2017-06-26 03:06:40 +02:00
Link string // current request URL
2017-11-28 01:43:51 -08:00
EscapedLink string
2014-11-18 11:07:16 -05:00
User * models . User
IsSigned bool
IsBasicAuth bool
2014-03-16 00:03:23 +08:00
2016-03-11 11:56:52 -05:00
Repo * Repository
2016-03-13 17:37:44 -04:00
Org * Organization
2014-03-15 19:01:50 +08:00
}
2021-01-05 21:05:40 +08:00
// GetData returns the data
func ( ctx * Context ) GetData ( ) map [ string ] interface { } {
return ctx . Data
}
2019-04-07 22:49:34 +00:00
// IsUserSiteAdmin returns true if current user is a site admin
func ( ctx * Context ) IsUserSiteAdmin ( ) bool {
return ctx . IsSigned && ctx . User . IsAdmin
}
// IsUserRepoOwner returns true if current user owns current repo
func ( ctx * Context ) IsUserRepoOwner ( ) bool {
return ctx . Repo . IsOwner ( )
}
// IsUserRepoAdmin returns true if current user is admin in current repo
func ( ctx * Context ) IsUserRepoAdmin ( ) bool {
return ctx . Repo . IsAdmin ( )
}
// IsUserRepoWriter returns true if current user has write privilege in current repo
func ( ctx * Context ) IsUserRepoWriter ( unitTypes [ ] models . UnitType ) bool {
for _ , unitType := range unitTypes {
if ctx . Repo . CanWrite ( unitType ) {
return true
}
}
return false
}
// IsUserRepoReaderSpecific returns true if current user can read current repo's specific part
func ( ctx * Context ) IsUserRepoReaderSpecific ( unitType models . UnitType ) bool {
return ctx . Repo . CanRead ( unitType )
}
// IsUserRepoReaderAny returns true if current user can read any part of current repo
func ( ctx * Context ) IsUserRepoReaderAny ( ) bool {
return ctx . Repo . HasAccess ( )
}
2016-11-25 14:51:01 +08:00
// HasAPIError returns true if error occurs in form validation.
func ( ctx * Context ) HasAPIError ( ) bool {
2014-05-05 13:08:01 -04:00
hasErr , ok := ctx . Data [ "HasError" ]
if ! ok {
return false
}
return hasErr . ( bool )
}
2016-11-25 14:51:01 +08:00
// GetErrMsg returns error message
2014-05-05 13:08:01 -04:00
func ( ctx * Context ) GetErrMsg ( ) string {
return ctx . Data [ "ErrorMsg" ] . ( string )
}
2014-03-15 10:52:14 -04:00
// HasError returns true if error occurs in form validation.
func ( ctx * Context ) HasError ( ) bool {
hasErr , ok := ctx . Data [ "HasError" ]
if ! ok {
return false
}
2014-04-13 18:12:07 -04:00
ctx . Flash . ErrorMsg = ctx . Data [ "ErrorMsg" ] . ( string )
ctx . Data [ "Flash" ] = ctx . Flash
2014-03-15 10:52:14 -04:00
return hasErr . ( bool )
}
2015-07-08 19:47:56 +08:00
// HasValue returns true if value of given name exists.
func ( ctx * Context ) HasValue ( name string ) bool {
_ , ok := ctx . Data [ name ]
return ok
}
2018-03-15 23:13:34 +02:00
// RedirectToFirst redirects to first not empty URL
func ( ctx * Context ) RedirectToFirst ( location ... string ) {
for _ , loc := range location {
if len ( loc ) == 0 {
continue
}
u , err := url . Parse ( loc )
2020-01-09 21:34:25 +00:00
if err != nil || ( ( u . Scheme != "" || u . Host != "" ) && ! strings . HasPrefix ( strings . ToLower ( loc ) , strings . ToLower ( setting . AppURL ) ) ) {
2018-03-15 23:13:34 +02:00
continue
}
ctx . Redirect ( loc )
return
}
ctx . Redirect ( setting . AppSubURL + "/" )
}
2014-08-02 13:47:33 -04:00
// HTML calls Context.HTML and converts template name to string.
2014-07-26 00:24:27 -04:00
func ( ctx * Context ) HTML ( status int , name base . TplName ) {
2015-12-20 01:06:54 -05:00
log . Debug ( "Template: %s" , name )
2014-08-02 13:47:33 -04:00
ctx . Context . HTML ( status , string ( name ) )
2014-03-20 07:50:26 -04:00
}
2014-03-15 10:52:14 -04:00
// RenderWithErr used for page has form validation but need to prompt error to users.
2014-07-26 00:24:27 -04:00
func ( ctx * Context ) RenderWithErr ( msg string , tpl base . TplName , form interface { } ) {
2014-04-03 15:50:55 -04:00
if form != nil {
auth . AssignForm ( form , ctx . Data )
}
2014-04-10 16:36:50 -04:00
ctx . Flash . ErrorMsg = msg
ctx . Data [ "Flash" ] = ctx . Flash
2014-03-20 07:50:26 -04:00
ctx . HTML ( 200 , tpl )
2014-03-15 10:52:14 -04:00
}
2018-01-10 22:34:17 +01:00
// NotFound displays a 404 (Not Found) page and prints the given error, if any.
func ( ctx * Context ) NotFound ( title string , err error ) {
2019-04-09 19:10:42 +01:00
ctx . notFoundInternal ( title , err )
}
func ( ctx * Context ) notFoundInternal ( title string , err error ) {
2014-05-01 18:53:41 -04:00
if err != nil {
2019-04-09 19:10:42 +01:00
log . ErrorWithSkip ( 2 , "%s: %v" , title , err )
2014-07-26 00:24:27 -04:00
if macaron . Env != macaron . PROD {
2014-05-01 18:53:41 -04:00
ctx . Data [ "ErrorMsg" ] = err
}
2014-03-19 04:48:45 -04:00
}
2019-02-19 17:09:47 -06:00
ctx . Data [ "IsRepo" ] = ctx . Repo . Repository != nil
2018-01-10 22:34:17 +01:00
ctx . Data [ "Title" ] = "Page Not Found"
ctx . HTML ( http . StatusNotFound , base . TplName ( "status/404" ) )
}
// ServerError displays a 500 (Internal Server Error) page and prints the given
// error, if any.
func ( ctx * Context ) ServerError ( title string , err error ) {
2019-04-09 19:10:42 +01:00
ctx . serverErrorInternal ( title , err )
}
func ( ctx * Context ) serverErrorInternal ( title string , err error ) {
2018-01-10 22:34:17 +01:00
if err != nil {
2019-04-09 19:10:42 +01:00
log . ErrorWithSkip ( 2 , "%s: %v" , title , err )
2018-01-10 22:34:17 +01:00
if macaron . Env != macaron . PROD {
ctx . Data [ "ErrorMsg" ] = err
}
2014-05-01 18:53:41 -04:00
}
2018-01-10 22:34:17 +01:00
ctx . Data [ "Title" ] = "Internal Server Error"
2019-01-30 22:00:00 +00:00
ctx . HTML ( http . StatusInternalServerError , base . TplName ( "status/500" ) )
2014-03-15 19:01:50 +08:00
}
2016-08-30 02:08:38 -07:00
// NotFoundOrServerError use error check function to determine if the error
// is about not found. It responses with 404 status code for not found error,
// or error context description for logging purpose of 500 server error.
func ( ctx * Context ) NotFoundOrServerError ( title string , errck func ( error ) bool , err error ) {
2016-07-26 02:48:17 +08:00
if errck ( err ) {
2019-04-09 19:10:42 +01:00
ctx . notFoundInternal ( title , err )
2016-07-26 02:48:17 +08:00
return
}
2019-04-09 19:10:42 +01:00
ctx . serverErrorInternal ( title , err )
2016-07-26 02:48:17 +08:00
}
2016-11-25 14:51:01 +08:00
// HandleText handles HTTP status code
2015-03-28 22:30:05 +08:00
func ( ctx * Context ) HandleText ( status int , title string ) {
2015-07-08 19:47:56 +08:00
if ( status / 100 == 4 ) || ( status / 100 == 5 ) {
2019-04-02 08:48:31 +01:00
log . Error ( "%s" , title )
2015-03-28 22:30:05 +08:00
}
2015-10-15 21:28:12 -04:00
ctx . PlainText ( status , [ ] byte ( title ) )
2015-03-28 22:30:05 +08:00
}
2016-11-25 14:51:01 +08:00
// ServeContent serves content to http request
2014-04-16 00:27:29 +08:00
func ( ctx * 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
}
}
2014-07-26 00:24:27 -04:00
ctx . Resp . Header ( ) . Set ( "Content-Description" , "File Transfer" )
ctx . Resp . Header ( ) . Set ( "Content-Type" , "application/octet-stream" )
ctx . Resp . Header ( ) . Set ( "Content-Disposition" , "attachment; filename=" + name )
ctx . Resp . Header ( ) . Set ( "Content-Transfer-Encoding" , "binary" )
ctx . Resp . Header ( ) . Set ( "Expires" , "0" )
ctx . Resp . Header ( ) . Set ( "Cache-Control" , "must-revalidate" )
ctx . Resp . Header ( ) . Set ( "Pragma" , "public" )
2020-08-13 18:18:18 +01:00
ctx . Resp . Header ( ) . Set ( "Access-Control-Expose-Headers" , "Content-Disposition" )
2014-10-18 23:26:55 -04:00
http . ServeContent ( ctx . Resp , ctx . Req . Request , name , modtime , r )
2014-04-10 14:37:43 -04:00
}
2014-07-26 00:24:27 -04:00
// Contexter initializes a classic context for a request.
func Contexter ( ) macaron . Handler {
2014-07-31 17:25:34 -04:00
return func ( c * macaron . Context , l i18n . Locale , cache cache . Cache , sess session . Store , f * session . Flash , x csrf . CSRF ) {
2014-03-15 19:01:50 +08:00
ctx := & Context {
2014-07-26 00:24:27 -04:00
Context : c ,
2014-07-31 17:25:34 -04:00
Cache : cache ,
csrf : x ,
2014-07-26 00:24:27 -04:00
Flash : f ,
Session : sess ,
2017-11-28 01:43:51 -08:00
Link : setting . AppSubURL + strings . TrimSuffix ( c . Req . URL . EscapedPath ( ) , "/" ) ,
2016-03-11 11:56:52 -05:00
Repo : & Repository {
PullRequest : & PullRequest { } ,
2016-03-06 23:57:46 -05:00
} ,
2016-03-13 17:37:44 -04:00
Org : & Organization { } ,
2014-03-15 19:01:50 +08:00
}
2019-03-18 12:49:01 +00:00
ctx . Data [ "Language" ] = ctx . Locale . Language ( )
2017-06-26 03:06:40 +02:00
c . Data [ "Link" ] = ctx . Link
2020-06-03 23:41:02 -04:00
ctx . Data [ "CurrentURL" ] = setting . AppSubURL + c . Req . URL . RequestURI ( )
2014-07-26 00:24:27 -04:00
ctx . Data [ "PageStartTime" ] = time . Now ( )
2017-06-26 03:06:40 +02:00
// Quick responses appropriate go-get meta with status 200
// regardless of if user have access to the repository,
// or the repository does not exist at all.
// This is particular a workaround for "go get" command which does not respect
// .netrc file.
if ctx . Query ( "go-get" ) == "1" {
ownerName := c . Params ( ":username" )
repoName := c . Params ( ":reponame" )
2019-09-06 21:44:59 +08:00
trimmedRepoName := strings . TrimSuffix ( repoName , ".git" )
if ownerName == "" || trimmedRepoName == "" {
_ , _ = c . Write ( [ ] byte ( ` < ! doctype html >
< html >
< body >
invalid import path
< / body >
< / html >
` ) )
c . WriteHeader ( 400 )
return
}
2017-06-26 03:06:40 +02:00
branchName := "master"
2017-12-02 15:34:39 +08:00
repo , err := models . GetRepositoryByOwnerAndName ( ownerName , repoName )
if err == nil && len ( repo . DefaultBranch ) > 0 {
branchName = repo . DefaultBranch
2017-06-26 03:06:40 +02:00
}
2019-03-18 10:00:23 -04:00
prefix := setting . AppURL + path . Join ( url . PathEscape ( ownerName ) , url . PathEscape ( repoName ) , "src" , "branch" , util . PathEscapeSegments ( branchName ) )
2019-05-27 22:08:38 +01:00
appURL , _ := url . Parse ( setting . AppURL )
insecure := ""
if appURL . Scheme == string ( setting . HTTP ) {
insecure = "--insecure "
}
2018-01-29 19:50:04 +02:00
c . Header ( ) . Set ( "Content-Type" , "text/html" )
c . WriteHeader ( http . StatusOK )
2019-06-12 21:41:28 +02:00
_ , _ = c . Write ( [ ] byte ( com . Expand ( ` < ! doctype html >
2017-06-26 03:06:40 +02:00
< html >
< head >
< meta name = "go-import" content = "{GoGetImport} git {CloneLink}" >
< meta name = "go-source" content = "{GoGetImport} _ {GoDocDirectory} {GoDocFile}" >
< / head >
< body >
2019-05-27 22:08:38 +01:00
go get { Insecure } { GoGetImport }
2017-06-26 03:06:40 +02:00
< / body >
< / html >
` , map [ string ] string {
2019-09-06 21:44:59 +08:00
"GoGetImport" : ComposeGoGetImport ( ownerName , trimmedRepoName ) ,
2017-06-26 03:06:40 +02:00
"CloneLink" : models . ComposeHTTPSCloneURL ( ownerName , repoName ) ,
"GoDocDirectory" : prefix + "{/dir}" ,
"GoDocFile" : prefix + "{/dir}/{file}#L{line}" ,
2019-05-27 22:08:38 +01:00
"Insecure" : insecure ,
2017-06-26 03:06:40 +02:00
} ) ) )
return
}
2014-03-22 20:49:53 +08:00
2017-03-14 20:52:01 -04:00
// Get user from session if logged in.
2021-01-06 09:38:00 +08:00
ctx . User , ctx . IsBasicAuth = sso . SignedInUser ( ctx . Req . Request , c . Resp , ctx , ctx . Session )
2014-11-07 14:46:13 -05:00
2014-07-26 00:24:27 -04:00
if ctx . User != nil {
ctx . IsSigned = true
ctx . Data [ "IsSigned" ] = ctx . IsSigned
ctx . Data [ "SignedUser" ] = ctx . User
2016-07-24 01:08:22 +08:00
ctx . Data [ "SignedUserID" ] = ctx . User . ID
2014-11-06 22:06:41 -05:00
ctx . Data [ "SignedUserName" ] = ctx . User . Name
2014-03-20 08:02:14 -04:00
ctx . Data [ "IsAdmin" ] = ctx . User . IsAdmin
2014-11-06 22:06:41 -05:00
} else {
2017-12-04 01:14:26 +02:00
ctx . Data [ "SignedUserID" ] = int64 ( 0 )
2014-11-06 22:06:41 -05:00
ctx . Data [ "SignedUserName" ] = ""
2014-03-15 20:50:17 +08:00
}
2014-03-15 19:01:50 +08:00
2014-07-24 15:19:59 +02:00
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
2014-07-26 00:24:27 -04:00
if ctx . Req . Method == "POST" && strings . Contains ( ctx . Req . Header . Get ( "Content-Type" ) , "multipart/form-data" ) {
2020-08-18 12:23:45 +08:00
if err := ctx . Req . ParseMultipartForm ( setting . Attachment . MaxSize << 20 ) ; err != nil && ! strings . Contains ( err . Error ( ) , "EOF" ) { // 32MB max size
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "ParseMultipartForm" , err )
2014-07-24 15:19:59 +02:00
return
}
}
2016-11-29 22:49:06 +01:00
ctx . Resp . Header ( ) . Set ( ` X-Frame-Options ` , ` SAMEORIGIN ` )
ctx . Data [ "CsrfToken" ] = html . EscapeString ( x . GetToken ( ) )
ctx . Data [ "CsrfTokenHtml" ] = template . HTML ( ` <input type="hidden" name="_csrf" value=" ` + ctx . Data [ "CsrfToken" ] . ( string ) + ` "> ` )
2015-12-21 04:24:11 -08:00
log . Debug ( "Session ID: %s" , sess . ID ( ) )
log . Debug ( "CSRF Token: %v" , ctx . Data [ "CsrfToken" ] )
2014-03-19 21:57:55 +08:00
2018-08-27 04:23:27 +02:00
ctx . Data [ "IsLandingPageHome" ] = setting . LandingPageURL == setting . LandingPageHome
ctx . Data [ "IsLandingPageExplore" ] = setting . LandingPageURL == setting . LandingPageExplore
ctx . Data [ "IsLandingPageOrganizations" ] = setting . LandingPageURL == setting . LandingPageOrganizations
2015-02-06 21:16:23 -05:00
ctx . Data [ "ShowRegistrationButton" ] = setting . Service . ShowRegistrationButton
2019-12-15 08:20:08 -06:00
ctx . Data [ "ShowMilestonesDashboardPage" ] = setting . Service . ShowMilestonesDashboardPage
2015-03-23 10:19:19 -04:00
ctx . Data [ "ShowFooterBranding" ] = setting . ShowFooterBranding
2015-11-18 22:32:31 +01:00
ctx . Data [ "ShowFooterVersion" ] = setting . ShowFooterVersion
2018-08-27 04:23:27 +02:00
2018-07-28 02:19:01 +02:00
ctx . Data [ "EnableSwagger" ] = setting . API . EnableSwagger
2017-03-29 12:57:43 +02:00
ctx . Data [ "EnableOpenIDSignIn" ] = setting . Service . EnableOpenIDSignIn
2020-12-21 15:39:41 +01:00
ctx . Data [ "DisableMigrations" ] = setting . Repository . DisableMigrations
2015-02-06 21:16:23 -05:00
2020-12-22 12:13:50 +01:00
ctx . Data [ "ManifestData" ] = setting . ManifestData
2014-03-15 19:01:50 +08:00
c . Map ( ctx )
}
}