<commit_before>package kazoo

import (
	"encoding/json"
	"errors"
	"fmt"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/samuel/go-zookeeper/zk"
)

var (
	FailedToClaimPartition = errors.New("Failed to claim partition for this consumer instance. Do you have a rogue consumer running?")
)

// ParseConnectionString parses a zookeeper connection string in the form of
// host1:2181,host2:2181/chroot and returns the list of servers, and the chroot.
func ParseConnectionString(zookeeper string) (nodes []string, chroot string) {
	nodesAndChroot := strings.SplitN(zookeeper, "/", 2)
	if len(nodesAndChroot) == 2 {
		chroot = fmt.Sprintf("/%s", nodesAndChroot[1])
	}
	nodes = strings.Split(nodesAndChroot[0], ",")
	return
}

// BuildConnectionString builds a Zookeeper connection string for a list of nodes.
// Returns a string like "zk1:2181,zk2:2181,zk3:2181"
func BuildConnectionString(nodes []string) string {
	return strings.Join(nodes, ",")
}

// ConnectionStringWithChroot builds a Zookeeper connection string for a list
// of nodes and a chroot. The chroot should start with "/".
// Returns a string like "zk1:2181,zk2:2181,zk3:2181/chroot"
func BuildConnectionStringWithChroot(nodes []string, chroot string) string {
	return fmt.Sprintf("%s%s", strings.Join(nodes, ","), chroot)
}

// Kazoo interacts with the Kafka metadata in Zookeeper
type Kazoo struct {
	conn *zk.Conn
	conf *Config
}

// Config holds configuration values f.
type Config struct {
	// The chroot the Kafka installation is registerde under. Defaults to "".
	Chroot string

	// The amount of time the Zookeeper client can be disconnected from the Zookeeper cluster
	// before the cluster will get rid of watches and ephemeral nodes. Defaults to 1 second.
	Timeout time.Duration

	// Logger
	Logger zk.Logger
}

// NewConfig instantiates a new Config struct with sane defaults.
func NewConfig() *Config {
	return &Config{
		Timeout: 1 * time.Second,
		Logger:  zk.DefaultLogger,
	}
}

// NewKazoo creates a new connection instance
func NewKazoo(servers []string, conf *Config) (*Kazoo, error) {
	if conf == nil {
		conf = NewConfig()
	}

	conn, _, err := zk.Connect(
		servers,
		conf.Timeout,
		func(c *zk.Conn) { c.SetLogger(conf.Logger) },
	)

	if err != nil {
		return nil, err
	}
	return &Kazoo{conn, conf}, nil
}

// NewKazooFromConnectionString creates a new connection instance
// based on a zookeeer connection string that can include a chroot.
func NewKazooFromConnectionString(connectionString string, conf *Config) (*Kazoo, error) {
	if conf == nil {
		conf = NewConfig()
	}

	nodes, chroot := ParseConnectionString(connectionString)
	conf.Chroot = chroot
	return NewKazoo(nodes, conf)
}

// Brokers returns a map of all the brokers that make part of the
// Kafka cluster that is registered in Zookeeper.
func (kz *Kazoo) Brokers() (map[int32]string, error) {
	root := fmt.Sprintf("%s/brokers/ids", kz.conf.Chroot)
	children, _, err := kz.conn.Children(root)
	if err != nil {
		return nil, err
	}

	type brokerEntry struct {
		Host string `json:"host"`
		Port int    `json:"port"`
	}

	result := make(map[int32]string)
	for _, child := range children {
		brokerID, err := strconv.ParseInt(child, 10, 32)
		if err != nil {
			return nil, err
		}

		value, _, err := kz.conn.Get(path.Join(root, child))
		if err != nil {
			return nil, err
		}

		var brokerNode brokerEntry
		if err := json.Unmarshal(value, &brokerNode); err != nil {
			return nil, err
		}

		result[int32(brokerID)] = fmt.Sprintf("%s:%d", brokerNode.Host, brokerNode.Port)
	}

	return result, nil
}

// BrokerList returns a slice of broker addresses that can be used to connect to
// the Kafka cluster, e.g. using `sarama.NewAsyncProducer()`.
func (kz *Kazoo) BrokerList() ([]string, error) {
	brokers, err := kz.Brokers()
	if err != nil {
		return nil, err
	}

	result := make([]string, 0, len(brokers))
	for _, broker := range brokers {
		result = append(result, broker)
	}

	return result, nil
}

// BrokerIDList returns a sorted slice of broker ids that can be used for manipulating topics and partitions.`.
func (kz *Kazoo) brokerIDList() ([]int32, error) {
	brokers, err := kz.Brokers()
	if err != nil {
		return nil, err
	}

	result := make([]int32, 0, len(brokers))
	for id := range brokers {
		result = append(result, id)
	}

	// return sorted list to match the offical kafka sdks
	sort.Sort(int32Slice(result))

	return result, nil
}

// Controller returns what broker is currently acting as controller of the Kafka cluster
func (kz *Kazoo) Controller() (int32, error) {
	type controllerEntry struct {
		BrokerID int32 `json:"brokerid"`
	}

	node := fmt.Sprintf("%s/controller", kz.conf.Chroot)
	data, _, err := kz.conn.Get(node)
	if err != nil {
		return -1, err
	}

	var controllerNode controllerEntry
	if err := json.Unmarshal(data, &controllerNode); err != nil {
		return -1, err
	}

	return controllerNode.BrokerID, nil
}

// Close closes the connection with the Zookeeper cluster
func (kz *Kazoo) Close() error {
	kz.conn.Close()
	return nil
}

////////////////////////////////////////////////////////////////////////
// Util methods
////////////////////////////////////////////////////////////////////////

// Exists checks existence of a node
func (kz *Kazoo) exists(node string) (ok bool, err error) {
	ok, _, err = kz.conn.Exists(node)
	return
}

// DeleteAll deletes a node recursively
func (kz *Kazoo) deleteRecursive(node string) (err error) {
	children, stat, err := kz.conn.Children(node)
	if err == zk.ErrNoNode {
		return nil
	} else if err != nil {
		return
	}

	for _, child := range children {
		if err = kz.deleteRecursive(path.Join(node, child)); err != nil {
			return
		}
	}

	return kz.conn.Delete(node, stat.Version)
}

// MkdirAll creates a directory recursively
func (kz *Kazoo) mkdirRecursive(node string) (err error) {
	parent := path.Dir(node)
	if parent != "/" {
		if err = kz.mkdirRecursive(parent); err != nil {
			return
		}
	}

	_, err = kz.conn.Create(node, nil, 0, zk.WorldACL(zk.PermAll))
	if err == zk.ErrNodeExists {
		err = nil
	}
	return
}

// Create stores a new value at node. Fails if already set.
func (kz *Kazoo) create(node string, value []byte, ephemeral bool) (err error) {
	if err = kz.mkdirRecursive(path.Dir(node)); err != nil {
		return
	}

	flags := int32(0)
	if ephemeral {
		flags = zk.FlagEphemeral
	}
	_, err = kz.conn.Create(node, value, flags, zk.WorldACL(zk.PermAll))
	return
}

// createOrUpdate first attempts to update a node. If the nodes does not exist it will create it.
func (kz *Kazoo) createOrUpdate(node string, value []byte, ephemeral bool) (err error) {
	if _, err = kz.conn.Set(node, value, -1); err == nil {
		return
	}

	if err == zk.ErrNoNode {
		err = kz.create(node, value, ephemeral)
	}
	return
}

// sort interface for int32 slice
type int32Slice []int32

