main.go 2.53 KB
Newer Older
Bruce Flynn's avatar
init  
Bruce Flynn committed
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
package main

import (
	"fmt"
	"io"
	"log"
	"os"
	"path"

	"github.com/spf13/pflag"
)

var (
	verbose  bool
	buildStr string
)

func info(fmt string, args ...interface{}) {
	log.Printf(fmt, args...)
}

func debug(fmt string, args ...interface{}) {
	if verbose {
		info(fmt, args...)
	}
}

func defaultPrivateKey() string {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		panic("could not determine user home dir")
	}
	return path.Join(homeDir, ".ssh/id_rsa")
}

func main() {

	log.SetOutput(os.Stderr)

	pflag.Usage = func() {
		fmt.Fprintf(os.Stderr, `%s [options]
Bruce Flynn's avatar
Bruce Flynn committed
42

Bruce Flynn's avatar
init  
Bruce Flynn committed
43
44
45
46
47
48
49
50
A mini JSON API for performing SFTP commands.

Supported commands read from commands:

    {"command": "PUT", "args": {"source": <path>, "dest": <path>}}
    {"command": "GET", "args": {"source": <path>, "dest": <path>}}
    {"command": "LISTDIR", "args": {"path": <path>}}
        returns [{"name": "<filename>", "size": <bytes>, "mtime": <unixtime>}, ...]
Bruce Flynn's avatar
Bruce Flynn committed
51
    {"command": "DELETE", "args": {"path": <path>}}
Bruce Flynn's avatar
init  
Bruce Flynn committed
52
53
54
55
56

Responses written to stdout:

    {"status": "(ok|error|fail)", "message": "<err message>", "data": (""|??)}

Bruce Flynn's avatar
Bruce Flynn committed
57
    where "data" is documented with the command.
Bruce Flynn's avatar
init  
Bruce Flynn committed
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

Options:

`, os.Args[0])
		pflag.PrintDefaults()

		fmt.Fprintf(os.Stderr, "\n%s\n", buildStr)
	}

	var (
		err    error
		input  io.Reader
		source = pflag.StringP("commands", "c", "-", "File where commands are read from.")
		help   = pflag.Bool("help", false, "Print help and exit")
		urlArg = pflag.String("url", "", "SFTP base url sftp://[<user>@]<hostname>[:port]. Any provided path will be ignored")
		// pflag.BoolVar(&verbose, "verbose", false, "Verbose output")
		pKey = pflag.StringP("pkey", "i", defaultPrivateKey(), "Path to PEM formatted private key.")
		hKey = pflag.StringP("hkey", "h", "", "Path to PEM formatted host public key. If not "+
			"provided server host key checking will be disabled. To get a server host key run "+
			"ssh-keyscan -t rsa <hostname>.")
	)
	pflag.BoolVar(&verbose, "verbose", false, "Verbose output to stderr")

	pflag.Parse()

	if *help {
		pflag.Usage()
		os.Exit(2)
	}

	if *source == "-" {
		input = os.Stdin
	} else {
		input, err = os.Open(*source)
		if err != nil {
			info("could not open source")
			os.Exit(2)
		}
	}

	sftp, err := newSftpApi(*urlArg, *pKey, *hKey, input, os.Stdout)
	if err != nil {
		info("could not initialize api: %s", err)
		os.Exit(2)
	}
	defer sftp.close()

	for cmd := range sftp.commands() {
		debug("command: %+v", cmd)
		zult := sftp.doCommand(cmd)
		debug("result %+v", zult)

		if zult.Status == "fail" {
			info("FAIL: %+v", zult)
		}
		sftp.writeResult(zult)
	}
}