func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string, isWiki bool) { task, err := models.GetUpdateTaskByUUID(uuid) if err != nil { if models.IsErrUpdateTaskNotExist(err) { log.GitLogger.Trace("No update task is presented: %s", uuid) return } log.GitLogger.Fatal(2, "GetUpdateTaskByUUID: %v", err) } else if err = models.DeleteUpdateTaskByUUID(uuid); err != nil { log.GitLogger.Fatal(2, "DeleteUpdateTaskByUUID: %v", err) } if isWiki { return } if err = models.PushUpdate(models.PushUpdateOptions{ RefName: task.RefName, OldCommitID: task.OldCommitID, NewCommitID: task.NewCommitID, PusherID: user.Id, PusherName: user.Name, RepoUserName: repoUser.Name, RepoName: reponame, }); err != nil { log.GitLogger.Error(2, "Update: %v", err) } // Ask for running deliver hook and test pull request tasks. reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" + strings.TrimPrefix(task.RefName, "refs/heads/") + "&secret=" + base.EncodeMD5(repoUser.Salt) log.GitLogger.Trace("Trigger task: %s", reqURL) resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: true, }).Response() if err == nil { resp.Body.Close() if resp.StatusCode/100 != 2 { log.GitLogger.Error(2, "Fail to trigger task: not 2xx response code") } } else { log.GitLogger.Error(2, "Fail to trigger task: %v", err) } }
func HTTP(ctx *context.Context) { username := ctx.Params(":username") reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git") var isPull bool service := ctx.Query("service") if service == "git-receive-pack" || strings.HasSuffix(ctx.Req.URL.Path, "git-receive-pack") { isPull = false } else if service == "git-upload-pack" || strings.HasSuffix(ctx.Req.URL.Path, "git-upload-pack") { isPull = true } else { isPull = (ctx.Req.Method == "GET") } isWiki := false if strings.HasSuffix(reponame, ".wiki") { isWiki = true reponame = reponame[:len(reponame)-5] } repoUser, err := models.GetUserByName(username) if err != nil { if models.IsErrUserNotExist(err) { ctx.Handle(404, "GetUserByName", nil) } else { ctx.Handle(500, "GetUserByName", err) } return } repo, err := models.GetRepositoryByName(repoUser.Id, reponame) if err != nil { if models.IsErrRepoNotExist(err) { ctx.Handle(404, "GetRepositoryByName", nil) } else { ctx.Handle(500, "GetRepositoryByName", err) } return } // Only public pull don't need auth. isPublicPull := !repo.IsPrivate && isPull var ( askAuth = !isPublicPull || setting.Service.RequireSignInView authUser *models.User authUsername string authPasswd string ) // check access if askAuth { authHead := ctx.Req.Header.Get("Authorization") if len(authHead) == 0 { authRequired(ctx) return } auths := strings.Fields(authHead) // currently check basic auth // TODO: support digit auth // FIXME: middlewares/context.go did basic auth check already, // maybe could use that one. if len(auths) != 2 || auths[0] != "Basic" { ctx.HandleText(401, "no basic auth and digit auth") return } authUsername, authPasswd, err = base.BasicAuthDecode(auths[1]) if err != nil { ctx.HandleText(401, "no basic auth and digit auth") return } authUser, err = models.UserSignIn(authUsername, authPasswd) if err != nil { if !models.IsErrUserNotExist(err) { ctx.Handle(500, "UserSignIn error: %v", err) return } // Assume username now is a token. token, err := models.GetAccessTokenBySHA(authUsername) if err != nil { if models.IsErrAccessTokenNotExist(err) { ctx.HandleText(401, "invalid token") } else { ctx.Handle(500, "GetAccessTokenBySha", err) } return } token.Updated = time.Now() if err = models.UpdateAccessToken(token); err != nil { ctx.Handle(500, "UpdateAccessToken", err) } authUser, err = models.GetUserByID(token.UID) if err != nil { ctx.Handle(500, "GetUserByID", err) return } } if !isPublicPull { var tp = models.ACCESS_MODE_WRITE if isPull { tp = models.ACCESS_MODE_READ } has, err := models.HasAccess(authUser, repo, tp) if err != nil { ctx.Handle(500, "HasAccess", err) return } else if !has { if tp == models.ACCESS_MODE_READ { has, err = models.HasAccess(authUser, repo, models.ACCESS_MODE_WRITE) if err != nil { ctx.Handle(500, "HasAccess2", err) return } else if !has { ctx.HandleText(403, "User permission denied") return } } else { ctx.HandleText(403, "User permission denied") return } } if !isPull && repo.IsMirror { ctx.HandleText(403, "mirror repository is read-only") return } } } callback := func(rpc string, input []byte) { if rpc != "receive-pack" || isWiki { return } var lastLine int64 = 0 for { head := input[lastLine : lastLine+2] if head[0] == '0' && head[1] == '0' { size, err := strconv.ParseInt(string(input[lastLine+2:lastLine+4]), 16, 32) if err != nil { log.Error(4, "%v", err) return } if size == 0 { //fmt.Println(string(input[lastLine:])) break } line := input[lastLine : lastLine+size] idx := bytes.IndexRune(line, '\000') if idx > -1 { line = line[:idx] } fields := strings.Fields(string(line)) if len(fields) >= 3 { oldCommitId := fields[0][4:] newCommitId := fields[1] refName := fields[2] // FIXME: handle error. if err = models.PushUpdate(models.PushUpdateOptions{ RefName: refName, OldCommitID: oldCommitId, NewCommitID: newCommitId, PusherID: authUser.Id, PusherName: authUser.Name, RepoUserName: username, RepoName: reponame, }); err == nil { go models.HookQueue.Add(repo.ID) go models.AddTestPullRequestTask(repo.ID, strings.TrimPrefix(refName, "refs/heads/")) } } lastLine = lastLine + size } else { break } } } HTTPBackend(ctx, &Config{ RepoRootPath: setting.RepoRootPath, GitBinPath: "git", UploadPack: true, ReceivePack: true, OnSucceed: callback, })(ctx.Resp, ctx.Req.Request) runtime.GC() }