Multiple Escaping Improvements (#17551)

There are multiple places where Gitea does not properly escape URLs that it is building and there are multiple places where it builds urls when there is already a simpler function available to use this.
    
This is an extensive PR attempting to fix these issues.

1. The first commit in this PR looks through all href, src and links in the Gitea codebase and has attempted to catch all the places where there is potentially incomplete escaping.
2. Whilst doing this we will prefer to use functions that create URLs over recreating them by hand.
3. All uses of strings should be directly escaped - even if they are not currently expected to contain escaping characters. The main benefit to doing this will be that we can consider relaxing the constraints on user names and reponames in future. 
4. The next commit looks at escaping in the wiki and re-considers the urls that are used there. Using the improved escaping here wiki files containing '/'. (This implementation will currently still place all of the wiki files the root directory of the repo but this would not be difficult to change.)
5. The title generation in feeds is now properly escaped.
6. EscapePound is no longer needed - urls should be PathEscaped / QueryEscaped as necessary but then re-escaped with Escape when creating html with locales Signed-off-by: Andrew Thornton <art27@cantab.net>

Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
zeripath 2021-11-16 18:18:25 +00:00 committed by GitHub
parent 7e1ae38097
commit bbffcc3aec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
153 changed files with 891 additions and 712 deletions

View file

@ -6,6 +6,7 @@ package org
import (
"net/http"
"net/url"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
@ -159,7 +160,7 @@ func IsMember(ctx *context.APIContext) {
}
}
redirectURL := setting.AppSubURL + "/api/v1/orgs/" + ctx.Org.Organization.Name + "/public_members/" + userToCheck.Name
redirectURL := setting.AppSubURL + "/api/v1/orgs/" + url.PathEscape(ctx.Org.Organization.Name) + "/public_members/" + url.PathEscape(userToCheck.Name)
ctx.Redirect(redirectURL, 302)
}

View file

@ -6,9 +6,11 @@ package repo
import (
"net/http"
"net/url"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils"
)
@ -89,11 +91,11 @@ func getGitRefsInternal(ctx *context.APIContext, filter string) {
for i := range refs {
apiRefs[i] = &api.Reference{
Ref: refs[i].Name,
URL: ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Name,
URL: ctx.Repo.Repository.APIURL() + "/git/" + util.PathEscapeSegments(refs[i].Name),
Object: &api.GitObject{
SHA: refs[i].Object.String(),
Type: refs[i].Type,
URL: ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Type + "s/" + refs[i].Object.String(),
URL: ctx.Repo.Repository.APIURL() + "/git/" + url.PathEscape(refs[i].Type) + "s/" + url.PathEscape(refs[i].Object.String()),
},
}
}

View file

@ -8,6 +8,7 @@ package repo
import (
"fmt"
"net/http"
"net/url"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
@ -33,8 +34,8 @@ func appendPrivateInformation(apiKey *api.DeployKey, key *models.DeployKey, repo
return apiKey, nil
}
func composeDeployKeysAPILink(repoPath string) string {
return setting.AppURL + "api/v1/repos/" + repoPath + "/keys/"
func composeDeployKeysAPILink(owner, name string) string {
return setting.AppURL + "api/v1/repos/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/keys/"
}
// ListDeployKeys list all the deploy keys of a repository
@ -94,7 +95,7 @@ func ListDeployKeys(ctx *context.APIContext) {
return
}
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
apiKeys := make([]*api.DeployKey, len(keys))
for i := range keys {
if err := keys[i].GetContent(); err != nil {
@ -154,7 +155,7 @@ func GetDeployKey(ctx *context.APIContext) {
return
}
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
apiKey := convert.ToDeployKey(apiLink, key)
if ctx.User.IsAdmin || ((ctx.Repo.Repository.ID == key.RepoID) && (ctx.User.ID == ctx.Repo.Owner.ID)) {
apiKey, _ = appendPrivateInformation(apiKey, key, ctx.Repo.Repository)
@ -233,7 +234,7 @@ func CreateDeployKey(ctx *context.APIContext) {
}
key.Content = content
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
ctx.JSON(http.StatusCreated, convert.ToDeployKey(apiLink, key))
}

View file

@ -8,7 +8,9 @@ import (
"errors"
"fmt"
"net/http"
"net/url"
"regexp"
"strconv"
"code.gitea.io/gitea/models/login"
"code.gitea.io/gitea/modules/auth/pam"
@ -396,7 +398,7 @@ func EditAuthSourcePost(ctx *context.Context) {
log.Trace("Authentication changed by admin(%s): %d", ctx.User.Name, source.ID)
ctx.Flash.Success(ctx.Tr("admin.auths.update_success"))
ctx.Redirect(setting.AppSubURL + "/admin/auths/" + fmt.Sprint(form.ID))
ctx.Redirect(setting.AppSubURL + "/admin/auths/" + strconv.FormatInt(form.ID, 10))
}
// DeleteAuthSource response for deleting an auth source
@ -414,7 +416,7 @@ func DeleteAuthSource(ctx *context.Context) {
ctx.Flash.Error(fmt.Sprintf("DeleteLoginSource: %v", err))
}
ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": setting.AppSubURL + "/admin/auths/" + ctx.Params(":authid"),
"redirect": setting.AppSubURL + "/admin/auths/" + url.PathEscape(ctx.Params(":authid")),
})
return
}

View file

@ -58,7 +58,7 @@ func DeleteRepo(ctx *context.Context) {
ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": setting.AppSubURL + "/admin/repos?page=" + ctx.FormString("page") + "&sort=" + ctx.FormString("sort"),
"redirect": setting.AppSubURL + "/admin/repos?page=" + url.QueryEscape(ctx.FormString("page")) + "&sort=" + url.QueryEscape(ctx.FormString("sort")),
})
}
@ -161,5 +161,5 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.delete_preexisting_success", dir))
}
ctx.Redirect(setting.AppSubURL + "/admin/repos/unadopted?search=true&q=" + url.QueryEscape(q) + "&page=" + page)
ctx.Redirect(setting.AppSubURL + "/admin/repos/unadopted?search=true&q=" + url.QueryEscape(q) + "&page=" + url.QueryEscape(page))
}

View file