func (s int32Slice) Len() int           { return len(s) }
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
func (s int32Slice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
<commit_msg>Make the node exist check explicit in mkdirRecursive<commit_after>package kazoo

import (
	"encoding/json"
	"errors"
	"fmt"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/samuel/go-zookeeper/zk"
)

var (
	FailedToClaimPartition = errors.New("Failed to claim partition for this consumer instance. Do you have a rogue consumer running?")
)

// ParseConnectionString parses a zookeeper connection string in the form of
// host1:2181,host2:2181/chroot and returns the list of servers, and the chroot.
func ParseConnectionString(zookeeper string) (nodes []string, chroot string) {
	nodesAndChroot := strings.SplitN(zookeeper, "/", 2)
	if len(nodesAndChroot) == 2 {
		chroot = fmt.Sprintf("/%s", nodesAndChroot[1])
	}
	nodes = strings.Split(nodesAndChroot[0], ",")
	return
}

// BuildConnectionString builds a Zookeeper connection string for a list of nodes.
// Returns a string like "zk1:2181,zk2:2181,zk3:2181"
func BuildConnectionString(nodes []string) string {
	return strings.Join(nodes, ",")
}

// ConnectionStringWithChroot builds a Zookeeper connection string for a list
// of nodes and a chroot. The chroot should start with "/".
// Returns a string like "zk1:2181,zk2:2181,zk3:2181/chroot"
func BuildConnectionStringWithChroot(nodes []string, chroot string) string {
	return fmt.Sprintf("%s%s", strings.Join(nodes, ","), chroot)
}

// Kazoo interacts with the Kafka metadata in Zookeeper
type Kazoo struct {
	conn *zk.Conn
	conf *Config
}

// Config holds configuration values f.
type Config struct {
	// The chroot the Kafka installation is registerde under. Defaults to "".
	Chroot string

	// The amount of time the Zookeeper client can be disconnected from the Zookeeper cluster
	// before the cluster will get rid of watches and ephemeral nodes. Defaults to 1 second.
	Timeout time.Duration

	// Logger
	Logger zk.Logger
}

// NewConfig instantiates a new Config struct with sane defaults.
func NewConfig() *Config {
	return &Config{
		Timeout: 1 * time.Second,
		Logger:  zk.DefaultLogger,
	}
}

// NewKazoo creates a new connection instance
func NewKazoo(servers []string, conf *Config) (*Kazoo, error) {
	if conf == nil {
		conf = NewConfig()
	}

	conn, _, err := zk.Connect(
		servers,
		conf.Timeout,
		func(c *zk.Conn) { c.SetLogger(conf.Logger) },
	)

	if err != nil {
		return nil, err
	}
	return &Kazoo{conn, conf}, nil
}

// NewKazooFromConnectionString creates a new connection instance
// based on a zookeeer connection string that can include a chroot.
func NewKazooFromConnectionString(connectionString string, conf *Config) (*Kazoo, error) {
	if conf == nil {
		conf = NewConfig()
	}

	nodes, chroot := ParseConnectionString(connectionString)
	conf.Chroot = chroot
	return NewKazoo(nodes, conf)
}

// Brokers returns a map of all the brokers that make part of the
// Kafka cluster that is registered in Zookeeper.
func (kz *Kazoo) Brokers() (map[int32]string, error) {
	root := fmt.Sprintf("%s/brokers/ids", kz.conf.Chroot)
	children, _, err := kz.conn.Children(root)
	if err != nil {
		return nil, err
	}

	type brokerEntry struct {
		Host string `json:"host"`
		Port int    `json:"port"`
	}

	result := make(map[int32]string)
	for _, child := range children {
		brokerID, err := strconv.ParseInt(child, 10, 32)
		if err != nil {
			return nil, err
		}

		value, _, err := kz.conn.Get(path.Join(root, child))
		if err != nil {
			return nil, err
		}

		var brokerNode brokerEntry
		if err := json.Unmarshal(value, &brokerNode); err != nil {
			return nil, err
		}

		result[int32(brokerID)] = fmt.Sprintf("%s:%d", brokerNode.Host, brokerNode.Port)
	}

	return result, nil
}

// BrokerList returns a slice of broker addresses that can be used to connect to
// the Kafka cluster, e.g. using `sarama.NewAsyncProducer()`.
func (kz *Kazoo) BrokerList() ([]string, error) {
	brokers, err := kz.Brokers()
	if err != nil {
		return nil, err
	}

	result := make([]string, 0, len(brokers))
	for _, broker := range brokers {
		result = append(result, broker)
	}

	return result, nil
}

// BrokerIDList returns a sorted slice of broker ids that can be used for manipulating topics and partitions.`.
func (kz *Kazoo) brokerIDList() ([]int32, error) {
	brokers, err := kz.Brokers()
	if err != nil {
		return nil, err
	}

	result := make([]int32, 0, len(brokers))
	for id := range brokers {
		result = append(result, id)
	}

	// return sorted list to match the offical kafka sdks
	sort.Sort(int32Slice(result))

	return result, nil
}

// Controller returns what broker is currently acting as controller of the Kafka cluster
func (kz *Kazoo) Controller() (int32, error) {
	type controllerEntry struct {
		BrokerID int32 `json:"brokerid"`
	}

	node := fmt.Sprintf("%s/controller", kz.conf.Chroot)
	data, _, err := kz.conn.Get(node)
	if err != nil {
		return -1, err
	}

	var controllerNode controllerEntry
	if err := json.Unmarshal(data, &controllerNode); err != nil {
		return -1, err
	}

	return controllerNode.BrokerID, nil
}

// Close closes the connection with the Zookeeper cluster
func (kz *Kazoo) Close() error {
	kz.conn.Close()
	return nil
}

////////////////////////////////////////////////////////////////////////
// Util methods
////////////////////////////////////////////////////////////////////////

// Exists checks existence of a node
func (kz *Kazoo) exists(node string) (ok bool, err error) {
	ok, _, err = kz.conn.Exists(node)
	return
}

// DeleteAll deletes a node recursively
func (kz *Kazoo) deleteRecursive(node string) (err error) {
	children, stat, err := kz.conn.Children(node)
	if err == zk.ErrNoNode {
		return nil
	} else if err != nil {
		return
	}

	for _, child := range children {
		if err = kz.deleteRecursive(path.Join(node, child)); err != nil {
			return
		}
	}

	return kz.conn.Delete(node, stat.Version)
}

// MkdirAll creates a directory recursively
func (kz *Kazoo) mkdirRecursive(node string) (err error) {
	parent := path.Dir(node)
	if parent != "/" {
		if err = kz.mkdirRecursive(parent); err != nil {
			return
		}
	}

	exists, _, err := kz.conn.Exists(node)
	if err != nil {
		return
	}

	if !exists {
		_, err = kz.conn.Create(node, nil, 0, zk.WorldACL(zk.PermAll))
		return
	}

	return
}

// Create stores a new value at node. Fails if already set.
func (kz *Kazoo) create(node string, value []byte, ephemeral bool) (err error) {
	if err = kz.mkdirRecursive(path.Dir(node)); err != nil {
		return
	}

	flags := int32(0)
	if ephemeral {
		flags = zk.FlagEphemeral
	}
	_, err = kz.conn.Create(node, value, flags, zk.WorldACL(zk.PermAll))
	return
}

// createOrUpdate first attempts to update a node. If the nodes does not exist it will create it.
func (kz *Kazoo) createOrUpdate(node string, value []byte, ephemeral bool) (err error) {
	if _, err = kz.conn.Set(node, value, -1); err == nil {
		return
	}

	if err == zk.ErrNoNode {
		err = kz.create(node, value, ephemeral)
	}
	return
}

// sort interface for int32 slice
type int32Slice []int32

func (s int32Slice) Len() int           { return len(s) }
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
func (s int32Slice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
<|endoftext|>


<commit_before>package main

/*
TODO
move all prepares statements to DB setup time
front end with appache
appache user auth
dockerize
api doc
figure out way to load secrets
encrypt keys in DB
DB backup

*/

import (
        "database/sql"
	_ "github.com/lib/pq"
        "log"
	"os"
	 "html/template"
	"net/http"
	"io"
	"strconv"
	"math/rand"
	"time"
	"github.com/gorilla/mux"
	"errors"
)

var db *sql.DB

var templates = template.Must(template.ParseFiles("index.html"))

var nonCryptoRand = rand.New( rand.NewSource( time.Now().UTC().UnixNano() ) )

func setupDatabase( hostName string, pgPassword string ) { // TODO pass in port, username
        var err error

        // set up DB
        db, err = sql.Open("postgres", "password='"+pgPassword+"' user=postgres dbname=postgres host="+hostName+" port=5432 sslmode=disable")
	if err != nil {
		log.Fatal(err)
	}

	// a postgess bigint is signed 64 bit int
	sqlSetup := `
        CREATE TABLE IF NOT EXISTS keys ( kID BIGINT NOT NULL, kVal bytea NOT NULL ,  oID BIGINT NOT NULL, PRIMARY KEY( kID ) );
        CREATE TABLE IF NOT EXISTS keyUsers ( kID BIGINT NOT NULL, uID BIGINT NOT NULL , PRIMARY KEY( kID,uID ) );
        CREATE TABLE IF NOT EXISTS keyAdmins ( kID BIGINT NOT NULL, uID BIGINT NOT NULL , PRIMARY KEY( kID,uID ) );
        `
        _, err = db.Exec(sqlSetup)
        if err != nil {
                log.Println("sql fatal error in setupDatabase")
                log.Printf("%q\n", err)
        }

	log.Printf("Setup Database\n")
}

func getKey( keyID int64, userID int64 ) (string) {
	// note if using mySQL use ? but Postgres is $1 in prepare statements
	stmt, err := db.Prepare(
		"SELECT keys.kVal  FROM keyUsers JOIN keys ON  keys.kID = keyUsers.kID WHERE keyUsers.uID = $2 AND keyUsers.kID = $1")
	if err != nil {
		log.Println("sql fatal error in getKey prep")
		log.Fatal(err)
	}
	var keyVal string
	err = stmt.QueryRow(keyID,userID).Scan( &keyVal )
	switch {
	case err == sql.ErrNoRows:
            log.Printf("no key found")
	case err != nil:
		log.Println("sql fatal error in getKey querry")
		log.Fatal(err)
	default:
		return keyVal;
	}
	return "";
}

func createKey(  userID int64, keyVal string ) (int64) {
	var keyID int64 = nonCryptoRand.Int63();
	var err error;

	var stmt [3]*sql.Stmt;
	var cmd  [3]string = [3]string{"INSERT INTO keys (kID, kVal, oID) VALUES ($1, $2::bytea,$3)",
		"INSERT INTO keyUsers (kID,uID) VALUES ($1,$2)",
		"INSERT INTO keyAdmins (kID,uID) VALUES ($1,$2);" }
	// note if using mySQL use ? but Postgres is $1 in prepare statements

	for i := range cmd {
		stmt[i], err = db.Prepare( cmd[i] )
		if err != nil {
			log.Println("sql fatal error in createKey prep for", cmd[i])
			log.Fatal(err)
		}
	}

	for i := range cmd {
		if i == 0 {
			_,err = stmt[i].Exec(keyID,keyVal,userID)
		} else {
			_,err = stmt[i].Exec(keyID,userID)
		}

		if err != nil {
			log.Println("sql error in createKey",err)
			return 0;
		}
	}

	return keyID;
}

func addRole(   keyID int64, userID int64, role string, roleID int64  ) (error) {
	var err error;
	var stmt *sql.Stmt;
	var cmd string

	switch {
	case role=="user":
		cmd = "INSERT INTO keyUsers (kID,uID) SELECT kID,$3 FROM keyAdmins WHERE keyAdmins.kID = $1 AND keyAdmins.uID = $2"
	case role=="admin":
		cmd = "INSERT INTO keyAdmins (kID,uID) SELECT kID,$3 FROM keys WHERE keys.kID = $1 AND keys.oID = $2"
	default:
		return errors.New("bad role");
	}

	stmt, err = db.Prepare( cmd )
	if err != nil {
		log.Println("sql fatal error in createKey prep for", cmd )
		log.Fatal(err)
	}

	_,err = stmt.Exec(keyID,userID,roleID)
	if err != nil {
		log.Println("sql error in addRole",err)
		return err;
	}

	return nil;
}

func getMeta( keyID int64, userID int64, meta string ) ([]int64,error) {
	return []int64{0,0,0},nil
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
        if r.URL.Path != "/" {
                http.NotFound(w, r)
                return
        }

        type PageData struct {
                Junk string
        }
        data := PageData{Junk: "nothing"}
        err := templates.ExecuteTemplate(w, "index.html", data)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
        }
}

func searchKeyHandler(w http.ResponseWriter, r *http.Request) {
	var err error
        w.Header().Set("Access-Control-Allow-Origin", "*")
	vars := mux.Vars(r)

	var keyID int64 = 0;
	var userID int64 = 1;

	keyID,err = strconv.ParseInt(  vars["keyID"] , 0, 64 );
	if err != nil {
		http.NotFound(w, r)
		return
	}

	log.Println("GET key: keyID=", keyID, " userID=",userID )

	io.WriteString(w, getKey(keyID,userID) )
}

func createKeyHandler(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")

	err := r.ParseForm()
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
        }

	var keyVal string = r.FormValue("keyVal");
	var userID int64 = 1;

	var keyID int64 = createKey( userID, keyVal );

	log.Println("POST createKey: keyID=", keyID, "userID=",userID )

	io.WriteString(w, "{ \"keyID\": " + strconv.FormatInt(keyID,10) +" }" )
}

func addRoleHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	var err error

	w.Header().Set("Access-Control-Allow-Origin", "*")

	var keyID int64 = 0;
	var userID int64 = 1;
	var roleID int64 = 0;

	keyID,err = strconv.ParseInt(  vars["keyID"] , 0, 64 );
	if err != nil {
		http.NotFound(w, r)
		return
	}

	roleID,err = strconv.ParseInt(  vars["roleID"] , 0, 64 );
	if err != nil {
		http.NotFound(w, r)
		return
	}

	var role string = vars["role"];

	switch {
	case role == "admin" :
	case role == "user" :
	default:
		http.NotFound(w, r)
		return
	}

	log.Println("POST addRole: keyID=", keyID, " userID=",userID , "roleID=", roleID , "role=" , role )

	err = addRole( keyID, userID, role, roleID )
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func getKeyMetaHandler (w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	var err error

	w.Header().Set("Access-Control-Allow-Origin", "*")

	var userID int64 = 1;

	var keyID int64 = 0;
		keyID,err = strconv.ParseInt(  vars["keyID"] , 0, 64 );
	if err != nil {
		http.NotFound(w, r)
		return
	}

	var meta string = vars["meta"];

	var vals []int64
	var idName string

	switch {
	case meta == "owner" :
		idName = "ownerIDs"
	case meta == "admins" :
		idName = "adminIDs"
	case meta == "users" :
		idName = "userIDs"
	default:
		http.NotFound(w, r)
		return
	}

	log.Println("GET meta: keyID=", keyID, "userID=", userID, "meta=" , meta )

	vals,err = getMeta( keyID, userID, meta )

	io.WriteString(w, "{ \"" +idName + "\": [ " )
	for i := range vals {
		if i != 0 {
			io.WriteString(w, "," )
		}
		io.WriteString(w, strconv.FormatInt( vals[i], 10) )
	}
	io.WriteString(w, " ] }" )
}

func main() {
	var err error

	// get all the configuration data
	if len( os.Args ) != 2 {
		log.Fatal( "must pass database hostname on CLI" );
	}
	var hostName string = os.Args[1]

	var pgPassword string = os.Getenv("SECM_DB_SECRET")
	if len( pgPassword ) < 1 {
		log.Fatal( "must set environ variable SECM_DB_SECRET" );
	}

	// set up the DB
	setupDatabase(hostName,pgPassword)
        defer db.Close()

	// Check DB is alive
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	// set up the routes
	router := mux.NewRouter()
	router.HandleFunc("/", mainHandler).Methods("GET")
	router.HandleFunc("/v1/key", createKeyHandler).Methods("POST")
	router.HandleFunc("/v1/key/{keyID}", searchKeyHandler).Methods("GET")
	router.HandleFunc("/v1/key/{keyID}/{role}/{roleID}", addRoleHandler).Methods("POST") // role is user | admin
	router.HandleFunc("/v1/key/{keyID}/{meta}", getKeyMetaHandler).Methods("GET") // meta is ownwer | users | admins
	http.Handle("/", router)

	// run the web server
        http.ListenAndServe(":8080", nil)
}
<commit_msg>can return meta data about keys<commit_after>package main

/*
TODO
move all prepares statements to DB setup time
defer stmt.Close() for all statements
front end with appache
appache user auth
dockerize
api doc
figure out way to load secrets
encrypt keys in DB
DB backup

*/

import (
        "database/sql"
	_ "github.com/lib/pq"
        "log"
	"os"
	 "html/template"
	"net/http"
	"io"
	"strconv"
	"math/rand"
	"time"
	"github.com/gorilla/mux"
	"errors"
)

var db *sql.DB

var templates = template.Must(template.ParseFiles("index.html"))

var nonCryptoRand = rand.New( rand.NewSource( time.Now().UTC().UnixNano() ) )

func setupDatabase( hostName string, pgPassword string ) { // TODO pass in port, username
        var err error

        // set up DB
        db, err = sql.Open("postgres", "password='"+pgPassword+"' user=postgres dbname=postgres host="+hostName+" port=5432 sslmode=disable")
	if err != nil {
		log.Fatal(err)
	}

	// a postgess bigint is signed 64 bit int
	sqlSetup := `
        CREATE TABLE IF NOT EXISTS keys ( kID BIGINT NOT NULL, kVal bytea NOT NULL ,  oID BIGINT NOT NULL, PRIMARY KEY( kID ) );
        CREATE TABLE IF NOT EXISTS keyUsers ( kID BIGINT NOT NULL, uID BIGINT NOT NULL , PRIMARY KEY( kID,uID ) );
        CREATE TABLE IF NOT EXISTS keyAdmins ( kID BIGINT NOT NULL, uID BIGINT NOT NULL , PRIMARY KEY( kID,uID ) );
        `
        _, err = db.Exec(sqlSetup)
        if err != nil {
                log.Println("sql fatal error in setupDatabase")
                log.Printf("%q\n", err)
        }

	log.Printf("Setup Database\n")
}

