<commit_before>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package SymbolTable

import (
	"container/vector";
	"unicode";
	"utf8";
)

type Type struct;

// ----------------------------------------------------------------------------
// Support

func assert(pred bool) {
	if !pred {
		panic("assertion failed");
	}
}

// ----------------------------------------------------------------------------
// Objects

// Object represents a language object, such as a constant, variable, type, etc.

const /* kind */ (
	BADOBJ = iota;  // error handling
	NONE;  // kind unknown
	CONST; TYPE; VAR; FIELD; FUNC; BUILTIN; PACKAGE; LABEL;
	END;  // end of scope (import/export only)
)

func KindStr(kind int) string {
	switch kind {
	case BADOBJ: return "BADOBJ";
	case NONE: return "NONE";
	case CONST: return "CONST";
	case TYPE: return "TYPE";
	case VAR: return "VAR";
	case FIELD: return "FIELD";
	case FUNC: return "FUNC";
	case BUILTIN: return "BUILTIN";
	case PACKAGE: return "PACKAGE";
	case LABEL: return "LABEL";
	case END: return "END";
	}
	return "<unknown Object kind>";
}

type Object struct {
	Id int;  // unique id

	Pos int;  // source position (< 0 if unknown position)
	Kind int;  // object kind
	Ident string;
	Typ *Type;  // nil for packages
	Pnolev int;  // >= 0: package no., <= 0: function nesting level, 0: global level
}

func (obj *Object) IsExported() bool {
	switch obj.Kind {
	case NONE /* FUNC for now */, CONST, TYPE, VAR, FUNC:
		ch, size := utf8.DecodeRuneInString(obj.Ident);
		return unicode.IsUpper(ch);
	}
	return false;
}

func (obj* Object) String() string {
	if obj != nil {
		return
			"Object(" +
			KindStr(obj.Kind) + ", " +
			obj.Ident +
			")";
	}
	return "nil";
}

var Universe_void_typ *Type  // initialized by Universe to Universe.void_typ
var objectId int;

func NewObject(pos, kind int, ident string) *Object {
	obj := new(Object);
	obj.Id = objectId;
	objectId++;

	obj.Pos = pos;
	obj.Kind = kind;
	obj.Ident = ident;
	obj.Typ = Universe_void_typ;  // TODO would it be better to use nil instead?
	obj.Pnolev = 0;

	return obj;
}

// ----------------------------------------------------------------------------
// Scopes

type Scope struct {
	Parent *Scope;
	entries map[string] *Object;
}

func NewScope(parent *Scope) *Scope {
	scope := new(Scope);
	scope.Parent = parent;
	scope.entries = make(map[string] *Object, 8);
	return scope;
}

func (scope *Scope) LookupLocal(ident string) *Object {
	obj, found := scope.entries[ident];
	if found {
		return obj;
	}
	return nil;
}

func (scope *Scope) Lookup(ident string) *Object {
	for scope != nil {
		obj := scope.LookupLocal(ident);
		if obj != nil {
			return obj;
		}
		scope = scope.Parent;
	}
	return nil;
}

func (scope *Scope) add(obj *Object) {
	scope.entries[obj.Ident] = obj;
}

func (scope *Scope) Insert(obj *Object) {
	if scope.LookupLocal(obj.Ident) != nil {
		panic("obj already inserted");
	}
	scope.add(obj);
}

func (scope *Scope) InsertImport(obj *Object) *Object {
	 p := scope.LookupLocal(obj.Ident);
	 if p == nil {
		scope.add(obj);
		p = obj;
	 }
	 return p;
}

func (scope *Scope) Print() {
	print("scope {");
	for key := range scope.entries {
		print("\n  ", key);
	}
	print("\n}\n");
}

// ----------------------------------------------------------------------------
// Types

const /* form */ (
	// internal types
	// We should never see one of these.
	UNDEF = iota;

	// VOID types are used when we don't have a type. Never exported.
	// (exported type forms must be > 0)
	VOID;

	// BADTYPE types are compatible with any type and don't cause further errors.
	// They are introduced only as a result of an error in the source code. A
	// correct program cannot have BAD types.
	BADTYPE;

	// FORWARD types are forward-declared (incomplete) types. They can only
	// be used as element types of pointer types and must be resolved before
	// their internals are accessible.
	FORWARD;

	// TUPLE types represent multi-valued result types of functions and
	// methods.
	TUPLE;

	// The type of nil.
	NIL;

	// A type name
	TYPENAME;

	// basic types
	BOOL; UINT; INT; FLOAT; STRING; INTEGER;

	// composite types
	ALIAS; ARRAY; STRUCT; INTERFACE; MAP; CHANNEL; FUNCTION; METHOD; POINTER;

	// open-ended parameter type
	ELLIPSIS
)

func FormStr(form int) string {
	switch form {
	case VOID: return "VOID";
	case BADTYPE: return "BADTYPE";
	case FORWARD: return "FORWARD";
	case TUPLE: return "TUPLE";
	case NIL: return "NIL";
	case TYPENAME: return "TYPENAME";
	case BOOL: return "BOOL";
	case UINT: return "UINT";
	case INT: return "INT";
	case FLOAT: return "FLOAT";
	case STRING: return "STRING";
	case ALIAS: return "ALIAS";
	case ARRAY: return "ARRAY";
	case STRUCT: return "STRUCT";
	case INTERFACE: return "INTERFACE";
	case MAP: return "MAP";
	case CHANNEL: return "CHANNEL";
	case FUNCTION: return "FUNCTION";
	case METHOD: return "METHOD";
	case POINTER: return "POINTER";
	case ELLIPSIS: return "ELLIPSIS";
	}
	return "<unknown Type form>";
}

const /* channel mode */ (
	FULL = iota;
	SEND;
	RECV;
)

type Type struct {
	Id int;  // unique id

	Ref int;  // for exporting only: >= 0 means already exported
	Form int;  // type form
	Size int;  // size in bytes
	Obj *Object;  // primary type object or nil
	Scope *Scope;  // locals, fields & methods

	// syntactic components
	Pos int;  // source position (< 0 if unknown position)
	Len int;  // array length
	Mode int;  // channel mode
	Key *Type;  // receiver type or map key
	Elt *Type;  // type name type, array, map, channel or pointer element type, function result type
	List *vector.Vector; End int;  // struct fields, interface methods, function parameters
}

var typeId int;

func NewType(pos, form int) *Type {
	typ := new(Type);
	typ.Id = typeId;
	typeId++;

	typ.Ref = -1;  // not yet exported
	typ.Pos = pos;
	typ.Form = form;

	return typ;
}

func (typ* Type) String() string {
	if typ != nil {
		return
			"Type(" +
			FormStr(typ.Form) +
			")";
	}
	return "nil";
}

// ----------------------------------------------------------------------------
// Universe scope

var (
	Universe *Scope;
	PredeclaredTypes vector.Vector;

	// internal types
	Void_typ,
	Bad_typ,
	Nil_typ,

	// basic types
	Bool_typ,
	Uint8_typ,
	Uint16_typ,
	Uint32_typ,
	Uint64_typ,
	Int8_typ,
	Int16_typ,
	Int32_typ,
	Int64_typ,
	Float32_typ,
	Float64_typ,
	Float80_typ,
	String_typ,
	Integer_typ,

	// convenience types
	Byte_typ,
	Uint_typ,
	Int_typ,
	Float_typ,
	Uintptr_typ *Type;

	True_obj,
	False_obj,
	Iota_obj,
	Nil_obj *Object;
)

func declObj(kind int, ident string, typ *Type) *Object {
	obj := NewObject(-1 /* no source pos */, kind, ident);
	obj.Typ = typ;
	if kind == TYPE && typ.Obj == nil {
		typ.Obj = obj;  // set primary type object
	}
	Universe.Insert(obj);
	return obj
}

func declType(form int, ident string, size int) *Type {
  typ := NewType(-1 /* no source pos */, form);
  typ.Size = size;
  return declObj(TYPE, ident, typ).Typ;
}

func register(typ *Type) *Type {
	typ.Ref = PredeclaredTypes.Len();
	PredeclaredTypes.Push(typ);
	return typ;
}

func init() {
	Universe = NewScope(nil);  // universe has no parent
	PredeclaredTypes.Init(32);

	// Interal types
	Void_typ = NewType(-1 /* no source pos */, VOID);
	Universe_void_typ = Void_typ;
	Bad_typ = NewType(-1 /* no source pos */, BADTYPE);
	Nil_typ = NewType(-1 /* no source pos */, NIL);

	// Basic types
	Bool_typ = register(declType(BOOL, "bool", 1));
	Uint8_typ = register(declType(UINT, "uint8", 1));
	Uint16_typ = register(declType(UINT, "uint16", 2));
	Uint32_typ = register(declType(UINT, "uint32", 4));
	Uint64_typ = register(declType(UINT, "uint64", 8));
	Int8_typ = register(declType(INT, "int8", 1));
	Int16_typ = register(declType(INT, "int16", 2));
	Int32_typ = register(declType(INT, "int32", 4));
	Int64_typ = register(declType(INT, "int64", 8));
	Float32_typ = register(declType(FLOAT, "float32", 4));
	Float64_typ = register(declType(FLOAT, "float64", 8));
	Float80_typ = register(declType(FLOAT, "float80", 10));
	String_typ = register(declType(STRING, "string", 8));
	Integer_typ = register(declType(INTEGER, "integer", 8));

	// All but 'byte' should be platform-dependent, eventually.
	Byte_typ = register(declType(UINT, "byte", 1));
	Uint_typ = register(declType(UINT, "uint", 4));
	Int_typ = register(declType(INT, "int", 4));
	Float_typ = register(declType(FLOAT, "float", 4));
	Uintptr_typ = register(declType(UINT, "uintptr", 8));

	// Predeclared constants
	True_obj = declObj(CONST, "true", Bool_typ);
	False_obj = declObj(CONST, "false", Bool_typ);
	Iota_obj = declObj(CONST, "iota", Int_typ);
	Nil_obj = declObj(CONST, "nil", Nil_typ);

	// Builtin functions
	declObj(BUILTIN, "len", Void_typ);
	declObj(BUILTIN, "new", Void_typ);
	declObj(BUILTIN, "panic", Void_typ);
	declObj(BUILTIN, "print", Void_typ);

	// scope.Print();
}
<commit_msg>fix to be able to run full gofmt test<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package SymbolTable

import (
	"container/vector";
	"unicode";
	"utf8";
)

// ----------------------------------------------------------------------------
// Support

func assert(pred bool) {
	if !pred {
		panic("assertion failed");
	}
}

// ----------------------------------------------------------------------------
// Objects

// Object represents a language object, such as a constant, variable, type, etc.

const /* kind */ (
	BADOBJ = iota;  // error handling
	NONE;  // kind unknown
	CONST; TYPE; VAR; FIELD; FUNC; BUILTIN; PACKAGE; LABEL;
	END;  // end of scope (import/export only)
)

func KindStr(kind int) string {
	switch kind {
	case BADOBJ: return "BADOBJ";
	case NONE: return "NONE";
	case CONST: return "CONST";
	case TYPE: return "TYPE";
	case VAR: return "VAR";
	case FIELD: return "FIELD";
	case FUNC: return "FUNC";
	case BUILTIN: return "BUILTIN";
	case PACKAGE: return "PACKAGE";
	case LABEL: return "LABEL";
	case END: return "END";
	}
	return "<unknown Object kind>";
}

type Object struct {
	Id int;  // unique id

	Pos int;  // source position (< 0 if unknown position)
	Kind int;  // object kind
	Ident string;
	Typ *Type;  // nil for packages
	Pnolev int;  // >= 0: package no., <= 0: function nesting level, 0: global level
}

func (obj *Object) IsExported() bool {
	switch obj.Kind {
	case NONE /* FUNC for now */, CONST, TYPE, VAR, FUNC:
		ch, size := utf8.DecodeRuneInString(obj.Ident);
		return unicode.IsUpper(ch);
	}
	return false;
}

func (obj* Object) String() string {
	if obj != nil {
		return
			"Object(" +
			KindStr(obj.Kind) + ", " +
			obj.Ident +
			")";
	}
	return "nil";
}

var Universe_void_typ *Type  // initialized by Universe to Universe.void_typ
var objectId int;

func NewObject(pos, kind int, ident string) *Object {
	obj := new(Object);
	obj.Id = objectId;
	objectId++;

	obj.Pos = pos;
	obj.Kind = kind;
	obj.Ident = ident;
	obj.Typ = Universe_void_typ;  // TODO would it be better to use nil instead?
	obj.Pnolev = 0;

	return obj;
}

// ----------------------------------------------------------------------------
// Scopes

type Scope struct {
	Parent *Scope;
	entries map[string] *Object;
}

func NewScope(parent *Scope) *Scope {
	scope := new(Scope);
	scope.Parent = parent;
	scope.entries = make(map[string] *Object, 8);
	return scope;
}

