<commit_before>/*

Copyright 2016 Continusec Pty Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

package safeadmin

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"path/filepath"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	pb "github.com/continusec/safeadmin/proto"
	"github.com/golang/protobuf/proto"
	homedir "github.com/mitchellh/go-homedir"
)

var (
	ErrBadCert            = errors.New("Unable to understand baked-in cert")
	ErrCertNotValidBefore = errors.New("Cert is not valid before now")
	ErrCertNotValidAfter  = errors.New("Cert is not after before now")
	ErrCertNotRSA         = errors.New("Cert should be RSA algorithm")
	ErrCertWontCast       = errors.New("Cert public key won't cast")
	ErrUnexpectedLengthOfBlock = errors.New("Unexpected length of block")
)

func encrypt(key []byte, in io.Reader, out io.Writer) error {
	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	iv := make([]byte, block.BlockSize())
	_, err = io.ReadFull(rand.Reader, iv)
	if err != nil {
		return err
	}

	// IV is not a secret, write it out
	_, err = out.Write(iv)
	if err != nil {
		return err
	}

	// And now write the rest...
	_, err = io.Copy(&cipher.StreamWriter{
		S: cipher.NewCTR(block, iv),
		W: out,
	}, in)
	if err != nil {
		return err
	}

	return nil
}

func decrypt(key []byte, in io.Reader, out io.Writer) error {
	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	iv := make([]byte, block.BlockSize())
	_, err = io.ReadFull(rand.Reader, iv)
	if err != nil {
		return err
	}

	// IV is not a secret, write it out
	_, err = out.Write(iv)
	if err != nil {
		return err
	}

	// And now write the rest...
	_, err = io.Copy(&cipher.StreamWriter{
		S: cipher.NewCTR(block, iv),
		W: out,
	}, in)
	if err != nil {
		return err
	}

	return nil
}

type EncryptHeader struct {
	PublicKey       *rsa.PublicKey // so server can find the right private key
	TTL             time.Time      // after which time the server requires intervention to decrypt
	EncryptedAESKey []byte         // the encrypted key (OAEP)
}

func EncryptWithTTL(rsaPubKey *rsa.PublicKey, spki []byte, ttl time.Time, in io.Reader, out io.Writer) error {
	ttlb := make([]byte, 8)
	binary.BigEndian.PutUint64(ttlb, uint64(ttl.Unix()))

	key := make([]byte, 32)
	_, err := io.ReadFull(rand.Reader, key)
	if err != nil {
		return err
	}

	oaepResult, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, rsaPubKey, key, ttlb)
	if err != nil {
		return err
	}

	gbs, err := proto.Marshal(&pb.EncryptedHeader{
		Ttl:             ttl.Unix(),
		EncryptedKey:    oaepResult,
		SpkiFingerprint: spki,
	})
	if err != nil {
		return err
	}

	// Length prefix the gob or we struggle to read it, since the decoder seems to be greedy
	err = binary.Write(out, binary.BigEndian, uint64(len(gbs)))
	if err != nil {
		return err
	}

	_, err = out.Write(gbs)
	if err != nil {
		return err
	}

	err = encrypt(key, in, out)
	if err != nil {
		return err
	}

	return nil
}

type Oracle interface {
	GetPrivateKey(*pb.EncryptedHeader) ([]byte, error)
}

type PrivateKeyOracle struct {
	Key *rsa.PrivateKey
}

func (pko *PrivateKeyOracle) GetPrivateKey(eh *pb.EncryptedHeader) ([]byte, error) {
	if time.Now().After(time.Unix(eh.Ttl, 0)) {
		return nil, errors.New("TTL expired.")
	}

	ttlb := make([]byte, 8)
	binary.BigEndian.PutUint64(ttlb, uint64(eh.Ttl))

	key, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, pko.Key, eh.EncryptedKey, ttlb)
	if err != nil {
		return nil, err
	}

	return key, nil
}

type GrpcOracle struct {
	Config *pb.ClientConfig
}

func (gko *GrpcOracle) GetPrivateKey(eh *pb.EncryptedHeader) ([]byte, error) {
	conn, err := CreateGrpcConn(gko.Config)
	if err != nil {
		return nil, err
	}
	defer conn.Close()
	client := pb.NewSafeDumpServiceClient(conn)

	resp, err := client.DecryptSecret(context.Background(), &pb.DecryptSecretRequest{Header: eh})
	if err != nil {
		return nil, err
	}

	return resp.Key, nil
}

func DecryptWithTTL(keyOracle Oracle, in io.Reader, out io.Writer) error {
	var ehLen uint64
	err := binary.Read(in, binary.BigEndian, &ehLen)
	if err != nil {
		return err
	}

    if ehLen > 100000 { // sanity check, should be much smaller
        return ErrUnexpectedLengthOfBlock
    }

	ehb := make([]byte, ehLen)
	_, err = in.Read(ehb)
	if err != nil {
		return err
	}

	eh := &pb.EncryptedHeader{}
	err = proto.Unmarshal(ehb, eh)
	if err != nil {
		return err
	}

	key, err := keyOracle.GetPrivateKey(eh)
	if err != nil {
		return err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	iv := make([]byte, aes.BlockSize)
	amt, err := in.Read(iv)
	if amt == len(iv) {
		err = nil // we want to ignore EOF for now
	}
	if err != nil {
		return err
	}
	_, err = io.Copy(out, &cipher.StreamReader{S: cipher.NewCTR(block, iv), R: in})
	if err != nil {
		return err
	}

	return nil
}

func LoadClientConfiguration() (*pb.ClientConfig, error) {
	hd, err := homedir.Dir()
	if err != nil {
		return nil, err
	}
	path := filepath.Join(hd, ".safedump_config")

	confData, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	conf := &pb.ClientConfig{}
	err = proto.UnmarshalText(string(confData), conf)
	if err != nil {
		return nil, err
	}

	return conf, nil
}

func GetPublicKeyIfValidForNow(der []byte, now time.Time) (*rsa.PublicKey, []byte, error) {
	certificate, err := x509.ParseCertificate(der)
	if err != nil {
		return nil, nil, err
	}
	if now.Before(certificate.NotBefore) {
		return nil, nil, ErrCertNotValidBefore
	}
	if now.After(certificate.NotAfter) {
		return nil, nil, ErrCertNotValidAfter
	}
	if certificate.PublicKeyAlgorithm != x509.RSA {
		return nil, nil, ErrCertNotRSA
	}
	rsaPubKey, ok := certificate.PublicKey.(*rsa.PublicKey)
	if !ok {
		return nil, nil, ErrCertWontCast
	}

	spki := sha256.Sum256(certificate.RawSubjectPublicKeyInfo)
	return rsaPubKey, spki[:], nil
}

func GetCurrentCertificate(config *pb.ClientConfig, now time.Time) (*rsa.PublicKey, []byte, error) {
	hd, err := homedir.Dir()
	if err != nil {
		return nil, nil, err
	}
	path := filepath.Join(hd, ".safedump_cached_cert")
	cd, err := ioutil.ReadFile(path)
	if err == nil {
		rv, spki, err := GetPublicKeyIfValidForNow(cd, now)
		if err == nil {
			return rv, spki, nil
		} // else, we'll fetch a new one
	} // else, we'll fetch a new one

	conn, err := CreateGrpcConn(config)
	if err != nil {
		return nil, nil, err
	}
	defer conn.Close()
	client := pb.NewSafeDumpServiceClient(conn)

	resp, err := client.GetPublicCert(context.Background(), &pb.GetPublicCertRequest{})
	if err != nil {
		return nil, nil, err
	}

	rv, spki, err := GetPublicKeyIfValidForNow(resp.Der, now)
	if err != nil {
		return nil, nil, err
	}

	err = ioutil.WriteFile(path, resp.Der, 0644)
	if err != nil {
		return nil, nil, err
	}

	return rv, spki, nil
}

func CreateGrpcConn(config *pb.ClientConfig) (*grpc.ClientConn, error) {
	// Get certs
	var dialOptions []grpc.DialOption
	if config.NoGrpcSecurity {
		// use system CA pool but disable cert validation
		log.Println("WARNING: Disabling TLS authentication when connecting to gRPC server")
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})))
	} else if config.UseSystemCaForGrpc {
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) // uses the system CA pool
	} else {
		// use baked in cert
		cp := x509.NewCertPool()
		if !cp.AppendCertsFromPEM([]byte(config.GrpcCert)) {
			return nil, ErrBadCert
		}
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{RootCAs: cp})))
	}

	conn, err := grpc.Dial(config.GrpcServer, dialOptions...)
	if err != nil {
		return nil, err
	}

	return conn, nil
}
<commit_msg>Switch to old context<commit_after>/*

Copyright 2016 Continusec Pty Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

package safeadmin

import (
	context "golang.org/x/net/context"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"path/filepath"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	pb "github.com/continusec/safeadmin/proto"
	"github.com/golang/protobuf/proto"
	homedir "github.com/mitchellh/go-homedir"
)

var (
	ErrBadCert            = errors.New("Unable to understand baked-in cert")
	ErrCertNotValidBefore = errors.New("Cert is not valid before now")
	ErrCertNotValidAfter  = errors.New("Cert is not after before now")
	ErrCertNotRSA         = errors.New("Cert should be RSA algorithm")
	ErrCertWontCast       = errors.New("Cert public key won't cast")
	ErrUnexpectedLengthOfBlock = errors.New("Unexpected length of block")
)

func encrypt(key []byte, in io.Reader, out io.Writer) error {
	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	iv := make([]byte, block.BlockSize())
	_, err = io.ReadFull(rand.Reader, iv)
	if err != nil {
		return err
	}

	// IV is not a secret, write it out
	_, err = out.Write(iv)
	if err != nil {
		return err
	}

	// And now write the rest...
	_, err = io.Copy(&cipher.StreamWriter{
		S: cipher.NewCTR(block, iv),
		W: out,
	}, in)
	if err != nil {
		return err
	}

	return nil
}

func decrypt(key []byte, in io.Reader, out io.Writer) error {
	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	iv := make([]byte, block.BlockSize())
	_, err = io.ReadFull(rand.Reader, iv)
	if err != nil {
		return err
	}

	// IV is not a secret, write it out
	_, err = out.Write(iv)
	if err != nil {
		return err
	}

	// And now write the rest...
	_, err = io.Copy(&cipher.StreamWriter{
		S: cipher.NewCTR(block, iv),
		W: out,
	}, in)
	if err != nil {
		return err
	}

	return nil
}

type EncryptHeader struct {
	PublicKey       *rsa.PublicKey // so server can find the right private key
	TTL             time.Time      // after which time the server requires intervention to decrypt
	EncryptedAESKey []byte         // the encrypted key (OAEP)
}

func EncryptWithTTL(rsaPubKey *rsa.PublicKey, spki []byte, ttl time.Time, in io.Reader, out io.Writer) error {
	ttlb := make([]byte, 8)
	binary.BigEndian.PutUint64(ttlb, uint64(ttl.Unix()))

	key := make([]byte, 32)
	_, err := io.ReadFull(rand.Reader, key)
	if err != nil {
		return err
	}

	oaepResult, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, rsaPubKey, key, ttlb)
	if err != nil {
		return err
	}

	gbs, err := proto.Marshal(&pb.EncryptedHeader{
		Ttl:             ttl.Unix(),
		EncryptedKey:    oaepResult,
		SpkiFingerprint: spki,
	})
	if err != nil {
		return err
	}

	// Length prefix the gob or we struggle to read it, since the decoder seems to be greedy
	err = binary.Write(out, binary.BigEndian, uint64(len(gbs)))
	if err != nil {
		return err
	}

	_, err = out.Write(gbs)
	if err != nil {
		return err
	}

	err = encrypt(key, in, out)
	if err != nil {
		return err
	}

	return nil
}

type Oracle interface {
	GetPrivateKey(*pb.EncryptedHeader) ([]byte, error)
}

type PrivateKeyOracle struct {
	Key *rsa.PrivateKey
}

func (pko *PrivateKeyOracle) GetPrivateKey(eh *pb.EncryptedHeader) ([]byte, error) {
	if time.Now().After(time.Unix(eh.Ttl, 0)) {
		return nil, errors.New("TTL expired.")
	}

	ttlb := make([]byte, 8)
	binary.BigEndian.PutUint64(ttlb, uint64(eh.Ttl))

	key, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, pko.Key, eh.EncryptedKey, ttlb)
	if err != nil {
		return nil, err
	}

	return key, nil
}

type GrpcOracle struct {
	Config *pb.ClientConfig
}

func (gko *GrpcOracle) GetPrivateKey(eh *pb.EncryptedHeader) ([]byte, error) {
	conn, err := CreateGrpcConn(gko.Config)
	if err != nil {
		return nil, err
	}
	defer conn.Close()
	client := pb.NewSafeDumpServiceClient(conn)

	resp, err := client.DecryptSecret(context.Background(), &pb.DecryptSecretRequest{Header: eh})
	if err != nil {
		return nil, err
	}

	return resp.Key, nil
}

func DecryptWithTTL(keyOracle Oracle, in io.Reader, out io.Writer) error {
	var ehLen uint64
	err := binary.Read(in, binary.BigEndian, &ehLen)
	if err != nil {
		return err
	}

    if ehLen > 100000 { // sanity check, should be much smaller
        return ErrUnexpectedLengthOfBlock
    }

	ehb := make([]byte, ehLen)
	_, err = in.Read(ehb)
	if err != nil {
		return err
	}

	eh := &pb.EncryptedHeader{}
	err = proto.Unmarshal(ehb, eh)
	if err != nil {
		return err
	}

	key, err := keyOracle.GetPrivateKey(eh)
	if err != nil {
		return err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return err
	}

	iv := make([]byte, aes.BlockSize)
	amt, err := in.Read(iv)
	if amt == len(iv) {
		err = nil // we want to ignore EOF for now
	}
	if err != nil {
		return err
	}
	_, err = io.Copy(out, &cipher.StreamReader{S: cipher.NewCTR(block, iv), R: in})
	if err != nil {
		return err
	}

	return nil
}

func LoadClientConfiguration() (*pb.ClientConfig, error) {
	hd, err := homedir.Dir()
	if err != nil {
		return nil, err
	}
	path := filepath.Join(hd, ".safedump_config")

	confData, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	conf := &pb.ClientConfig{}
	err = proto.UnmarshalText(string(confData), conf)
	if err != nil {
		return nil, err
	}

	return conf, nil
}

func GetPublicKeyIfValidForNow(der []byte, now time.Time) (*rsa.PublicKey, []byte, error) {
	certificate, err := x509.ParseCertificate(der)
	if err != nil {
		return nil, nil, err
	}
	if now.Before(certificate.NotBefore) {
		return nil, nil, ErrCertNotValidBefore
	}
	if now.After(certificate.NotAfter) {
		return nil, nil, ErrCertNotValidAfter
	}
	if certificate.PublicKeyAlgorithm != x509.RSA {
		return nil, nil, ErrCertNotRSA
	}
	rsaPubKey, ok := certificate.PublicKey.(*rsa.PublicKey)
	if !ok {
		return nil, nil, ErrCertWontCast
	}

	spki := sha256.Sum256(certificate.RawSubjectPublicKeyInfo)
	return rsaPubKey, spki[:], nil
}

func GetCurrentCertificate(config *pb.ClientConfig, now time.Time) (*rsa.PublicKey, []byte, error) {
	hd, err := homedir.Dir()
	if err != nil {
		return nil, nil, err
	}
	path := filepath.Join(hd, ".safedump_cached_cert")
	cd, err := ioutil.ReadFile(path)
	if err == nil {
		rv, spki, err := GetPublicKeyIfValidForNow(cd, now)
		if err == nil {
			return rv, spki, nil
		} // else, we'll fetch a new one
	} // else, we'll fetch a new one

	conn, err := CreateGrpcConn(config)
	if err != nil {
		return nil, nil, err
	}
	defer conn.Close()
	client := pb.NewSafeDumpServiceClient(conn)

	resp, err := client.GetPublicCert(context.Background(), &pb.GetPublicCertRequest{})
	if err != nil {
		return nil, nil, err
	}

	rv, spki, err := GetPublicKeyIfValidForNow(resp.Der, now)
	if err != nil {
		return nil, nil, err
	}

	err = ioutil.WriteFile(path, resp.Der, 0644)
	if err != nil {
		return nil, nil, err
	}

	return rv, spki, nil
}

func CreateGrpcConn(config *pb.ClientConfig) (*grpc.ClientConn, error) {
	// Get certs
	var dialOptions []grpc.DialOption
	if config.NoGrpcSecurity {
		// use system CA pool but disable cert validation
		log.Println("WARNING: Disabling TLS authentication when connecting to gRPC server")
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})))
	} else if config.UseSystemCaForGrpc {
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) // uses the system CA pool
	} else {
		// use baked in cert
		cp := x509.NewCertPool()
		if !cp.AppendCertsFromPEM([]byte(config.GrpcCert)) {
			return nil, ErrBadCert
		}
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{RootCAs: cp})))
	}

	conn, err := grpc.Dial(config.GrpcServer, dialOptions...)
	if err != nil {
		return nil, err
	}

	return conn, nil
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"fmt"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwire"
	"github.com/conformal/go-flags"
	"net"
	"os"
	"path/filepath"
	"strings"
)

const (
	defaultCAFilename     = "btcd.cert"
	defaultConfigFilename = "btcwallet.conf"
	defaultBtcNet         = btcwire.TestNet3
	defaultLogLevel       = "info"
)

var (
	btcwalletHomeDir   = btcutil.AppDataDir("btcwallet", false)
	defaultCAFile      = filepath.Join(btcwalletHomeDir, defaultCAFilename)
	defaultConfigFile  = filepath.Join(btcwalletHomeDir, defaultConfigFilename)
	defaultDataDir     = btcwalletHomeDir
	defaultRPCKeyFile  = filepath.Join(btcwalletHomeDir, "rpc.key")
	defaultRPCCertFile = filepath.Join(btcwalletHomeDir, "rpc.cert")
)

type config struct {
	ShowVersion  bool     `short:"V" long:"version" description:"Display version information and exit"`
	CAFile       string   `long:"cafile" description:"File containing root certificates to authenticate a TLS connections with btcd"`
	Connect      string   `short:"c" long:"connect" description:"Server and port of btcd instance to connect to (default localhost:18334, mainnet: localhost:8334)"`
	DebugLevel   string   `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
	ConfigFile   string   `short:"C" long:"configfile" description:"Path to configuration file"`
	SvrListeners []string `long:"listen" description:"Listen for RPC/websocket connections on this interface/port (default port: 18332, mainnet: 8332)"`
	DataDir      string   `short:"D" long:"datadir" description:"Directory to store wallets and transactions"`
	Username     string   `short:"u" long:"username" description:"Username for btcd authorization"`
	Password     string   `short:"P" long:"password" default-mask:"-" description:"Password for btcd authorization"`
	RPCCert      string   `long:"rpccert" description:"File containing the certificate file"`
	RPCKey       string   `long:"rpckey" description:"File containing the certificate key"`
	MainNet      bool     `long:"mainnet" description:"Use the main Bitcoin network (default testnet3)"`
	Proxy        string   `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
	ProxyUser    string   `long:"proxyuser" description:"Username for proxy server"`
	ProxyPass    string   `long:"proxypass" default-mask:"-" description:"Password for proxy server"`
	Profile      string   `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
}

// cleanAndExpandPath expands environement variables and leading ~ in the
// passed path, cleans the result, and returns it.
func cleanAndExpandPath(path string) string {
	// Expand initial ~ to OS specific home directory.
	if strings.HasPrefix(path, "~") {
		homeDir := filepath.Dir(btcwalletHomeDir)
		path = strings.Replace(path, "~", homeDir, 1)
	}

	// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
	// but they variables can still be expanded via POSIX-style $VARIABLE.
	return filepath.Clean(os.ExpandEnv(path))
}

// removeDuplicateAddresses returns a new slice with all duplicate entries in
// addrs removed.
func removeDuplicateAddresses(addrs []string) []string {
	result := make([]string, 0)
	seen := map[string]bool{}
	for _, val := range addrs {
		if _, ok := seen[val]; !ok {
			result = append(result, val)
			seen[val] = true
		}
	}
	return result
}

// normalizeAddresses returns a new slice with all the passed peer addresses
// normalized with the given default port, and all duplicates removed.
func normalizeAddresses(addrs []string, defaultPort string) []string {
	for i, addr := range addrs {
		addrs[i] = normalizeAddress(addr, defaultPort)
	}

	return removeDuplicateAddresses(addrs)
}

// filesExists reports whether the named file or directory exists.
func fileExists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// normalizeAddress returns addr with the passed default port appended if
// there is not already a port specified.
func normalizeAddress(addr, defaultPort string) string {
	_, _, err := net.SplitHostPort(addr)
	if err != nil {
		return net.JoinHostPort(addr, defaultPort)
	}
	return addr
}

// loadConfig initializes and parses the config using a config file and command
// line options.
//
// The configuration proceeds as follows:
//      1) Start with a default config with sane settings
//      2) Pre-parse the command line to check for an alternative config file
//      3) Load configuration file overwriting defaults with any specified options
//      4) Parse CLI options and overwrite/add any specified options
//
// The above results in btcwallet functioning properly without any config
// settings while still allowing the user to override settings with config files
// and command line options.  Command line options always take precedence.
func loadConfig() (*config, []string, error) {
	// Default config.
	cfg := config{
		DebugLevel: defaultLogLevel,
		CAFile:     defaultCAFile,
		ConfigFile: defaultConfigFile,
		DataDir:    defaultDataDir,
		RPCKey:     defaultRPCKeyFile,
		RPCCert:    defaultRPCCertFile,
	}

	// A config file in the current directory takes precedence.
	if fileExists(defaultConfigFilename) {
		cfg.ConfigFile = defaultConfigFile
	}

	// Pre-parse the command line options to see if an alternative config
	// file or the version flag was specified.
	preCfg := cfg
	preParser := flags.NewParser(&preCfg, flags.Default)
	_, err := preParser.Parse()
	if err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			preParser.WriteHelp(os.Stderr)
		}
		return nil, nil, err
	}

	// Show the version and exit if the version flag was specified.
	if preCfg.ShowVersion {
		appName := filepath.Base(os.Args[0])
		appName = strings.TrimSuffix(appName, filepath.Ext(appName))
		fmt.Println(appName, "version", version())
		os.Exit(0)
	}

	// Load additional config from file.
	var configFileError error
	parser := flags.NewParser(&cfg, flags.Default)
	err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
	if err != nil {
		if _, ok := err.(*os.PathError); !ok {
			fmt.Fprintln(os.Stderr, err)
			parser.WriteHelp(os.Stderr)
			return nil, nil, err
		}
		configFileError = err
	}

	// Parse command line options again to ensure they take precedence.
	remainingArgs, err := parser.Parse()
	if err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			parser.WriteHelp(os.Stderr)
		}
		return nil, nil, err
	}

	// Warn about missing config file after the final command line parse
	// succeeds.  This prevents the warning on help messages and invalid
	// options.
	if configFileError != nil {
		log.Warnf("%v", configFileError)
	}

	// Choose the active network params based on the mainnet net flag.
	if cfg.MainNet {
		activeNetParams = netParams(btcwire.MainNet)
	}

	// Validate debug log level
	if !validLogLevel(cfg.DebugLevel) {
		str := "%s: The specified debug level [%v] is invalid"
		err := fmt.Errorf(str, "loadConfig", cfg.DebugLevel)
		fmt.Fprintln(os.Stderr, err)
		parser.WriteHelp(os.Stderr)
		return nil, nil, err
	}

	if cfg.Connect == "" {
		cfg.Connect = activeNetParams.connect
	}

	// Add default port to connect flag if missing.
	cfg.Connect = normalizeAddress(cfg.Connect, activeNetParams.btcdPort)

	if len(cfg.SvrListeners) == 0 {
		addrs, err := net.LookupHost("localhost")
		if err != nil {
			return nil, nil, err
		}
		cfg.SvrListeners = make([]string, 0, len(addrs))
		for _, addr := range addrs {
			addr = net.JoinHostPort(addr, activeNetParams.svrPort)
			cfg.SvrListeners = append(cfg.SvrListeners, addr)
		}
	}

	// Add default port to all rpc listener addresses if needed and remove
	// duplicate addresses.
	cfg.SvrListeners = normalizeAddresses(cfg.SvrListeners,
		activeNetParams.svrPort)

	// Expand environment variable and leading ~ for filepaths.
	cfg.CAFile = cleanAndExpandPath(cfg.CAFile)

	return &cfg, remainingArgs, nil
}

func (c *config) Net() btcwire.BitcoinNet {
	if cfg.MainNet {
		return btcwire.MainNet
	}
	return btcwire.TestNet3
}

// validLogLevel returns whether or not logLevel is a valid debug log level.
func validLogLevel(logLevel string) bool {
	switch logLevel {
	case "trace":
		fallthrough
	case "debug":
		fallthrough
	case "info":
		fallthrough
	case "warn":
		fallthrough
	case "error":
		fallthrough
	case "critical":
		return true
	}
	return false
}
<commit_msg>Use smarter btcd cert path logic.<commit_after>/*
 * Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"fmt"
	"github.com/conformal/btcutil"
	"github.com/conformal/btcwire"
	"github.com/conformal/go-flags"
	"net"
	"os"
	"path/filepath"
	"strings"
)

const (
	defaultCAFilename     = "btcd.cert"
	defaultConfigFilename = "btcwallet.conf"
	defaultBtcNet         = btcwire.TestNet3
	defaultLogLevel       = "info"
)

var (
	btcdHomeDir        = btcutil.AppDataDir("btcd", false)
	btcwalletHomeDir   = btcutil.AppDataDir("btcwallet", false)
	defaultCAFile      = filepath.Join(btcwalletHomeDir, defaultCAFilename)
	btcdHomedirCAFile  = filepath.Join(btcdHomeDir, "rpc.cert")
	defaultConfigFile  = filepath.Join(btcwalletHomeDir, defaultConfigFilename)
	defaultDataDir     = btcwalletHomeDir
	defaultRPCKeyFile  = filepath.Join(btcwalletHomeDir, "rpc.key")
	defaultRPCCertFile = filepath.Join(btcwalletHomeDir, "rpc.cert")
)

type config struct {
	ShowVersion  bool     `short:"V" long:"version" description:"Display version information and exit"`
	CAFile       string   `long:"cafile" description:"File containing root certificates to authenticate a TLS connections with btcd"`
	Connect      string   `short:"c" long:"connect" description:"Server and port of btcd instance to connect to (default localhost:18334, mainnet: localhost:8334)"`
	DebugLevel   string   `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
	ConfigFile   string   `short:"C" long:"configfile" description:"Path to configuration file"`
	SvrListeners []string `long:"listen" description:"Listen for RPC/websocket connections on this interface/port (default port: 18332, mainnet: 8332)"`
	DataDir      string   `short:"D" long:"datadir" description:"Directory to store wallets and transactions"`
	Username     string   `short:"u" long:"username" description:"Username for btcd authorization"`
	Password     string   `short:"P" long:"password" default-mask:"-" description:"Password for btcd authorization"`
	RPCCert      string   `long:"rpccert" description:"File containing the certificate file"`
	RPCKey       string   `long:"rpckey" description:"File containing the certificate key"`
	MainNet      bool     `long:"mainnet" description:"Use the main Bitcoin network (default testnet3)"`
	Proxy        string   `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
	ProxyUser    string   `long:"proxyuser" description:"Username for proxy server"`
	ProxyPass    string   `long:"proxypass" default-mask:"-" description:"Password for proxy server"`
	Profile      string   `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
}

// cleanAndExpandPath expands environement variables and leading ~ in the
// passed path, cleans the result, and returns it.
func cleanAndExpandPath(path string) string {
	// Expand initial ~ to OS specific home directory.
	if strings.HasPrefix(path, "~") {
		homeDir := filepath.Dir(btcwalletHomeDir)
		path = strings.Replace(path, "~", homeDir, 1)
	}

	// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
	// but they variables can still be expanded via POSIX-style $VARIABLE.
	return filepath.Clean(os.ExpandEnv(path))
}

// removeDuplicateAddresses returns a new slice with all duplicate entries in
// addrs removed.
func removeDuplicateAddresses(addrs []string) []string {
	result := make([]string, 0)
	seen := map[string]bool{}
	for _, val := range addrs {
		if _, ok := seen[val]; !ok {
			result = append(result, val)
			seen[val] = true
		}
	}
	return result
}

// normalizeAddresses returns a new slice with all the passed peer addresses
// normalized with the given default port, and all duplicates removed.
func normalizeAddresses(addrs []string, defaultPort string) []string {
	for i, addr := range addrs {
		addrs[i] = normalizeAddress(addr, defaultPort)
	}

	return removeDuplicateAddresses(addrs)
}

// filesExists reports whether the named file or directory exists.
func fileExists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// normalizeAddress returns addr with the passed default port appended if
// there is not already a port specified.
func normalizeAddress(addr, defaultPort string) string {
	_, _, err := net.SplitHostPort(addr)
	if err != nil {
		return net.JoinHostPort(addr, defaultPort)
	}
	return addr
}

// loadConfig initializes and parses the config using a config file and command
// line options.
//
// The configuration proceeds as follows:
//      1) Start with a default config with sane settings
//      2) Pre-parse the command line to check for an alternative config file
//      3) Load configuration file overwriting defaults with any specified options
//      4) Parse CLI options and overwrite/add any specified options
//
// The above results in btcwallet functioning properly without any config
// settings while still allowing the user to override settings with config files
// and command line options.  Command line options always take precedence.
func loadConfig() (*config, []string, error) {
	// Default config.
	cfg := config{
		DebugLevel: defaultLogLevel,
		ConfigFile: defaultConfigFile,
		DataDir:    defaultDataDir,
		RPCKey:     defaultRPCKeyFile,
		RPCCert:    defaultRPCCertFile,
	}

	// A config file in the current directory takes precedence.
	if fileExists(defaultConfigFilename) {
		cfg.ConfigFile = defaultConfigFile
	}

	// Pre-parse the command line options to see if an alternative config
	// file or the version flag was specified.
	preCfg := cfg
	preParser := flags.NewParser(&preCfg, flags.Default)
	_, err := preParser.Parse()
	if err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			preParser.WriteHelp(os.Stderr)
		}
		return nil, nil, err
	}

	// Show the version and exit if the version flag was specified.
	if preCfg.ShowVersion {
		appName := filepath.Base(os.Args[0])
		appName = strings.TrimSuffix(appName, filepath.Ext(appName))
		fmt.Println(appName, "version", version())
		os.Exit(0)
	}

	// Load additional config from file.
	var configFileError error
	parser := flags.NewParser(&cfg, flags.Default)
	err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
	if err != nil {
		if _, ok := err.(*os.PathError); !ok {
			fmt.Fprintln(os.Stderr, err)
			parser.WriteHelp(os.Stderr)
			return nil, nil, err
		}
		configFileError = err
	}

	// Parse command line options again to ensure they take precedence.
	remainingArgs, err := parser.Parse()
	if err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			parser.WriteHelp(os.Stderr)
		}
		return nil, nil, err
	}

	// Warn about missing config file after the final command line parse
	// succeeds.  This prevents the warning on help messages and invalid
	// options.
	if configFileError != nil {
		log.Warnf("%v", configFileError)
	}

	// Choose the active network params based on the mainnet net flag.
	if cfg.MainNet {
		activeNetParams = netParams(btcwire.MainNet)
	}

	// Validate debug log level
	if !validLogLevel(cfg.DebugLevel) {
		str := "%s: The specified debug level [%v] is invalid"
		err := fmt.Errorf(str, "loadConfig", cfg.DebugLevel)
		fmt.Fprintln(os.Stderr, err)
		parser.WriteHelp(os.Stderr)
		return nil, nil, err
	}

	if cfg.Connect == "" {
		cfg.Connect = activeNetParams.connect
	}

	// Add default port to connect flag if missing.
	cfg.Connect = normalizeAddress(cfg.Connect, activeNetParams.btcdPort)

	// If CAFile is unset, choose either the copy or local btcd cert.
	if cfg.CAFile == "" {
		cfg.CAFile = defaultCAFile

		// If the CA copy does not exist, check if we're connecting to
		// a local btcd and switch to its RPC cert if it exists.
		if !fileExists(cfg.CAFile) {
			host, _, err := net.SplitHostPort(cfg.Connect)
			if err != nil {
				return nil, nil, err
			}
			switch host {
			case "localhost":
				fallthrough

			case "127.0.0.1":
				fallthrough

			case "::1":
				if fileExists(btcdHomedirCAFile) {
					cfg.CAFile = btcdHomedirCAFile
				}
			}
		}
	}

	if len(cfg.SvrListeners) == 0 {
		addrs, err := net.LookupHost("localhost")
		if err != nil {
			return nil, nil, err
		}
		cfg.SvrListeners = make([]string, 0, len(addrs))
		for _, addr := range addrs {
			addr = net.JoinHostPort(addr, activeNetParams.svrPort)
			cfg.SvrListeners = append(cfg.SvrListeners, addr)
		}
	}

	// Add default port to all rpc listener addresses if needed and remove
	// duplicate addresses.
	cfg.SvrListeners = normalizeAddresses(cfg.SvrListeners,
		activeNetParams.svrPort)

	// Expand environment variable and leading ~ for filepaths.
	cfg.CAFile = cleanAndExpandPath(cfg.CAFile)

	return &cfg, remainingArgs, nil
}

func (c *config) Net() btcwire.BitcoinNet {
	if cfg.MainNet {
		return btcwire.MainNet
	}
	return btcwire.TestNet3
}

// validLogLevel returns whether or not logLevel is a valid debug log level.
func validLogLevel(logLevel string) bool {
	switch logLevel {
	case "trace":
		fallthrough
	case "debug":
		fallthrough
	case "info":
		fallthrough
	case "warn":
		fallthrough
	case "error":
		fallthrough
	case "critical":
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>
#include <git2/errors.h>
*/
import "C"
import (
	"runtime"
	"unsafe"
)