@ -6,8 +6,8 @@
package admin
import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
@ -188,7 +188,7 @@ func NewUserPost(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name))
ctx.Redirect(setting.AppSubURL + "/admin/users/" + fmt.Sprint(u.ID))
ctx.Redirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
}
func prepareUserInfo(ctx *context.Context) *models.User {
@ -366,7 +366,7 @@ func EditUserPost(ctx *context.Context) {
log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name)
ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success"))
ctx.Redirect(setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"))
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
}
// DeleteUser response for deleting a user
@ -382,12 +382,12 @@ func DeleteUser(ctx *context.Context) {
case models.IsErrUserOwnRepos(err):
ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo"))
ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"),
"redirect": setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")),
})
case models.IsErrUserHasOrgs(err):
ctx.Flash.Error(ctx.Tr("admin.users.still_has_org"))
ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"),
"redirect": setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")),
})
default:
ctx.ServerError("DeleteUser", err)

View file

@ -15,10 +15,35 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util"
"github.com/gorilla/feeds"
)
func toBranchLink(act *models.Action) string {
return act.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
}
func toTagLink(act *models.Action) string {
return act.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
}
func toIssueLink(act *models.Action) string {
return act.GetRepoLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
}
func toPullLink(act *models.Action) string {
return act.GetRepoLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
}
func toSrcLink(act *models.Action) string {
return act.GetRepoLink() + "/src/" + util.PathEscapeSegments(act.GetBranch())
}
func toReleaseLink(act *models.Action) string {
return act.GetRepoLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
}
// feedActionsToFeedItems convert gitea's Action feed to feeds Item
func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (items []*feeds.Item, err error) {
for _, act := range actions {
@ -32,62 +57,111 @@ func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (ite
title = act.ActUser.DisplayName() + " "
switch act.OpType {
case models.ActionCreateRepo:
title += ctx.Tr("action.create_repo", act.GetRepoLink(), act.ShortRepoPath())
title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoLink(), act.ShortRepoPath())
link.Href = act.GetRepoLink()
case models.ActionRenameRepo:
title += ctx.Tr("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
link.Href = act.GetRepoLink()
case models.ActionCommitRepo:
branchLink := act.GetBranch()
link.Href = toBranchLink(act)
if len(act.Content) != 0 {
title += ctx.Tr("action.commit_repo", act.GetRepoLink(), branchLink, act.GetBranch(), act.ShortRepoPath())
title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
} else {
title += ctx.Tr("action.create_branch", act.GetRepoLink(), branchLink, act.GetBranch(), act.ShortRepoPath())
title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
}
case models.ActionCreateIssue:
title += ctx.Tr("action.create_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
link.Href = toIssueLink(act)
title += ctx.TrHTMLEscapeArgs("action.create_issue", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionCreatePullRequest:
title += ctx.Tr("action.create_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
link.Href = toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionTransferRepo:
title += ctx.Tr("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
link.Href = act.GetRepoLink()
title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
case models.ActionPushTag:
title += ctx.Tr("action.push_tag", act.GetRepoLink(), url.QueryEscape(act.GetTag()), act.ShortRepoPath())
link.Href = toTagLink(act)
title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoLink(), link.Href, act.GetTag(), act.ShortRepoPath())
case models.ActionCommentIssue:
title += ctx.Tr("action.comment_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
issueLink := toIssueLink(act)
if link.Href == "#" {
link.Href = issueLink
}
title += ctx.TrHTMLEscapeArgs("action.comment_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionMergePullRequest:
title += ctx.Tr("action.merge_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
pullLink := toPullLink(act)
if link.Href == "#" {
link.Href = pullLink
}
title += ctx.TrHTMLEscapeArgs("action.merge_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionCloseIssue:
title += ctx.Tr("action.close_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
issueLink := toIssueLink(act)
if link.Href == "#" {
link.Href = issueLink
}
title += ctx.TrHTMLEscapeArgs("action.close_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionReopenIssue:
title += ctx.Tr("action.reopen_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
issueLink := toIssueLink(act)
if link.Href == "#" {
link.Href = issueLink
}
title += ctx.TrHTMLEscapeArgs("action.reopen_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionClosePullRequest:
title += ctx.Tr("action.close_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
pullLink := toPullLink(act)
if link.Href == "#" {
link.Href = pullLink
}
title += ctx.TrHTMLEscapeArgs("action.close_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionReopenPullRequest:
title += ctx.Tr("action.reopen_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath)
pullLink := toPullLink(act)
if link.Href == "#" {
link.Href = pullLink
}
title += ctx.TrHTMLEscapeArgs("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionDeleteTag:
title += ctx.Tr("action.delete_tag", act.GetRepoLink(), html.EscapeString(act.GetTag()), act.ShortRepoPath())
link.Href = act.GetRepoLink()
title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoLink(), act.GetTag(), act.ShortRepoPath())
case models.ActionDeleteBranch:
title += ctx.Tr("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
link.Href = act.GetRepoLink()
title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
case models.ActionMirrorSyncPush:
title += ctx.Tr("action.mirror_sync_push", act.GetRepoLink(), url.QueryEscape(act.GetBranch()), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
srcLink := toSrcLink(act)
if link.Href == "#" {
link.Href = srcLink
}
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
case models.ActionMirrorSyncCreate:
title += ctx.Tr("action.mirror_sync_create", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
srcLink := toSrcLink(act)
if link.Href == "#" {
link.Href = srcLink
}
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
case models.ActionMirrorSyncDelete:
title += ctx.Tr("action.mirror_sync_delete", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
link.Href = act.GetRepoLink()
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoLink(), act.GetBranch(), act.ShortRepoPath())
case models.ActionApprovePullRequest:
title += ctx.Tr("action.approve_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionRejectPullRequest:
title += ctx.Tr("action.reject_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.reject_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionCommentPull:
title += ctx.Tr("action.comment_pull", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.comment_pull", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionPublishRelease:
title += ctx.Tr("action.publish_release", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath(), act.Content)
releaseLink := toReleaseLink(act)
if link.Href == "#" {
link.Href = releaseLink
}
title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoLink(), releaseLink, act.ShortRepoPath(), act.Content)
case models.ActionPullReviewDismissed:
title += ctx.Tr("action.review_dismissed", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
case models.ActionStarRepo:
title += ctx.Tr("action.starred_repo", act.GetRepoLink(), act.GetRepoPath())
link = &feeds.Link{Href: act.GetRepoLink()}
link.Href = act.GetRepoLink()
title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoLink(), act.GetRepoPath())
case models.ActionWatchRepo:
title += ctx.Tr("action.watched_repo", act.GetRepoLink(), act.GetRepoPath())
link = &feeds.Link{Href: act.GetRepoLink()}
link.Href = act.GetRepoLink()
title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoLink(), act.GetRepoPath())
default:
return nil, fmt.Errorf("unknown action type: %v", act.OpType)
}
@ -104,7 +178,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (ite
desc += "\n\n"
}
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), commit.Sha1),
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), commit.Sha1)),
commit.Sha1,
templates.RenderCommitMessage(commit.Message, repoLink, nil),
)

View file

@ -7,6 +7,7 @@ package org
import (
"net/http"
"net/url"
"strings"
"code.gitea.io/gitea/models"
@ -76,7 +77,7 @@ func SettingsPost(ctx *context.Context) {
return
}
// reset ctx.org.OrgLink with new name
ctx.Org.OrgLink = setting.AppSubURL + "/org/" + form.Name
ctx.Org.OrgLink = setting.AppSubURL + "/org/" + url.PathEscape(form.Name)
log.Trace("Organization name changed: %s -> %s", org.Name, form.Name)
nameChanged = false
}

View file

@ -7,6 +7,7 @@ package org
import (
"net/http"
"net/url"
"path"
"strings"
@ -105,7 +106,7 @@ func TeamsAction(ctx *context.Context) {
}
ctx.JSON(http.StatusOK,
map[string]interface{}{
"redirect": ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName,
"redirect": ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName),
})
return
case "add":
@ -119,7 +120,7 @@ func TeamsAction(ctx *context.Context) {
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
} else {
ctx.ServerError(" GetUserByName", err)
}
@ -128,7 +129,7 @@ func TeamsAction(ctx *context.Context) {
if u.IsOrganization() {
ctx.Flash.Error(ctx.Tr("form.cannot_add_org_to_team"))
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
return
}
@ -156,7 +157,7 @@ func TeamsAction(ctx *context.Context) {
switch page {
case "team":
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
case "home":
ctx.Redirect(ctx.Org.Organization.HomeLink())
default:
@ -181,7 +182,7 @@ func TeamsRepoAction(ctx *context.Context) {
if err != nil {
if models.IsErrRepoNotExist(err) {
ctx.Flash.Error(ctx.Tr("org.teams.add_nonexistent_repo"))
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
return
}
ctx.ServerError("GetRepositoryByName", err)
@ -204,11 +205,11 @@ func TeamsRepoAction(ctx *context.Context) {
if action == "addall" || action == "removeall" {
ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories",
"redirect": ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories",
})
return
}
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
}
// NewTeam render create new team page
@ -273,7 +274,7 @@ func NewTeamPost(ctx *context.Context) {
return
}
log.Trace("Team created: %s/%s", ctx.Org.Organization.Name, t.Name)
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName)
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
}
// TeamMembers render team members page
@ -375,7 +376,7 @@ func EditTeamPost(ctx *context.Context) {
}
return
}
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName)
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
}
// DeleteTeam response for the delete team request

View file

@ -8,6 +8,7 @@ import (
"fmt"
gotemplate "html/template"
"net/http"
"net/url"
"strings"
"code.gitea.io/gitea/models"
@ -17,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
)
const (
@ -54,7 +56,7 @@ func RefBlame(ctx *context.Context) {
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
if len(ctx.Repo.TreePath) > 0 {
treeLink += "/" + ctx.Repo.TreePath
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
var treeNames []string
@ -85,7 +87,7 @@ func RefBlame(ctx *context.Context) {
ctx.Data["TreeNames"] = treeNames
ctx.Data["BranchLink"] = branchLink
ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath
ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
ctx.Data["PageIsViewCode"] = true
ctx.Data["IsBlame"] = true
@ -236,8 +238,8 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
br.RepoLink = repoLink
br.PartSha = part.Sha
br.PreviousSha = previousSha
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, previousSha, ctx.Repo.TreePath)
br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, part.Sha)
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(previousSha), util.PathEscapeSegments(ctx.Repo.TreePath))
br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(part.Sha))
br.CommitMessage = commit.CommitMessage
br.CommitSince = commitSince
}

View file

@ -8,7 +8,6 @@ package repo
import (
"errors"
"net/http"
"path"
"strings"
"code.gitea.io/gitea/models"
@ -323,8 +322,7 @@ func Diff(ctx *context.Context) {
return
}
}
headTarget := path.Join(userName, repoName)
setCompareContext(ctx, parentCommit, commit, headTarget)
setCompareContext(ctx, parentCommit, commit, userName, repoName)
ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
ctx.Data["Commit"] = commit
ctx.Data["Diff"] = diff

View file

@ -12,7 +12,7 @@ import (
"html"
"io"
"net/http"
"path"
"net/url"
"path/filepath"
"strings"
@ -38,7 +38,7 @@ const (
)
// setCompareContext sets context data.
func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headTarget string) {
func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headOwner, headName string) {
ctx.Data["BaseCommit"] = base
ctx.Data["HeadCommit"] = head
@ -54,22 +54,28 @@ func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit,
return blob
}
setPathsCompareContext(ctx, base, head, headTarget)
setPathsCompareContext(ctx, base, head, headOwner, headName)
setImageCompareContext(ctx)
setCsvCompareContext(ctx)
}
// setPathsCompareContext sets context data for source and raw paths
func setPathsCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headTarget string) {
sourcePath := setting.AppSubURL + "/%s/src/commit/%s"
rawPath := setting.AppSubURL + "/%s/raw/commit/%s"
// SourceCommitURL creates a relative URL for a commit in the given repository
func SourceCommitURL(owner, name string, commit *git.Commit) string {
return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/src/commit/" + url.PathEscape(commit.ID.String())
}
ctx.Data["SourcePath"] = fmt.Sprintf(sourcePath, headTarget, head.ID)
ctx.Data["RawPath"] = fmt.Sprintf(rawPath, headTarget, head.ID)
// RawCommitURL creates a relative URL for the raw commit in the given repository
func RawCommitURL(owner, name string, commit *git.Commit) string {
return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/raw/commit/" + url.PathEscape(commit.ID.String())
}
// setPathsCompareContext sets context data for source and raw paths
func setPathsCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headOwner, headName string) {
ctx.Data["SourcePath"] = SourceCommitURL(headOwner, headName, head)
ctx.Data["RawPath"] = RawCommitURL(headOwner, headName, head)
if base != nil {
baseTarget := path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
ctx.Data["BeforeSourcePath"] = fmt.Sprintf(sourcePath, baseTarget, base.ID)
ctx.Data["BeforeRawPath"] = fmt.Sprintf(rawPath, baseTarget, base.ID)
ctx.Data["BeforeSourcePath"] = SourceCommitURL(headOwner, headName, head)
ctx.Data["BeforeRawPath"] = RawCommitURL(headOwner, headName, head)
}
}
@ -619,8 +625,7 @@ func PrepareCompareDiff(
ctx.Data["Username"] = ci.HeadUser.Name
ctx.Data["Reponame"] = ci.HeadRepo.Name
headTarget := path.Join(ci.HeadUser.Name, repo.Name)
setCompareContext(ctx, baseCommit, headCommit, headTarget)
setCompareContext(ctx, baseCommit, headCommit, ci.HeadUser.Name, repo.Name)
return false
}

View file

@ -204,7 +204,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
ctx.Data["TreePath"] = form.TreePath
ctx.Data["TreeNames"] = treeNames
ctx.Data["TreePaths"] = treePaths
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + ctx.Repo.BranchName
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
ctx.Data["FileContent"] = form.Content
ctx.Data["commit_summary"] = form.CommitSummary
ctx.Data["commit_message"] = form.CommitMessage
@ -299,9 +299,9 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
ctx.Error(http.StatusInternalServerError, err.Error())
}
} else if models.IsErrCommitIDDoesNotMatch(err) {
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplEditFile, &form)
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplEditFile, &form)
} else if git.IsErrPushOutOfDate(err) {
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
} else if git.IsErrPushRejected(err) {
errPushRej := err.(*git.ErrPushRejected)
if len(errPushRej.Message) == 0 {
@ -495,7 +495,7 @@ func DeleteFilePost(ctx *context.Context) {
ctx.Error(http.StatusInternalServerError, err.Error())
}
} else if models.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplDeleteFile, &form)
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
} else if git.IsErrPushRejected(err) {
errPushRej := err.(*git.ErrPushRejected)
if len(errPushRej.Message) == 0 {
@ -602,7 +602,7 @@ func UploadFilePost(ctx *context.Context) {
ctx.Data["TreePath"] = form.TreePath
ctx.Data["TreeNames"] = treeNames
ctx.Data["TreePaths"] = treePaths
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + branchName
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName)
ctx.Data["commit_summary"] = form.CommitSummary
ctx.Data["commit_message"] = form.CommitMessage
ctx.Data["commit_choice"] = form.CommitChoice
@ -698,7 +698,7 @@ func UploadFilePost(ctx *context.Context) {
branchErr := err.(models.ErrBranchAlreadyExists)
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplUploadFile, &form)
} else if git.IsErrPushOutOfDate(err) {
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+ctx.Repo.CommitID+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(ctx.Repo.CommitID)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
} else if git.IsErrPushRejected(err) {
errPushRej := err.(*git.ErrPushRejected)
if len(errPushRej.Message) == 0 {

View file

@ -11,6 +11,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"path"
"strconv"
"strings"
@ -106,7 +107,7 @@ func MustAllowPulls(ctx *context.Context) {
// User can send pull request if owns a forked repository.
if ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID) {
ctx.Repo.PullRequest.Allowed = true
ctx.Repo.PullRequest.HeadInfo = ctx.User.Name + ":" + ctx.Repo.BranchName
ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.User.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName)
}
}
@ -764,7 +765,7 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleDirs [
for _, repoLabel := range repoLabels {
if strings.EqualFold(repoLabel.Name, metaLabel) {
repoLabel.IsChecked = true
labelIDs = append(labelIDs, fmt.Sprintf("%d", repoLabel.ID))
labelIDs = append(labelIDs, strconv.FormatInt(repoLabel.ID, 10))
break
}
}
@ -983,6 +984,7 @@ func NewIssuePost(ctx *context.Context) {
issue := &models.Issue{
RepoID: repo.ID,
Repo: repo,
Title: form.Title,
PosterID: ctx.User.ID,
Poster: ctx.User,
@ -1009,9 +1011,9 @@ func NewIssuePost(ctx *context.Context) {
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
if ctx.FormString("redirect_after_creation") == "project" {
ctx.Redirect(ctx.Repo.RepoLink + "/projects/" + fmt.Sprint(form.ProjectID))
ctx.Redirect(ctx.Repo.RepoLink + "/projects/" + strconv.FormatInt(form.ProjectID, 10))
} else {
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
}
}
@ -1097,13 +1099,16 @@ func ViewIssue(ctx *context.Context) {
}
return
}
if issue.Repo == nil {
issue.Repo = ctx.Repo.Repository
}
// Make sure type and URL matches.
if ctx.Params(":type") == "issues" && issue.IsPull {
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
return
} else if ctx.Params(":type") == "pulls" && !issue.IsPull {
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
return
}
@ -1496,7 +1501,7 @@ func ViewIssue(ctx *context.Context) {
log.Error("IsProtectedBranch: %v", err)
} else if !protected {
canDelete = true
ctx.Data["DeleteBranchLink"] = ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index) + "/cleanup"
ctx.Data["DeleteBranchLink"] = issue.Link() + "/cleanup"
}
}
}
@ -1624,7 +1629,7 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["NumParticipants"] = len(participants)
ctx.Data["Issue"] = issue
ctx.Data["ReadOnly"] = false
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + ctx.Data["Link"].(string)
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + url.QueryEscape(ctx.Data["Link"].(string))
ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID)
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
ctx.Data["HasProjectsWritePermission"] = ctx.Repo.CanWrite(unit.TypeProjects)
@ -1773,7 +1778,7 @@ func UpdateIssueContent(ctx *context.Context) {
}
content, err := markdown.RenderString(&markup.RenderContext{
URLPrefix: ctx.FormString("context"),
URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
Ctx: ctx,
@ -2205,7 +2210,7 @@ func UpdateCommentContent(ctx *context.Context) {
}
content, err := markdown.RenderString(&markup.RenderContext{
URLPrefix: ctx.FormString("context"),
URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
Ctx: ctx,

View file

@ -94,6 +94,7 @@ func GetActiveStopwatch(c *context.Context) {
}
c.Data["ActiveStopwatch"] = StopwatchTmplInfo{
issue.Link(),
issue.Repo.FullName(),
issue.Index,
sw.Seconds() + 1, // ensure time is never zero in ui
@ -102,6 +103,7 @@ func GetActiveStopwatch(c *context.Context) {
// StopwatchTmplInfo is a view on a stopwatch specifically for template rendering
type StopwatchTmplInfo struct {
IssueLink string
RepoSlug string
IssueIndex int64
Seconds int64

View file

@ -10,6 +10,7 @@ import (
gotemplate "html/template"
"io"
"net/http"
"net/url"
"path"
"strconv"
"strings"
@ -285,7 +286,7 @@ func LFSFileGet(ctx *context.Context) {
fileSize := meta.Size
ctx.Data["FileSize"] = meta.Size
ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s.git/info/lfs/objects/%s/%s", setting.AppURL, ctx.Repo.Repository.FullName(), meta.Oid, "direct")
ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s/%s.git/info/lfs/objects/%s/%s", setting.AppURL, url.PathEscape(ctx.Repo.Repository.OwnerName), url.PathEscape(ctx.Repo.Repository.Name), url.PathEscape(meta.Oid), "direct")
switch {
case isRepresentableAsText:
if st.IsSvgImage() {

View file

@ -7,6 +7,7 @@ package repo
import (
"net/http"
"net/url"
"strings"
"code.gitea.io/gitea/models"
@ -237,7 +238,7 @@ func MigratePost(ctx *context.Context) {
err = task.MigrateRepository(ctx.User, ctxUser, opts)
if err == nil {
ctx.Redirect(ctxUser.HomeLink() + "/" + opts.RepoName)
ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(opts.RepoName))
return
}

View file

@ -6,6 +6,7 @@ package repo
import (
"net/http"
"net/url"
"time"
"code.gitea.io/gitea/models"
@ -244,7 +245,7 @@ func ChangeMilestoneStatus(ctx *context.Context) {
}
return
}
ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=" + ctx.Params(":action"))
ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=" + url.QueryEscape(ctx.Params(":action")))
}
// DeleteMilestone delete a milestone

View file

@ -7,6 +7,7 @@ package repo
import (
"fmt"
"net/http"
"net/url"
"strings"
"code.gitea.io/gitea/models"
@ -173,7 +174,7 @@ func ChangeProjectStatus(ctx *context.Context) {
}
return
}
ctx.Redirect(ctx.Repo.RepoLink + "/projects?state=" + ctx.Params(":action"))
ctx.Redirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action")))
}
// DeleteProject delete a project

View file

@ -10,8 +10,10 @@ import (
"crypto/subtle"
"errors"
"fmt"
"html"
"net/http"
"path"
"net/url"
"strconv"
"strings"
"time"
@ -34,7 +36,6 @@ import (
"code.gitea.io/gitea/services/gitdiff"
pull_service "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
"github.com/unknwon/com"
)
const (
@ -109,8 +110,7 @@ func getForkRepository(ctx *context.Context) *models.Repository {
ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate
canForkToUser := forkRepo.OwnerID != ctx.User.ID && !ctx.User.HasForkedRepo(forkRepo.ID)
ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name
ctx.Data["ForkFromOwnerID"] = forkRepo.Owner.ID
ctx.Data["ForkRepo"] = forkRepo
if err := ctx.User.GetOwnedOrganizations(); err != nil {
ctx.ServerError("GetOwnedOrganizations", err)
@ -202,7 +202,7 @@ func ForkPost(ctx *context.Context) {
}
repo, has := models.HasForkedRepo(ctxUser.ID, traverseParentRepo.ID)
if has {
ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
return
}
if !traverseParentRepo.IsFork {
@ -248,7 +248,7 @@ func ForkPost(ctx *context.Context) {
}
log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name)
ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
}
func checkPullInfo(ctx *context.Context) *models.Issue {
@ -682,8 +682,7 @@ func ViewPullFiles(ctx *context.Context) {
}
}
headTarget := path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
setCompareContext(ctx, baseCommit, commit, headTarget)
setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
ctx.Data["RequireHighlightJS"] = true
ctx.Data["RequireSimpleMDE"] = true
@ -746,7 +745,7 @@ func UpdatePullRequest(ctx *context.Context) {
// ToDo: add check if maintainers are allowed to change branch ... (need migration & co)
if (!allowedUpdateByMerge && !rebase) || (rebase && !allowedUpdateByRebase) {
ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
return
}
@ -766,7 +765,7 @@ func UpdatePullRequest(ctx *context.Context) {
return
}
ctx.Flash.Error(flashError)
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
return
} else if models.IsErrRebaseConflicts(err) {
conflictError := err.(models.ErrRebaseConflicts)
@ -780,19 +779,19 @@ func UpdatePullRequest(ctx *context.Context) {
return
}
ctx.Flash.Error(flashError)
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
return
}
ctx.Flash.Error(err.Error())
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
return
}
time.Sleep(1 * time.Second)
ctx.Flash.Success(ctx.Tr("repo.pulls.update_branch_success"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
}
// MergePullRequest response for merging pull request
@ -805,11 +804,11 @@ func MergePullRequest(ctx *context.Context) {
if issue.IsClosed {
if issue.IsPull {
ctx.Flash.Error(ctx.Tr("repo.pulls.is_closed"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
return
}
ctx.Flash.Error(ctx.Tr("repo.issues.closed_title"))
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
return
}
@ -822,13 +821,13 @@ func MergePullRequest(ctx *context.Context) {
}
if !allowedMerge {
ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
ctx.Redirect(issue.Link())
return
}
if pr.HasMerged {
ctx.Flash.Error(ctx.Tr("repo.pulls.has_merged"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
ctx.Redirect(issue.Link())
return
}
@ -837,11 +836,11 @@ func MergePullRequest(ctx *context.Context) {
if err = pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
if models.IsErrInvalidMergeStyle(err) {
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
ctx.Redirect(issue.Link())
return
} else if strings.Contains(err.Error(), "Wrong commit ID") {
ctx.Flash.Error(ctx.Tr("repo.pulls.wrong_commit_id"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
ctx.Redirect(issue.Link())
return
}
@ -849,19 +848,19 @@ func MergePullRequest(ctx *context.Context) {
return
}
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
ctx.Redirect(issue.Link())
return
}
if !pr.CanAutoMerge() {
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
ctx.Redirect(issue.Link())
return
}
if pr.IsWorkInProgress() {
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_wip"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
}
@ -875,14 +874,14 @@ func MergePullRequest(ctx *context.Context) {
return
} else if !isRepoAdmin {
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
}
}
if ctx.HasError() {
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
}
@ -914,14 +913,14 @@ func MergePullRequest(ctx *context.Context) {
if !noDeps {
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
}
if err = pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
if models.IsErrInvalidMergeStyle(err) {
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
} else if models.IsErrMergeConflicts(err) {
conflictError := err.(models.ErrMergeConflicts)
@ -935,7 +934,7 @@ func MergePullRequest(ctx *context.Context) {
return
}
ctx.Flash.Error(flashError)
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
} else if models.IsErrRebaseConflicts(err) {
conflictError := err.(models.ErrRebaseConflicts)
@ -949,17 +948,17 @@ func MergePullRequest(ctx *context.Context) {
return
}
ctx.Flash.Error(flashError)
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
} else if models.IsErrMergeUnrelatedHistories(err) {
log.Debug("MergeUnrelatedHistories error: %v", err)
ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
} else if git.IsErrPushOutOfDate(err) {
log.Debug("MergePushOutOfDate error: %v", err)
ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date"))
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
} else if git.IsErrPushRejected(err) {
log.Debug("MergePushRejected error: %v", err)
@ -979,7 +978,7 @@ func MergePullRequest(ctx *context.Context) {
}
ctx.Flash.Error(flashError)
}
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
return
}
ctx.ServerError("Merge", err)
@ -1008,7 +1007,7 @@ func MergePullRequest(ctx *context.Context) {
deleteBranch(ctx, pr, headRepo)
}
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
ctx.Redirect(issue.Link())
}
func stopTimerIfAvailable(user *models.User, issue *models.Issue) error {
@ -1097,6 +1096,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
pullIssue := &models.Issue{
RepoID: repo.ID,
Repo: repo,
Title: form.Title,
PosterID: ctx.User.ID,
Poster: ctx.User,
@ -1138,7 +1138,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
}
ctx.Flash.Error(flashError)
}
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pullIssue.Index))
ctx.Redirect(pullIssue.Link())
return
}
ctx.ServerError("NewPullRequest", err)
@ -1146,7 +1146,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
}
log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pullIssue.Index))
ctx.Redirect(pullIssue.Link())
}
// TriggerTask response for a trigger task request
@ -1261,7 +1261,7 @@ func CleanUpPullRequest(ctx *context.Context) {
defer func() {
ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": pr.BaseRepo.Link() + "/pulls/" + fmt.Sprint(issue.Index),
"redirect": issue.Link(),
})
}()
@ -1369,7 +1369,7 @@ func UpdatePullRequestTarget(ctx *context.Context) {
err := err.(models.ErrPullRequestAlreadyExists)
RepoRelPath := ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
errorMessage := ctx.Tr("repo.pulls.has_pull_request", ctx.Repo.RepoLink, RepoRelPath, err.IssueID)
errorMessage := ctx.Tr("repo.pulls.has_pull_request", html.EscapeString(ctx.Repo.RepoLink+"/pulls/"+strconv.FormatInt(err.IssueID, 10)), html.EscapeString(RepoRelPath), err.IssueID) // FIXME: Creates url insidde locale string
ctx.Flash.Error(errorMessage)
ctx.JSON(http.StatusConflict, map[string]interface{}{

View file

@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
releaseservice "code.gitea.io/gitea/services/release"
@ -350,7 +351,7 @@ func NewReleasePost(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + form.TagName)
ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
return
}

View file

@ -244,7 +244,7 @@ func CreatePost(ctx *context.Context) {
repo, err = repo_service.GenerateRepository(ctx.User, ctxUser, templateRepo, opts)
if err == nil {
log.Trace("Repository generated [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
ctx.Redirect(repo.Link())
return
}
} else {
@ -263,7 +263,7 @@ func CreatePost(ctx *context.Context) {
})
if err == nil {
log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
ctx.Redirect(repo.Link())
return
}
}

View file

@ -615,7 +615,7 @@ func SettingsPost(ctx *context.Context) {
log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName()))
ctx.Redirect(ctx.Repo.Owner.HomeLink() + "/" + repo.Name + "/settings")
ctx.Redirect(repo.Link() + "/settings")
case "cancel_transfer":
if !ctx.Repo.IsOwner() {
@ -627,7 +627,7 @@ func SettingsPost(ctx *context.Context) {
if err != nil {
if models.IsErrNoPendingTransfer(err) {
ctx.Flash.Error("repo.settings.transfer_abort_invalid")
ctx.Redirect(ctx.User.HomeLink() + "/" + repo.Name + "/settings")
ctx.Redirect(repo.Link() + "/settings")
} else {
ctx.ServerError("GetPendingRepositoryTransfer", err)
}
@ -647,7 +647,7 @@ func SettingsPost(ctx *context.Context) {
log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name)
ctx.Flash.Success(ctx.Tr("repo.settings.transfer_abort_success", repoTransfer.Recipient.Name))
ctx.Redirect(ctx.Repo.Owner.HomeLink() + "/" + repo.Name + "/settings")
ctx.Redirect(repo.Link() + "/settings")
case "delete":
if !ctx.Repo.IsOwner() {
@ -796,7 +796,7 @@ func Collaboration(ctx *context.Context) {
func CollaborationPost(ctx *context.Context) {
name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("collaborator")))
if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
return
}
@ -804,7 +804,7 @@ func CollaborationPost(ctx *context.Context) {
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
} else {
ctx.ServerError("GetUserByName", err)
}
@ -813,14 +813,14 @@ func CollaborationPost(ctx *context.Context) {
if !u.IsActive {
ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_inactive_user"))
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
return
}
// Organization is not allowed to be added as a collaborator.
if u.IsOrganization() {
ctx.Flash.Error(ctx.Tr("repo.settings.org_not_allowed_to_be_collaborator"))
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
return
}
@ -840,7 +840,7 @@ func CollaborationPost(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
}
// ChangeCollaborationAccessMode response for changing access of a collaboration

View file

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
pull_service "code.gitea.io/gitea/services/pull"
@ -89,7 +90,7 @@ func ProtectedBranchPost(ctx *context.Context) {
log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
default:
ctx.NotFound("", nil)
}
@ -197,7 +198,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
}
if f.RequiredApprovals < 0 {
ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min"))
ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch))
ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch)))
}
var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64
@ -274,7 +275,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.update_protect_branch_success", branch))
ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch))
ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch)))
} else {
if protectBranch != nil {
if err := ctx.Repo.Repository.DeleteProtectedBranch(protectBranch.ID); err != nil {

View file

@ -58,7 +58,7 @@ func NewProtectedTagPost(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
}
// EditProtectedTag render the page to edit a protect tag

View file

@ -232,7 +232,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
}
if readmeFile != nil {
readmeFile.name = entry.Name() + "/" + readmeFile.name
readmeTreelink = treeLink + "/" + entry.GetSubJumpablePathName()
readmeTreelink = treeLink + "/" + util.PathEscapeSegments(entry.GetSubJumpablePathName())
break
}
}
@ -301,7 +301,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
fileSize = meta.Size
ctx.Data["FileSize"] = meta.Size
filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.name))
ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s.git/info/lfs/objects/%s/%s", setting.AppURL, ctx.Repo.Repository.FullName(), meta.Oid, filenameBase64)
ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.HTMLURL(), url.PathEscape(meta.Oid), url.PathEscape(filenameBase64))
}
}
}
@ -376,7 +376,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
fileSize := blob.Size()
ctx.Data["FileIsSymlink"] = entry.IsLink()
ctx.Data["FileName"] = blob.Name()
ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath
ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
buf := make([]byte, 1024)
n, _ := util.ReadAtMost(dataRc, buf)
@ -422,7 +422,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
isTextFile = st.IsText()
fileSize = meta.Size
ctx.Data["RawFileLink"] = fmt.Sprintf("%s/media/%s/%s", ctx.Repo.RepoLink, ctx.Repo.BranchNameSubURL(), ctx.Repo.TreePath)
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
}
}
@ -628,7 +628,7 @@ func checkHomeCodeViewable(ctx *context.Context) {
}
if firstUnit != nil {
ctx.Redirect(fmt.Sprintf("%s/%s%s", setting.AppSubURL, ctx.Repo.Repository.FullName(), firstUnit.URI))
ctx.Redirect(fmt.Sprintf("%s%s", ctx.Repo.Repository.Link(), firstUnit.URI))
return
}
}
@ -684,7 +684,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
return nil
}
ctx.Data["LastCommitLoaderURL"] = ctx.Repo.RepoLink + "/lastcommit/" + ctx.Repo.CommitID + "/" + ctx.Repo.TreePath
ctx.Data["LastCommitLoaderURL"] = ctx.Repo.RepoLink + "/lastcommit/" + url.PathEscape(ctx.Repo.CommitID) + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
// Get current entry user currently looking at.
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
@ -766,7 +766,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
treeLink := branchLink
if len(ctx.Repo.TreePath) > 0 {
treeLink += "/" + ctx.Repo.TreePath
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
ctx.Data["TreeLink"] = treeLink
@ -815,7 +815,7 @@ func renderCode(ctx *context.Context) {
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
if len(ctx.Repo.TreePath) > 0 {
treeLink += "/" + ctx.Repo.TreePath
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
// Get Topics of this repo

View file

@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"net/http"
"net/url"
"path"
"strings"
@ -414,7 +415,7 @@ func TelegramHooksNewPost(ctx *context.Context) {
w := &webhook.Webhook{
RepoID: orCtx.RepoID,
URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID),
URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID)),
ContentType: webhook.ContentTypeJSON,
HookEvent: ParseHookEvent(form.WebhookForm),
IsActive: form.Active,
@ -468,7 +469,7 @@ func MatrixHooksNewPost(ctx *context.Context) {
w := &webhook.Webhook{
RepoID: orCtx.RepoID,
URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID),
URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
ContentType: webhook.ContentTypeJSON,
HTTPMethod: "PUT",
HookEvent: ParseHookEvent(form.WebhookForm),
@ -976,7 +977,7 @@ func TelegramHooksEditPost(ctx *context.Context) {
return
}
w.Meta = string(meta)
w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID)
w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID))
w.HookEvent = ParseHookEvent(form.WebhookForm)
w.IsActive = form.Active
if err := w.UpdateEvent(); err != nil {
@ -1020,7 +1021,7 @@ func MatrixHooksEditPost(ctx *context.Context) {
return
}
w.Meta = string(meta)
w.URL = fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID)
w.URL = fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID))
w.HookEvent = ParseHookEvent(form.WebhookForm)
w.IsActive = form.Active
@ -1162,7 +1163,7 @@ func TestWebhook(ctx *context.Context) {
apiCommit := &api.PayloadCommit{
ID: commit.ID.String(),
Message: commit.Message(),
URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()),
Author: &api.PayloadUser{
Name: commit.Author.Name,
Email: commit.Author.Email,

View file

@ -180,7 +180,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
ctx.Data["Pages"] = pages
// get requested pagename
pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
if len(pageName) == 0 {
pageName = "Home"
}
@ -193,7 +193,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
//lookup filename in wiki - get filecontent, gitTree entry , real filename
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
if noEntry {
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
}
if entry == nil || ctx.Written() {
if wikiRepo != nil {
@ -276,7 +276,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
}
// get requested pagename
pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
if len(pageName) == 0 {
pageName = "Home"
}
@ -291,7 +291,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
//lookup filename in wiki - get filecontent, gitTree entry , real filename
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
if noEntry {
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
}
if entry == nil || ctx.Written() {
if wikiRepo != nil {
@ -352,7 +352,7 @@ func renderEditPage(ctx *context.Context) {
}()
// get requested pagename
pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
if len(pageName) == 0 {
pageName = "Home"
}
@ -365,7 +365,7 @@ func renderEditPage(ctx *context.Context) {
//lookup filename in wiki - get filecontent, gitTree entry , real filename
data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
if noEntry {
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
}
if entry == nil || ctx.Written() {
return
@ -378,6 +378,32 @@ func renderEditPage(ctx *context.Context) {
ctx.Data["footerContent"] = ""
}
// WikiPost renders post of wiki page
func WikiPost(ctx *context.Context) {
switch ctx.FormString("action") {
case "_new":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return
}
NewWikiPost(ctx)
return
case "_delete":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return
}
DeleteWikiPagePost(ctx)
return
}
if !ctx.Repo.CanWrite(unit.TypeWiki) {
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return
}
EditWikiPost(ctx)
}
// Wiki renders single wiki page
func Wiki(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true
@ -389,6 +415,29 @@ func Wiki(ctx *context.Context) {
return
}
switch ctx.FormString("action") {
case "_pages":
WikiPages(ctx)
return
case "_revision":
WikiRevision(ctx)
return
case "_edit":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return
}
EditWiki(ctx)
return
case "_new":
if !ctx.Repo.CanWrite(unit.TypeWiki) {
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return
}
NewWiki(ctx)
return
}
wikiRepo, entry := renderViewPage(ctx)
defer func() {
if wikiRepo != nil {
@ -652,7 +701,7 @@ func EditWikiPost(ctx *context.Context) {
return
}
oldWikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
oldWikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
newWikiName := wiki_service.NormalizeWikiName(form.Title)
if len(form.Message) == 0 {
@ -669,7 +718,7 @@ func EditWikiPost(ctx *context.Context) {
// DeleteWikiPagePost delete wiki page
func DeleteWikiPagePost(ctx *context.Context) {
wikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
wikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
if len(wikiName) == 0 {
wikiName = "Home"
}

View file

@ -76,8 +76,8 @@ func assertPagesMetas(t *testing.T, expectedNames []string, metas interface{}) {
func TestWiki(t *testing.T) {
unittest.PrepareTestEnv(t)
ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
ctx.SetParams(":page", "Home")
ctx := test.MockContext(t, "user2/repo1/wiki/?action=_pages")
ctx.SetParams("*", "Home")
test.LoadRepo(t, ctx, 1)
Wiki(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
@ -88,7 +88,7 @@ func TestWiki(t *testing.T) {
func TestWikiPages(t *testing.T) {
unittest.PrepareTestEnv(t)
ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
ctx := test.MockContext(t, "user2/repo1/wiki/?action=_pages")
test.LoadRepo(t, ctx, 1)
WikiPages(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
@ -98,7 +98,7 @@ func TestWikiPages(t *testing.T) {
func TestNewWiki(t *testing.T) {
unittest.PrepareTestEnv(t)
ctx := test.MockContext(t, "user2/repo1/wiki/_new")
ctx := test.MockContext(t, "user2/repo1/wiki/?action=_new")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
NewWiki(ctx)
@ -113,7 +113,7 @@ func TestNewWikiPost(t *testing.T) {
} {
unittest.PrepareTestEnv(t)
ctx := test.MockContext(t, "user2/repo1/wiki/_new")
ctx := test.MockContext(t, "user2/repo1/wiki/?action=_new")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
web.SetForm(ctx, &forms.NewWikiForm{
@ -131,7 +131,7 @@ func TestNewWikiPost(t *testing.T) {
func TestNewWikiPost_ReservedName(t *testing.T) {
unittest.PrepareTestEnv(t)
ctx := test.MockContext(t, "user2/repo1/wiki/_new")
ctx := test.MockContext(t, "user2/repo1/wiki/?action=_new")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
web.SetForm(ctx, &forms.NewWikiForm{
@ -148,8 +148,8 @@ func TestNewWikiPost_ReservedName(t *testing.T) {
func TestEditWiki(t *testing.T) {
unittest.PrepareTestEnv(t)
ctx := test.MockContext(t, "user2/repo1/wiki/_edit/Home")
ctx.SetParams(":page", "Home")
ctx := test.MockContext(t, "user2/repo1/wiki/Home?action=_edit")
ctx.SetParams("*", "Home")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
EditWiki(ctx)
@ -164,8 +164,8 @@ func TestEditWikiPost(t *testing.T) {
"New/<page>",
} {
unittest.PrepareTestEnv(t)
ctx := test.MockContext(t, "user2/repo1/wiki/_new/Home")
ctx.SetParams(":page", "Home")
ctx := test.MockContext(t, "user2/repo1/wiki/Home?action=_new")
ctx.SetParams("*", "Home")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
web.SetForm(ctx, &forms.NewWikiForm{
@ -186,7 +186,7 @@ func TestEditWikiPost(t *testing.T) {
func TestDeleteWikiPagePost(t *testing.T) {
unittest.PrepareTestEnv(t)
ctx := test.MockContext(t, "user2/repo1/wiki/Home/delete")
ctx := test.MockContext(t, "user2/repo1/wiki/Home?action=_delete")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
DeleteWikiPagePost(ctx)

View file

@ -887,5 +887,5 @@ func Email2User(ctx *context.Context) {
}
return
}
ctx.Redirect(setting.AppSubURL + "/user/" + u.Name)
ctx.Redirect(u.HomeLink())
}

View file

@ -167,7 +167,7 @@ func NotificationStatusPost(c *context.Context) {
}
if !c.FormBool("noredirect") {
url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, c.FormString("page"))
url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, url.QueryEscape(c.FormString("page")))
c.Redirect(url, http.StatusSeeOther)
}
@ -189,6 +189,5 @@ func NotificationPurgePost(c *context.Context) {
return
}
url := fmt.Sprintf("%s/notifications", setting.AppSubURL)
c.Redirect(url, http.StatusSeeOther)
c.Redirect(setting.AppSubURL+"/notifications", http.StatusSeeOther)
}

View file

@ -454,7 +454,7 @@ func AuthorizeOAuth(ctx *context.Context) {
ctx.Data["State"] = form.State
ctx.Data["Scope"] = form.Scope
ctx.Data["Nonce"] = form.Nonce
ctx.Data["ApplicationUserLink"] = "<a href=\"" + html.EscapeString(setting.AppURL) + html.EscapeString(url.PathEscape(user.LowerName)) + "\">@" + html.EscapeString(user.Name) + "</a>"
ctx.Data["ApplicationUserLink"] = "<a href=\"" + html.EscapeString(user.HTMLURL()) + "\">@" + html.EscapeString(user.Name) + "</a>"
ctx.Data["ApplicationRedirectDomainHTML"] = "<strong>" + html.EscapeString(form.RedirectURI) + "</strong>"
// TODO document SESSION <=> FORM
err = ctx.Session.Set("client_id", app.ClientID)

View file

@ -364,6 +364,6 @@ func Action(ctx *context.Context) {
ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
return
}
// FIXME: We should check this URL and make sure that it's a valid Gitea URL
ctx.RedirectToFirst(ctx.FormString("redirect_to"), u.HomeLink())
}

View file

@ -895,21 +895,23 @@ func RegisterRoutes(m *web.Route) {
}, reqRepoProjectsReader, repo.MustEnableProjects)
m.Group("/wiki", func() {
m.Get("/", repo.Wiki)
m.Get("/{page}", repo.Wiki)
m.Get("/_pages", repo.WikiPages)
m.Get("/{page}/_revision", repo.WikiRevision)
m.Combo("/").
Get(repo.Wiki).
Post(context.RepoMustNotBeArchived(),
reqSignIn,
reqRepoWikiWriter,
bindIgnErr(forms.NewWikiForm{}),
repo.WikiPost)
m.Combo("/*").
Get(repo.Wiki).
Post(context.RepoMustNotBeArchived(),
reqSignIn,
reqRepoWikiWriter,
bindIgnErr(forms.NewWikiForm{}),
repo.WikiPost)
m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Get("/commit/{sha:[a-f0-9]{7,40}}.{ext:patch|diff}", repo.RawDiff)
m.Group("", func() {
m.Combo("/_new").Get(repo.NewWiki).
Post(bindIgnErr(forms.NewWikiForm{}), repo.NewWikiPost)
m.Combo("/{page}/_edit").Get(repo.EditWiki).
Post(bindIgnErr(forms.NewWikiForm{}), repo.EditWikiPost)
m.Post("/{page}/delete", repo.DeleteWikiPagePost)
}, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter)
}, repo.MustEnableWiki, context.RepoRef(), func(ctx *context.Context) {
}, repo.MustEnableWiki, func(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true
})