<commit_before>package graph

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"sourcegraph.com/sourcegraph/srcgraph/repo"
)

type (
	SID        int64
	SymbolPath string
	SymbolKind string
)

// SymbolKey specifies a symbol, either concretely or abstractly. A concrete
// symbol key has a non-empty CommitID and refers to a symbol defined in a
// specific commit. An abstract symbol key has an empty CommitID and is
// considered to refer to symbols from any number of commits (so long as the
// Repo, UnitType, Unit, and Path match).
//
// You can think of CommitID as the time dimension. With an empty CommitID, you
// are referring to a symbol that may or may not exist at various times. With a
// non-empty CommitID, you are referring to a specific definition of a symbol at
// the time specified by the CommitID.
type SymbolKey struct {
	// Repo is the VCS repository that defines this symbol. Its Elasticsearch mapping is defined
	// separately.
	Repo repo.URI `json:",omitempty"`

	// CommitID is the ID of the VCS commit that this symbol was defined in. The
	// CommitID is always a full commit ID (40 hexadecimal characters for git
	// and hg), never a branch or tag name.
	CommitID string `db:"commit_id" json:",omitempty"`

	// UnitType is the type name of the source unit (obtained from unit.Type(u))
	// that this symbol was defined in.
	UnitType string `db:"unit_type" json:",omitempty"`

	// Unit is the name of the source unit (obtained from u.Name()) that this
	// symbol was defined in.
	Unit string `json:",omitempty"`

	// Path is the path to this symbol, relative to the repo. Its Elasticsearch mapping is defined
	// separately (because it is a multi_field, which the struct tag can't currently represent).
	Path SymbolPath
}

func (s SymbolKey) String() string {
	b, err := json.Marshal(s)
	if err != nil {
		panic("SymbolKey.String: " + err.Error())
	}
	return string(b)
}

type Symbol struct {
	// SID is a unique, sequential ID for a symbol. It is regenerated each time
	// the symbol is emitted by the grapher and saved to the database. The SID
	// is used as an optimization (e.g., joins are faster on SID than on
	// SymbolKey).
	SID SID `db:"sid" json:",omitempty" elastic:"type:integer,index:no"`

	// SymbolKey is the natural unique key for a symbol. It is stable
	// (subsequent runs of a grapher will emit the same symbols with the same
	// SymbolKeys).
	SymbolKey

	// SpecificPath is the language-specific "path" to this symbol, using
	// language-specific separators (e.g., "::" and "." instead of "/", which is
	// used in the SymbolKey.Path value).
	SpecificPath string `db:"specific_path"`

	// Kind is the language-independent kind of this symbol.
	Kind SymbolKind `elastic:"type:string,index:analyzed"`

	// SpecificKind is the language-specific kind of this symbol (which is in
	// some cases equal to the Kind).
	SpecificKind string `db:"specific_kind"`

	Name string

	// Callable is true if this symbol may be called or invoked, such as in the
	// case of functions or methods.
	Callable bool `db:"callable"`

	File string `elastic:"type:string,index:no"`

	DefStart int `db:"def_start" elastic:"type:integer,index:no"`
	DefEnd   int `db:"def_end" elastic:"type:integer,index:no"`

	Exported bool `elastic:"type:boolean,index:not_analyzed"`

	// Test is whether this symbol is defined in test code (as opposed to main
	// code). For example, definitions in Go *_test.go files have Test = true.
	Test bool `elastic:"type:boolean,index:not_analyzed"`

	TypeExpr string `db:"type_expr" json:",omitempty"`
}

func (s *Symbol) Language() string {
	switch s.UnitType {
	case "GoPackage":
		return "Go"
		// TODO(sqs): add Python, JS, etc.
	}
	return "unknown language"
}

// TODO!(sqs): factor this into the individual source unit packages
func (s *Symbol) Signature() string {
	var removeOwnImportPath = func(str string) string {
		if s.UnitType == "GoPackage" {
			return strings.Replace(strings.Replace(str, string(s.Repo)+".", "", -1), string(s.Repo)+"/", "", -1)
		}
		return str
	}

	if s.TypeExpr == "" {
		return ""
	}
	if !s.Callable {
		if (s.Kind == Field || s.Kind == Var || s.Kind == Type) && len(s.TypeExpr) < 50 {
			return " " + removeOwnImportPath(s.TypeExpr)
		}
		return ""
	}

	switch s.UnitType {
	case "GoPackage":
		return removeOwnImportPath(strings.TrimPrefix(s.TypeExpr, "func"))
	case "js":
		return strings.TrimPrefix(s.TypeExpr, "fn")
	case "python":
		// remove up to first paren
		i := strings.Index(s.TypeExpr, "(")
		if i == -1 {
			return s.TypeExpr
		}
		return s.TypeExpr[:i]
	case "ruby":
		return s.TypeExpr
	}
	return s.TypeExpr
}

func (s *Symbol) sortKey() string { return s.SymbolKey.String() }

// Propagate describes type/value propagation in code. A Propagate entry from A
// (src) to B (dst) indicates that the type/value of A propagates to B. In Tern,
// this is indicated by A having a "fwd" property whose value is an array that
// includes B.
//
//
// ## Motivation & example
//
// For example, consider the following JavaScript code:
//
//   var a = Foo;
//   var b = a;
//
// Foo, a, and b are each their own symbol. We could resolve all of them to the
// symbol of their original type (perhaps Foo), but there are occasions when you
// do want to see only the definition of a or b and examples thereof. Therefore,
// we need to represent them as distinct symbols.
//
// Even though Foo, a, and b are distinct symbols, there are propagation
// relationships between them that are important to represent. The type of Foo
// propagates to both a and b, and the type of a propagates to b. In this case,
// we would have 3 Propagates: Propagate{Src: "Foo", Dst: "a"}, Propagate{Src:
// "Foo", Dst: "b"}, and Propagate{Src: "a", Dst: "b"}. (The propagation
// relationships could be described by just the first and last Propagates, but
// we explicitly include all paths as a denormalization optimization to avoid
// requiring an unbounded number of DB queries to determine which symbols a type
// propagates to or from.)
//
//
// ## Directionality
//
// Propagation is unidirectional, in the general case. In the example above, if
// Foo referred to a JavaScript object and if the code were evaluated, any
// *runtime* type changes (e.g., setting a property) on Foo, a, and b would be
// reflected on all of the others. But this doesn't hold for static analysis;
// it's not always true that if a property "a.x" or "b.x" exists, then "Foo.x"
// exists. The simplest example is when Foo is an external definition. Perhaps
// this example file (which uses Foo as a library) modifies Foo to add a new
// property, but other libraries that use Foo would never see that property
// because they wouldn't be executed in the same context as this example file.
// So, in general, we cannot say that Foo receives all types applied to symbols
// that Foo propagates to.
//
//
// ## Hypothetical Python example
//
// Consider the following 2 Python files:
//
//   """file1.py"""
//   class Foo(object): end
//
//   """file2.py"""
//   from .file1 import Foo
//   Foo2 = Foo
//
// In this example, there would be one Propagate: Propagate{Src: "file1/Foo",
// Dst: "file2/Foo2}.
type Propagate struct {
	// Src is the symbol whose type/value is being propagated to the dst symbol.
	SrcRepo     repo.URI
	SrcPath     SymbolPath
	SrcUnit     string
	SrcUnitType string

	// Dst is the symbol that is receiving a propagated type/value from the src symbol.
	DstRepo     repo.URI
	DstPath     SymbolPath
	DstUnit     string
	DstUnitType string
}

const (
	Const   SymbolKind = "const"
	Field              = "field"
	Func               = "func"
	Module             = "module"
	Package            = "package"
	Type               = "type"
	Var                = "var"
)

var AllSymbolKinds = []SymbolKind{Const, Field, Func, Module, Package, Type, Var}

// Returns true iff k is a known symbol kind.
func (k SymbolKind) Valid() bool {
	for _, kk := range AllSymbolKinds {
		if k == kk {
			return true
		}
	}
	return false
}

func KindName(k string) string {
	if strings.HasSuffix(k, "_module") {
		return "module"
	}
	return strings.Replace(k, "_", " ", -1)
}

type ConstData struct {
	ConstValue string
}

// SQL

func (x SID) Value() (driver.Value, error) {
	return int64(x), nil
}

