Get rid of sandboxed fmtserver.

The current sandbox is excess complexity, needs manual steps to setup,
and doesn't isolate different users of a formatting server from each other.
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 849d506..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,20 +0,0 @@
-FROM openjdk:8-jre
-
-WORKDIR /app
-
-# ADD /app/google-java-format.jar
-
-# https://github.com/google/google-java-format/releases/download/google-java-format-1.7/google-java-format-1.7-all-deps.jar
-COPY google-java-format-1.7-all-deps.jar /usr/bin/google-java-format-all-deps.jar
-
-# go build github.com/google/gerritfmt/cmd/fmtserver
-COPY fmtserver /usr/bin/fmtserver
-
-# go get github.com/bazelbuild/buildtools/buildifier
-COPY buildifier /usr/bin/buildifier
-
-# ??
-COPY gofmt /usr/bin/gofmt
-
-EXPOSE 80
-CMD ["fmtserver",  "-port=80"]
diff --git a/README.md b/README.md
index 365f22e..b50b7b9 100644
--- a/README.md
+++ b/README.md
@@ -6,27 +6,25 @@
 This is a style verifier intended to be used with the Gerrit checks
 plugin.
 
-It consists of the following components
-
-   * fmtserver: an RPC server that reformats source code. It is
-     intended to be run in a Docker container that is secured with
-     gvisor.
-
-   * checker: a daemon that contacts gerrit, and sends pending changes
-     to fmtserver to validate correct formatting
-
-
 TODO
 ====
 
    * handle file types (symlink) and deletions
 
-   * use the full checker API
-
    * more formatters: clang-format, typescript, jsformat, ... ?
 
-   * isolate each formatter run with a separate gvisor/docker
-     container?
+   * isolate each formatter to run with a separate gvisor/docker
+     container.
+
+   * tests: the only way to test this reliably is to spin up a gerrit server,
+     and create changes against the server.
+
+
+SECURITY
+========
+
+This currently runs the formatters without sandboxing. Critical bugs in
+formatters can be escalated to obtain the OAuth2 token used for authentication.
 
 
 DISCLAIMER
diff --git a/cmd/checker/checker.go b/cmd/checker/checker.go
index 930a1bb..f45b0c9 100644
--- a/cmd/checker/checker.go
+++ b/cmd/checker/checker.go
@@ -30,8 +30,7 @@
 
 // gerritChecker run formatting checks against a gerrit server.
 type gerritChecker struct {
-	server    *gerrit.Server
-	fmtClient *rpc.Client
+	server *gerrit.Server
 
 	// UUID => language
 	checkerUUIDs []string
@@ -48,11 +47,10 @@
 	return fields[0], true
 }
 
