package service import ( "bufio" "context" "fmt" "io" "os" "path/filepath" "github.com/pkg/errors" "github.com/urfave/cli" ) func signCommands(client *Client) []cli.Command { return []cli.Command{ cli.Command{ Name: "sign", Usage: "Sign", Flags: []cli.Flag{ cli.StringFlag{Name: "signer, s", Usage: "signer"}, cli.StringFlag{Name: "in, i", Usage: "file to read"}, cli.StringFlag{Name: "out, o", Usage: "file to write, defaults to .sig (detached) or .signed (attached)"}, cli.BoolFlag{Name: "attached, t", Usage: "output attached signature (.signed)"}, cli.BoolFlag{Name: "detached, d", Usage: "output detached signature (.sig)"}, cli.BoolFlag{Name: "armor, a", Usage: "armored"}, cli.BoolFlag{Name: "binary, b", Usage: "binary"}, }, Action: func(c *cli.Context) error { mode, err := parseMode(c) if err != nil { return err } if c.String("in") != "" { return signFileForCLI(c, client, mode) } return signStdin(c, client, mode) }, }, } } func signStdin(c *cli.Context, client *Client, mode signMode) error { reader := bufio.NewReader(os.Stdin) writer := os.Stdout detached := mode.isDetached(stdIn) signClient, streamErr := client.RPCClient().SignStream(context.TODO()) if streamErr != nil { return streamErr } if err := signClient.Send(&SignInput{ Signer: c.String("signer"), Armored: mode.isArmored(stdIn, detached), Detached: detached, }); err != nil { return err } var readErr error go func() { _, inErr := readFrom(reader, 1024*1024, func(b []byte) error { if len(b) > 0 { if err := signClient.Send(&SignInput{ Data: b, }); err != nil { return err } } else { if err := signClient.CloseSend(); err != nil { return err } } return nil }) if inErr != nil { readErr = inErr } }() for { resp, recvErr := signClient.Recv() if recvErr != nil { if recvErr == io.EOF { return readErr } return recvErr } _, writeErr := writer.Write(resp.Data) if writeErr != nil { return writeErr } } } func signFileForCLI(c *cli.Context, client *Client, mode signMode) error { detached := mode.isDetached(fileIn) return signFile(client, c.String("signer"), mode.isArmored(fileIn, detached), detached, c.String("in"), c.String("out")) } func signFile(client *Client, signer string, armored bool, detached bool, in string, out string) error { in, err := filepath.Abs(in) if err != nil { return err } if out != "" { out, err = filepath.Abs(out) if err != nil { return err } } signClient, err := client.RPCClient().SignFile(context.TODO()) if err != nil { return err } if err := signClient.Send(&SignFileInput{ Signer: signer, Armored: armored, Detached: detached, In: in, Out: out, }); err != nil { return err } resp, recvErr := signClient.Recv() if recvErr != nil { // if recvErr == io.EOF { // break // } return recvErr } // if err := encryptClient.CloseSend(); err != nil { // return err // } if resp.Out != "" { fmt.Fprintf(client.out, "out: %s\n", resp.Out) } return nil } type option string const ( defaultOption option = "default" trueOption option = "true" falseOption option = "false" ) type signMode struct { armored option detached option } type inputType string const ( stdIn inputType = "stdin" fileIn inputType = "file" ) func (m signMode) isArmored(it inputType, detached bool) bool { if m.armored == defaultOption { if detached || it == stdIn { return true } return false } return m.armored == trueOption } func (m signMode) isDetached(it inputType) bool { if m.detached == defaultOption { switch it { case fileIn: return true default: return false } } return m.detached == trueOption } func parseMode(c *cli.Context) (signMode, error) { mode := signMode{ armored: defaultOption, detached: defaultOption, } if c.Bool("attached") { mode.detached = falseOption } if c.Bool("detached") { mode.detached = trueOption } if c.Bool("armor") { mode.armored = trueOption } if c.Bool("binary") { mode.armored = falseOption } // Check conflicts if c.Bool("attached") && c.Bool("detached") { return mode, errors.Errorf("conflicting attached and detached options") } if c.Bool("armor") && c.Bool("binary") { return mode, errors.Errorf("conflicting armor and binary options") } return mode, nil }