func (x *SID) Scan(v interface{}) error {
	if data, ok := v.(int64); ok {
		*x = SID(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x SymbolPath) Value() (driver.Value, error) {
	return string(x), nil
}

func (x *SymbolPath) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = SymbolPath(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x SymbolKind) Value() (driver.Value, error) {
	return string(x), nil
}

func (x *SymbolKind) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = SymbolKind(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x *ConstData) Value() (driver.Value, error) {
	if x == nil {
		return nil, nil
	}
	return json.Marshal(x)
}

func (x *ConstData) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		return json.Unmarshal(data, x)
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

// Debugging

func (x Symbol) String() string {
	s, _ := json.Marshal(x)
	return string(s)
}

// Sorting

type Symbols []*Symbol

func (vs Symbols) Len() int           { return len(vs) }
func (vs Symbols) Swap(i, j int)      { vs[i], vs[j] = vs[j], vs[i] }
func (vs Symbols) Less(i, j int) bool { return vs[i].sortKey() < vs[j].sortKey() }

func (syms Symbols) Keys() (keys []SymbolKey) {
	keys = make([]SymbolKey, len(syms))
	for i, sym := range syms {
		keys[i] = sym.SymbolKey
	}
	return
}

func (syms Symbols) SIDs() (ids []SID) {
	ids = make([]SID, len(syms))
	for i, sym := range syms {
		ids[i] = sym.SID
	}
	return
}

func ParseSIDs(sidstrs []string) (sids []SID) {
	sids = make([]SID, len(sidstrs))
	for i, sidstr := range sidstrs {
		sid, err := strconv.Atoi(sidstr)
		if err == nil {
			sids[i] = SID(sid)
		}
	}
	return
}
<commit_msg>Test field omitempty (json tag)<commit_after>package graph

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"sourcegraph.com/sourcegraph/srcgraph/repo"
)

type (
	SID        int64
	SymbolPath string
	SymbolKind string
)

// SymbolKey specifies a symbol, either concretely or abstractly. A concrete
// symbol key has a non-empty CommitID and refers to a symbol defined in a
// specific commit. An abstract symbol key has an empty CommitID and is
// considered to refer to symbols from any number of commits (so long as the
// Repo, UnitType, Unit, and Path match).
//
// You can think of CommitID as the time dimension. With an empty CommitID, you
// are referring to a symbol that may or may not exist at various times. With a
// non-empty CommitID, you are referring to a specific definition of a symbol at
// the time specified by the CommitID.
type SymbolKey struct {
	// Repo is the VCS repository that defines this symbol. Its Elasticsearch mapping is defined
	// separately.
	Repo repo.URI `json:",omitempty"`

	// CommitID is the ID of the VCS commit that this symbol was defined in. The
	// CommitID is always a full commit ID (40 hexadecimal characters for git
	// and hg), never a branch or tag name.
	CommitID string `db:"commit_id" json:",omitempty"`

	// UnitType is the type name of the source unit (obtained from unit.Type(u))
	// that this symbol was defined in.
	UnitType string `db:"unit_type" json:",omitempty"`

	// Unit is the name of the source unit (obtained from u.Name()) that this
	// symbol was defined in.
	Unit string `json:",omitempty"`

	// Path is the path to this symbol, relative to the repo. Its Elasticsearch mapping is defined
	// separately (because it is a multi_field, which the struct tag can't currently represent).
	Path SymbolPath
}

func (s SymbolKey) String() string {
	b, err := json.Marshal(s)
	if err != nil {
		panic("SymbolKey.String: " + err.Error())
	}
	return string(b)
}

type Symbol struct {
	// SID is a unique, sequential ID for a symbol. It is regenerated each time
	// the symbol is emitted by the grapher and saved to the database. The SID
	// is used as an optimization (e.g., joins are faster on SID than on
	// SymbolKey).
	SID SID `db:"sid" json:",omitempty" elastic:"type:integer,index:no"`

	// SymbolKey is the natural unique key for a symbol. It is stable
	// (subsequent runs of a grapher will emit the same symbols with the same
	// SymbolKeys).
	SymbolKey

	// SpecificPath is the language-specific "path" to this symbol, using
	// language-specific separators (e.g., "::" and "." instead of "/", which is
	// used in the SymbolKey.Path value).
	SpecificPath string `db:"specific_path"`

	// Kind is the language-independent kind of this symbol.
	Kind SymbolKind `elastic:"type:string,index:analyzed"`

	// SpecificKind is the language-specific kind of this symbol (which is in
	// some cases equal to the Kind).
	SpecificKind string `db:"specific_kind"`

	Name string

	// Callable is true if this symbol may be called or invoked, such as in the
	// case of functions or methods.
	Callable bool `db:"callable"`

	File string `elastic:"type:string,index:no"`

	DefStart int `db:"def_start" elastic:"type:integer,index:no"`
	DefEnd   int `db:"def_end" elastic:"type:integer,index:no"`

	Exported bool `elastic:"type:boolean,index:not_analyzed"`

	// Test is whether this symbol is defined in test code (as opposed to main
	// code). For example, definitions in Go *_test.go files have Test = true.
	Test bool `elastic:"type:boolean,index:not_analyzed" json:",omitempty"`

	TypeExpr string `db:"type_expr" json:",omitempty"`
}

func (s *Symbol) Language() string {
	switch s.UnitType {
	case "GoPackage":
		return "Go"
		// TODO(sqs): add Python, JS, etc.
	}
	return "unknown language"
}

// TODO!(sqs): factor this into the individual source unit packages
func (s *Symbol) Signature() string {
	var removeOwnImportPath = func(str string) string {
		if s.UnitType == "GoPackage" {
			return strings.Replace(strings.Replace(str, string(s.Repo)+".", "", -1), string(s.Repo)+"/", "", -1)
		}
		return str
	}

	if s.TypeExpr == "" {
		return ""
	}
	if !s.Callable {
		if (s.Kind == Field || s.Kind == Var || s.Kind == Type) && len(s.TypeExpr) < 50 {
			return " " + removeOwnImportPath(s.TypeExpr)
		}
		return ""
	}

	switch s.UnitType {
	case "GoPackage":
		return removeOwnImportPath(strings.TrimPrefix(s.TypeExpr, "func"))
	case "js":
		return strings.TrimPrefix(s.TypeExpr, "fn")
	case "python":
		// remove up to first paren
		i := strings.Index(s.TypeExpr, "(")
		if i == -1 {
			return s.TypeExpr
		}
		return s.TypeExpr[:i]
	case "ruby":
		return s.TypeExpr
	}
	return s.TypeExpr
}

func (s *Symbol) sortKey() string { return s.SymbolKey.String() }

// Propagate describes type/value propagation in code. A Propagate entry from A
// (src) to B (dst) indicates that the type/value of A propagates to B. In Tern,
// this is indicated by A having a "fwd" property whose value is an array that
// includes B.
//
//
// ## Motivation & example
//
// For example, consider the following JavaScript code:
//
//   var a = Foo;
//   var b = a;
//
// Foo, a, and b are each their own symbol. We could resolve all of them to the
// symbol of their original type (perhaps Foo), but there are occasions when you
// do want to see only the definition of a or b and examples thereof. Therefore,
// we need to represent them as distinct symbols.
//
// Even though Foo, a, and b are distinct symbols, there are propagation
// relationships between them that are important to represent. The type of Foo
// propagates to both a and b, and the type of a propagates to b. In this case,
// we would have 3 Propagates: Propagate{Src: "Foo", Dst: "a"}, Propagate{Src:
// "Foo", Dst: "b"}, and Propagate{Src: "a", Dst: "b"}. (The propagation
// relationships could be described by just the first and last Propagates, but
// we explicitly include all paths as a denormalization optimization to avoid
// requiring an unbounded number of DB queries to determine which symbols a type
// propagates to or from.)
//
//
// ## Directionality
//
// Propagation is unidirectional, in the general case. In the example above, if
// Foo referred to a JavaScript object and if the code were evaluated, any
// *runtime* type changes (e.g., setting a property) on Foo, a, and b would be
// reflected on all of the others. But this doesn't hold for static analysis;
// it's not always true that if a property "a.x" or "b.x" exists, then "Foo.x"
// exists. The simplest example is when Foo is an external definition. Perhaps
// this example file (which uses Foo as a library) modifies Foo to add a new
// property, but other libraries that use Foo would never see that property
// because they wouldn't be executed in the same context as this example file.
// So, in general, we cannot say that Foo receives all types applied to symbols
// that Foo propagates to.
//
//
// ## Hypothetical Python example
//
// Consider the following 2 Python files:
//
//   """file1.py"""
//   class Foo(object): end
//
//   """file2.py"""
//   from .file1 import Foo
//   Foo2 = Foo
//
// In this example, there would be one Propagate: Propagate{Src: "file1/Foo",
// Dst: "file2/Foo2}.
type Propagate struct {
	// Src is the symbol whose type/value is being propagated to the dst symbol.
	SrcRepo     repo.URI
	SrcPath     SymbolPath
	SrcUnit     string
	SrcUnitType string

	// Dst is the symbol that is receiving a propagated type/value from the src symbol.
	DstRepo     repo.URI
	DstPath     SymbolPath
	DstUnit     string
	DstUnitType string
}

const (
	Const   SymbolKind = "const"
	Field              = "field"
	Func               = "func"
	Module             = "module"
	Package            = "package"
	Type               = "type"
	Var                = "var"
)

var AllSymbolKinds = []SymbolKind{Const, Field, Func, Module, Package, Type, Var}

// Returns true iff k is a known symbol kind.
func (k SymbolKind) Valid() bool {
	for _, kk := range AllSymbolKinds {
		if k == kk {
			return true
		}
	}
	return false
}

func KindName(k string) string {
	if strings.HasSuffix(k, "_module") {
		return "module"
	}
	return strings.Replace(k, "_", " ", -1)
}

type ConstData struct {
	ConstValue string
}

// SQL

func (x SID) Value() (driver.Value, error) {
	return int64(x), nil
}

func (x *SID) Scan(v interface{}) error {
	if data, ok := v.(int64); ok {
		*x = SID(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x SymbolPath) Value() (driver.Value, error) {
	return string(x), nil
}

func (x *SymbolPath) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = SymbolPath(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x SymbolKind) Value() (driver.Value, error) {
	return string(x), nil
}

func (x *SymbolKind) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = SymbolKind(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x *ConstData) Value() (driver.Value, error) {
	if x == nil {
		return nil, nil
	}
	return json.Marshal(x)
}

func (x *ConstData) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		return json.Unmarshal(data, x)
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

// Debugging

func (x Symbol) String() string {
	s, _ := json.Marshal(x)
	return string(s)
}

// Sorting

type Symbols []*Symbol

func (vs Symbols) Len() int           { return len(vs) }
func (vs Symbols) Swap(i, j int)      { vs[i], vs[j] = vs[j], vs[i] }
func (vs Symbols) Less(i, j int) bool { return vs[i].sortKey() < vs[j].sortKey() }

func (syms Symbols) Keys() (keys []SymbolKey) {
	keys = make([]SymbolKey, len(syms))
	for i, sym := range syms {
		keys[i] = sym.SymbolKey
	}
	return
}

func (syms Symbols) SIDs() (ids []SID) {
	ids = make([]SID, len(syms))
	for i, sym := range syms {
		ids[i] = sym.SID
	}
	return
}

func ParseSIDs(sidstrs []string) (sids []SID) {
	sids = make([]SID, len(sidstrs))
	for i, sidstr := range sidstrs {
		sid, err := strconv.Atoi(sidstr)
		if err == nil {
			sids[i] = SID(sid)
		}
	}
	return
}
<|endoftext|>


<commit_before>package ark

import (
  "github.com/james4k/rcon"
  "errors"
  "strings"
  "fmt"
  "log"
  "regexp"
)

type ARKRcon struct {
  rc *rcon.RemoteConsole
  address string
}

type ARKPlayer struct {
  Username string
  Steam64 string
}

type ARKChatMsg struct {
  Username string
  Playername string
  Message string
}

var (
  EmptyResponse = errors.New("No Server Response")
  FailResponse = errors.New("Server failed at request")
)

/*
  All command information based on:
  http://steamcommunity.com/sharedfiles/filedetails/?id=454529617
*/

func (a *ARKRcon) ListPlayers() ([]ARKPlayer, error) {
  /* CMD: listplayers
     Success:
    - No Players Connected
    - 0. CyFreeze, 76561198025588951
      ...
  */
  resp, err := a.Query("listplayers")
  if err != nil {
    return nil, err
  }
  rex := regexp.MustCompile(`\d+\. ([^,]+), (\d+)`)
  list := make([]ARKPlayer, 0)
  all := rex.FindAllStringSubmatch(resp, -1)
  for _, m := range all {
    list = append(list, ARKPlayer {m[1], m[2]})
  }
  return list, nil
}

func (a *ARKRcon) SaveWorld() error {
  /* CMD: saveworld
     Success: World Saved
   */
  resp, err := a.Query("saveworld")
  if err != nil {
    return err
  }
  if !strings.Contains(resp, "World Saved") {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) DoExit() error {
  /* CMD: doexit
     Success: Exiting...
  */
  resp, err := a.Query("doexit")
  if err != nil {
    return err
  }
  if !strings.Contains(resp, "Exiting") {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) SendChatToPlayer(player, message string) error {
  /* CMD: serverchattoplayer "player" "msg"
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`serverchattoplayer "%s" "%s"`, player, message))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

func (a *ARKRcon) SendChatToID(steam64, message string) error {
  /* CMD: serverchatto "steam64" "msg"
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`serverchatto "%s" "%s"`, steam64, message))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

func (a *ARKRcon) GetChat() ([]ARKChatMsg, error) {
  /* CMD: getchat
    Success: - SERVER: foo
               CyFreeze (Bob The Builder): foobar
               Valki(Valki): wup wup
  */
  resp, err := a.Query("getchat")
  if err != nil {
    return nil, err
  }
  rex := regexp.MustCompile(`(\w+)\s*(?:\(([\w\s]+)\))?:\s*(.*?)$`)
  list := make([]ARKChatMsg, 0)
  all := rex.FindAllStringSubmatch(resp, -1)
  for _, m := range all {
    list = append(list, ARKChatMsg{m[1], m[2], m[3]})
  }
  return list, nil
}

func (a *ARKRcon) SetTimeOfDay(time string) error {
  /* CMD: settimeofday
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`settimeofday %s`, time))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

func (a *ARKRcon) WhitelistPlayer (steam64 string) error {
  /* CMD: allowplayertojoinnocheck steam64
     Success: <steam64> Allow Player To Join No Check
  */
  resp, err := a.Query(fmt.Sprintf(`allowplayertojoinnocheck %s`, steam64))
  if err != nil {
    return err
  }

  if !strings.Contains(resp, fmt.Sprintf(`%s Allow`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) RemoveWhitelist (steam64 string) error {
  /* CMD: disallowplayertojoinnocheck steam64
     Success: <steam64> Disallowed Player To Join No Checknned
  */
  resp, err := a.Query(fmt.Sprintf(`disallowplayertojoinnocheck %s`, steam64))
  if err != nil {
    return err
  }

  if !strings.Contains(resp, fmt.Sprintf(`%s Disallowed`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) SetMessageOfTheDay (motd string) error {
  /* CMD: setmessageoftheday motd
     Success: Message of set to <motd>
  */

  resp, err := a.Query(fmt.Sprintf(`setmessageoftheday %s`, motd))
  if err != nil {
    return err
  }

  if !strings.Contains(resp, "Message of set to") {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) Broadcast(message string) error {
  /* CMD: broadcast
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`broadcast %s`, message))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

func (a *ARKRcon) KickPlayer(steam64 string) error {
  /* CMD: kickplayer steam64
     Success: <steam64> Kicked
  */
  resp, err := a.Query(fmt.Sprintf(`kickplayer %s`, steam64))
  if err != nil {
    return err
  }
  if !strings.Contains(resp, fmt.Sprintf(`%s Kicked`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) BanPlayer(steam64 string) error {
  /* CMD: banplayer steam64
     Success: <steam64> Banned
  */
  resp, err := a.Query(fmt.Sprintf(`banplayer %s`, steam64))
  if err != nil {
    return err
  }
  if !strings.Contains(resp, fmt.Sprintf(`%s Banned`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) UnbanPlayer(steam64 string) error {
  /* CMD: unbanplayer steam64
     Success: <steam64> Unbanned
  */
  resp, err := a.Query(fmt.Sprintf(`unbanplayer %s`, steam64))
  if err != nil {
    return err
  }
  if !strings.Contains(resp, fmt.Sprintf(`%s Unbanned`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) Slomo(multiplier int) error {
  /* CMD: slomo multiplier
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`slomo %d`, multiplier))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

/*
  No idea how to get ark player id yet
  Just keep them in mind for now
*/
func (a *ARKRcon) giveItemToPlayer(playerID, itemID, quantity, quality int, blueprint bool) {
  //giveitemnumtoplayer
}

func (a *ARKRcon) clearPlayerInventory(playerID int, clrInv, clrSlot, clrEquip bool) {
  //clearplayerinventory
}

func (a *ARKRcon) killPlayer(playerID int) {
  //killplayer
}

func (a *ARKRcon) giveExpToPlayer(playerID, exp int, fromtribe, preventshare bool) {
  //giveexptoplayer
}

func (a *ARKRcon) forcePlayerToJoinTribe(playerID, tribeID int) {
  //forceplayertojointribe
}

func (a *ARKRcon) Query(cmd string) (string, error) {
  reqID, reqErr := a.rc.Write(cmd)
  if reqErr != nil {
    log.Println(reqID, reqErr)
    return "", reqErr
  }

  resp, respID, respErr := a.rc.Read()
  if respErr != nil {
    log.Println(resp, respID, respErr)
    return "", respErr
  }

  if strings.Contains(resp, "no response!!") {
    return "", EmptyResponse
  }
  return resp, nil
}

func NewARKRconConnection(address, password string) (*ARKRcon, error) {
  rc, err := rcon.Dial (address, password)
  if err != nil {
    return nil, err
  }
  return &ARKRcon{rc, address}, nil
}
<commit_msg>switch for servermessage<commit_after>package ark

import (
  "github.com/james4k/rcon"
  "errors"
  "strings"
  "fmt"
  "log"
  "regexp"
)

type ARKRcon struct {
  rc *rcon.RemoteConsole
  address string
}

type ARKPlayer struct {
  Username string
  Steam64 string
}

type ARKChatMsg struct {
  Username string
  Playername string
  Message string
  ServerMessage bool
}

var (
  EmptyResponse = errors.New("No Server Response")
  FailResponse = errors.New("Server failed at request")
)

/*
  All command information based on:
  http://steamcommunity.com/sharedfiles/filedetails/?id=454529617
*/

func (a *ARKRcon) ListPlayers() ([]ARKPlayer, error) {
  /* CMD: listplayers
     Success:
    - No Players Connected
    - 0. CyFreeze, 76561198025588951
      ...
  */
  resp, err := a.Query("listplayers")
  if err != nil {
    return nil, err
  }
  rex := regexp.MustCompile(`\d+\. ([^,]+), (\d+)`)
  list := make([]ARKPlayer, 0)
  all := rex.FindAllStringSubmatch(resp, -1)
  for _, m := range all {
    list = append(list, ARKPlayer {m[1], m[2]})
  }
  return list, nil
}

func (a *ARKRcon) SaveWorld() error {
  /* CMD: saveworld
     Success: World Saved
   */
  resp, err := a.Query("saveworld")
  if err != nil {
    return err
  }
  if !strings.Contains(resp, "World Saved") {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) DoExit() error {
  /* CMD: doexit
     Success: Exiting...
  */
  resp, err := a.Query("doexit")
  if err != nil {
    return err
  }
  if !strings.Contains(resp, "Exiting") {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) SendChatToPlayer(player, message string) error {
  /* CMD: serverchattoplayer "player" "msg"
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`serverchattoplayer "%s" "%s"`, player, message))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

func (a *ARKRcon) SendChatToID(steam64, message string) error {
  /* CMD: serverchatto "steam64" "msg"
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`serverchatto "%s" "%s"`, steam64, message))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

func (a *ARKRcon) GetChat() ([]ARKChatMsg, error) {
  /* CMD: getchat
    Success: - SERVER: foo
               CyFreeze (Bob The Builder): foobar
               Valki(Valki): wup wup
  */
  resp, err := a.Query("getchat")
  if err != nil {
    return nil, err
  }
  rex := regexp.MustCompile(`(\w+)\s*(?:\(([\w\s]+)\))?:\s*(.*?)$`)
  list := make([]ARKChatMsg, 0)
  all := rex.FindAllStringSubmatch(resp, -1)
  for _, m := range all {
    list = append(list, ARKChatMsg{m[1], m[2], m[3], strings.HasPrefix(m[1], "SERVER")})
  }
  return list, nil
}

func (a *ARKRcon) SetTimeOfDay(time string) error {
  /* CMD: settimeofday
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`settimeofday %s`, time))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

func (a *ARKRcon) WhitelistPlayer (steam64 string) error {
  /* CMD: allowplayertojoinnocheck steam64
     Success: <steam64> Allow Player To Join No Check
  */
  resp, err := a.Query(fmt.Sprintf(`allowplayertojoinnocheck %s`, steam64))
  if err != nil {
    return err
  }

  if !strings.Contains(resp, fmt.Sprintf(`%s Allow`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) RemoveWhitelist (steam64 string) error {
  /* CMD: disallowplayertojoinnocheck steam64
     Success: <steam64> Disallowed Player To Join No Checknned
  */
  resp, err := a.Query(fmt.Sprintf(`disallowplayertojoinnocheck %s`, steam64))
  if err != nil {
    return err
  }

  if !strings.Contains(resp, fmt.Sprintf(`%s Disallowed`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) SetMessageOfTheDay (motd string) error {
  /* CMD: setmessageoftheday motd
     Success: Message of set to <motd>
  */

  resp, err := a.Query(fmt.Sprintf(`setmessageoftheday %s`, motd))
  if err != nil {
    return err
  }

  if !strings.Contains(resp, "Message of set to") {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) Broadcast(message string) error {
  /* CMD: broadcast
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`broadcast %s`, message))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

func (a *ARKRcon) KickPlayer(steam64 string) error {
  /* CMD: kickplayer steam64
     Success: <steam64> Kicked
  */
  resp, err := a.Query(fmt.Sprintf(`kickplayer %s`, steam64))
  if err != nil {
    return err
  }
  if !strings.Contains(resp, fmt.Sprintf(`%s Kicked`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) BanPlayer(steam64 string) error {
  /* CMD: banplayer steam64
     Success: <steam64> Banned
  */
  resp, err := a.Query(fmt.Sprintf(`banplayer %s`, steam64))
  if err != nil {
    return err
  }
  if !strings.Contains(resp, fmt.Sprintf(`%s Banned`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) UnbanPlayer(steam64 string) error {
  /* CMD: unbanplayer steam64
     Success: <steam64> Unbanned
  */
  resp, err := a.Query(fmt.Sprintf(`unbanplayer %s`, steam64))
  if err != nil {
    return err
  }
  if !strings.Contains(resp, fmt.Sprintf(`%s Unbanned`, steam64)) {
    return FailResponse
  }
  return nil
}

func (a *ARKRcon) Slomo(multiplier int) error {
  /* CMD: slomo multiplier
     Success: /
  */
  _, err := a.Query(fmt.Sprintf(`slomo %d`, multiplier))
  if err == EmptyResponse {
    return nil
  } else {
    return err
  }
}

/*
  No idea how to get ark player id yet
  Just keep them in mind for now
*/
func (a *ARKRcon) giveItemToPlayer(playerID, itemID, quantity, quality int, blueprint bool) {
  //giveitemnumtoplayer
}

func (a *ARKRcon) clearPlayerInventory(playerID int, clrInv, clrSlot, clrEquip bool) {
  //clearplayerinventory
}

func (a *ARKRcon) killPlayer(playerID int) {
  //killplayer
}

func (a *ARKRcon) giveExpToPlayer(playerID, exp int, fromtribe, preventshare bool) {
  //giveexptoplayer
}

func (a *ARKRcon) forcePlayerToJoinTribe(playerID, tribeID int) {
  //forceplayertojointribe
}

func (a *ARKRcon) Query(cmd string) (string, error) {
  reqID, reqErr := a.rc.Write(cmd)
  if reqErr != nil {
    log.Println(reqID, reqErr)
    return "", reqErr
  }

  resp, respID, respErr := a.rc.Read()
  if respErr != nil {
    log.Println(resp, respID, respErr)
    return "", respErr
  }

  if strings.Contains(resp, "no response!!") {
    return "", EmptyResponse
  }
  return resp, nil
}

func NewARKRconConnection(address, password string) (*ARKRcon, error) {
  rc, err := rcon.Dial (address, password)
  if err != nil {
    return nil, err
  }
  return &ARKRcon{rc, address}, nil
}
<|endoftext|>


<commit_before>package serialapi

import (
	"encoding/binary"
	"errors"
	"fmt"
	"time"

	"github.com/bjyoungblood/gozw/zwave/frame"
	"github.com/bjyoungblood/gozw/zwave/protocol"
	"github.com/bjyoungblood/gozw/zwave/session"
)

type TransmitStatus struct {
	Status byte
	TxTime uint16
}

func (s *SerialAPILayer) SendData(nodeId byte, payload []byte) (txTime uint16, err error) {

	transmitDone := make(chan bool)
	retStatus := make(chan error)
	txStatus := make(chan TransmitStatus)

	payload = append([]byte{nodeId, byte(len(payload))}, payload...)
	payload = append(payload, protocol.TransmitOptionAck)

	request := &session.Request{
		FunctionId:       protocol.FnSendData,
		Payload:          payload,
		HasReturn:        true,
		ReceivesCallback: true,
		Lock:             true,
		Release:          transmitDone,
		Timeout:          10 * time.Second,

		ReturnCallback: func(err error, ret *frame.Frame) bool {
			if ret.Payload[1] == 0 {
				transmitDone <- true
				retStatus <- errors.New("SendData: transmit buffer overflow")
			} else {
				retStatus <- nil
			}

			return true
		},

		Callback: func(cbFrame frame.Frame) {
			status := TransmitStatus{}
			status.Status = cbFrame.Payload[2]
			if len(cbFrame.Payload) == 5 {
				status.TxTime = binary.BigEndian.Uint16(cbFrame.Payload[3:5])
			}

			transmitDone <- true
			txStatus <- status
		},
	}

	s.sessionLayer.MakeRequest(request)

	err = <-retStatus
	if err != nil {
		return
	}

	status := <-txStatus
	switch status.Status {
	case protocol.TransmitCompleteOk:
		return status.TxTime, nil
	case protocol.TransmitCompleteNoAck:
		return status.TxTime, errors.New("Transmit complete: no ack from destination")
	case protocol.TransmitCompleteFail:
		return status.TxTime, errors.New("Transmit failure: network busy/jammed")
	case protocol.TransmitRoutingNotIdle:
		return status.TxTime, errors.New("Transmit failure: routing not idle")
	case protocol.TransmitCompleteNoRoute:
		return status.TxTime, errors.New("Transmit complete: no route")
	default:
		return status.TxTime, fmt.Errorf("Unknown tranmission status: %d", status.Status)
	}
}
<commit_msg>Fix send data error handling<commit_after>package serialapi

import (
	"encoding/binary"
	"errors"
	"fmt"
	"time"

	"github.com/bjyoungblood/gozw/zwave/frame"
	"github.com/bjyoungblood/gozw/zwave/protocol"
	"github.com/bjyoungblood/gozw/zwave/session"
)

type TransmitStatus struct {
	Status byte
	TxTime uint16
}

func (s *SerialAPILayer) SendData(nodeId byte, payload []byte) (txTime uint16, err error) {

	transmitDone := make(chan bool)
	retStatus := make(chan error)
	txStatus := make(chan TransmitStatus)

	payload = append([]byte{nodeId, byte(len(payload))}, payload...)
	payload = append(payload, protocol.TransmitOptionAck)

	request := &session.Request{
		FunctionId:       protocol.FnSendData,
		Payload:          payload,
		HasReturn:        true,
		ReceivesCallback: true,
		Lock:             true,
		Release:          transmitDone,
		Timeout:          10 * time.Second,

		ReturnCallback: func(err error, ret *frame.Frame) bool {
			if err != nil {
				transmitDone <- true
				retStatus <- err
				return false
			}

			if ret.Payload[1] == 0 {
				transmitDone <- true
				retStatus <- errors.New("SendData: transmit buffer overflow")
			} else {
				retStatus <- nil
			}

			return true
		},

		Callback: func(cbFrame frame.Frame) {
			status := TransmitStatus{}
			status.Status = cbFrame.Payload[2]
			if len(cbFrame.Payload) == 5 {
				status.TxTime = binary.BigEndian.Uint16(cbFrame.Payload[3:5])
			}

			transmitDone <- true
			txStatus <- status
		},
	}

	s.sessionLayer.MakeRequest(request)

	err = <-retStatus
	if err != nil {
		return 0, err
	}

	status := <-txStatus
	switch status.Status {
	case protocol.TransmitCompleteOk:
		return status.TxTime, nil
	case protocol.TransmitCompleteNoAck:
		return status.TxTime, errors.New("Transmit complete: no ack from destination")
	case protocol.TransmitCompleteFail:
		return status.TxTime, errors.New("Transmit failure: network busy/jammed")
	case protocol.TransmitRoutingNotIdle:
		return status.TxTime, errors.New("Transmit failure: routing not idle")
	case protocol.TransmitCompleteNoRoute:
		return status.TxTime, errors.New("Transmit complete: no route")
	default:
		return status.TxTime, fmt.Errorf("Unknown tranmission status: %d", status.Status)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author: polaris	polaris@studygolang.com

package http

import (
	"bytes"
	"global"
	"html/template"
	"logic"
	"model"
	"net/http"
	"strings"
	"time"
	"util"

	"github.com/gorilla/sessions"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/standard"
	"github.com/polaris1119/config"
	"github.com/polaris1119/goutils"
)

var Store = sessions.NewCookieStore([]byte(config.ConfigFile.MustValue("global", "cookie_secret")))

func SetCookie(ctx echo.Context, username string) {
	Store.Options.HttpOnly = true

	session := GetCookieSession(ctx)
	if ctx.FormValue("remember_me") != "1" {
		// 浏览器关闭，cookie删除，否则保存30天(github.com/gorilla/sessions 包的默认值)
		session.Options = &sessions.Options{
			Path:     "/",
			HttpOnly: true,
		}
	}
	session.Values["username"] = username
	req := Request(ctx)
	resp := ResponseWriter(ctx)
	session.Save(req, resp)
}

// 必须是 http.Request
func GetCookieSession(ctx echo.Context) *sessions.Session {
	session, _ := Store.Get(Request(ctx), "user")
	return session
}

func Request(ctx echo.Context) *http.Request {
	return ctx.Request().(*standard.Request).Request
}

func ResponseWriter(ctx echo.Context) http.ResponseWriter {
	return ctx.Response().(*standard.Response).ResponseWriter
}

// 自定义模板函数
var funcMap = template.FuncMap{
	// 获取gravatar头像
	"gravatar": util.Gravatar,
	// 转为前端显示需要的时间格式
	"formatTime": func(i interface{}) string {
		ctime, ok := i.(string)
		if !ok {
			return ""
		}
		t, _ := time.Parse("2006-01-02 15:04:05", ctime)
		return t.Format(time.RFC3339) + "+08:00"
	},
	"substring": util.Substring,
	"add": func(nums ...interface{}) int {
		total := 0
		for _, num := range nums {
			if n, ok := num.(int); ok {
				total += n
			}
		}
		return total
	},
	"explode": func(s, sep string) []string {
		return strings.Split(s, sep)
	},
	"noescape": func(s string) template.HTML {
		return template.HTML(s)
	},
	"timestamp": func(ts ...time.Time) int64 {
		if len(ts) > 0 {
			return ts[0].Unix()
		}
		return time.Now().Unix()
	},
	"canEdit": logic.CanEdit,
}

const (
	LayoutTpl      = "common/layout.html"
	AdminLayoutTpl = "common.html"
)

// Render html 输出
func Render(ctx echo.Context, contentTpl string, data map[string]interface{}) error {
	if data == nil {
		data = map[string]interface{}{}
	}

	objLog := logic.GetLogger(ctx)

	contentTpl = LayoutTpl + "," + contentTpl
	// 为了使用自定义的模板函数，首先New一个以第一个模板文件名为模板名。
	// 这样，在ParseFiles时，新返回的*Template便还是原来的模板实例
	htmlFiles := strings.Split(contentTpl, ",")
	for i, contentTpl := range htmlFiles {
		htmlFiles[i] = config.TemplateDir + contentTpl
	}
	tpl, err := template.New("layout.html").Funcs(funcMap).ParseFiles(htmlFiles...)
	if err != nil {
		objLog.Errorf("解析模板出错（ParseFiles）：[%q] %s\n", Request(ctx).RequestURI, err)
		return err
	}

	data["pos_ad"] = logic.DefaultAd.FindAll(ctx, ctx.Path())

	return executeTpl(ctx, tpl, data)
}

// RenderAdmin html 输出
func RenderAdmin(ctx echo.Context, contentTpl string, data map[string]interface{}) error {
	if data == nil {
		data = map[string]interface{}{}
	}

	objLog := logic.GetLogger(ctx)

	contentTpl = AdminLayoutTpl + "," + contentTpl
	// 为了使用自定义的模板函数，首先New一个以第一个模板文件名为模板名。
	// 这样，在ParseFiles时，新返回的*Template便还是原来的模板实例
	htmlFiles := strings.Split(contentTpl, ",")
	for i, contentTpl := range htmlFiles {
		htmlFiles[i] = config.TemplateDir + "admin/" + contentTpl
	}

	requestURI := Request(ctx).RequestURI
	tpl, err := template.New("common.html").Funcs(funcMap).ParseFiles(htmlFiles...)
	if err != nil {
		objLog.Errorf("解析模板出错（ParseFiles）：[%q] %s\n", requestURI, err)
		return err
	}

	// 当前用户信息
	curUser := ctx.Get("user").(*model.Me)

	if menu1, menu2, curMenu1 := logic.DefaultAuthority.GetUserMenu(ctx, curUser, requestURI); menu2 != nil {
		data["menu1"] = menu1
		data["menu2"] = menu2
		data["uri"] = requestURI
		data["cur_menu1"] = curMenu1
	}

	return executeTpl(ctx, tpl, data)
}

// 后台 query 查询返回结果
func RenderQuery(ctx echo.Context, contentTpl string, data map[string]interface{}) error {
	objLog := logic.GetLogger(ctx)

	contentTpl = "common_query.html," + contentTpl
	contentTpls := strings.Split(contentTpl, ",")
	for i, contentTpl := range contentTpls {
		contentTpls[i] = config.TemplateDir + "admin/" + strings.TrimSpace(contentTpl)
	}

	requestURI := Request(ctx).RequestURI
	tpl, err := template.New("common_query.html").Funcs(funcMap).ParseFiles(contentTpls...)
	if err != nil {
		objLog.Errorf("解析模板出错（ParseFiles）：[%q] %s\n", requestURI, err)
		return err
	}

	buf := new(bytes.Buffer)
	err = tpl.Execute(buf, data)
	if err != nil {
		objLog.Errorf("执行模板出错（Execute）：[%q] %s\n", requestURI, err)
		return err
	}

	return ctx.HTML(http.StatusOK, buf.String())
}

func executeTpl(ctx echo.Context, tpl *template.Template, data map[string]interface{}) error {
	objLog := logic.GetLogger(ctx)

	// 如果没有定义css和js模板，则定义之
	if jsTpl := tpl.Lookup("js"); jsTpl == nil {
		tpl.Parse(`{{define "js"}}{{end}}`)
	}
	if cssTpl := tpl.Lookup("css"); cssTpl == nil {
		tpl.Parse(`{{define "css"}}{{end}}`)
	}
	// 如果没有 seo 模板，则定义之
	if seoTpl := tpl.Lookup("seo"); seoTpl == nil {
		tpl.Parse(`{{define "seo"}}
			<meta name="keywords" content="` + logic.WebsiteSetting.SeoKeywords + `">
			<meta name="description" content="` + logic.WebsiteSetting.SeoKeywords + `">
		{{end}}`)
	}

	// 当前用户信息
	curUser, ok := ctx.Get("user").(*model.Me)
	if ok {
		data["me"] = curUser
	} else {
		data["me"] = nil
	}

	// websocket主机
	if global.OnlineEnv() {
		data["wshost"] = logic.WebsiteSetting.Domain
	} else {
		data["wshost"] = global.App.Host + ":" + global.App.Port
	}
	global.App.SetUptime()
	global.App.SetCopyright()

	isHttps := goutils.MustBool(ctx.Request().Header().Get("X-Https"))
	cdnDomain := global.App.CDNHttp
	if isHttps {
		cdnDomain = global.App.CDNHttps
	}
	data["app"] = global.App
	data["is_https"] = isHttps
	data["cdn_domain"] = cdnDomain

	data["online_users"] = map[string]int{"online": logic.Book.Len(), "maxonline": logic.MaxOnlineNum()}

	data["setting"] = logic.WebsiteSetting

	buf := new(bytes.Buffer)
	err := tpl.Execute(buf, data)
	if err != nil {
		objLog.Errorln("excute template error:", err)
		return err
	}

	return ctx.HTML(http.StatusOK, buf.String())
}

///////////////////////////////// APP 相关 //////////////////////////////

const (
	TokenSalt       = "b3%JFOykZx_golang_polaris"
	NeedReLoginCode = 600
)

func ParseToken(token string) (int, bool) {
	if len(token) < 32 {
		return 0, false
	}

	pos := strings.LastIndex(token, "uid")
	if pos == -1 {
		return 0, false
	}
	return goutils.MustInt(token[pos+3:]), true
}

func ValidateToken(token string) bool {
	_, ok := ParseToken(token)
	if !ok {
		return false
	}

	expireTime := time.Unix(goutils.MustInt64(token[:10]), 0)
	if time.Now().Before(expireTime) {
		return true
	}
	return false
}

func GenToken(uid int) string {
	expireTime := time.Now().Add(30 * 24 * time.Hour).Unix()

	buffer := goutils.NewBuffer().Append(expireTime).Append(uid).Append(TokenSalt)

	md5 := goutils.Md5(buffer.String())

	buffer = goutils.NewBuffer().Append(expireTime).Append(md5).Append("uid").Append(uid)
	return buffer.String()
}

func AccessControl(ctx echo.Context) {
	ctx.Response().Header().Add("Access-Control-Allow-Origin", "*")
}
<commit_msg>bugfix<commit_after>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author: polaris	polaris@studygolang.com

package http

import (
	"bytes"
	"global"
	"html/template"
	"logic"
	"model"
	"net/http"
	"strings"
	"time"
	"util"

	"github.com/gorilla/sessions"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/standard"
	"github.com/polaris1119/config"
	"github.com/polaris1119/goutils"
)

var Store = sessions.NewCookieStore([]byte(config.ConfigFile.MustValue("global", "cookie_secret")))

func SetCookie(ctx echo.Context, username string) {
	Store.Options.HttpOnly = true

	session := GetCookieSession(ctx)
	if ctx.FormValue("remember_me") != "1" {
		// 浏览器关闭，cookie删除，否则保存30天(github.com/gorilla/sessions 包的默认值)
		session.Options = &sessions.Options{
			Path:     "/",
			HttpOnly: true,
		}
	}
	session.Values["username"] = username
	req := Request(ctx)
	resp := ResponseWriter(ctx)
	session.Save(req, resp)
}

// 必须是 http.Request
func GetCookieSession(ctx echo.Context) *sessions.Session {
	session, _ := Store.Get(Request(ctx), "user")
	return session
}

func Request(ctx echo.Context) *http.Request {
	return ctx.Request().(*standard.Request).Request
}

func ResponseWriter(ctx echo.Context) http.ResponseWriter {
	return ctx.Response().(*standard.Response).ResponseWriter
}

// 自定义模板函数
var funcMap = template.FuncMap{
	// 获取gravatar头像
	"gravatar": util.Gravatar,
	// 转为前端显示需要的时间格式
	"formatTime": func(i interface{}) string {
		ctime, ok := i.(string)
		if !ok {
			return ""
		}
		t, _ := time.Parse("2006-01-02 15:04:05", ctime)
		return t.Format(time.RFC3339) + "+08:00"
	},
	"substring": util.Substring,
	"add": func(nums ...interface{}) int {
		total := 0
		for _, num := range nums {
			if n, ok := num.(int); ok {
				total += n
			}
		}
		return total
	},
	"explode": func(s, sep string) []string {
		return strings.Split(s, sep)
	},
	"noescape": func(s string) template.HTML {
		return template.HTML(s)
	},
	"timestamp": func(ts ...time.Time) int64 {
		if len(ts) > 0 {
			return ts[0].Unix()
		}
		return time.Now().Unix()
	},
	"canEdit": logic.CanEdit,
}

const (
	LayoutTpl      = "common/layout.html"
	AdminLayoutTpl = "common.html"
)

// Render html 输出
func Render(ctx echo.Context, contentTpl string, data map[string]interface{}) error {
	if data == nil {
		data = map[string]interface{}{}
	}

	objLog := logic.GetLogger(ctx)

	contentTpl = LayoutTpl + "," + contentTpl
	// 为了使用自定义的模板函数，首先New一个以第一个模板文件名为模板名。
	// 这样，在ParseFiles时，新返回的*Template便还是原来的模板实例
	htmlFiles := strings.Split(contentTpl, ",")
	for i, contentTpl := range htmlFiles {
		htmlFiles[i] = config.TemplateDir + contentTpl
	}
	tpl, err := template.New("layout.html").Funcs(funcMap).ParseFiles(htmlFiles...)
	if err != nil {
		objLog.Errorf("解析模板出错（ParseFiles）：[%q] %s\n", Request(ctx).RequestURI, err)
		return err
	}

	data["pos_ad"] = logic.DefaultAd.FindAll(ctx, ctx.Path())

	return executeTpl(ctx, tpl, data)
}

// RenderAdmin html 输出
func RenderAdmin(ctx echo.Context, contentTpl string, data map[string]interface{}) error {
	if data == nil {
		data = map[string]interface{}{}
	}

	objLog := logic.GetLogger(ctx)

	contentTpl = AdminLayoutTpl + "," + contentTpl
	// 为了使用自定义的模板函数，首先New一个以第一个模板文件名为模板名。
	// 这样，在ParseFiles时，新返回的*Template便还是原来的模板实例
	htmlFiles := strings.Split(contentTpl, ",")
	for i, contentTpl := range htmlFiles {
		htmlFiles[i] = config.TemplateDir + "admin/" + contentTpl
	}

	requestURI := Request(ctx).RequestURI
	tpl, err := template.New("common.html").Funcs(funcMap).ParseFiles(htmlFiles...)
	if err != nil {
		objLog.Errorf("解析模板出错（ParseFiles）：[%q] %s\n", requestURI, err)
		return err
	}

	// 当前用户信息
	curUser := ctx.Get("user").(*model.Me)

	if menu1, menu2, curMenu1 := logic.DefaultAuthority.GetUserMenu(ctx, curUser, requestURI); menu2 != nil {
		data["menu1"] = menu1
		data["menu2"] = menu2
		data["uri"] = requestURI
		data["cur_menu1"] = curMenu1
	}

	return executeTpl(ctx, tpl, data)
}

// 后台 query 查询返回结果
func RenderQuery(ctx echo.Context, contentTpl string, data map[string]interface{}) error {
	objLog := logic.GetLogger(ctx)

	contentTpl = "common_query.html," + contentTpl
	contentTpls := strings.Split(contentTpl, ",")
	for i, contentTpl := range contentTpls {
		contentTpls[i] = config.TemplateDir + "admin/" + strings.TrimSpace(contentTpl)
	}

	requestURI := Request(ctx).RequestURI
	tpl, err := template.New("common_query.html").Funcs(funcMap).ParseFiles(contentTpls...)
	if err != nil {
		objLog.Errorf("解析模板出错（ParseFiles）：[%q] %s\n", requestURI, err)
		return err
	}

	buf := new(bytes.Buffer)
	err = tpl.Execute(buf, data)
	if err != nil {
		objLog.Errorf("执行模板出错（Execute）：[%q] %s\n", requestURI, err)
		return err
	}

	return ctx.HTML(http.StatusOK, buf.String())
}

func executeTpl(ctx echo.Context, tpl *template.Template, data map[string]interface{}) error {
	objLog := logic.GetLogger(ctx)

	// 如果没有定义css和js模板，则定义之
	if jsTpl := tpl.Lookup("js"); jsTpl == nil {
		tpl.Parse(`{{define "js"}}{{end}}`)
	}
	if cssTpl := tpl.Lookup("css"); cssTpl == nil {
		tpl.Parse(`{{define "css"}}{{end}}`)
	}
	// 如果没有 seo 模板，则定义之
	if seoTpl := tpl.Lookup("seo"); seoTpl == nil {
		tpl.Parse(`{{define "seo"}}
			<meta name="keywords" content="` + logic.WebsiteSetting.SeoKeywords + `">
			<meta name="description" content="` + logic.WebsiteSetting.SeoKeywords + `">
		{{end}}`)
	}

	// 当前用户信息
	curUser, ok := ctx.Get("user").(*model.Me)
	if ok {
		data["me"] = curUser
	} else {
		data["me"] = &model.Me{}
	}

	// websocket主机
	if global.OnlineEnv() {
		data["wshost"] = logic.WebsiteSetting.Domain
	} else {
		data["wshost"] = global.App.Host + ":" + global.App.Port
	}
	global.App.SetUptime()
	global.App.SetCopyright()

	isHttps := goutils.MustBool(ctx.Request().Header().Get("X-Https"))
	cdnDomain := global.App.CDNHttp
	if isHttps {
		cdnDomain = global.App.CDNHttps
	}
	data["app"] = global.App
	data["is_https"] = isHttps
	data["cdn_domain"] = cdnDomain

	data["online_users"] = map[string]int{"online": logic.Book.Len(), "maxonline": logic.MaxOnlineNum()}

	data["setting"] = logic.WebsiteSetting

	buf := new(bytes.Buffer)
	err := tpl.Execute(buf, data)
	if err != nil {
		objLog.Errorln("excute template error:", err)
		return err
	}

	return ctx.HTML(http.StatusOK, buf.String())
}

///////////////////////////////// APP 相关 //////////////////////////////

const (
	TokenSalt       = "b3%JFOykZx_golang_polaris"
	NeedReLoginCode = 600
)

func ParseToken(token string) (int, bool) {
	if len(token) < 32 {
		return 0, false
	}

	pos := strings.LastIndex(token, "uid")
	if pos == -1 {
		return 0, false
	}
	return goutils.MustInt(token[pos+3:]), true
}

func ValidateToken(token string) bool {
	_, ok := ParseToken(token)
	if !ok {
		return false
	}

	expireTime := time.Unix(goutils.MustInt64(token[:10]), 0)
	if time.Now().Before(expireTime) {
		return true
	}
	return false
}

func GenToken(uid int) string {
	expireTime := time.Now().Add(30 * 24 * time.Hour).Unix()

	buffer := goutils.NewBuffer().Append(expireTime).Append(uid).Append(TokenSalt)

	md5 := goutils.Md5(buffer.String())

	buffer = goutils.NewBuffer().Append(expireTime).Append(md5).Append("uid").Append(uid)
	return buffer.String()
}

func AccessControl(ctx echo.Context) {
	ctx.Response().Header().Add("Access-Control-Allow-Origin", "*")
}
<|endoftext|>


<commit_before>// +build !confonly

package tls

import (
	"crypto/tls"
	"crypto/x509"
	"strings"
	"sync"
	"time"

	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol/tls/cert"
	"v2ray.com/core/transport/internet"
)

var (
	globalSessionCache = tls.NewLRUClientSessionCache(128)
)

const exp8357 = "experiment:8357"

// ParseCertificate converts a cert.Certificate to Certificate.
func ParseCertificate(c *cert.Certificate) *Certificate {
	certPEM, keyPEM := c.ToPEM()
	return &Certificate{
		Certificate: certPEM,
		Key:         keyPEM,
	}
}

func (c *Config) loadSelfCertPool() (*x509.CertPool, error) {
	root := x509.NewCertPool()
	for _, cert := range c.Certificate {
		if !root.AppendCertsFromPEM(cert.Certificate) {
			return nil, newError("failed to append cert").AtWarning()
		}
	}
	return root, nil
}

// BuildCertificates builds a list of TLS certificates from proto definition.
func (c *Config) BuildCertificates() []tls.Certificate {
	certs := make([]tls.Certificate, 0, len(c.Certificate))
	for _, entry := range c.Certificate {
		if entry.Usage != Certificate_ENCIPHERMENT {
			continue
		}
		keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
		if err != nil {
			newError("ignoring invalid X509 key pair").Base(err).AtWarning().WriteToLog()
			continue
		}
		certs = append(certs, keyPair)
	}
	return certs
}

func isCertificateExpired(c *tls.Certificate) bool {
	if c.Leaf == nil && len(c.Certificate) > 0 {
		if pc, err := x509.ParseCertificate(c.Certificate[0]); err == nil {
			c.Leaf = pc
		}
	}

	// If leaf is not there, the certificate is probably not used yet. We trust user to provide a valid certificate.
	return c.Leaf != nil && c.Leaf.NotAfter.Before(time.Now().Add(-time.Minute))
}

func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, error) {
	parent, err := cert.ParseCertificate(rawCA.Certificate, rawCA.Key)
	if err != nil {
		return nil, newError("failed to parse raw certificate").Base(err)
	}
	newCert, err := cert.Generate(parent, cert.CommonName(domain), cert.DNSNames(domain))
	if err != nil {
		return nil, newError("failed to generate new certificate for ", domain).Base(err)
	}
	newCertPEM, newKeyPEM := newCert.ToPEM()
	cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM)
	return &cert, err
}

func (c *Config) getCustomCA() []*Certificate {
	certs := make([]*Certificate, 0, len(c.Certificate))
	for _, certificate := range c.Certificate {
		if certificate.Usage == Certificate_AUTHORITY_ISSUE {
			certs = append(certs, certificate)
		}
	}
	return certs
}

func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	var access sync.RWMutex

	return func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
		domain := hello.ServerName
		certExpired := false

		access.RLock()
		certificate, found := c.NameToCertificate[domain]
		access.RUnlock()

		if found {
			if !isCertificateExpired(certificate) {
				return certificate, nil
			}
			certExpired = true
		}

		if certExpired {
			newCerts := make([]tls.Certificate, 0, len(c.Certificates))

			access.Lock()
			for _, certificate := range c.Certificates {
				if !isCertificateExpired(&certificate) {
					newCerts = append(newCerts, certificate)
				}
			}

			c.Certificates = newCerts
			access.Unlock()
		}

		var issuedCertificate *tls.Certificate

		// Create a new certificate from existing CA if possible
		for _, rawCert := range ca {
			if rawCert.Usage == Certificate_AUTHORITY_ISSUE {
				newCert, err := issueCertificate(rawCert, domain)
				if err != nil {
					newError("failed to issue new certificate for ", domain).Base(err).WriteToLog()
					continue
				}

				access.Lock()
				c.Certificates = append(c.Certificates, *newCert)
				issuedCertificate = &c.Certificates[len(c.Certificates)-1]
				access.Unlock()
				break
			}
		}

		if issuedCertificate == nil {
			return nil, newError("failed to create a new certificate for ", domain)
		}

		access.Lock()
		c.BuildNameToCertificate()
		access.Unlock()

		return issuedCertificate, nil
	}
}

func (c *Config) IsExperiment8357() bool {
	return strings.HasPrefix(c.ServerName, exp8357)
}

func (c *Config) parseServerName() string {
	if c.IsExperiment8357() {
		return c.ServerName[len(exp8357):]
	}

	return c.ServerName
}

// GetTLSConfig converts this Config into tls.Config.
func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
	root, err := c.getCertPool()
	if err != nil {
		newError("failed to load system root certificate").AtError().Base(err).WriteToLog()
	}

	config := &tls.Config{
		ClientSessionCache:     globalSessionCache,
		RootCAs:                root,
		SessionTicketsDisabled: c.DisableSessionResumption,
	}
	if c == nil {
		return config
	}

	for _, opt := range opts {
		opt(config)
	}

	if !c.AllowInsecureCiphers && len(config.CipherSuites) == 0 {
		config.CipherSuites = []uint16{
			tls.TLS_AES_128_GCM_SHA256,
			tls.TLS_AES_256_GCM_SHA384,
			tls.TLS_CHACHA20_POLY1305_SHA256,

			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
			tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
		}
	}

	config.InsecureSkipVerify = c.AllowInsecure
	config.Certificates = c.BuildCertificates()
	config.BuildNameToCertificate()

	caCerts := c.getCustomCA()
	if len(caCerts) > 0 {
		config.GetCertificate = getGetCertificateFunc(config, caCerts)
	}

	if sn := c.parseServerName(); len(sn) > 0 {
		config.ServerName = sn
	}

	if len(c.NextProtocol) > 0 {
		config.NextProtos = c.NextProtocol
	}
	if len(config.NextProtos) == 0 {
		config.NextProtos = []string{"http/1.1"}
	}

	return config
}

// Option for building TLS config.
type Option func(*tls.Config)

// WithDestination sets the server name in TLS config.
func WithDestination(dest net.Destination) Option {
	return func(config *tls.Config) {
		if dest.Address.Family().IsDomain() && config.ServerName == "" {
			config.ServerName = dest.Address.Domain()
		}
	}
}

// WithNextProto sets the ALPN values in TLS config.
func WithNextProto(protocol ...string) Option {
	return func(config *tls.Config) {
		if len(config.NextProtos) == 0 {
			config.NextProtos = protocol
		}
	}
}

// ConfigFromStreamSettings fetches Config from stream settings. Nil if not found.
func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config {
	if settings == nil {
		return nil
	}
	config, ok := settings.SecuritySettings.(*Config)
	if !ok {
		return nil
	}
	return config
}
<commit_msg>tls use crypto std cipher suites<commit_after>// +build !confonly

package tls

import (
	"crypto/tls"
	"crypto/x509"
	"strings"
	"sync"
	"time"

	"v2ray.com/core/common/net"
	"v2ray.com/core/common/protocol/tls/cert"
	"v2ray.com/core/transport/internet"
)

var (
	globalSessionCache = tls.NewLRUClientSessionCache(128)
)

const exp8357 = "experiment:8357"

// ParseCertificate converts a cert.Certificate to Certificate.
func ParseCertificate(c *cert.Certificate) *Certificate {
	certPEM, keyPEM := c.ToPEM()
	return &Certificate{
		Certificate: certPEM,
		Key:         keyPEM,
	}
}

func (c *Config) loadSelfCertPool() (*x509.CertPool, error) {
	root := x509.NewCertPool()
	for _, cert := range c.Certificate {
		if !root.AppendCertsFromPEM(cert.Certificate) {
			return nil, newError("failed to append cert").AtWarning()
		}
	}
	return root, nil
}

// BuildCertificates builds a list of TLS certificates from proto definition.
func (c *Config) BuildCertificates() []tls.Certificate {
	certs := make([]tls.Certificate, 0, len(c.Certificate))
	for _, entry := range c.Certificate {
		if entry.Usage != Certificate_ENCIPHERMENT {
			continue
		}
		keyPair, err := tls.X509KeyPair(entry.Certificate, entry.Key)
		if err != nil {
			newError("ignoring invalid X509 key pair").Base(err).AtWarning().WriteToLog()
			continue
		}
		certs = append(certs, keyPair)
	}
	return certs
}

func isCertificateExpired(c *tls.Certificate) bool {
	if c.Leaf == nil && len(c.Certificate) > 0 {
		if pc, err := x509.ParseCertificate(c.Certificate[0]); err == nil {
			c.Leaf = pc
		}
	}

	// If leaf is not there, the certificate is probably not used yet. We trust user to provide a valid certificate.
	return c.Leaf != nil && c.Leaf.NotAfter.Before(time.Now().Add(-time.Minute))
}

func issueCertificate(rawCA *Certificate, domain string) (*tls.Certificate, error) {
	parent, err := cert.ParseCertificate(rawCA.Certificate, rawCA.Key)
	if err != nil {
		return nil, newError("failed to parse raw certificate").Base(err)
	}
	newCert, err := cert.Generate(parent, cert.CommonName(domain), cert.DNSNames(domain))
	if err != nil {
		return nil, newError("failed to generate new certificate for ", domain).Base(err)
	}
	newCertPEM, newKeyPEM := newCert.ToPEM()
	cert, err := tls.X509KeyPair(newCertPEM, newKeyPEM)
	return &cert, err
}

func (c *Config) getCustomCA() []*Certificate {
	certs := make([]*Certificate, 0, len(c.Certificate))
	for _, certificate := range c.Certificate {
		if certificate.Usage == Certificate_AUTHORITY_ISSUE {
			certs = append(certs, certificate)
		}
	}
	return certs
}

func getGetCertificateFunc(c *tls.Config, ca []*Certificate) func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
	var access sync.RWMutex

	return func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
		domain := hello.ServerName
		certExpired := false

		access.RLock()
		certificate, found := c.NameToCertificate[domain]
		access.RUnlock()

		if found {
			if !isCertificateExpired(certificate) {
				return certificate, nil
			}
			certExpired = true
		}

		if certExpired {
			newCerts := make([]tls.Certificate, 0, len(c.Certificates))

			access.Lock()
			for _, certificate := range c.Certificates {
				if !isCertificateExpired(&certificate) {
					newCerts = append(newCerts, certificate)
				}
			}

			c.Certificates = newCerts
			access.Unlock()
		}

		var issuedCertificate *tls.Certificate

		// Create a new certificate from existing CA if possible
		for _, rawCert := range ca {
			if rawCert.Usage == Certificate_AUTHORITY_ISSUE {
				newCert, err := issueCertificate(rawCert, domain)
				if err != nil {
					newError("failed to issue new certificate for ", domain).Base(err).WriteToLog()
					continue
				}

				access.Lock()
				c.Certificates = append(c.Certificates, *newCert)
				issuedCertificate = &c.Certificates[len(c.Certificates)-1]
				access.Unlock()
				break
			}
		}

		if issuedCertificate == nil {
			return nil, newError("failed to create a new certificate for ", domain)
		}

		access.Lock()
		c.BuildNameToCertificate()
		access.Unlock()

		return issuedCertificate, nil
	}
}

func (c *Config) IsExperiment8357() bool {
	return strings.HasPrefix(c.ServerName, exp8357)
}

func (c *Config) parseServerName() string {
	if c.IsExperiment8357() {
		return c.ServerName[len(exp8357):]
	}

	return c.ServerName
}

// GetTLSConfig converts this Config into tls.Config.
func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
	root, err := c.getCertPool()
	if err != nil {
		newError("failed to load system root certificate").AtError().Base(err).WriteToLog()
	}

	config := &tls.Config{
		ClientSessionCache:     globalSessionCache,
		RootCAs:                root,
		SessionTicketsDisabled: c.DisableSessionResumption,
	}
	if c == nil {
		return config
	}

	for _, opt := range opts {
		opt(config)
	}

	if !c.AllowInsecureCiphers && len(config.CipherSuites) == 0 {
		// use tls cipher suites from cryto/tls
		config.CipherSuites = []uint16{}
		for _, s := range tls.CipherSuites() {
			config.CipherSuites = append(config.CipherSuites, s.ID)
		}
	}

	config.InsecureSkipVerify = c.AllowInsecure
	config.Certificates = c.BuildCertificates()
	config.BuildNameToCertificate()

	caCerts := c.getCustomCA()
	if len(caCerts) > 0 {
		config.GetCertificate = getGetCertificateFunc(config, caCerts)
	}

	if sn := c.parseServerName(); len(sn) > 0 {
		config.ServerName = sn
	}

	if len(c.NextProtocol) > 0 {
		config.NextProtos = c.NextProtocol
	}
	if len(config.NextProtos) == 0 {
		config.NextProtos = []string{"http/1.1"}
	}

	return config
}

// Option for building TLS config.
type Option func(*tls.Config)

// WithDestination sets the server name in TLS config.
func WithDestination(dest net.Destination) Option {
	return func(config *tls.Config) {
		if dest.Address.Family().IsDomain() && config.ServerName == "" {
			config.ServerName = dest.Address.Domain()
		}
	}
}

// WithNextProto sets the ALPN values in TLS config.
func WithNextProto(protocol ...string) Option {
	return func(config *tls.Config) {
		if len(config.NextProtos) == 0 {
			config.NextProtos = protocol
		}
	}
}

// ConfigFromStreamSettings fetches Config from stream settings. Nil if not found.
func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config {
	if settings == nil {
		return nil
	}
	config, ok := settings.SecuritySettings.(*Config)
	if !ok {
		return nil
	}
	return config
}
<|endoftext|>


<commit_before>package killable

import (
	"errors"
	"time"
)

var (
	ErrDying      = errors.New("killable: dying")
	ErrKill       = errors.New("killable: killed")
	ErrStillAlive = errors.New("killable: still alive")
)

type Killable interface {
	// Dying is closed immediatly after Kill is called
	Dying() <-chan struct{}

	// Dead is closed after all executing functions have returned
	// These executing functions must have been started with Do or Go
	Dead() <-chan struct{}

	// Put the Killable into the dying state
	Kill(reason error)

	// Return the error passed to Kill
	// blocks until in dying state
	Err() error

	// faster state access
	isDead() bool
	isDying() bool

	// access to underlying WaitGroup
	add()
	done()
	wait()
}

func New(killables ...Killable) Killable {
	if len(killables) == 0 {
		return newSingle()
	} else {
		return newGroup(killables...)
	}
}

// Sleep blocks for a specified duration
// If the Killable is marked as dying it will return
// immediatly with ErrDying
func Sleep(k Killable, d time.Duration) error {
	select {
	case <-time.After(d):
		return nil
	case <-k.Dying():
		return ErrDying
	}
}

// Do executes a function and retuns its error value.
// * If the Killable is marked as dying it will return immediatly with ErrDying.
// * The Killable will not be marked as dead until all calls to Do have returned.
func Do(k Killable, fn func() error) error {
	if Dying(k) {
		return ErrDying
	}
	k.add()
	ch := make(chan error)
	go func() {
		defer k.done()
		select {
		case ch <- fn():
		case <-k.Dying():
		}
	}()
	select {
	case err := <-ch:
		return err
	case <-k.Dying():
		return ErrDying
	}
}

// Go executes a function in a goroutine
// * If the function returns a non-nil error the Killable is killed using that error
// * The Killable will no be marked as dead until all calls to Go have returned
func Go(k Killable, fn func() error) {
	if Dying(k) {
		return
	}
	k.add()
	go func() {
		defer k.done()
		if err := fn(); err != nil && err != ErrDying {
			k.Kill(err)
		}
	}()
}

// Defer invokes a callback once a Killable is dead
func Defer(k Killable, fn func()) {
	go func() {
		<-k.Dead()
		fn()
	}()
}

// Dying returns true if a Killable is in the dying or dead state
func Dying(k Killable) bool {
	return k.isDying()
}

// Dead returns true if the Killable is in the dead state
func Dead(k Killable) bool {
	return k.isDead()
}

// Alive returns true if the Killable is not in a dying state
func Alive(k Killable) bool {
	return !Dying(k)
}

// Err returns the Killable error
// If the Killable is alive, it returns ErrStillAlive
func Err(k Killable) error {
	if Dead(k) {
		return k.Err()
	} else {
		return ErrStillAlive
	}
}
<commit_msg>rename some public methods<commit_after>package killable

import (
	"errors"
	"time"
)

var (
	ErrDying      = errors.New("killable: dying")
	ErrKill       = errors.New("killable: killed")
	ErrStillAlive = errors.New("killable: still alive")
)

type Killable interface {
	// Dying is closed immediatly after Kill is called
	Dying() <-chan struct{}

	// Dead is closed after all executing functions have returned
	// These executing functions must have been started with Do or Go
	Dead() <-chan struct{}

	// Put the Killable into the dying state
	Kill(reason error)

	// Return the error passed to Kill
	// blocks until in dying state
	Err() error

	// faster state access
	isDead() bool
	isDying() bool

	// access to underlying WaitGroup
	add()
	done()
	wait()
}

func New(killables ...Killable) Killable {
	if len(killables) == 0 {
		return newSingle()
	} else {
		return newGroup(killables...)
	}
}

// Sleep blocks for a specified duration
// If the Killable is marked as dying it will return
// immediatly with ErrDying
func Sleep(k Killable, d time.Duration) error {
	select {
	case <-time.After(d):
		return nil
	case <-k.Dying():
		return ErrDying
	}
}

// Do executes a function and retuns its error value.
// * If the Killable is marked as dying it will return immediatly with ErrDying.
// * The Killable will not be marked as dead until all calls to Do have returned.
func Do(k Killable, fn func() error) error {
	if IsDying(k) {
		return ErrDying
	}
	k.add()
	ch := make(chan error)
	go func() {
		defer k.done()
		select {
		case ch <- fn():
		case <-k.Dying():
		}
	}()
	select {
	case err := <-ch:
		return err
	case <-k.Dying():
		return ErrDying
	}
}

// Go executes a function in a goroutine
// * If the function returns a non-nil error the Killable is killed using that error
// * The Killable will no be marked as dead until all calls to Go have returned
func Go(k Killable, fn func() error) {
	if IsDying(k) {
		return
	}
	k.add()
	go func() {
		defer k.done()
		if err := fn(); err != nil && err != ErrDying {
			k.Kill(err)
		}
	}()
}

// Defer invokes a callback once a Killable is dead
func Defer(k Killable, fn func()) {
	go func() {
		<-k.Dead()
		fn()
	}()
}

// IsDying returns true if a Killable is in the dying or dead state
func IsDying(k Killable) bool { return k.isDying() }

// IsDead true if the Killable is in the dead state
func IsDead(k Killable) bool { return k.isDead() }

// IsAlive returns true if the Killable isn't in the dead or dying states
func IsAlive(k Killable) bool { return !IsDying(k) }

// Err returns the Killable error
// If the Killable is alive, it returns ErrStillAlive
func Err(k Killable) error {
	if IsDead(k) {
		return k.Err()
	} else {
		return ErrStillAlive
	}
}
<|endoftext|>


<commit_before>package ldp

import (
	"errors"
	"fmt"
	"io"
	"ldpserver/rdf"
	"ldpserver/textstore"
	"ldpserver/util"
	"log"
	"strings"
	"time"
)

var NodeNotFoundError = errors.New("Node not found")
var DuplicateNodeError = errors.New("Node already exists")
var EtagMissingError = errors.New("Missing Etag")
var EtagMismatchError = errors.New("Etag mismatch")
var ServerManagedPropertyError = errors.New("Attempted to update server managed property")

const metaFile = "meta.rdf"
const dataFile = "data.txt"

type Node struct {
	isRdf   bool
	uri     string
	headers map[string][]string
	graph   rdf.RdfGraph
	binary  string // should be []byte or reader

	settings Settings
	rootUri  string // http://localhost/
	store    textstore.Store

	isBasicContainer   bool
	isDirectContainer  bool
	membershipResource string
	hasMemberRelation  string
	// TODO isMemberOfRelation string
}

func (node Node) AddChild(child Node) error {
	triple := rdf.NewTriple("<"+node.uri+">", "<"+rdf.LdpContainsUri+">", "<"+child.uri+">")
	err := node.store.AppendToFile(metaFile, triple.StringLn())
	if err != nil {
		return err
	}

	if node.isDirectContainer {
		return node.addDirectContainerChild(child)
	}
	return nil
}

func (node Node) Content() string {
	if node.isRdf {
		return node.graph.String()
	}
	return node.binary
}

func (node Node) DebugString() string {
	if !node.isRdf {
		return fmt.Sprintf("Non-RDF: %s", node.uri)
	}

	triples := ""
	for i, triple := range node.graph {
		triples += fmt.Sprintf("%d %s\n", i, triple)
	}
	debugString := fmt.Sprintf("RDF: %s\n %s", node.uri, triples)
	return debugString
}

func (node *Node) Etag() string {
	subject := "<" + node.uri + ">"
	etag, etagFound := node.graph.GetObject(subject, "<"+rdf.ServerETagUri+">")
	if !etagFound {
		panic(fmt.Sprintf("No etag found for node %s", node.uri))
	}
	return etag
}

func (node Node) HasTriple(predicate, object string) bool {
	return node.graph.HasTriple("<"+node.uri+">", predicate, object)
}

func (node Node) Headers() map[string][]string {
	return node.headers
}

func (node Node) IsBasicContainer() bool {
	return node.isBasicContainer
}

func (node Node) IsDirectContainer() bool {
	return node.isDirectContainer
}

func (node Node) IsRdf() bool {
	return node.isRdf
}

func (node Node) Uri() string {
	return node.uri
}

func (node *Node) Patch(triples string) error {
	if !node.isRdf {
		return errors.New("Cannot PATCH non-RDF Source")
	}

	graph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		return err
	}

	// This is pretty useless as-is since it does not allow to update
	// a triple. It always adds triples.
	// Also, there are some triples that can exist only once (e.g. direct container triples)
	// and this code does not validate them.
	node.graph.Append(graph)

	// write it to disk
	if err := node.writeToDisk(nil); err != nil {
		return err
	}

	return nil
}

func (node Node) Path() string {
	return util.PathFromUri(node.rootUri, node.uri)
}

func (node Node) String() string {
	return node.uri
}

func GetNode(settings Settings, path string) (Node, error) {
	node := newNode(settings, path)
	err := node.loadNode(true)
	return node, err
}

func GetHead(settings Settings, path string) (Node, error) {
	node := newNode(settings, path)
	err := node.loadNode(false)
	return node, err
}

func NewRdfNode(settings Settings, triples string, path string) (Node, error) {
	node := newNode(settings, path)
	graph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		return Node{}, err
	}
	return node, node.writeRdfToDisk(graph)
}

func NewNonRdfNode(settings Settings, reader io.ReadCloser, path string) (Node, error) {
	node := newNode(settings, path)
	return node, node.writeNonRdfToDisk(reader)
}

func ReplaceNonRdfNode(settings Settings, reader io.ReadCloser, path string, etag string) (Node, error) {
	node, err := GetHead(settings, path)
	if err != nil {
		return Node{}, err
	}

	if node.isRdf {
		return Node{}, errors.New("Cannot replace RDF source with a Non-RDF source")
	}

	if etag == "" {
		return Node{}, EtagMissingError
	}

	if node.Etag() != etag {
		// log.Printf("Cannot replace RDF source. Etag mismatch. Expected: %s. Found: %s", node.Etag(), etag)
		return Node{}, EtagMismatchError
	}
	return node, node.writeNonRdfToDisk(reader)
}

func ReplaceRdfNode(settings Settings, triples string, path string, etag string) (Node, error) {
	node, err := GetNode(settings, path)
	if err != nil {
		return Node{}, err
	}

	if !node.isRdf {
		return Node{}, errors.New("Cannot replace non-RDF source with an RDF source")
	}

	if etag == "" {
		return Node{}, EtagMissingError
	}

	if node.Etag() != etag {
		// log.Printf("Cannot replace RDF source. Etag mismatch. Expected: %s. Found: %s", node.Etag(), etag)
		return Node{}, EtagMismatchError
	}

	graph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		return Node{}, err
	}

	// TODO: What other server-managed properties should we handle?
	if graph.HasPredicate("<"+node.uri+">", "<"+rdf.LdpContainsUri+">") {
		return Node{}, ServerManagedPropertyError
	}

	return node, node.writeRdfToDisk(graph)
}

func (node Node) addDirectContainerChild(child Node) error {
	// TODO: account for isMemberOfRelation
	targetUri := removeAngleBrackets(node.membershipResource)
	targetPath := util.PathFromUri(node.rootUri, targetUri)

	targetNode, err := GetNode(node.settings, targetPath)
	if err != nil {
		log.Printf("Could not find target node %s.", targetPath)
		return err
	}

	tripleForTarget := rdf.NewTriple("<"+targetNode.uri+">", node.hasMemberRelation, "<"+child.uri+">")

	err = targetNode.store.AppendToFile(metaFile, tripleForTarget.StringLn())
	if err != nil {
		log.Printf("Error appending child %s to %s. %s", child.uri, targetNode.uri, err)
		return err
	}
	return nil
}

func (node *Node) loadNode(isIncludeBody bool) error {
	err := node.loadMeta()
	if err != nil {
		return err
	}

	if node.isRdf || isIncludeBody == false {
		return nil
	}

	return node.loadBinary()
}

func (node *Node) loadBinary() error {
	var err error
	node.binary, err = node.store.ReadFile(dataFile)
	return err
}

func (node *Node) loadMeta() error {
	if !node.store.Exists() {
		return NodeNotFoundError
	}

	meta, err := node.store.ReadFile(metaFile)
	if err != nil {
		return err
	}

	graph, err := rdf.StringToGraph(meta, node.uri)
	if err != nil {
		return err
	}

	if graph.IsRdfSource("<" + node.uri + ">") {
		node.setAsRdf(graph)
	} else {
		node.setAsNonRdf(graph)
	}
	return nil
}

func (node *Node) writeNonRdfToDisk(reader io.ReadCloser) error {
	graph := defaultGraphNonRdf(node.uri)
	node.setAsNonRdf(graph)
	return node.writeToDisk(reader)
}

func (node *Node) writeRdfToDisk(graph rdf.RdfGraph) error {
	subject := "<" + node.uri + ">"
	etagPredicate := "<" + rdf.ServerETagUri + ">"
	graph.SetObject(subject, etagPredicate, calculateEtag())

	graph.AppendTriple(rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpResourceUri+">"))
	graph.AppendTriple(rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpRdfSourceUri+">"))
	graph.AppendTriple(rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpContainerUri+">"))
	graph.AppendTriple(rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpBasicContainerUri+">"))

	node.setAsRdf(graph)
	return node.writeToDisk(nil)
}

func (node *Node) writeToDisk(reader io.ReadCloser) error {
	// Write the RDF metadata
	err := node.store.SaveFile(metaFile, node.graph.String())
	if err != nil {
		return err
	}

	if node.isRdf {
		return nil
	}

	// Write the binary...
	err = node.store.SaveReader(dataFile, reader)
	if err != nil {
		return err
	}

	// ...update the copy in memory (this would get
	// tricky when we switch "node.binary" to a
	// reader.
	node.binary, err = node.store.ReadFile(dataFile)
	return err
}

func (node *Node) setAsRdf(graph rdf.RdfGraph) {
	subject := "<" + node.uri + ">"
	node.isRdf = true
	node.graph = graph
	node.headers = make(map[string][]string)
	node.headers["Content-Type"] = []string{rdf.TurtleContentType}

	if graph.IsBasicContainer(subject) {
		// Is there a way to indicate that PUT is allowed
		// for creation only (and not to overwrite?)
		node.headers["Allow"] = []string{"GET, HEAD, POST, PUT, PATCH"}
	} else {
		node.headers["Allow"] = []string{"GET, HEAD, PUT, PATCH"}
	}
	node.headers["Accept-Post"] = []string{"text/turtle"}
	node.headers["Accept-Patch"] = []string{"text/turtle"}

	node.headers["Etag"] = []string{node.Etag()}

	links := make([]string, 0)
	links = append(links, rdf.LdpResourceLink)
	if graph.IsBasicContainer(subject) {
		node.isBasicContainer = true
		links = append(links, rdf.LdpContainerLink)
		links = append(links, rdf.LdpBasicContainerLink)
		// TODO: validate membershipResource is a sub-URI of rootURI
		node.membershipResource, node.hasMemberRelation, node.isDirectContainer = graph.GetDirectContainerInfo()
		if node.isDirectContainer {
			links = append(links, rdf.LdpDirectContainerLink)
		}
	}
	node.headers["Link"] = links
}

func (node *Node) setAsNonRdf(graph rdf.RdfGraph) {
	// TODO Figure out a way to pass the binary as a stream
	node.isRdf = false
	node.graph = graph
	node.binary = ""
	node.headers = make(map[string][]string)
	node.headers["Link"] = []string{rdf.LdpNonRdfSourceLink}
	node.headers["Allow"] = []string{"GET, HEAD, PUT"}
	node.headers["Content-Type"] = []string{"application/binary"}
	// TODO: guess the content-type from meta

	node.headers["Etag"] = []string{node.Etag()}
}

func calculateEtag() string {
	// TODO: Come up with a more precise value.
	now := time.Now().Format(time.RFC3339)
	etag := strings.Replace(now, ":", "_", -1)
	return "\"" + etag + "\""
}

func defaultGraphNonRdf(uri string) rdf.RdfGraph {
	subject := "<" + uri + ">"
	// define the triples
	resource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpResourceUri+">")
	nonRdfSource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpNonRdfSourceUri+">")
	title := rdf.NewTriple(subject, "<"+rdf.DcTitleUri+">", "\"This is a new entry\"")
	nowString := "\"" + time.Now().Format(time.RFC3339) + "\""
	created := rdf.NewTriple(subject, "<"+rdf.DcCreatedUri+">", nowString)
	etag := rdf.NewTriple(subject, "<"+rdf.ServerETagUri+">", calculateEtag())
	// create the graph
	graph := rdf.RdfGraph{resource, nonRdfSource, title, created, etag}
	return graph
}

func newNode(settings Settings, path string) Node {
	if strings.HasPrefix(path, "http://") {
		panic("newNode expects a path, received a URI: " + path)
	}
	var node Node
	node.settings = settings
	pathOnDisk := util.PathConcat(settings.dataPath, path)
	node.store = textstore.NewStore(pathOnDisk)
	node.rootUri = settings.RootUri()
	node.uri = util.UriConcat(node.rootUri, path)
	return node
}

func removeAngleBrackets(text string) string {
	if strings.HasPrefix(text, "<") && strings.HasSuffix(text, ">") {
		return text[1 : len(text)-1]
	}
	return text
}
<commit_msg>Better handling of RDF associated with a non-RDF source<commit_after>package ldp

import (
	"errors"
	"fmt"
	"io"
	"ldpserver/rdf"
	"ldpserver/textstore"
	"ldpserver/util"
	"log"
	"strings"
	"time"
)

var NodeNotFoundError = errors.New("Node not found")
var DuplicateNodeError = errors.New("Node already exists")
var EtagMissingError = errors.New("Missing Etag")
var EtagMismatchError = errors.New("Etag mismatch")
var ServerManagedPropertyError = errors.New("Attempted to update server managed property")

const metaFile = "meta.rdf"
const dataFile = "data.txt"
const etagPredicate = "<" + rdf.ServerETagUri + ">"
const rdfTypePredicate = "<" + rdf.RdfTypeUri + ">"

type Node struct {
	isRdf   bool
	uri     string
	headers map[string][]string
	graph   rdf.RdfGraph
	binary  string // should be []byte or reader

	settings Settings
	rootUri  string // http://localhost/
	store    textstore.Store

	isBasicContainer   bool
	isDirectContainer  bool
	membershipResource string
	hasMemberRelation  string
	// TODO isMemberOfRelation string
}

func (node Node) AddChild(child Node) error {
	triple := rdf.NewTriple("<"+node.uri+">", "<"+rdf.LdpContainsUri+">", "<"+child.uri+">")
	err := node.store.AppendToFile(metaFile, triple.StringLn())
	if err != nil {
		return err
	}

	if node.isDirectContainer {
		return node.addDirectContainerChild(child)
	}
	return nil
}

func (node Node) Content() string {
	if node.isRdf {
		return node.graph.String()
	}
	return node.binary
}

func (node Node) DebugString() string {
	if !node.isRdf {
		return fmt.Sprintf("Non-RDF: %s", node.uri)
	}

	triples := ""
	for i, triple := range node.graph {
		triples += fmt.Sprintf("%d %s\n", i, triple)
	}
	debugString := fmt.Sprintf("RDF: %s\n %s", node.uri, triples)
	return debugString
}

func (node *Node) Etag() string {
	subject := "<" + node.uri + ">"
	etag, etagFound := node.graph.GetObject(subject, "<"+rdf.ServerETagUri+">")
	if !etagFound {
		panic(fmt.Sprintf("No etag found for node %s", node.uri))
	}
	return etag
}

func (node Node) HasTriple(predicate, object string) bool {
	return node.graph.HasTriple("<"+node.uri+">", predicate, object)
}

func (node Node) Headers() map[string][]string {
	return node.headers
}

func (node Node) IsBasicContainer() bool {
	return node.isBasicContainer
}

func (node Node) IsDirectContainer() bool {
	return node.isDirectContainer
}

func (node Node) IsRdf() bool {
	return node.isRdf
}

func (node Node) Uri() string {
	return node.uri
}

func (node *Node) Patch(triples string) error {
	if !node.isRdf {
		return errors.New("Cannot PATCH non-RDF Source")
	}

	graph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		return err
	}

	// This is pretty useless as-is since it does not allow to update
	// a triple. It always adds triples.
	// Also, there are some triples that can exist only once (e.g. direct container triples)
	// and this code does not validate them.
	node.graph.Append(graph)

	// write it to disk
	if err := node.writeToDisk(nil); err != nil {
		return err
	}

	return nil
}

func (node Node) Path() string {
	return util.PathFromUri(node.rootUri, node.uri)
}

func (node Node) String() string {
	return node.uri
}

func GetNode(settings Settings, path string) (Node, error) {
	node := newNode(settings, path)
	err := node.loadNode(true)
	return node, err
}

func GetHead(settings Settings, path string) (Node, error) {
	node := newNode(settings, path)
	err := node.loadNode(false)
	return node, err
}

func NewRdfNode(settings Settings, triples string, path string) (Node, error) {
	node := newNode(settings, path)
	graph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		return Node{}, err
	}
	return node, node.writeRdfToDisk(graph)
}

func NewNonRdfNode(settings Settings, reader io.ReadCloser, path string) (Node, error) {
	node := newNode(settings, path)
	return node, node.writeNonRdfToDisk(reader)
}

func ReplaceNonRdfNode(settings Settings, reader io.ReadCloser, path string, etag string) (Node, error) {
	node, err := GetHead(settings, path)
	if err != nil {
		return Node{}, err
	}

	if node.isRdf {
		return Node{}, errors.New("Cannot replace RDF source with a Non-RDF source")
	}

	if etag == "" {
		return Node{}, EtagMissingError
	}

	if node.Etag() != etag {
		// log.Printf("Cannot replace RDF source. Etag mismatch. Expected: %s. Found: %s", node.Etag(), etag)
		return Node{}, EtagMismatchError
	}
	return node, node.writeNonRdfToDisk(reader)
}

func ReplaceRdfNode(settings Settings, triples string, path string, etag string) (Node, error) {
	node, err := GetNode(settings, path)
	if err != nil {
		return Node{}, err
	}

	if !node.isRdf {
		return Node{}, errors.New("Cannot replace non-RDF source with an RDF source")
	}

	if etag == "" {
		return Node{}, EtagMissingError
	}

	if node.Etag() != etag {
		// log.Printf("Cannot replace RDF source. Etag mismatch. Expected: %s. Found: %s", node.Etag(), etag)
		return Node{}, EtagMismatchError
	}

	graph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		return Node{}, err
	}

	// TODO: What other server-managed properties should we handle?
	if graph.HasPredicate("<"+node.uri+">", "<"+rdf.LdpContainsUri+">") {
		return Node{}, ServerManagedPropertyError
	}

	return node, node.writeRdfToDisk(graph)
}

func (node Node) addDirectContainerChild(child Node) error {
	// TODO: account for isMemberOfRelation
	targetUri := removeAngleBrackets(node.membershipResource)
	targetPath := util.PathFromUri(node.rootUri, targetUri)

	targetNode, err := GetNode(node.settings, targetPath)
	if err != nil {
		log.Printf("Could not find target node %s.", targetPath)
		return err
	}

	tripleForTarget := rdf.NewTriple("<"+targetNode.uri+">", node.hasMemberRelation, "<"+child.uri+">")

	err = targetNode.store.AppendToFile(metaFile, tripleForTarget.StringLn())
	if err != nil {
		log.Printf("Error appending child %s to %s. %s", child.uri, targetNode.uri, err)
		return err
	}
	return nil
}

func (node *Node) loadNode(isIncludeBody bool) error {
	err := node.loadMeta()
	if err != nil {
		return err
	}

	if node.isRdf || isIncludeBody == false {
		return nil
	}

	return node.loadBinary()
}

func (node *Node) loadBinary() error {
	var err error
	node.binary, err = node.store.ReadFile(dataFile)
	return err
}

func (node *Node) loadMeta() error {
	if !node.store.Exists() {
		return NodeNotFoundError
	}

	meta, err := node.store.ReadFile(metaFile)
	if err != nil {
		return err
	}

	graph, err := rdf.StringToGraph(meta, node.uri)
	if err != nil {
		return err
	}

	if graph.IsRdfSource("<" + node.uri + ">") {
		node.setAsRdf(graph)
	} else {
		node.setAsNonRdf(graph)
	}
	return nil
}

func (node *Node) writeNonRdfToDisk(reader io.ReadCloser) error {
	subject := "<" + node.uri + ">"
	graph := rdf.RdfGraph{}
	graph.SetObject(subject, etagPredicate, calculateEtag())

	graph.AppendTriple(rdf.NewTriple(subject, rdfTypePredicate, "<"+rdf.LdpResourceUri+">"))
	graph.AppendTriple(rdf.NewTriple(subject, rdfTypePredicate, "<"+rdf.LdpNonRdfSourceUri+">"))

	node.setAsNonRdf(graph)
	return node.writeToDisk(reader)
}

func (node *Node) writeRdfToDisk(graph rdf.RdfGraph) error {
	subject := "<" + node.uri + ">"
	graph.SetObject(subject, etagPredicate, calculateEtag())

	graph.AppendTriple(rdf.NewTriple(subject, rdfTypePredicate, "<"+rdf.LdpResourceUri+">"))
	graph.AppendTriple(rdf.NewTriple(subject, rdfTypePredicate, "<"+rdf.LdpRdfSourceUri+">"))
	graph.AppendTriple(rdf.NewTriple(subject, rdfTypePredicate, "<"+rdf.LdpContainerUri+">"))
	graph.AppendTriple(rdf.NewTriple(subject, rdfTypePredicate, "<"+rdf.LdpBasicContainerUri+">"))

	node.setAsRdf(graph)
	return node.writeToDisk(nil)
}

func (node *Node) writeToDisk(reader io.ReadCloser) error {
	// Write the RDF metadata
	err := node.store.SaveFile(metaFile, node.graph.String())
	if err != nil {
		return err
	}

	if node.isRdf {
		return nil
	}

	// Write the binary...
	err = node.store.SaveReader(dataFile, reader)
	if err != nil {
		return err
	}

	// ...update the copy in memory (this would get
	// tricky when we switch "node.binary" to a
	// reader.
	node.binary, err = node.store.ReadFile(dataFile)
	return err
}

func (node *Node) setAsRdf(graph rdf.RdfGraph) {
	subject := "<" + node.uri + ">"
	node.isRdf = true
	node.graph = graph
	node.headers = make(map[string][]string)
	node.headers["Content-Type"] = []string{rdf.TurtleContentType}

	if graph.IsBasicContainer(subject) {
		// Is there a way to indicate that PUT is allowed
		// for creation only (and not to overwrite?)
		node.headers["Allow"] = []string{"GET, HEAD, POST, PUT, PATCH"}
	} else {
		node.headers["Allow"] = []string{"GET, HEAD, PUT, PATCH"}
	}
	node.headers["Accept-Post"] = []string{"text/turtle"}
	node.headers["Accept-Patch"] = []string{"text/turtle"}

	node.headers["Etag"] = []string{node.Etag()}

	links := make([]string, 0)
	links = append(links, rdf.LdpResourceLink)
	if graph.IsBasicContainer(subject) {
		node.isBasicContainer = true
		links = append(links, rdf.LdpContainerLink)
		links = append(links, rdf.LdpBasicContainerLink)
		// TODO: validate membershipResource is a sub-URI of rootURI
		node.membershipResource, node.hasMemberRelation, node.isDirectContainer = graph.GetDirectContainerInfo()
		if node.isDirectContainer {
			links = append(links, rdf.LdpDirectContainerLink)
		}
	}
	node.headers["Link"] = links
}

func (node *Node) setAsNonRdf(graph rdf.RdfGraph) {
	// TODO Figure out a way to pass the binary as a stream
	node.isRdf = false
	node.graph = graph
	node.binary = ""
	node.headers = make(map[string][]string)
	node.headers["Link"] = []string{rdf.LdpNonRdfSourceLink}
	node.headers["Allow"] = []string{"GET, HEAD, PUT"}
	node.headers["Content-Type"] = []string{"application/binary"}
	// TODO: guess the content-type from meta

	node.headers["Etag"] = []string{node.Etag()}
}

func calculateEtag() string {
	// TODO: Come up with a more precise value.
	now := time.Now().Format(time.RFC3339)
	etag := strings.Replace(now, ":", "_", -1)
	return "\"" + etag + "\""
}

func newNode(settings Settings, path string) Node {
	if strings.HasPrefix(path, "http://") {
		panic("newNode expects a path, received a URI: " + path)
	}
	var node Node
	node.settings = settings
	pathOnDisk := util.PathConcat(settings.dataPath, path)
	node.store = textstore.NewStore(pathOnDisk)
	node.rootUri = settings.RootUri()
	node.uri = util.UriConcat(node.rootUri, path)
	return node
}

func removeAngleBrackets(text string) string {
	if strings.HasPrefix(text, "<") && strings.HasSuffix(text, ">") {
		return text[1 : len(text)-1]
	}
	return text
}
<|endoftext|>


<commit_before>
package main

import (
	"fmt"
	"time"
	"math/rand"
)

type Result string
type Search func(query string) Result

var (
	Web = fakeSearch("web")
	Image = fakeSearch("image")
	Video = fakeSearch("video")
)

func fakeSearch(kind string) func(query string) Result {
	return func(query string) Result {
		time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
		return Result(fmt.Sprintf("%s result for %q\n", kind, query))
	}
}

func Google(query string) []Result {
	return append(make([]Result, 0), Web(query), Image(query), Video(query))
}

func main() {
	rand.Seed(time.Now().UnixNano())
	start := time.Now()
	results := Google("golang")
	elapsed := time.Since(start)
	fmt.Println(results)
	fmt.Println(elapsed)
}
<commit_msg>Parallelized Google search example<commit_after>
package main

import (
	"fmt"
	"time"
	"math/rand"
)

type Result string
type Search func(query string) Result

var (
	Web = fakeSearch("web")
	Image = fakeSearch("image")
	Video = fakeSearch("video")
)

func fakeSearch(kind string) func(query string) Result {
	return func(query string) Result {
		time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
		return Result(fmt.Sprintf("%s result for %q\n", kind, query))
	}
}

func Google(query string) (results []Result) {
	c := make(chan Result)

	go func() { c <- Web(query) }()
	go func() { c <- Image(query) }()
	go func() { c <- Video(query) }()

	for i := 0; i < 3; i++ {
		results = append(results, <- c)
	}

	return
}

func main() {
	rand.Seed(time.Now().UnixNano())
	start := time.Now()
	results := Google("golang")
	elapsed := time.Since(start)
	fmt.Println(results)
	fmt.Println(elapsed)
}
<|endoftext|>