@@ -20,6 +20,7 @@ import (
2020 user_model "code.gitea.io/gitea/models/user"
2121 "code.gitea.io/gitea/modules/base"
2222 "code.gitea.io/gitea/modules/git"
23+ gitUrl "code.gitea.io/gitea/modules/git/url"
2324 "code.gitea.io/gitea/modules/httplib"
2425 "code.gitea.io/gitea/modules/log"
2526 "code.gitea.io/gitea/modules/markup"
@@ -635,14 +636,26 @@ type CloneLink struct {
635636}
636637
637638// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
638- func ComposeHTTPSCloneURL (owner , repo string ) string {
639- return fmt .Sprintf ("%s%s/%s.git" , setting . AppURL , url .PathEscape (owner ), url .PathEscape (repo ))
639+ func ComposeHTTPSCloneURL (ctx context. Context , owner , repo string ) string {
640+ return fmt .Sprintf ("%s%s/%s.git" , httplib . GuessCurrentAppURL ( ctx ) , url .PathEscape (owner ), url .PathEscape (repo ))
640641}
641642
642- func ComposeSSHCloneURL (ownerName , repoName string ) string {
643+ func ComposeSSHCloneURL (doer * user_model. User , ownerName , repoName string ) string {
643644 sshUser := setting .SSH .User
644645 sshDomain := setting .SSH .Domain
645646
647+ if sshUser == "(DOER_USERNAME)" {
648+ // Some users use SSH reverse-proxy and need to use the current signed-in username as the SSH user
649+ // to make the SSH reverse-proxy could prepare the user's public keys ahead.
650+ // For most cases we have the correct "doer", then use it as the SSH user.
651+ // If we can't get the doer, then use the built-in SSH user.
652+ if doer != nil {
653+ sshUser = doer .Name
654+ } else {
655+ sshUser = setting .SSH .BuiltinServerUser
656+ }
657+ }
658+
646659 // non-standard port, it must use full URI
647660 if setting .SSH .Port != 22 {
648661 sshHost := net .JoinHostPort (sshDomain , strconv .Itoa (setting .SSH .Port ))
@@ -660,21 +673,20 @@ func ComposeSSHCloneURL(ownerName, repoName string) string {
660673 return fmt .Sprintf ("%s@%s:%s/%s.git" , sshUser , sshHost , url .PathEscape (ownerName ), url .PathEscape (repoName ))
661674}
662675
663- func (repo * Repository ) cloneLink (isWiki bool ) * CloneLink {
664- repoName := repo .Name
665- if isWiki {
666- repoName += ".wiki"
667- }
668-
676+ func (repo * Repository ) cloneLink (ctx context.Context , doer * user_model.User , repoPathName string ) * CloneLink {
669677 cl := new (CloneLink )
670- cl .SSH = ComposeSSHCloneURL (repo .OwnerName , repoName )
671- cl .HTTPS = ComposeHTTPSCloneURL (repo .OwnerName , repoName )
678+ cl .SSH = ComposeSSHCloneURL (doer , repo .OwnerName , repoPathName )
679+ cl .HTTPS = ComposeHTTPSCloneURL (ctx , repo .OwnerName , repoPathName )
672680 return cl
673681}
674682
675683// CloneLink returns clone URLs of repository.
676- func (repo * Repository ) CloneLink () (cl * CloneLink ) {
677- return repo .cloneLink (false )
684+ func (repo * Repository ) CloneLink (ctx context.Context , doer * user_model.User ) (cl * CloneLink ) {
685+ return repo .cloneLink (ctx , doer , repo .Name )
686+ }
687+
688+ func (repo * Repository ) CloneLinkGeneral (ctx context.Context ) (cl * CloneLink ) {
689+ return repo .cloneLink (ctx , nil /* no doer, use a general git user */ , repo .Name )
678690}
679691
680692// GetOriginalURLHostname returns the hostname of a URL or the URL
@@ -770,47 +782,66 @@ func GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*Repo
770782 return & repo , err
771783}
772784
773- // getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url
774- func getRepositoryURLPathSegments (repoURL string ) []string {
775- if strings .HasPrefix (repoURL , setting .AppURL ) {
776- return strings .Split (strings .TrimPrefix (repoURL , setting .AppURL ), "/" )
785+ func parseRepositoryURL (ctx context.Context , repoURL string ) (ret struct {
786+ OwnerName , RepoName , RemainingPath string
787+ },
788+ ) {
789+ // possible urls for git:
790+ // https://my.domain/sub-path/<owner>/<repo>[.git]
791+ // git+ssh://[email protected] /<owner>/<repo>[.git] 792+ // ssh://[email protected] /<owner>/<repo>[.git] 793+ // [email protected] :<owner>/<repo>[.git] 794+
795+ fillPathParts := func (s string ) {
796+ s = strings .TrimPrefix (s , "/" )
797+ fields := strings .SplitN (s , "/" , 3 )
798+ if len (fields ) >= 2 {
799+ ret .OwnerName = fields [0 ]
800+ ret .RepoName = strings .TrimSuffix (fields [1 ], ".git" )
801+ if len (fields ) == 3 {
802+ ret .RemainingPath = "/" + fields [2 ]
803+ }
804+ }
777805 }
778806
779- sshURLVariants := [4 ]string {
780- setting .SSH .Domain + ":" ,
781- setting .SSH .User + "@" + setting .SSH .Domain + ":" ,
782- "git+ssh://" + setting .SSH .Domain + "/" ,
783- "git+ssh://" + setting .SSH .User + "@" + setting .SSH .Domain + "/" ,
807+ parsed , err := gitUrl .ParseGitURL (repoURL )
808+ if err != nil {
809+ return ret
784810 }
785-
786- for _ , sshURL := range sshURLVariants {
787- if strings .HasPrefix (repoURL , sshURL ) {
788- return strings .Split (strings .TrimPrefix (repoURL , sshURL ), "/" )
811+ if parsed .URL .Scheme == "http" || parsed .URL .Scheme == "https" {
812+ if ! httplib .IsCurrentGiteaSiteURL (ctx , repoURL ) {
813+ return ret
814+ }
815+ fillPathParts (strings .TrimPrefix (parsed .URL .Path , setting .AppSubURL ))
816+ return ret
817+ }
818+ if parsed .URL .Scheme == "ssh" || parsed .URL .Scheme == "git+ssh" {
819+ domainApp := setting .Domain
820+ domainCur := httplib .GuessCurrentHostDomain (ctx )
821+ urlDomain , _ , _ := net .SplitHostPort (parsed .URL .Host )
822+ urlDomain = util .IfZero (urlDomain , parsed .URL .Host )
823+ if urlDomain == "" {
824+ return ret
789825 }
826+ // check whether URL domain is the App domain
827+ domainMatches := domainApp == urlDomain
828+ // check whether URL domain is current domain from context
829+ domainMatches = domainMatches || (domainCur != "" && domainCur == urlDomain )
830+ if domainMatches {
831+ fillPathParts (parsed .URL .Path )
832+ }
833+ return ret
790834 }
791-
792- return nil
835+ return ret
793836}
794837
795838// GetRepositoryByURL returns the repository by given url
796839func GetRepositoryByURL (ctx context.Context , repoURL string ) (* Repository , error ) {
797- // possible urls for git:
798- // https://my.domain/sub-path/<owner>/<repo>.git
799- // https://my.domain/sub-path/<owner>/<repo>
800- // git+ssh://[email protected] /<owner>/<repo>.git 801- // git+ssh://[email protected] /<owner>/<repo> 802- // [email protected] :<owner>/<repo>.git 803- // [email protected] :<owner>/<repo> 804-
805- pathSegments := getRepositoryURLPathSegments (repoURL )
806-
807- if len (pathSegments ) != 2 {
840+ ret := parseRepositoryURL (ctx , repoURL )
841+ if ret .OwnerName == "" {
808842 return nil , fmt .Errorf ("unknown or malformed repository URL" )
809843 }
810-
811- ownerName := pathSegments [0 ]
812- repoName := strings .TrimSuffix (pathSegments [1 ], ".git" )
813- return GetRepositoryByOwnerAndName (ctx , ownerName , repoName )
844+ return GetRepositoryByOwnerAndName (ctx , ret .OwnerName , ret .RepoName )
814845}
815846
816847// GetRepositoryByID returns the repository by given id if exists.
0 commit comments