-func NewGerritChecker(server *gerrit.Server, fmtClient *rpc.Client) (*gerritChecker, error) {
+func NewGerritChecker(server *gerrit.Server) (*gerritChecker, error) {
 	gc := &gerritChecker{
-		server:    server,
-		fmtClient: fmtClient,
-		todo:      make(chan *gerrit.PendingChecksInfo, 5),
+		server: server,
+		todo:   make(chan *gerrit.PendingChecksInfo, 5),
 	}
 
 	if out, err := ListCheckers(server); err != nil {
@@ -92,7 +90,7 @@
 	}
 
 	rep := gerritfmt.FormatReply{}
-	if err := c.fmtClient.Call("Server.Format", &req, &rep); err != nil {
+	if err := gerritfmt.Format(&req, &rep); err != nil {
 		_, ok := err.(rpc.ServerError)
 		if ok {
 			return nil, fmt.Errorf("server returned: %s", err)
@@ -170,7 +168,7 @@
 		statusUnset:      "UNSET",
 		statusIrrelevant: "IRRELEVANT",
 		statusRunning:    "RUNNING",
-		statusFail:       "FAIL",
+		statusFail:       "FAILED",
 		statusSuccessful: "SUCCESSFUL",
 	}[s]
 }
diff --git a/cmd/checker/main.go b/cmd/checker/main.go
index 1e643f2..451e049 100644
--- a/cmd/checker/main.go
+++ b/cmd/checker/main.go
@@ -23,7 +23,6 @@
 	"fmt"
 	"io/ioutil"
 	"log"
-	"net/rpc"
 	"net/url"
 	"os"
 	"strings"
@@ -101,7 +100,6 @@
 
 func main() {
 	gerritURL := flag.String("gerrit", "", "URL to gerrit host")
-	addr := flag.String("addr", "", "Address of the fmtserver")
 	register := flag.Bool("register", false, "Register with the host")
 	update := flag.Bool("update", false, "Update an existing checker on the host")
 	list := flag.Bool("list", false, "List pending checks")
@@ -186,15 +184,8 @@
 		log.Printf("CreateChecker result: %v", ch)
 		os.Exit(0)
 	}
-	if *addr == "" {
-		log.Fatal("must set --addr")
-	}
-	fmtClient, err := rpc.DialHTTP("tcp", *addr)
-	if err != nil {
-		log.Fatal(err)
-	}
 
-	gc, err := NewGerritChecker(g, fmtClient)
+	gc, err := NewGerritChecker(g)
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/server.go b/server.go
index e4cc445..b2692a0 100644
--- a/server.go
+++ b/server.go
@@ -28,34 +28,71 @@
 	"strings"
 )
 
-// Formatter is a definition of a formatting
-type Formatter struct {
+// Formatter is a definition of a formatting engine
+type Formatter interface {
+	// Format returns the files but formatted. All files are
+	// assumed to have the same language.
+	Format(in []File, outSink io.Writer) (out []FormattedFile, err error)
+}
+
+// FormatterConfig defines the mapping configurable
+type FormatterConfig struct {
 	// Regex is the typical filename regexp to use
 	Regex *regexp.Regexp
 
 	// Query is used to filter inside Gerrit
 	Query string
+
+	// The formatter
+	Formatter Formatter
 }
 
 // Formatters holds all the formatters supported
-var Formatters = map[string]*Formatter{
-	"java": {
-		Regex: regexp.MustCompile(`\.java$`),
-		Query: "ext:java",
-	},
-	"bzl": {
-		Regex: regexp.MustCompile(`(\.bzl|/BUILD|^BUILD)$`),
-		Query: "(ext:bzl OR file:BUILD OR file:WORKSPACE)",
-	},
-	"go": {
-		Regex: regexp.MustCompile(`\.go$`),
-		Query: "ext:go",
-	},
+var Formatters = map[string]*FormatterConfig{
 	"commitmsg": {
-		Regex: regexp.MustCompile(`^/COMMIT_MSG$`),
+		Regex:     regexp.MustCompile(`^/COMMIT_MSG$`),
+		Formatter: &commitMsgFormatter{},
 	},
 }
 
+func init() {
+	gjf, err := exec.LookPath("google-java-format")
+	if err == nil {
+		Formatters["java"] = &FormatterConfig{
+			Regex: regexp.MustCompile(`\.java$`),
+			Query: "ext:java",
+			Formatter: &toolFormatter{
+				bin:  "java",
+				args: []string{"-jar", gjf, "-i"},
+			},
+		}
+	}
+
+	bzl, err := exec.LookPath("buildifier")
+	if err == nil {
+		Formatters["bzl"] = &FormatterConfig{
+			Regex: regexp.MustCompile(`(\.bzl|/BUILD|^BUILD)$`),
+			Query: "(ext:bzl OR file:BUILD OR file:WORKSPACE)",
+			Formatter: &toolFormatter{
+				bin:  bzl,
+				args: []string{"-mode=fix"},
+			},
+		}
+	}
+
+	gofmt, err := exec.LookPath("gofmt")
+	if err == nil {
+		Formatters["go"] = &FormatterConfig{
+			Regex: regexp.MustCompile(`\.go$`),
+			Query: "ext:go",
+			Formatter: &toolFormatter{
+				bin:  gofmt,
+				args: []string{"-w"},
+			},
+		}
+	}
+}
+
 // IsSupported returns if the given language is supported.
 func IsSupported(lang string) bool {
 	_, ok := Formatters[lang]
@@ -72,30 +109,6 @@
 	return r
 }
 
-// Server holds the settings for a server.
-type Server struct {
-	JavaJar      string
-	Buildifier   string
-	Gofmt        string
-	formatterMap map[string]formatterFunc
-}
-
-type formatterFunc func(in []File, out io.Writer) ([]FormattedFile, error)
-
-// NewServer constructs a new server.
-func NewServer() *Server {
-	s := &Server{}
-
-	s.formatterMap = map[string]formatterFunc{
-		"java":      s.javaFormat,
-		"go":        s.goFormat,
-		"bzl":       s.bazelFormat,
-		"commitmsg": s.commitCheck,
-	}
-
-	return s
-}
-
 func splitByLang(in []File) map[string][]File {
 	res := map[string][]File{}
 	for _, f := range in {
@@ -104,8 +117,8 @@
 	return res
 }
 
-// Format is the formatserver RPC endpoint.
-func (s *Server) Format(req *FormatRequest, rep *FormatReply) error {
+// Format formats all the files in the request for which a formatter exists.
+func Format(req *FormatRequest, rep *FormatReply) error {
 	for _, f := range req.Files {
 		if f.Language == "" {
 			return fmt.Errorf("file %q has empty language", f.Name)
@@ -114,7 +127,10 @@
 
 	for language, fs := range splitByLang(req.Files) {
 		var buf bytes.Buffer
-		out, err := s.formatterMap[language](fs, &buf)
+		entry := Formatters[language]
+		log.Println("init", Formatters)
+
+		out, err := entry.Formatter.Format(fs, &buf)
 		if err != nil {
 			return err
 		}
@@ -127,8 +143,10 @@
 	return nil
 }
 
-func (s *Server) commitCheck(in []File, outSink io.Writer) (out []FormattedFile, err error) {
-	complaint := s.checkCommitMessage(string(in[0].Content))
+type commitMsgFormatter struct{}
+
+func (f *commitMsgFormatter) Format(in []File, outSink io.Writer) (out []FormattedFile, err error) {
+	complaint := checkCommitMessage(string(in[0].Content))
 	ff := FormattedFile{}
 	ff.Name = in[0].Name
 	if complaint != "" {
@@ -140,7 +158,7 @@
 	return out, nil
 }
 
-func (s *Server) checkCommitMessage(msg string) (complaint string) {
+func checkCommitMessage(msg string) (complaint string) {
 	lines := strings.Split(msg, "\n")
 	if len(lines) < 2 {
 		return "must have multiple lines"
@@ -161,42 +179,14 @@
 	return ""
 }
 
-func (s *Server) javaFormat(in []File, outSink io.Writer) (out []FormattedFile, err error) {
-	if _, err := os.Stat(s.JavaJar); err != nil {
-		return nil, fmt.Errorf("Stat(%q): %v", s.JavaJar, err)
-	}
-	cmd := exec.Command(
-		"java",
-		"-jar",
-		s.JavaJar,
-		"-i",
-	)
-	return s.inlineFixTool(cmd, in, outSink)
+type toolFormatter struct {
+	bin  string
+	args []string
 }
 
-func (s *Server) bazelFormat(in []File, outSink io.Writer) (out []FormattedFile, err error) {
-	if _, err := os.Stat(s.Buildifier); err != nil {
-		return nil, fmt.Errorf("Stat(%q): %v", s.Buildifier, err)
-	}
-	cmd := exec.Command(
-		s.Buildifier,
-		"-mode=fix",
-	)
-	return s.inlineFixTool(cmd, in, outSink)
-}
+func (f *toolFormatter) Format(in []File, outSink io.Writer) (out []FormattedFile, err error) {
+	cmd := exec.Command(f.bin, f.args...)
 
-func (s *Server) goFormat(in []File, outSink io.Writer) (out []FormattedFile, err error) {
-	if _, err := os.Stat(s.Buildifier); err != nil {
-		return nil, fmt.Errorf("Stat(%q): %v", s.Buildifier, err)
-	}
-	cmd := exec.Command(
-		s.Gofmt,
-		"-w",
-	)
-	return s.inlineFixTool(cmd, in, outSink)
-}
-
-func (s *Server) inlineFixTool(cmd *exec.Cmd, in []File, outSink io.Writer) (out []FormattedFile, err error) {
 	tmpDir, err := ioutil.TempDir("", "gerritfmt")
 	if err != nil {
 		return nil, err