type ConfigLevel int

const (
	// System-wide configuration file; /etc/gitconfig on Linux systems
	ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM

	// XDG compatible configuration file; typically ~/.config/git/config
	ConfigLevelXDG ConfigLevel = C.GIT_CONFIG_LEVEL_XDG

	// User-specific configuration file (also called Global configuration
	// file); typically ~/.gitconfig
	ConfigLevelGlobal ConfigLevel = C.GIT_CONFIG_LEVEL_GLOBAL

	// Repository specific configuration file; $WORK_DIR/.git/config on
	// non-bare repos
	ConfigLevelLocal ConfigLevel = C.GIT_CONFIG_LEVEL_LOCAL

	// Application specific configuration file; freely defined by applications
	ConfigLevelApp ConfigLevel = C.GIT_CONFIG_LEVEL_APP

	// Represents the highest level available config file (i.e. the most
	// specific config file available that actually is loaded)
	ConfigLevelHighest ConfigLevel = C.GIT_CONFIG_HIGHEST_LEVEL
)

type ConfigEntry struct {
	Name string
	Value string
	Level ConfigLevel
}

func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry {
	return &ConfigEntry{
		Name: C.GoString(centry.name),
		Value: C.GoString(centry.value),
		Level: ConfigLevel(centry.level),
	}
}