func getKey( keyID int64, userID int64 ) (string) {
	// note if using mySQL use ? but Postgres is $1 in prepare statements
	stmt, err := db.Prepare(
		"SELECT keys.kVal  FROM keyUsers JOIN keys ON  keys.kID = keyUsers.kID WHERE keyUsers.uID = $2 AND keyUsers.kID = $1")
	if err != nil {
		log.Println("sql fatal error in getKey prep")
		log.Fatal(err)
	}
	var keyVal string
	err = stmt.QueryRow(keyID,userID).Scan( &keyVal )
	switch {
	case err == sql.ErrNoRows:
            log.Printf("no key found")
	case err != nil:
		log.Println("sql fatal error in getKey querry")
		log.Fatal(err)
	default:
		return keyVal;
	}
	return "";
}

func createKey(  userID int64, keyVal string ) (int64) {
	var keyID int64 = nonCryptoRand.Int63();
	var err error;

	var stmt [3]*sql.Stmt;
	var cmd  [3]string = [3]string{"INSERT INTO keys (kID, kVal, oID) VALUES ($1, $2::bytea,$3)",
		"INSERT INTO keyUsers (kID,uID) VALUES ($1,$2)",
		"INSERT INTO keyAdmins (kID,uID) VALUES ($1,$2);" }
	// note if using mySQL use ? but Postgres is $1 in prepare statements

	for i := range cmd {
		stmt[i], err = db.Prepare( cmd[i] )
		if err != nil {
			log.Println("sql fatal error in createKey prep for", cmd[i])
			log.Fatal(err)
		}
	}

	for i := range cmd {
		if i == 0 {
			_,err = stmt[i].Exec(keyID,keyVal,userID)
		} else {
			_,err = stmt[i].Exec(keyID,userID)
		}

		if err != nil {
			log.Println("sql error in createKey",err)
			return 0;
		}
	}

	return keyID;
}

func addRole(   keyID int64, userID int64, role string, roleID int64  ) (error) {
	var err error;
	var stmt *sql.Stmt;
	var cmd string

	switch {
	case role=="user":
		cmd = "INSERT INTO keyUsers (kID,uID) SELECT kID,$3 FROM keyAdmins WHERE keyAdmins.kID = $1 AND keyAdmins.uID = $2"
	case role=="admin":
		cmd = "INSERT INTO keyAdmins (kID,uID) SELECT kID,$3 FROM keys WHERE keys.kID = $1 AND keys.oID = $2"
	default:
		return errors.New("bad role");
	}

	stmt, err = db.Prepare( cmd )
	if err != nil {
		log.Println("sql fatal error in createKey prep for", cmd )
		log.Fatal(err)
	}

	_,err = stmt.Exec(keyID,userID,roleID)
	if err != nil {
		log.Println("sql error in addRole",err)
		return err;
	}

	return nil;
}

func getMeta( keyID int64, userID int64, meta string ) ([]int64,error) {

	var cmd string

	switch {
	case meta == "owner":
		cmd = "SELECT keys.oID FROM keys JOIN  keyUsers ON keys.kID = keyUsers.kID WHERE keys.kID = $1 AND keyUsers.uID = $2"
	case meta == "admins":
		cmd = "SELECT keyAdmins.uID FROM keyAdmins JOIN keyUsers ON keyAdmins.kID = keyUsers.kID WHERE keyUsers.kID = $1 AND keyUsers.uID = $2"
	case meta == "users":
		cmd = "SELECT users.uID FROM keyUsers AS users JOIN keyUsers AS perm ON users.kID = perm.kID WHERE perm.kID = $1 AND perm.uID = $2"
	default:
		return []int64{}, errors.New("bad meta");
	}

	// note if using mySQL use ? but Postgres is $1 in prepare statements
	stmt, err := db.Prepare( cmd )
	if err != nil {
		log.Println("sql fatal error in getMeta prep meta=",meta)
		log.Fatal(err)
	}

	rows,err := stmt.Query(keyID,userID)
	switch {
	case err == sql.ErrNoRows:
		log.Printf("no data found") // key probably does not exist for this to happen
	case err != nil:
		log.Println("sql fatal error in getMeta querry")
		log.Fatal(err)
	}
	defer rows.Close()

	var ret []int64 = []int64{}

	for rows.Next() {
		var uID int64
		err := rows.Scan(&uID)
		if err != nil {
			log.Fatal(err)
		}
		log.Println( "got id =", uID )
		ret = append( ret, uID )
	}
	if err = rows.Err(); err != nil {
		log.Fatal(err)
	}

	return ret,nil
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
        if r.URL.Path != "/" {
                http.NotFound(w, r)
                return
        }

        type PageData struct {
                Junk string
        }
        data := PageData{Junk: "nothing"}
        err := templates.ExecuteTemplate(w, "index.html", data)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
        }
}

func searchKeyHandler(w http.ResponseWriter, r *http.Request) {
	var err error
        w.Header().Set("Access-Control-Allow-Origin", "*")
	vars := mux.Vars(r)

	var keyID int64 = 0;
	var userID int64 = 1;

	keyID,err = strconv.ParseInt(  vars["keyID"] , 0, 64 );
	if err != nil {
		http.NotFound(w, r)
		return
	}

	log.Println("GET key: keyID=", keyID, " userID=",userID )

	io.WriteString(w, getKey(keyID,userID) )
}

func createKeyHandler(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")

	err := r.ParseForm()
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
        }

	var keyVal string = r.FormValue("keyVal");
	var userID int64 = 1;

	var keyID int64 = createKey( userID, keyVal );

	log.Println("POST createKey: keyID=", keyID, "userID=",userID )

	io.WriteString(w, "{ \"keyID\": " + strconv.FormatInt(keyID,10) +" }" )
}

func addRoleHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	var err error

	w.Header().Set("Access-Control-Allow-Origin", "*")

	var keyID int64 = 0;
	var userID int64 = 1;
	var roleID int64 = 0;

	keyID,err = strconv.ParseInt(  vars["keyID"] , 0, 64 );
	if err != nil {
		http.NotFound(w, r)
		return
	}

	roleID,err = strconv.ParseInt(  vars["roleID"] , 0, 64 );
	if err != nil {
		http.NotFound(w, r)
		return
	}

	var role string = vars["role"];

	switch {
	case role == "admin" :
	case role == "user" :
	default:
		http.NotFound(w, r)
		return
	}

	log.Println("POST addRole: keyID=", keyID, " userID=",userID , "roleID=", roleID , "role=" , role )

	err = addRole( keyID, userID, role, roleID )
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func getKeyMetaHandler (w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	var err error

	w.Header().Set("Access-Control-Allow-Origin", "*")

	var userID int64 = 1;

	var keyID int64 = 0;
		keyID,err = strconv.ParseInt(  vars["keyID"] , 0, 64 );
	if err != nil {
		http.NotFound(w, r)
		return
	}

	var meta string = vars["meta"];

	var vals []int64
	var idName string

	switch {
	case meta == "owner" :
		idName = "ownerIDs"
	case meta == "admins" :
		idName = "adminIDs"
	case meta == "users" :
		idName = "userIDs"
	default:
		http.NotFound(w, r)
		return
	}

	log.Println("GET meta: keyID=", keyID, "userID=", userID, "meta=" , meta )

	vals,err = getMeta( keyID, userID, meta )

	io.WriteString(w, "{ \"" +idName + "\": [ " )
	for i := range vals {
		if i != 0 {
			io.WriteString(w, "," )
		}
		io.WriteString(w, strconv.FormatInt( vals[i], 10) )
	}
	io.WriteString(w, " ] }" )
}

