<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"

	manners "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/braintree/manners"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
	cmds "github.com/jbenet/go-ipfs/commands"
	cmdsHttp "github.com/jbenet/go-ipfs/commands/http"
	core "github.com/jbenet/go-ipfs/core"
	commands "github.com/jbenet/go-ipfs/core/commands"
	daemon "github.com/jbenet/go-ipfs/core/daemon"
	util "github.com/jbenet/go-ipfs/util"
	"github.com/jbenet/go-ipfs/util/debugerror"
)

const (
	initOptionKwd = "init"
	mountKwd      = "mount"
	ipfsMountKwd  = "mount-ipfs"
	ipnsMountKwd  = "mount-ipns"
	// apiAddrKwd    = "address-api"
	// swarmAddrKwd  = "address-swarm"
	originEnvKey = "API_ORIGIN"
)

var daemonCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Run a network-connected IPFS node",
		ShortDescription: `
'ipfs daemon' runs a persistent IPFS daemon that can serve commands
over the network. Most applications that use IPFS will do so by
communicating with a daemon over the HTTP API. While the daemon is
running, calls to 'ipfs' commands will be sent over the network to
the daemon.
`,
	},

	Options: []cmds.Option{
		cmds.BoolOption(initOptionKwd, "Initialize IPFS with default settings if not already initialized"),
		cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem"),
		cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount)"),
		cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount)"),

		// TODO: add way to override addresses. tricky part: updating the config if also --init.
		// cmds.StringOption(apiAddrKwd, "Address for the daemon rpc API (overrides config)"),
		// cmds.StringOption(swarmAddrKwd, "Address for the swarm socket (overrides config)"),
	},
	Subcommands: map[string]*cmds.Command{},
	Run:         daemonFunc,
}

func daemonFunc(req cmds.Request) (interface{}, error) {

	// first, whether user has provided the initialization flag. we may be
	// running in an uninitialized state.
	initialize, _, err := req.Option(initOptionKwd).Bool()
	if err != nil {
		return nil, err
	}
	if initialize {

		// now, FileExists is our best method of detecting whether IPFS is
		// configured. Consider moving this into a config helper method
		// `IsInitialized` where the quality of the signal can be improved over
		// time, and many call-sites can benefit.
		if !util.FileExists(req.Context().ConfigRoot) {
			err := initWithDefaults(req.Context().ConfigRoot)
			if err != nil {
				return nil, debugerror.Wrap(err)
			}
		}
	}

	// To ensure that IPFS has been initialized, fetch the config. Do this
	// _before_ acquiring the daemon lock so the user gets an appropriate error
	// message.
	// NB: It's safe to read the config without the daemon lock, but not safe
	// to write.
	ctx := req.Context()
	cfg, err := ctx.GetConfig()
	if err != nil {
		return nil, err
	}

	// acquire the daemon lock _before_ constructing a node. we need to make
	// sure we are permitted to access the resources (datastore, etc.)
	lock, err := daemon.Lock(req.Context().ConfigRoot)
	if err != nil {
		return nil, debugerror.Errorf("Couldn't obtain lock. Is another daemon already running?")
	}
	defer lock.Close()

	// OK!!! Now we're ready to construct the node.
	// make sure we construct an online node.
	ctx.Online = true
	node, err := ctx.GetNode()
	if err != nil {
		return nil, err
	}

	// verify api address is valid multiaddr
	apiMaddr, err := ma.NewMultiaddr(cfg.Addresses.API)
	if err != nil {
		return nil, err
	}

	// mount if the user provided the --mount flag
	mount, _, err := req.Option(mountKwd).Bool()
	if err != nil {
		return nil, err
	}
	if mount {
		fsdir, found, err := req.Option(ipfsMountKwd).String()
		if err != nil {
			return nil, err
		}
		if !found {
			fsdir = cfg.Mounts.IPFS
		}

		nsdir, found, err := req.Option(ipnsMountKwd).String()
		if err != nil {
			return nil, err
		}
		if !found {
			nsdir = cfg.Mounts.IPNS
		}

		err = commands.Mount(node, fsdir, nsdir)
		if err != nil {
			return nil, err
		}
		fmt.Printf("IPFS mounted at: %s\n", fsdir)
		fmt.Printf("IPNS mounted at: %s\n", nsdir)
	}

	return nil, listenAndServeAPI(node, req, apiMaddr)
}

func listenAndServeAPI(node *core.IpfsNode, req cmds.Request, addr ma.Multiaddr) error {

	_, host, err := manet.DialArgs(addr)
	if err != nil {
		return err
	}

	origin := os.Getenv(originEnvKey)

	server := manners.NewServer()
	mux := http.NewServeMux()
	cmdHandler := cmdsHttp.NewHandler(*req.Context(), commands.Root, origin)
	mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)

	ifpsHandler := &ipfsHandler{node}
	mux.Handle("/ipfs/", ifpsHandler)

	// if the server exits beforehand
	var serverError error
	serverExited := make(chan struct{})

	go func() {
		fmt.Printf("daemon listening on %s\n", addr)
		serverError = server.ListenAndServe(host, mux)
		close(serverExited)
	}()

	// wait for server to exit.
	select {
	case <-serverExited:

	// if node being closed before server exits, close server
	case <-node.Closing():
		log.Infof("daemon at %s terminating...", addr)
		server.Shutdown <- true
		<-serverExited // now, DO wait until server exits
	}

	log.Infof("daemon at %s terminated", addr)
	return serverError
}
<commit_msg>cmd/ipfs: Added gateway server to daemon<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"

	manners "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/braintree/manners"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
	cmds "github.com/jbenet/go-ipfs/commands"
	cmdsHttp "github.com/jbenet/go-ipfs/commands/http"
	core "github.com/jbenet/go-ipfs/core"
	commands "github.com/jbenet/go-ipfs/core/commands"
	daemon "github.com/jbenet/go-ipfs/core/daemon"
	util "github.com/jbenet/go-ipfs/util"
	"github.com/jbenet/go-ipfs/util/debugerror"
)

const (
	initOptionKwd = "init"
	mountKwd      = "mount"
	ipfsMountKwd  = "mount-ipfs"
	ipnsMountKwd  = "mount-ipns"
	// apiAddrKwd    = "address-api"
	// swarmAddrKwd  = "address-swarm"
	originEnvKey = "API_ORIGIN"
)

var daemonCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Run a network-connected IPFS node",
		ShortDescription: `
'ipfs daemon' runs a persistent IPFS daemon that can serve commands
over the network. Most applications that use IPFS will do so by
communicating with a daemon over the HTTP API. While the daemon is
running, calls to 'ipfs' commands will be sent over the network to
the daemon.
`,
	},

	Options: []cmds.Option{
		cmds.BoolOption(initOptionKwd, "Initialize IPFS with default settings if not already initialized"),
		cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem"),
		cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount)"),
		cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount)"),

		// TODO: add way to override addresses. tricky part: updating the config if also --init.
		// cmds.StringOption(apiAddrKwd, "Address for the daemon rpc API (overrides config)"),
		// cmds.StringOption(swarmAddrKwd, "Address for the swarm socket (overrides config)"),
	},
	Subcommands: map[string]*cmds.Command{},
	Run:         daemonFunc,
}

func daemonFunc(req cmds.Request) (interface{}, error) {

	// first, whether user has provided the initialization flag. we may be
	// running in an uninitialized state.
	initialize, _, err := req.Option(initOptionKwd).Bool()
	if err != nil {
		return nil, err
	}
	if initialize {

		// now, FileExists is our best method of detecting whether IPFS is
		// configured. Consider moving this into a config helper method
		// `IsInitialized` where the quality of the signal can be improved over
		// time, and many call-sites can benefit.
		if !util.FileExists(req.Context().ConfigRoot) {
			err := initWithDefaults(req.Context().ConfigRoot)
			if err != nil {
				return nil, debugerror.Wrap(err)
			}
		}
	}

	// To ensure that IPFS has been initialized, fetch the config. Do this
	// _before_ acquiring the daemon lock so the user gets an appropriate error
	// message.
	// NB: It's safe to read the config without the daemon lock, but not safe
	// to write.
	ctx := req.Context()
	cfg, err := ctx.GetConfig()
	if err != nil {
		return nil, err
	}

	// acquire the daemon lock _before_ constructing a node. we need to make
	// sure we are permitted to access the resources (datastore, etc.)
	lock, err := daemon.Lock(req.Context().ConfigRoot)
	if err != nil {
		return nil, debugerror.Errorf("Couldn't obtain lock. Is another daemon already running?")
	}
	defer lock.Close()

	// OK!!! Now we're ready to construct the node.
	// make sure we construct an online node.
	ctx.Online = true
	node, err := ctx.GetNode()
	if err != nil {
		return nil, err
	}

	// verify api address is valid multiaddr
	apiMaddr, err := ma.NewMultiaddr(cfg.Addresses.API)
	if err != nil {
		return nil, err
	}

	// ignore error for gateway address
	// if there is an error (invalid address), then don't run the gateway
	gatewayMaddr, _ := ma.NewMultiaddr(cfg.Addresses.Gateway)
	if gatewayMaddr == nil {
		fmt.Println("Invalid gateway address, not running gateway")
	}

	// mount if the user provided the --mount flag
	mount, _, err := req.Option(mountKwd).Bool()
	if err != nil {
		return nil, err
	}
	if mount {
		fsdir, found, err := req.Option(ipfsMountKwd).String()
		if err != nil {
			return nil, err
		}
		if !found {
			fsdir = cfg.Mounts.IPFS
		}

		nsdir, found, err := req.Option(ipnsMountKwd).String()
		if err != nil {
			return nil, err
		}
		if !found {
			nsdir = cfg.Mounts.IPNS
		}

		err = commands.Mount(node, fsdir, nsdir)
		if err != nil {
			return nil, err
		}
		fmt.Printf("IPFS mounted at: %s\n", fsdir)
		fmt.Printf("IPNS mounted at: %s\n", nsdir)
	}

	if gatewayMaddr != nil {
		listenAndServeGateway(node, gatewayMaddr)
	}

	return nil, listenAndServeAPI(node, req, apiMaddr)
}

func listenAndServeAPI(node *core.IpfsNode, req cmds.Request, addr ma.Multiaddr) error {
	_, host, err := manet.DialArgs(addr)
	if err != nil {
		return err
	}

	origin := os.Getenv(originEnvKey)

	server := manners.NewServer()
	mux := http.NewServeMux()
	cmdHandler := cmdsHttp.NewHandler(*req.Context(), commands.Root, origin)
	mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)

	ifpsHandler := &ipfsHandler{node}
	mux.Handle("/ipfs/", ifpsHandler)

	// if the server exits beforehand
	var serverError error
	serverExited := make(chan struct{})

	go func() {
		fmt.Printf("API server listening on %s\n", addr)
		serverError = server.ListenAndServe(host, mux)
		close(serverExited)
	}()

	// wait for server to exit.
	select {
	case <-serverExited:

	// if node being closed before server exits, close server
	case <-node.Closing():
		log.Infof("daemon at %s terminating...", addr)
		server.Shutdown <- true
		<-serverExited // now, DO wait until server exit
	}

	log.Infof("daemon at %s terminated", addr)
	return serverError
}

