From cdc46007255c0f6161aed6ec4ee7e0e11cf299e7 Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Mon, 6 Nov 2017 12:12:55 +0800
Subject: [PATCH] Fix fork repository cycle to self (#2860)

* fix fork repository cycle to self

* rename testForkRepo to traverseParentRepo
---
 routers/repo/pull.go | 50 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 45 insertions(+), 5 deletions(-)

diff --git a/routers/repo/pull.go b/routers/repo/pull.go
index f80e8cb1e5..ac0d36c278 100644
--- a/routers/repo/pull.go
+++ b/routers/repo/pull.go
@@ -62,7 +62,6 @@ func getForkRepository(ctx *context.Context) *models.Repository {
 	ctx.Data["description"] = forkRepo.Description
 	ctx.Data["IsPrivate"] = forkRepo.IsPrivate
 	canForkToUser := forkRepo.OwnerID != ctx.User.ID && !ctx.User.HasForkedRepo(forkRepo.ID)
-	ctx.Data["CanForkToUser"] = canForkToUser
 
 	if err = forkRepo.GetOwner(); err != nil {
 		ctx.Handle(500, "GetOwner", err)
@@ -81,6 +80,31 @@ func getForkRepository(ctx *context.Context) *models.Repository {
 			orgs = append(orgs, org)
 		}
 	}
+
+	var traverseParentRepo = forkRepo
+	for {
+		if ctx.User.ID == traverseParentRepo.OwnerID {
+			canForkToUser = false
+		} else {
+			for i, org := range orgs {
+				if org.ID == traverseParentRepo.OwnerID {
+					orgs = append(orgs[:i], orgs[i+1:]...)
+					break
+				}
+			}
+		}
+
+		if !traverseParentRepo.IsFork {
+			break
+		}
+		traverseParentRepo, err = models.GetRepositoryByID(traverseParentRepo.ForkID)
+		if err != nil {
+			ctx.Handle(500, "GetRepositoryByID", err)
+			return nil
+		}
+	}
+
+	ctx.Data["CanForkToUser"] = canForkToUser
 	ctx.Data["Orgs"] = orgs
 
 	if canForkToUser {
@@ -125,10 +149,26 @@ func ForkPost(ctx *context.Context, form auth.CreateRepoForm) {
 		return
 	}
 
-	repo, has := models.HasForkedRepo(ctxUser.ID, forkRepo.ID)
-	if has {
-		ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name)
-		return
+	var err error
+	var traverseParentRepo = forkRepo
+	for {
+		if ctxUser.ID == traverseParentRepo.OwnerID {
+			ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form)
+			return
+		}
+		repo, has := models.HasForkedRepo(ctxUser.ID, traverseParentRepo.ID)
+		if has {
+			ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name)
+			return
+		}
+		if !traverseParentRepo.IsFork {
+			break
+		}
+		traverseParentRepo, err = models.GetRepositoryByID(traverseParentRepo.ForkID)
+		if err != nil {
+			ctx.Handle(500, "GetRepositoryByID", err)
+			return
+		}
 	}
 
 	// Check ownership of organization.