~gitpocket/gitpocket

8
63d04abd18b3dda61f0429d71ade08bf45510023
mainfeed-discovery
gitpocket/forge/shell.go -rw-r--r--2.7 KiB ViewLogBlameView rawPermalink f116a8c05a feat(agit): open pull requests by pushing to refs/for 13 hours ago
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
package forge

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
)

func keys(root, binary string, args []string) int {
	if len(args) != 4 {
		return 1
	}
	store, err := openStore(filepath.Join(root, "gitpocket.db"), true)
	if err != nil {
		return 1
	}
	user, err := store.UserByKey(args[2] + " " + args[3])
	if err != nil {
		return 1
	}
	fmt.Printf("command=\"%s -root %s shell %s\",restrict %s %s\n", binary, root, user.Name, args[2], args[3])
	return 0
}

func shell(root, name string) int {
	original := os.Getenv("SSH_ORIGINAL_COMMAND")
	fail := func(msg string) int {
		fmt.Fprintln(os.Stderr, msg)
		return 1
	}
	if original == "" {
		return fail("hello " + name + ", this host only serves git")
	}
	verb, arg, ok := strings.Cut(original, " ")
	if !ok {
		return fail("unsupported command")
	}
	switch verb {
	case "git-upload-pack", "git-receive-pack", "git-upload-archive":
	default:
		return fail("unsupported command")
	}
	arg = strings.Trim(strings.TrimSpace(arg), "'\"")
	arg = strings.TrimPrefix(strings.TrimPrefix(arg, "/"), "~")
	arg = strings.TrimSuffix(arg, ".git")
	owner, repoName, ok := strings.Cut(arg, "/")
	if !ok || !validName(owner) || !validName(repoName) {
		return fail("repository path must be ~owner/repo")
	}
	store, err := openStore(filepath.Join(root, "gitpocket.db"), true)
	if err != nil {
		return fail("database unavailable")
	}
	user, err := store.UserByName(name)
	if err != nil {
		return fail("unknown user")
	}
	repo, err := store.RepoByPath(owner, repoName)
	if err != nil {
		moved, ok := store.Redirect(owner, repoName)
		if !ok {
			return fail("repository not found")
		}
		repo = moved
	}
	canWrite := store.CanWrite(repo, &user)
	if repo.Private && !canWrite {
		return fail("repository not found")
	}
	if verb == "git-receive-pack" && !hooksReady(root) {
		return fail("pushes are unavailable right now")
	}
	if verb == "git-receive-pack" && repo.Archived {
		return fail("repository is archived and read only")
	}
	if verb == "git-receive-pack" {
		if over, used, quota := overQuota(root, repo.Owner.Name); over {
			return fail(quotaMessage(used, quota))
		}
	}
	dir := filepath.Join(root, "repos", repo.Owner.Name, repo.Name+".git")
	git, err := exec.LookPath("git")
	if err != nil {
		return fail("git not installed")
	}
	env := []string{"PATH=" + os.Getenv("PATH"), "HOME=" + os.Getenv("HOME")}
	if p := os.Getenv("GIT_PROTOCOL"); p != "" {
		env = append(env, "GIT_PROTOCOL="+p)
	}
	argv := []string{"git"}
	if verb == "git-receive-pack" {
		argv = append(append(argv, receiveArgs(root)...), pushLimit(root, repo.Owner.Name, canWrite)...)
		env = append(env, receiveEnv(repo, user)...)
	}
	err = syscall.Exec(git, append(argv, strings.TrimPrefix(verb, "git-"), dir), env)
	return fail(err.Error())
}