func main() {
	var err error

	// get all the configuration data
	if len( os.Args ) != 2 {
		log.Fatal( "must pass database hostname on CLI" );
	}
	var hostName string = os.Args[1]

	var pgPassword string = os.Getenv("SECM_DB_SECRET")
	if len( pgPassword ) < 1 {
		log.Fatal( "must set environ variable SECM_DB_SECRET" );
	}

	// set up the DB
	setupDatabase(hostName,pgPassword)
        defer db.Close()

	// Check DB is alive
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	// set up the routes
	router := mux.NewRouter()
	router.HandleFunc("/", mainHandler).Methods("GET")
	router.HandleFunc("/v1/key", createKeyHandler).Methods("POST")
	router.HandleFunc("/v1/key/{keyID}", searchKeyHandler).Methods("GET")
	router.HandleFunc("/v1/key/{keyID}/{role}/{roleID}", addRoleHandler).Methods("POST") // role is user | admin
	router.HandleFunc("/v1/key/{keyID}/{meta}", getKeyMetaHandler).Methods("GET") // meta is ownwer | users | admins
	http.Handle("/", router)

	// run the web server
        http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Tetsuo Kiso

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 kytea provides access to KyTea.
//
// NOTE: This package calls the C APIs of a forked KyTea
// (https://github.com/tetsuok/kytea). This is because the original
// Kytea provides useful C++ APIs, but C APIs are not available.
//
// BUG: This package is in incomplete. Until APIs of C APIs and Go are fixed,
// the compatiblities are not guaranteed.
package kytea

/*
#cgo LDFLAGS: -lkytea -lstdc++

#include "kytea/c.h"
#include <stdlib.h>
*/
import "C"

import (
	"errors"
	"unsafe"
)

type Tagger struct {
	rep    *C.kytea_t
	config *C.kytea_config_t
}

type StringUtil struct {
	rep *C.kytea_stringutil_t
}

type Sentence struct {
	rep *C.kytea_sentence_t
}

// Create creates a Tagger object
// It is the caller's responsiblity to delete the tagger when it is done with it.
func Create(filename string) (*Tagger, error) {
	var rep *C.kytea_t
	name := C.CString(filename)
	defer C.free(unsafe.Pointer(name))
	rep = C.kytea_create()
	if rep == nil {
		return nil, errors.New("Cannot create a KyTea object")
	}
	if ret := C.kytea_read_model(rep, name); ret < 0 {
		return nil, errors.New("Cannot read the model")
	}

	config := C.kytea_get_config(rep)
	if config == nil {
		return nil, errors.New("Cannot get a KyTea config object")
	}
	return &Tagger{rep: rep, config: config}, nil
}

// Destroy frees the allocated C object.
func (t *Tagger) Destroy() {
	C.kytea_destroy(t.rep)
	C.kytea_config_destroy(t.config)
	t.rep = nil
	t.config = nil
}

func (t *Tagger) CalculateWS(s *Sentence) {
	C.kytea_calculate_ws(t.rep, s.rep)
}

func (t *Tagger) CalculateAllTags(s *Sentence) {
	i := C.int(0)
	for i = 0; i < C.kytea_config_get_num_tags(t.config); i++ {
		C.kytea_calculate_tags(t.rep, s.rep, i)
	}
}

// CreateStringUtil creates a StringUtil object.  It is the caller's
// responsiblity to delete the StringUtil object when it is done with
// it.
func (t *Tagger) CreateStringUtil() (*StringUtil, error) {
	rep := C.kytea_get_stringutil(t.rep)
	if rep == nil {
		return nil, errors.New("Cannot get a KyTea stringutil object")
	}
	return &StringUtil{rep}, nil
}

func (u *StringUtil) Destroy() {
	C.kytea_stringutil_destroy(u.rep)
	u.rep = nil
}

// CreateSentence creates a Sentence object.  It is the caller's
// responsiblity to delete the sentence when it is done with it.
func (u *StringUtil) CreateSentence(s string) (*Sentence, error) {
	cs := C.CString(s)
	defer C.free(unsafe.Pointer(cs))
	rep := C.kytea_sentence_create(u.rep, cs)
	if rep == nil {
		return nil, errors.New("Cannot create a sentence object")
	}
	return &Sentence{rep}, nil
}

func (s *Sentence) Destroy() {
	C.kytea_sentence_destroy(s.rep)
	s.rep = nil
}

// NumWords returns the number of tokens. This method needs to be called
// after tagger.CalculateWS() is called.
func (s *Sentence) NumWords() int {
	return int(C.kytea_sentence_get_num_words(s.rep))
}

// SurfaceAt returns the surface form at i in the tokenized sentence.
func (u *StringUtil) SurfaceAt(s *Sentence, i int) string {
	cs := C.kytea_sentence_surface_at(u.rep, s.rep, C.int(i))
	defer C.free(unsafe.Pointer(cs))
	return C.GoString(cs)
}

// ReadingAt returns the reading form at i in the tokenized sentence.
// This method needs to be called after tagger.CalculateAllTags() is called.
func (u *StringUtil) ReadingAt(s *Sentence, i int) string {
	cs := C.kytea_sentence_reading_at(u.rep, s.rep, C.int(i))
	defer C.free(unsafe.Pointer(cs))
	return C.GoString(cs)
}
<commit_msg>Fix link error<commit_after>/*
Copyright 2014 Tetsuo Kiso

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 kytea provides access to KyTea.
//
// NOTE: This package calls the C APIs of a forked KyTea
// (https://github.com/tetsuok/kytea). This is because the original
// Kytea provides useful C++ APIs, but C APIs are not available.
//
// BUG: This package is in incomplete. Until APIs of C APIs and Go are fixed,
// the compatiblities are not guaranteed.
package kytea

/*
#cgo LDFLAGS: -lkytea -lstdc++ -lm

#include "kytea/c.h"
#include <stdlib.h>
*/
import "C"

import (
	"errors"
	"unsafe"
)

type Tagger struct {
	rep    *C.kytea_t
	config *C.kytea_config_t
}

type StringUtil struct {
	rep *C.kytea_stringutil_t
}

type Sentence struct {
	rep *C.kytea_sentence_t
}

// Create creates a Tagger object
// It is the caller's responsiblity to delete the tagger when it is done with it.
func Create(filename string) (*Tagger, error) {
	var rep *C.kytea_t
	name := C.CString(filename)
	defer C.free(unsafe.Pointer(name))
	rep = C.kytea_create()
	if rep == nil {
		return nil, errors.New("Cannot create a KyTea object")
	}
	if ret := C.kytea_read_model(rep, name); ret < 0 {
		return nil, errors.New("Cannot read the model")
	}

	config := C.kytea_get_config(rep)
	if config == nil {
		return nil, errors.New("Cannot get a KyTea config object")
	}
	return &Tagger{rep: rep, config: config}, nil
}

// Destroy frees the allocated C object.
func (t *Tagger) Destroy() {
	C.kytea_destroy(t.rep)
	C.kytea_config_destroy(t.config)
	t.rep = nil
	t.config = nil
}

func (t *Tagger) CalculateWS(s *Sentence) {
	C.kytea_calculate_ws(t.rep, s.rep)
}

func (t *Tagger) CalculateAllTags(s *Sentence) {
	i := C.int(0)
	for i = 0; i < C.kytea_config_get_num_tags(t.config); i++ {
		C.kytea_calculate_tags(t.rep, s.rep, i)
	}
}

// CreateStringUtil creates a StringUtil object.  It is the caller's
// responsiblity to delete the StringUtil object when it is done with
// it.
func (t *Tagger) CreateStringUtil() (*StringUtil, error) {
	rep := C.kytea_get_stringutil(t.rep)
	if rep == nil {
		return nil, errors.New("Cannot get a KyTea stringutil object")
	}
	return &StringUtil{rep}, nil
}

func (u *StringUtil) Destroy() {
	C.kytea_stringutil_destroy(u.rep)
	u.rep = nil
}

// CreateSentence creates a Sentence object.  It is the caller's
// responsiblity to delete the sentence when it is done with it.
func (u *StringUtil) CreateSentence(s string) (*Sentence, error) {
	cs := C.CString(s)
	defer C.free(unsafe.Pointer(cs))
	rep := C.kytea_sentence_create(u.rep, cs)
	if rep == nil {
		return nil, errors.New("Cannot create a sentence object")
	}
	return &Sentence{rep}, nil
}

func (s *Sentence) Destroy() {
	C.kytea_sentence_destroy(s.rep)
	s.rep = nil
}

// NumWords returns the number of tokens. This method needs to be called
// after tagger.CalculateWS() is called.
func (s *Sentence) NumWords() int {
	return int(C.kytea_sentence_get_num_words(s.rep))
}

// SurfaceAt returns the surface form at i in the tokenized sentence.
func (u *StringUtil) SurfaceAt(s *Sentence, i int) string {
	cs := C.kytea_sentence_surface_at(u.rep, s.rep, C.int(i))
	defer C.free(unsafe.Pointer(cs))
	return C.GoString(cs)
}

// ReadingAt returns the reading form at i in the tokenized sentence.
// This method needs to be called after tagger.CalculateAllTags() is called.
func (u *StringUtil) ReadingAt(s *Sentence, i int) string {
	cs := C.kytea_sentence_reading_at(u.rep, s.rep, C.int(i))
	defer C.free(unsafe.Pointer(cs))
	return C.GoString(cs)
}
<|endoftext|>


<commit_before>package cwl

import (
	"fmt"
	"strings"
)

// Input represents "CommandInputParameter".
// @see http://www.commonwl.org/v1.0/CommandLineTool.html#CommandInputParameter
type Input struct {
	ID             string          `json:"id"`
	Label          string          `json:"label"`
	Doc            string          `json:"doc"`
	Format         string          `json:"format"`
	Binding        *Binding        `json:"inputBinding"`
	Default        *InputDefault   `json:"default"`
	Types          []Type          `json:"type"`
	SecondaryFiles []SecondaryFile `json:"secondary_files"`
	// Input.Provided is what provided by parameters.(json|yaml)
	Provided interface{} `json:"-"`
}

// New constructs "Input" struct from interface{}.
func (_ Input) New(i interface{}) Input {
	dest := Input{}
	switch x := i.(type) {
	case map[string]interface{}:
		for key, v := range x {
			switch key {
			case "id":
				dest.ID = v.(string)
			case "type":
				dest.Types = Type{}.NewList(v)
			case "label":
				dest.Label = v.(string)
			case "doc":
				dest.Doc = v.(string)
			case "inputBinding":
				dest.Binding = Binding{}.New(v)
			case "default":
				dest.Default = InputDefault{}.New(v)
			case "format":
				dest.Format = v.(string)
			case "secondaryFiles":
				dest.SecondaryFiles = SecondaryFile{}.NewList(v)
			}
		}
	case string:
		dest.Types = Type{}.NewList(x)
	case []interface{}:
		for _, v := range x {
			dest.Types = append(dest.Types, Type{}.New(v))
		}
	}
	return dest
}

// Flatten ...
func (input Input) Flatten() []string {
	if input.Provided == nil {
		// In case "input.Default == nil" should be validated by usage layer.
		return input.Default.Flatten(input.Binding)
	}
	flattened := []string{}
	if repr := input.Types[0]; len(input.Types) == 1 {
		switch repr.Type {
		case "array":
			switch repr.Items[0].Type {
			case "int": // Array of Int
				tobejoined := []string{}
				for _, e := range input.Provided.([]interface{}) {
					tobejoined = append(tobejoined, fmt.Sprintf("%v", e))
				}
				flattened = append(flattened, strings.Join(tobejoined, input.Binding.Separator))
			case "File": // Array of Files
				switch arr := input.Provided.(type) {
				case []string:
					// TODO:
				case []interface{}:
					separated := []string{}
					for _, e := range arr {
						switch v := e.(type) {
						case map[interface{}]interface{}:
							separated = append(separated, fmt.Sprintf("%v", v["location"]))
						default:
							// TODO:
						}
					}
					// In case it's Array of Files, unlike array of int,
					// it's NOT gonna be joined with .Binding.Separator.
					flattened = append(flattened, separated...)
				}
			default:
				// TODO:
			}
		case "int":
			flattened = append(flattened, fmt.Sprintf("%v", input.Provided.(int)))
		case "File":
			switch provided := input.Provided.(type) {
			case map[interface{}]interface{}:
				// TODO: more strict type casting
				flattened = append(flattened, fmt.Sprintf("%v", provided["location"]))
			default:
			}
		default:
			flattened = append(flattened, fmt.Sprintf("%v", input.Provided))
		}
	}
	if input.Binding != nil && input.Binding.Prefix != "" {
		flattened = append([]string{input.Binding.Prefix}, flattened...)
	}

	return flattened
}

// Inputs represents "inputs" field in CWL.
type Inputs []Input

// New constructs new "Inputs" struct.
func (_ Inputs) New(i interface{}) Inputs {
	dest := Inputs{}
	switch x := i.(type) {
	case []interface{}:
		for _, v := range x {
			dest = append(dest, Input{}.New(v))
		}
	case map[string]interface{}:
		for key, v := range x {
			input := Input{}.New(v)
			input.ID = key
			dest = append(dest, input)
		}
	}
	return dest
}

// Len for sorting.
func (ins Inputs) Len() int {
	return len(ins)
}

// Less for sorting.
func (ins Inputs) Less(i, j int) bool {
	prev, next := ins[i].Binding, ins[j].Binding
	switch [2]bool{prev == nil, next == nil} {
	case [2]bool{true, true}:
		return true
	case [2]bool{false, true}:
		return prev.Position < 0
	case [2]bool{true, false}:
		return next.Position > 0
	default:
		return prev.Position <= next.Position
	}
}

// Swap for sorting.
func (ins Inputs) Swap(i, j int) {
	ins[i], ins[j] = ins[j], ins[i]
}
<commit_msg>Prepend prefix of each item with type "array of File"<commit_after>package cwl

import (
	"fmt"
	"strings"
)

// Input represents "CommandInputParameter".
// @see http://www.commonwl.org/v1.0/CommandLineTool.html#CommandInputParameter
type Input struct {
	ID             string          `json:"id"`
	Label          string          `json:"label"`
	Doc            string          `json:"doc"`
	Format         string          `json:"format"`
	Binding        *Binding        `json:"inputBinding"`
	Default        *InputDefault   `json:"default"`
	Types          []Type          `json:"type"`
	SecondaryFiles []SecondaryFile `json:"secondary_files"`
	// Input.Provided is what provided by parameters.(json|yaml)
	Provided interface{} `json:"-"`
}

// New constructs "Input" struct from interface{}.
func (_ Input) New(i interface{}) Input {
	dest := Input{}
	switch x := i.(type) {
	case map[string]interface{}:
		for key, v := range x {
			switch key {
			case "id":
				dest.ID = v.(string)
			case "type":
				dest.Types = Type{}.NewList(v)
			case "label":
				dest.Label = v.(string)
			case "doc":
				dest.Doc = v.(string)
			case "inputBinding":
				dest.Binding = Binding{}.New(v)
			case "default":
				dest.Default = InputDefault{}.New(v)
			case "format":
				dest.Format = v.(string)
			case "secondaryFiles":
				dest.SecondaryFiles = SecondaryFile{}.NewList(v)
			}
		}
	case string:
		dest.Types = Type{}.NewList(x)
	case []interface{}:
		for _, v := range x {
			dest.Types = append(dest.Types, Type{}.New(v))
		}
	}
	return dest
}

// Flatten ...
func (input Input) Flatten() []string {
	if input.Provided == nil {
		// In case "input.Default == nil" should be validated by usage layer.
		return input.Default.Flatten(input.Binding)
	}
	flattened := []string{}
	if repr := input.Types[0]; len(input.Types) == 1 {
		switch repr.Type {
		case "array":
			switch repr.Items[0].Type {
			case "int": // Array of Int
				tobejoined := []string{}
				for _, e := range input.Provided.([]interface{}) {
					tobejoined = append(tobejoined, fmt.Sprintf("%v", e))
				}
				flattened = append(flattened, strings.Join(tobejoined, input.Binding.Separator))
			case "File": // Array of Files
				switch arr := input.Provided.(type) {
				case []string:
					// TODO:
				case []interface{}:
					separated := []string{}
					for _, e := range arr {
						switch v := e.(type) {
						case map[interface{}]interface{}:
							if repr.Binding != nil
								separated = app
							}
							separated = append(separated, fmt.Sprintf("%v", v["location"]))
						default:
							// TODO:
						}
					}
					// In case it's Array of Files, unlike array of int,
					// it's NOT gonna be joined with .Binding.Separator.
					flattened = append(flattened, separated...)
				}
			default:
				// TODO:
			}
		case "int":
			flattened = append(flattened, fmt.Sprintf("%v", input.Provided.(int)))
		case "File":
			switch provided := input.Provided.(type) {
			case map[interface{}]interface{}:
				// TODO: more strict type casting
				flattened = append(flattened, fmt.Sprintf("%v", provided["location"]))
			default:
			}
		default:
			flattened = append(flattened, fmt.Sprintf("%v", input.Provided))
		}
	}
	if input.Binding != nil && input.Binding.Prefix != "" {
		flattened = append([]string{input.Binding.Prefix}, flattened...)
	}

	return flattened
}

// Inputs represents "inputs" field in CWL.
type Inputs []Input