func listenAndServeGateway(node *core.IpfsNode, addr ma.Multiaddr) error {
	_, host, err := manet.DialArgs(addr)
	if err != nil {
		return err
	}

	server := manners.NewServer()
	mux := http.NewServeMux()
	ifpsHandler := &ipfsHandler{node}
	mux.Handle("/ipfs/", ifpsHandler)

	done := make(chan struct{}, 1)
	defer func() {
		done <- struct{}{}
	}()

	// go wait until the node dies
	go func() {
		select {
		case <-node.Closed():
		case <-done:
			return
		}

		log.Infof("terminating gateway at %s...", addr)
		server.Shutdown <- true
	}()

	fmt.Printf("Gateway listening on %s\n", addr)
	go func() {
		if err := server.ListenAndServe(host, mux); err != nil {
			log.Error(err)
		}
	}()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"strings"

	"github.com/BellerophonMobile/hippo"
	"github.com/BellerophonMobile/logberry"
)

type claims map[string]interface{}

func (c claims) String() string {
	bytes, _ := json.Marshal(c)
	return string(bytes)
}

func (c claims) Set(flag string) error {
	v := strings.SplitAfterN(flag, ",", 2)
	if len(v) != 2 {
		return fmt.Errorf("Claim format is \"key,value\"")
	}

	key := v[0][:len(v[0])-1]

	dec := json.NewDecoder(strings.NewReader(v[1]))
	dec.UseNumber()

	tok, err := dec.Token()
	if err != nil {
		return fmt.Errorf("Invalid claim value format")
	}

	switch tok := tok.(type) {
	case bool, string:
		c[key] = tok

	case json.Number:
		// First try to decode as int
		c[key], err = tok.Int64()
		if err == nil {
			break
		}
		// Next try float
		c[key], err = tok.Float64()
		if err == nil {
			break
		}
		// Finally, just store the string value
		c[key] = tok.String()

	default:
		return fmt.Errorf("Invalid claim value type")
	}

	return nil
}

func main() {
	certId := flag.String("certid", "", "Certificate ID.")
	subjectId := flag.String("subjectid", "", "Subject ID.")
	publicKeyFile := flag.String("subjectkey", "", "Subject public key.")
	signingKeyFile := flag.String("signingkey", "", "Signing private key.")
	outFile := flag.String("out", "", "Output certificate.")

	claims := make(claims)

	flag.Var(claims, "claim", "Add claim \"key,value\"")

	flag.Parse()

	publicKey := readPublicKey(*publicKeyFile)

	testament := hippo.NewTestament(*subjectId, *publicKey)
	testament.Claims = claims

	signer := makeSigner(*signingKeyFile)
	decl := sign(*certId, testament, signer)

	cert := &hippo.Certificate{
		Declarations: hippo.Chain{decl},
	}

	writeCert(cert, *outFile)

}

func readPublicKey(filename string) *hippo.PublicKey {
	task := logberry.Main.Task("Read public key", logberry.D{"filename": filename})
	key, err := hippo.PublicKeyFromFile(filename)
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
	return key
}

func readPrivateKey(filename string) *hippo.PrivateKey {
	task := logberry.Main.Task("Read private key", logberry.D{"filename": filename})
	key, err := hippo.PrivateKeyFromFile(filename)
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
	return key
}

func makeSigner(filename string) hippo.Credentials {
	task := logberry.Main.Task("Load signer", logberry.D{"filename": filename})
	credentials, err := hippo.NewSigner(*readPrivateKey(filename))
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
	return credentials
}

func sign(id string, testament *hippo.Testament, signer hippo.Credentials) *hippo.Declaration {
	task := logberry.Main.Task("Sign certificate", logberry.D{"id": id})
	decl, err := testament.Sign(id, signer)
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
	return decl
}

func writeCert(cert *hippo.Certificate, filename string) {
	task := logberry.Main.Task("Write certificate")
	err := cert.ToFile(filename)
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
}
<commit_msg>Add -chain option to mkcert.<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"strings"

	"github.com/BellerophonMobile/hippo"
	"github.com/BellerophonMobile/logberry"
)

type claims map[string]interface{}

func (c claims) String() string {
	bytes, _ := json.Marshal(c)
	return string(bytes)
}

func (c claims) Set(flag string) error {
	v := strings.SplitAfterN(flag, ",", 2)
	if len(v) != 2 {
		return fmt.Errorf("Claim format is \"key,value\"")
	}

	key := v[0][:len(v[0])-1]

	dec := json.NewDecoder(strings.NewReader(v[1]))
	dec.UseNumber()

	tok, err := dec.Token()
	if err != nil {
		return fmt.Errorf("Invalid claim value format")
	}

	switch tok := tok.(type) {
	case bool, string:
		c[key] = tok

	case json.Number:
		// First try to decode as int
		c[key], err = tok.Int64()
		if err == nil {
			break
		}
		// Next try float
		c[key], err = tok.Float64()
		if err == nil {
			break
		}
		// Finally, just store the string value
		c[key] = tok.String()

	default:
		return fmt.Errorf("Invalid claim value type")
	}

	return nil
}

type chain hippo.Chain

func (c *chain) String() string {
	bytes, _ := json.Marshal(c)
	return string(bytes)
}

func (c *chain) Set(flag string) error {
	cert, err := hippo.CertificateFromFile(flag)
	if err != nil {
		return err
	}

	*c = append(*c, cert.Declarations...)

	return nil
}

func main() {
	certId := flag.String("certid", "", "Certificate ID.")
	subjectId := flag.String("subjectid", "", "Subject ID.")
	publicKeyFile := flag.String("subjectkey", "", "Subject public key.")
	signingKeyFile := flag.String("signingkey", "", "Signing private key.")
	outFile := flag.String("out", "", "Output certificate.")

	claims := make(claims)
	var chain chain

	flag.Var(claims, "claim", "Add claim \"key,value\".")
	flag.Var(&chain, "chain", "Add chained certificates.")

	flag.Parse()

	fmt.Printf("Chains: %v\n", chain)

	publicKey := readPublicKey(*publicKeyFile)

	testament := hippo.NewTestament(*subjectId, *publicKey)
	testament.Claims = claims

	signer := makeSigner(*signingKeyFile)
	decl := sign(*certId, testament, signer)

	cert := &hippo.Certificate{Declarations: hippo.Chain{decl}}
	cert.Declarations = append(cert.Declarations, chain...)

	writeCert(cert, *outFile)

}

func readPublicKey(filename string) *hippo.PublicKey {
	task := logberry.Main.Task("Read public key", logberry.D{"filename": filename})
	key, err := hippo.PublicKeyFromFile(filename)
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
	return key
}

func readPrivateKey(filename string) *hippo.PrivateKey {
	task := logberry.Main.Task("Read private key", logberry.D{"filename": filename})
	key, err := hippo.PrivateKeyFromFile(filename)
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
	return key
}

func makeSigner(filename string) hippo.Credentials {
	task := logberry.Main.Task("Load signer", logberry.D{"filename": filename})
	credentials, err := hippo.NewSigner(*readPrivateKey(filename))
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
	return credentials
}

func sign(id string, testament *hippo.Testament, signer hippo.Credentials) *hippo.Declaration {
	task := logberry.Main.Task("Sign certificate", logberry.D{"id": id})
	decl, err := testament.Sign(id, signer)
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
	return decl
}

func writeCert(cert *hippo.Certificate, filename string) {
	task := logberry.Main.Task("Write certificate")
	err := cert.ToFile(filename)
	if err != nil {
		task.Fatal(err)
	}
	task.Success()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/tarm/serial"
)

var port *serial.Port

func main() {
	var connstr string
	flag.StringVar(&connstr, "c", "/dev/ptyp5,19200,n,8,1", "serial port connect string (port,baud,handshake,data-bits,stop-bits)")
	flag.Parse()

	cfg, err := parseConfig(connstr)
	check(err)

	portCfg := &serial.Config{
		Name:        cfg.port,
		Baud:        cfg.baud,
		ReadTimeout: 100 * time.Millisecond,
	}

	port, err = serial.OpenPort(portCfg)
	check(err)
	fmt.Printf("modem port: %s\n", connstr)

	var (
		fclass = "1"
		vcid   = "0"
	)

	r := bufio.NewReader(port)
	go func() {
		for {
			discardWhitespace(r)
			s, err := r.ReadString('\r')
			if err != nil {
				continue
			}
			s = strings.TrimSpace(s)
			fmt.Printf("-> %s\n", s)
			port.Write([]byte(s))
			port.Write([]byte("\r\n"))
			if s == "ATI3" {
				write(port, "CX93001-EIS_V0.2002-V92\r\n")
			} else if s == "AT+FCLASS=?" {
				write(port, "1,2,1.0,8\r\n")
			} else if s == "AT+FCLASS?" {
				write(port, fmt.Sprintf("%s\r\n", fclass))
			} else if strings.Contains(s, "AT+FCLASS=") {
				fclass = s[10:]
				write(port, "OK\r\n")
			} else if s == "AT+VCID=?" {
				write(port, "(0-2)\r\n")
			} else if s == "AT+VCID?" {
				write(port, fmt.Sprintf("%s\r\n", vcid))
			} else if strings.Contains(s, "AT+VCID=") {
				vcid = s[8:]
				write(port, "OK\r\n")
			} else {
				write(port, "OK\r\n")
			}
		}
	}()

	fmt.Printf("http server on :8087\n")
	http.HandleFunc("/call", serveCall)
	http.ListenAndServe(":8087", nil)
}

func serveCall(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "/call only accepts POST", http.StatusBadRequest)
		return
	}

	q := r.URL.Query()
	name := q.Get("name")
	number := q.Get("number")

	fmt.Printf("call from %s at %s\n", name, number)

	t := time.Now()
	write(port, "RING\r\n")
	time.Sleep(500 * time.Millisecond)
	write(port, fmt.Sprintf("DATE = %s\r\n", t.Format("0102")))
	write(port, fmt.Sprintf("TIME = %s\r\n", t.Format("1504")))
	write(port, fmt.Sprintf("NMBR = %s\r\n", number))
	write(port, fmt.Sprintf("NAME = %s\r\n", name))
	write(port, "RING\r\n")
	time.Sleep(500 * time.Millisecond)
	write(port, "RING\r\n")
	time.Sleep(500 * time.Millisecond)
}

func httpError(err error, w http.ResponseWriter, status int) {
	if err != nil {
		fmt.Println(err)
	}
	http.Error(w, "", status)
}

func write(p *serial.Port, s string) {
	if _, err := p.Write([]byte(s)); err != nil {
		println(err)
		return
	}
	fmt.Printf("<- %s\n", strings.TrimSpace(s))
}

func discardWhitespace(r *bufio.Reader) {
	c, _, err := r.ReadRune()
	if err != nil || !unicode.IsSpace(c) {
		r.UnreadRune()
		r.UnreadRune()
	}
}

type config struct {
	port string
	baud int
}

func parseConfig(s string) (*config, error) {
	a := strings.Split(s, ",")
	if len(a) != 5 {
		return nil, fmt.Errorf("expected 5 parameters, got %d", len(a))
	}

	baud, err := strconv.Atoi(a[1])
	if err != nil {
		return nil, err
	}

	cfg := &config{
		port: a[0],
		baud: baud,
	}

	return cfg, nil
}

func check(err error) {
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}
}
<commit_msg>differentiate sim from real modem<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/tarm/serial"
)

var port *serial.Port