func (scope *Scope) LookupLocal(ident string) *Object {
	obj, found := scope.entries[ident];
	if found {
		return obj;
	}
	return nil;
}

func (scope *Scope) Lookup(ident string) *Object {
	for scope != nil {
		obj := scope.LookupLocal(ident);
		if obj != nil {
			return obj;
		}
		scope = scope.Parent;
	}
	return nil;
}

func (scope *Scope) add(obj *Object) {
	scope.entries[obj.Ident] = obj;
}

func (scope *Scope) Insert(obj *Object) {
	if scope.LookupLocal(obj.Ident) != nil {
		panic("obj already inserted");
	}
	scope.add(obj);
}

func (scope *Scope) InsertImport(obj *Object) *Object {
	 p := scope.LookupLocal(obj.Ident);
	 if p == nil {
		scope.add(obj);
		p = obj;
	 }
	 return p;
}

func (scope *Scope) Print() {
	print("scope {");
	for key := range scope.entries {
		print("\n  ", key);
	}
	print("\n}\n");
}

// ----------------------------------------------------------------------------
// Types

const /* form */ (
	// internal types
	// We should never see one of these.
	UNDEF = iota;

	// VOID types are used when we don't have a type. Never exported.
	// (exported type forms must be > 0)
	VOID;

	// BADTYPE types are compatible with any type and don't cause further errors.
	// They are introduced only as a result of an error in the source code. A
	// correct program cannot have BAD types.
	BADTYPE;

	// FORWARD types are forward-declared (incomplete) types. They can only
	// be used as element types of pointer types and must be resolved before
	// their internals are accessible.
	FORWARD;

	// TUPLE types represent multi-valued result types of functions and
	// methods.
	TUPLE;

	// The type of nil.
	NIL;

	// A type name
	TYPENAME;

	// basic types
	BOOL; UINT; INT; FLOAT; STRING; INTEGER;

	// composite types
	ALIAS; ARRAY; STRUCT; INTERFACE; MAP; CHANNEL; FUNCTION; METHOD; POINTER;

	// open-ended parameter type
	ELLIPSIS
)

func FormStr(form int) string {
	switch form {
	case VOID: return "VOID";
	case BADTYPE: return "BADTYPE";
	case FORWARD: return "FORWARD";
	case TUPLE: return "TUPLE";
	case NIL: return "NIL";
	case TYPENAME: return "TYPENAME";
	case BOOL: return "BOOL";
	case UINT: return "UINT";
	case INT: return "INT";
	case FLOAT: return "FLOAT";
	case STRING: return "STRING";
	case ALIAS: return "ALIAS";
	case ARRAY: return "ARRAY";
	case STRUCT: return "STRUCT";
	case INTERFACE: return "INTERFACE";
	case MAP: return "MAP";
	case CHANNEL: return "CHANNEL";
	case FUNCTION: return "FUNCTION";
	case METHOD: return "METHOD";
	case POINTER: return "POINTER";
	case ELLIPSIS: return "ELLIPSIS";
	}
	return "<unknown Type form>";
}

const /* channel mode */ (
	FULL = iota;
	SEND;
	RECV;
)

type Type struct {
	Id int;  // unique id

	Ref int;  // for exporting only: >= 0 means already exported
	Form int;  // type form
	Size int;  // size in bytes
	Obj *Object;  // primary type object or nil
	Scope *Scope;  // locals, fields & methods

	// syntactic components
	Pos int;  // source position (< 0 if unknown position)
	Len int;  // array length
	Mode int;  // channel mode
	Key *Type;  // receiver type or map key
	Elt *Type;  // type name type, array, map, channel or pointer element type, function result type
	List *vector.Vector; End int;  // struct fields, interface methods, function parameters
}

var typeId int;

func NewType(pos, form int) *Type {
	typ := new(Type);
	typ.Id = typeId;
	typeId++;

	typ.Ref = -1;  // not yet exported
	typ.Pos = pos;
	typ.Form = form;

	return typ;
}

func (typ* Type) String() string {
	if typ != nil {
		return
			"Type(" +
			FormStr(typ.Form) +
			")";
	}
	return "nil";
}

// ----------------------------------------------------------------------------
// Universe scope

var (
	Universe *Scope;
	PredeclaredTypes vector.Vector;

	// internal types
	Void_typ,
	Bad_typ,
	Nil_typ,

	// basic types
	Bool_typ,
	Uint8_typ,
	Uint16_typ,
	Uint32_typ,
	Uint64_typ,
	Int8_typ,
	Int16_typ,
	Int32_typ,
	Int64_typ,
	Float32_typ,
	Float64_typ,
	Float80_typ,
	String_typ,
	Integer_typ,

	// convenience types
	Byte_typ,
	Uint_typ,
	Int_typ,
	Float_typ,
	Uintptr_typ *Type;

	True_obj,
	False_obj,
	Iota_obj,
	Nil_obj *Object;
)

func declObj(kind int, ident string, typ *Type) *Object {
	obj := NewObject(-1 /* no source pos */, kind, ident);
	obj.Typ = typ;
	if kind == TYPE && typ.Obj == nil {
		typ.Obj = obj;  // set primary type object
	}
	Universe.Insert(obj);
	return obj
}

func declType(form int, ident string, size int) *Type {
  typ := NewType(-1 /* no source pos */, form);
  typ.Size = size;
  return declObj(TYPE, ident, typ).Typ;
}

func register(typ *Type) *Type {
	typ.Ref = PredeclaredTypes.Len();
	PredeclaredTypes.Push(typ);
	return typ;
}

func init() {
	Universe = NewScope(nil);  // universe has no parent
	PredeclaredTypes.Init(32);

	// Interal types
	Void_typ = NewType(-1 /* no source pos */, VOID);
	Universe_void_typ = Void_typ;
	Bad_typ = NewType(-1 /* no source pos */, BADTYPE);
	Nil_typ = NewType(-1 /* no source pos */, NIL);

	// Basic types
	Bool_typ = register(declType(BOOL, "bool", 1));
	Uint8_typ = register(declType(UINT, "uint8", 1));
	Uint16_typ = register(declType(UINT, "uint16", 2));
	Uint32_typ = register(declType(UINT, "uint32", 4));
	Uint64_typ = register(declType(UINT, "uint64", 8));
	Int8_typ = register(declType(INT, "int8", 1));
	Int16_typ = register(declType(INT, "int16", 2));
	Int32_typ = register(declType(INT, "int32", 4));
	Int64_typ = register(declType(INT, "int64", 8));
	Float32_typ = register(declType(FLOAT, "float32", 4));
	Float64_typ = register(declType(FLOAT, "float64", 8));
	Float80_typ = register(declType(FLOAT, "float80", 10));
	String_typ = register(declType(STRING, "string", 8));
	Integer_typ = register(declType(INTEGER, "integer", 8));

	// All but 'byte' should be platform-dependent, eventually.
	Byte_typ = register(declType(UINT, "byte", 1));
	Uint_typ = register(declType(UINT, "uint", 4));
	Int_typ = register(declType(INT, "int", 4));
	Float_typ = register(declType(FLOAT, "float", 4));
	Uintptr_typ = register(declType(UINT, "uintptr", 8));

	// Predeclared constants
	True_obj = declObj(CONST, "true", Bool_typ);
	False_obj = declObj(CONST, "false", Bool_typ);
	Iota_obj = declObj(CONST, "iota", Int_typ);
	Nil_obj = declObj(CONST, "nil", Nil_typ);

	// Builtin functions
	declObj(BUILTIN, "len", Void_typ);
	declObj(BUILTIN, "new", Void_typ);
	declObj(BUILTIN, "panic", Void_typ);
	declObj(BUILTIN, "print", Void_typ);

	// scope.Print();
}
<|endoftext|>


<commit_before>package extract

import (
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/koffeinsource/kaffeeshare/data"
)

func youtube(i *data.Item, sourceURL string, doc *goquery.Document, log logger) {
	if !strings.Contains(sourceURL, "www.youtube.com") {
		return
	}

	log.Infof("Running Youtube plugin.")

	// update title

	videoIDstart := strings.Index(i.URL, "v=")
	if videoIDstart == -1 {
		log.Infof("Youtube plugin found no video ID. " + sourceURL)
		return
	}
	videoIDstart += 2 // ID is after 'v='
	videoID := i.URL[videoIDstart:]
	i.Description += "

"

	i.ImageURL = ""
}
<commit_msg>Updated youtube plugin so it uses https instead of http.<commit_after>package extract

import (
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/koffeinsource/kaffeeshare/data"
)

func youtube(i *data.Item, sourceURL string, doc *goquery.Document, log logger) {
	if !strings.Contains(sourceURL, "www.youtube.com") {
		return
	}

	log.Infof("Running Youtube plugin.")

	// update title

	videoIDstart := strings.Index(i.URL, "v=")
	if videoIDstart == -1 {
		log.Infof("Youtube plugin found no video ID. " + sourceURL)
		return
	}
	videoIDstart += 2 // ID is after 'v='
	videoID := i.URL[videoIDstart:]
	i.Description += "

"

	i.ImageURL = ""
}
<|endoftext|>


<commit_before>package rethinkdb

import (
	"fmt"
	"log"
	"os"

	r "github.com/dancannon/gorethink"
)

var s *r.Session

// GetSession retyrbs a pointer to a `gorethink` Session struct which can be
// used across manu goroutines
func GetSession() *r.Session {
	return s
}

// SetupDatabase establishes database session connection to RethinkDB using the
// the default localhost:28015 or from the environment variables
// DATABASE_PORT_28015_TCP_ADDR and DATABASE_PORT_28015_TCP_PORT for address and
// port respectively
func SetupDatabase() {
	var err error
	var dbhost, dbport string

	dbhost = os.Getenv("DATABASE_PORT_28015_TCP_ADDR")
	dbport = os.Getenv("DATABASE_PORT_28015_TCP_PORT")
	if dbhost != "" && dbport != "" {
		log.Println("Using environment variables DATABASE_PORT_28015_TCP ...")
	} else {
		dbhost = "localhost"
		dbport = "28015"
	}

	dbpeer := fmt.Sprintf("%s:%s", dbhost, dbport)
	log.Printf("Connecting to %s ...", dbpeer)

	s, err = r.Connect(r.ConnectOpts{
		Address:       dbpeer,
		Database:      "pkr",
		DiscoverHosts: true,
	})
	if err != nil {
		log.Fatalf("Could not establish database connection: %s", err)
	}

	log.Println("Database connected")
}
<commit_msg>Rename SetupDatabase() to ConnectDatabase()<commit_after>package rethinkdb

import (
	"fmt"
	"log"
	"os"

	r "github.com/dancannon/gorethink"
)

var s *r.Session

// GetSession retyrbs a pointer to a `gorethink` Session struct which can be
// used across manu goroutines
func GetSession() *r.Session {
	return s
}

// ConnectDatabase establishes database session connection to RethinkDB using the
// the default localhost:28015 or from the environment variables
// DATABASE_PORT_28015_TCP_ADDR and DATABASE_PORT_28015_TCP_PORT for address and
// port respectively
func ConnectDatabase() {
	var err error
	var dbhost, dbport string

	dbhost = os.Getenv("DATABASE_PORT_28015_TCP_ADDR")
	dbport = os.Getenv("DATABASE_PORT_28015_TCP_PORT")
	if dbhost != "" && dbport != "" {
		log.Println("Using environment variables DATABASE_PORT_28015_TCP ...")
	} else {
		dbhost = "localhost"
		dbport = "28015"
	}

	dbpeer := fmt.Sprintf("%s:%s", dbhost, dbport)
	log.Printf("Connecting to %s ...", dbpeer)

	s, err = r.Connect(r.ConnectOpts{
		Address:       dbpeer,
		Database:      "pkr",
		DiscoverHosts: true,
	})
	if err != nil {
		log.Fatalf("Could not establish database connection: %s", err)
	}

	log.Println("Database connected")
}
<|endoftext|>


<commit_before>package tfjson

import (
	"encoding/json"
	"errors"
	"fmt"

	"github.com/zclconf/go-cty/cty"
)

// ProviderSchemasFormatVersion is the version of the JSON provider
// schema format that is supported by this package.
const ProviderSchemasFormatVersion = "0.1"

// ProviderSchemas represents the schemas of all providers and
// resources in use by the configuration.
type ProviderSchemas struct {
	// The version of the plan format. This should always match the
	// ProviderSchemasFormatVersion constant in this package, or else
	// an unmarshal will be unstable.
	FormatVersion string `json:"format_version,omitempty"`

	// The schemas for the providers in this configuration, indexed by
	// provider type. Aliases are not included, and multiple instances
	// of a provider in configuration will be represented by a single
	// provider here.
	Schemas map[string]*ProviderSchema `json:"provider_schemas,omitempty"`
}