// New constructs new "Inputs" struct.
func (_ Inputs) New(i interface{}) Inputs {
	dest := Inputs{}
	switch x := i.(type) {
	case []interface{}:
		for _, v := range x {
			dest = append(dest, Input{}.New(v))
		}
	case map[string]interface{}:
		for key, v := range x {
			input := Input{}.New(v)
			input.ID = key
			dest = append(dest, input)
		}
	}
	return dest
}

// Len for sorting.
func (ins Inputs) Len() int {
	return len(ins)
}

// Less for sorting.
func (ins Inputs) Less(i, j int) bool {
	prev, next := ins[i].Binding, ins[j].Binding
	switch [2]bool{prev == nil, next == nil} {
	case [2]bool{true, true}:
		return true
	case [2]bool{false, true}:
		return prev.Position < 0
	case [2]bool{true, false}:
		return next.Position > 0
	default:
		return prev.Position <= next.Position
	}
}

// Swap for sorting.
func (ins Inputs) Swap(i, j int) {
	ins[i], ins[j] = ins[j], ins[i]
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
)

type IPAddress struct {
	IP string `json:"ip"`
}

func jsonip(w http.ResponseWriter, r *http.Request) {
	host := net.ParseIP(r.Header["X-Forwarded-For"][len(r.Header["X-Forwarded-For"])-1]).String()
	jsonStr, _ := json.Marshal(IPAddress{host})

	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, string(jsonStr))
}

func textip(w http.ResponseWriter, r *http.Request) {
	host := net.ParseIP(r.Header["X-Forwarded-For"][len(r.Header["X-Forwarded-For"])-1]).String()

	w.Header().Set("Content-Type", "text/plain")
	fmt.Fprintf(w, host)
}

func errorHandler(w http.ResponseWriter, r *http.Request, status int) {
	w.WriteHeader(status)
}

func main() {
	http.HandleFunc("/json", jsonip)
	http.HandleFunc("/text", textip)

	err := http.ListenAndServe(":"+os.Getenv("PORT"), nil)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Attempting to fix our broken NotFound handler.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
)

type IPAddress struct {
	IP string `json:"ip"`
}

func jsonip(w http.ResponseWriter, r *http.Request) {
	host := net.ParseIP(r.Header["X-Forwarded-For"][len(r.Header["X-Forwarded-For"])-1]).String()
	jsonStr, _ := json.Marshal(IPAddress{host})

	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, string(jsonStr))
}

func textip(w http.ResponseWriter, r *http.Request) {
	host := net.ParseIP(r.Header["X-Forwarded-For"][len(r.Header["X-Forwarded-For"])-1]).String()

	w.Header().Set("Content-Type", "text/plain")
	fmt.Fprintf(w, host)
}

func NotFound(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
}