func main() {
	var connstr string
	flag.StringVar(&connstr, "c", "/dev/ptyp5,19200,n,8,1", "serial port connect string (port,baud,handshake,data-bits,stop-bits)")
	flag.Parse()

	cfg, err := parseConfig(connstr)
	check(err)

	portCfg := &serial.Config{
		Name:        cfg.port,
		Baud:        cfg.baud,
		ReadTimeout: 100 * time.Millisecond,
	}

	port, err = serial.OpenPort(portCfg)
	check(err)
	fmt.Printf("modem port: %s\n", connstr)

	var (
		fclass = "1"
		vcid   = "0"
	)

	r := bufio.NewReader(port)
	go func() {
		for {
			discardWhitespace(r)
			s, err := r.ReadString('\r')
			if err != nil {
				continue
			}
			s = strings.TrimSpace(s)
			fmt.Printf("-> %s\n", s)
			port.Write([]byte(s))
			port.Write([]byte("\r\n"))
			if s == "ATI3" {
				write(port, "NOROBO MODEM SIMULATOR\r\n")
			} else if s == "AT+FCLASS=?" {
				write(port, "1,2,1.0,8\r\n")
			} else if s == "AT+FCLASS?" {
				write(port, fmt.Sprintf("%s\r\n", fclass))
			} else if strings.Contains(s, "AT+FCLASS=") {
				fclass = s[10:]
				write(port, "OK\r\n")
			} else if s == "AT+VCID=?" {
				write(port, "(0-2)\r\n")
			} else if s == "AT+VCID?" {
				write(port, fmt.Sprintf("%s\r\n", vcid))
			} else if strings.Contains(s, "AT+VCID=") {
				vcid = s[8:]
				write(port, "OK\r\n")
			} else {
				write(port, "OK\r\n")
			}
		}
	}()

	fmt.Printf("http server on :8087\n")
	http.HandleFunc("/call", serveCall)
	http.ListenAndServe(":8087", nil)
}

func serveCall(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "/call only accepts POST", http.StatusBadRequest)
		return
	}

	q := r.URL.Query()
	name := q.Get("name")
	number := q.Get("number")

	fmt.Printf("call from %s at %s\n", name, number)

	t := time.Now()
	write(port, "RING\r\n")
	time.Sleep(500 * time.Millisecond)
	write(port, fmt.Sprintf("DATE = %s\r\n", t.Format("0102")))
	write(port, fmt.Sprintf("TIME = %s\r\n", t.Format("1504")))
	write(port, fmt.Sprintf("NMBR = %s\r\n", number))
	write(port, fmt.Sprintf("NAME = %s\r\n", name))
	write(port, "RING\r\n")
	time.Sleep(500 * time.Millisecond)
	write(port, "RING\r\n")
	time.Sleep(500 * time.Millisecond)
}

func httpError(err error, w http.ResponseWriter, status int) {
	if err != nil {
		fmt.Println(err)
	}
	http.Error(w, "", status)
}

func write(p *serial.Port, s string) {
	if _, err := p.Write([]byte(s)); err != nil {
		println(err)
		return
	}
	fmt.Printf("<- %s\n", strings.TrimSpace(s))
}

func discardWhitespace(r *bufio.Reader) {
	c, _, err := r.ReadRune()
	if err != nil || !unicode.IsSpace(c) {
		r.UnreadRune()
		r.UnreadRune()
	}
}

type config struct {
	port string
	baud int
}

func parseConfig(s string) (*config, error) {
	a := strings.Split(s, ",")
	if len(a) != 5 {
		return nil, fmt.Errorf("expected 5 parameters, got %d", len(a))
	}

	baud, err := strconv.Atoi(a[1])
	if err != nil {
		return nil, err
	}

	cfg := &config{
		port: a[0],
		baud: baud,
	}

	return cfg, nil
}

func check(err error) {
	if err != nil {
		println(err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/cayleygraph/cayley"
	cayleyGraph "github.com/cayleygraph/cayley/graph"

	_ "github.com/cayleygraph/cayley/graph/kv/bolt" // required for cayley

	"nimona.io/internal/api"
	"nimona.io/internal/context"
	"nimona.io/internal/log"
	"nimona.io/internal/store/graph"
	"nimona.io/internal/version"
	"nimona.io/pkg/crypto"
	"nimona.io/pkg/discovery"
	"nimona.io/pkg/discovery/hyperspace"
	"nimona.io/pkg/exchange"
	"nimona.io/pkg/middleware/handshake"
	"nimona.io/pkg/net"
	"nimona.io/pkg/object/aggregate"
	"nimona.io/pkg/object/dag"
	"nimona.io/pkg/peer"
)

func main() {
	cfgFile := os.Getenv("NIMONA_CONFIG")
	if cfgFile == "" {
		cfgFile = ".nimona/config.json"
	}

	ctx := context.New(
		context.WithCorrelationID("mochi"),
	)

	logger := log.FromContext(ctx).With(
		log.String("configFile", cfgFile),
		log.String("build.version", version.Version),
		log.String("build.commit", version.Commit),
		log.String("build.timestamp", version.Date),
	)

	// load config
	logger.Info("loading config file")
	config, err := LoadConfig(cfgFile)
	if err != nil {
		logger.Fatal("could not load config file", log.Error(err))
	}

	// create peer key pair if it does not exist
	if config.Daemon.PeerKey == nil {
		logger.Info("creating new peer key pair")
		peerKey, err := crypto.GenerateKey()
		if err != nil {
			logger.Fatal("could not generate peer key", log.Error(err))
		}
		config.Daemon.PeerKey = peerKey
	}

	// create identity key pair if it does not exist
	// TODO this is temporary
	if config.Daemon.IdentityKey == nil {
		logger.Info("creating new identity key pair")
		identityKey, err := crypto.GenerateKey()
		if err != nil {
			logger.Fatal("could not generate identity key", log.Error(err))
		}
		config.Daemon.IdentityKey = identityKey
	}

	// make sure relays are valid
	for i, addr := range config.Daemon.RelayAddresses {
		if !strings.HasPrefix(addr, "relay:") {
			config.Daemon.RelayAddresses[i] = "relay:" + addr
		}
	}

	// update config
	if err := UpdateConfig(cfgFile, config); err != nil {
		logger.Fatal("could not update config", log.Error(err))
	}

	logger.Info("loaded config", log.Any("config", config))

	// check cayley quad store
	if err := cayleyGraph.InitQuadStore(
		"bolt",
		config.Daemon.ObjectPath,
		nil,
	); err != nil {
		logger.Fatal("could not init quad store", log.Error(err))
	}

	// start daemon

	// construct discoverer
	discoverer := discovery.NewDiscoverer()

	// construct local info
	localInfo, err := peer.NewLocalPeer(
		config.Daemon.AnnounceHostname,
		config.Daemon.PeerKey,
	)
	if err != nil {
		logger.Fatal("could not create local info", log.Error(err))
	}

	// add identity key to local info
	if err := localInfo.AddIdentityKey(config.Daemon.IdentityKey); err != nil {
		logger.Fatal("could not register identity key", log.Error(err))
	}

	// add relay addresses to local info
	localInfo.AddAddress("relay", config.Daemon.RelayAddresses)

	network, err := net.New(discoverer, localInfo)
	if err != nil {
		logger.Fatal("could not create network", log.Error(err))
	}

	// construct tcp transport
	tcpTransport := net.NewTCPTransport(
		localInfo,
		fmt.Sprintf("0.0.0.0:%d", config.Daemon.TCPPort),
	)

	// construct http transport
	httpTransport := net.NewHTTPTransport(
		localInfo,
		fmt.Sprintf("0.0.0.0:%d", config.Daemon.HTTPPort),
	)

	// add transports to network
	network.AddTransport("tcps", tcpTransport)
	network.AddTransport("https", httpTransport)

	// construct handshake
	handshakeMiddleware := handshake.New(
		localInfo,
		discoverer,
	)

	// add middleware to network
	network.AddMiddleware(handshakeMiddleware.Handle())

	// construct cayley db
	cayleyStore, err := cayley.NewGraph(
		"bolt",
		config.Daemon.ObjectPath,
		nil,
	)
	if err != nil {
		logger.Fatal("could not init graph store", log.Error(err))
	}

	// construct graph store
	graphStore := graph.NewCayley(cayleyStore)

	// construct exchange
	exchange, err := exchange.New(
		ctx,
		config.Daemon.PeerKey,
		network,
		graphStore,
		discoverer,
		localInfo,
	)
	if err != nil {
		logger.Fatal("could not construct exchange", log.Error(err))
	}

	// construct dag
	dag, err := dag.New(
		graphStore,
		exchange,
		nil,
		localInfo,
	)
	if err != nil {
		logger.Fatal("could not construct dag", log.Error(err))
	}

	// construct aggregate
	aggregate, err := aggregate.New(
		graphStore,
		exchange,
		dag,
	)
	if err != nil {
		logger.Fatal("could not construct dag", log.Error(err))
	}

	// construct hyperspace discoverer
	hyperspace, err := hyperspace.NewDiscoverer(
		ctx,
		network,
		exchange,
		localInfo,
		config.Daemon.BootstrapAddresses,
	)
	if err != nil {
		logger.Fatal("could not construct hyperspace", log.Error(err))
	}

	// add hyperspace provider
	if err := discoverer.AddProvider(hyperspace); err != nil {
		logger.Fatal("could not add hyperspace provider", log.Error(err))
	}

	// print some info
	fmt.Println("Started daemon")
	fmt.Println("* Peer fingerprint:\n  *", config.Daemon.PeerKey.Fingerprint())
	ik := config.Daemon.IdentityKey
	if ik != nil {
		fmt.Println("* Identity fingerprint:\n  *", ik.PublicKey.Fingerprint())
	}
	peerAddresses := localInfo.GetAddresses()
	fmt.Println("* Peer addresses:")
	if len(peerAddresses) > 0 {
		for _, addr := range peerAddresses {
			fmt.Println("  *", addr)
		}
	} else {
		fmt.Println("  * No addresses available")
	}

	// construct api server
	apiServer := api.New(
		config.Daemon.PeerKey,
		network,
		discoverer,
		exchange,
		localInfo,
		graphStore,
		dag,
		aggregate,
		version.Version,
		version.Commit,
		version.Date,
		config.API.Token,
	)

	fmt.Println("* HTTP API address:")
	fmt.Printf("  * http://localhost:%d\n", config.API.Port)
	apiServer.Serve(fmt.Sprintf("0.0.0.0:%d", config.API.Port))
}
<commit_msg>chore(cmd/nimona): fix correlation id, aggregate construction log<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/cayleygraph/cayley"
	cayleyGraph "github.com/cayleygraph/cayley/graph"

	_ "github.com/cayleygraph/cayley/graph/kv/bolt" // required for cayley

	"nimona.io/internal/api"
	"nimona.io/internal/context"
	"nimona.io/internal/log"
	"nimona.io/internal/store/graph"
	"nimona.io/internal/version"
	"nimona.io/pkg/crypto"
	"nimona.io/pkg/discovery"
	"nimona.io/pkg/discovery/hyperspace"
	"nimona.io/pkg/exchange"
	"nimona.io/pkg/middleware/handshake"
	"nimona.io/pkg/net"
	"nimona.io/pkg/object/aggregate"
	"nimona.io/pkg/object/dag"
	"nimona.io/pkg/peer"
)

func main() {
	cfgFile := os.Getenv("NIMONA_CONFIG")
	if cfgFile == "" {
		cfgFile = ".nimona/config.json"
	}

	ctx := context.New(
		context.WithCorrelationID("nimona"),
	)

	logger := log.FromContext(ctx).With(
		log.String("configFile", cfgFile),
		log.String("build.version", version.Version),
		log.String("build.commit", version.Commit),
		log.String("build.timestamp", version.Date),
	)

	// load config
	logger.Info("loading config file")
	config, err := LoadConfig(cfgFile)
	if err != nil {
		logger.Fatal("could not load config file", log.Error(err))
	}

	// create peer key pair if it does not exist
	if config.Daemon.PeerKey == nil {
		logger.Info("creating new peer key pair")
		peerKey, err := crypto.GenerateKey()
		if err != nil {
			logger.Fatal("could not generate peer key", log.Error(err))
		}
		config.Daemon.PeerKey = peerKey
	}

	// create identity key pair if it does not exist
	// TODO this is temporary
	if config.Daemon.IdentityKey == nil {
		logger.Info("creating new identity key pair")
		identityKey, err := crypto.GenerateKey()
		if err != nil {
			logger.Fatal("could not generate identity key", log.Error(err))
		}
		config.Daemon.IdentityKey = identityKey
	}

	// make sure relays are valid
	for i, addr := range config.Daemon.RelayAddresses {
		if !strings.HasPrefix(addr, "relay:") {
			config.Daemon.RelayAddresses[i] = "relay:" + addr
		}
	}

	// update config
	if err := UpdateConfig(cfgFile, config); err != nil {
		logger.Fatal("could not update config", log.Error(err))
	}

	logger.Info("loaded config", log.Any("config", config))

	// check cayley quad store
	if err := cayleyGraph.InitQuadStore(
		"bolt",
		config.Daemon.ObjectPath,
		nil,
	); err != nil {
		logger.Fatal("could not init quad store", log.Error(err))
	}

	// start daemon

	// construct discoverer
	discoverer := discovery.NewDiscoverer()

	// construct local info
	localInfo, err := peer.NewLocalPeer(
		config.Daemon.AnnounceHostname,
		config.Daemon.PeerKey,
	)
	if err != nil {
		logger.Fatal("could not create local info", log.Error(err))
	}

	// add identity key to local info
	if err := localInfo.AddIdentityKey(config.Daemon.IdentityKey); err != nil {
		logger.Fatal("could not register identity key", log.Error(err))
	}

	// add relay addresses to local info
	localInfo.AddAddress("relay", config.Daemon.RelayAddresses)

	network, err := net.New(discoverer, localInfo)
	if err != nil {
		logger.Fatal("could not create network", log.Error(err))
	}

	// construct tcp transport
	tcpTransport := net.NewTCPTransport(
		localInfo,
		fmt.Sprintf("0.0.0.0:%d", config.Daemon.TCPPort),
	)

	// construct http transport
	httpTransport := net.NewHTTPTransport(
		localInfo,
		fmt.Sprintf("0.0.0.0:%d", config.Daemon.HTTPPort),
	)

	// add transports to network
	network.AddTransport("tcps", tcpTransport)
	network.AddTransport("https", httpTransport)

	// construct handshake
	handshakeMiddleware := handshake.New(
		localInfo,
		discoverer,
	)

	// add middleware to network
	network.AddMiddleware(handshakeMiddleware.Handle())

	// construct cayley db
	cayleyStore, err := cayley.NewGraph(
		"bolt",
		config.Daemon.ObjectPath,
		nil,
	)
	if err != nil {
		logger.Fatal("could not init graph store", log.Error(err))
	}

	// construct graph store
	graphStore := graph.NewCayley(cayleyStore)

	// construct exchange
	exchange, err := exchange.New(
		ctx,
		config.Daemon.PeerKey,
		network,
		graphStore,
		discoverer,
		localInfo,
	)
	if err != nil {
		logger.Fatal("could not construct exchange", log.Error(err))
	}

	// construct dag
	dag, err := dag.New(
		graphStore,
		exchange,
		nil,
		localInfo,
	)
	if err != nil {
		logger.Fatal("could not construct dag", log.Error(err))
	}

	// construct aggregate
	aggregate, err := aggregate.New(
		graphStore,
		exchange,
		dag,
	)
	if err != nil {
		logger.Fatal("could not construct aggregate", log.Error(err))
	}

	// construct hyperspace discoverer
	hyperspace, err := hyperspace.NewDiscoverer(
		ctx,
		network,
		exchange,
		localInfo,
		config.Daemon.BootstrapAddresses,
	)
	if err != nil {
		logger.Fatal("could not construct hyperspace", log.Error(err))
	}

	// add hyperspace provider
	if err := discoverer.AddProvider(hyperspace); err != nil {
		logger.Fatal("could not add hyperspace provider", log.Error(err))
	}

	// print some info
	fmt.Println("Started daemon")
	fmt.Println("* Peer fingerprint:\n  *", config.Daemon.PeerKey.Fingerprint())
	ik := config.Daemon.IdentityKey
	if ik != nil {
		fmt.Println("* Identity fingerprint:\n  *", ik.PublicKey.Fingerprint())
	}
	peerAddresses := localInfo.GetAddresses()
	fmt.Println("* Peer addresses:")
	if len(peerAddresses) > 0 {
		for _, addr := range peerAddresses {
			fmt.Println("  *", addr)
		}
	} else {
		fmt.Println("  * No addresses available")
	}

	// construct api server
	apiServer := api.New(
		config.Daemon.PeerKey,
		network,
		discoverer,
		exchange,
		localInfo,
		graphStore,
		dag,
		aggregate,
		version.Version,
		version.Commit,
		version.Date,
		config.API.Token,
	)

	fmt.Println("* HTTP API address:")
	fmt.Printf("  * http://localhost:%d\n", config.API.Port)
	apiServer.Serve(fmt.Sprintf("0.0.0.0:%d", config.API.Port))
}
<|endoftext|>


<commit_before>// +build !appengine

// main.go holds bits needed when not serving hkjnweb on appengine.
package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"

	"hkjn.me/hkjnweb"
)