type Config struct {
	ptr *C.git_config
}

// NewConfig creates a new empty configuration object
func NewConfig() (*Config, error) {
	config := new(Config)

	ret := C.git_config_new(&config.ptr)
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// AddFile adds a file-backed backend to the config object at the specified level.
func (c *Config) AddFile(path string, level ConfigLevel, force bool) error {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) LookupInt32(name string) (int32, error) {
	var out C.int32_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_int32(&out, c.ptr, cname)
	if ret < 0 {
		return 0, LastError()
	}

	return int32(out), nil
}

func (c *Config) LookupInt64(name string) (int64, error) {
	var out C.int64_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_int64(&out, c.ptr, cname)
	if ret < 0 {
		return 0, LastError()
	}

	return int64(out), nil
}

func (c *Config) LookupString(name string) (string, error) {
	var ptr *C.char
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_string(&ptr, c.ptr, cname)
	if ret < 0 {
		return "", LastError()
	}

	return C.GoString(ptr), nil
}

func (c *Config) LookupBool(name string) (bool, error) {
	var out C.int
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_get_bool(&out, c.ptr, cname)
	if ret < 0 {
		return false, LastError()
	}

	return out != 0, nil
}

func (c *Config) NewMultivarIterator(name, regexp string) (*ConfigIterator, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	var cregexp *C.char
	if regexp == "" {
		cregexp = nil
	} else {
		cregexp = C.CString(regexp)
		defer C.free(unsafe.Pointer(cregexp))
	}

	iter := new(ConfigIterator)
	ret := C.git_config_multivar_iterator_new(&iter.ptr, c.ptr, cname, cregexp)
	if ret < 0 {
		return nil, LastError()
	}

	runtime.SetFinalizer(iter, (*ConfigIterator).Free)
	return iter, nil
}

// NewIterator creates an iterator over each entry in the
// configuration
func (c *Config) NewIterator() (*ConfigIterator, error) {
	iter := new(ConfigIterator)
	ret := C.git_config_iterator_new(&iter.ptr, c.ptr)
	if ret < 0 {
		return nil, LastError()
	}

	return iter, nil
}

// NewIteratorGlob creates an iterator over each entry in the
// configuration whose name matches the given regular expression
func (c *Config) NewIteratorGlob(regexp string) (*ConfigIterator, error) {
	iter := new(ConfigIterator)
	cregexp := C.CString(regexp)
	defer C.free(unsafe.Pointer(cregexp))

	ret := C.git_config_iterator_glob_new(&iter.ptr, c.ptr, cregexp)
	if ret < 0 {
		return nil, LastError()
	}

	return iter, nil
}

func (c *Config) SetString(name, value string) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_set_string(c.ptr, cname, cvalue)
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) Free() {
	runtime.SetFinalizer(c, nil)
	C.git_config_free(c.ptr)
}