// Validate checks to ensure that ProviderSchemas is present, and the
// version matches the version supported by this library.
func (p *ProviderSchemas) Validate() error {
	if p == nil {
		return errors.New("provider schema data is nil")
	}

	if p.FormatVersion == "" {
		return errors.New("unexpected provider schema data, format version is missing")
	}

	if ProviderSchemasFormatVersion != p.FormatVersion {
		return fmt.Errorf("unsupported provider schema data format version: expected %q, got %q", PlanFormatVersion, p.FormatVersion)
	}

	return nil
}

func (p *ProviderSchemas) UnmarshalJSON(b []byte) error {
	type rawSchemas ProviderSchemas
	var schemas rawSchemas

	err := json.Unmarshal(b, &schemas)
	if err != nil {
		return err
	}

	*p = *(*ProviderSchemas)(&schemas)

	return p.Validate()
}

// ProviderSchema is the JSON representation of the schema of an
// entire provider, including the provider configuration and any
// resources and data sources included with the provider.
type ProviderSchema struct {
	// The schema for the provider's configuration.
	ConfigSchema *Schema `json:"provider,omitempty"`

	// The schemas for any resources in this provider.
	ResourceSchemas map[string]*Schema `json:"resource_schemas,omitempty"`

	// The schemas for any data sources in this provider.
	DataSourceSchemas map[string]*Schema `json:"data_source_schemas,omitempty"`
}

// Schema is the JSON representation of a particular schema
// (provider configuration, resources, data sources).
type Schema struct {
	// The version of the particular resource schema.
	Version uint64 `json:"version"`

	// The root-level block of configuration values.
	Block *SchemaBlock `json:"block,omitempty"`
}

// SchemaDescriptionKind describes the format type for a particular description's field.
type SchemaDescriptionKind string

const (
	// SchemaDescriptionKindPlain indicates a string in plain text format.
	SchemaDescriptionKindPlain SchemaDescriptionKind = "plain"

	// SchemaDescriptionKindMarkdown indicates a Markdown string and may need to be
	// processed prior to presentation.
	SchemaDescriptionKindMarkdown SchemaDescriptionKind = "markdown"
)

// SchemaBlock represents a nested block within a particular schema.
type SchemaBlock struct {
	// The attributes defined at the particular level of this block.
	Attributes map[string]*SchemaAttribute `json:"attributes,omitempty"`

	// Any nested blocks within this particular block.
	NestedBlocks map[string]*SchemaBlockType `json:"block_types,omitempty"`

	// The description for this block and format of the description. If
	// no kind is provided, it can be assumed to be plain text.
	Description     string                `json:"description,omitempty"`
	DescriptionKind SchemaDescriptionKind `json:"description_kind,omitempty"`

	// If true, this block is deprecated.
	Deprecated bool `json:"deprecated,omitempty"`
}

// SchemaNestingMode is the nesting mode for a particular nested
// schema block.
type SchemaNestingMode string

const (
	// SchemaNestingModeSingle denotes single block nesting mode, which
	// allows a single block of this specific type only in
	// configuration. This is generally the same as list or set types
	// with a single-element constraint.
	SchemaNestingModeSingle SchemaNestingMode = "single"

	// SchemaNestingModeList denotes list block nesting mode, which
	// allows an ordered list of blocks where duplicates are allowed.
	SchemaNestingModeList SchemaNestingMode = "list"

	// SchemaNestingModeSet denotes set block nesting mode, which
	// allows an unordered list of blocks where duplicates are
	// generally not allowed. What is considered a duplicate is up to
	// the rules of the set itself, which may or may not cover all
	// fields in the block.
	SchemaNestingModeSet SchemaNestingMode = "set"

	// SchemaNestingModeMap denotes map block nesting mode. This
	// creates a map of all declared blocks of the block type within
	// the parent, keying them on the label supplied in the block
	// declaration. This allows for blocks to be declared in the same
	// style as resources.
	SchemaNestingModeMap SchemaNestingMode = "map"
)

// SchemaBlockType describes a nested block within a schema.
type SchemaBlockType struct {
	// The nesting mode for this block.
	NestingMode SchemaNestingMode `json:"nesting_mode,omitempty"`

	// The block data for this block type, including attributes and
	// subsequent nested blocks.
	Block *SchemaBlock `json:"block,omitempty"`

	// The lower limit on items that can be declared of this block
	// type.
	MinItems uint64 `json:"min_items,omitempty"`

	// The upper limit on items that can be declared of this block
	// type.
	MaxItems uint64 `json:"max_items,omitempty"`
}

// SchemaAttribute describes an attribute within a schema block.
type SchemaAttribute struct {
	// The attribute type.
	AttributeType cty.Type `json:"type,omitempty"`

	// The description field for this attribute. If no kind is
	// provided, it can be assumed to be plain text.
	Description     string                `json:"description,omitempty"`
	DescriptionKind SchemaDescriptionKind `json:"description_kind,omitempty"`

	// If true, this attribute is deprecated.
	Deprecated bool `json:"deprecated,omitempty"`

	// If true, this attribute is required - it has to be entered in
	// configuration.
	Required bool `json:"required,omitempty"`

	// If true, this attribute is optional - it does not need to be
	// entered in configuration.
	Optional bool `json:"optional,omitempty"`

	// If true, this attribute is computed - it can be set by the
	// provider. It may also be set by configuration if Optional is
	// true.
	Computed bool `json:"computed,omitempty"`

	// If true, this attribute is sensitive and will not be displayed
	// in logs. Future versions of Terraform may encrypt or otherwise
	// treat these values with greater care than non-sensitive fields.
	Sensitive bool `json:"sensitive,omitempty"`
}
<commit_msg>Reflect v0.15 schema changes (#28)<commit_after>package tfjson

import (
	"encoding/json"
	"errors"
	"fmt"

	"github.com/zclconf/go-cty/cty"
)

// ProviderSchemasFormatVersion is the version of the JSON provider
// schema format that is supported by this package.
const ProviderSchemasFormatVersion = "0.2"

// ProviderSchemas represents the schemas of all providers and
// resources in use by the configuration.
type ProviderSchemas struct {
	// The version of the plan format. This should always match the
	// ProviderSchemasFormatVersion constant in this package, or else
	// an unmarshal will be unstable.
	FormatVersion string `json:"format_version,omitempty"`

	// The schemas for the providers in this configuration, indexed by
	// provider type. Aliases are not included, and multiple instances
	// of a provider in configuration will be represented by a single
	// provider here.
	Schemas map[string]*ProviderSchema `json:"provider_schemas,omitempty"`
}

// Validate checks to ensure that ProviderSchemas is present, and the
// version matches the version supported by this library.
func (p *ProviderSchemas) Validate() error {
	if p == nil {
		return errors.New("provider schema data is nil")
	}

	if p.FormatVersion == "" {
		return errors.New("unexpected provider schema data, format version is missing")
	}

	oldVersion := "0.1"
	if p.FormatVersion != ProviderSchemasFormatVersion && p.FormatVersion != oldVersion {
		return fmt.Errorf("unsupported provider schema data format version: expected %q or %q, got %q",
			PlanFormatVersion, oldVersion, p.FormatVersion)
	}

	return nil
}

func (p *ProviderSchemas) UnmarshalJSON(b []byte) error {
	type rawSchemas ProviderSchemas
	var schemas rawSchemas

	err := json.Unmarshal(b, &schemas)
	if err != nil {
		return err
	}

	*p = *(*ProviderSchemas)(&schemas)

	return p.Validate()
}

// ProviderSchema is the JSON representation of the schema of an
// entire provider, including the provider configuration and any
// resources and data sources included with the provider.
type ProviderSchema struct {
	// The schema for the provider's configuration.
	ConfigSchema *Schema `json:"provider,omitempty"`

	// The schemas for any resources in this provider.
	ResourceSchemas map[string]*Schema `json:"resource_schemas,omitempty"`

	// The schemas for any data sources in this provider.
	DataSourceSchemas map[string]*Schema `json:"data_source_schemas,omitempty"`
}

// Schema is the JSON representation of a particular schema
// (provider configuration, resources, data sources).
type Schema struct {
	// The version of the particular resource schema.
	Version uint64 `json:"version"`

	// The root-level block of configuration values.
	Block *SchemaBlock `json:"block,omitempty"`
}

// SchemaDescriptionKind describes the format type for a particular description's field.
type SchemaDescriptionKind string

const (
	// SchemaDescriptionKindPlain indicates a string in plain text format.
	SchemaDescriptionKindPlain SchemaDescriptionKind = "plain"

	// SchemaDescriptionKindMarkdown indicates a Markdown string and may need to be
	// processed prior to presentation.
	SchemaDescriptionKindMarkdown SchemaDescriptionKind = "markdown"
)

// SchemaBlock represents a nested block within a particular schema.
type SchemaBlock struct {
	// The attributes defined at the particular level of this block.
	Attributes map[string]*SchemaAttribute `json:"attributes,omitempty"`

	// Any nested blocks within this particular block.
	NestedBlocks map[string]*SchemaBlockType `json:"block_types,omitempty"`

	// The description for this block and format of the description. If
	// no kind is provided, it can be assumed to be plain text.
	Description     string                `json:"description,omitempty"`
	DescriptionKind SchemaDescriptionKind `json:"description_kind,omitempty"`

	// If true, this block is deprecated.
	Deprecated bool `json:"deprecated,omitempty"`
}

// SchemaNestingMode is the nesting mode for a particular nested
// schema block.
type SchemaNestingMode string

const (
	// SchemaNestingModeSingle denotes single block nesting mode, which
	// allows a single block of this specific type only in
	// configuration. This is generally the same as list or set types
	// with a single-element constraint.
	SchemaNestingModeSingle SchemaNestingMode = "single"

	// SchemaNestingModeGroup is similar to SchemaNestingModeSingle in that it
	// calls for only a single instance of a given block type with no labels,
	// but it additonally guarantees that its result will never be null,
	// even if the block is absent, and instead the nested attributes
	// and blocks will be treated as absent in that case.
	//
	// This is useful for the situation where a remote API has a feature that
	// is always enabled but has a group of settings related to that feature
	// that themselves have default values. By using SchemaNestingModeGroup
	// instead of SchemaNestingModeSingle in that case, generated plans will
	// show the block as present even when not present in configuration,
	// thus allowing any default values within to be displayed to the user.
	SchemaNestingModeGroup SchemaNestingMode = "group"

	// SchemaNestingModeList denotes list block nesting mode, which
	// allows an ordered list of blocks where duplicates are allowed.
	SchemaNestingModeList SchemaNestingMode = "list"

	// SchemaNestingModeSet denotes set block nesting mode, which
	// allows an unordered list of blocks where duplicates are
	// generally not allowed. What is considered a duplicate is up to
	// the rules of the set itself, which may or may not cover all
	// fields in the block.
	SchemaNestingModeSet SchemaNestingMode = "set"

	// SchemaNestingModeMap denotes map block nesting mode. This
	// creates a map of all declared blocks of the block type within
	// the parent, keying them on the label supplied in the block
	// declaration. This allows for blocks to be declared in the same
	// style as resources.
	SchemaNestingModeMap SchemaNestingMode = "map"
)

// SchemaBlockType describes a nested block within a schema.
type SchemaBlockType struct {
	// The nesting mode for this block.
	NestingMode SchemaNestingMode `json:"nesting_mode,omitempty"`

	// The block data for this block type, including attributes and
	// subsequent nested blocks.
	Block *SchemaBlock `json:"block,omitempty"`

	// The lower limit on items that can be declared of this block
	// type.
	MinItems uint64 `json:"min_items,omitempty"`

	// The upper limit on items that can be declared of this block
	// type.
	MaxItems uint64 `json:"max_items,omitempty"`
}

// SchemaAttribute describes an attribute within a schema block.
type SchemaAttribute struct {
	// The attribute type
	// Either AttributeType or AttributeNestedType is set, never both.
	AttributeType cty.Type `json:"type,omitempty"`

	// Details about a nested attribute type
	// Either AttributeType or AttributeNestedType is set, never both.
	AttributeNestedType *SchemaNestedAttributeType `json:"nested_type,omitempty"`

	// The description field for this attribute. If no kind is
	// provided, it can be assumed to be plain text.
	Description     string                `json:"description,omitempty"`
	DescriptionKind SchemaDescriptionKind `json:"description_kind,omitempty"`

	// If true, this attribute is deprecated.
	Deprecated bool `json:"deprecated,omitempty"`

	// If true, this attribute is required - it has to be entered in
	// configuration.
	Required bool `json:"required,omitempty"`

	// If true, this attribute is optional - it does not need to be
	// entered in configuration.
	Optional bool `json:"optional,omitempty"`

	// If true, this attribute is computed - it can be set by the
	// provider. It may also be set by configuration if Optional is
	// true.
	Computed bool `json:"computed,omitempty"`

	// If true, this attribute is sensitive and will not be displayed
	// in logs. Future versions of Terraform may encrypt or otherwise
	// treat these values with greater care than non-sensitive fields.
	Sensitive bool `json:"sensitive,omitempty"`
}

// SchemaNestedAttributeType describes a nested attribute
// which could also be just expressed simply as cty.Object(...),
// cty.List(cty.Object(...)) etc. but this allows tracking additional
// metadata which can help interpreting or validating the data.
type SchemaNestedAttributeType struct {
	// A map of nested attributes
	Attributes map[string]*SchemaAttribute `json:"attributes,omitempty"`

	// The nesting mode for this attribute.
	NestingMode string `json:"nesting_mode,omitempty"`

	// The lower limit on number of items that can be declared
	// of this attribute type.
	MinItems uint64 `json:"min_items,omitempty"`

	// The upper limit on number of items that can be declared
	// of this attribute type.
	MaxItems uint64 `json:"max_items,omitempty"`
}
<|endoftext|>


<commit_before>//watch handles reloading of a command by watching a directory and if supplied a set of given extensions for change
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/howeyc/fsnotify"
)