// registerStatic registers handler for static files.
func registerStatic(dir string) {
	if dir == "" {
		dir = "static"
	}
	path := fmt.Sprintf("/%s/", dir)
	h := http.StripPrefix(
		path,
		http.FileServer(http.Dir(dir)))
	http.Handle(path, h)
}

func main() {
	flag.Parse()
	hkjnweb.Register(os.Getenv("PROD") != "")
	//	hkjnweb.Logger = autosite.Glogger{}
	registerStatic(os.Getenv("STATIC_DIR"))

	addr := os.Getenv("BIND_ADDR")
	if addr == "" {
		addr = ":12345"
	}
	if os.Getenv("SERVE_HTTP") != "" {
		log.Printf("webserver serving HTTP on %s..\n", addr)
		err := http.ListenAndServe(addr, nil)
		if err != nil {
			log.Fatalf("FATAL: http server exited: %v\n", err)
		}
	} else {
		log.Println("Since SERVE_HTTP isn't set, we should serve https")
		certFile := os.Getenv("HTTPS_CERT_FILE")
		keyFile := os.Getenv("HTTPS_KEY_FILE")
		if certFile == "" || keyFile == "" {
			log.Fatalln("FATAL: No HTTPS_CERT_FILE or HTTPS_KEY_FILE specified.")
		}
		log.Printf("webserver serving HTTPS on %s..\n", addr)
		err := http.ListenAndServeTLS(addr, certFile, keyFile, nil)
		if err != nil {
			log.Fatalf("FATAL: https server exited: %v\n", err)
		}
	}
}
<commit_msg>Drop commented-out line<commit_after>// +build !appengine

// main.go holds bits needed when not serving hkjnweb on appengine.
package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"

	"hkjn.me/hkjnweb"
)

// registerStatic registers handler for static files.
func registerStatic(dir string) {
	if dir == "" {
		dir = "static"
	}
	path := fmt.Sprintf("/%s/", dir)
	h := http.StripPrefix(
		path,
		http.FileServer(http.Dir(dir)))
	http.Handle(path, h)
}