func (c *Config) SetInt32(name string, value int32) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_int32(c.ptr, cname, C.int32_t(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetInt64(name string, value int64) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_int64(c.ptr, cname, C.int64_t(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetBool(name string, value bool) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_bool(c.ptr, cname, cbool(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetMultivar(name, regexp, value string) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	cregexp := C.CString(regexp)
	defer C.free(unsafe.Pointer(cregexp))

	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))

	ret := C.git_config_set_multivar(c.ptr, cname, cregexp, cvalue)
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) Delete(name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_delete_entry(c.ptr, cname)

	if ret < 0 {
		return LastError()
	}

	return nil
}

// OpenLevel creates a single-level focused config object from a multi-level one
func (c *Config) OpenLevel(parent *Config, level ConfigLevel) (*Config, error) {
	config := new(Config)
	ret := C.git_config_open_level(&config.ptr, parent.ptr, C.git_config_level_t(level))
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// OpenOndisk creates a new config instance containing a single on-disk file
func OpenOndisk(parent *Config, path string) (*Config, error) {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	config := new(Config)
	ret := C.git_config_open_ondisk(&config.ptr, cpath)
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// Refresh refreshes the configuration to reflect any changes made externally e.g. on disk
func (c *Config) Refresh() error {
	ret := C.git_config_refresh(c.ptr)
	if ret < 0 {
		return LastError()
	}

	return nil
}

type ConfigIterator struct {
	ptr *C.git_config_iterator
}

// Next returns the next entry for this iterator
func (iter *ConfigIterator) Next() (*ConfigEntry, error) {
	var centry *C.git_config_entry

	ret := C.git_config_next(&centry, iter.ptr)
	if ret < 0 {
		return nil, LastError()
	}

	return newConfigEntryFromC(centry), nil
}

func (iter *ConfigIterator) Free() {
	runtime.SetFinalizer(iter, nil)
	C.free(unsafe.Pointer(iter.ptr))
}

<commit_msg>Lock the thread so we can get the error message<commit_after>package git

/*
#include <git2.h>
#include <git2/errors.h>
*/
import "C"
import (
	"runtime"
	"unsafe"
)

type ConfigLevel int

const (
	// System-wide configuration file; /etc/gitconfig on Linux systems
	ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM

	// XDG compatible configuration file; typically ~/.config/git/config
	ConfigLevelXDG ConfigLevel = C.GIT_CONFIG_LEVEL_XDG

	// User-specific configuration file (also called Global configuration
	// file); typically ~/.gitconfig
	ConfigLevelGlobal ConfigLevel = C.GIT_CONFIG_LEVEL_GLOBAL

	// Repository specific configuration file; $WORK_DIR/.git/config on
	// non-bare repos
	ConfigLevelLocal ConfigLevel = C.GIT_CONFIG_LEVEL_LOCAL

	// Application specific configuration file; freely defined by applications
	ConfigLevelApp ConfigLevel = C.GIT_CONFIG_LEVEL_APP

	// Represents the highest level available config file (i.e. the most
	// specific config file available that actually is loaded)
	ConfigLevelHighest ConfigLevel = C.GIT_CONFIG_HIGHEST_LEVEL
)

type ConfigEntry struct {
	Name string
	Value string
	Level ConfigLevel
}

func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry {
	return &ConfigEntry{
		Name: C.GoString(centry.name),
		Value: C.GoString(centry.value),
		Level: ConfigLevel(centry.level),
	}
}

type Config struct {
	ptr *C.git_config
}

// NewConfig creates a new empty configuration object
func NewConfig() (*Config, error) {
	config := new(Config)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if ret := C.git_config_new(&config.ptr); ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// AddFile adds a file-backed backend to the config object at the specified level.
func (c *Config) AddFile(path string, level ConfigLevel, force bool) error {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) LookupInt32(name string) (int32, error) {
	var out C.int32_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_int32(&out, c.ptr, cname)
	if ret < 0 {
		return 0, LastError()
	}

	return int32(out), nil
}

func (c *Config) LookupInt64(name string) (int64, error) {
	var out C.int64_t
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_int64(&out, c.ptr, cname)
	if ret < 0 {
		return 0, LastError()
	}

	return int64(out), nil
}

func (c *Config) LookupString(name string) (string, error) {
	var ptr *C.char
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if ret := C.git_config_get_string(&ptr, c.ptr, cname); ret < 0 {
		return "", LastError()
	}

	return C.GoString(ptr), nil
}

func (c *Config) LookupBool(name string) (bool, error) {
	var out C.int
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_get_bool(&out, c.ptr, cname)
	if ret < 0 {
		return false, LastError()
	}

	return out != 0, nil
}

func (c *Config) NewMultivarIterator(name, regexp string) (*ConfigIterator, error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	var cregexp *C.char
	if regexp == "" {
		cregexp = nil
	} else {
		cregexp = C.CString(regexp)
		defer C.free(unsafe.Pointer(cregexp))
	}

	iter := new(ConfigIterator)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_multivar_iterator_new(&iter.ptr, c.ptr, cname, cregexp)
	if ret < 0 {
		return nil, LastError()
	}

	runtime.SetFinalizer(iter, (*ConfigIterator).Free)
	return iter, nil
}

// NewIterator creates an iterator over each entry in the
// configuration
func (c *Config) NewIterator() (*ConfigIterator, error) {
	iter := new(ConfigIterator)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_iterator_new(&iter.ptr, c.ptr)
	if ret < 0 {
		return nil, LastError()
	}

	return iter, nil
}

// NewIteratorGlob creates an iterator over each entry in the
// configuration whose name matches the given regular expression
func (c *Config) NewIteratorGlob(regexp string) (*ConfigIterator, error) {
	iter := new(ConfigIterator)
	cregexp := C.CString(regexp)
	defer C.free(unsafe.Pointer(cregexp))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_iterator_glob_new(&iter.ptr, c.ptr, cregexp)
	if ret < 0 {
		return nil, LastError()
	}

	return iter, nil
}

func (c *Config) SetString(name, value string) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_set_string(c.ptr, cname, cvalue)
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) Free() {
	runtime.SetFinalizer(c, nil)
	C.git_config_free(c.ptr)
}

func (c *Config) SetInt32(name string, value int32) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	ret := C.git_config_set_int32(c.ptr, cname, C.int32_t(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetInt64(name string, value int64) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_set_int64(c.ptr, cname, C.int64_t(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetBool(name string, value bool) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_set_bool(c.ptr, cname, cbool(value))
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) SetMultivar(name, regexp, value string) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	cregexp := C.CString(regexp)
	defer C.free(unsafe.Pointer(cregexp))

	cvalue := C.CString(value)
	defer C.free(unsafe.Pointer(cvalue))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_set_multivar(c.ptr, cname, cregexp, cvalue)
	if ret < 0 {
		return LastError()
	}

	return nil
}

func (c *Config) Delete(name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_delete_entry(c.ptr, cname)

	if ret < 0 {
		return LastError()
	}

	return nil
}

// OpenLevel creates a single-level focused config object from a multi-level one
func (c *Config) OpenLevel(parent *Config, level ConfigLevel) (*Config, error) {
	config := new(Config)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_config_open_level(&config.ptr, parent.ptr, C.git_config_level_t(level))
	if ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// OpenOndisk creates a new config instance containing a single on-disk file
func OpenOndisk(parent *Config, path string) (*Config, error) {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	config := new(Config)

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if ret := C.git_config_open_ondisk(&config.ptr, cpath); ret < 0 {
		return nil, LastError()
	}

	return config, nil
}

// Refresh refreshes the configuration to reflect any changes made externally e.g. on disk
func (c *Config) Refresh() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if ret := C.git_config_refresh(c.ptr); ret < 0 {
		return LastError()
	}

	return nil
}

type ConfigIterator struct {
	ptr *C.git_config_iterator
}

// Next returns the next entry for this iterator
func (iter *ConfigIterator) Next() (*ConfigEntry, error) {
	var centry *C.git_config_entry

	ret := C.git_config_next(&centry, iter.ptr)
	if ret < 0 {
		return nil, LastError()
	}

	return newConfigEntryFromC(centry), nil
}

func (iter *ConfigIterator) Free() {
	runtime.SetFinalizer(iter, nil)
	C.free(unsafe.Pointer(iter.ptr))
}

<|endoftext|>


<commit_before>//Package goconfig uses a struct as input and populates the
//fields of this struct with parameters fom command
//line, environment variables and configuration file.
package goconfig

import (
	"errors"
	"fmt"
	"path"

	"path/filepath"

	"github.com/crgimenes/goconfig/goenv"
	"github.com/crgimenes/goconfig/goflags"
)

// Tag to set main name of field
var Tag = "cfg"

// TagDefault to set default value
var TagDefault = "cfgDefault"

// Path sets default config path
var Path string

// File name of default config file
var File string

// FileRequired config file required
var FileRequired bool

// HelpString temporarily saves help
var HelpString string

// PrefixFlag is a string that would be placed at the beginning of the generated Flag tags.
var PrefixFlag string

// PrefixEnv is a string that would be placed at the beginning of the generated Event tags.
var PrefixEnv string

// ErrFileFormatNotDefined Is the error that is returned when there is no defined configuration file format.
var ErrFileFormatNotDefined = errors.New("file format not defined")

//Usage is a function to show the help, can be replaced by your own version.
var Usage func()

// Fileformat struct holds the functions to Load the file containing the settings
type Fileformat struct {
	Extension   string
	Load        func(config interface{}) (err error)
	PrepareHelp func(config interface{}) (help string, err error)
}

// Formats is the list of registered formats.
var Formats []Fileformat

func findFileFormat(extension string) (format Fileformat, err error) {
	format = Fileformat{}
	for _, f := range Formats {
		if f.Extension == extension {
			format = f
			return
		}
	}
	err = ErrFileFormatNotDefined
	return
}

func init() {
	Usage = DefaultUsage
	Path = "./"
	File = ""
	FileRequired = false
}

// Parse configuration
func Parse(config interface{}) (err error) {
	ext := path.Ext(File)
	if ext != "" {
		var format Fileformat
		format, err = findFileFormat(ext)
		if err != nil {
			return
		}
		err = format.Load(config)
		if err != nil {
			return
		}
		HelpString, err = format.PrepareHelp(config)
		if err != nil {
			return
		}
	}

	goenv.Prefix = PrefixEnv
	goenv.Setup(Tag, TagDefault)
	err = goenv.Parse(config)
	if err != nil {
		return
	}

	goflags.Prefix = PrefixFlag
	goflags.Setup(Tag, TagDefault)
	goflags.Usage = Usage
	goflags.Preserve = true
	err = goflags.Parse(config)
	if err != nil {
		return
	}

	return
}

// PrintDefaults print the default help
func PrintDefaults() {
	if File != "" {
		fmt.Printf("Config file %q:\n", filepath.Join(Path, File))
		fmt.Println(HelpString)
	}
}

// DefaultUsage is assigned for Usage function by default
func DefaultUsage() {
	fmt.Println("Usage")
	goflags.PrintDefaults()
	goenv.PrintDefaults()
	PrintDefaults()
}
<commit_msg>Add validate tag<commit_after>//Package goconfig uses a struct as input and populates the
//fields of this struct with parameters fom command
//line, environment variables and configuration file.
package goconfig

import (
	"errors"
	"fmt"
	"path"

	"path/filepath"

	"github.com/crgimenes/goconfig/goenv"
	"github.com/crgimenes/goconfig/goflags"
	"github.com/crgimenes/goconfig/validate"
)

// Tag to set main name of field
var Tag = "cfg"

// TagDefault to set default value
var TagDefault = "cfgDefault"

// Path sets default config path
var Path string

// File name of default config file
var File string

// FileRequired config file required
var FileRequired bool

// HelpString temporarily saves help
var HelpString string

// PrefixFlag is a string that would be placed at the beginning of the generated Flag tags.
var PrefixFlag string

// PrefixEnv is a string that would be placed at the beginning of the generated Event tags.
var PrefixEnv string

// ErrFileFormatNotDefined Is the error that is returned when there is no defined configuration file format.
var ErrFileFormatNotDefined = errors.New("file format not defined")

//Usage is a function to show the help, can be replaced by your own version.
var Usage func()

// Fileformat struct holds the functions to Load the file containing the settings
type Fileformat struct {
	Extension   string
	Load        func(config interface{}) (err error)
	PrepareHelp func(config interface{}) (help string, err error)
}

// Formats is the list of registered formats.
var Formats []Fileformat

func findFileFormat(extension string) (format Fileformat, err error) {
	format = Fileformat{}
	for _, f := range Formats {
		if f.Extension == extension {
			format = f
			return
		}
	}
	err = ErrFileFormatNotDefined
	return
}

func init() {
	Usage = DefaultUsage
	Path = "./"
	File = ""
	FileRequired = false
}

// Parse configuration
func Parse(config interface{}) (err error) {
	ext := path.Ext(File)
	if ext != "" {
		var format Fileformat
		format, err = findFileFormat(ext)
		if err != nil {
			return
		}
		err = format.Load(config)
		if err != nil {
			return
		}
		HelpString, err = format.PrepareHelp(config)
		if err != nil {
			return
		}
	}

	goenv.Prefix = PrefixEnv
	goenv.Setup(Tag, TagDefault)
	err = goenv.Parse(config)
	if err != nil {
		return
	}

	goflags.Prefix = PrefixFlag
	goflags.Setup(Tag, TagDefault)
	goflags.Usage = Usage
	goflags.Preserve = true
	err = goflags.Parse(config)
	if err != nil {
		return
	}

	validate.Prefix = PrefixFlag
	validate.Setup(Tag, TagDefault)
	err = validate.Parse(config)

	return
}

// PrintDefaults print the default help
func PrintDefaults() {
	if File != "" {
		fmt.Printf("Config file %q:\n", filepath.Join(Path, File))
		fmt.Println(HelpString)
	}
}

// DefaultUsage is assigned for Usage function by default
func DefaultUsage() {
	fmt.Println("Usage")
	goflags.PrintDefaults()
	goenv.PrintDefaults()
	PrintDefaults()
}
<|endoftext|>


<commit_before>package libkb

import (
	"github.com/keybase/go-jsonw"
)

type JsonConfigFile struct {
	JsonFile
}

type JsonConfigAdjuster struct {
	file *JsonConfigFile
}

func NewJsonConfigFile(s string) *JsonConfigFile {
	return &JsonConfigFile{*NewJsonFile(s, "config")}
}

func NewJsonConfigAdjuster(f *JsonConfigFile) *JsonConfigAdjuster {
	return &JsonConfigAdjuster{f}
}

type valueGetter func(*jsonw.Wrapper) (interface{}, error)

func (f JsonConfigFile) getValueAtPath(
	p string, getter valueGetter) (ret interface{}, is_set bool) {
	is_set = false
	if f.jw != nil {
		var err error
		ret, err = getter(f.jw.AtPath(p))
		if err == nil {
			is_set = true
		}
	}
	return
}

func getString(w *jsonw.Wrapper) (interface{}, error) {
	return w.GetString()
}

func getBool(w *jsonw.Wrapper) (interface{}, error) {
	return w.GetBool()
}

func getInt(w *jsonw.Wrapper) (interface{}, error) {
	return w.GetInt()
}

func (f JsonConfigFile) GetStringAtPath(p string) (ret string, is_set bool) {
	i, is_set := f.getValueAtPath(p, getString)
	ret = i.(string)
	return
}

func (f JsonConfigFile) GetBoolAtPath(p string) (ret bool, is_set bool) {
	i, is_set := f.getValueAtPath(p, getBool)
	ret = i.(bool)
	return
}

func (f JsonConfigFile) GetIntAtPath(p string) (ret int, is_set bool) {
	i, is_set := f.getValueAtPath(p, getInt)
	ret = i.(int)
	return
}

func (f JsonConfigFile) GetNullAtPath(p string) (is_set bool) {
	is_set = false
	if f.jw != nil {
		w := f.jw.AtPath(p)
		is_set = w.IsNil() && w.Error() == nil
	}
	return
}

func (f JsonConfigFile) GetTopLevelString(s string) (ret string) {
	var e error
	if f.jw != nil {
		f.jw.AtKey(s).GetStringVoid(&ret, &e)
		G.Log.Debug("Config: mapping %s -> %s", s, ret)
	}
	return
}

func (f JsonConfigFile) GetTopLevelBool(s string) (res bool, is_set bool) {
	is_set = false
	res = false
	if f.jw != nil {
		if w := f.jw.AtKey(s); !w.IsNil() {
			is_set = true
			var e error
			w.GetBoolVoid(&res, &e)
		}
	}
	return
}

func (f *JsonConfigFile) UserDict() *jsonw.Wrapper {
	if f.jw == nil {
		f.jw = jsonw.NewDictionary()
	}
	if f.jw.AtKey("user").IsNil() {
		f.jw.SetKey("user", jsonw.NewDictionary())
	}
	return f.jw.AtKey("user")
}

func (f *JsonConfigFile) setValueAtPath(
	p string, getter valueGetter, v interface{}) (err error) {
	existing, err := getter(f.jw.AtPath(p))

	if err != nil || existing != v {
		err = f.jw.SetValueAtPath(p, jsonw.NewWrapper(v))
		if err == nil {
			f.dirty = true
		}
	}
	return
}

func (f *JsonConfigFile) SetStringAtPath(p string, v string) (err error) {
	return f.setValueAtPath(p, getString, v)
}

func (f *JsonConfigFile) SetBoolAtPath(p string, v bool) (err error) {
	return f.setValueAtPath(p, getBool, v)
}

func (f *JsonConfigFile) SetIntAtPath(p string, v int) (err error) {
	return f.setValueAtPath(p, getInt, v)
}

func (f *JsonConfigFile) SetNullAtPath(p string) (err error) {
	existing := f.jw.AtPath(p)

	if !existing.IsNil() || existing.Error() != nil {
		err = f.jw.SetValueAtPath(p, jsonw.NewNil())
		if err == nil {
			f.dirty = true
		}
	}
	return
}

func (f *JsonConfigFile) SetUsername(s string) {
	f.SetUserField("name", s)
}

func (f *JsonConfigFile) SetUid(s string) {
	f.SetUserField("id", s)
}

func (f *JsonConfigFile) SetSalt(s string) {
	f.SetUserField("salt", s)
}

func (f *JsonConfigFile) SetUserField(k, v string) {
	existing := f.GetUserField(k)
	if existing != v {
		f.UserDict().SetKey(k, jsonw.NewString(v))
		f.dirty = true
	}
}

func (f *JsonConfigFile) DeleteAtPath(p string) {
	f.jw.DeleteValueAtPath(p)
	f.dirty = true
}

func (f *JsonConfigFile) Reset() {
	f.jw = jsonw.NewDictionary()
	f.dirty = true
}

func (f *JsonConfigFile) Write() error {
	return f.MaybeSave(true, 0)
}

func (f JsonConfigFile) GetUserField(s string) string {
	var ret string
	var err error
	if f.jw != nil {
		ret, err = f.jw.AtKey("user").AtKey(s).GetString()
		if err != nil {
			ret = ""
		}
	}
	G.Log.Debug("Config: mapping user.%s-> %s", s, ret)
	return ret
}

func (f JsonConfigFile) GetHome() (ret string) {
	return f.GetTopLevelString("home")
}
func (f JsonConfigFile) GetServerUri() (ret string) {
	return f.GetTopLevelString("server")
}
func (f JsonConfigFile) GetConfigFilename() (ret string) {
	return f.GetTopLevelString("config")
}
func (f JsonConfigFile) GetSessionFilename() (ret string) {
	return f.GetTopLevelString("session")
}
func (f JsonConfigFile) GetDbFilename() (ret string) {
	return f.GetTopLevelString("db")
}
func (f JsonConfigFile) GetUsername() string {
	return f.GetUserField("name")
}
func (f JsonConfigFile) GetSalt() string {
	return f.GetUserField("salt")
}
func (f JsonConfigFile) GetUid() string {
	return f.GetUserField("id")
}
func (f JsonConfigFile) GetEmail() (ret string) {
	return f.GetTopLevelString("email")
}
func (f JsonConfigFile) GetProxy() (ret string) {
	return f.GetTopLevelString("proxy")
}
func (f JsonConfigFile) GetDebug() (bool, bool) {
	return f.GetTopLevelBool("debug")
}
func (f JsonConfigFile) GetPlainLogging() (bool, bool) {
	return f.GetTopLevelBool("plain_logging")
}
func (f JsonConfigFile) GetUserCacheSize() (ret int, ok bool) {
	if f.jw != nil {
		ret, ok = f.jw.AtPathGetInt("cache.limits.users")
	} else {
		ok = false
	}
	return
}

func (f JsonConfigFile) GetMerkleKeyFingerprints() []string {
	if v, err := f.jw.AtKey("keys").AtKey("merkle").ToArray(); err != nil || v == nil {
		return nil
	} else if l, err := v.Len(); err != nil {
		return nil
	} else if l == 0 {
		return make([]string, 0, 0)
	} else {
		ret := make([]string, l, 0)
		for i := 0; i < l; i++ {
			if s, err := v.AtIndex(i).GetString(); err != nil {
				return nil
			} else {
				ret = append(ret, s)
			}
		}
		return ret
	}
}

func (f JsonConfigFile) GetPgpDir() (ret string) {
	ret = f.GetTopLevelString("pgpdir")
	if len(ret) == 0 {
		ret = f.GetTopLevelString("gpgdir")
	}
	if len(ret) == 0 {
		ret = f.GetTopLevelString("gnupgdir")
	}
	return ret
}

func (f JsonConfigFile) GetBundledCA(host string) (ret string) {

	if f.jw != nil {
		var err error
		f.jw.AtKey("bundled_CAs").AtKey(host).GetStringVoid(&ret, &err)
		if err == nil {
			G.Log.Debug("Read bundled CA for %s", host)
		}
	}
	return ret
}
<commit_msg>fix a crasher with a config file-not-found issue<commit_after>package libkb

import (
	"github.com/keybase/go-jsonw"
)

type JsonConfigFile struct {
	JsonFile
}

type JsonConfigAdjuster struct {
	file *JsonConfigFile
}

func NewJsonConfigFile(s string) *JsonConfigFile {
	return &JsonConfigFile{*NewJsonFile(s, "config")}
}

func NewJsonConfigAdjuster(f *JsonConfigFile) *JsonConfigAdjuster {
	return &JsonConfigAdjuster{f}
}

type valueGetter func(*jsonw.Wrapper) (interface{}, error)

func (f JsonConfigFile) getValueAtPath(
	p string, getter valueGetter) (ret interface{}, is_set bool) {
	is_set = false
	if f.jw != nil {
		var err error
		ret, err = getter(f.jw.AtPath(p))
		if err == nil {
			is_set = true
		}
	}
	return
}

func getString(w *jsonw.Wrapper) (interface{}, error) {
	return w.GetString()
}

func getBool(w *jsonw.Wrapper) (interface{}, error) {
	return w.GetBool()
}

func getInt(w *jsonw.Wrapper) (interface{}, error) {
	return w.GetInt()
}

func (f JsonConfigFile) GetStringAtPath(p string) (ret string, is_set bool) {
	i, is_set := f.getValueAtPath(p, getString)
	ret = i.(string)
	return
}

func (f JsonConfigFile) GetBoolAtPath(p string) (ret bool, is_set bool) {
	i, is_set := f.getValueAtPath(p, getBool)
	ret = i.(bool)
	return
}

func (f JsonConfigFile) GetIntAtPath(p string) (ret int, is_set bool) {
	i, is_set := f.getValueAtPath(p, getInt)
	ret = i.(int)
	return
}

func (f JsonConfigFile) GetNullAtPath(p string) (is_set bool) {
	is_set = false
	if f.jw != nil {
		w := f.jw.AtPath(p)
		is_set = w.IsNil() && w.Error() == nil
	}
	return
}

func (f JsonConfigFile) GetTopLevelString(s string) (ret string) {
	var e error
	if f.jw != nil {
		f.jw.AtKey(s).GetStringVoid(&ret, &e)
		G.Log.Debug("Config: mapping %s -> %s", s, ret)
	}
	return
}

func (f JsonConfigFile) GetTopLevelBool(s string) (res bool, is_set bool) {
	is_set = false
	res = false
	if f.jw != nil {
		if w := f.jw.AtKey(s); !w.IsNil() {
			is_set = true
			var e error
			w.GetBoolVoid(&res, &e)
		}
	}
	return
}

func (f *JsonConfigFile) UserDict() *jsonw.Wrapper {
	if f.jw == nil {
		f.jw = jsonw.NewDictionary()
	}
	if f.jw.AtKey("user").IsNil() {
		f.jw.SetKey("user", jsonw.NewDictionary())
	}
	return f.jw.AtKey("user")
}

func (f *JsonConfigFile) setValueAtPath(
	p string, getter valueGetter, v interface{}) (err error) {
	existing, err := getter(f.jw.AtPath(p))

	if err != nil || existing != v {
		err = f.jw.SetValueAtPath(p, jsonw.NewWrapper(v))
		if err == nil {
			f.dirty = true
		}
	}
	return
}

func (f *JsonConfigFile) SetStringAtPath(p string, v string) (err error) {
	return f.setValueAtPath(p, getString, v)
}

func (f *JsonConfigFile) SetBoolAtPath(p string, v bool) (err error) {
	return f.setValueAtPath(p, getBool, v)
}

func (f *JsonConfigFile) SetIntAtPath(p string, v int) (err error) {
	return f.setValueAtPath(p, getInt, v)
}

func (f *JsonConfigFile) SetNullAtPath(p string) (err error) {
	existing := f.jw.AtPath(p)

	if !existing.IsNil() || existing.Error() != nil {
		err = f.jw.SetValueAtPath(p, jsonw.NewNil())
		if err == nil {
			f.dirty = true
		}
	}
	return
}

func (f *JsonConfigFile) SetUsername(s string) {
	f.SetUserField("name", s)
}

func (f *JsonConfigFile) SetUid(s string) {
	f.SetUserField("id", s)
}

func (f *JsonConfigFile) SetSalt(s string) {
	f.SetUserField("salt", s)
}

func (f *JsonConfigFile) SetUserField(k, v string) {
	existing := f.GetUserField(k)
	if existing != v {
		f.UserDict().SetKey(k, jsonw.NewString(v))
		f.dirty = true
	}
}

func (f *JsonConfigFile) DeleteAtPath(p string) {
	f.jw.DeleteValueAtPath(p)
	f.dirty = true
}

func (f *JsonConfigFile) Reset() {
	f.jw = jsonw.NewDictionary()
	f.dirty = true
}

func (f *JsonConfigFile) Write() error {
	return f.MaybeSave(true, 0)
}

func (f JsonConfigFile) GetUserField(s string) string {
	var ret string
	var err error
	if f.jw != nil {
		ret, err = f.jw.AtKey("user").AtKey(s).GetString()
		if err != nil {
			ret = ""
		}
	}
	G.Log.Debug("Config: mapping user.%s-> %s", s, ret)
	return ret
}

func (f JsonConfigFile) GetHome() (ret string) {
	return f.GetTopLevelString("home")
}
func (f JsonConfigFile) GetServerUri() (ret string) {
	return f.GetTopLevelString("server")
}
func (f JsonConfigFile) GetConfigFilename() (ret string) {
	return f.GetTopLevelString("config")
}
func (f JsonConfigFile) GetSessionFilename() (ret string) {
	return f.GetTopLevelString("session")
}
func (f JsonConfigFile) GetDbFilename() (ret string) {
	return f.GetTopLevelString("db")
}
func (f JsonConfigFile) GetUsername() string {
	return f.GetUserField("name")
}
func (f JsonConfigFile) GetSalt() string {
	return f.GetUserField("salt")
}
func (f JsonConfigFile) GetUid() string {
	return f.GetUserField("id")
}
func (f JsonConfigFile) GetEmail() (ret string) {
	return f.GetTopLevelString("email")
}
func (f JsonConfigFile) GetProxy() (ret string) {
	return f.GetTopLevelString("proxy")
}
func (f JsonConfigFile) GetDebug() (bool, bool) {
	return f.GetTopLevelBool("debug")
}
func (f JsonConfigFile) GetPlainLogging() (bool, bool) {
	return f.GetTopLevelBool("plain_logging")
}
func (f JsonConfigFile) GetUserCacheSize() (ret int, ok bool) {
	if f.jw != nil {
		ret, ok = f.jw.AtPathGetInt("cache.limits.users")
	} else {
		ok = false
	}
	return
}

func (f JsonConfigFile) GetMerkleKeyFingerprints() []string {
	if f.jw == nil {
		return nil
	} else if v, err := f.jw.AtKey("keys").AtKey("merkle").ToArray();
		err != nil || v == nil {
		return nil
	} else if l, err := v.Len(); err != nil {
		return nil
	} else if l == 0 {
		return make([]string, 0, 0)
	} else {
		ret := make([]string, l, 0)
		for i := 0; i < l; i++ {
			if s, err := v.AtIndex(i).GetString(); err != nil {
				return nil
			} else {
				ret = append(ret, s)
			}
		}
		return ret
	}
}

func (f JsonConfigFile) GetPgpDir() (ret string) {
	ret = f.GetTopLevelString("pgpdir")
	if len(ret) == 0 {
		ret = f.GetTopLevelString("gpgdir")
	}
	if len(ret) == 0 {
		ret = f.GetTopLevelString("gnupgdir")
	}
	return ret
}

func (f JsonConfigFile) GetBundledCA(host string) (ret string) {

	if f.jw != nil {
		var err error
		f.jw.AtKey("bundled_CAs").AtKey(host).GetStringVoid(&ret, &err)
		if err == nil {
			G.Log.Debug("Read bundled CA for %s", host)
		}
	}
	return ret
}
<|endoftext|>


<commit_before>// Package config is a config file parser.
//
// A note on usage: Due to the fact that we use the reflect package, you must
// pass in the struct for which you want to parse config keys using all
// exported fields, or this config package cannot set those fields.
//
// Key names are case insensitive.
//
// For an example of using this package, see the test(s).
//
// For the types that we support parsing out of the struct, refer to the
// populateConfig() function.
package config

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"reflect"
	"strconv"
	"strings"
)

// ReadStringMap a config file and returns the keys and values in a map where
// keys and values are strings.
//
// The config file syntax is:
// key = value
//
// Lines may be commented if they begin with a '#' with only whitespace or no
// whitespace in front of the '#' character. Lines currently MAY NOT have
// trailing '#' to be treated as comments.
func ReadStringMap(path string) (map[string]string, error) {
	if len(path) == 0 {
		return nil, fmt.Errorf("invalid path. Path may not be blank")
	}

	fi, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err := fi.Close(); err != nil {
			log.Printf("error closing %s: %s", path, err)
		}
	}()

	config := make(map[string]string)

	scanner := bufio.NewScanner(fi)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix(line, "#") {
			continue
		}

		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := strings.ToLower(strings.TrimSpace(parts[0]))
		value := strings.TrimSpace(parts[1])

		if len(key) == 0 {
			return nil, fmt.Errorf("key length is 0")
		}

		_, exists := config[key]
		if exists {
			return nil, fmt.Errorf("config key defined twice: %s", err)
		}

		// Permit value to be blank.

		config[key] = value
	}

	err = scanner.Err()
	if err != nil {
		return nil, fmt.Errorf("error reading from file: %s", err)
	}

	return config, nil
}

// PopulateStruct takes values read from a config as a string map, and uses them
// to populate a struct. The values will be converted to the struct's types as
// necessary.
//
// To understand the use of reflect in this function, refer to the article Laws
// of Reflection, or the documentation of the reflect package.
func PopulateStruct(config interface{}, rawValues map[string]string) error {
	// Make a reflect.Value from the interface.
	v := reflect.ValueOf(config)

	// Access the value that the interface contains.
	elem := v.Elem()

	// Make a reflect.Type. This describes the Go type. We can use it to get
	// struct field names.
	elemType := elem.Type()

	// Iterate over every field of the struct.
	for i := 0; i < elem.NumField(); i++ {
		// Access the field.
		f := elem.Field(i)

		// Determine the field name.
		fieldName := elemType.Field(i).Name

		// We require this field was in the config file.
		rawValue, ok := rawValues[strings.ToLower(fieldName)]
		if !ok {
			return fmt.Errorf("field %s not found in config file", fieldName)
		}

		// Convert each value string, if necessary, to the necessary Go type.
		// We support a subset of types ('kinds' in reflect) currently.

		if f.Kind() == reflect.Int32 {
			converted, err := strconv.ParseInt(rawValue, 10, 32)
			if err != nil {
				return fmt.Errorf("unable to convert field %s value %s to int32: %s",
					fieldName, rawValue, err)
			}

			f.SetInt(converted)
			continue
		}

		if f.Kind() == reflect.Int64 {
			converted, err := strconv.ParseInt(rawValue, 10, 64)
			if err != nil {
				return fmt.Errorf("unable to convert field %s value %s to int64: %s",
					fieldName, rawValue, err)
			}

			f.SetInt(converted)
			continue
		}

		if f.Kind() == reflect.Uint64 {
			converted, err := strconv.ParseUint(rawValue, 10, 64)
			if err != nil {
				return fmt.Errorf("unable to convert field %s value %s to uint64: %s",
					fieldName, rawValue, err)
			}

			f.SetUint(converted)
			continue
		}

		if f.Kind() == reflect.String {
			f.SetString(rawValue)
			continue
		}

		return fmt.Errorf("field %s: Value: %s: Field kind not yet supported: %s",
			fieldName, rawValue, f.Kind().String())
	}

	return nil
}

// GetConfig reads a config file and populates a struct with what is read.
//
// We use the reflect package to populate the struct from the config.
//
// Currently every member of the struct must have had a value set in the
// config. That is, every config option is required.
func GetConfig(path string, config interface{}) error {
	// We don't need to parameter check path or keys. Why? Because path will get
	// checked when we read the config.

	// We do not need to check anything with the config as it is up to the caller
	// to ensure that they gave us a struct with members they want parsed out of
	// a config.

	// First read in the config. Every key will be associated with a value which
	// is a string.
	rawValues, err := ReadStringMap(path)
	if err != nil {
		return fmt.Errorf("unable to read config: %s: %s", err, path)
	}

	// Fill the struct with the values read from the config.
	err = PopulateStruct(config, rawValues)
	if err != nil {
		return fmt.Errorf("unable to populate config: %s", err)
	}

	return nil
}
<commit_msg>Reference dupe key rather than non-existing error<commit_after>// Package config is a config file parser.
//
// A note on usage: Due to the fact that we use the reflect package, you must
// pass in the struct for which you want to parse config keys using all
// exported fields, or this config package cannot set those fields.
//
// Key names are case insensitive.
//
// For an example of using this package, see the test(s).
//
// For the types that we support parsing out of the struct, refer to the
// populateConfig() function.
package config

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"reflect"
	"strconv"
	"strings"
)

// ReadStringMap a config file and returns the keys and values in a map where
// keys and values are strings.
//
// The config file syntax is:
// key = value
//
// Lines may be commented if they begin with a '#' with only whitespace or no
// whitespace in front of the '#' character. Lines currently MAY NOT have
// trailing '#' to be treated as comments.
func ReadStringMap(path string) (map[string]string, error) {
	if len(path) == 0 {
		return nil, fmt.Errorf("invalid path. Path may not be blank")
	}

	fi, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err := fi.Close(); err != nil {
			log.Printf("error closing %s: %s", path, err)
		}
	}()

	config := make(map[string]string)

	scanner := bufio.NewScanner(fi)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix(line, "#") {
			continue
		}

		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			continue
		}

		key := strings.ToLower(strings.TrimSpace(parts[0]))
		value := strings.TrimSpace(parts[1])

		if len(key) == 0 {
			return nil, fmt.Errorf("key length is 0")
		}

		_, exists := config[key]
		if exists {
			return nil, fmt.Errorf("config key defined twice: %s", key)
		}

		// Permit value to be blank.

		config[key] = value
	}

	err = scanner.Err()
	if err != nil {
		return nil, fmt.Errorf("error reading from file: %s", err)
	}

	return config, nil
}