var multispaces = regexp.MustCompile(`\s+`)

func goDeps(targetdir string) (bool, error) {
	cmdline := []string{"go", "get"}

	cmdline = append(cmdline, targetdir)

	//setup the executor and use a shard buffer
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err := cmd.Run()

	if buf.Len() > 0 {
		return false, fmt.Errorf("go install failed: %s: %s", buf.String(), err.Error())
	}

	return true, nil
}

//goRun runs the runs a command
func goRun(cmd string) string {
	var cmdline []string
	com := strings.Split(cmd, " ")

	if len(com) < 0 {
		return ""
	}

	if len(com) == 1 {
		cmdline = append(cmdline, com...)
	} else {
		cmdline = append(cmdline, com[0])
		cmdline = append(cmdline, com[1:]...)
	}

	//setup the executor and use a shard buffer
	cmdo := exec.Command(cmdline[0], cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmdo.Stdout = buf
	cmdo.Stderr = buf

	_ = cmdo.Run()

	return buf.String()
}

//gobuild runs the build process and returns true/false and an error
func gobuild(dir, name string) (bool, error) {
	cmdline := []string{"go", "build"}

	if runtime.GOOS == "windows" {
		name = fmt.Sprintf("%s.exe", name)
	}

	target := filepath.Join(dir, name)
	cmdline = append(cmdline, "-o", target)

	//setup the executor and use a shard buffer
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err := cmd.Run()

	if buf.Len() > 0 {
		return false, fmt.Errorf("go build failed: %s: %s", buf.String(), err.Error())
	}

	return true, nil
}

// runBin runs the generated bin file with the arguments expected
func runBin(bindir, bin string, args []string) chan bool {
	var relunch = make(chan bool)
	go func() {
		binfile := fmt.Sprintf("%s/%s", bindir, bin)
		// cmdline := append([]string{bin}, args...)
		var proc *os.Process

		for dosig := range relunch {
			if proc != nil {
				if err := proc.Signal(os.Interrupt); err != nil {
					log.Printf("Error in sending signal %s", err)
					proc.Kill()
				}
				proc.Wait()
			}

			if !dosig {
				continue
			}

			cmd := exec.Command(binfile, args...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr

			if err := cmd.Start(); err != nil {
				log.Printf("Error starting process: %s", err)
			}

			proc = cmd.Process
		}
	}()
	return relunch
}

func buildPkgWatcher(pkpath string, assets map[string]bool) (*fsnotify.Watcher, error) {
	ws, err := fsnotify.NewWatcher()
	add2Watcher(ws, pkpath, assets)
	return ws, err
}

func buildWatcher(pkpath string) (*fsnotify.Watcher, error) {
	ws, err := fsnotify.NewWatcher()
	ws.Watch(pkpath)
	return ws, err
}

func hasIn(paths []string, dt string) bool {
	for _, so := range paths {
		if strings.Contains(so, dt) || so == dt {
			return true
		}
	}
	return false
}

func watchDir(ws *fsnotify.Watcher, dir string, assets map[string]bool, skip []string) {

	mo, err := os.Stat(dir)

	if err != nil {
		return
	}

	if !mo.IsDir() {
		return
	}

	filepath.Walk(filepath.ToSlash(dir), func(path string, info os.FileInfo, err error) error {

		if strings.Contains(path, ".git") {
			return nil
		}

		if info == nil {
			return nil
		}

		if hasIn(skip, path) {
			return nil
		}

		// if !info.IsDir() {
		// 	return nil
		// }

		if assets[path] {
			return nil
		}

		ws.Watch(path)
		assets[path] = true
		return nil
	})
}

func add2Watcher(ws *fsnotify.Watcher, pkgpath string, assets map[string]bool) {
	pkg, err := build.Import(pkgpath, "", 0)

	if err != nil {
		return
	}

	if pkg.Goroot {
		return
	}

	ws.Watch(pkg.Dir)
	assets[pkgpath] = true

	for _, imp := range pkg.Imports {
		if !assets[imp] {
			add2Watcher(ws, imp, assets)
		}
	}
}

func watch(command, importable, bin, exts string, dobuild, withdir bool, args []string) error {
	log.Printf("Command: %s %s %s %t", command, importable, bin, dobuild)

	extcls := multispaces.ReplaceAllString(exts, " ")
	extens := multispaces.Split(extcls, -1)

	if len(extens) == 1 && extens[0] == "" {
		extens = extens[:0]
	}

	var buildName string
	var ubin string

	var buildHandler = func() error {
		var pkgs *build.Package
		var err error
		pkgs, err = build.Import(importable, "", 0)

		if err != nil {
			return err
		}

		_, buildName = path.Split(pkgs.ImportPath)
		// _, buildName := path.Split("./")

		wd, _ := os.Getwd()
		if bin != "" {
			ubin = filepath.ToSlash(filepath.Join(wd, bin))
		} else {
			ubin = pkgs.BinDir
		}

		// lets install
		_, err = goDeps("./")

		if err != nil {
			return err
		}

		log.Printf("Building Pkg %s \n Bin: %s \nUsing name: %s", pkgs.ImportPath, ubin, buildName)

		done, err := gobuild(ubin, buildName)

		if err != nil {
			return err
		}

		_ = done
		return nil
	}

	var buildWatch = func() (*fsnotify.Watcher, error) {
		var err error
		var watch *fsnotify.Watcher

		added := make(map[string]bool)

		if dobuild {
			watch, err = buildPkgWatcher(importable, added)
		} else {
			if !added["./"] {
				watch, err = buildWatcher("./")
			}
		}

		//lets watch the current directory also if allowed
		if withdir && err == nil {
			wod, ex := os.Getwd()

			if ex == nil && wod != "" {
				watchDir(watch, wod, added, []string{ubin})
			}
		}

		return watch, err
	}

	var err error
	var watch *fsnotify.Watcher
	var binRun bool
	var binChan chan bool

	//lets build if we are allowed
	if dobuild {
		if err = buildHandler(); err != nil {
			return err
		}

		binChan = runBin(ubin, buildName, args)
		binRun = true
		binChan <- true
	}

	log.Printf("Building dir watchers.....")
	watch, err = buildWatch()

	if err != nil {
		log.Printf("Unable to build err %s", err.Error())
		return err
	}

	for {

		//should we watch
		we, _ := <-watch.Event

		exo := filepath.Ext(we.Name)

		//if its a .git directory skip it
		if strings.Contains(filepath.ToSlash(we.Name), ".git") {
			continue
		}

		//if its our bin directory skip it
		if filepath.ToSlash(we.Name) == filepath.ToSlash(ubin) {
			continue
		}

		log.Printf("Watch: %s -> %s with extensions: %s", exo, we.Name, extens)

		if len(extens) > 0 {
			var found bool
			for _, mo := range extens {
				if exo == mo {
					found = true
					break
				}
			}

			if !found {
				continue
			}
		}

		log.Printf("Watcher notified change: %s", we.Name)

		watch.Close()

		go func(evs chan *fsnotify.FileEvent) {
			for _ = range evs {
			}
		}(watch.Event)

		log.Printf("Re-initiating watch scans .....")

		if command != "" {
			log.Printf("Running cmd '%s' with result: '%s'", command, goRun(command))
		}

		if dobuild {
			if err = buildHandler(); err != nil {
				return err
			}

			if binRun {
				binChan <- true
			}
		}

		watch, err = buildWatch()

		if err != nil {
			return err
		}

		go func(errors chan error) {
			for _ = range errors {
			}
		}(watch.Error)

	}
}

func usage() {
	fmt.Printf(`Watch:
    About: provides a simple but combined go dir builder and file watcher
    Version: %s
    Usage: watch [--import] <import path> [--cmd] <cmd_to_rerun> [--ext] <extensions> [--bin] <bin path to store> --dir
    `, version)
}

var version = "0.0.1"

func main() {
	exts := flag.String("ext", "", "a space seperated string of extensions to watch")
	cmd := flag.String("cmd", "", "Command to run instead on every change")
	withdir := flag.Bool("dir", false, "This sets the current directories and subdirectories to be watched")
	bindir := flag.String("bin", "./bin", "The build directory for storing the build file")
	importdir := flag.String("import", "", "Command to run instead on every change")

	flag.Parse()

	if *cmd == "" && *importdir == "" {
		usage()
		return
	}

	build := (*importdir != "")

	err := watch(*cmd, *importdir, *bindir, *exts, build, *withdir, flag.Args())

	if err != nil {
		log.Printf("Errored: %s", err.Error())
	}
}
<commit_msg>fixing bin checks<commit_after>//watch handles reloading of a command by watching a directory and if supplied a set of given extensions for change
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/howeyc/fsnotify"
)

var multispaces = regexp.MustCompile(`\s+`)

func goDeps(targetdir string) (bool, error) {
	cmdline := []string{"go", "get"}

	cmdline = append(cmdline, targetdir)

	//setup the executor and use a shard buffer
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err := cmd.Run()

	if buf.Len() > 0 {
		return false, fmt.Errorf("go install failed: %s: %s", buf.String(), err.Error())
	}

	return true, nil
}

//goRun runs the runs a command
func goRun(cmd string) string {
	var cmdline []string
	com := strings.Split(cmd, " ")

	if len(com) < 0 {
		return ""
	}

	if len(com) == 1 {
		cmdline = append(cmdline, com...)
	} else {
		cmdline = append(cmdline, com[0])
		cmdline = append(cmdline, com[1:]...)
	}

	//setup the executor and use a shard buffer
	cmdo := exec.Command(cmdline[0], cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmdo.Stdout = buf
	cmdo.Stderr = buf

	_ = cmdo.Run()

	return buf.String()
}

//gobuild runs the build process and returns true/false and an error
func gobuild(dir, name string) (bool, error) {
	cmdline := []string{"go", "build"}

	if runtime.GOOS == "windows" {
		name = fmt.Sprintf("%s.exe", name)
	}

	target := filepath.Join(dir, name)
	cmdline = append(cmdline, "-o", target)

	//setup the executor and use a shard buffer
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err := cmd.Run()

	if buf.Len() > 0 {
		return false, fmt.Errorf("go build failed: %s: %s", buf.String(), err.Error())
	}

	return true, nil
}

// runBin runs the generated bin file with the arguments expected
func runBin(bindir, bin string, args []string) chan bool {
	var relunch = make(chan bool)
	go func() {
		binfile := fmt.Sprintf("%s/%s", bindir, bin)
		// cmdline := append([]string{bin}, args...)
		var proc *os.Process

		for dosig := range relunch {
			if proc != nil {
				if err := proc.Signal(os.Interrupt); err != nil {
					log.Printf("Error in sending signal %s", err)
					proc.Kill()
				}
				proc.Wait()
			}

			if !dosig {
				continue
			}

			cmd := exec.Command(binfile, args...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr

			if err := cmd.Start(); err != nil {
				log.Printf("Error starting process: %s", err)
			}

			proc = cmd.Process
		}
	}()
	return relunch
}

func buildPkgWatcher(pkpath string, assets map[string]bool) (*fsnotify.Watcher, error) {
	ws, err := fsnotify.NewWatcher()
	add2Watcher(ws, pkpath, assets)
	return ws, err
}

func buildWatcher(pkpath string) (*fsnotify.Watcher, error) {
	ws, err := fsnotify.NewWatcher()
	ws.Watch(pkpath)
	return ws, err
}

func hasIn(paths []string, dt string) bool {
	for _, so := range paths {
		if strings.Contains(so, dt) || so == dt {
			return true
		}
	}
	return false
}

func watchDir(ws *fsnotify.Watcher, dir string, assets map[string]bool, skip []string) {

	mo, err := os.Stat(dir)

	if err != nil {
		return
	}

	if !mo.IsDir() {
		return
	}

	filepath.Walk(filepath.ToSlash(dir), func(path string, info os.FileInfo, err error) error {

		if strings.Contains(path, ".git") {
			return nil
		}

		if info == nil {
			return nil
		}

		if hasIn(skip, path) {
			return nil
		}

		// if !info.IsDir() {
		// 	return nil
		// }

		if assets[path] {
			return nil
		}

		ws.Watch(path)
		assets[path] = true
		return nil
	})
}

func add2Watcher(ws *fsnotify.Watcher, pkgpath string, assets map[string]bool) {
	pkg, err := build.Import(pkgpath, "", 0)

	if err != nil {
		return
	}

	if pkg.Goroot {
		return
	}

	ws.Watch(pkg.Dir)
	assets[pkgpath] = true

	for _, imp := range pkg.Imports {
		if !assets[imp] {
			add2Watcher(ws, imp, assets)
		}
	}
}

func watch(command, importable, bin, exts string, dobuild, withdir bool, args []string) error {
	log.Printf("Command: %s %s %s %t", command, importable, bin, dobuild)

	extcls := multispaces.ReplaceAllString(exts, " ")
	extens := multispaces.Split(extcls, -1)

	if len(extens) == 1 && extens[0] == "" {
		extens = extens[:0]
	}

	var buildName string
	var ubin string

	var buildHandler = func() error {
		var pkgs *build.Package
		var err error
		pkgs, err = build.Import(importable, "", 0)

		if err != nil {
			return err
		}

		_, buildName = path.Split(pkgs.ImportPath)
		// _, buildName := path.Split("./")

		wd, _ := os.Getwd()
		if bin != "" {
			ubin = filepath.ToSlash(filepath.Join(wd, bin))
		} else {
			ubin = pkgs.BinDir
		}

		// lets install
		_, err = goDeps("./")

		if err != nil {
			return err
		}

		log.Printf("Building Pkg %s \nBin: %s \nUsing name: %s", pkgs.ImportPath, ubin, buildName)

		done, err := gobuild(ubin, buildName)

		if err != nil {
			return err
		}

		_ = done
		return nil
	}

	var buildWatch = func() (*fsnotify.Watcher, error) {
		var err error
		var watch *fsnotify.Watcher

		added := make(map[string]bool)

		if dobuild {
			watch, err = buildPkgWatcher(importable, added)
		} else {
			if !added["./"] {
				watch, err = buildWatcher("./")
			}
		}

		//lets watch the current directory also if allowed
		if withdir && err == nil {
			wod, ex := os.Getwd()

			if ex == nil && wod != "" {
				watchDir(watch, wod, added, []string{ubin})
			}
		}

		return watch, err
	}

	var err error
	var watch *fsnotify.Watcher
	var binRun bool
	var binChan chan bool

	//lets build if we are allowed
	if dobuild {
		if err = buildHandler(); err != nil {
			return err
		}

		binChan = runBin(ubin, buildName, args)
		binRun = true
		binChan <- true
	}

	log.Printf("Building dir watchers.....")
	watch, err = buildWatch()

	if err != nil {
		log.Printf("Unable to build err %s", err.Error())
		return err
	}

	for {

		//should we watch
		we, _ := <-watch.Event

		exo := filepath.Ext(we.Name)

		//if its a .git directory skip it
		if strings.Contains(filepath.ToSlash(we.Name), ".git") {
			continue
		}

		//if its our bin directory skip it
		if filepath.ToSlash(we.Name) == filepath.ToSlash(ubin) {
			continue
		}

		log.Printf("Watch: %s -> %s with extensions: %s", exo, we.Name, extens)

		if len(extens) > 0 {
			var found bool
			for _, mo := range extens {
				if exo == mo {
					found = true
					break
				}
			}

			if !found {
				continue
			}
		}

		log.Printf("Watcher notified change: %s", we.Name)

		watch.Close()

		go func(evs chan *fsnotify.FileEvent) {
			for _ = range evs {
			}
		}(watch.Event)

		log.Printf("Re-initiating watch scans .....")

		if command != "" {
			log.Printf("Running cmd '%s' with result: '%s'", command, goRun(command))
		}

		if dobuild {
			if err = buildHandler(); err != nil {
				return err
			}

			if binRun {
				binChan <- true
			}
		}

		watch, err = buildWatch()

		if err != nil {
			return err
		}

		go func(errors chan error) {
			for _ = range errors {
			}
		}(watch.Error)

	}
}

func usage() {
	fmt.Printf(`Watch:
    About: provides a simple but combined go dir builder and file watcher
    Version: %s
    Usage: watch [--import] <import path> [--cmd] <cmd_to_rerun> [--ext] <extensions> [--bin] <bin path to store> --dir
    `, version)
}

var version = "0.0.1"

func main() {
	exts := flag.String("ext", "", "a space seperated string of extensions to watch")
	cmd := flag.String("cmd", "", "Command to run instead on every change")
	withdir := flag.Bool("dir", false, "This sets the current directories and subdirectories to be watched")
	bindir := flag.String("bin", "./bin", "The build directory for storing the build file")
	importdir := flag.String("import", "", "Command to run instead on every change")

	flag.Parse()

	if *cmd == "" && *importdir == "" {
		usage()
		return
	}

	build := (*importdir != "")

	err := watch(*cmd, *importdir, *bindir, *exts, build, *withdir, flag.Args())

	if err != nil {
		log.Printf("Errored: %s", err.Error())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/howeyc/fsnotify"
	"log"
	"os"
	"os/exec"
	"sync"
	"time"
	"strings"
)

var (
	cmd       *exec.Cmd
	state     sync.Mutex
	eventTime = make(map[string]time.Time)
)

func NewWatcher(paths []string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		for {
			select {
			case e := <-watcher.Event:
				isbuild := true
				if t, ok := eventTime[e.String()]; ok {
					// if 500ms change many times, then ignore it.
					// for liteide often gofmt code after save.
					if t.Add(time.Millisecond * 500).After(time.Now()) {
						isbuild = false
					}
				}
				eventTime[e.String()] = time.Now()

				if isbuild {
					fmt.Println(e)
					go Autobuild()
				}
			case err := <-watcher.Error:
				log.Fatal("error:", err)
			}
		}
	}()
	for _, path := range paths {
		fmt.Println(path)
		err = watcher.Watch(path)
		if err != nil {
			log.Fatal(err)
		}
	}

}

func Autobuild() {
	state.Lock()
	defer state.Unlock()

	fmt.Println("start autobuild")
	path, _ := os.Getwd()
	os.Chdir(path)
	bcmd := exec.Command("go", "build")
	bcmd.Stdout = os.Stdout
	bcmd.Stderr = os.Stderr
	err := bcmd.Run()

	if err != nil {
		fmt.Println("============== build failed ===================")
		return
	}
	fmt.Println("build success")
	Restart(appname)
}

func Kill() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("Kill -> ", e)
		}
	}()
	if cmd != nil {
		cmd.Process.Kill()
	}
}