func main() {
	flag.Parse()
	hkjnweb.Register(os.Getenv("PROD") != "")
	registerStatic(os.Getenv("STATIC_DIR"))

	addr := os.Getenv("BIND_ADDR")
	if addr == "" {
		addr = ":12345"
	}
	if os.Getenv("SERVE_HTTP") != "" {
		log.Printf("webserver serving HTTP on %s..\n", addr)
		err := http.ListenAndServe(addr, nil)
		if err != nil {
			log.Fatalf("FATAL: http server exited: %v\n", err)
		}
	} else {
		log.Println("Since SERVE_HTTP isn't set, we should serve https")
		certFile := os.Getenv("HTTPS_CERT_FILE")
		keyFile := os.Getenv("HTTPS_KEY_FILE")
		if certFile == "" || keyFile == "" {
			log.Fatalln("FATAL: No HTTPS_CERT_FILE or HTTPS_KEY_FILE specified.")
		}
		log.Printf("webserver serving HTTPS on %s..\n", addr)
		err := http.ListenAndServeTLS(addr, certFile, keyFile, nil)
		if err != nil {
			log.Fatalf("FATAL: https server exited: %v\n", err)
		}
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"bufio"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/MakeNowJust/heredoc/v2"
	"github.com/araddon/dateparse"
	"github.com/charmbracelet/glamour"
	"github.com/charmbracelet/glamour/ansi"
	"github.com/fatih/color"
	"github.com/jaytaylor/html2text"
	"github.com/muesli/termenv"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/config"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

func inRange(val int, min int, max int) bool {
	return val >= min && val <= max
}

// maxPadding returns the max value of two string numbers
func maxPadding(x int, y int) int {
	if x > y {
		return len(strconv.Itoa(x))
	}
	return len(strconv.Itoa(y))
}

// printDiffLine does a color print of a diff lines.  Red lines are removals
// and green lines are additions.
func printDiffLine(strColor string, maxChars int, sOld string, sNew string, ltext string) string {

	switch strColor {
	case "green":
		return color.GreenString("|%*s %*s %s\n", maxChars, sOld, maxChars, sNew, ltext)
	case "red":
		return color.RedString("|%*s %*s %s\n", maxChars, sOld, maxChars, sNew, ltext)
	}
	return fmt.Sprintf("|%*s %*s %s\n", maxChars, sOld, maxChars, sNew, ltext)
}

// displayDiff displays the diff referenced in a discussion
func displayDiff(diff string, newLine int, oldLine int, outputAll bool) string {
	var (
		oldLineNum int = 0
		newLineNum int = 0
		maxChars   int
		output     bool   = false
		diffOutput string = ""
	)

	scanner := bufio.NewScanner(strings.NewReader(diff))
	for scanner.Scan() {
		if regexp.MustCompile(`^@@`).MatchString(scanner.Text()) {
			s := strings.Split(scanner.Text(), " ")
			dOld := strings.Split(s[1], ",")
			dNew := strings.Split(s[2], ",")

			// get the new line number of the first line of the diff
			newDiffStart, err := strconv.Atoi(strings.Replace(dNew[0], "+", "", -1))
			if err != nil {
				log.Fatal(err)
			}
			newDiffRange, err := strconv.Atoi(dNew[1])
			if err != nil {
				log.Fatal(err)
			}
			newDiffEnd := newDiffStart + newDiffRange
			newLineNum = newDiffStart - 1

			// get the old line number of the first line of the diff
			oldDiffStart, err := strconv.Atoi(strings.Replace(dOld[0], "-", "", -1))
			if err != nil {
				log.Fatal(err)
			}

			oldDiffEnd := newDiffRange
			oldLineNum = oldDiffStart - 1
			if len(dOld) > 1 {
				oldDiffRange, err := strconv.Atoi(dOld[1])
				if err != nil {
					log.Fatal(err)
				}
				oldDiffEnd = oldDiffStart + oldDiffRange
			}

			if (oldLine != 0) && inRange(oldLine, oldDiffStart, oldDiffEnd) {
				output = true
			} else if (newLine != 0) && inRange(newLine, newDiffStart, newDiffEnd) {
				output = true
			} else {
				output = false
			}

			if outputAll {
				mrShowNoColorDiff = true
				output = true
			}

			// padding to align diff output (depends on the line numbers' length)
			// The patch can have, for example, either
			// @@ -1 +1 @@
			// or
			// @@ -1272,6 +1272,8 @@
			// so (len - 1) makes sense in both cases.
			maxChars = maxPadding(oldDiffEnd, newDiffEnd) + 1
		}

		if !output {
			continue
		}

		var (
			sOld string
			sNew string
		)
		strColor := ""
		ltext := scanner.Text()
		ltag := string(ltext[0])
		switch ltag {
		case " ":
			strColor = ""
			oldLineNum++
			newLineNum++
			sOld = strconv.Itoa(oldLineNum)
			sNew = strconv.Itoa(newLineNum)
		case "-":
			strColor = "red"
			oldLineNum++
			sOld = strconv.Itoa(oldLineNum)
			sNew = " "
		case "+":
			strColor = "green"
			newLineNum++
			sOld = " "
			sNew = strconv.Itoa(newLineNum)
		}

		// output line
		if mrShowNoColorDiff {
			strColor = ""
		}
		if outputAll {
			diffOutput += printDiffLine(strColor, maxChars, sOld, sNew, ltext)
		} else if newLine != 0 {
			if newLineNum <= newLine && newLineNum >= (newLine-4) {
				diffOutput += printDiffLine(strColor, maxChars, sOld, sNew, ltext)
			}
		} else if oldLineNum <= oldLine && oldLineNum >= (oldLine-4) {
			diffOutput += printDiffLine(strColor, maxChars, sOld, sNew, ltext)
		}
	}
	return diffOutput
}

func displayCommitDiscussion(project string, idNum int, note *gitlab.Note) {

	// Previously, the GitLab API only supports showing comments on the
	// entire changeset and not per-commit.  IOW, all diffs were shown
	// against HEAD.  This was confusing in some scenarios, however it
	// was what the API provided.
	//
	// At some point, note.Position.HeadSHA was changed to report the
	// commit that was being commented on, and note.Position.BaseSHA
	// points to the commit just before it.  I cannot find the GitLab
	// commit that changed this behaviour but it has changed.
	//
	// I am leaving this comment here in case it ever changes again.

	// Get a unified diff for the entire file
	ds, err := lab.GetCommitDiff(project, note.CommitID)
	if err != nil {
		fmt.Printf("    Could not get diff for commit %s.\n", note.CommitID)
		return
	}

	if len(ds) == 0 {
		log.Fatal("    No diff found for %s.", note.CommitID)
	}

	// In general, only have to display the NewPath, however there
	// are some unusual cases where the OldPath may be displayed
	if note.Position.NewPath == note.Position.OldPath {
		fmt.Println("commit:" + note.CommitID)
		fmt.Println("File:" + note.Position.NewPath)
	} else {
		fmt.Println("commit:" + note.CommitID)
		fmt.Println("Files[old:" + note.Position.OldPath + " new:" + note.Position.NewPath + "]")
	}

	for _, d := range ds {
		if note.Position.NewPath == d.NewPath && note.Position.OldPath == d.OldPath {
			newLine := note.Position.NewLine
			oldLine := note.Position.OldLine
			if note.Position.LineRange != nil {
				newLine = note.Position.LineRange.StartRange.NewLine
				oldLine = note.Position.LineRange.StartRange.OldLine
			}
			diffstring := displayDiff(d.Diff, newLine, oldLine, false)
			fmt.Printf(diffstring)
		}
	}
	fmt.Println("")
}

func getBoldStyle() ansi.StyleConfig {
	var style ansi.StyleConfig
	if termenv.HasDarkBackground() {
		style = glamour.DarkStyleConfig
	} else {
		style = glamour.LightStyleConfig
	}
	bold := true
	style.Document.Bold = &bold
	return style
}

func getTermRenderer(style glamour.TermRendererOption) (*glamour.TermRenderer, error) {
	r, err := glamour.NewTermRenderer(
		glamour.WithWordWrap(0),
		// There are PAGERs and TERMs that supports only 16 colors,
		// since we aren't a beauty-driven project, lets use it.
		glamour.WithColorProfile(termenv.ANSI),
		style,
	)
	return r, err
}

func printDiscussions(project string, discussions []*gitlab.Discussion, since string, idstr string, idNum int, renderMarkdown bool) {
	newAccessTime := time.Now().UTC()

	issueEntry := fmt.Sprintf("%s%d", idstr, idNum)
	// if specified on command line use that, o/w use config, o/w Now
	var (
		comparetime time.Time
		err         error
		sinceIsSet  = true
	)
	comparetime, err = dateparse.ParseLocal(since)
	if err != nil || comparetime.IsZero() {
		comparetime = getMainConfig().GetTime(commandPrefix + issueEntry)
		if comparetime.IsZero() {
			comparetime = time.Now().UTC()
		}
		sinceIsSet = false
	}

	mdRendererNormal, _ := getTermRenderer(glamour.WithAutoStyle())
	mdRendererBold, _ := getTermRenderer(glamour.WithStyles(getBoldStyle()))

	// for available fields, see
	// https://godoc.org/github.com/xanzy/go-gitlab#Note
	// https://godoc.org/github.com/xanzy/go-gitlab#Discussion
	for _, discussion := range discussions {
		for i, note := range discussion.Notes {

			indentHeader, indentNote := "", ""
			commented := "commented"
			if !time.Time(*note.CreatedAt).Equal(time.Time(*note.UpdatedAt)) {
				commented = "updated comment"
			}

			if !discussion.IndividualNote {
				indentNote = "    "

				if i == 0 {
					commented = "started a discussion"
					if note.Position != nil {
						commented = "started a commit discussion"
					}
				} else {
					indentHeader = "    "
				}
			}

			noteBody := strings.Replace(note.Body, "\n", "
\n", -1)
			html2textOptions := html2text.Options{
				PrettyTables: true,
				OmitLinks:    true,
			}
			noteBody, _ = html2text.FromString(noteBody, html2textOptions)
			mdRenderer := mdRendererNormal
			printit := color.New().PrintfFunc()
			if note.System {
				splitNote := strings.SplitN(noteBody, "\n", 2)

				// system notes are informational messages only
				// and cannot have replies.  Do not output the
				// note.ID
				printit(
					heredoc.Doc(`
						* %s %s at %s
					`),
					note.Author.Username, splitNote[0], time.Time(*note.UpdatedAt).String())
				if len(splitNote) == 2 {
					if renderMarkdown {
						splitNote[1], _ = mdRenderer.Render(splitNote[1])
					}
					printit(
						heredoc.Doc(`
							%s
						`),
						splitNote[1])
				}
				continue
			}

			printit(
				heredoc.Doc(`
					%s-----------------------------------
				`),
				indentHeader)

			if time.Time(*note.UpdatedAt).After(comparetime) {
				mdRenderer = mdRendererBold
				printit = color.New(color.Bold).PrintfFunc()
			}

			if renderMarkdown {
				noteBody, _ = mdRenderer.Render(noteBody)
			}
			noteBody = strings.Replace(noteBody, "\n", "\n"+indentNote, -1)

			printit(`%s#%d: %s %s at %s

`,
				indentHeader, note.ID, note.Author.Username, commented, time.Time(*note.UpdatedAt).String())
			if note.Position != nil && i == 0 {
				displayCommitDiscussion(project, idNum, note)
			}
			printit(`%s%s
`,

				indentNote, noteBody)
		}
	}

	if !sinceIsSet {
		config.WriteConfigEntry(commandPrefix+issueEntry, newAccessTime, "", "")
	}
}
<commit_msg>lab: Use HeadSHA value when CommitID is not specified<commit_after>package cmd

import (
	"bufio"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/MakeNowJust/heredoc/v2"
	"github.com/araddon/dateparse"
	"github.com/charmbracelet/glamour"
	"github.com/charmbracelet/glamour/ansi"
	"github.com/fatih/color"
	"github.com/jaytaylor/html2text"
	"github.com/muesli/termenv"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/config"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

func inRange(val int, min int, max int) bool {
	return val >= min && val <= max
}

// maxPadding returns the max value of two string numbers
func maxPadding(x int, y int) int {
	if x > y {
		return len(strconv.Itoa(x))
	}
	return len(strconv.Itoa(y))
}

// printDiffLine does a color print of a diff lines.  Red lines are removals
// and green lines are additions.
func printDiffLine(strColor string, maxChars int, sOld string, sNew string, ltext string) string {

	switch strColor {
	case "green":
		return color.GreenString("|%*s %*s %s\n", maxChars, sOld, maxChars, sNew, ltext)
	case "red":
		return color.RedString("|%*s %*s %s\n", maxChars, sOld, maxChars, sNew, ltext)
	}
	return fmt.Sprintf("|%*s %*s %s\n", maxChars, sOld, maxChars, sNew, ltext)
}

// displayDiff displays the diff referenced in a discussion
func displayDiff(diff string, newLine int, oldLine int, outputAll bool) string {
	var (
		oldLineNum int = 0
		newLineNum int = 0
		maxChars   int
		output     bool   = false
		diffOutput string = ""
	)

	scanner := bufio.NewScanner(strings.NewReader(diff))
	for scanner.Scan() {
		if regexp.MustCompile(`^@@`).MatchString(scanner.Text()) {
			s := strings.Split(scanner.Text(), " ")
			dOld := strings.Split(s[1], ",")
			dNew := strings.Split(s[2], ",")

			// get the new line number of the first line of the diff
			newDiffStart, err := strconv.Atoi(strings.Replace(dNew[0], "+", "", -1))
			if err != nil {
				log.Fatal(err)
			}
			newDiffRange, err := strconv.Atoi(dNew[1])
			if err != nil {
				log.Fatal(err)
			}
			newDiffEnd := newDiffStart + newDiffRange
			newLineNum = newDiffStart - 1

			// get the old line number of the first line of the diff
			oldDiffStart, err := strconv.Atoi(strings.Replace(dOld[0], "-", "", -1))
			if err != nil {
				log.Fatal(err)
			}

			oldDiffEnd := newDiffRange
			oldLineNum = oldDiffStart - 1
			if len(dOld) > 1 {
				oldDiffRange, err := strconv.Atoi(dOld[1])
				if err != nil {
					log.Fatal(err)
				}
				oldDiffEnd = oldDiffStart + oldDiffRange
			}

			if (oldLine != 0) && inRange(oldLine, oldDiffStart, oldDiffEnd) {
				output = true
			} else if (newLine != 0) && inRange(newLine, newDiffStart, newDiffEnd) {
				output = true
			} else {
				output = false
			}

			if outputAll {
				mrShowNoColorDiff = true
				output = true
			}

			// padding to align diff output (depends on the line numbers' length)
			// The patch can have, for example, either
			// @@ -1 +1 @@
			// or
			// @@ -1272,6 +1272,8 @@
			// so (len - 1) makes sense in both cases.
			maxChars = maxPadding(oldDiffEnd, newDiffEnd) + 1
		}

		if !output {
			continue
		}

		var (
			sOld string
			sNew string
		)
		strColor := ""
		ltext := scanner.Text()
		ltag := string(ltext[0])
		switch ltag {
		case " ":
			strColor = ""
			oldLineNum++
			newLineNum++
			sOld = strconv.Itoa(oldLineNum)
			sNew = strconv.Itoa(newLineNum)
		case "-":
			strColor = "red"
			oldLineNum++
			sOld = strconv.Itoa(oldLineNum)
			sNew = " "
		case "+":
			strColor = "green"
			newLineNum++
			sOld = " "
			sNew = strconv.Itoa(newLineNum)
		}

		// output line
		if mrShowNoColorDiff {
			strColor = ""
		}
		if outputAll {
			diffOutput += printDiffLine(strColor, maxChars, sOld, sNew, ltext)
		} else if newLine != 0 {
			if newLineNum <= newLine && newLineNum >= (newLine-4) {
				diffOutput += printDiffLine(strColor, maxChars, sOld, sNew, ltext)
			}
		} else if oldLineNum <= oldLine && oldLineNum >= (oldLine-4) {
			diffOutput += printDiffLine(strColor, maxChars, sOld, sNew, ltext)
		}
	}
	return diffOutput
}

func displayCommitDiscussion(project string, idNum int, note *gitlab.Note) {

	// Previously, the GitLab API only supports showing comments on the
	// entire changeset and not per-commit.  IOW, all diffs were shown
	// against HEAD.  This was confusing in some scenarios, however it
	// was what the API provided.
	//
	// At some point, note.Position.HeadSHA was changed to report the
	// commit that was being commented on, and note.Position.BaseSHA
	// points to the commit just before it.  I cannot find the GitLab
	// commit that changed this behaviour but it has changed.
	//
	// I am leaving this comment here in case it ever changes again.

	// In some cases the CommitID field is still not populated correctly.
	// In those cases use the HeadSHA value instead of the CommitID.
	commitID := note.CommitID
	if commitID == "" {
		commitID = note.Position.HeadSHA
	}

	// Get a unified diff for the entire file
	ds, err := lab.GetCommitDiff(project, commitID)
	if err != nil {
		fmt.Printf("    Could not get diff for commit %s.\n", commitID)
		return
	}

	if len(ds) == 0 {
		log.Fatal("    No diff found for %s.", commitID)
	}

	// In general, only have to display the NewPath, however there
	// are some unusual cases where the OldPath may be displayed
	if note.Position.NewPath == note.Position.OldPath {
		fmt.Println("commit:" + commitID)
		fmt.Println("File:" + note.Position.NewPath)
	} else {
		fmt.Println("commit:" + commitID)
		fmt.Println("Files[old:" + note.Position.OldPath + " new:" + note.Position.NewPath + "]")
	}

	for _, d := range ds {
		if note.Position.NewPath == d.NewPath && note.Position.OldPath == d.OldPath {
			newLine := note.Position.NewLine
			oldLine := note.Position.OldLine
			if note.Position.LineRange != nil {
				newLine = note.Position.LineRange.StartRange.NewLine
				oldLine = note.Position.LineRange.StartRange.OldLine
			}
			diffstring := displayDiff(d.Diff, newLine, oldLine, false)
			fmt.Printf(diffstring)
		}
	}
	fmt.Println("")
}

func getBoldStyle() ansi.StyleConfig {
	var style ansi.StyleConfig
	if termenv.HasDarkBackground() {
		style = glamour.DarkStyleConfig
	} else {
		style = glamour.LightStyleConfig
	}
	bold := true
	style.Document.Bold = &bold
	return style
}

func getTermRenderer(style glamour.TermRendererOption) (*glamour.TermRenderer, error) {
	r, err := glamour.NewTermRenderer(
		glamour.WithWordWrap(0),
		// There are PAGERs and TERMs that supports only 16 colors,
		// since we aren't a beauty-driven project, lets use it.
		glamour.WithColorProfile(termenv.ANSI),
		style,
	)
	return r, err
}

func printDiscussions(project string, discussions []*gitlab.Discussion, since string, idstr string, idNum int, renderMarkdown bool) {
	newAccessTime := time.Now().UTC()

	issueEntry := fmt.Sprintf("%s%d", idstr, idNum)
	// if specified on command line use that, o/w use config, o/w Now
	var (
		comparetime time.Time
		err         error
		sinceIsSet  = true
	)
	comparetime, err = dateparse.ParseLocal(since)
	if err != nil || comparetime.IsZero() {
		comparetime = getMainConfig().GetTime(commandPrefix + issueEntry)
		if comparetime.IsZero() {
			comparetime = time.Now().UTC()
		}
		sinceIsSet = false
	}

	mdRendererNormal, _ := getTermRenderer(glamour.WithAutoStyle())
	mdRendererBold, _ := getTermRenderer(glamour.WithStyles(getBoldStyle()))

	// for available fields, see
	// https://godoc.org/github.com/xanzy/go-gitlab#Note
	// https://godoc.org/github.com/xanzy/go-gitlab#Discussion
	for _, discussion := range discussions {
		for i, note := range discussion.Notes {

			indentHeader, indentNote := "", ""
			commented := "commented"
			if !time.Time(*note.CreatedAt).Equal(time.Time(*note.UpdatedAt)) {
				commented = "updated comment"
			}

			if !discussion.IndividualNote {
				indentNote = "    "

				if i == 0 {
					commented = "started a discussion"
					if note.Position != nil {
						commented = "started a commit discussion"
					}
				} else {
					indentHeader = "    "
				}
			}

			noteBody := strings.Replace(note.Body, "\n", "
\n", -1)
			html2textOptions := html2text.Options{
				PrettyTables: true,
				OmitLinks:    true,
			}
			noteBody, _ = html2text.FromString(noteBody, html2textOptions)
			mdRenderer := mdRendererNormal
			printit := color.New().PrintfFunc()
			if note.System {
				splitNote := strings.SplitN(noteBody, "\n", 2)

				// system notes are informational messages only
				// and cannot have replies.  Do not output the
				// note.ID
				printit(
					heredoc.Doc(`
						* %s %s at %s
					`),
					note.Author.Username, splitNote[0], time.Time(*note.UpdatedAt).String())
				if len(splitNote) == 2 {
					if renderMarkdown {
						splitNote[1], _ = mdRenderer.Render(splitNote[1])
					}
					printit(
						heredoc.Doc(`
							%s
						`),
						splitNote[1])
				}
				continue
			}

			printit(
				heredoc.Doc(`
					%s-----------------------------------
				`),
				indentHeader)

			if time.Time(*note.UpdatedAt).After(comparetime) {
				mdRenderer = mdRendererBold
				printit = color.New(color.Bold).PrintfFunc()
			}

			if renderMarkdown {
				noteBody, _ = mdRenderer.Render(noteBody)
			}
			noteBody = strings.Replace(noteBody, "\n", "\n"+indentNote, -1)

			printit(`%s#%d: %s %s at %s

`,
				indentHeader, note.ID, note.Author.Username, commented, time.Time(*note.UpdatedAt).String())
			if note.Position != nil && i == 0 {
				displayCommitDiscussion(project, idNum, note)
			}
			printit(`%s%s
`,

				indentNote, noteBody)
		}
	}

	if !sinceIsSet {
		config.WriteConfigEntry(commandPrefix+issueEntry, newAccessTime, "", "")
	}
}
<|endoftext|>


<commit_before>package utils

import (
	"crypto/ecdsa"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"runtime"

	"github.com/codegangsta/cli"
	"github.com/ethereum/ethash"
	"github.com/ethereum/go-ethereum/accounts"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/eth"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/p2p/nat"
	"github.com/ethereum/go-ethereum/rpc"
	"github.com/ethereum/go-ethereum/xeth"
)

func init() {
	cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]

VERSION:
   {{.Version}}

COMMANDS:
   {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
   {{end}}{{if .Flags}}
GLOBAL OPTIONS:
   {{range .Flags}}{{.}}
   {{end}}{{end}}
`

	cli.CommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
	{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
	{{end}}{{end}}{{if .Flags}}
OPTIONS:
	{{range .Flags}}{{.}}
	{{end}}{{end}}
`
}

// NewApp creates an app with sane defaults.
func NewApp(version, usage string) *cli.App {
	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Author = ""
	//app.Authors = nil
	app.Email = ""
	app.Version = version
	app.Usage = usage
	return app
}

// These are all the command line flags we support.
// If you add to this list, please remember to include the
// flag in the appropriate command definition.
//
// The flags are defined here so their names and help texts
// are the same for all commands.

var (
	// General settings
	DataDirFlag = DirectoryFlag{
		Name:  "datadir",
		Usage: "Data directory to be used",
		Value: DirectoryString{common.DefaultDataDir()},
	}
	ProtocolVersionFlag = cli.IntFlag{
		Name:  "protocolversion",
		Usage: "ETH protocol version (integer)",
		Value: eth.ProtocolVersion,
	}
	NetworkIdFlag = cli.IntFlag{
		Name:  "networkid",
		Usage: "Network Id (integer)",
		Value: eth.NetworkId,
	}
	BlockchainVersionFlag = cli.IntFlag{
		Name:  "blockchainversion",
		Usage: "Blockchain version (integer)",
		Value: core.BlockChainVersion,
	}
	IdentityFlag = cli.StringFlag{
		Name:  "identity",
		Usage: "Custom node name",
	}
	NatspecEnabledFlag = cli.BoolFlag{
		Name:  "natspec",
		Usage: "Enable NatSpec confirmation notice",
	}

	// miner settings
	MinerThreadsFlag = cli.IntFlag{
		Name:  "minerthreads",
		Usage: "Number of miner threads",
		Value: runtime.NumCPU(),
	}
	MiningEnabledFlag = cli.BoolFlag{
		Name:  "mine",
		Usage: "Enable mining",
	}
	EtherbaseFlag = cli.StringFlag{
		Name:  "etherbase",
		Usage: "Public address for block mining rewards. By default the address of your primary account is used",
		Value: "primary",
	}

	UnlockedAccountFlag = cli.StringFlag{
		Name:  "unlock",
		Usage: "Unlock the account given until this program exits (prompts for password). '--unlock primary' unlocks the primary account",
		Value: "",
	}
	PasswordFileFlag = cli.StringFlag{
		Name:  "password",
		Usage: "Path to password file to use with options and subcommands needing a password",
		Value: "",
	}

	// logging and debug settings
	LogFileFlag = cli.StringFlag{
		Name:  "logfile",
		Usage: "Send log output to a file",
	}
	LogLevelFlag = cli.IntFlag{
		Name:  "loglevel",
		Usage: "Logging verbosity: 0-6 (0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=debug detail)",
		Value: int(logger.InfoLevel),
	}
	LogJSONFlag = cli.StringFlag{
		Name:  "logjson",
		Usage: "Send json structured log output to a file or '-' for standard output (default: no json output)",
		Value: "",
	}
	LogToStdErrFlag = cli.BoolFlag{
		Name:  "logtostderr",
		Usage: "Logs are written to standard error instead of to files.",
	}
	LogVModuleFlag = cli.GenericFlag{
		Name:  "vmodule",
		Usage: "The syntax of the argument is a comma-separated list of pattern=N, where pattern is a literal file name (minus the \".go\" suffix) or \"glob\" pattern and N is a log verbosity level.",
		Value: glog.GetVModule(),
	}
	VMDebugFlag = cli.BoolFlag{
		Name:  "vmdebug",
		Usage: "Virtual Machine debug output",
	}
	BacktraceAtFlag = cli.GenericFlag{
		Name:  "backtrace_at",
		Usage: "If set to a file and line number (e.g., \"block.go:271\") holding a logging statement, a stack trace will be logged",
		Value: glog.GetTraceLocation(),
	}
	PProfEanbledFlag = cli.BoolFlag{
		Name:  "pprof",
		Usage: "Enable the profiling server on localhost",
	}
	PProfPortFlag = cli.IntFlag{
		Name:  "pprofport",
		Usage: "Port on which the profiler should listen",
		Value: 6060,
	}

	// RPC settings
	RPCEnabledFlag = cli.BoolFlag{
		Name:  "rpc",
		Usage: "Enable the JSON-RPC server",
	}
	RPCListenAddrFlag = cli.StringFlag{
		Name:  "rpcaddr",
		Usage: "Listening address for the JSON-RPC server",
		Value: "127.0.0.1",
	}
	RPCPortFlag = cli.IntFlag{
		Name:  "rpcport",
		Usage: "Port on which the JSON-RPC server should listen",
		Value: 8545,
	}
	RPCCORSDomainFlag = cli.StringFlag{
		Name:  "rpccorsdomain",
		Usage: "Domain on which to send Access-Control-Allow-Origin header",
		Value: "",
	}
	// Network Settings
	MaxPeersFlag = cli.IntFlag{
		Name:  "maxpeers",
		Usage: "Maximum number of network peers (network disabled if set to 0)",
		Value: 16,
	}
	MaxPendingPeersFlag = cli.IntFlag{
		Name:  "maxpendpeers",
		Usage: "Maximum number of pending connection attempts (defaults used if set to 0)",
		Value: 0,
	}
	ListenPortFlag = cli.IntFlag{
		Name:  "port",
		Usage: "Network listening port",
		Value: 30303,
	}
	BootnodesFlag = cli.StringFlag{
		Name:  "bootnodes",
		Usage: "Space-separated enode URLs for p2p discovery bootstrap",
		Value: "",
	}
	NodeKeyFileFlag = cli.StringFlag{
		Name:  "nodekey",
		Usage: "P2P node key file",
	}
	NodeKeyHexFlag = cli.StringFlag{
		Name:  "nodekeyhex",
		Usage: "P2P node key as hex (for testing)",
	}
	NATFlag = cli.StringFlag{
		Name:  "nat",
		Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>)",
		Value: "any",
	}
	WhisperEnabledFlag = cli.BoolFlag{
		Name:  "shh",
		Usage: "Enable whisper",
	}
	// ATM the url is left to the user and deployment to
	JSpathFlag = cli.StringFlag{
		Name:  "jspath",
		Usage: "JS library path to be used with console and js subcommands",
		Value: ".",
	}
	SolcPathFlag = cli.StringFlag{
		Name:  "solc",
		Usage: "solidity compiler to be used",
		Value: "solc",
	}
)

func GetNAT(ctx *cli.Context) nat.Interface {
	natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
	if err != nil {
		Fatalf("Option %s: %v", NATFlag.Name, err)
	}
	return natif
}

func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
	hex, file := ctx.GlobalString(NodeKeyHexFlag.Name), ctx.GlobalString(NodeKeyFileFlag.Name)
	var err error
	switch {
	case file != "" && hex != "":
		Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
	case file != "":
		if key, err = crypto.LoadECDSA(file); err != nil {
			Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
		}
	case hex != "":
		if key, err = crypto.HexToECDSA(hex); err != nil {
			Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
		}
	}
	return key
}

func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
	// Set verbosity on glog
	glog.SetV(ctx.GlobalInt(LogLevelFlag.Name))
	// Set the log type
	//glog.SetToStderr(ctx.GlobalBool(LogToStdErrFlag.Name))
	glog.SetToStderr(true)
	// Set the log dir
	glog.SetLogDir(ctx.GlobalString(LogFileFlag.Name))

	customName := ctx.GlobalString(IdentityFlag.Name)
	if len(customName) > 0 {
		clientID += "/" + customName
	}

	return &eth.Config{
		Name:               common.MakeName(clientID, version),
		DataDir:            ctx.GlobalString(DataDirFlag.Name),
		ProtocolVersion:    ctx.GlobalInt(ProtocolVersionFlag.Name),
		BlockChainVersion:  ctx.GlobalInt(BlockchainVersionFlag.Name),
		SkipBcVersionCheck: false,
		NetworkId:          ctx.GlobalInt(NetworkIdFlag.Name),
		LogFile:            ctx.GlobalString(LogFileFlag.Name),
		LogLevel:           ctx.GlobalInt(LogLevelFlag.Name),
		LogJSON:            ctx.GlobalString(LogJSONFlag.Name),
		Etherbase:          ctx.GlobalString(EtherbaseFlag.Name),
		MinerThreads:       ctx.GlobalInt(MinerThreadsFlag.Name),
		AccountManager:     GetAccountManager(ctx),
		VmDebug:            ctx.GlobalBool(VMDebugFlag.Name),
		MaxPeers:           ctx.GlobalInt(MaxPeersFlag.Name),
		MaxPendingPeers:    ctx.GlobalInt(MaxPendingPeersFlag.Name),
		Port:               ctx.GlobalString(ListenPortFlag.Name),
		NAT:                GetNAT(ctx),
		NatSpec:            ctx.GlobalBool(NatspecEnabledFlag.Name),
		NodeKey:            GetNodeKey(ctx),
		Shh:                ctx.GlobalBool(WhisperEnabledFlag.Name),
		Dial:               true,
		BootNodes:          ctx.GlobalString(BootnodesFlag.Name),
	}

}

func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Database) {
	dataDir := ctx.GlobalString(DataDirFlag.Name)

	blockDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "blockchain"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}

	stateDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "state"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}

	extraDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "extra"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}

	eventMux := new(event.TypeMux)
	chainManager := core.NewChainManager(blockDb, stateDb, eventMux)
	pow := ethash.New()
	txPool := core.NewTxPool(eventMux, chainManager.State, chainManager.GasLimit)
	blockProcessor := core.NewBlockProcessor(stateDb, extraDb, pow, txPool, chainManager, eventMux)
	chainManager.SetProcessor(blockProcessor)

	return chainManager, blockDb, stateDb
}