// PopulateStruct takes values read from a config as a string map, and uses them
// to populate a struct. The values will be converted to the struct's types as
// necessary.
//
// To understand the use of reflect in this function, refer to the article Laws
// of Reflection, or the documentation of the reflect package.
func PopulateStruct(config interface{}, rawValues map[string]string) error {
	// Make a reflect.Value from the interface.
	v := reflect.ValueOf(config)

	// Access the value that the interface contains.
	elem := v.Elem()

	// Make a reflect.Type. This describes the Go type. We can use it to get
	// struct field names.
	elemType := elem.Type()

	// Iterate over every field of the struct.
	for i := 0; i < elem.NumField(); i++ {
		// Access the field.
		f := elem.Field(i)

		// Determine the field name.
		fieldName := elemType.Field(i).Name

		// We require this field was in the config file.
		rawValue, ok := rawValues[strings.ToLower(fieldName)]
		if !ok {
			return fmt.Errorf("field %s not found in config file", fieldName)
		}

		// Convert each value string, if necessary, to the necessary Go type.
		// We support a subset of types ('kinds' in reflect) currently.

		if f.Kind() == reflect.Int32 {
			converted, err := strconv.ParseInt(rawValue, 10, 32)
			if err != nil {
				return fmt.Errorf("unable to convert field %s value %s to int32: %s",
					fieldName, rawValue, err)
			}

			f.SetInt(converted)
			continue
		}

		if f.Kind() == reflect.Int64 {
			converted, err := strconv.ParseInt(rawValue, 10, 64)
			if err != nil {
				return fmt.Errorf("unable to convert field %s value %s to int64: %s",
					fieldName, rawValue, err)
			}

			f.SetInt(converted)
			continue
		}

		if f.Kind() == reflect.Uint64 {
			converted, err := strconv.ParseUint(rawValue, 10, 64)
			if err != nil {
				return fmt.Errorf("unable to convert field %s value %s to uint64: %s",
					fieldName, rawValue, err)
			}

			f.SetUint(converted)
			continue
		}

		if f.Kind() == reflect.String {
			f.SetString(rawValue)
			continue
		}

		return fmt.Errorf("field %s: Value: %s: Field kind not yet supported: %s",
			fieldName, rawValue, f.Kind().String())
	}

	return nil
}

