mirror of https://github.com/perkeep/perkeep.git
camput --init; First-time config helper.
This commit is contained in:
parent
c761e1afc8
commit
3eaf825674
|
@ -2,6 +2,7 @@ include $(GOROOT)/src/Make.inc
|
|||
|
||||
PREREQ=$(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH)/camli/schema.a \
|
||||
$(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH)/camli/client.a \
|
||||
$(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH)/camli/jsonsign.a \
|
||||
$(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH)/crypto/openpgp/packet.a \
|
||||
$(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH)/crypto/openpgp/error.a \
|
||||
$(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH)/crypto/openpgp/armor.a
|
||||
|
@ -9,6 +10,7 @@ PREREQ=$(QUOTED_GOROOT)/pkg/$(GOOS)_$(GOARCH)/camli/schema.a \
|
|||
TARG=camput
|
||||
GOFILES=\
|
||||
camput.go\
|
||||
init.go\
|
||||
|
||||
include $(GOROOT)/src/Make.cmd
|
||||
|
||||
|
|
|
@ -20,11 +20,12 @@ import (
|
|||
)
|
||||
|
||||
// Things that can be uploaded. (at most one of these)
|
||||
var flagBlob *bool = flag.Bool("blob", false, "upload a file's bytes as a single blob")
|
||||
var flagFile *bool = flag.Bool("file", false, "upload a file's bytes as a blob, as well as its JSON file record")
|
||||
var flagPermanode *bool = flag.Bool("permanode", false, "create a new permanode")
|
||||
var flagBlob = flag.Bool("blob", false, "upload a file's bytes as a single blob")
|
||||
var flagFile = flag.Bool("file", false, "upload a file's bytes as a blob, as well as its JSON file record")
|
||||
var flagPermanode = flag.Bool("permanode", false, "create a new permanode")
|
||||
var flagInit = flag.Bool("init", false, "First-time configuration.")
|
||||
|
||||
var flagVerbose *bool = flag.Bool("verbose", false, "be verbose")
|
||||
var flagVerbose = flag.Bool("verbose", false, "be verbose")
|
||||
|
||||
var wereErrors = false
|
||||
|
||||
|
@ -190,10 +191,11 @@ func usage(msg string) {
|
|||
fmt.Println("Error:", msg)
|
||||
}
|
||||
fmt.Println(`
|
||||
Usage: camliup
|
||||
Usage: camput
|
||||
|
||||
camliup --blob <filename(s) to upload as blobs>
|
||||
camliup --file <filename(s) to upload as blobs + JSON metadata>
|
||||
camput --init # first time configuration
|
||||
camput --blob <filename(s) to upload as blobs>
|
||||
camput --file <filename(s) to upload as blobs + JSON metadata>
|
||||
`)
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
|
@ -215,13 +217,17 @@ func handleResult(what string, pr *client.PutResult, err os.Error) {
|
|||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if sumSet(flagFile, flagBlob, flagPermanode) != 1 {
|
||||
usage("Exactly one of --blob and --file may be set")
|
||||
if sumSet(flagFile, flagBlob, flagPermanode, flagInit) != 1 {
|
||||
// TODO: say which ones are conflicting
|
||||
usage("Conflicting mode options.")
|
||||
}
|
||||
|
||||
uploader := &Uploader{client.NewOrFail()}
|
||||
|
||||
switch {
|
||||
case *flagInit:
|
||||
doInit()
|
||||
return
|
||||
case *flagPermanode:
|
||||
if flag.NArg() > 0 {
|
||||
log.Exitf("--permanode doesn't take any additional arguments")
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"camli/blobref"
|
||||
"camli/client"
|
||||
"camli/jsonsign"
|
||||
"crypto/sha1"
|
||||
"exec"
|
||||
"flag"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"json"
|
||||
"log"
|
||||
)
|
||||
|
||||
var flagGpgKey = flag.String("gpgkey", "", "(init option only) GPG key to use for signing.")
|
||||
|
||||
func doInit() {
|
||||
blobDir := path.Join(client.ConfigDir(), "blobs")
|
||||
os.Mkdir(client.ConfigDir(), 0700)
|
||||
os.Mkdir(blobDir, 0700)
|
||||
|
||||
keyId := *flagGpgKey
|
||||
if keyId == "" {
|
||||
keyId = os.Getenv("GPGKEY")
|
||||
}
|
||||
if keyId == "" {
|
||||
// TODO: run and parse gpg --list-secret-keys and see if there's just one and suggest that? Or show
|
||||
// a list of them?
|
||||
log.Exitf("Initialization requires your public GPG key. Set --gpgkey=<pubid> or set $GPGKEY in your environment. Run gpg --list-secret-keys to find their key IDs.")
|
||||
}
|
||||
|
||||
if os.Getenv("GPG_AGENT_INFO") == "" {
|
||||
log.Printf("No GPG_AGENT_INFO found in environment; you should setup gnupg-agent. camput will be annoying otherwise.")
|
||||
}
|
||||
|
||||
// TODO: use same command-line flag as the jsonsign package.
|
||||
// unify them into a shared package just for gpg-related stuff?
|
||||
gpgBinary, err := exec.LookPath("gpg")
|
||||
if err != nil {
|
||||
log.Exitf("Failed to find gpg binary in your path.")
|
||||
}
|
||||
cmd, err := exec.Run(gpgBinary,
|
||||
[]string{"gpg", "--export", "--armor", keyId},
|
||||
os.Environ(),
|
||||
"/",
|
||||
exec.DevNull,
|
||||
exec.Pipe,
|
||||
exec.DevNull)
|
||||
if err != nil {
|
||||
log.Exitf("Error running gpg to export public key: %v", err)
|
||||
}
|
||||
keyBytes, err := ioutil.ReadAll(cmd.Stdout)
|
||||
if err != nil {
|
||||
log.Exitf("Error read from gpg to export public key: %v", err)
|
||||
}
|
||||
|
||||
hash := sha1.New()
|
||||
hash.Write(keyBytes)
|
||||
bref := blobref.FromHash("sha1", hash)
|
||||
|
||||
keyBlobPath := path.Join(blobDir, bref.String() + ".camli")
|
||||
if err = ioutil.WriteFile(keyBlobPath, keyBytes, 0644); err != nil {
|
||||
log.Exitf("Error writing public key blob to %q: %v", keyBlobPath, err)
|
||||
}
|
||||
|
||||
if ok, err := jsonsign.VerifyPublicKeyFile(keyBlobPath, keyId); !ok {
|
||||
log.Exitf("Error verifying public key at %q: %v", keyBlobPath, err)
|
||||
}
|
||||
|
||||
log.Printf("Your Camlistore identity (your GPG public key's blobref) is: %s", bref.String())
|
||||
|
||||
_, err = os.Stat(client.ConfigFilePath())
|
||||
if err == nil {
|
||||
log.Exitf("Config file %q already exists; quitting without touching it.", client.ConfigFilePath())
|
||||
}
|
||||
|
||||
if f, err := os.Open(client.ConfigFilePath(), os.O_CREAT|os.O_WRONLY, 0600); err == nil {
|
||||
defer f.Close()
|
||||
m := make(map[string]interface{})
|
||||
m["blobServer"] = "http://localhost:3179/"
|
||||
m["blobServerPassword"] = "test"
|
||||
m["publicKeyBlobref"] = bref.String()
|
||||
jsonBytes, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
log.Exitf("JSON serialization error: %v", err)
|
||||
}
|
||||
_, err = f.Write(jsonBytes)
|
||||
if err != nil {
|
||||
log.Exitf("Error writing to %q: %v", client.ConfigFilePath(), err)
|
||||
}
|
||||
log.Printf("Wrote %q; modify as necessary.", client.ConfigFilePath())
|
||||
}
|
||||
}
|
|
@ -17,26 +17,30 @@ import (
|
|||
var flagServer *string = flag.String("blobserver", "", "camlistore blob server")
|
||||
var flagPassword *string = flag.String("password", "", "password for blob server")
|
||||
|
||||
func configFilePath() string {
|
||||
func ConfigDir() string {
|
||||
return path.Join(os.Getenv("HOME"), ".camli")
|
||||
}
|
||||
|
||||
func ConfigFilePath() string {
|
||||
return path.Join(os.Getenv("HOME"), ".camli", "config")
|
||||
}
|
||||
|
||||
var configOnce sync.Once
|
||||
var config = make(map[string]interface{})
|
||||
func parseConfig() {
|
||||
f, err := os.Open(configFilePath(), os.O_RDONLY, 0)
|
||||
f, err := os.Open(ConfigFilePath(), os.O_RDONLY, 0)
|
||||
switch {
|
||||
case err != nil && err.(*os.PathError).Error.(os.Errno) == syscall.ENOENT:
|
||||
// TODO: write empty file?
|
||||
return
|
||||
case err != nil:
|
||||
log.Printf("Error opening config file %q: %v", configFilePath(), err)
|
||||
log.Printf("Error opening config file %q: %v", ConfigFilePath(), err)
|
||||
return
|
||||
default:
|
||||
defer f.Close()
|
||||
dj := json.NewDecoder(f)
|
||||
if err := dj.Decode(&config); err != nil {
|
||||
log.Printf("Error parsing JSON in config file %q: %v", configFilePath(), err)
|
||||
log.Printf("Error parsing JSON in config file %q: %v", ConfigFilePath(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,9 +62,16 @@ func blobServerOrDie() string {
|
|||
return cleanServer(*flagServer)
|
||||
}
|
||||
configOnce.Do(parseConfig)
|
||||
|
||||
log.Exitf("No --blobserver parameter specified.")
|
||||
return ""
|
||||
value, ok := config["blobServer"]
|
||||
var server string
|
||||
if ok {
|
||||
server = value.(string)
|
||||
}
|
||||
server = cleanServer(server)
|
||||
if !ok || server == "" {
|
||||
log.Exitf("Missing or invalid \"blobServer\" in %q", ConfigFilePath())
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
func passwordOrDie() string {
|
||||
|
@ -68,9 +79,22 @@ func passwordOrDie() string {
|
|||
return *flagPassword
|
||||
}
|
||||
configOnce.Do(parseConfig)
|
||||
|
||||
log.Exitf("No --password parameter specified.")
|
||||
return ""
|
||||
value, ok := config["blobServerPassword"]
|
||||
var password string
|
||||
if ok {
|
||||
password, ok = value.(string)
|
||||
}
|
||||
if !ok {
|
||||
log.Exitf("No --password parameter specified, and no \"blobServerPassword\" defined in %q", ConfigFilePath())
|
||||
}
|
||||
if password == "" {
|
||||
// TODO: provide way to override warning?
|
||||
// Or make a way to do deferred errors? A blank password might
|
||||
// be valid, but it might also signal the root cause of an error
|
||||
// in the future.
|
||||
log.Printf("Warning: blank \"blobServerPassword\" defined in %q", ConfigFilePath())
|
||||
}
|
||||
return password
|
||||
}
|
||||
|
||||
// Returns blobref of signer's public key, or nil if unconfigured.
|
||||
|
@ -79,18 +103,18 @@ func (c *Client) SignerPublicKeyBlobref() *blobref.BlobRef {
|
|||
key := "publicKeyBlobref"
|
||||
v, ok := config[key]
|
||||
if !ok {
|
||||
log.Printf("No key %q in JSON configuration file %q", key, configFilePath())
|
||||
log.Printf("No key %q in JSON configuration file %q", key, ConfigFilePath())
|
||||
return nil
|
||||
}
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
log.Printf("Expected a string value for key %q in JSON file %q",
|
||||
key, configFilePath())
|
||||
key, ConfigFilePath())
|
||||
}
|
||||
ref := blobref.Parse(s)
|
||||
if ref == nil {
|
||||
log.Printf("Bogus value %#v for key %q in file %q; not a valid blobref",
|
||||
s, key, configFilePath())
|
||||
s, key, ConfigFilePath())
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
|
|
@ -8,10 +8,28 @@ import (
|
|||
"io/ioutil"
|
||||
"crypto/openpgp/armor"
|
||||
"crypto/openpgp/packet"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const publicKeyMaxSize = 256 * 1024
|
||||
|
||||
func VerifyPublicKeyFile(file, keyid string) (bool, os.Error) {
|
||||
f, err := os.Open(file, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
key, err := openArmoredPublicKeyFile(f)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if key.KeyIdString() != strings.ToUpper(keyid) {
|
||||
return false, os.NewError(fmt.Sprintf("Key in file %q has id %q; expected %q",
|
||||
file, key.KeyIdString(), keyid))
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func openArmoredPublicKeyFile(reader io.ReadCloser) (*packet.PublicKeyPacket, os.Error) {
|
||||
defer reader.Close()
|
||||
|
||||
|
|
Loading…
Reference in New Issue