func GetAccountManager(ctx *cli.Context) *accounts.Manager {
	dataDir := ctx.GlobalString(DataDirFlag.Name)
	ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
	return accounts.NewManager(ks)
}

func StartRPC(eth *eth.Ethereum, ctx *cli.Context) error {
	config := rpc.RpcConfig{
		ListenAddress: ctx.GlobalString(RPCListenAddrFlag.Name),
		ListenPort:    uint(ctx.GlobalInt(RPCPortFlag.Name)),
		CorsDomain:    ctx.GlobalString(RPCCORSDomainFlag.Name),
	}

	xeth := xeth.New(eth, nil)
	return rpc.Start(xeth, config)
}

func StartPProf(ctx *cli.Context) {
	address := fmt.Sprintf("localhost:%d", ctx.GlobalInt(PProfPortFlag.Name))
	go func() {
		log.Println(http.ListenAndServe(address, nil))
	}()
}
<commit_msg>cmd/utils: bump default maxpeers to 25<commit_after>package utils

import (
	"crypto/ecdsa"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"runtime"

	"github.com/codegangsta/cli"
	"github.com/ethereum/ethash"
	"github.com/ethereum/go-ethereum/accounts"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/eth"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/event"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/p2p/nat"
	"github.com/ethereum/go-ethereum/rpc"
	"github.com/ethereum/go-ethereum/xeth"
)