// GetConfig reads a config file and populates a struct with what is read.
//
// We use the reflect package to populate the struct from the config.
//
// Currently every member of the struct must have had a value set in the
// config. That is, every config option is required.
func GetConfig(path string, config interface{}) error {
	// We don't need to parameter check path or keys. Why? Because path will get
	// checked when we read the config.

	// We do not need to check anything with the config as it is up to the caller
	// to ensure that they gave us a struct with members they want parsed out of
	// a config.

	// First read in the config. Every key will be associated with a value which
	// is a string.
	rawValues, err := ReadStringMap(path)
	if err != nil {
		return fmt.Errorf("unable to read config: %s: %s", err, path)
	}

	// Fill the struct with the values read from the config.
	err = PopulateStruct(config, rawValues)
	if err != nil {
		return fmt.Errorf("unable to populate config: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package walnut

import (
	"errors"
	"sort"
	"time"
)

var (
	ErrUndefined = errors.New("key is not defined")
	ErrWrongType = errors.New("key is not of the expected type")
)

type Config map[string]interface{}

// Returns a list of all defined keys, sorted lexographically.
func (c *Config) Keys() []string {
	self := *c
	keys := make([]string, 0)

	for key, _ := range self {
		keys = append(keys, key)
	}

	sort.Strings(keys)

	return keys
}

// Retrieves a value. The second return value will be `false` if the
// value hasn't been defined.
func (c *Config) Get(key string) (interface{}, bool) {
	v, ok := (*c)[key]
	return v, ok
}

// Retrieves a string value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) String(key string) (string, error) {
	v, ok := (*c)[key]
	if !ok {
		return "", ErrUndefined
	}

	s, ok := v.(string)
	if !ok {
		return "", ErrWrongType
	}

	return s, nil
}

// Retrieves a bool value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Bool(key string) (bool, error) {
	v, ok := (*c)[key]
	if !ok {
		return false, ErrUndefined
	}

	b, ok := v.(bool)
	if !ok {
		return false, ErrWrongType
	}

	return b, nil
}

// Retrieves an integer value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Int64(key string) (int64, error) {
	v, ok := (*c)[key]
	if !ok {
		return 0, ErrUndefined
	}

	i, ok := v.(int64)
	if !ok {
		return 0, ErrWrongType
	}

	return i, nil
}