func main() {
	http.HandleFunc("/json", jsonip)
	http.HandleFunc("/text", textip)

	err := http.ListenAndServe(":"+os.Getenv("PORT"), nil)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their corresponding JavaScript syntax.
//
// Use MakeWrapper to expose methods to JavaScript. When passing values directly, the following type conversions are performed:
//
//  | Go type               | JavaScript type       | Conversions back to interface{} |
//  | --------------------- | --------------------- | ------------------------------- |
//  | bool                  | Boolean               | bool                            |
//  | integers and floats   | Number                | float64                         |
//  | string                | String                | string                          |
//  | []int8                | Int8Array             | []int8                          |
//  | []int16               | Int16Array            | []int16                         |
//  | []int32, []int        | Int32Array            | []int                           |
//  | []uint8               | Uint8Array            | []uint8                         |
//  | []uint16              | Uint16Array           | []uint16                        |
//  | []uint32, []uint      | Uint32Array           | []uint                          |
//  | []float32             | Float32Array          | []float32                       |
//  | []float64             | Float64Array          | []float64                       |
//  | all other slices      | Array                 | []interface{}                   |
//  | arrays                | see slice type        | see slice type                  |
//  | functions             | Function              | func(...interface{}) *js.object |
//  | time.Time             | Date                  | time.Time                       |
//  | -                     | instanceof Node       | *js.object                      |
//  | maps, structs         | instanceof Object     | map[string]interface{}          |
//
// Additionally, for a struct containing a *js.object field, only the content of the field will be passed to JavaScript and vice versa.
package js

// Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax. A nil pointer to Object is equal to JavaScript's "null". Object can not be used as a map key.
type Object struct{ object *Object }

// Get returns the object's property with the given key.
func (o *Object) Get(key string) *Object { return o.object.Get(key) }

// Set assigns the value to the object's property with the given key.
func (o *Object) Set(key string, value interface{}) { o.object.Set(key, value) }

// Delete removes the object's property with the given key.
func (o *Object) Delete(key string) { o.object.Delete(key) }

// Length returns the object's "length" property, converted to int.
func (o *Object) Length() int { return o.object.Length() }

// Index returns the i'th element of an array.
func (o *Object) Index(i int) *Object { return o.object.Index(i) }

// SetIndex sets the i'th element of an array.
func (o *Object) SetIndex(i int, value interface{}) { o.object.SetIndex(i, value) }

// Call calls the object's method with the given name.
func (o *Object) Call(name string, args ...interface{}) *Object { return o.object.Call(name, args...) }

// Invoke calls the object itself. This will fail if it is not a function.
func (o *Object) Invoke(args ...interface{}) *Object { return o.object.Invoke(args...) }

// New creates a new instance of this type object. This will fail if it not a function (constructor).
func (o *Object) New(args ...interface{}) *Object { return o.object.New(args...) }

// Bool returns the object converted to bool according to JavaScript type conversions.
func (o *Object) Bool() bool { return o.object.Bool() }

// String returns the object converted to string according to JavaScript type conversions.
func (o *Object) String() string { return o.object.String() }

// Int returns the object converted to int according to JavaScript type conversions (parseInt).
func (o *Object) Int() int { return o.object.Int() }

// Int64 returns the object converted to int64 according to JavaScript type conversions (parseInt).
func (o *Object) Int64() int64 { return o.object.Int64() }

// Uint64 returns the object converted to uint64 according to JavaScript type conversions (parseInt).
func (o *Object) Uint64() uint64 { return o.object.Uint64() }

// Float returns the object converted to float64 according to JavaScript type conversions (parseFloat).
func (o *Object) Float() float64 { return o.object.Float() }

// Interface returns the object converted to interface{}. See GopherJS' README for details.
func (o *Object) Interface() interface{} { return o.object.Interface() }

// Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use.
func (o *Object) Unsafe() uintptr { return o.object.Unsafe() }

// Error encapsulates JavaScript errors. Those are turned into a Go panic and may be rescued, giving an *Error that holds the JavaScript error object.
type Error struct {
	*Object
}

// Error returns the message of the encapsulated JavaScript error object.
func (err *Error) Error() string {
	return "JavaScript error: " + err.Get("message").String()
}

// Stack returns the stack property of the encapsulated JavaScript error object.
func (err *Error) Stack() string {
	return err.Get("stack").String()
}

// Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js).
var Global *Object

// Module gives the value of the "module" variable set by Node.js. Hint: Set a module export with 'js.Module.Get("exports").Set("exportName", ...)'.
var Module *Object

// Undefined gives the JavaScript value "undefined".
var Undefined *Object

// Debugger gets compiled to JavaScript's "debugger;" statement.
func Debugger() {}

// InternalObject returns the internal JavaScript object that represents i. Not intended for public use.
func InternalObject(i interface{}) *Object {
	return nil
}

// MakeFunc wraps a function and gives access to the values of JavaScript's "this" and "arguments" keywords.
func MakeFunc(func(this *Object, arguments []*Object) interface{}) *Object {
	return nil
}

// Keys returns the keys of the given JavaScript object.
func Keys(o *Object) []string {
	if o == nil || o == Undefined {
		return nil
	}
	a := Global.Get("Object").Call("keys", o)
	s := make([]string, a.Length())
	for i := 0; i < a.Length(); i++ {
		s[i] = a.Index(i).String()
	}
	return s
}

// MakeWrapper creates a JavaScript object which has wrappers for the exported methods of i. Use explicit getter and setter methods to expose struct fields to JavaScript.
func MakeWrapper(i interface{}) *Object {
	v := InternalObject(i)
	o := Global.Get("Object").New()
	methods := v.Get("constructor").Get("methods")
	for i := 0; i < methods.Length(); i++ {
		m := methods.Index(i)
		if m.Get("pkg").String() != "" { // not exported
			continue
		}
		o.Set(m.Get("name").String(), func(args ...*Object) *Object {
			return Global.Call("$externalizeFunction", v.Get(m.Get("prop").String()), m.Get("typ"), true).Call("apply", v, args)
		})
	}
	return o
}

// NewArrayBuffer creates a JavaScript ArrayBuffer from a byte slice.
func NewArrayBuffer(b []byte) *Object {
	slice := InternalObject(b)
	offset := slice.Get("$offset").Int()
	length := slice.Get("$length").Int()
	return slice.Get("$array").Get("buffer").Call("slice", offset, offset+length)
}

// M is a simple map type. It is intended as a shorthand for JavaScript objects (before conversion).
type M map[string]interface{}

// S is a simple slice type. It is intended as a shorthand for JavaScript arrays (before conversion).
type S []interface{}

func init() {
	// avoid dead code elimination
	e := Error{}
	_ = e
}
<commit_msg>fixed docs for js package<commit_after>// Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their corresponding JavaScript syntax.
//
// Use MakeWrapper to expose methods to JavaScript. When passing values directly, the following type conversions are performed:
//
//  | Go type               | JavaScript type       | Conversions back to interface{} |
//  | --------------------- | --------------------- | ------------------------------- |
//  | bool                  | Boolean               | bool                            |
//  | integers and floats   | Number                | float64                         |
//  | string                | String                | string                          |
//  | []int8                | Int8Array             | []int8                          |
//  | []int16               | Int16Array            | []int16                         |
//  | []int32, []int        | Int32Array            | []int                           |
//  | []uint8               | Uint8Array            | []uint8                         |
//  | []uint16              | Uint16Array           | []uint16                        |
//  | []uint32, []uint      | Uint32Array           | []uint                          |
//  | []float32             | Float32Array          | []float32                       |
//  | []float64             | Float64Array          | []float64                       |
//  | all other slices      | Array                 | []interface{}                   |
//  | arrays                | see slice type        | see slice type                  |
//  | functions             | Function              | func(...interface{}) *js.Object |
//  | time.Time             | Date                  | time.Time                       |
//  | -                     | instanceof Node       | *js.Object                      |
//  | maps, structs         | instanceof Object     | map[string]interface{}          |
//
// Additionally, for a struct containing a *js.Object field, only the content of the field will be passed to JavaScript and vice versa.
package js

// Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax. A nil pointer to Object is equal to JavaScript's "null". Object can not be used as a map key.
type Object struct{ object *Object }

// Get returns the object's property with the given key.
func (o *Object) Get(key string) *Object { return o.object.Get(key) }

// Set assigns the value to the object's property with the given key.
func (o *Object) Set(key string, value interface{}) { o.object.Set(key, value) }

// Delete removes the object's property with the given key.
func (o *Object) Delete(key string) { o.object.Delete(key) }

// Length returns the object's "length" property, converted to int.
func (o *Object) Length() int { return o.object.Length() }

// Index returns the i'th element of an array.
func (o *Object) Index(i int) *Object { return o.object.Index(i) }

// SetIndex sets the i'th element of an array.
func (o *Object) SetIndex(i int, value interface{}) { o.object.SetIndex(i, value) }

// Call calls the object's method with the given name.
func (o *Object) Call(name string, args ...interface{}) *Object { return o.object.Call(name, args...) }

// Invoke calls the object itself. This will fail if it is not a function.
func (o *Object) Invoke(args ...interface{}) *Object { return o.object.Invoke(args...) }

// New creates a new instance of this type object. This will fail if it not a function (constructor).
func (o *Object) New(args ...interface{}) *Object { return o.object.New(args...) }

// Bool returns the object converted to bool according to JavaScript type conversions.
func (o *Object) Bool() bool { return o.object.Bool() }

// String returns the object converted to string according to JavaScript type conversions.
func (o *Object) String() string { return o.object.String() }

// Int returns the object converted to int according to JavaScript type conversions (parseInt).
func (o *Object) Int() int { return o.object.Int() }

// Int64 returns the object converted to int64 according to JavaScript type conversions (parseInt).
func (o *Object) Int64() int64 { return o.object.Int64() }

// Uint64 returns the object converted to uint64 according to JavaScript type conversions (parseInt).
func (o *Object) Uint64() uint64 { return o.object.Uint64() }

// Float returns the object converted to float64 according to JavaScript type conversions (parseFloat).
func (o *Object) Float() float64 { return o.object.Float() }

// Interface returns the object converted to interface{}. See GopherJS' README for details.
func (o *Object) Interface() interface{} { return o.object.Interface() }

// Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use.
func (o *Object) Unsafe() uintptr { return o.object.Unsafe() }

// Error encapsulates JavaScript errors. Those are turned into a Go panic and may be rescued, giving an *Error that holds the JavaScript error object.
type Error struct {
	*Object
}

// Error returns the message of the encapsulated JavaScript error object.
func (err *Error) Error() string {
	return "JavaScript error: " + err.Get("message").String()
}

// Stack returns the stack property of the encapsulated JavaScript error object.
func (err *Error) Stack() string {
	return err.Get("stack").String()
}

// Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js).
var Global *Object

// Module gives the value of the "module" variable set by Node.js. Hint: Set a module export with 'js.Module.Get("exports").Set("exportName", ...)'.
var Module *Object

// Undefined gives the JavaScript value "undefined".
var Undefined *Object

// Debugger gets compiled to JavaScript's "debugger;" statement.
func Debugger() {}

// InternalObject returns the internal JavaScript object that represents i. Not intended for public use.
func InternalObject(i interface{}) *Object {
	return nil
}

// MakeFunc wraps a function and gives access to the values of JavaScript's "this" and "arguments" keywords.
func MakeFunc(func(this *Object, arguments []*Object) interface{}) *Object {
	return nil
}

// Keys returns the keys of the given JavaScript object.
func Keys(o *Object) []string {
	if o == nil || o == Undefined {
		return nil
	}
	a := Global.Get("Object").Call("keys", o)
	s := make([]string, a.Length())
	for i := 0; i < a.Length(); i++ {
		s[i] = a.Index(i).String()
	}
	return s
}

// MakeWrapper creates a JavaScript object which has wrappers for the exported methods of i. Use explicit getter and setter methods to expose struct fields to JavaScript.
func MakeWrapper(i interface{}) *Object {
	v := InternalObject(i)
	o := Global.Get("Object").New()
	methods := v.Get("constructor").Get("methods")
	for i := 0; i < methods.Length(); i++ {
		m := methods.Index(i)
		if m.Get("pkg").String() != "" { // not exported
			continue
		}
		o.Set(m.Get("name").String(), func(args ...*Object) *Object {
			return Global.Call("$externalizeFunction", v.Get(m.Get("prop").String()), m.Get("typ"), true).Call("apply", v, args)
		})
	}
	return o
}

// NewArrayBuffer creates a JavaScript ArrayBuffer from a byte slice.
func NewArrayBuffer(b []byte) *Object {
	slice := InternalObject(b)
	offset := slice.Get("$offset").Int()
	length := slice.Get("$length").Int()
	return slice.Get("$array").Get("buffer").Call("slice", offset, offset+length)
}

// M is a simple map type. It is intended as a shorthand for JavaScript objects (before conversion).
type M map[string]interface{}

// S is a simple slice type. It is intended as a shorthand for JavaScript arrays (before conversion).
type S []interface{}

func init() {
	// avoid dead code elimination
	e := Error{}
	_ = e
}
<|endoftext|>


<commit_before>package main

import "os"
import "github.com/docker/engine-api/client"
import "math/rand"
import "os/exec"
import "strconv"
import "github.com/seehuhn/mt19937"
import "time"
import "os/user"

var cli *client.Client

type ExecRequest struct {
	Image string
	Cmd []string
	SourceFileName string
}

type Judge struct {
	Code string
	Compile *ExecRequest
	Exec ExecRequest
	Time int64
	Mem  int64
	TCCount int // The number of test cases
}

type JudgeResult int

const (
	Accepted            JudgeResult = 0
	WrongAnswer         JudgeResult = 1
	CompileError        JudgeResult = 2
	TimeLimitExceeded   JudgeResult = 3
	MemoryLimitExceeded JudgeResult = 4
	RuntimeError        JudgeResult = 5
	InternalError       JudgeResult = 6
	Judging             JudgeResult = 7
	CompileTimeLimitExceeded JudgeResult = 8
	CompileMemoryLimitExceeded JudgeResult = 9
)

type JudgeStatus struct {
	Case     *string
	JR       JudgeResult
	Mem      int64
	Time     int64
	Msg      *string
}

func CreateInternalError(msg string) JudgeStatus {
	return JudgeStatus{nil, InternalError, 0, 0, &msg}
}

const BASE_RAND_STRING = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

func RandomName() string {
	rng := rand.New(mt19937.New())
	rng.Seed(time.Now().UnixNano())

	res := make([]byte, 0, 32)
	for i := 0; i < 32; i++ {
		res = append(res, BASE_RAND_STRING[rng.Intn(len(BASE_RAND_STRING))])
	}

	return string(res)
}

func (j *Judge) Run(ch chan<- JudgeStatus, tests <-chan struct {
	Name string
	In  string
	Out string
}) {
	// Close a channel to send results of judging
	defer close(ch)

	// Identity
	id := RandomName()

	// User
	_, err := exec.Command("useradd", "--no-create-home", id).Output()

	if err != nil {
		ch <- CreateInternalError("Failed to create a directory to build your code. " + err.Error())

		return
	}

	uid, err := user.Lookup(id)

	if err != nil {
		ch <- CreateInternalError("Failed to look up a user. " + err.Error())

		return
	}

	uidInt, err := strconv.ParseInt(uid.Uid, 10, 64)

	if err != nil {
		ch <- CreateInternalError("Failed to parseInt uid. " + err.Error())

		return
	}
	gidInt, err := strconv.ParseInt(uid.Gid, 10, 64)
	if err != nil {
		ch <- CreateInternalError("Failed to parseInt gid. " + err.Error())

		return
	}

	defer exec.Command("userdel", id)

	// Working Directory
	path := workingDirectory + "/" + id

	err = os.Mkdir(path, 0664)

	if err != nil {
		ch <- CreateInternalError("Failed to create a directory. " + err.Error())

		return
	}

	defer os.RemoveAll(path)

	uidInt = uidInt * gidInt
	err = os.Chown(path, int(uidInt), int(gidInt))

	if err != nil {
		ch <- CreateInternalError("Failed to chown the directory. " + err.Error())

		return
	}
	err = os.Chmod(path, 0664)

	if err != nil {
		ch <- CreateInternalError("Failed to chmod the directory. " + err.Error())

		return
	}
	// Source File
	fp, err := os.Create(path + "/" + j.Compile.SourceFileName)

	if err != nil {
		ch <- CreateInternalError("Failed to create source file." + err.Error())

		return
	}

	l, err := fp.Write([]byte(j.Code))

	if err != nil {
		ch <- CreateInternalError("Failed to write your code on your file. " + err.Error())

		return
	}

	if l != len(j.Code) {
		ch <- CreateInternalError("Failed to write your code on your file.")

		return
	}

	fp.Close()

	err = os.Chmod(path + "/" + j.Compile.SourceFileName, 0644)

	if err != nil {
		ch <- CreateInternalError("Failed to chmod the source file. " + err.Error())

		return
	}

	// Compile
	if j.Compile != nil {
		exe, err := NewExecutor(id, 512 * 1024 * 1024, []string{"ls", "-la", "/work"}, j.Compile.Image, []string{path + ":" + "/work"}, uid.Uid)

		if err != nil {
			ch <- CreateInternalError("Failed to create a Docker container to compile your code." + err.Error())

			return
		}

		res := exe.Run(10000, "")

		exe.Delete()
		if res.Status != ExecFinished {
			switch res.Status {
			case ExecError:
				ch <- CreateInternalError("Failed to execute a compiler." + res.Stderr)

				return
			case ExecMemoryLimitExceeded:
				ch <- JudgeStatus{JR: CompileMemoryLimitExceeded}

				return
			case ExecTimeLimitExceeded:
				ch <- JudgeStatus{JR: CompileTimeLimitExceeded}

				return
			}
		}

		if res.ExitCode != 0 || true { // Debug
			msg := res.Stdout + res.Stderr
			ch <- JudgeStatus{JR: CompileError, Msg: &msg}

			return
		}
	}
	return

	exe, err := NewExecutor(id, j.Mem, j.Exec.Cmd, j.Exec.Image, []string{path + ":" + "/work:ro"}, uid.Uid)

	if err != nil {
		ch <- CreateInternalError("Failed to create a Docker container to judge." + err.Error())

		return
	}

	defer exe.Delete()

	tcCounter := 0
	for tc, res := <-tests; res; tc, res = <-tests {
		res := exe.Run(j.Time, tc.In)

		if res.Status != ExecFinished {
			switch res.Status {
			case ExecError:
				msg := "Failed to execute your code." + res.Stderr
				ch <- JudgeStatus{Case: &tc.Name, JR: InternalError, Msg: &msg}
			case ExecMemoryLimitExceeded:
				ch <- JudgeStatus{Case: &tc.Name, JR: MemoryLimitExceeded}
			case ExecTimeLimitExceeded:
				ch <- JudgeStatus{Case: &tc.Name, JR: TimeLimitExceeded}
			}
		}else {
			if res.ExitCode != 0 {
				ch <- JudgeStatus{Case: &tc.Name, JR: RuntimeError}
			}else {
				if res.Stdout == tc.Out {
					ch <- JudgeStatus{Case: &tc.Name, JR: Accepted}
				}else {
					ch <- JudgeStatus{Case: &tc.Name, JR: WrongAnswer}
				}
			}
		}

		tcCounter++

		msg := strconv.FormatInt(int64(tcCounter), 10) + "/" + strconv.FormatInt(int64(j.TCCount), 10)
		ch <- JudgeStatus{JR: Judging, Msg: &msg}
	}

}
<commit_msg>Sun May 22 19:25:13 JST 2016<commit_after>package main

import "os"
import "github.com/docker/engine-api/client"
import "math/rand"
import "os/exec"
import "strconv"
import "github.com/seehuhn/mt19937"
import "time"
import "os/user"

var cli *client.Client

type ExecRequest struct {
	Image string
	Cmd []string
	SourceFileName string
}

type Judge struct {
	Code string
	Compile *ExecRequest
	Exec ExecRequest
	Time int64
	Mem  int64
	TCCount int // The number of test cases
}

type JudgeResult int

const (
	Accepted            JudgeResult = 0
	WrongAnswer         JudgeResult = 1
	CompileError        JudgeResult = 2
	TimeLimitExceeded   JudgeResult = 3
	MemoryLimitExceeded JudgeResult = 4
	RuntimeError        JudgeResult = 5
	InternalError       JudgeResult = 6
	Judging             JudgeResult = 7
	CompileTimeLimitExceeded JudgeResult = 8
	CompileMemoryLimitExceeded JudgeResult = 9
)

type JudgeStatus struct {
	Case     *string
	JR       JudgeResult
	Mem      int64
	Time     int64
	Msg      *string
}

func CreateInternalError(msg string) JudgeStatus {
	return JudgeStatus{nil, InternalError, 0, 0, &msg}
}

const BASE_RAND_STRING = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

func RandomName() string {
	rng := rand.New(mt19937.New())
	rng.Seed(time.Now().UnixNano())

	res := make([]byte, 0, 32)
	for i := 0; i < 32; i++ {
		res = append(res, BASE_RAND_STRING[rng.Intn(len(BASE_RAND_STRING))])
	}

	return string(res)
}

func (j *Judge) Run(ch chan<- JudgeStatus, tests <-chan struct {
	Name string
	In  string
	Out string
}) {
	// Close a channel to send results of judging
	defer close(ch)

	// Identity
	id := RandomName()

	// User
	_, err := exec.Command("useradd", "--no-create-home", id).Output()

	if err != nil {
		ch <- CreateInternalError("Failed to create a directory to build your code. " + err.Error())

		return
	}

	uid, err := user.Lookup(id)

	if err != nil {
		ch <- CreateInternalError("Failed to look up a user. " + err.Error())

		return
	}

	uidInt, err := strconv.ParseInt(uid.Uid, 10, 64)

	if err != nil {
		ch <- CreateInternalError("Failed to parseInt uid. " + err.Error())

		return
	}
	gidInt, err := strconv.ParseInt(uid.Gid, 10, 64)
	if err != nil {
		ch <- CreateInternalError("Failed to parseInt gid. " + err.Error())

		return
	}

	defer exec.Command("userdel", id)

	// Working Directory
	path := workingDirectory + "/" + id

	err = os.Mkdir(path, 0777)

	if err != nil {
		ch <- CreateInternalError("Failed to create a directory. " + err.Error())

		return
	}

	defer os.RemoveAll(path)

	uidInt = uidInt * gidInt
	err = os.Chown(path, int(uidInt), int(gidInt))

	if err != nil {
		ch <- CreateInternalError("Failed to chown the directory. " + err.Error())

		return
	}
	err = os.Chmod(path, 0777)

	if err != nil {
		ch <- CreateInternalError("Failed to chmod the directory. " + err.Error())

		return
	}
	// Source File
	fp, err := os.Create(path + "/" + j.Compile.SourceFileName)

	if err != nil {
		ch <- CreateInternalError("Failed to create source file." + err.Error())

		return
	}

	l, err := fp.Write([]byte(j.Code))

	if err != nil {
		ch <- CreateInternalError("Failed to write your code on your file. " + err.Error())

		return
	}

	if l != len(j.Code) {
		ch <- CreateInternalError("Failed to write your code on your file.")

		return
	}

	fp.Close()

	err = os.Chmod(path + "/" + j.Compile.SourceFileName, 0644)

	if err != nil {
		ch <- CreateInternalError("Failed to chmod the source file. " + err.Error())

		return
	}

	// Compile
	if j.Compile != nil {
		exe, err := NewExecutor(id, 512 * 1024 * 1024, []string{"ls"}, j.Compile.Image, []string{path + ":" + "/work"}, uid.Uid)

		if err != nil {
			ch <- CreateInternalError("Failed to create a Docker container to compile your code." + err.Error())

			return
		}

		res := exe.Run(10000, "")

		exe.Delete()
		if res.Status != ExecFinished {
			switch res.Status {
			case ExecError:
				ch <- CreateInternalError("Failed to execute a compiler." + res.Stderr)

				return
			case ExecMemoryLimitExceeded:
				ch <- JudgeStatus{JR: CompileMemoryLimitExceeded}

				return
			case ExecTimeLimitExceeded:
				ch <- JudgeStatus{JR: CompileTimeLimitExceeded}

				return
			}
		}

		if res.ExitCode != 0 || true { // Debug
			msg := res.Stdout + res.Stderr
			ch <- JudgeStatus{JR: CompileError, Msg: &msg}

			return
		}
	}
	return

	exe, err := NewExecutor(id, j.Mem, j.Exec.Cmd, j.Exec.Image, []string{path + ":" + "/work:ro"}, uid.Uid)

	if err != nil {
		ch <- CreateInternalError("Failed to create a Docker container to judge." + err.Error())

		return
	}

	defer exe.Delete()

	tcCounter := 0
	for tc, res := <-tests; res; tc, res = <-tests {
		res := exe.Run(j.Time, tc.In)

		if res.Status != ExecFinished {
			switch res.Status {
			case ExecError:
				msg := "Failed to execute your code." + res.Stderr
				ch <- JudgeStatus{Case: &tc.Name, JR: InternalError, Msg: &msg}
			case ExecMemoryLimitExceeded:
				ch <- JudgeStatus{Case: &tc.Name, JR: MemoryLimitExceeded}
			case ExecTimeLimitExceeded:
				ch <- JudgeStatus{Case: &tc.Name, JR: TimeLimitExceeded}
			}
		}else {
			if res.ExitCode != 0 {
				ch <- JudgeStatus{Case: &tc.Name, JR: RuntimeError}
			}else {
				if res.Stdout == tc.Out {
					ch <- JudgeStatus{Case: &tc.Name, JR: Accepted}
				}else {
					ch <- JudgeStatus{Case: &tc.Name, JR: WrongAnswer}
				}
			}
		}

		tcCounter++

		msg := strconv.FormatInt(int64(tcCounter), 10) + "/" + strconv.FormatInt(int64(j.TCCount), 10)
		ch <- JudgeStatus{JR: Judging, Msg: &msg}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"flag"
	"io"
	"log"
	"os"
	"strconv"

	"github.com/flynn/go-discover/discover"
	"github.com/flynn/lorne/types"
	sampic "github.com/flynn/sampi/client"
	"github.com/flynn/sampi/types"
	"github.com/titanous/go-dockerclient"
)

var state = NewState()
var Docker *docker.Client

func main() {
	externalAddr := flag.String("external", "", "external IP of host")
	configFile := flag.String("config", "", "configuration file")
	hostID := flag.String("id", randomID(), "host id")
	flag.Parse()

	go server()
	go allocatePorts()

	disc, err := discover.NewClient()
	if err != nil {
		log.Fatal(err)
	}
	if err := disc.Register("flynn-lorne."+*hostID, "1113", nil); err != nil {
		log.Fatal(err)
	}

	scheduler, err := sampic.New()
	if err != nil {
		log.Fatal(err)
	}
	log.Print("Connected to scheduler")

	Docker, err = docker.NewClient("http://localhost:4243")
	if err != nil {
		log.Fatal(err)
	}

	go streamEvents(Docker)
	go syncScheduler(scheduler)

	var host *sampi.Host
	if *configFile != "" {
		host, err = parseConfig(*configFile)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		host = &sampi.Host{Resources: make(map[string]sampi.ResourceValue)}
	}
	if _, ok := host.Resources["memory"]; !ok {
		host.Resources["memory"] = sampi.ResourceValue{Value: 1024}
	}
	host.ID = *hostID

	jobs := make(chan *sampi.Job)
	scheduler.RegisterHost(host, jobs)
	log.Print("Host registered")
	for job := range jobs {
		log.Printf("%#v", job.Config)
		var hostConfig *docker.HostConfig
		if job.TCPPorts > 0 {
			port := strconv.Itoa(<-portAllocator)
			job.Config.Env = append(job.Config.Env, "PORT="+port)
			job.Config.ExposedPorts = map[string]struct{}{port + "/tcp": struct{}{}}
			job.Config.Name = "flynn-" + job.ID
			hostConfig = &docker.HostConfig{
				PortBindings:    map[string][]docker.PortBinding{port + "/tcp": {{HostPort: port}}},
				PublishAllPorts: true,
			}
		}
		if *externalAddr != "" {
			job.Config.Env = append(job.Config.Env, "EXTERNAL_IP="+*externalAddr, "SD_HOST="+*externalAddr, "DISCOVERD="+*externalAddr+":1111")
		}
		state.AddJob(job)
		container, err := Docker.CreateContainer(job.Config)
		if err == docker.ErrNoSuchImage {
			err = Docker.PullImage(docker.PullImageOptions{Repository: job.Config.Image}, os.Stdout)
			if err != nil {
				log.Fatal(err)
			}
			container, err = Docker.CreateContainer(job.Config)
		}
		if err != nil {
			log.Fatal(err)
		}
		state.SetContainerID(job.ID, container.ID)
		state.WaitAttach(job.ID)
		if err := Docker.StartContainer(container.ID, hostConfig); err != nil {
			log.Fatal(err)
		}
		state.SetStatusRunning(job.ID)
	}
}

func syncScheduler(scheduler *sampic.Client) {
	events := make(chan lorne.Event)
	state.AddListener("all", events)
	for event := range events {
		if event.Event != "stop" {
			continue
		}
		log.Println("remove job", event.JobID)
		if err := scheduler.RemoveJobs([]string{event.JobID}); err != nil {
			log.Println("remove job", event.JobID, "error:", err)
			// TODO: try to reconnect?
		}
	}
}

func streamEvents(client *docker.Client) {
	stream, err := client.Events()
	if err != nil {
		log.Fatal(err)
	}
	for event := range stream.Events {
		log.Printf("%#v", event)
		if event.Status != "die" {
			continue
		}
		container, err := client.InspectContainer(event.ID)
		if err != nil {
			log.Println("inspect container", event.ID, "error:", err)
			// TODO: set job status anyway?
			continue
		}
		state.SetStatusDone(event.ID, container.State.ExitCode)
	}
	log.Println("events done", stream.Error)
}

func randomID() string {
	b := make([]byte, 16)
	enc := make([]byte, 24)
	_, err := io.ReadFull(rand.Reader, b)
	if err != nil {
		panic(err) // This shouldn't ever happen, right?
	}
	base64.URLEncoding.Encode(enc, b)
	return string(bytes.TrimRight(enc, "="))
}

var portAllocator = make(chan int)

// TODO: fix this, horribly broken
const startPort = 55000
const endPort = 65535

func allocatePorts() {
	for i := startPort; i < endPort; i++ {
		portAllocator <- i
	}
	// TODO: handle wrap-around
}
<commit_msg>host: Move job processing into a separate function<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"flag"
	"io"
	"log"
	"os"
	"strconv"

	"github.com/flynn/go-discover/discover"
	"github.com/flynn/lorne/types"
	sampic "github.com/flynn/sampi/client"
	"github.com/flynn/sampi/types"
	"github.com/titanous/go-dockerclient"
)

var state = NewState()
var Docker *docker.Client

func main() {
	externalAddr := flag.String("external", "", "external IP of host")
	configFile := flag.String("config", "", "configuration file")
	hostID := flag.String("id", randomID(), "host id")
	flag.Parse()

	go server()
	go allocatePorts()

	disc, err := discover.NewClient()
	if err != nil {
		log.Fatal(err)
	}
	if err := disc.Register("flynn-lorne."+*hostID, "1113", nil); err != nil {
		log.Fatal(err)
	}

	scheduler, err := sampic.New()
	if err != nil {
		log.Fatal(err)
	}
	log.Print("Connected to scheduler")

	Docker, err = docker.NewClient("http://localhost:4243")
	if err != nil {
		log.Fatal(err)
	}

	go streamEvents(Docker)
	go syncScheduler(scheduler)

	var host *sampi.Host
	if *configFile != "" {
		host, err = parseConfig(*configFile)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		host = &sampi.Host{Resources: make(map[string]sampi.ResourceValue)}
	}
	if _, ok := host.Resources["memory"]; !ok {
		host.Resources["memory"] = sampi.ResourceValue{Value: 1024}
	}
	host.ID = *hostID

	jobs := make(chan *sampi.Job)
	scheduler.RegisterHost(host, jobs)
	log.Print("Host registered")
	processJobs(jobs, *externalAddr)
}

func processJobs(jobs chan *sampi.Job, externalAddr string) {
	for job := range jobs {
		log.Printf("%#v", job.Config)
		var hostConfig *docker.HostConfig
		if job.TCPPorts > 0 {
			port := strconv.Itoa(<-portAllocator)
			job.Config.Env = append(job.Config.Env, "PORT="+port)
			job.Config.ExposedPorts = map[string]struct{}{port + "/tcp": struct{}{}}
			job.Config.Name = "flynn-" + job.ID
			hostConfig = &docker.HostConfig{
				PortBindings:    map[string][]docker.PortBinding{port + "/tcp": {{HostPort: port}}},
				PublishAllPorts: true,
			}
		}
		if externalAddr != "" {
			job.Config.Env = append(job.Config.Env, "EXTERNAL_IP="+externalAddr, "SD_HOST="+externalAddr, "DISCOVERD="+externalAddr+":1111")
		}
		state.AddJob(job)
		container, err := Docker.CreateContainer(job.Config)
		if err == docker.ErrNoSuchImage {
			err = Docker.PullImage(docker.PullImageOptions{Repository: job.Config.Image}, os.Stdout)
			if err != nil {
				log.Fatal(err)
			}
			container, err = Docker.CreateContainer(job.Config)
		}
		if err != nil {
			log.Fatal(err)
		}
		state.SetContainerID(job.ID, container.ID)
		state.WaitAttach(job.ID)
		if err := Docker.StartContainer(container.ID, hostConfig); err != nil {
			log.Fatal(err)
		}
		state.SetStatusRunning(job.ID)
	}
}

func syncScheduler(scheduler *sampic.Client) {
	events := make(chan lorne.Event)
	state.AddListener("all", events)
	for event := range events {
		if event.Event != "stop" {
			continue
		}
		log.Println("remove job", event.JobID)
		if err := scheduler.RemoveJobs([]string{event.JobID}); err != nil {
			log.Println("remove job", event.JobID, "error:", err)
			// TODO: try to reconnect?
		}
	}
}

func streamEvents(client *docker.Client) {
	stream, err := client.Events()
	if err != nil {
		log.Fatal(err)
	}
	for event := range stream.Events {
		log.Printf("%#v", event)
		if event.Status != "die" {
			continue
		}
		container, err := client.InspectContainer(event.ID)
		if err != nil {
			log.Println("inspect container", event.ID, "error:", err)
			// TODO: set job status anyway?
			continue
		}
		state.SetStatusDone(event.ID, container.State.ExitCode)
	}
	log.Println("events done", stream.Error)
}

func randomID() string {
	b := make([]byte, 16)
	enc := make([]byte, 24)
	_, err := io.ReadFull(rand.Reader, b)
	if err != nil {
		panic(err) // This shouldn't ever happen, right?
	}
	base64.URLEncoding.Encode(enc, b)
	return string(bytes.TrimRight(enc, "="))
}

var portAllocator = make(chan int)

// TODO: fix this, horribly broken
const startPort = 55000
const endPort = 65535

func allocatePorts() {
	for i := startPort; i < endPort; i++ {
		portAllocator <- i
	}
	// TODO: handle wrap-around
}
<|endoftext|>


<commit_before>// +build linux,cgo darwin,cgo !windows
// +build 386 amd64

/*
 * magic.go
 *
 * Copyright 2013 Krzysztof Wilczynski
 *
 * 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 magic

/*
#cgo LDFLAGS: -lmagic
#include "functions.h"
*/
import "C"

import (
	"fmt"
	"os"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"unsafe"
)

type magic struct {
	sync.Mutex
	flags  int
	path   []string
	cookie C.magic_t
}

func (m *magic) close() {
	if m != nil && m.cookie != nil {
		C.magic_close(m.cookie)
		m.cookie = nil
	}
	runtime.SetFinalizer(m, nil)
}

type Magic struct {
	*magic
}

func New() (*Magic, error) {
	rv := C.magic_open(C.int(NONE))
	if rv == nil {
		errno := syscall.ENOMEM
		return nil, &MagicError{int(errno), errno.Error()}
	}

	mgc := &Magic{&magic{flags: NONE, cookie: rv}}
	runtime.SetFinalizer(mgc.magic, (*magic).close)
	return mgc, nil
}

func (mgc *Magic) Close() {
	mgc.Lock()
	defer mgc.Unlock()
	mgc.magic.close()
}

func (mgc *Magic) String() string {
	return fmt.Sprintf("Magic{flags:%d path:%s cookie:%p}",
		mgc.flags, mgc.path, mgc.cookie)
}

func (mgc *Magic) Path() ([]string, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return []string{}, mgc.error()
	}

	if len(mgc.path) != 0 && os.Getenv("MAGIC") == "" {
		return mgc.path, nil
	}
	rv := C.GoString(C.magic_getpath_wrapper())
	mgc.path = strings.Split(rv, ":")
	return mgc.path, nil
}