func init() {
	cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]

VERSION:
   {{.Version}}

COMMANDS:
   {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
   {{end}}{{if .Flags}}
GLOBAL OPTIONS:
   {{range .Flags}}{{.}}
   {{end}}{{end}}
`

	cli.CommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
	{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
	{{end}}{{end}}{{if .Flags}}
OPTIONS:
	{{range .Flags}}{{.}}
	{{end}}{{end}}
`
}

// NewApp creates an app with sane defaults.
func NewApp(version, usage string) *cli.App {
	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Author = ""
	//app.Authors = nil
	app.Email = ""
	app.Version = version
	app.Usage = usage
	return app
}

// These are all the command line flags we support.
// If you add to this list, please remember to include the
// flag in the appropriate command definition.
//
// The flags are defined here so their names and help texts
// are the same for all commands.

var (
	// General settings
	DataDirFlag = DirectoryFlag{
		Name:  "datadir",
		Usage: "Data directory to be used",
		Value: DirectoryString{common.DefaultDataDir()},
	}
	ProtocolVersionFlag = cli.IntFlag{
		Name:  "protocolversion",
		Usage: "ETH protocol version (integer)",
		Value: eth.ProtocolVersion,
	}
	NetworkIdFlag = cli.IntFlag{
		Name:  "networkid",
		Usage: "Network Id (integer)",
		Value: eth.NetworkId,
	}
	BlockchainVersionFlag = cli.IntFlag{
		Name:  "blockchainversion",
		Usage: "Blockchain version (integer)",
		Value: core.BlockChainVersion,
	}
	IdentityFlag = cli.StringFlag{
		Name:  "identity",
		Usage: "Custom node name",
	}
	NatspecEnabledFlag = cli.BoolFlag{
		Name:  "natspec",
		Usage: "Enable NatSpec confirmation notice",
	}

	// miner settings
	MinerThreadsFlag = cli.IntFlag{
		Name:  "minerthreads",
		Usage: "Number of miner threads",
		Value: runtime.NumCPU(),
	}
	MiningEnabledFlag = cli.BoolFlag{
		Name:  "mine",
		Usage: "Enable mining",
	}
	EtherbaseFlag = cli.StringFlag{
		Name:  "etherbase",
		Usage: "Public address for block mining rewards. By default the address of your primary account is used",
		Value: "primary",
	}

	UnlockedAccountFlag = cli.StringFlag{
		Name:  "unlock",
		Usage: "Unlock the account given until this program exits (prompts for password). '--unlock primary' unlocks the primary account",
		Value: "",
	}
	PasswordFileFlag = cli.StringFlag{
		Name:  "password",
		Usage: "Path to password file to use with options and subcommands needing a password",
		Value: "",
	}

	// logging and debug settings
	LogFileFlag = cli.StringFlag{
		Name:  "logfile",
		Usage: "Send log output to a file",
	}
	LogLevelFlag = cli.IntFlag{
		Name:  "loglevel",
		Usage: "Logging verbosity: 0-6 (0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=debug detail)",
		Value: int(logger.InfoLevel),
	}
	LogJSONFlag = cli.StringFlag{
		Name:  "logjson",
		Usage: "Send json structured log output to a file or '-' for standard output (default: no json output)",
		Value: "",
	}
	LogToStdErrFlag = cli.BoolFlag{
		Name:  "logtostderr",
		Usage: "Logs are written to standard error instead of to files.",
	}
	LogVModuleFlag = cli.GenericFlag{
		Name:  "vmodule",
		Usage: "The syntax of the argument is a comma-separated list of pattern=N, where pattern is a literal file name (minus the \".go\" suffix) or \"glob\" pattern and N is a log verbosity level.",
		Value: glog.GetVModule(),
	}
	VMDebugFlag = cli.BoolFlag{
		Name:  "vmdebug",
		Usage: "Virtual Machine debug output",
	}
	BacktraceAtFlag = cli.GenericFlag{
		Name:  "backtrace_at",
		Usage: "If set to a file and line number (e.g., \"block.go:271\") holding a logging statement, a stack trace will be logged",
		Value: glog.GetTraceLocation(),
	}
	PProfEanbledFlag = cli.BoolFlag{
		Name:  "pprof",
		Usage: "Enable the profiling server on localhost",
	}
	PProfPortFlag = cli.IntFlag{
		Name:  "pprofport",
		Usage: "Port on which the profiler should listen",
		Value: 6060,
	}

	// RPC settings
	RPCEnabledFlag = cli.BoolFlag{
		Name:  "rpc",
		Usage: "Enable the JSON-RPC server",
	}
	RPCListenAddrFlag = cli.StringFlag{
		Name:  "rpcaddr",
		Usage: "Listening address for the JSON-RPC server",
		Value: "127.0.0.1",
	}
	RPCPortFlag = cli.IntFlag{
		Name:  "rpcport",
		Usage: "Port on which the JSON-RPC server should listen",
		Value: 8545,
	}
	RPCCORSDomainFlag = cli.StringFlag{
		Name:  "rpccorsdomain",
		Usage: "Domain on which to send Access-Control-Allow-Origin header",
		Value: "",
	}
	// Network Settings
	MaxPeersFlag = cli.IntFlag{
		Name:  "maxpeers",
		Usage: "Maximum number of network peers (network disabled if set to 0)",
		Value: 25,
	}
	MaxPendingPeersFlag = cli.IntFlag{
		Name:  "maxpendpeers",
		Usage: "Maximum number of pending connection attempts (defaults used if set to 0)",
		Value: 0,
	}
	ListenPortFlag = cli.IntFlag{
		Name:  "port",
		Usage: "Network listening port",
		Value: 30303,
	}
	BootnodesFlag = cli.StringFlag{
		Name:  "bootnodes",
		Usage: "Space-separated enode URLs for p2p discovery bootstrap",
		Value: "",
	}
	NodeKeyFileFlag = cli.StringFlag{
		Name:  "nodekey",
		Usage: "P2P node key file",
	}
	NodeKeyHexFlag = cli.StringFlag{
		Name:  "nodekeyhex",
		Usage: "P2P node key as hex (for testing)",
	}
	NATFlag = cli.StringFlag{
		Name:  "nat",
		Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>)",
		Value: "any",
	}
	WhisperEnabledFlag = cli.BoolFlag{
		Name:  "shh",
		Usage: "Enable whisper",
	}
	// ATM the url is left to the user and deployment to
	JSpathFlag = cli.StringFlag{
		Name:  "jspath",
		Usage: "JS library path to be used with console and js subcommands",
		Value: ".",
	}
	SolcPathFlag = cli.StringFlag{
		Name:  "solc",
		Usage: "solidity compiler to be used",
		Value: "solc",
	}
)