func Restart(appname string) {
	Debugf("kill running process")
	Kill()
	go Start(appname)
}

func Start(appname string) {
	fmt.Println("start", appname)

	if strings.Index(appname, "./") == -1 {
		appname = "./" + appname
	}

	cmd = exec.Command(appname)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	go cmd.Run()
}
<commit_msg>skip TMP files and gofmt events<commit_after>package main

import (
	"fmt"
	"github.com/howeyc/fsnotify"
	"log"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"
)

var (
	cmd       *exec.Cmd
	state     sync.Mutex
	eventTime = make(map[string]time.Time)
)

func NewWatcher(paths []string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		for {
			select {
			case e := <-watcher.Event:
				isbuild := true

				// Skip TMP files for Sublime Text.
				if checkTMPFile(e.Name) {
					continue
				}

				if t, ok := eventTime[e.Name]; ok {
					// if 500ms change many times, then ignore it.
					// for liteide often gofmt code after save.
					if t.Add(time.Millisecond * 500).After(time.Now()) {
						fmt.Println("[SKIP]", e.String())
						isbuild = false
					}
				}
				eventTime[e.Name] = time.Now()

				if isbuild {
					fmt.Println(e)
					go Autobuild()
				}
			case err := <-watcher.Error:
				log.Fatal("error:", err)
			}
		}
	}()
	for _, path := range paths {
		fmt.Println(path)
		err = watcher.Watch(path)
		if err != nil {
			log.Fatal(err)
		}
	}

}

func Autobuild() {
	state.Lock()
	defer state.Unlock()

	fmt.Println("start autobuild")
	path, _ := os.Getwd()
	os.Chdir(path)
	bcmd := exec.Command("go", "build")
	bcmd.Stdout = os.Stdout
	bcmd.Stderr = os.Stderr
	err := bcmd.Run()

	if err != nil {
		fmt.Println("============== build failed ===================")
		return
	}
	fmt.Println("build success")
	Restart(appname)
}

func Kill() {
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("Kill -> ", e)
		}
	}()
	if cmd != nil {
		cmd.Process.Kill()
	}
}

func Restart(appname string) {
	Debugf("kill running process")
	Kill()
	go Start(appname)
}

func Start(appname string) {
	fmt.Println("start", appname)

	if strings.Index(appname, "./") == -1 {
		appname = "./" + appname
	}

	cmd = exec.Command(appname)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	go cmd.Run()
}