// Retrieves a float value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Float64(key string) (float64, error) {
	v, ok := (*c)[key]
	if !ok {
		return 0, ErrUndefined
	}

	f, ok := v.(float64)
	if !ok {
		return 0, ErrWrongType
	}

	return f, nil
}

// Retrieves a duration value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Duration(key string) (time.Duration, error) {
	v, ok := (*c)[key]
	if !ok {
		return time.Duration(0), ErrUndefined
	}

	d, ok := v.(time.Duration)
	if !ok {
		return time.Duration(0), ErrWrongType
	}

	return d, nil
}

// Retrieves a time value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Time(key string) (time.Time, error) {
	v, ok := (*c)[key]
	if !ok {
		return time.Time{}, ErrUndefined
	}

	t, ok := v.(time.Time)
	if !ok {
		return time.Time{}, ErrWrongType
	}

	return t, nil
}
<commit_msg>Swap the Config.Time() and .Duration() methods for consistency<commit_after>package walnut

import (
	"errors"
	"sort"
	"time"
)

var (
	ErrUndefined = errors.New("key is not defined")
	ErrWrongType = errors.New("key is not of the expected type")
)

type Config map[string]interface{}

// Returns a list of all defined keys, sorted lexographically.
func (c *Config) Keys() []string {
	self := *c
	keys := make([]string, 0)

	for key, _ := range self {
		keys = append(keys, key)
	}

	sort.Strings(keys)

	return keys
}