func GetNAT(ctx *cli.Context) nat.Interface {
	natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
	if err != nil {
		Fatalf("Option %s: %v", NATFlag.Name, err)
	}
	return natif
}

func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
	hex, file := ctx.GlobalString(NodeKeyHexFlag.Name), ctx.GlobalString(NodeKeyFileFlag.Name)
	var err error
	switch {
	case file != "" && hex != "":
		Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
	case file != "":
		if key, err = crypto.LoadECDSA(file); err != nil {
			Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
		}
	case hex != "":
		if key, err = crypto.HexToECDSA(hex); err != nil {
			Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
		}
	}
	return key
}

func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
	// Set verbosity on glog
	glog.SetV(ctx.GlobalInt(LogLevelFlag.Name))
	// Set the log type
	//glog.SetToStderr(ctx.GlobalBool(LogToStdErrFlag.Name))
	glog.SetToStderr(true)
	// Set the log dir
	glog.SetLogDir(ctx.GlobalString(LogFileFlag.Name))

	customName := ctx.GlobalString(IdentityFlag.Name)
	if len(customName) > 0 {
		clientID += "/" + customName
	}

	return &eth.Config{
		Name:               common.MakeName(clientID, version),
		DataDir:            ctx.GlobalString(DataDirFlag.Name),
		ProtocolVersion:    ctx.GlobalInt(ProtocolVersionFlag.Name),
		BlockChainVersion:  ctx.GlobalInt(BlockchainVersionFlag.Name),
		SkipBcVersionCheck: false,
		NetworkId:          ctx.GlobalInt(NetworkIdFlag.Name),
		LogFile:            ctx.GlobalString(LogFileFlag.Name),
		LogLevel:           ctx.GlobalInt(LogLevelFlag.Name),
		LogJSON:            ctx.GlobalString(LogJSONFlag.Name),
		Etherbase:          ctx.GlobalString(EtherbaseFlag.Name),
		MinerThreads:       ctx.GlobalInt(MinerThreadsFlag.Name),
		AccountManager:     GetAccountManager(ctx),
		VmDebug:            ctx.GlobalBool(VMDebugFlag.Name),
		MaxPeers:           ctx.GlobalInt(MaxPeersFlag.Name),
		MaxPendingPeers:    ctx.GlobalInt(MaxPendingPeersFlag.Name),
		Port:               ctx.GlobalString(ListenPortFlag.Name),
		NAT:                GetNAT(ctx),
		NatSpec:            ctx.GlobalBool(NatspecEnabledFlag.Name),
		NodeKey:            GetNodeKey(ctx),
		Shh:                ctx.GlobalBool(WhisperEnabledFlag.Name),
		Dial:               true,
		BootNodes:          ctx.GlobalString(BootnodesFlag.Name),
	}

}

func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Database) {
	dataDir := ctx.GlobalString(DataDirFlag.Name)

	blockDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "blockchain"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}

	stateDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "state"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}

	extraDb, err := ethdb.NewLDBDatabase(path.Join(dataDir, "extra"))
	if err != nil {
		Fatalf("Could not open database: %v", err)
	}

	eventMux := new(event.TypeMux)
	chainManager := core.NewChainManager(blockDb, stateDb, eventMux)
	pow := ethash.New()
	txPool := core.NewTxPool(eventMux, chainManager.State, chainManager.GasLimit)
	blockProcessor := core.NewBlockProcessor(stateDb, extraDb, pow, txPool, chainManager, eventMux)
	chainManager.SetProcessor(blockProcessor)

	return chainManager, blockDb, stateDb
}

func GetAccountManager(ctx *cli.Context) *accounts.Manager {
	dataDir := ctx.GlobalString(DataDirFlag.Name)
	ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
	return accounts.NewManager(ks)
}

func StartRPC(eth *eth.Ethereum, ctx *cli.Context) error {
	config := rpc.RpcConfig{
		ListenAddress: ctx.GlobalString(RPCListenAddrFlag.Name),
		ListenPort:    uint(ctx.GlobalInt(RPCPortFlag.Name)),
		CorsDomain:    ctx.GlobalString(RPCCORSDomainFlag.Name),
	}

	xeth := xeth.New(eth, nil)
	return rpc.Start(xeth, config)
}

func StartPProf(ctx *cli.Context) {
	address := fmt.Sprintf("localhost:%d", ctx.GlobalInt(PProfPortFlag.Name))
	go func() {
		log.Println(http.ListenAndServe(address, nil))
	}()
}
<|endoftext|>


<commit_before>// Ogo

package ogo

import (
	//"fmt"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
	"net/http"
	"strings"
)

type Handler func(c *HttpContext)

type Route struct {
	Pattern string
	Method  string
	Handler Handler
}

type Controller struct {
	Endpoint string
	Routes   map[string]*Route
	Request  int
}

type ControllerInterface interface {
	Get(c *HttpContext)
	Post(c *HttpContext)
	Put(c *HttpContext)
	Delete(c *HttpContext)
	Patch(c *HttpContext)
	Head(c *HttpContext)
}

func NewRoute(p string, m string, h Handler) *Route {
	return &Route{
		Pattern: p,
		Method:  m,
		Handler: h,
	}
}

// 封装
func handlerWrap(f Handler) web.HandlerFunc { //这里封装了webC到本地的结构中
	return func(c web.C, w http.ResponseWriter, r *http.Request) {
		f(newContext(c, w, r))
	}
}

func (ctr *Controller) Get(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Post(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Put(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Delete(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Patch(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Head(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}

func (ctr *Controller) AddRoute(m string, p string, h Handler) {
	rt := NewRoute(p, m, h)
	switch strings.ToLower(m) {
	case "get":
		ctr.RouteGet(rt)
	case "post":
		ctr.RoutePost(rt)
	case "put":
		ctr.RoutePut(rt)
	case "delete":
		ctr.RouteDelete(rt)
	case "patch":
		ctr.RoutePatch(rt)
	case "head":
		ctr.RouteHead(rt)
	default:
		// unknow method
	}
}

// controller default route
// 默认路由, 如果已经定义了则忽略，没有定义则加上
//func (ctr *Controller) DefaultRoutes() {
func (ctr *Controller) DefaultRoutes(c ControllerInterface) {
	// GET /{endpoint}
	ctr.RouteGet(NewRoute("/"+ctr.Endpoint, "GET", c.Get))

	// GET /{endpoint}/{id}
	ctr.RouteGet(NewRoute("/"+ctr.Endpoint+"/:_id_", "GET", c.Get))

	// POST /{endpoint}
	ctr.RoutePost(NewRoute("/"+ctr.Endpoint, "POST", c.Post))

	// DELETE /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "DELETE", c.Delete))

	// PATCH /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "PATCH", c.Patch))

	// PUT /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "PUT", c.Put))

}

func (ctr *Controller) RouteGet(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Get(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RoutePost(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Post(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RoutePut(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Put(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RouteDelete(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Delete(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RoutePatch(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Patch(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RouteHead(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Head(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
<commit_msg>update<commit_after>// Ogo

package ogo

import (
	//"fmt"
	"github.com/zenazn/goji"
	"github.com/zenazn/goji/web"
	"net/http"
	"strings"
)

type Handler func(c *HttpContext)

type Route struct {
	Pattern string
	Method  string
	Handler Handler
}

type Controller struct {
	Endpoint string
	Routes   map[string]*Route
	Request  int
}

type ControllerInterface interface {
	Init(endpoint string)
	Get(c *HttpContext)
	Post(c *HttpContext)
	Put(c *HttpContext)
	Delete(c *HttpContext)
	Patch(c *HttpContext)
	Head(c *HttpContext)
}

func NewRoute(p string, m string, h Handler) *Route {
	return &Route{
		Pattern: p,
		Method:  m,
		Handler: h,
	}
}

// 封装
func handlerWrap(f Handler) web.HandlerFunc { //这里封装了webC到本地的结构中
	return func(c web.C, w http.ResponseWriter, r *http.Request) {
		f(newContext(c, w, r))
	}
}

func (ctr *Controller) Init(endpoint string) {
	ctr.Endpoint = endpoint
	ctr.Routes = make(map[string]*Route)
	//默认路由
	ctr.DefaultRoutes(ctr)
}

func (ctr *Controller) Get(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Post(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Put(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Delete(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Patch(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}
func (ctr *Controller) Head(c *HttpContext) {
	http.Error(c.Response, "Method Not Allowed", http.StatusMethodNotAllowed)
}

func (ctr *Controller) AddRoute(m string, p string, h Handler) {
	rt := NewRoute(p, m, h)
	switch strings.ToLower(m) {
	case "get":
		ctr.RouteGet(rt)
	case "post":
		ctr.RoutePost(rt)
	case "put":
		ctr.RoutePut(rt)
	case "delete":
		ctr.RouteDelete(rt)
	case "patch":
		ctr.RoutePatch(rt)
	case "head":
		ctr.RouteHead(rt)
	default:
		// unknow method
	}
}

// controller default route
// 默认路由, 如果已经定义了则忽略，没有定义则加上
//func (ctr *Controller) DefaultRoutes() {
func (ctr *Controller) DefaultRoutes(c ControllerInterface) {
	// GET /{endpoint}
	ctr.RouteGet(NewRoute("/"+ctr.Endpoint, "GET", c.Get))

	// GET /{endpoint}/{id}
	ctr.RouteGet(NewRoute("/"+ctr.Endpoint+"/:_id_", "GET", c.Get))

	// POST /{endpoint}
	ctr.RoutePost(NewRoute("/"+ctr.Endpoint, "POST", c.Post))

	// DELETE /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "DELETE", c.Delete))

	// PATCH /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "PATCH", c.Patch))

	// PUT /{endpoint}/{id}
	ctr.RouteDelete(NewRoute("/"+ctr.Endpoint+"/:_id_", "PUT", c.Put))

}

func (ctr *Controller) RouteGet(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Get(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RoutePost(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Post(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RoutePut(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Put(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RouteDelete(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Delete(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RoutePatch(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Patch(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
func (ctr *Controller) RouteHead(rt *Route) {
	key := strings.ToUpper(rt.Method) + " " + rt.Pattern
	if _, ok := ctr.Routes[key]; ok {
		// exists
	} else {
		goji.Head(rt.Pattern, handlerWrap(rt.Handler))
		ctr.Routes[key] = rt
	}
}
<|endoftext|>