func (mgc *Magic) Flags() (int, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return -1, mgc.error()
	}
	return mgc.flags, nil
}

func (mgc *Magic) SetFlags(flags int) error {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return mgc.error()
	}

	rv, err := C.magic_setflags_wrapper(mgc.cookie, C.int(flags))
	if err != nil {
		errno := err.(syscall.Errno)
		return &MagicError{int(errno), errno.Error()}
	}
	if rv < 0 {
		return mgc.error()
	}

	mgc.flags = flags
	return nil
}

func (mgc *Magic) Load(files ...string) error {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return mgc.error()
	}

	var cfiles *C.char
	defer C.free(unsafe.Pointer(cfiles))

	if len(files) != 0 {
		cfiles = C.CString(strings.Join(files, ":"))
	} else {
		cfiles = C.magic_getpath_wrapper()
	}

	if rv := C.magic_load_wrapper(mgc.cookie, cfiles); rv < 0 {
		return mgc.error()
	}
	mgc.path = strings.Split(C.GoString(cfiles), ":")
	return nil
}

func (mgc *Magic) Compile(files ...string) error {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return mgc.error()
	}

	var cfiles *C.char
	if len(files) != 0 {
		cfiles = C.CString(strings.Join(files, ":"))
		defer C.free(unsafe.Pointer(cfiles))
	}

	if rv := C.magic_compile_wrapper(mgc.cookie, cfiles); rv < 0 {
		return mgc.error()
	}
	return nil
}