// Retrieves a value. The second return value will be `false` if the
// value hasn't been defined.
func (c *Config) Get(key string) (interface{}, bool) {
	v, ok := (*c)[key]
	return v, ok
}

// Retrieves a string value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) String(key string) (string, error) {
	v, ok := (*c)[key]
	if !ok {
		return "", ErrUndefined
	}

	s, ok := v.(string)
	if !ok {
		return "", ErrWrongType
	}

	return s, nil
}

// Retrieves a bool value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Bool(key string) (bool, error) {
	v, ok := (*c)[key]
	if !ok {
		return false, ErrUndefined
	}

	b, ok := v.(bool)
	if !ok {
		return false, ErrWrongType
	}

	return b, nil
}

// Retrieves an integer value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Int64(key string) (int64, error) {
	v, ok := (*c)[key]
	if !ok {
		return 0, ErrUndefined
	}

	i, ok := v.(int64)
	if !ok {
		return 0, ErrWrongType
	}

	return i, nil
}

// Retrieves a float value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Float64(key string) (float64, error) {
	v, ok := (*c)[key]
	if !ok {
		return 0, ErrUndefined
	}

	f, ok := v.(float64)
	if !ok {
		return 0, ErrWrongType
	}

	return f, nil
}

// Retrieves a time value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Time(key string) (time.Time, error) {
	v, ok := (*c)[key]
	if !ok {
		return time.Time{}, ErrUndefined
	}

	t, ok := v.(time.Time)
	if !ok {
		return time.Time{}, ErrWrongType
	}

	return t, nil
}

// Retrieves a duration value. Will return a non-nil error if the key
// either hasn't been defined or is of a different type.
func (c *Config) Duration(key string) (time.Duration, error) {
	v, ok := (*c)[key]
	if !ok {
		return time.Duration(0), ErrUndefined
	}

	d, ok := v.(time.Duration)
	if !ok {
		return time.Duration(0), ErrWrongType
	}

	return d, nil
}
<|endoftext|>


<commit_before>
package libkb

import (
)

type JsonConfigFile struct {
	JsonFile
}

func NewJsonConfigFile(s string) *JsonConfigFile {
	return &JsonConfigFile { JsonFile { s, "config", nil } }
}

func (f JsonConfigFile) GetTopLevelString(s string) (ret string) {
	var e error
	f.jw.AtKey(s).GetStringVoid(&ret, &e)
	G.Log.Debug("Config: mapping %s -> %s", s, ret)
	return
}

func (f JsonConfigFile) GetTopLevelBool(s string) (res bool, is_set bool) {
	w := f.jw.AtKey("debug")
	if w.IsNil() {
		is_set = false
		res = false
	} else {
		is_set = true
		var e error
		w.GetBoolVoid(&res, &e)
	}
	return
}

func (f JsonConfigFile) GetHome() (ret string) { return f.GetTopLevelString("home") }
func (f JsonConfigFile) GetServerUri() (ret string) { return f.GetTopLevelString("server") }
func (f JsonConfigFile) GetConfigFilename() (ret string) { return f.GetTopLevelString("config") }
func (f JsonConfigFile) GetSessionFilename() (ret string) { return f.GetTopLevelString("session") }
func (f JsonConfigFile) GetDbFilename() (ret string) { return f.GetTopLevelString("db") }
func (f JsonConfigFile) GetApiUriPathPrefix() (ret string) { return f.GetTopLevelString("api_uri_path_prefix") }
func (f JsonConfigFile) GetUsername() (ret string) { return f.GetTopLevelString("username") }
func (f JsonConfigFile) GetProxy() (ret string) { return f.GetTopLevelString("proxy") }
func (f JsonConfigFile) GetDebug() (bool, bool) { return f.GetTopLevelBool("debug") }
func (f JsonConfigFile) GetPlainLogging() (bool, bool) { return f.GetTopLevelBool("plain_logging") }
func (f JsonConfigFile) GetPgpDir() (ret string) {
	ret = f.GetTopLevelString("pgpdir")
	if len(ret) == 0 {ret = f.GetTopLevelString("gpgdir") }
	if len(ret) == 0 {ret = f.GetTopLevelString("gnupgdir") }
	return ret
}

<commit_msg>it works without a config file now<commit_after>
package libkb

import (
)

type JsonConfigFile struct {
	JsonFile
}

func NewJsonConfigFile(s string) *JsonConfigFile {
	return &JsonConfigFile { JsonFile { s, "config", nil } }
}

func (f JsonConfigFile) GetTopLevelString(s string) (ret string) {
	var e error
	if f.jw != nil {
		f.jw.AtKey(s).GetStringVoid(&ret, &e)
		G.Log.Debug("Config: mapping %s -> %s", s, ret)
	}
	return
}

func (f JsonConfigFile) GetTopLevelBool(s string) (res bool, is_set bool) {
	is_set = false
	res = false
	if f.jw != nil {
		if w := f.jw.AtKey(s); !w.IsNil() {
			is_set = true
			var e error
			w.GetBoolVoid(&res, &e)
		}
	}
	return
}

func (f JsonConfigFile) GetHome() (ret string) { return f.GetTopLevelString("home") }
func (f JsonConfigFile) GetServerUri() (ret string) { return f.GetTopLevelString("server") }
func (f JsonConfigFile) GetConfigFilename() (ret string) { return f.GetTopLevelString("config") }
func (f JsonConfigFile) GetSessionFilename() (ret string) { return f.GetTopLevelString("session") }
func (f JsonConfigFile) GetDbFilename() (ret string) { return f.GetTopLevelString("db") }
func (f JsonConfigFile) GetApiUriPathPrefix() (ret string) { return f.GetTopLevelString("api_uri_path_prefix") }
func (f JsonConfigFile) GetUsername() (ret string) { return f.GetTopLevelString("username") }
func (f JsonConfigFile) GetProxy() (ret string) { return f.GetTopLevelString("proxy") }
func (f JsonConfigFile) GetDebug() (bool, bool) { return f.GetTopLevelBool("debug") }
func (f JsonConfigFile) GetPlainLogging() (bool, bool) { return f.GetTopLevelBool("plain_logging") }
func (f JsonConfigFile) GetPgpDir() (ret string) {
	ret = f.GetTopLevelString("pgpdir")
	if len(ret) == 0 {ret = f.GetTopLevelString("gpgdir") }
	if len(ret) == 0 {ret = f.GetTopLevelString("gnupgdir") }
	return ret
}

<|endoftext|>