// checkTMPFile returns true if the event was for TMP files.
func checkTMPFile(name string) bool {
	if strings.HasSuffix(strings.ToLower(name), ".tmp") {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package enroll

type Service interface {
	Enroll() (Profile, error)
}

func NewService(pushCertPath string, pushCertPass string) (Service, error) {
	pushTopic, err := GetPushTopicFromPKCS12(pushCertPath, pushCertPass)
	if err != nil {
		return nil, err
	}

	scepSubject := [][][]string{
		[][]string{
			[]string{"O", "MicroMDM"},
			[]string{"CN", "MDM Identity Certificate:UDID"},
		},
	}

	return &service{
		Url:         "https://micromdm.local:6443",
		SCEPUrl:     "http://micromdm.local:2019/scep",
		SCEPSubject: scepSubject,
		Topic:       pushTopic,
		CACert:      []byte{},
	}, nil
}

type service struct {
	Url           string
	SCEPUrl       string
	SCEPChallenge string
	SCEPSubject   [][][]string
	Topic         string // APNS Topic for MDM notifications
	CACert        []byte
}

func (svc service) Enroll() (Profile, error) {
	profile := NewProfile()
	profile.PayloadIdentifier = "com.github.micromdm.micromdm.mdm"
	profile.PayloadOrganization = "MicroMDM"
	profile.PayloadDisplayName = "Enrollment Profile"
	profile.PayloadDescription = "The server may alter your settings"

	scepContent := SCEPPayloadContent{
		Challenge: svc.SCEPChallenge,
		URL:       svc.SCEPUrl,
		Keysize:   1024,
		KeyType:   "RSA",
		KeyUsage:  0,
		Name:      "Device Management Identity Certificate",
		Subject:   svc.SCEPSubject,
	}

	scepPayload := NewPayload("com.apple.security.scep")
	scepPayload.PayloadDescription = "Configures SCEP"
	scepPayload.PayloadDisplayName = "SCEP"
	scepPayload.PayloadIdentifier = "com.github.micromdm.scep"
	scepPayload.PayloadContent = scepContent

	mdmPayload := NewPayload("com.apple.mdm")
	mdmPayload.PayloadDescription = "Enrolls with the MDM server"
	mdmPayload.PayloadOrganization = "MicroMDM"
	mdmPayload.PayloadIdentifier = "com.github.micromdm.mdm"

	mdmPayloadContent := MDMPayloadContent{
		Payload:                 *mdmPayload,
		AccessRights:            8191,
		CheckInURL:              svc.Url + "/mdm/checkin",
		CheckOutWhenRemoved:     true,
		ServerURL:               svc.Url + "/mdm/connect",
		IdentityCertificateUUID: scepPayload.PayloadUUID,
		Topic: svc.Topic,
	}

	//caPayload := NewPayload("com.apple.ssl.certificate")
	//caPayload.PayloadDisplayName = "Root certificate for MicroMDM"
	//caPayload.PayloadDescription = "Installs the root CA certificate for MicroMDM"
	//caPayload.PayloadContent = []byte{}

	profile.PayloadContent = []interface{}{*scepPayload, mdmPayloadContent}

	return *profile, nil
}
<commit_msg>Add cli flag `tls-ca-cert` and environment var MICROMDM_TLS_CA_CERT to specify a CA certificate which will be included in the enrollment profile. This is handy if you are using self signed certificates and you need to establish a CA trust.<commit_after>package enroll

import "io/ioutil"

type Service interface {
	Enroll() (Profile, error)
}

func NewService(pushCertPath string, pushCertPass string, caCertPath string) (Service, error) {
	pushTopic, err := GetPushTopicFromPKCS12(pushCertPath, pushCertPass)
	if err != nil {
		return nil, err
	}

	var caCert []byte

	if caCertPath != "" {
		caCert, err = ioutil.ReadFile(caCertPath)

		if err != nil {
			return nil, err
		}
	}

	scepSubject := [][][]string{
		[][]string{
			[]string{"O", "MicroMDM"},
			[]string{"CN", "MDM Identity Certificate:UDID"},
		},
	}

	return &service{
		Url:         "https://micromdm.local:6443",
		SCEPUrl:     "http://micromdm.local:2019/scep",
		SCEPSubject: scepSubject,
		Topic:       pushTopic,
		CACert:      caCert,
	}, nil
}

type service struct {
	Url           string
	SCEPUrl       string
	SCEPChallenge string
	SCEPSubject   [][][]string
	Topic         string // APNS Topic for MDM notifications
	CACert        []byte
}

func (svc service) Enroll() (Profile, error) {
	profile := NewProfile()
	profile.PayloadIdentifier = "com.github.micromdm.micromdm.mdm"
	profile.PayloadOrganization = "MicroMDM"
	profile.PayloadDisplayName = "Enrollment Profile"
	profile.PayloadDescription = "The server may alter your settings"

	scepContent := SCEPPayloadContent{
		Challenge: svc.SCEPChallenge,
		URL:       svc.SCEPUrl,
		Keysize:   1024,
		KeyType:   "RSA",
		KeyUsage:  0,
		Name:      "Device Management Identity Certificate",
		Subject:   svc.SCEPSubject,
	}

	scepPayload := NewPayload("com.apple.security.scep")
	scepPayload.PayloadDescription = "Configures SCEP"
	scepPayload.PayloadDisplayName = "SCEP"
	scepPayload.PayloadIdentifier = "com.github.micromdm.scep"
	scepPayload.PayloadContent = scepContent

	mdmPayload := NewPayload("com.apple.mdm")
	mdmPayload.PayloadDescription = "Enrolls with the MDM server"
	mdmPayload.PayloadOrganization = "MicroMDM"
	mdmPayload.PayloadIdentifier = "com.github.micromdm.mdm"

	mdmPayloadContent := MDMPayloadContent{
		Payload:                 *mdmPayload,
		AccessRights:            8191,
		CheckInURL:              svc.Url + "/mdm/checkin",
		CheckOutWhenRemoved:     true,
		ServerURL:               svc.Url + "/mdm/connect",
		IdentityCertificateUUID: scepPayload.PayloadUUID,
		Topic: svc.Topic,
	}

	if len(svc.CACert) > 0 {
		caPayload := NewPayload("com.apple.ssl.certificate")
		caPayload.PayloadDisplayName = "Root certificate for MicroMDM"
		caPayload.PayloadDescription = "Installs the root CA certificate for MicroMDM"
		caPayload.PayloadContent = svc.CACert

		profile.PayloadContent = []interface{}{*scepPayload, mdmPayloadContent, *caPayload}
	} else {
		profile.PayloadContent = []interface{}{*scepPayload, mdmPayloadContent}
	}

	return *profile, nil
}
<|endoftext|>


<commit_before>/*

Classical-inheritance-style service declarations.
Services can be started, then stopped.
Users can override the OnStart/OnStop methods.
These methods are guaranteed to be called at most once.
Caller must ensure that Start() and Stop() are not called concurrently.
It is ok to call Stop() without calling Start() first.
Services cannot be re-started unless otherwise documented.

Typical usage:

type FooService struct {
	BaseService
	// private fields
}

func NewFooService() *FooService {
	fs := &FooService{
		// init
	}
	fs.BaseService = *NewBaseService(log, "FooService", fs)
	return fs
}

func (fs *FooService) OnStart() error {
	fs.BaseService.OnStart() // Always call the overridden method.
	// initialize private fields
	// start subroutines, etc.
}

func (fs *FooService) OnStop() error {
	fs.BaseService.OnStop() // Always call the overridden method.
	// close/destroy private fields
	// stop subroutines, etc.
}

*/
package common

import (
	"sync/atomic"

	"github.com/tendermint/log15"
)

type Service interface {
	Start() (bool, error)
	OnStart() error

	Stop() bool
	OnStop()

	IsRunning() bool

	String() string
}

type BaseService struct {
	log     log15.Logger
	name    string
	started uint32 // atomic
	stopped uint32 // atomic

	// The "subclass" of BaseService
	impl Service
}

func NewBaseService(log log15.Logger, name string, impl Service) *BaseService {
	return &BaseService{
		log:  log,
		name: name,
		impl: impl,
	}
}

// Implements Servce
func (bs *BaseService) Start() (bool, error) {
	if atomic.CompareAndSwapUint32(&bs.started, 0, 1) {
		if atomic.LoadUint32(&bs.stopped) == 1 {
			if bs.log != nil {
				bs.log.Warn(Fmt("Not starting %v -- already stopped", bs.name), "impl", bs.impl)
			}
			return false, nil
		} else {
			if bs.log != nil {
				bs.log.Notice(Fmt("Starting %v", bs.name), "impl", bs.impl)
			}
		}
		err := bs.impl.OnStart()
		return true, err
	} else {
		if bs.log != nil {
			bs.log.Info(Fmt("Not starting %v -- already started", bs.name), "impl", bs.impl)
		}
		return false, nil
	}
}

// Implements Service
func (bs *BaseService) OnStart() error { return nil }

// Implements Service
func (bs *BaseService) Stop() bool {
	if atomic.CompareAndSwapUint32(&bs.stopped, 0, 1) {
		if bs.log != nil {
			bs.log.Notice(Fmt("Stopping %v", bs.name), "impl", bs.impl)
		}
		bs.impl.OnStop()
		return true
	} else {
		if bs.log != nil {
			bs.log.Debug(Fmt("Stopping %v (ignoring: already stopped)", bs.name), "impl", bs.impl)
		}
		return false
	}
}

// Implements Service
func (bs *BaseService) OnStop() {}

// Implements Service
func (bs *BaseService) IsRunning() bool {
	return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
}

// Implements Servce
func (bs *BaseService) String() string {
	return bs.name
}

//----------------------------------------

type QuitService struct {
	BaseService
	Quit chan struct{}
}

func NewQuitService(log log15.Logger, name string, impl Service) *QuitService {
	return &QuitService{
		BaseService: *NewBaseService(log, name, impl),
		Quit:        nil,
	}
}

// NOTE: when overriding OnStart, must call .QuitService.OnStart().
func (qs *QuitService) OnStart() error {
	qs.Quit = make(chan struct{})
	return nil
}

// NOTE: when overriding OnStop, must call .QuitService.OnStop().
func (qs *QuitService) OnStop() {
	if qs.Quit != nil {
		close(qs.Quit)
	}
}
<commit_msg>service: start/stop logs are info, ignored are debug<commit_after>/*

Classical-inheritance-style service declarations.
Services can be started, then stopped.
Users can override the OnStart/OnStop methods.
These methods are guaranteed to be called at most once.
Caller must ensure that Start() and Stop() are not called concurrently.
It is ok to call Stop() without calling Start() first.
Services cannot be re-started unless otherwise documented.

Typical usage:

type FooService struct {
	BaseService
	// private fields
}

func NewFooService() *FooService {
	fs := &FooService{
		// init
	}
	fs.BaseService = *NewBaseService(log, "FooService", fs)
	return fs
}

func (fs *FooService) OnStart() error {
	fs.BaseService.OnStart() // Always call the overridden method.
	// initialize private fields
	// start subroutines, etc.
}

func (fs *FooService) OnStop() error {
	fs.BaseService.OnStop() // Always call the overridden method.
	// close/destroy private fields
	// stop subroutines, etc.
}

*/
package common

import (
	"sync/atomic"

	"github.com/tendermint/log15"
)

type Service interface {
	Start() (bool, error)
	OnStart() error

	Stop() bool
	OnStop()

	IsRunning() bool

	String() string
}

type BaseService struct {
	log     log15.Logger
	name    string
	started uint32 // atomic
	stopped uint32 // atomic

	// The "subclass" of BaseService
	impl Service
}

func NewBaseService(log log15.Logger, name string, impl Service) *BaseService {
	return &BaseService{
		log:  log,
		name: name,
		impl: impl,
	}
}

// Implements Servce
func (bs *BaseService) Start() (bool, error) {
	if atomic.CompareAndSwapUint32(&bs.started, 0, 1) {
		if atomic.LoadUint32(&bs.stopped) == 1 {
			if bs.log != nil {
				bs.log.Warn(Fmt("Not starting %v -- already stopped", bs.name), "impl", bs.impl)
			}
			return false, nil
		} else {
			if bs.log != nil {
				bs.log.Info(Fmt("Starting %v", bs.name), "impl", bs.impl)
			}
		}
		err := bs.impl.OnStart()
		return true, err
	} else {
		if bs.log != nil {
			bs.log.Debug(Fmt("Not starting %v -- already started", bs.name), "impl", bs.impl)
		}
		return false, nil
	}
}

// Implements Service
func (bs *BaseService) OnStart() error { return nil }

// Implements Service
func (bs *BaseService) Stop() bool {
	if atomic.CompareAndSwapUint32(&bs.stopped, 0, 1) {
		if bs.log != nil {
			bs.log.Info(Fmt("Stopping %v", bs.name), "impl", bs.impl)
		}
		bs.impl.OnStop()
		return true
	} else {
		if bs.log != nil {
			bs.log.Debug(Fmt("Stopping %v (ignoring: already stopped)", bs.name), "impl", bs.impl)
		}
		return false
	}
}

// Implements Service
func (bs *BaseService) OnStop() {}

// Implements Service
func (bs *BaseService) IsRunning() bool {
	return atomic.LoadUint32(&bs.started) == 1 && atomic.LoadUint32(&bs.stopped) == 0
}

// Implements Servce
func (bs *BaseService) String() string {
	return bs.name
}

//----------------------------------------

type QuitService struct {
	BaseService
	Quit chan struct{}
}

func NewQuitService(log log15.Logger, name string, impl Service) *QuitService {
	return &QuitService{
		BaseService: *NewBaseService(log, name, impl),
		Quit:        nil,
	}
}

// NOTE: when overriding OnStart, must call .QuitService.OnStart().
func (qs *QuitService) OnStart() error {
	qs.Quit = make(chan struct{})
	return nil
}

// NOTE: when overriding OnStop, must call .QuitService.OnStop().
func (qs *QuitService) OnStop() {
	if qs.Quit != nil {
		close(qs.Quit)
	}
}
<|endoftext|>


<commit_before>package gocomet

import (
	"container/list"
	"errors"
	"sync"
	"time"
)

// Maximum number of retry to avoid conflict
const MAX_ID_GEN_RETRY = 100

// Maximum number of IDs to kept to avoid conflict
const MAX_ID_KEPT_TIME = 10 * time.Minute

type timeAndValue struct {
	value  interface{}
	expire time.Time
}

type UniqueStringPool struct {
	sync.Locker
	newValue func() string
	values   map[string]*list.Element
	order    *list.List
}

func newUniqueStringPool(f func() string) *UniqueStringPool {
	return &UniqueStringPool{&sync.Mutex{}, f, make(map[string]*list.Element), list.New()}
}

func (pool *UniqueStringPool) get() (value string, err error) {
	pool.Lock()
	defer pool.Unlock()

	var limit = MAX_ID_GEN_RETRY
	for limit > 0 {
		value = pool.newValue()
		if _, ok := pool.values[value]; !ok {
			break
		}
		limit--
	}
	if limit == 0 {
		err = errors.New("Unable to obtain new unique ID. Try again later.")
	}

	now := time.Now()
	pool.values[value] = pool.order.PushBack(&timeAndValue{value, now.Add(MAX_ID_KEPT_TIME)})
	for e := pool.order.Front(); e != nil; e = e.Next() {
		if e.Value.(*timeAndValue).expire.After(now) {
			break
		}
		pool.order.Remove(e)
	}

	return
}

func (pool *UniqueStringPool) touch(value string) (ok bool) {
	pool.Lock()
	defer pool.Unlock()

	var e *list.Element
	if e, ok = pool.values[value]; ok {
		pool.order.Remove(e)
		e = pool.order.PushBack(&timeAndValue{value, time.Now().Add(MAX_ID_KEPT_TIME)})
		pool.values[value] = e
	}
	return
}

// Maximum allowed session idele. After that, the session is
// considered as disconnected.
const MAX_SESSION_IDEL = 10 * time.Minute

// The unsent messages are kept temporarily in a mailbox. But only
// last MAILBOX_SIZE messages are kept.
const MAILBOX_SIZE = 1000

type Session struct {
	channelReq   chan bool
	channelResp  chan chan *Message
	channelFail  chan *Message
	channelClose chan bool
}

var closedChannel chan *Message = func() chan *Message {
	ch := make(chan *Message)
	close(ch)
	return ch
}()

func newSession(input chan *Message, cleanup func()) *Session {
	channelReq := make(chan bool)
	channelResp := make(chan chan *Message)
	channelFail := make(chan *Message)
	channelClose := make(chan bool)

	go func() {
		var isConnected, isConnect bool
		var mailbox *list.List = list.New()
		var output chan *Message
		var isRunning = true
		for isRunning {
			// Session's major responsibilities are:
			// 1. transimit the message from broker to clients;
			// 2. respond to client's channel request;
			// 3. close downstream channel and push back message; and
			// 4. auto-disconnect those clients that exceed max idel time.
			select {
			case msg, ok := <-input:
				if !ok { // upstream channel is closed
					isRunning = false
					isConnected = false
					close(output)
					output = nil
				} else if output == nil { // no downstream channel
					// log.Printf("Saved message: %v", msg)
					mailbox.PushBack(msg)
					if mailbox.Len() > MAILBOX_SIZE {
						mailbox.Remove(mailbox.Front())
					}
				} else {
					// log.Printf("Received message: %v", msg)
					if msg == nil {
						panic("message should not be nil")
					}
					output <- msg
				}
			case b := <-channelReq:
				if !isConnected {
					// no existing active channel
					isConnected = true
					isConnect = b
					// try re-send the messages by using a large size channel
					output = make(chan *Message, mailbox.Len())
					if mailbox.Len() > 0 {
						for e := mailbox.Front(); e != nil; e = e.Next() {
							if e.Value == nil {
								panic("message should not be nil")
							}
							output <- e.Value.(*Message)
						}
						mailbox.Init()
					}
					channelResp <- output
				} else if !isConnect && b {
					// override existing non-connect active channel
					isConnect = true
					close(output)
					output = make(chan *Message)
					channelResp <- output
				} else {
					// active connect channel already exists
					channelResp <- closedChannel
				}
			case msg := <-channelFail:
				if msg != nil {
					mailbox.PushFront(msg)
				}
				isConnected = false
				close(output)
				output = nil
			case <-channelClose:
				isRunning = false
				isConnected = false
				close(output)
				output = nil
				if mailbox.Len() > 0 {
					ch := make(chan *Message)
					go func() {
						for e := mailbox.Front(); e != nil; e = e.Next() {
							if e.Value == nil {
								panic("message should not be nil")
							}
							ch <- e.Value.(*Message)
						}
					}()
					channelResp <- ch
				} else {
					channelResp <- closedChannel
				}
			case <-time.After(MAX_SESSION_IDEL):
				isRunning = false
				isConnected = false
				close(output)
				output = nil
			}
		}

		go cleanup()
	}()

	return &Session{
		channelReq:   channelReq,
		channelResp:  channelResp,
		channelFail:  channelFail,
		channelClose: channelClose,
	}
}

func (ss *Session) obtainChannel(isConnect bool) chan *Message {
	ss.channelReq <- isConnect
	return <-ss.channelResp
}

func (ss *Session) close() chan *Message {
	ss.channelClose <- true
	return <-ss.channelResp
}
<commit_msg>fix typo<commit_after>package gocomet

import (
	"container/list"
	"errors"
	"sync"
	"time"
)

// Maximum number of retry to avoid conflict
const MAX_ID_GEN_RETRY = 100

// Maximum time to keep IDs from being auto-released
const MAX_ID_KEPT_TIME = 30 * time.Minute

type timeAndValue struct {
	value  interface{}
	expire time.Time
}

type UniqueStringPool struct {
	sync.Locker
	newValue func() string
	values   map[string]*list.Element
	order    *list.List
}

func newUniqueStringPool(f func() string) *UniqueStringPool {
	return &UniqueStringPool{&sync.Mutex{}, f, make(map[string]*list.Element), list.New()}
}

func (pool *UniqueStringPool) get() (value string, err error) {
	pool.Lock()
	defer pool.Unlock()

	var limit = MAX_ID_GEN_RETRY
	for limit > 0 {
		value = pool.newValue()
		if _, ok := pool.values[value]; !ok {
			break
		}
		limit--
	}
	if limit == 0 {
		err = errors.New("Unable to obtain new unique ID. Try again later.")
	}

	now := time.Now()
	pool.values[value] = pool.order.PushBack(&timeAndValue{value, now.Add(MAX_ID_KEPT_TIME)})
	for e := pool.order.Front(); e != nil; e = e.Next() {
		if e.Value.(*timeAndValue).expire.After(now) {
			break
		}
		pool.order.Remove(e)
	}

	return
}

func (pool *UniqueStringPool) touch(value string) (ok bool) {
	pool.Lock()
	defer pool.Unlock()

	var e *list.Element
	if e, ok = pool.values[value]; ok {
		pool.order.Remove(e)
		e = pool.order.PushBack(&timeAndValue{value, time.Now().Add(MAX_ID_KEPT_TIME)})
		pool.values[value] = e
	}
	return
}

// Maximum allowed session idel. After that, the session is
// considered as disconnected.
const MAX_SESSION_IDEL = 10 * time.Minute

// The unsent messages are kept temporarily in a mailbox. But only
// last MAILBOX_SIZE messages are kept.
const MAILBOX_SIZE = 1000

type Session struct {
	channelReq   chan bool
	channelResp  chan chan *Message
	channelFail  chan *Message
	channelClose chan bool
}

var closedChannel chan *Message = func() chan *Message {
	ch := make(chan *Message)
	close(ch)
	return ch
}()

func newSession(input chan *Message, cleanup func()) *Session {
	channelReq := make(chan bool)
	channelResp := make(chan chan *Message)
	channelFail := make(chan *Message)
	channelClose := make(chan bool)

	go func() {
		var isConnected, isConnect bool
		var mailbox *list.List = list.New()
		var output chan *Message
		var isRunning = true
		for isRunning {
			// Session's major responsibilities are:
			// 1. transimit the message from broker to clients;
			// 2. respond to client's channel request;
			// 3. close downstream channel and push back message; and
			// 4. auto-disconnect those clients that exceed max idel time.
			select {
			case msg, ok := <-input:
				if !ok { // upstream channel is closed
					isRunning = false
					isConnected = false
					close(output)
					output = nil
				} else if output == nil { // no downstream channel
					// log.Printf("Saved message: %v", msg)
					mailbox.PushBack(msg)
					if mailbox.Len() > MAILBOX_SIZE {
						mailbox.Remove(mailbox.Front())
					}
				} else {
					// log.Printf("Received message: %v", msg)
					if msg == nil {
						panic("message should not be nil")
					}
					output <- msg
				}
			case b := <-channelReq:
				if !isConnected {
					// no existing active channel
					isConnected = true
					isConnect = b
					// try re-send the messages by using a large size channel
					output = make(chan *Message, mailbox.Len())
					if mailbox.Len() > 0 {
						for e := mailbox.Front(); e != nil; e = e.Next() {
							if e.Value == nil {
								panic("message should not be nil")
							}
							output <- e.Value.(*Message)
						}
						mailbox.Init()
					}
					channelResp <- output
				} else if !isConnect && b {
					// override existing non-connect active channel
					isConnect = true
					close(output)
					output = make(chan *Message)
					channelResp <- output
				} else {
					// active connect channel already exists
					channelResp <- closedChannel
				}
			case msg := <-channelFail:
				if msg != nil {
					mailbox.PushFront(msg)
				}
				isConnected = false
				close(output)
				output = nil
			case <-channelClose:
				isRunning = false
				isConnected = false
				close(output)
				output = nil
				if mailbox.Len() > 0 {
					ch := make(chan *Message)
					go func() {
						for e := mailbox.Front(); e != nil; e = e.Next() {
							if e.Value == nil {
								panic("message should not be nil")
							}
							ch <- e.Value.(*Message)
						}
					}()
					channelResp <- ch
				} else {
					channelResp <- closedChannel
				}
			case <-time.After(MAX_SESSION_IDEL):
				isRunning = false
				isConnected = false
				close(output)
				output = nil
			}
		}

		go cleanup()
	}()

	return &Session{
		channelReq:   channelReq,
		channelResp:  channelResp,
		channelFail:  channelFail,
		channelClose: channelClose,
	}
}

func (ss *Session) obtainChannel(isConnect bool) chan *Message {
	ss.channelReq <- isConnect
	return <-ss.channelResp
}

func (ss *Session) close() chan *Message {
	ss.channelClose <- true
	return <-ss.channelResp
}
<|endoftext|>


<commit_before>// Copyright 2015 JPH <jph@hackworth.be>

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

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

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

package cryptoauth

import (
	"net"
)

// Need a type to hold local state

type State struct {
	KeyPair   *KeyPair
	Passwords map[[32]byte]*Passwd
}

// Need a type to hold peer-side state

type Peer struct {
	Addr               *net.UDPAddr // remote address
	Conn               *net.UDPConn // local connection
	NextNonce          uint32
	Secret             *[32]byte
	PublicKey          [32]byte
	TempKeyPair        *KeyPair // Our Temporary Keypair
	TempPublicKey      [32]byte // peer temporary public key
	PasswordHash       [32]byte // hashed version of password
	Initiator          bool
	Established        bool
	AuthRequired       bool
	LastPacketReceived uint32
}

type ReplayProtection struct {
	bits              uint64
	offset            uint32
	dupes             uint32
	packetsLost       uint32
	packetsOutOfRange uint32
}

type KeyPair struct {
	PublicKey  *[32]byte
	PrivateKey *[32]byte
}

// Neet a type to hold passwords

type Passwd struct {
	user     string
	password string
	hash     [32]string
}
<commit_msg>added name to peer struct<commit_after>// Copyright 2015 JPH <jph@hackworth.be>

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

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

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

package cryptoauth

import (
	"net"
)

// Need a type to hold local state

type State struct {
	KeyPair   *KeyPair
	Passwords map[[32]byte]*Passwd
}

// Need a type to hold peer-side state

type Peer struct {
	Addr               *net.UDPAddr // remote address
	Conn               *net.UDPConn // local connection
	Name               string
	NextNonce          uint32
	Secret             *[32]byte
	PublicKey          [32]byte
	TempKeyPair        *KeyPair // Our Temporary Keypair
	TempPublicKey      [32]byte // peer temporary public key
	PasswordHash       [32]byte // hashed version of password
	Initiator          bool
	Established        bool
	AuthRequired       bool
	LastPacketReceived uint32
}

type ReplayProtection struct {
	bits              uint64
	offset            uint32
	dupes             uint32
	packetsLost       uint32
	packetsOutOfRange uint32
}

type KeyPair struct {
	PublicKey  *[32]byte
	PrivateKey *[32]byte
}

// Neet a type to hold passwords

type Passwd struct {
	user     string
	password string
	hash     [32]string
}
<|endoftext|>


<commit_before>package buffalo

import (
	"net/http"

	"github.com/gorilla/sessions"
)

// Session wraps the "github.com/gorilla/sessions" API
// in something a little cleaner and a bit more useable.
type Session struct {
	Session *sessions.Session
	req     *http.Request
	res     http.ResponseWriter
}

// Save the current session
func (s *Session) Save() error {
	return s.Session.Save(s.req, s.res)
}

// Get a value from the current session
func (s *Session) Get(name interface{}) interface{} {
	return s.Session.Values[name]
}

// Set a value onto the current session. If a value with that name
// already exists it will be overridden with the new value.
func (s *Session) Set(name, value interface{}) {
	s.Session.Values[name] = value
}

// Delete a value from the current session.
func (s *Session) Delete(name interface{}) {
	delete(s.Session.Values, name)
}

// Get a session using a request and response.
func (a *App) getSession(r *http.Request, w http.ResponseWriter) *Session {
	session, _ := a.SessionStore.Get(r, a.SessionName)
	return &Session{
		Session: session,
		req:     r,
		res:     w,
	}
}
<commit_msg>Add punctuation to be consistent<commit_after>package buffalo

import (
	"net/http"

	"github.com/gorilla/sessions"
)

// Session wraps the "github.com/gorilla/sessions" API
// in something a little cleaner and a bit more useable.
type Session struct {
	Session *sessions.Session
	req     *http.Request
	res     http.ResponseWriter
}

// Save the current session.
func (s *Session) Save() error {
	return s.Session.Save(s.req, s.res)
}

// Get a value from the current session.
func (s *Session) Get(name interface{}) interface{} {
	return s.Session.Values[name]
}

// Set a value onto the current session. If a value with that name
// already exists it will be overridden with the new value.
func (s *Session) Set(name, value interface{}) {
	s.Session.Values[name] = value
}

// Delete a value from the current session.
func (s *Session) Delete(name interface{}) {
	delete(s.Session.Values, name)
}

// Get a session using a request and response.
func (a *App) getSession(r *http.Request, w http.ResponseWriter) *Session {
	session, _ := a.SessionStore.Get(r, a.SessionName)
	return &Session{
		Session: session,
		req:     r,
		res:     w,
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Nan Deng
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package main

import (
	"io"
	//"time"
	"encoding/binary"
	"sync"
	"sync/atomic"
	"errors"
)

const (
	PROTOCOL_VERSION = 1
)

const (
	sessionState_UNAUTH = iota
	sessionState_AUTHED
	sessionState_DISCON
)

const (
	sessionContent_AUTHREQ = iota
	sessionContent_AUTHRES
	sessionContent_APPDATA
	sessionContent_CONTROL
)

var ErrUnauth = errors.New("Unauthorized session")
var ErrBadContentType = errors.New("Bad Content Type")

type ErrorBadProtoImpl struct {
	msg string
}

func (self *ErrorBadProtoImpl) Error() string {
	return "Bad Protocol Implementation: " + self.msg
}

// A session deals with:
// - Authentication
// - Encryption
// - Compression
//
// It provides a ReadWriteCloser implementation.
// Read, Write could be safely called in parallel.
type Session struct {
	state int32
	transport io.ReadWriteCloser
	buf *ListBuffer
	writeLock *sync.Mutex
}

type sessionRecord struct {
	contentType uint8
	version uint8
	buf []byte
}

func NewSession(transport io.ReadWriteCloser) *Session {
	ret := new(Session)
	ret.transport = transport
	ret.state = sessionState_UNAUTH

	// The buffer will hold at most 8K bytes of data
	ret.buf = NewListBuffer(8192)
	ret.writeLock = new(sync.Mutex)
	return ret
}

func (self *Session) readRecord() (rec *sessionRecord, err error) {
	rec = new(sessionRecord)
	err = binary.Read(self.transport, binary.LittleEndian, &rec.contentType)
	if err != nil {
		rec = nil
		return
	}
	err = binary.Read(self.transport, binary.LittleEndian, &rec.version)
	if err != nil {
		rec = nil
		return
	}
	var length uint16
	err = binary.Read(self.transport, binary.LittleEndian, &length)
	if err != nil {
		rec = nil
		return
	}
	rec.buf = make([]byte, length)
	_, err = io.ReadFull(self.transport, rec.buf)
	if err != nil {
		rec = nil
		return
	}
	err = nil
	return
}

func (self *Session) writeRecord(rec *sessionRecord) error {
	var err error
	self.writeLock.Lock()
	defer self.writeLock.Unlock()
	err = binary.Write(self.transport, binary.LittleEndian, rec.contentType)
	if err != nil {
		return err
	}
	err = binary.Write(self.transport, binary.LittleEndian, rec.version)
	if err != nil {
		return err
	}
	var length uint16
	length = uint16(len(rec.buf))
	err = binary.Write(self.transport, binary.LittleEndian, length)
	if err != nil {
		return err
	}

	_, err = self.transport.Write(rec.buf)
	return err
}

// Write() writes the buf to the transport layer.
// It will first compress the data, and then encrypt it.
//
// This method is goroutine-safe
func (self *Session) Write(buf []byte) (n int, err error) {
	if atomic.LoadInt32(&self.state) != sessionState_AUTHED {
		return 0, ErrUnauth
	}
	rec := new(sessionRecord)
	rec.contentType = sessionContent_APPDATA
	rec.version = PROTOCOL_VERSION
	rec.buf = buf
	n = 0
	err = self.writeRecord(rec)
	if err != nil {
		return
	}
	n = len(buf)
	return
}

// Read() will first read data from the transport layer,
// then decrypt the data, then decompress it and copy
// the finaly data to the buf.
//
// OK. I am cheating. Read() is actually reading data from
// an internal buffer. All data there has already been
// decrepted & decompressed by another goroutine.
//
// If you cannot understand what I said, simply think it as
// a wrapper of another Reader and can do some magic stuff
// on reading.
//
// This method is goroutine-safe
func (self *Session) Read(buf []byte) (n int, err error) {
	if atomic.LoadInt32(&self.state) != sessionState_AUTHED {
		return 0, ErrUnauth
	}
	n, err = self.buf.Read(buf)
	if err != nil && err != io.EOF {
		return
	}
	for err == io.EOF {
		// If the connection is disconnected, then...
		if atomic.LoadInt32(&self.state) == sessionState_DISCON {
			if n == 0 {
				// return io.EOF if no data is read.
				err = io.EOF
			} else {
				// Otherwise, return nil error with data first,
				// then return an EOF on next call of Read().
				err = nil
			}
			return
		}
		self.buf.WaitForData()
		n, err = self.buf.Read(buf)
		if err != nil && err != io.EOF {
			return
		}
	}
	return
}

func (self *Session) recvLoop() {
	if atomic.LoadInt32(&self.state) != sessionState_AUTHED {
		return
	}
	for {
		rec, err := self.readRecord()
		if err != nil {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				atomic.StoreInt32(&self.state, sessionState_DISCON)
			}
			break
		}
		switch rec.contentType {
		case sessionContent_APPDATA:
			_, err := self.buf.Write(rec.buf)

			for err == ErrFull {
				self.buf.WaitForSpace(len(rec.buf))
				_, err = self.buf.Write(rec.buf)
			}
		}
	}
}

type Authorizer interface {
	Authorize(name string, token string) bool
}

func getString(buf []byte) (str string, newbuf []byte) {
	stop := 0
	str = ""
	for i := 0; i < len(buf); i++ {
		if buf[i] == 0 {
			stop = i
			break
		}
	}

	if stop == 0 {
		newbuf = buf
		return
	}

	var ok bool

	if str, ok = string(buf[:stop]); !ok {
		newbuf = buf
		return
	}

	newbuf = buf[stop:]
	return
}

// This method should always be called before Read and Write.
// If it returns true, nil, then it means the session now is authorized and ecrypted using
// the new key. Otherwise, any call on Read or Write will return ErrUnauth error.
func (self *Session) WaitAuth(auth Authorizer, timeOut time.Duration) (succ bool, err error) {
	var rec *sessionRecord
	err = nil
	succ = false

	rec, err = self.readRecord()
	if err != nil {
		return
	}

	if rec.contentType != sessionContent_AUTHREQ {
		err = ErrBadContentType
		return
	}

	buf := rec.buf

	// Extract name part
	name, buf := getString(buf)
	if len(name) == 0 {
		err = &ErrorBadProtoImpl{"Empty auth req"}
		return
	}
	token, buf := getString(buf)
	if len(token) == 0 {
		err = &ErrorBadProtoImpl{"Bad token"}
		return
	}

	if len(buf) == 0 {
		err = &ErrorBadProtoImpl{"No key"}
		return
	}

	succ = auth.Authorize(name, token)

	if succ {
		atomic.StoreInt32(&self.state, sessionState_AUTHED)
	}

	go recvLoop()
	return
}

<commit_msg>WaitAuth()<commit_after>/*
 * Copyright 2012 Nan Deng
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package main

import (
	"io"
	"time"
	"encoding/binary"
	"sync"
	"sync/atomic"
	"errors"
	"crypto/sha1"
)

const (
	PROTOCOL_VERSION = 1
)

const (
	sessionState_UNAUTH = iota
	sessionState_AUTHED
	sessionState_DISCON
)

const (
	sessionContent_AUTHREQ = iota
	sessionContent_AUTHRES
	sessionContent_APPDATA
	sessionContent_CONTROL
)

var ErrUnauth = errors.New("Unauthorized session")
var ErrBadContentType = errors.New("Bad Content Type")

type ErrorBadProtoImpl struct {
	msg string
}

func (self *ErrorBadProtoImpl) Error() string {
	return "Bad Protocol Implementation: " + self.msg
}

// A session deals with:
// - Authentication
// - Encryption
// - Compression
//
// It provides a ReadWriteCloser implementation.
// Read, Write could be safely called in parallel.
type Session struct {
	state int32
	transport io.ReadWriteCloser
	buf *ListBuffer
	writeLock *sync.Mutex
}

type sessionRecord struct {
	contentType uint8
	version uint8
	buf []byte
}

func NewSession(transport io.ReadWriteCloser) *Session {
	ret := new(Session)
	ret.transport = transport
	ret.state = sessionState_UNAUTH

	// The buffer will hold at most 8K bytes of data
	ret.buf = NewListBuffer(8192)
	ret.writeLock = new(sync.Mutex)
	return ret
}

func (self *Session) readRecord() (rec *sessionRecord, err error) {
	rec = new(sessionRecord)
	err = binary.Read(self.transport, binary.LittleEndian, &rec.contentType)
	if err != nil {
		rec = nil
		return
	}
	err = binary.Read(self.transport, binary.LittleEndian, &rec.version)
	if err != nil {
		rec = nil
		return
	}
	var length uint16
	err = binary.Read(self.transport, binary.LittleEndian, &length)
	if err != nil {
		rec = nil
		return
	}
	rec.buf = make([]byte, length)
	_, err = io.ReadFull(self.transport, rec.buf)
	if err != nil {
		rec = nil
		return
	}
	err = nil
	return
}

func (self *Session) writeRecord(rec *sessionRecord) error {
	var err error
	self.writeLock.Lock()
	defer self.writeLock.Unlock()
	err = binary.Write(self.transport, binary.LittleEndian, rec.contentType)
	if err != nil {
		return err
	}
	err = binary.Write(self.transport, binary.LittleEndian, rec.version)
	if err != nil {
		return err
	}
	var length uint16
	length = uint16(len(rec.buf))
	err = binary.Write(self.transport, binary.LittleEndian, length)
	if err != nil {
		return err
	}

	_, err = self.transport.Write(rec.buf)
	return err
}

// Write() writes the buf to the transport layer.
// It will first compress the data, and then encrypt it.
//
// This method is goroutine-safe
func (self *Session) Write(buf []byte) (n int, err error) {
	if atomic.LoadInt32(&self.state) != sessionState_AUTHED {
		return 0, ErrUnauth
	}
	rec := new(sessionRecord)
	rec.contentType = sessionContent_APPDATA
	rec.version = PROTOCOL_VERSION
	rec.buf = buf
	n = 0
	err = self.writeRecord(rec)
	if err != nil {
		return
	}
	n = len(buf)
	return
}

// Read() will first read data from the transport layer,
// then decrypt the data, then decompress it and copy
// the finaly data to the buf.
//
// OK. I am cheating. Read() is actually reading data from
// an internal buffer. All data there has already been
// decrepted & decompressed by another goroutine.
//
// If you cannot understand what I said, simply think it as
// a wrapper of another Reader and can do some magic stuff
// on reading.
//
// This method is goroutine-safe
func (self *Session) Read(buf []byte) (n int, err error) {
	if atomic.LoadInt32(&self.state) != sessionState_AUTHED {
		return 0, ErrUnauth
	}
	n, err = self.buf.Read(buf)
	if err != nil && err != io.EOF {
		return
	}
	for err == io.EOF {
		// If the connection is disconnected, then...
		if atomic.LoadInt32(&self.state) == sessionState_DISCON {
			if n == 0 {
				// return io.EOF if no data is read.
				err = io.EOF
			} else {
				// Otherwise, return nil error with data first,
				// then return an EOF on next call of Read().
				err = nil
			}
			return
		}
		self.buf.WaitForData()
		n, err = self.buf.Read(buf)
		if err != nil && err != io.EOF {
			return
		}
	}
	return
}

func (self *Session) recvLoop() {
	if atomic.LoadInt32(&self.state) != sessionState_AUTHED {
		return
	}
	for {
		rec, err := self.readRecord()
		if err != nil {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				atomic.StoreInt32(&self.state, sessionState_DISCON)
			}
			break
		}
		switch rec.contentType {
		case sessionContent_APPDATA:
			_, err := self.buf.Write(rec.buf)

			for err == ErrFull {
				self.buf.WaitForSpace(len(rec.buf))
				_, err = self.buf.Write(rec.buf)
			}
		}
	}
}

// The Authorizer interface defines one method
// used to authorize the user's name and token
// combination.
type Authorizer interface {
	Authorize(name string, token string) bool
}

func getString(buf []byte) (str string, newbuf []byte) {
	stop := 0
	str = ""
	for i := 0; i < len(buf); i++ {
		if buf[i] == 0 {
			stop = i
			break
		}
	}

	if stop == 0 {
		newbuf = buf
		return
	}

	var ok bool

	if str, ok = string(buf[:stop]); !ok {
		newbuf = buf
		return
	}

	newbuf = buf[stop:]
	return
}

// This method should always be called before Read and Write.
// If it returns true, nil, then it means the session now is authorized and ecrypted using
// the new key. Otherwise, any call on Read or Write will return ErrUnauth error.
func (self *Session) WaitAuth(auth Authorizer, timeOut time.Duration) (succ bool, err error) {
	var rec *sessionRecord
	err = nil
	succ = false

	rec, err = self.readRecord()
	if err != nil {
		return
	}

	if rec.contentType != sessionContent_AUTHREQ {
		err = ErrBadContentType
		return
	}

	buf := rec.buf

	// Extract name part
	name, buf := getString(buf)
	if len(name) == 0 {
		err = &ErrorBadProtoImpl{"Empty auth req"}
		return
	}
	token, buf := getString(buf)
	if len(token) == 0 {
		err = &ErrorBadProtoImpl{"Bad token"}
		return
	}

	if len(buf) == 0 {
		err = &ErrorBadProtoImpl{"No key"}
		return
	}

	succ = auth.Authorize(name, token)

	if succ {
		atomic.StoreInt32(&self.state, sessionState_AUTHED)
	}

	res := new(sessionRecord)
	res.contentType = sessionContent_AUTHRES
	res.version = PROTOCOL_VERSION

	hash := sha1.New()
	hash.Write(rec.buf)
	res.buf = hash.Sum(res.buf)

	err = self.writeRecord(res)
	if err != nil {
		return
	}

	go recvLoop()
	return
}

<|endoftext|>