func (mgc *Magic) Check(files ...string) error {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return mgc.error()
	}

	var cfiles *C.char
	if len(files) != 0 {
		cfiles = C.CString(strings.Join(files, ":"))
		defer C.free(unsafe.Pointer(cfiles))
	}

	if rv := C.magic_check_wrapper(mgc.cookie, cfiles); rv < 0 {
		return mgc.error()
	}
	return nil
}

func (mgc *Magic) File(filename string) (string, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return "", mgc.error()
	}

	cfilename := C.CString(filename)
	defer C.free(unsafe.Pointer(cfilename))

	cstring := C.magic_file(mgc.cookie, cfilename)
	if cstring == nil {
		return "", mgc.error()
	}
	return C.GoString(cstring), nil
}

func (mgc *Magic) Buffer(buffer []byte) (string, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return "", mgc.error()
	}

	p, length := unsafe.Pointer(&buffer[0]), C.size_t(len(buffer))

	cstring := C.magic_buffer(mgc.cookie, p, length)
	if cstring == nil {
		return "", mgc.error()
	}
	return C.GoString(cstring), nil
}

func (mgc *Magic) Descriptor(fd uintptr) (string, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return "", mgc.error()
	}

	cstring := C.magic_descriptor(mgc.cookie, C.int(fd))
	if cstring == nil {
		return "", mgc.error()
	}
	return C.GoString(cstring), nil
}

func (mgc *Magic) Version() (int, error) {
	mgc.Lock()
	defer mgc.Unlock()

	rv, err := C.magic_version_wrapper()
	if err != nil {
		errno := err.(syscall.Errno)
		return -1, &MagicError{int(errno), errno.Error()}
	}
	return int(rv), nil
}

func (mgc *Magic) error() *MagicError {
	if mgc.cookie == nil {
		errno := syscall.EINVAL
		return &MagicError{int(syscall.EINVAL), errno.Error()}
	}

	errno := int(C.magic_errno(mgc.cookie))
	cstring := C.magic_error(mgc.cookie)
	if cstring != nil {
		return &MagicError{errno, C.GoString(cstring)}
	}
	return nil
}

func (mgc *Magic) destroy() {
	mgc.Close()
}

func Compile(files ...string) error {
	mgc, err := New()
	if err != nil {
		return err
	}
	defer mgc.Close()

	if err := mgc.Compile(files...); err != nil {
		return err
	}
	return nil
}

func Check(files ...string) error {
	mgc, err := New()
	if err != nil {
		return err
	}
	defer mgc.Close()

	if err := mgc.Check(files...); err != nil {
		return err
	}
	return nil
}

func FileMime(filename string, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME)
	return mgc.File(filename)
}

func FileEncoding(filename string, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME_ENCODING)
	return mgc.File(filename)
}

func FileType(filename string, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME_TYPE)
	return mgc.File(filename)
}

func BufferMime(buffer []byte, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME)
	return mgc.Buffer(buffer)
}

func BufferEncoding(buffer []byte, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME_ENCODING)
	return mgc.Buffer(buffer)
}

func BufferType(buffer []byte, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME_TYPE)
	return mgc.Buffer(buffer)
}
<commit_msg>Re-factor to make it more concise.<commit_after>// +build linux,cgo darwin,cgo !windows
// +build 386 amd64

/*
 * magic.go
 *
 * Copyright 2013 Krzysztof Wilczynski
 *
 * 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 magic

/*
#cgo LDFLAGS: -lmagic
#include "functions.h"
*/
import "C"

import (
	"fmt"
	"os"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"unsafe"
)

type magic struct {
	sync.Mutex
	flags  int
	path   []string
	cookie C.magic_t
}

func (m *magic) close() {
	if m != nil && m.cookie != nil {
		C.magic_close(m.cookie)
		m.cookie = nil
	}
	runtime.SetFinalizer(m, nil)
}

type Magic struct {
	*magic
}

func New() (*Magic, error) {
	rv := C.magic_open(C.int(NONE))
	if rv == nil {
		errno := syscall.ENOMEM
		return nil, &MagicError{int(errno), errno.Error()}
	}

	mgc := &Magic{&magic{flags: NONE, cookie: rv}}
	runtime.SetFinalizer(mgc.magic, (*magic).close)
	return mgc, nil
}

func (mgc *Magic) Close() {
	mgc.Lock()
	defer mgc.Unlock()
	mgc.magic.close()
}

func (mgc *Magic) String() string {
	return fmt.Sprintf("Magic{flags:%d path:%s cookie:%p}",
		mgc.flags, mgc.path, mgc.cookie)
}

func (mgc *Magic) Path() ([]string, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return []string{}, mgc.error()
	}

	if len(mgc.path) != 0 && os.Getenv("MAGIC") == "" {
		return mgc.path, nil
	}
	rv := C.GoString(C.magic_getpath_wrapper())
	mgc.path = strings.Split(rv, ":")
	return mgc.path, nil
}

func (mgc *Magic) Flags() (int, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return -1, mgc.error()
	}
	return mgc.flags, nil
}

func (mgc *Magic) SetFlags(flags int) error {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return mgc.error()
	}

	rv, err := C.magic_setflags_wrapper(mgc.cookie, C.int(flags))
	if rv < 0 && err != nil {
		errno := err.(syscall.Errno)
		return &MagicError{int(errno), errno.Error()}
	}

	mgc.flags = flags
	return nil
}

func (mgc *Magic) Load(files ...string) error {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return mgc.error()
	}

	var cfiles *C.char
	defer C.free(unsafe.Pointer(cfiles))

	if len(files) != 0 {
		cfiles = C.CString(strings.Join(files, ":"))
	} else {
		cfiles = C.magic_getpath_wrapper()
	}

	if rv := C.magic_load_wrapper(mgc.cookie, cfiles); rv < 0 {
		return mgc.error()
	}
	mgc.path = strings.Split(C.GoString(cfiles), ":")
	return nil
}

func (mgc *Magic) Compile(files ...string) error {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return mgc.error()
	}

	var cfiles *C.char
	if len(files) != 0 {
		cfiles = C.CString(strings.Join(files, ":"))
		defer C.free(unsafe.Pointer(cfiles))
	}

	if rv := C.magic_compile_wrapper(mgc.cookie, cfiles); rv < 0 {
		return mgc.error()
	}
	return nil
}

func (mgc *Magic) Check(files ...string) error {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return mgc.error()
	}

	var cfiles *C.char
	if len(files) != 0 {
		cfiles = C.CString(strings.Join(files, ":"))
		defer C.free(unsafe.Pointer(cfiles))
	}

	if rv := C.magic_check_wrapper(mgc.cookie, cfiles); rv < 0 {
		return mgc.error()
	}
	return nil
}

func (mgc *Magic) File(filename string) (string, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return "", mgc.error()
	}

	cfilename := C.CString(filename)
	defer C.free(unsafe.Pointer(cfilename))

	cstring := C.magic_file(mgc.cookie, cfilename)
	if cstring == nil {
		return "", mgc.error()
	}
	return C.GoString(cstring), nil
}

func (mgc *Magic) Buffer(buffer []byte) (string, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return "", mgc.error()
	}

	p, length := unsafe.Pointer(&buffer[0]), C.size_t(len(buffer))

	cstring := C.magic_buffer(mgc.cookie, p, length)
	if cstring == nil {
		return "", mgc.error()
	}
	return C.GoString(cstring), nil
}

func (mgc *Magic) Descriptor(fd uintptr) (string, error) {
	mgc.Lock()
	defer mgc.Unlock()

	if mgc.cookie == nil {
		return "", mgc.error()
	}

	cstring := C.magic_descriptor(mgc.cookie, C.int(fd))
	if cstring == nil {
		return "", mgc.error()
	}
	return C.GoString(cstring), nil
}

func (mgc *Magic) Version() (int, error) {
	mgc.Lock()
	defer mgc.Unlock()

	rv, err := C.magic_version_wrapper()
	if rv < 0 && err != nil {
		errno := err.(syscall.Errno)
		return -1, &MagicError{int(errno), errno.Error()}
	}
	return int(rv), nil
}

func (mgc *Magic) error() *MagicError {
	if mgc.cookie == nil {
		errno := syscall.EINVAL
		return &MagicError{int(syscall.EINVAL), errno.Error()}
	}

	errno := int(C.magic_errno(mgc.cookie))
	cstring := C.magic_error(mgc.cookie)
	if cstring != nil {
		return &MagicError{errno, C.GoString(cstring)}
	}
	return nil
}

func (mgc *Magic) destroy() {
	mgc.Close()
}

func Compile(files ...string) error {
	mgc, err := New()
	if err != nil {
		return err
	}
	defer mgc.Close()

	if err := mgc.Compile(files...); err != nil {
		return err
	}
	return nil
}

func Check(files ...string) error {
	mgc, err := New()
	if err != nil {
		return err
	}
	defer mgc.Close()

	if err := mgc.Check(files...); err != nil {
		return err
	}
	return nil
}

func FileMime(filename string, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME)
	return mgc.File(filename)
}

func FileEncoding(filename string, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME_ENCODING)
	return mgc.File(filename)
}

func FileType(filename string, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME_TYPE)
	return mgc.File(filename)
}

func BufferMime(buffer []byte, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME)
	return mgc.Buffer(buffer)
}

func BufferEncoding(buffer []byte, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME_ENCODING)
	return mgc.Buffer(buffer)
}

func BufferType(buffer []byte, files ...string) (string, error) {
	mgc, err := New()
	if err != nil {
		return "", err
	}
	defer mgc.Close()

	if err := mgc.Load(files...); err != nil {
		return "", err
	}
	mgc.SetFlags(MIME_TYPE)
	return mgc.Buffer(buffer)
}
<|endoftext|>