@@ -20,6 +20,7 @@ import (
20
20
user_model "code.gitea.io/gitea/models/user"
21
21
"code.gitea.io/gitea/modules/base"
22
22
"code.gitea.io/gitea/modules/git"
23
+ giturl "code.gitea.io/gitea/modules/git/url"
23
24
"code.gitea.io/gitea/modules/httplib"
24
25
"code.gitea.io/gitea/modules/log"
25
26
"code.gitea.io/gitea/modules/markup"
@@ -637,14 +638,26 @@ type CloneLink struct {
637
638
}
638
639
639
640
// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
640
- func ComposeHTTPSCloneURL (owner , repo string ) string {
641
- return fmt .Sprintf ("%s%s/%s.git" , setting . AppURL , url .PathEscape (owner ), url .PathEscape (repo ))
641
+ func ComposeHTTPSCloneURL (ctx context. Context , owner , repo string ) string {
642
+ return fmt .Sprintf ("%s%s/%s.git" , httplib . GuessCurrentAppURL ( ctx ) , url .PathEscape (owner ), url .PathEscape (repo ))
642
643
}
643
644
644
- func ComposeSSHCloneURL (ownerName , repoName string ) string {
645
+ func ComposeSSHCloneURL (doer * user_model. User , ownerName , repoName string ) string {
645
646
sshUser := setting .SSH .User
646
647
sshDomain := setting .SSH .Domain
647
648
649
+ if sshUser == "(DOER_USERNAME)" {
650
+ // Some users use SSH reverse-proxy and need to use the current signed-in username as the SSH user
651
+ // to make the SSH reverse-proxy could prepare the user's public keys ahead.
652
+ // For most cases we have the correct "doer", then use it as the SSH user.
653
+ // If we can't get the doer, then use the built-in SSH user.
654
+ if doer != nil {
655
+ sshUser = doer .Name
656
+ } else {
657
+ sshUser = setting .SSH .BuiltinServerUser
658
+ }
659
+ }
660
+
648
661
// non-standard port, it must use full URI
649
662
if setting .SSH .Port != 22 {
650
663
sshHost := net .JoinHostPort (sshDomain , strconv .Itoa (setting .SSH .Port ))
@@ -662,21 +675,20 @@ func ComposeSSHCloneURL(ownerName, repoName string) string {
662
675
return fmt .Sprintf ("%s@%s:%s/%s.git" , sshUser , sshHost , url .PathEscape (ownerName ), url .PathEscape (repoName ))
663
676
}
664
677
665
- func (repo * Repository ) cloneLink (isUncyclo bool ) * CloneLink {
666
- repoName := repo .Name
667
- if isUncyclo {
668
- repoName += ".wiki"
669
- }
670
-
678
+ func (repo * Repository ) cloneLink (ctx context.Context , doer * user_model.User , repoPathName string ) * CloneLink {
671
679
cl := new (CloneLink )
672
- cl .SSH = ComposeSSHCloneURL (repo .OwnerName , repoName )
673
- cl .HTTPS = ComposeHTTPSCloneURL (repo .OwnerName , repoName )
680
+ cl .SSH = ComposeSSHCloneURL (doer , repo .OwnerName , repoPathName )
681
+ cl .HTTPS = ComposeHTTPSCloneURL (ctx , repo .OwnerName , repoPathName )
674
682
return cl
675
683
}
676
684
677
685
// CloneLink returns clone URLs of repository.
678
- func (repo * Repository ) CloneLink () (cl * CloneLink ) {
679
- return repo .cloneLink (false )
686
+ func (repo * Repository ) CloneLink (ctx context.Context , doer * user_model.User ) (cl * CloneLink ) {
687
+ return repo .cloneLink (ctx , doer , repo .Name )
688
+ }
689
+
690
+ func (repo * Repository ) CloneLinkGeneral (ctx context.Context ) (cl * CloneLink ) {
691
+ return repo .cloneLink (ctx , nil /* no doer, use a general git user */ , repo .Name )
680
692
}
681
693
682
694
// GetOriginalURLHostname returns the hostname of a URL or the URL
@@ -772,47 +784,75 @@ func GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*Repo
772
784
return & repo , err
773
785
}
774
786
775
- // getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url
776
- func getRepositoryURLPathSegments (repoURL string ) []string {
777
- if strings .HasPrefix (repoURL , setting .AppURL ) {
778
- return strings .Split (strings .TrimPrefix (repoURL , setting .AppURL ), "/" )
787
+ func parseRepositoryURL (ctx context.Context , repoURL string ) (ret struct {
788
+ OwnerName , RepoName , RemainingPath string
789
+ },
790
+ ) {
791
+ // possible urls for git:
792
+ // https://my.domain/sub-path/<owner>/<repo>[.git]
793
+ // git+ssh://[email protected] /<owner>/<repo>[.git]
794
+ // ssh://[email protected] /<owner>/<repo>[.git]
795
+ // [email protected] :<owner>/<repo>[.git]
796
+
797
+ fillPathParts := func (s string ) {
798
+ s = strings .TrimPrefix (s , "/" )
799
+ fields := strings .SplitN (s , "/" , 3 )
800
+ if len (fields ) >= 2 {
801
+ ret .OwnerName = fields [0 ]
802
+ ret .RepoName = strings .TrimSuffix (fields [1 ], ".git" )
803
+ if len (fields ) == 3 {
804
+ ret .RemainingPath = "/" + fields [2 ]
805
+ }
806
+ }
779
807
}
780
808
781
- sshURLVariants := [4 ]string {
782
- setting .SSH .Domain + ":" ,
783
- setting .SSH .User + "@" + setting .SSH .Domain + ":" ,
784
- "git+ssh://" + setting .SSH .Domain + "/" ,
785
- "git+ssh://" + setting .SSH .User + "@" + setting .SSH .Domain + "/" ,
809
+ parsed , err := giturl .ParseGitURL (repoURL )
810
+ if err != nil {
811
+ return ret
786
812
}
787
-
788
- for _ , sshURL := range sshURLVariants {
789
- if strings .HasPrefix (repoURL , sshURL ) {
790
- return strings .Split (strings .TrimPrefix (repoURL , sshURL ), "/" )
813
+ if parsed .URL .Scheme == "http" || parsed .URL .Scheme == "https" {
814
+ if ! httplib .IsCurrentGiteaSiteURL (ctx , repoURL ) {
815
+ return ret
816
+ }
817
+ fillPathParts (strings .TrimPrefix (parsed .URL .Path , setting .AppSubURL ))
818
+ } else if parsed .URL .Scheme == "ssh" || parsed .URL .Scheme == "git+ssh" {
819
+ domainSSH := setting .SSH .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
825
+ }
826
+ // check whether URL domain is the App domain
827
+ domainMatches := domainSSH == 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 )
791
832
}
792
833
}
793
-
794
- return nil
834
+ return ret
795
835
}
796
836
797
837
// GetRepositoryByURL returns the repository by given url
798
838
func GetRepositoryByURL (ctx context.Context , repoURL string ) (* Repository , error ) {
799
- // possible urls for git:
800
- // https://my.domain/sub-path/<owner>/<repo>.git
801
- // https://my.domain/sub-path/<owner>/<repo>
802
- // git+ssh://[email protected] /<owner>/<repo>.git
803
- // git+ssh://[email protected] /<owner>/<repo>
804
- // [email protected] :<owner>/<repo>.git
805
- // [email protected] :<owner>/<repo>
806
-
807
- pathSegments := getRepositoryURLPathSegments (repoURL )
808
-
809
- if len (pathSegments ) != 2 {
839
+ ret := parseRepositoryURL (ctx , repoURL )
840
+ if ret .OwnerName == "" {
810
841
return nil , fmt .Errorf ("unknown or malformed repository URL" )
811
842
}
843
+ return GetRepositoryByOwnerAndName (ctx , ret .OwnerName , ret .RepoName )
844
+ }
812
845
813
- ownerName := pathSegments [0 ]
814
- repoName := strings .TrimSuffix (pathSegments [1 ], ".git" )
815
- return GetRepositoryByOwnerAndName (ctx , ownerName , repoName )
846
+ // GetRepositoryByURLRelax also accepts an SSH clone URL without user part
847
+ func GetRepositoryByURLRelax (ctx context.Context , repoURL string ) (* Repository , error ) {
848
+ if ! strings .Contains (repoURL , "://" ) && ! strings .Contains (repoURL , "@" ) {
849
+ // convert "example.com:owner/repo" to "@example.com:owner/repo"
850
+ p1 , p2 , p3 := strings .Index (repoURL , "." ), strings .Index (repoURL , ":" ), strings .Index (repoURL , "/" )
851
+ if 0 < p1 && p1 < p2 && p2 < p3 {
852
+ repoURL = "@" + repoURL
853
+ }
854
+ }
855
+ return GetRepositoryByURL (ctx , repoURL )
816
856
}
817
857
818
858
// GetRepositoryByID returns the repository by given id if exists.
0 commit comments