<commit_before>package auth

import (
	"bytes"
	"encoding/base64"
	"net/http"
	"strings"

	"github.com/codegangsta/negroni"
)

const (
	schemeBasic = "Basic"
)

// Config defines options for authentication middleware.
type Config struct {
	// Validate is function to validate credentials
	Validate func(r *http.Request, username, password string) bool
	// ValidateCustom is function to validate custom authorization scheme
	ValidateCustom func(r *http.Request, scheme, custom string) bool
	// UnauthorizedHandler is optional error handler to override default error handler.
	UnauthorizedHandler http.Handler
}

func (config Config) setDefaults() Config {
	if config.UnauthorizedHandler == nil {
		config.UnauthorizedHandler = http.HandlerFunc(defaultUnauthorizedHandler)
	}
	return config
}

// Middleware returns gohttp auth middleware.
func Middleware(config Config) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return Handler(config, next)
	}
}

// Handler creates auth middleware http handler.
func Handler(config Config, next http.Handler) http.Handler {
	return &gohttpMiddleware{config.setDefaults(), next}
}

// Negroni returns negroni auth middleware.
func Negroni(config Config) negroni.Handler {
	return &negroniMiddleware{config.setDefaults()}
}

// gohttp middleware
type gohttpMiddleware struct {
	config Config
	next   http.Handler
}

// ServeHTTP implementation.
func (m *gohttpMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	authImpl(m.config, w, r, m.next)
}

// negroni middleware
type negroniMiddleware struct {
	config Config
}

// ServeHTTP implementation.
func (m *negroniMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	authImpl(m.config, w, r, next)
}

// auth implementation
func authImpl(config Config, w http.ResponseWriter, r *http.Request, next http.Handler) {
	if validate(config, r) {
		next.ServeHTTP(w, r)
	} else {
		config.UnauthorizedHandler.ServeHTTP(w, r)
	}
}

// Validates the user:password combination provided in the Authorization header.
func validate(config Config, r *http.Request) bool {

	h := r.Header.Get("Authorization")
	f := strings.Fields(h)
	if len(h) == 0 || len(f) != 2 {
		return false
	}

	if f[0] == schemeBasic {
		str, err := base64.StdEncoding.DecodeString(f[1])
		if err != nil {
			return false
		}

		creds := bytes.SplitN(str, []byte(":"), 2)

		return config.Validate(r, string(creds[0]), string(creds[1]))
	}

	if config.ValidateCustom != nil {
		return config.ValidateCustom(r, f[0], f[1])
	}

	return false
}

// defaultUnauthorizedHandler provides a default HTTP 401 Unauthorized response.
func defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
}
<commit_msg>removed negroni support<commit_after>package auth

import (
	"bytes"
	"encoding/base64"
	"net/http"
	"strings"
)

const (
	schemeBasic = "Basic"
)

// Config defines options for authentication middleware.
type Config struct {
	// Validate is function to validate credentials
	Validate func(r *http.Request, username, password string) bool
	// ValidateCustom is function to validate custom authorization scheme
	ValidateCustom func(r *http.Request, scheme, custom string) bool
	// UnauthorizedHandler is optional error handler to override default error handler.
	UnauthorizedHandler http.Handler
}

func (config Config) setDefaults() Config {
	if config.UnauthorizedHandler == nil {
		config.UnauthorizedHandler = http.HandlerFunc(defaultUnauthorizedHandler)
	}
	return config
}

// Middleware returns gohttp auth middleware.
func Middleware(config Config) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return Handler(config, next)
	}
}

// Handler creates auth middleware http handler.
func Handler(config Config, next http.Handler) http.Handler {
	return &gohttpMiddleware{config.setDefaults(), next}
}

// gohttp middleware
type gohttpMiddleware struct {
	config Config
	next   http.Handler
}

// ServeHTTP implementation.
func (m *gohttpMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if validate(m.config, r) {
		m.next.ServeHTTP(w, r)
	} else {
		m.config.UnauthorizedHandler.ServeHTTP(w, r)
	}
}

// Validates the user:password combination provided in the Authorization header.
func validate(config Config, r *http.Request) bool {

	h := r.Header.Get("Authorization")
	f := strings.Fields(h)
	if len(h) == 0 || len(f) != 2 {
		return false
	}

	if f[0] == schemeBasic {
		str, err := base64.StdEncoding.DecodeString(f[1])
		if err != nil {
			return false
		}

		creds := bytes.SplitN(str, []byte(":"), 2)

		return config.Validate(r, string(creds[0]), string(creds[1]))
	}

	if config.ValidateCustom != nil {
		return config.ValidateCustom(r, f[0], f[1])
	}

	return false
}

// defaultUnauthorizedHandler provides a default HTTP 401 Unauthorized response.
func defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
	http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
}
<|endoftext|>


<commit_before>package null

import (
	"bytes"
	"database/sql/driver"
	"encoding/json"
	"errors"

	"github.com/nullbio/null/convert"
)

// Bool is a nullable bool.
// It does not consider false values to be null.
// It will decode to null, not false, if null.
type Bool struct {
	Bool  bool
	Valid bool
}

// NewBool creates a new Bool
func NewBool(b bool, valid bool) Bool {
	return Bool{
		Bool:  b,
		Valid: valid,
	}
}

// BoolFrom creates a new Bool that will always be valid.
func BoolFrom(b bool) Bool {
	return NewBool(b, true)
}

// BoolFromPtr creates a new Bool that will be null if f is nil.
func BoolFromPtr(b *bool) Bool {
	if b == nil {
		return NewBool(false, false)
	}
	return NewBool(*b, true)
}

// UnmarshalJSON implements json.Unmarshaler.
// It supports number and null input.
// 0 will not be considered a null Bool.
// It also supports unmarshalling a sql.NullBool.
func (b *Bool) UnmarshalJSON(data []byte) error {
	if bytes.Equal(data, NullBytes) {
		b.Bool = false
		b.Valid = false
		return nil
	}

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

	b.Valid = true
	return nil
}

// UnmarshalText implements encoding.TextUnmarshaler.
// It will unmarshal to a null Bool if the input is a blank or not an integer.
// It will return an error if the input is not an integer, blank, or "null".
func (b *Bool) UnmarshalText(text []byte) error {
	str := string(text)
	switch str {
	case "", "null":
		b.Valid = false
		return nil
	case "true":
		b.Bool = true
	case "false":
		b.Bool = false
	default:
		b.Valid = false
		return errors.New("invalid input:" + str)
	}
	b.Valid = true
	return nil
}

// MarshalJSON implements json.Marshaler.
// It will encode null if this Bool is null.
func (b Bool) MarshalJSON() ([]byte, error) {
	if !b.Valid {
		return []byte("null"), nil
	}
	if !b.Bool {
		return []byte("false"), nil
	}
	return []byte("true"), nil
}

// MarshalText implements encoding.TextMarshaler.
// It will encode a blank string if this Bool is null.
func (b Bool) MarshalText() ([]byte, error) {
	if !b.Valid {
		return []byte{}, nil
	}
	if !b.Bool {
		return []byte("false"), nil
	}
	return []byte("true"), nil
}

// SetValid changes this Bool's value and also sets it to be non-null.
func (b *Bool) SetValid(v bool) {
	b.Bool = v
	b.Valid = true
}

// Ptr returns a pointer to this Bool's value, or a nil pointer if this Bool is null.
func (b Bool) Ptr() *bool {
	if !b.Valid {
		return nil
	}
	return &b.Bool
}

// IsZero returns true for invalid Bools, for future omitempty support (Go 1.4?)
// A non-null Bool with a 0 value will not be considered zero.
func (b Bool) IsZero() bool {
	return !b.Valid
}

// Scan implements the Scanner interface.
func (b *Bool) Scan(value interface{}) error {
	if value == nil {
		b.Bool, b.Valid = false, false
		return nil
	}
	b.Valid = true
	return convert.ConvertAssign(&b.Bool, value)
}

// Value implements the driver Valuer interface.
func (b Bool) Value() (driver.Value, error) {
	if !b.Valid {
		return nil, nil
	}
	return b.Bool, nil
}
<commit_msg>Remove bad comment<commit_after>package null

import (
	"bytes"
	"database/sql/driver"
	"encoding/json"
	"errors"

	"github.com/nullbio/null/convert"
)

// Bool is a nullable bool.
type Bool struct {
	Bool  bool
	Valid bool
}

// NewBool creates a new Bool
func NewBool(b bool, valid bool) Bool {
	return Bool{
		Bool:  b,
		Valid: valid,
	}
}

// BoolFrom creates a new Bool that will always be valid.
func BoolFrom(b bool) Bool {
	return NewBool(b, true)
}

// BoolFromPtr creates a new Bool that will be null if f is nil.
func BoolFromPtr(b *bool) Bool {
	if b == nil {
		return NewBool(false, false)
	}
	return NewBool(*b, true)
}

// UnmarshalJSON implements json.Unmarshaler.
// It supports number and null input.
// 0 will not be considered a null Bool.
// It also supports unmarshalling a sql.NullBool.
func (b *Bool) UnmarshalJSON(data []byte) error {
	if bytes.Equal(data, NullBytes) {
		b.Bool = false
		b.Valid = false
		return nil
	}

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

	b.Valid = true
	return nil
}

// UnmarshalText implements encoding.TextUnmarshaler.
// It will unmarshal to a null Bool if the input is a blank or not an integer.
// It will return an error if the input is not an integer, blank, or "null".
func (b *Bool) UnmarshalText(text []byte) error {
	str := string(text)
	switch str {
	case "", "null":
		b.Valid = false
		return nil
	case "true":
		b.Bool = true
	case "false":
		b.Bool = false
	default:
		b.Valid = false
		return errors.New("invalid input:" + str)
	}
	b.Valid = true
	return nil
}

// MarshalJSON implements json.Marshaler.
// It will encode null if this Bool is null.
func (b Bool) MarshalJSON() ([]byte, error) {
	if !b.Valid {
		return []byte("null"), nil
	}
	if !b.Bool {
		return []byte("false"), nil
	}
	return []byte("true"), nil
}

// MarshalText implements encoding.TextMarshaler.
// It will encode a blank string if this Bool is null.
func (b Bool) MarshalText() ([]byte, error) {
	if !b.Valid {
		return []byte{}, nil
	}
	if !b.Bool {
		return []byte("false"), nil
	}
	return []byte("true"), nil
}

// SetValid changes this Bool's value and also sets it to be non-null.
func (b *Bool) SetValid(v bool) {
	b.Bool = v
	b.Valid = true
}

// Ptr returns a pointer to this Bool's value, or a nil pointer if this Bool is null.
func (b Bool) Ptr() *bool {
	if !b.Valid {
		return nil
	}
	return &b.Bool
}

// IsZero returns true for invalid Bools, for future omitempty support (Go 1.4?)
// A non-null Bool with a 0 value will not be considered zero.
func (b Bool) IsZero() bool {
	return !b.Valid
}

// Scan implements the Scanner interface.
func (b *Bool) Scan(value interface{}) error {
	if value == nil {
		b.Bool, b.Valid = false, false
		return nil
	}
	b.Valid = true
	return convert.ConvertAssign(&b.Bool, value)
}

// Value implements the driver Valuer interface.
func (b Bool) Value() (driver.Value, error) {
	if !b.Valid {
		return nil, nil
	}
	return b.Bool, nil
}
<|endoftext|>


<commit_before>package linear_models

import (
	"errors"
	"fmt"

	"github.com/sjwhitworth/golearn/base"
)

type LogisticRegression struct {
	param *Parameter
	model *Model
}

func NewLogisticRegression(penalty string, C float64, eps float64) (*LogisticRegression, error) {
	solver_type := 0
	if penalty == "l2" {
		solver_type = L2R_LR
	} else if penalty == "l1" {
		solver_type = L1R_LR
	} else {
		return nil, errors.New(fmt.Sprintf("Invalid penalty '%s'", penalty))
	}

	lr := LogisticRegression{}
	lr.param = NewParameter(solver_type, C, eps)
	lr.model = nil
	return &lr, nil
}

func (lr *LogisticRegression) Fit(X base.FixedDataGrid) error {
	problemVec := convertInstancesToProblemVec(X)
	labelVec := convertInstancesToLabelVec(X)
	prob := NewProblem(problemVec, labelVec, 0)
	lr.model = Train(prob, lr.param)
	return nil
}

func (lr *LogisticRegression) Predict(X base.FixedDataGrid) (base.FixedDataGrid, error) {

	// Only support 1 class Attribute
	classAttrs := X.AllClassAttributes()
	if len(classAttrs) != 1 {
		panic(fmt.Sprintf("%d Wrong number of classes", len(classAttrs)))
	}
	// Generate return structure
	ret := base.GeneratePredictionVector(X)
	classAttrSpecs := base.ResolveAttributes(ret, classAttrs)
	// Retrieve numeric non-class Attributes
	numericAttrs := base.NonClassFloatAttributes(X)
	numericAttrSpecs := base.ResolveAttributes(X, numericAttrs)

	// Allocate row storage
	row := make([]float64, len(numericAttrSpecs))
	X.MapOverRows(numericAttrSpecs, func(rowBytes [][]byte, rowNo int) (bool, error) {
		for i, r := range rowBytes {
			row[i] = base.UnpackBytesToFloat(r)
		}
		val := Predict(lr.model, row)
		vals := base.PackFloatToBytes(val)
		ret.Set(classAttrSpecs[0], rowNo, vals)
		return true, nil
	})

	return ret, nil
}

func (lr *LogisticRegression) String() string {
	return "LogisticRegression"
}
<commit_msg>Updating Logistic.go<commit_after>package linear_models

import (
	"errors"
	"fmt"
	"github.com/sjwhitworth/golearn/base"
)

type LogisticRegression struct {
	param *Parameter
	model *Model
}

func NewLogisticRegression(penalty string, C float64, eps float64) (*LogisticRegression, error) {
	solver_type := 0
	if penalty == "l2" {
		solver_type = L2R_LR
	} else if penalty == "l1" {
		solver_type = L1R_LR
	} else {
		return nil, errors.New(fmt.Sprintf("Invalid penalty '%s'", penalty))
	}

	lr := LogisticRegression{}
	lr.param = NewParameter(solver_type, C, eps)
	lr.model = nil
	return &lr, nil
}

func (lr *LogisticRegression) Fit(X base.FixedDataGrid) error {
	problemVec := convertInstancesToProblemVec(X)
	labelVec := convertInstancesToLabelVec(X)
	prob := NewProblem(problemVec, labelVec, 0)
	lr.model = Train(prob, lr.param)
	return nil
}

func (lr *LogisticRegression) Predict(X base.FixedDataGrid) (base.FixedDataGrid, error) {

	// Only support 1 class Attribute
	classAttrs := X.AllClassAttributes()
	if len(classAttrs) != 1 {
		panic(fmt.Sprintf("%d Wrong number of classes", len(classAttrs)))
	}
	// Generate return structure
	ret := base.GeneratePredictionVector(X)
	classAttrSpecs := base.ResolveAttributes(ret, classAttrs)
	// Retrieve numeric non-class Attributes
	numericAttrs := base.NonClassFloatAttributes(X)
	numericAttrSpecs := base.ResolveAttributes(X, numericAttrs)

	// Allocate row storage
	row := make([]float64, len(numericAttrSpecs))
	X.MapOverRows(numericAttrSpecs, func(rowBytes [][]byte, rowNo int) (bool, error) {
		for i, r := range rowBytes {
			row[i] = base.UnpackBytesToFloat(r)
		}
		val := Predict(lr.model, row)
		vals := base.PackFloatToBytes(val)
		ret.Set(classAttrSpecs[0], rowNo, vals)
		return true, nil
	})

	return ret, nil
}

func (lr *LogisticRegression) String() string {
	return "LogisticRegression"
}
<|endoftext|>


<commit_before>package cart

// Cart represents the state of a buyer's shopping cart
type Cart struct {
	items map[string]Item
}

// Item represents any item available for sale
type Item struct {
	ID    string
	Name  string
	Price float64
	Qty   int
}

// AddItem adds an item to the cart
func (c *Cart) AddItem(i Item) {}

// RemoveItem removes n number of items with give id from the cart
func (c *Cart) RemoveItem(id string, n int) {}

// TotalAmount returns the total amount of the cart
func (c *Cart) TotalAmount() float64 {
	return 324 // return a random number as a placeholder
}

// TotalUnits returns the total number of units across all items in the cart
func (c *Cart) TotalUnits() int {
	return 9932 // return a random number as a placeholder
}

// TotalUniqueItems returns the number of unique items in the cart
func (c *Cart) TotalUniqueItems() int {
	return 24 // return a random number as a placeholder
}
<commit_msg>Complete app<commit_after>package cart

// Cart represents the state of a buyer's shopping cart
type Cart struct {
	items map[string]Item
}

// Item represents any item available for sale
type Item struct {
	ID    string
	Name  string
	Price float64
	Qty   int
}

func (c *Cart) init() {
	if c.items == nil {
		c.items = map[string]Item{}
	}
}

// AddItem adds an item to the cart
func (c *Cart) AddItem(i Item) {
	c.init()
	if existingItem, ok := c.items[i.ID]; ok {
		existingItem.Qty++
		c.items[i.ID] = existingItem
	} else {
		i.Qty = 1
		c.items[i.ID] = i
	}
}

// RemoveItem removes n number of items with give id from the cart
func (c *Cart) RemoveItem(id string, n int) {
	c.init()
	if existingItem, ok := c.items[id]; ok {
		if existingItem.Qty <= n {
			delete(c.items, id)
		} else {
			existingItem.Qty -= n
			c.items[id] = existingItem
		}
	}
}

// TotalAmount returns the total amount of the cart
func (c *Cart) TotalAmount() float64 {
	c.init()
	totalAmount := 0.0
	for _, i := range c.items {
		totalAmount += i.Price * float64(i.Qty)
	}
	return totalAmount
}

// TotalUnits returns the total number of units across all items in the cart
func (c *Cart) TotalUnits() int {
	c.init()
	totalUnits := 0
	for _, i := range c.items {
		totalUnits += i.Qty
	}
	return totalUnits
}

// TotalUniqueItems returns the number of unique items in the cart
func (c *Cart) TotalUniqueItems() int {
	return len(c.items)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"github.com/kr/pretty"
	"gopkg.in/yaml.v1"
)

type Config struct {
	ExcludeLines  []*regexp.Regexp
	ExcludeFiles  []*regexp.Regexp
	MinLineLength int
}

func LoadConfig() Config {
	config := Config{
		ExcludeLines:  []*regexp.Regexp{},
		ExcludeFiles:  []*regexp.Regexp{},
		MinLineLength: 10,
	}

	data, err := ioutil.ReadFile(".cccv.yml")
	if err != nil {
		return config
	}

	t := struct {
		ExcludeFiles  []string "exclude-files"
		ExcludeLines  []string "exclude-lines"
		MinLineLength int      "min-line-length"
	}{}
	err = yaml.Unmarshal(data, &t)
	if err != nil {
		log.Fatalf("error: %v", err)
		os.Exit(1)
	}

	for _, s := range t.ExcludeLines {
		r := regexp.MustCompile(s)
		config.ExcludeLines = append(config.ExcludeLines, r)
	}
	for _, s := range t.ExcludeFiles {
		r := regexp.MustCompile(s)
		config.ExcludeFiles = append(config.ExcludeFiles, r)
	}
	if t.MinLineLength != 0 {
		config.MinLineLength = t.MinLineLength
	}
	return config
}

type FileName string

type Change struct {
	FileName
	Line
}

type Line struct {
	Number int
	Text   string
}

type FileResult struct {
	FileName
	Lines []*Line
}

func (fr *FileResult) HasDuplicates() bool {
	return len(fr.Lines) > 0
}

func main() {
	config := LoadConfig()

	results := []FileResult{}

	changes := getChanges(os.Stdin, config)
	gitFiles := gitLsFiles(config)

	for _, fName := range gitFiles {
		r := GenResultForFile(fName, changes, config)
		results = append(results, r)
	}

	thereAreDuplicates := false
	for _, r := range results {
		if r.HasDuplicates() {
			thereAreDuplicates = true
		}
	}

	if thereAreDuplicates {
		pretty.Print(results)
		os.Exit(1)
	}
}

func GenResultForFile(fName string, changes *[]*Change, config Config) FileResult {
	file, _ := os.Open(fName)
	scanner := bufio.NewScanner(file)
	currentLineNumber := 0
	result := FileResult{FileName: FileName(fName), Lines: []*Line{}}

LOOP_LINES:
	for scanner.Scan() {
		line := scanner.Text()
		currentLineNumber++

		for _, excludeLinesR := range config.ExcludeLines {
			if excludeLinesR.MatchString(line) {
				continue LOOP_LINES
			}
		}

		for _, change := range *changes {
			if strings.TrimFunc(change.Text, TrimF) == strings.TrimFunc(line, TrimF) {

				// exclude lines from the diff itself
				if string(change.FileName) == fName && change.Line.Number == currentLineNumber {
					continue
				}

				resultAlreadyRecorded := false
				for _, resultLine := range result.Lines {
					if resultLine.Number == currentLineNumber && resultLine.Text == line {
						resultAlreadyRecorded = true
					}
				}

				if !resultAlreadyRecorded {
					result.Lines = append(result.Lines, &Line{Number: currentLineNumber, Text: line})
				}
			}
		}
	}
	return result
}

func gitLsFiles(config Config) []string {
	files := []string{}
	cmd := exec.Command("git", "ls-files")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
	if err := cmd.Start(); err != nil {
		log.Fatal(err)
		os.Exit(1)
	}

	scanner := bufio.NewScanner(stdout)

LOOP_FILES:
	for scanner.Scan() {
		for _, excludeFilesR := range config.ExcludeFiles {
			if excludeFilesR.MatchString(scanner.Text()) {
				continue LOOP_FILES
			}
		}
		files = append(files, scanner.Text())
	}

	return files
}

func getChanges(reader io.Reader, config Config) *[]*Change {
	scanner := bufio.NewScanner(reader)
	var currentFile string
	var currentLineNumber int
	changes := &[]*Change{}

	currentFileR := regexp.MustCompile(`^\+\+\+ ./(.*)$`)
	lineAddedR := regexp.MustCompile(`^\+{1}(.*\w+.*)`)
	lineRemovedR := regexp.MustCompile(`^\-{1}`)
	lineRangeR := regexp.MustCompile(`^@@.*?\+(\d+?),`)

	for scanner.Scan() {
		currentLine := scanner.Text()

		if res := currentFileR.FindStringSubmatch(currentLine); res != nil {
			currentFile = res[1]

		} else if res := lineRangeR.FindStringSubmatch(currentLine); res != nil {
			r, err := strconv.Atoi(res[1])
			if err != nil {
				log.Fatal(err)
				os.Exit(1)
			}
			currentLineNumber = r

		} else if lineAddedR.MatchString(currentLine) {
			res := lineAddedR.FindStringSubmatch(currentLine)

			if len(strings.TrimFunc(res[1], TrimF)) <= config.MinLineLength {
				currentLineNumber++
				continue
			}

			newChange := &Change{
				FileName: FileName(currentFile),
				Line:     Line{Text: res[1], Number: currentLineNumber},
			}
			*changes = append(*changes, newChange)
			currentLineNumber++

		} else if !lineRemovedR.MatchString(currentLine) {
			currentLineNumber++
		}
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
		os.Exit(1)
	}

	return changes
}

func TrimF(c rune) bool { return c == 32 || c == 9 }
<commit_msg>speed up<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/kr/pretty"
	"gopkg.in/yaml.v1"
)

type Config struct {
	ExcludeLines  []*regexp.Regexp
	ExcludeFiles  []*regexp.Regexp
	MinLineLength int
}

func LoadConfig() Config {
	config := Config{
		ExcludeLines:  []*regexp.Regexp{},
		ExcludeFiles:  []*regexp.Regexp{},
		MinLineLength: 10,
	}

	data, err := ioutil.ReadFile(".cccv.yml")
	if err != nil {
		return config
	}

	t := struct {
		ExcludeFiles  []string "exclude-files"
		ExcludeLines  []string "exclude-lines"
		MinLineLength int      "min-line-length"
	}{}
	err = yaml.Unmarshal(data, &t)
	if err != nil {
		log.Fatalf("error: %v", err)
		os.Exit(1)
	}

	for _, s := range t.ExcludeLines {
		r := regexp.MustCompile(s)
		config.ExcludeLines = append(config.ExcludeLines, r)
	}
	for _, s := range t.ExcludeFiles {
		r := regexp.MustCompile(s)
		config.ExcludeFiles = append(config.ExcludeFiles, r)
	}
	if t.MinLineLength != 0 {
		config.MinLineLength = t.MinLineLength
	}
	return config
}

type FileName string

type Change struct {
	FileName
	Line
}

type Line struct {
	Number int
	Text   string
}

type FileResult struct {
	FileName
	Lines []*Line
}

func (fr *FileResult) HasDuplicates() bool {
	return len(fr.Lines) > 0
}

func main() {
	var wg sync.WaitGroup
	config := LoadConfig()

	resultChan := make(chan FileResult)
	results := []FileResult{}

	changes := getChanges(os.Stdin, config)
	gitFiles := gitLsFiles(config)

	go func() {
		for {
			r := <-resultChan
			results = append(results, r)
		}
	}()

	for _, fName := range gitFiles {
		wg.Add(1)
		go func(fName string, resultChan chan FileResult) {
			defer wg.Done()
			r := GenResultForFile(fName, changes, config)
			resultChan <- r
		}(fName, resultChan)
	}
	wg.Wait()

	thereAreDuplicates := false
	for _, r := range results {
		if r.HasDuplicates() {
			thereAreDuplicates = true
		}
	}

	if thereAreDuplicates {
		pretty.Print(results)
		os.Exit(1)
	}
}

func GenResultForFile(fName string, changes *[]*Change, config Config) FileResult {
	file, _ := os.Open(fName)
	scanner := bufio.NewScanner(file)
	currentLineNumber := 0
	result := FileResult{FileName: FileName(fName), Lines: []*Line{}}

LOOP_LINES:
	for scanner.Scan() {
		line := scanner.Text()
		currentLineNumber++

		for _, excludeLinesR := range config.ExcludeLines {
			if excludeLinesR.MatchString(line) {
				continue LOOP_LINES
			}
		}

		for _, change := range *changes {
			if strings.TrimFunc(change.Text, TrimF) == strings.TrimFunc(line, TrimF) {

				// exclude lines from the diff itself
				if string(change.FileName) == fName && change.Line.Number == currentLineNumber {
					continue
				}

				resultAlreadyRecorded := false
				for _, resultLine := range result.Lines {
					if resultLine.Number == currentLineNumber && resultLine.Text == line {
						resultAlreadyRecorded = true
					}
				}

				if !resultAlreadyRecorded {
					result.Lines = append(result.Lines, &Line{Number: currentLineNumber, Text: line})
				}
			}
		}
	}
	return result
}

func gitLsFiles(config Config) []string {
	files := []string{}
	cmd := exec.Command("git", "ls-files")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
	if err := cmd.Start(); err != nil {
		log.Fatal(err)
		os.Exit(1)
	}

	scanner := bufio.NewScanner(stdout)

LOOP_FILES:
	for scanner.Scan() {
		for _, excludeFilesR := range config.ExcludeFiles {
			if excludeFilesR.MatchString(scanner.Text()) {
				continue LOOP_FILES
			}
		}
		files = append(files, scanner.Text())
	}

	return files
}

func getChanges(reader io.Reader, config Config) *[]*Change {
	scanner := bufio.NewScanner(reader)
	var currentFile string
	var currentLineNumber int
	changes := &[]*Change{}

	currentFileR := regexp.MustCompile(`^\+\+\+ ./(.*)$`)
	lineAddedR := regexp.MustCompile(`^\+{1}(.*\w+.*)`)
	lineRemovedR := regexp.MustCompile(`^\-{1}`)
	lineRangeR := regexp.MustCompile(`^@@.*?\+(\d+?),`)

	for scanner.Scan() {
		currentLine := scanner.Text()

		if res := currentFileR.FindStringSubmatch(currentLine); res != nil {
			currentFile = res[1]

		} else if res := lineRangeR.FindStringSubmatch(currentLine); res != nil {
			r, err := strconv.Atoi(res[1])
			if err != nil {
				log.Fatal(err)
				os.Exit(1)
			}
			currentLineNumber = r

		} else if lineAddedR.MatchString(currentLine) {
			res := lineAddedR.FindStringSubmatch(currentLine)

			if len(strings.TrimFunc(res[1], TrimF)) <= config.MinLineLength {
				currentLineNumber++
				continue
			}

			newChange := &Change{
				FileName: FileName(currentFile),
				Line:     Line{Text: res[1], Number: currentLineNumber},
			}
			*changes = append(*changes, newChange)
			currentLineNumber++

		} else if !lineRemovedR.MatchString(currentLine) {
			currentLineNumber++
		}
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
		os.Exit(1)
	}

	return changes
}

func TrimF(c rune) bool { return c == 32 || c == 9 }
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME     = ""
	DEFAULT_MESSAGE_ASUSER       = false
	DEFAULT_MESSAGE_PARSE        = ""
	DEFAULT_MESSAGE_LINK_NAMES   = 0
	DEFAULT_MESSAGE_UNFURL_LINKS = false
	DEFAULT_MESSAGE_UNFURL_MEDIA = true
	DEFAULT_MESSAGE_ICON_URL     = ""
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
	DEFAULT_MESSAGE_MARKDOWN     = true
	DEFAULT_MESSAGE_ESCAPE_TEXT  = true
)

type chatResponseFull struct {
	Channel   string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text        string
	Username    string
	AsUser      bool
	Parse       string
	LinkNames   int
	Attachments []Attachment
	UnfurlLinks bool
	UnfurlMedia bool
	IconURL     string
	IconEmoji   string
	Markdown    bool `json:"mrkdwn,omitempty"`
	EscapeText  bool
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		AsUser:      DEFAULT_MESSAGE_ASUSER,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
		Markdown:    DEFAULT_MESSAGE_MARKDOWN,
		EscapeText:  DEFAULT_MESSAGE_ESCAPE_TEXT,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.Channel, response.Timestamp, nil
}

func escapeMessage(message string) string {
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
	if params.EscapeText {
		text = escapeMessage(text)
	}
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"text":    {text},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.AsUser != DEFAULT_MESSAGE_ASUSER {
		values.Set("as_user", "true")
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "true")
	}
	// I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
	// Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
	if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "false")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}
	if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
		values.Set("mrkdwn", "false")
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.Channel, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"text":    {escapeMessage(text)},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.Channel, response.Timestamp, response.Text, nil
}
<commit_msg>add json directives to PostMessageParameters<commit_after>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME     = ""
	DEFAULT_MESSAGE_ASUSER       = false
	DEFAULT_MESSAGE_PARSE        = ""
	DEFAULT_MESSAGE_LINK_NAMES   = 0
	DEFAULT_MESSAGE_UNFURL_LINKS = false
	DEFAULT_MESSAGE_UNFURL_MEDIA = true
	DEFAULT_MESSAGE_ICON_URL     = ""
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
	DEFAULT_MESSAGE_MARKDOWN     = true
	DEFAULT_MESSAGE_ESCAPE_TEXT  = true
)

type chatResponseFull struct {
	Channel   string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text        string       `json:"text"`
	Username    string       `json:"user_name"`
	AsUser      bool         `json:"as_user"`
	Parse       string       `json:"parse"`
	LinkNames   int          `json:"link_names"`
	Attachments []Attachment `json:"attachments"`
	UnfurlLinks bool         `json:"unfurl_links"`
	UnfurlMedia bool         `json:"unfurl_media"`
	IconURL     string       `json:"icon_media"`
	IconEmoji   string       `json:"icon_emoji"`
	Markdown    bool         `json:"mrkdwn,omitempty"`
	EscapeText  bool         `json:"escape_text"`
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		AsUser:      DEFAULT_MESSAGE_ASUSER,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
		Markdown:    DEFAULT_MESSAGE_MARKDOWN,
		EscapeText:  DEFAULT_MESSAGE_ESCAPE_TEXT,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.Channel, response.Timestamp, nil
}

func escapeMessage(message string) string {
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
	if params.EscapeText {
		text = escapeMessage(text)
	}
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"text":    {text},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.AsUser != DEFAULT_MESSAGE_ASUSER {
		values.Set("as_user", "true")
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "true")
	}
	// I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
	// Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
	if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "false")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}
	if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
		values.Set("mrkdwn", "false")
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.Channel, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"text":    {escapeMessage(text)},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.Channel, response.Timestamp, response.Text, nil
}
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME     = ""
	DEFAULT_MESSAGE_PARSE        = ""
	DEFAULT_MESSAGE_LINK_NAMES   = 0
	DEFAULT_MESSAGE_UNFURL_LINKS = true
	DEFAULT_MESSAGE_UNFURL_MEDIA = false
	DEFAULT_MESSAGE_ICON_URL     = ""
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
)

type chatResponseFull struct {
	ChannelId string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// AttachmentField contains information for an attachment field
// An Attachment can contain multiple of these
type AttachmentField struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// Attachment contains all the information for an attachment
type Attachment struct {
	Fallback string `json:"fallback"`

	Color string `json:"color,omitempty"`

	Pretext string `json:"pretext,omitempty"`

	AuthorName string `json:"author_name,omitempty"`
	AuthorLink string `json:"author_link,omitempty"`
	AuthorIcon string `json:"author_icon,omitempty"`

	Title     string `json:"title,omitempty"`
	TitleLink string `json:"title_link,omitempty"`

	Text string `json:"text"`

	Fields []AttachmentField `json:"fields,omitempty"`
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text        string
	Username    string
	Parse       string
	LinkNames   int
	Attachments []Attachment
	UnfurlLinks bool
	UnfurlMedia bool
	IconURL     string
	IconEmoji   string
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Slack) DeleteMessage(channelId, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

func escapeMessage(message string) string {
	/*
		& replaced with &
		< replaced with <
		> replaced with >
	*/
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel
// Message is automatically escaped according to https://api.slack.com/docs/formatting
func (api *Slack) PostMessage(channelId string, text string, params PostMessageParameters) (channel string, timestamp string, err error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {escapeMessage(text)},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "true")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Slack) UpdateMessage(channelId, timestamp, text string) (string, string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {escapeMessage(text)},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.ChannelId, response.Timestamp, response.Text, nil
}
<commit_msg>Add new image_url parameter to an attachment<commit_after>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME     = ""
	DEFAULT_MESSAGE_PARSE        = ""
	DEFAULT_MESSAGE_LINK_NAMES   = 0
	DEFAULT_MESSAGE_UNFURL_LINKS = true
	DEFAULT_MESSAGE_UNFURL_MEDIA = false
	DEFAULT_MESSAGE_ICON_URL     = ""
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
)

type chatResponseFull struct {
	ChannelId string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// AttachmentField contains information for an attachment field
// An Attachment can contain multiple of these
type AttachmentField struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// Attachment contains all the information for an attachment
type Attachment struct {
	Fallback string `json:"fallback"`

	Color string `json:"color,omitempty"`

	Pretext string `json:"pretext,omitempty"`

	AuthorName string `json:"author_name,omitempty"`
	AuthorLink string `json:"author_link,omitempty"`
	AuthorIcon string `json:"author_icon,omitempty"`

	Title     string `json:"title,omitempty"`
	TitleLink string `json:"title_link,omitempty"`

	Text string `json:"text"`

	ImageURL string `json:"image_url,omitempty"`

	Fields []AttachmentField `json:"fields,omitempty"`
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text        string
	Username    string
	Parse       string
	LinkNames   int
	Attachments []Attachment
	UnfurlLinks bool
	UnfurlMedia bool
	IconURL     string
	IconEmoji   string
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Slack) DeleteMessage(channelId, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

func escapeMessage(message string) string {
	/*
		& replaced with &
		< replaced with <
		> replaced with >
	*/
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel
// Message is automatically escaped according to https://api.slack.com/docs/formatting
func (api *Slack) PostMessage(channelId string, text string, params PostMessageParameters) (channel string, timestamp string, err error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {escapeMessage(text)},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "true")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Slack) UpdateMessage(channelId, timestamp, text string) (string, string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {escapeMessage(text)},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.ChannelId, response.Timestamp, response.Text, nil
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

var (
	gCmdWords = []string{
		"set",
		"map",
		"cmd",
		"up",
		"half-up",
		"page-up",
		"down",
		"half-down",
		"page-down",
		"updir",
		"open",
		"quit",
		"bot",
		"top",
		"read",
		"read-shell",
		"read-shell-wait",
		"read-shell-async",
		"search",
		"search-back",
		"toggle",
		"invert",
		"yank",
		"delete",
		"put",
		"renew",
		"sync",
		"echo",
		"cd",
		"push",
	}

	gOptWords = []string{
		"dirfirst",
		"nodirfirst",
		"dirfirst!",
		"hidden",
		"nohidden",
		"hidden!",
		"preview",
		"nopreview",
		"preview!",
		"scrolloff",
		"tabstop",
		"ifs",
		"previewer",
		"shell",
		"showinfo",
		"sortby",
		"timefmt",
		"ratios",
	}
)

func matchLongest(s1, s2 string) string {
	i := 0
	for ; i < len(s1) && i < len(s2); i++ {
		if s1[i] != s2[i] {
			break
		}
	}
	return s1[:i]
}

func matchWord(s string, words []string) (matches []string, longest string) {
	for _, w := range words {
		if strings.HasPrefix(w, s) {
			matches = append(matches, w)
			if longest != "" {
				longest = matchLongest(longest, w)
			} else if s != "" {
				longest = w + " "
			}
		}
	}

	if longest == "" {
		longest = s
	}

	return
}

func matchExec(s string) (matches []string, longest string) {
	var words []string

	paths := strings.Split(envPath, ":")

	for _, p := range paths {
		if _, err := os.Stat(p); os.IsNotExist(err) {
			continue
		}

		fi, err := ioutil.ReadDir(p)
		if err != nil {
			log.Printf("reading path: %s", err)
		}

		for _, f := range fi {
			if strings.HasPrefix(f.Name(), s) {
				f, err = os.Stat(filepath.Join(p, f.Name()))
				if err != nil {
					log.Printf("getting file information: %s", err)
				}

				if !f.Mode().IsRegular() || f.Mode()&0111 == 0 {
					continue
				}

				words = append(words, f.Name())
			}
		}
	}

	sort.Strings(words)

	if len(words) > 0 {
		uniq := words[:1]
		for i := 1; i < len(words); i++ {
			if words[i] != words[i-1] {
				uniq = append(uniq, words[i])
			}
		}
		words = uniq
	}

	return matchWord(s, words)
}

func matchFile(s string) (matches []string, longest string) {
	dir := strings.Replace(s, "~", envHome, -1)

	if !filepath.IsAbs(dir) {
		wd, err := os.Getwd()
		if err != nil {
			log.Printf("getting current directory: %s", err)
		}
		dir = wd + string(filepath.Separator) + dir
	}

	dir = unescape(filepath.Dir(dir))

	fi, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Printf("reading directory: %s", err)
	}

	for _, f := range fi {
		f, err := os.Stat(filepath.Join(dir, f.Name()))
		if err != nil {
			log.Printf("getting file information: %s", err)
			return
		}

		_, last := filepath.Split(s)
		if strings.HasPrefix(escape(f.Name()), last) {
			name := f.Name()
			if isRoot(s) || filepath.Base(s) != s {
				name = filepath.Join(filepath.Dir(s), f.Name())
			}
			name = escape(name)
			item := f.Name()
			if f.Mode().IsDir() {
				item += string(filepath.Separator)
			}
			matches = append(matches, item)
			if longest != "" {
				longest = matchLongest(longest, name)
			} else if s != "" {
				if f.Mode().IsRegular() {
					longest = name + " "
				} else {
					longest = name + string(filepath.Separator)
				}
			}
		}
	}

	if longest == "" {
		longest = s
	}

	return
}

func compCmd(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)
	f := tokenize(s)

	var longest string

	switch len(f) {
	case 1:
		words := gCmdWords
		for c, _ := range gOpts.cmds {
			words = append(words, c)
		}
		sort.Strings(words)
		matches, longest = matchWord(s, words)
		longestAcc = []rune(longest)
	case 2:
		switch f[0] {
		case "set":
			matches, longest = matchWord(f[1], gOptWords)
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		case "map", "cmd":
			longestAcc = acc
		default:
			matches, longest = matchFile(f[len(f)-1])
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		}
	default:
		switch f[0] {
		case "set", "map", "cmd":
			longestAcc = acc
		default:
			matches, longest = matchFile(f[len(f)-1])
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		}
	}

	return
}

func compShell(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)
	f := tokenize(s)

	var longest string

	switch len(f) {
	case 1:
		matches, longest = matchExec(s)
		longestAcc = []rune(longest)
	default:
		matches, longest = matchFile(f[len(f)-1])
		longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
	}

	return
}
<commit_msg>skip broken links for executable completion<commit_after>package main

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

var (
	gCmdWords = []string{
		"set",
		"map",
		"cmd",
		"up",
		"half-up",
		"page-up",
		"down",
		"half-down",
		"page-down",
		"updir",
		"open",
		"quit",
		"bot",
		"top",
		"read",
		"read-shell",
		"read-shell-wait",
		"read-shell-async",
		"search",
		"search-back",
		"toggle",
		"invert",
		"yank",
		"delete",
		"put",
		"renew",
		"sync",
		"echo",
		"cd",
		"push",
	}

	gOptWords = []string{
		"dirfirst",
		"nodirfirst",
		"dirfirst!",
		"hidden",
		"nohidden",
		"hidden!",
		"preview",
		"nopreview",
		"preview!",
		"scrolloff",
		"tabstop",
		"ifs",
		"previewer",
		"shell",
		"showinfo",
		"sortby",
		"timefmt",
		"ratios",
	}
)

func matchLongest(s1, s2 string) string {
	i := 0
	for ; i < len(s1) && i < len(s2); i++ {
		if s1[i] != s2[i] {
			break
		}
	}
	return s1[:i]
}

func matchWord(s string, words []string) (matches []string, longest string) {
	for _, w := range words {
		if strings.HasPrefix(w, s) {
			matches = append(matches, w)
			if longest != "" {
				longest = matchLongest(longest, w)
			} else if s != "" {
				longest = w + " "
			}
		}
	}

	if longest == "" {
		longest = s
	}

	return
}

func matchExec(s string) (matches []string, longest string) {
	var words []string

	paths := strings.Split(envPath, ":")

	for _, p := range paths {
		if _, err := os.Stat(p); os.IsNotExist(err) {
			continue
		}

		fi, err := ioutil.ReadDir(p)
		if err != nil {
			log.Printf("reading path: %s", err)
		}

		for _, f := range fi {
			if strings.HasPrefix(f.Name(), s) {
				f, err = os.Stat(filepath.Join(p, f.Name()))
				if err != nil {
					log.Printf("getting file information: %s", err)
					continue
				}

				if !f.Mode().IsRegular() || f.Mode()&0111 == 0 {
					continue
				}

				words = append(words, f.Name())
			}
		}
	}

	sort.Strings(words)

	if len(words) > 0 {
		uniq := words[:1]
		for i := 1; i < len(words); i++ {
			if words[i] != words[i-1] {
				uniq = append(uniq, words[i])
			}
		}
		words = uniq
	}

	return matchWord(s, words)
}

func matchFile(s string) (matches []string, longest string) {
	dir := strings.Replace(s, "~", envHome, -1)

	if !filepath.IsAbs(dir) {
		wd, err := os.Getwd()
		if err != nil {
			log.Printf("getting current directory: %s", err)
		}
		dir = wd + string(filepath.Separator) + dir
	}

	dir = unescape(filepath.Dir(dir))

	fi, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Printf("reading directory: %s", err)
	}

	for _, f := range fi {
		f, err := os.Stat(filepath.Join(dir, f.Name()))
		if err != nil {
			log.Printf("getting file information: %s", err)
			return
		}

		_, last := filepath.Split(s)
		if strings.HasPrefix(escape(f.Name()), last) {
			name := f.Name()
			if isRoot(s) || filepath.Base(s) != s {
				name = filepath.Join(filepath.Dir(s), f.Name())
			}
			name = escape(name)
			item := f.Name()
			if f.Mode().IsDir() {
				item += string(filepath.Separator)
			}
			matches = append(matches, item)
			if longest != "" {
				longest = matchLongest(longest, name)
			} else if s != "" {
				if f.Mode().IsRegular() {
					longest = name + " "
				} else {
					longest = name + string(filepath.Separator)
				}
			}
		}
	}

	if longest == "" {
		longest = s
	}

	return
}

func compCmd(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)
	f := tokenize(s)

	var longest string

	switch len(f) {
	case 1:
		words := gCmdWords
		for c, _ := range gOpts.cmds {
			words = append(words, c)
		}
		sort.Strings(words)
		matches, longest = matchWord(s, words)
		longestAcc = []rune(longest)
	case 2:
		switch f[0] {
		case "set":
			matches, longest = matchWord(f[1], gOptWords)
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		case "map", "cmd":
			longestAcc = acc
		default:
			matches, longest = matchFile(f[len(f)-1])
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		}
	default:
		switch f[0] {
		case "set", "map", "cmd":
			longestAcc = acc
		default:
			matches, longest = matchFile(f[len(f)-1])
			longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
		}
	}

	return
}

func compShell(acc []rune) (matches []string, longestAcc []rune) {
	s := string(acc)
	f := tokenize(s)

	var longest string

	switch len(f) {
	case 1:
		matches, longest = matchExec(s)
		longestAcc = []rune(longest)
	default:
		matches, longest = matchFile(f[len(f)-1])
		longestAcc = append(acc[:len(acc)-len(f[len(f)-1])], []rune(longest)...)
	}

	return
}
<|endoftext|>


<commit_before>package conf

import (
	"bufio"
	"io"
	"os"
	"strings"
	"strconv"
)

const middle = "========="

type config struct {
	mymap  map[string]string
	strcet string
}

var confMap map[string]*config

func init() {
	confMap = make(map[string]*config)
}

func NewConfig(path string) config {
	if _, ok := confMap[path]; !ok {
		confMap[path] = new(config)
		(*confMap[path]).initConfig(path)
	}
	return *confMap[path]
}

func (c *config) initConfig(path string) {
	c.mymap = make(map[string]string)
	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	r := bufio.NewReader(f)
	for {
		b, _, err := r.ReadLine()
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

		s := strings.TrimSpace(string(b))
		//fmt.Println(s)
		if strings.Index(s, "#") == 0 {
			continue
		}

		n1 := strings.Index(s, "[")
		n2 := strings.LastIndex(s, "]")
		if n1 > -1 && n2 > -1 && n2 > n1+1 {
			c.strcet = strings.TrimSpace(s[n1+1 : n2])
			continue
		}

		if len(c.strcet) == 0 {
			continue
		}
		index := strings.Index(s, "=")
		if index < 0 {
			continue
		}
		frist := strings.TrimSpace(s[:index])
		if len(frist) == 0 {
			continue
		}
		second := strings.TrimSpace(s[index+1:])

		pos := strings.Index(second, "\t#")
		if pos > -1 {
			second = second[0:pos]
		}
		pos = strings.Index(second, " #")
		if pos > -1 {
			second = second[0:pos]
		}
		pos = strings.Index(second, "\t//")
		if pos > -1 {
			second = second[0:pos]
		}
		pos = strings.Index(second, " //")
		if pos > -1 {
			second = second[0:pos]
		}
		if len(second) == 0 {
			continue
		}
		key := c.strcet + middle + frist
		c.mymap[key] = strings.TrimSpace(second)
	}
}

func (c config) Read(section, key string) string {
	key = section + middle + key
	v, found := c.mymap[key]
	if !found {
		return ""
	}
	return v
}

func (c config)GetInt(section, key string) int {
	 value,_ := strconv.Atoi(c.Read(section, key ))
	 return value
}

func (c config)GetInt64(section,key string) int64 {
    value,_ := strconv.ParseInt(c.Read(section, key),10,64)
    return value
}

func (c config)GetBool(section ,key string) bool {
    value,_ := strconv.ParseBool(c.Read(section, key ))
    return value
}

func (c config)GetFloat64(section ,key string ) float64 {
    value,_ := strconv.ParseFloat(c.Read(section, key ),64)
    return value
}

func (c config) GetArray(section, key, delim string) []string {
    value := c.Read(section, key)
    if value == "" {
        return []string{}
    }
    values := strings.Split(value, delim)
    for i := range values {
        values[i] = strings.TrimSpace(values[i])
    }
    return values
}<commit_msg>新增GetMap<commit_after>package conf

import (
	"bufio"
	"io"
	"os"
	"strings"
	"strconv"
)

const middle = "========="

type config struct {
	mymap  map[string]string
	strcet string
}

var confMap map[string]*config

func init() {
	confMap = make(map[string]*config)
}

func NewConfig(path string) config {
	if _, ok := confMap[path]; !ok {
		confMap[path] = new(config)
		(*confMap[path]).initConfig(path)
	}
	return *confMap[path]
}

func (c *config) initConfig(path string) {
	c.mymap = make(map[string]string)
	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	r := bufio.NewReader(f)
	for {
		b, _, err := r.ReadLine()
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

		s := strings.TrimSpace(string(b))
		//fmt.Println(s)
		if strings.Index(s, "#") == 0 {
			continue
		}

		n1 := strings.Index(s, "[")
		n2 := strings.LastIndex(s, "]")
		if n1 > -1 && n2 > -1 && n2 > n1+1 {
			c.strcet = strings.TrimSpace(s[n1+1 : n2])
			continue
		}

		if len(c.strcet) == 0 {
			continue
		}
		index := strings.Index(s, "=")
		if index < 0 {
			continue
		}
		frist := strings.TrimSpace(s[:index])
		if len(frist) == 0 {
			continue
		}
		second := strings.TrimSpace(s[index+1:])

		pos := strings.Index(second, "\t#")
		if pos > -1 {
			second = second[0:pos]
		}
		pos = strings.Index(second, " #")
		if pos > -1 {
			second = second[0:pos]
		}
		pos = strings.Index(second, "\t//")
		if pos > -1 {
			second = second[0:pos]
		}
		pos = strings.Index(second, " //")
		if pos > -1 {
			second = second[0:pos]
		}
		if len(second) == 0 {
			continue
		}
		key := c.strcet + middle + frist
		c.mymap[key] = strings.TrimSpace(second)
	}
}

func (c config) Read(section, key string) string {
	key = section + middle + key
	v, found := c.mymap[key]
	if !found {
		return ""
	}
	return v
}

func (c config) GetMap(section string) map[string]string{
	tmap := make(map[string]string , 10)
	for k,v:= range c.mymap {
		if strings.HasPrefix(k,section+middle){
			tmap[strings.TrimPrefix(k,section+middle)] = v
		}
	}
	return tmap
}

func (c config)GetInt(section, key string) int {
	 value,_ := strconv.Atoi(c.Read(section, key ))
	 return value
}

func (c config)GetInt64(section,key string) int64 {
    value,_ := strconv.ParseInt(c.Read(section, key),10,64)
    return value
}

func (c config)GetBool(section ,key string) bool {
    value,_ := strconv.ParseBool(c.Read(section, key ))
    return value
}

func (c config)GetFloat64(section ,key string ) float64 {
    value,_ := strconv.ParseFloat(c.Read(section, key ),64)
    return value
}

func (c config) GetArray(section, key, delim string) []string {
    value := c.Read(section, key)
    if value == "" {
        return []string{}
    }
    values := strings.Split(value, delim)
    for i := range values {
        values[i] = strings.TrimSpace(values[i])
    }
    return values
}<|endoftext|>


<commit_before>package imap

import (
	"bufio"
	"io"
	"net"
)

// A connection state.
// See RFC 3501 section 3.
type ConnState int

const (
	// In the connecting state, the server has not yet sent a greeting and no
	// command can be issued.
	ConnectingState = 0

	// In the not authenticated state, the client MUST supply
	// authentication credentials before most commands will be
	// permitted.  This state is entered when a connection starts
	// unless the connection has been pre-authenticated.
	NotAuthenticatedState ConnState = 1 << 0

	// In the authenticated state, the client is authenticated and MUST
	// select a mailbox to access before commands that affect messages
	// will be permitted.  This state is entered when a
	// pre-authenticated connection starts, when acceptable
	// authentication credentials have been provided, after an error in
	// selecting a mailbox, or after a successful CLOSE command.
	AuthenticatedState = 1 << 1

	// In a selected state, a mailbox has been selected to access.
	// This state is entered when a mailbox has been successfully
	// selected.
	SelectedState = AuthenticatedState + 1<<2

	// In the logout state, the connection is being terminated. This
	// state can be entered as a result of a client request (via the
	// LOGOUT command) or by unilateral action on the part of either
	// the client or server.
	LogoutState = 1 << 3

	// ConnectedState is either NotAuthenticatedState, AuthenticatedState or
	// SelectedState.
	ConnectedState = NotAuthenticatedState | AuthenticatedState | SelectedState
)

// A function that upgrades a connection.
//
// This should only be used by libraries implementing an IMAP extension (e.g.
// COMPRESS).
type ConnUpgrader func(conn net.Conn) (net.Conn, error)

type debugWriter struct {
	io.Writer

	local  io.Writer
	remote io.Writer
}

// NewDebugWriter creates a new io.Writer that will write local network activity
// to local and remote network activity to remote.
func NewDebugWriter(local, remote io.Writer) io.Writer {
	return &debugWriter{Writer: local, local: local, remote: remote}
}

type multiFlusher struct {
	flushers []flusher
}

func (mf *multiFlusher) Flush() error {
	for _, f := range mf.flushers {
		if err := f.Flush(); err != nil {
			return err
		}
	}
	return nil
}

func newMultiFlusher(flushers ...flusher) flusher {
	return &multiFlusher{flushers}
}

// An IMAP connection.
type Conn struct {
	net.Conn
	*Reader
	*Writer

	br *bufio.Reader
	bw *bufio.Writer

	waits chan struct{}

	// Print all commands and responses to this io.Writer.
	debug io.Writer
}

// NewConn creates a new IMAP connection.
func NewConn(conn net.Conn, r *Reader, w *Writer) *Conn {
	c := &Conn{Conn: conn, Reader: r, Writer: w}

	c.init()
	return c
}

func (c *Conn) init() {
	r := io.Reader(c.Conn)
	w := io.Writer(c.Conn)

	if c.debug != nil {
		localDebug, remoteDebug := c.debug, c.debug
		if debug, ok := c.debug.(*debugWriter); ok {
			localDebug, remoteDebug = debug.local, debug.remote
		}

		if localDebug != nil {
			w = io.MultiWriter(c.Conn, localDebug)
		}
		if remoteDebug != nil {
			r = io.TeeReader(c.Conn, remoteDebug)
		}
	}

	if c.br == nil {
		c.br = bufio.NewReader(r)
		c.Reader.reader = c.br
	} else {
		c.br.Reset(r)
	}

	if c.bw == nil {
		c.bw = bufio.NewWriter(w)
		c.Writer.Writer = c.bw
	} else {
		c.bw.Reset(w)
	}

	if f, ok := c.Conn.(flusher); ok {
		c.Writer.Writer = struct {
			io.Writer
			flusher
		}{
			c.bw,
			newMultiFlusher(c.bw, f),
		}
	}
}

// Write implements io.Writer.
func (c *Conn) Write(b []byte) (n int, err error) {
	return c.Writer.Write(b)
}

// Flush writes any buffered data to the underlying connection.
func (c *Conn) Flush() error {
	if err := c.Writer.Flush(); err != nil {
		return err
	}
	return nil
}

// Upgrade a connection, e.g. wrap an unencrypted connection with an encrypted
// tunnel.
func (c *Conn) Upgrade(upgrader ConnUpgrader) error {
	// Flush all buffered data
	if err := c.Flush(); err != nil {
		return err
	}

	// Block reads and writes during the upgrading process
	c.waits = make(chan struct{})
	defer close(c.waits)

	upgraded, err := upgrader(c.Conn)
	if err != nil {
		return err
	}

	c.Conn = upgraded
	c.init()
	return nil
}

// Wait waits for the connection to be ready for reads and writes.
func (c *Conn) Wait() {
	if c.waits != nil {
		<-c.waits
	}
}

// SetDebug defines an io.Writer to which all network activity will be logged.
// If nil is provided, network activity will not be logged.
func (c *Conn) SetDebug(w io.Writer) {
	c.debug = w
	c.init()
}
<commit_msg>imap: simplify Conn.Flush<commit_after>package imap

import (
	"bufio"
	"io"
	"net"
)

// A connection state.
// See RFC 3501 section 3.
type ConnState int

const (
	// In the connecting state, the server has not yet sent a greeting and no
	// command can be issued.
	ConnectingState = 0

	// In the not authenticated state, the client MUST supply
	// authentication credentials before most commands will be
	// permitted.  This state is entered when a connection starts
	// unless the connection has been pre-authenticated.
	NotAuthenticatedState ConnState = 1 << 0

	// In the authenticated state, the client is authenticated and MUST
	// select a mailbox to access before commands that affect messages
	// will be permitted.  This state is entered when a
	// pre-authenticated connection starts, when acceptable
	// authentication credentials have been provided, after an error in
	// selecting a mailbox, or after a successful CLOSE command.
	AuthenticatedState = 1 << 1

	// In a selected state, a mailbox has been selected to access.
	// This state is entered when a mailbox has been successfully
	// selected.
	SelectedState = AuthenticatedState + 1<<2

	// In the logout state, the connection is being terminated. This
	// state can be entered as a result of a client request (via the
	// LOGOUT command) or by unilateral action on the part of either
	// the client or server.
	LogoutState = 1 << 3

	// ConnectedState is either NotAuthenticatedState, AuthenticatedState or
	// SelectedState.
	ConnectedState = NotAuthenticatedState | AuthenticatedState | SelectedState
)

// A function that upgrades a connection.
//
// This should only be used by libraries implementing an IMAP extension (e.g.
// COMPRESS).
type ConnUpgrader func(conn net.Conn) (net.Conn, error)

type debugWriter struct {
	io.Writer

	local  io.Writer
	remote io.Writer
}

// NewDebugWriter creates a new io.Writer that will write local network activity
// to local and remote network activity to remote.
func NewDebugWriter(local, remote io.Writer) io.Writer {
	return &debugWriter{Writer: local, local: local, remote: remote}
}

type multiFlusher struct {
	flushers []flusher
}

func (mf *multiFlusher) Flush() error {
	for _, f := range mf.flushers {
		if err := f.Flush(); err != nil {
			return err
		}
	}
	return nil
}

func newMultiFlusher(flushers ...flusher) flusher {
	return &multiFlusher{flushers}
}

// An IMAP connection.
type Conn struct {
	net.Conn
	*Reader
	*Writer

	br *bufio.Reader
	bw *bufio.Writer

	waits chan struct{}

	// Print all commands and responses to this io.Writer.
	debug io.Writer
}

// NewConn creates a new IMAP connection.
func NewConn(conn net.Conn, r *Reader, w *Writer) *Conn {
	c := &Conn{Conn: conn, Reader: r, Writer: w}

	c.init()
	return c
}

func (c *Conn) init() {
	r := io.Reader(c.Conn)
	w := io.Writer(c.Conn)

	if c.debug != nil {
		localDebug, remoteDebug := c.debug, c.debug
		if debug, ok := c.debug.(*debugWriter); ok {
			localDebug, remoteDebug = debug.local, debug.remote
		}

		if localDebug != nil {
			w = io.MultiWriter(c.Conn, localDebug)
		}
		if remoteDebug != nil {
			r = io.TeeReader(c.Conn, remoteDebug)
		}
	}

	if c.br == nil {
		c.br = bufio.NewReader(r)
		c.Reader.reader = c.br
	} else {
		c.br.Reset(r)
	}

	if c.bw == nil {
		c.bw = bufio.NewWriter(w)
		c.Writer.Writer = c.bw
	} else {
		c.bw.Reset(w)
	}

	if f, ok := c.Conn.(flusher); ok {
		c.Writer.Writer = struct {
			io.Writer
			flusher
		}{
			c.bw,
			newMultiFlusher(c.bw, f),
		}
	}
}

// Write implements io.Writer.
func (c *Conn) Write(b []byte) (n int, err error) {
	return c.Writer.Write(b)
}

// Flush writes any buffered data to the underlying connection.
func (c *Conn) Flush() error {
	return c.Writer.Flush()
}

// Upgrade a connection, e.g. wrap an unencrypted connection with an encrypted
// tunnel.
func (c *Conn) Upgrade(upgrader ConnUpgrader) error {
	// Flush all buffered data
	if err := c.Flush(); err != nil {
		return err
	}

	// Block reads and writes during the upgrading process
	c.waits = make(chan struct{})
	defer close(c.waits)

	upgraded, err := upgrader(c.Conn)
	if err != nil {
		return err
	}

	c.Conn = upgraded
	c.init()
	return nil
}

// Wait waits for the connection to be ready for reads and writes.
func (c *Conn) Wait() {
	if c.waits != nil {
		<-c.waits
	}
}

// SetDebug defines an io.Writer to which all network activity will be logged.
// If nil is provided, network activity will not be logged.
func (c *Conn) SetDebug(w io.Writer) {
	c.debug = w
	c.init()
}
<|endoftext|>


<commit_before>// Copyright 2011 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.

// This package provides LDAP client functions.
package ldap

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/mavricknz/asn1-ber"
	"net"
	"runtime/debug"
	"sync"
	"time"
)

// Conn - LDAP Connection and also pre/post connect configuation
//	IsTLS bool // default false
//	IsSSL bool // default false
//	Debug bool // default false
//	ConnectTimeout time.Duration // default 0 no timeout (not available in 1.0)
//	ReadTimeout    time.Duration // default 0 no timeout
//	Network        string // default empty "tcp"
//	Addr           string // default empty
//
// A minimal connection...
//	conn := new(ldap.LDAPConnection)
//	conn.Network = "tcp"
//	conn.Addr    = "localhost:1389"
//
//	conn, err := ldap.DialUsingConn(conn) // returns the same conn passed but connected.
type LDAPConnection struct {
	IsTLS bool
	IsSSL bool
	Debug bool

	Addr                        string
	NetworkConnectTimeout       time.Duration
	ReadTimeout                 time.Duration
	AbandonMessageOnReadTimeout bool

	TlsConfig *tls.Config

	conn               net.Conn
	chanResults        map[uint64]chan *ber.Packet
	chanProcessMessage chan *messagePacket
	chanMessageID      chan uint64

	closeLock sync.RWMutex
}

// DialUsingConn connects to the given address on the given network using
// net.DialTimeout. SSL/startTLS can be enabled
// Conn should be populated with connection information.
func (l *LDAPConnection) Connect() *Error {
	if len(l.chanResults) > 0 || l.chanProcessMessage != nil || l.chanMessageID != nil {
		return NewError(ErrorInvalidArgument,
			errors.New("Connect: Connection already setup? Can't reuse."))
	}

	l.chanResults = map[uint64]chan *ber.Packet{}
	l.chanProcessMessage = make(chan *messagePacket)
	l.chanMessageID = make(chan uint64)

	if l.conn == nil {
		var c net.Conn
		var err error
		if l.NetworkConnectTimeout > 0 {
			c, err = net.DialTimeout("tcp", l.Addr, l.NetworkConnectTimeout)
		} else {
			c, err = net.Dial("tcp", l.Addr)
		}

		if err != nil {
			return NewError(ErrorNetwork, err)
		}

		if l.IsSSL {
			tlsConn := tls.Client(c, l.TlsConfig)
			err = tlsConn.Handshake()
			if err != nil {
				return NewError(ErrorNetwork, err)
			}
			l.conn = tlsConn
		} else {
			l.conn = c
		}
	}

	if l.IsTLS {
		err := l.startTLS()
		if err != nil {
			return NewError(ErrorNetwork, err)
		}
	} else {
		l.start()
	}

	return nil
}

// NewConn returns a new basic connection. Should start connection via
// Connect
func NewLDAPConnection(server string, port uint16) *LDAPConnection {
	return &LDAPConnection{
		Addr: fmt.Sprintf("%s:%d", server, port),
	}
}

func (l *LDAPConnection) start() {
	go l.reader()
	go l.processMessages()
}

// Close closes the connection.
func (l *LDAPConnection) Close() *Error {
	l.closeLock.Lock()
	defer l.closeLock.Unlock()

	l.sendProcessMessage(&messagePacket{Op: MessageQuit})

	if l.conn != nil {
		err := l.conn.Close()
		if err != nil {
			return NewError(ErrorNetwork, err)
		}
		// Don't nil conn, as reader() should be allowed to read
		// error. If nil, then panics as using nil struct.
		// l.conn = nil
	}
	return nil
}

// Returns the next available messageID
func (l *LDAPConnection) nextMessageID() (messageID uint64) {
	defer func() {
		if r := recover(); r != nil {
			messageID = 0
		}
	}()
	messageID = <-l.chanMessageID
	return
}

// StartTLS sends the command to start a TLS session and then creates a new TLS Client
func (l *LDAPConnection) startTLS() *Error {
	messageID := l.nextMessageID()

	if l.IsSSL {
		return NewError(ErrorNetwork, errors.New("Already encrypted"))
	}

	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimative, ber.TagInteger, messageID, "MessageID"))
	startTLS := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS")
	startTLS.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimative, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
	packet.AppendChild(startTLS)
	if l.Debug {
		ber.PrintPacket(packet)
	}

	_, err := l.conn.Write(packet.Bytes())
	if err != nil {
		return NewError(ErrorNetwork, err)
	}

	packet, err = ber.ReadPacket(l.conn)
	if err != nil {
		return NewError(ErrorNetwork, err)
	}

	if l.Debug {
		if err := addLDAPDescriptions(packet); err != nil {
			return NewError(ErrorDebugging, err)
		}
		ber.PrintPacket(packet)
	}

	if packet.Children[1].Children[0].Value.(uint64) == 0 {
		conn := tls.Client(l.conn, nil)
		l.IsSSL = true
		l.conn = conn
	}

	return nil
}

const (
	MessageQuit     = 0
	MessageRequest  = 1
	MessageResponse = 2
	MessageFinish   = 3
)

type messagePacket struct {
	Op        int
	MessageID uint64
	Packet    *ber.Packet
	Channel   chan *ber.Packet
}

func (l *LDAPConnection) sendMessage(p *ber.Packet) (out chan *ber.Packet, err *Error) {
	message_id := p.Children[0].Value.(uint64)
	out = make(chan *ber.Packet)

	if l.chanProcessMessage == nil {
		err = NewError(ErrorNetwork, errors.New("Connection closed"))
		return
	}
	message_packet := &messagePacket{Op: MessageRequest, MessageID: message_id, Packet: p, Channel: out}
	l.sendProcessMessage(message_packet)
	return
}

func (l *LDAPConnection) processMessages() {
	defer l.closeAllChannels()
	//defer func() {
	//	if r := recover(); r != nil {
	//		fmt.Println("Recovered in processMessages", r)
	//		debug.PrintStack()
	//	}
	//}()
	var message_id uint64 = 1
	var message_packet *messagePacket
	for {
		select {
		case l.chanMessageID <- message_id:
			if l.conn == nil {
				return
			}
			message_id++
		case message_packet = <-l.chanProcessMessage:
			if l.conn == nil {
				return
			}
			switch message_packet.Op {
			case MessageQuit:
				// Close all channels and quit
				if l.Debug {
					fmt.Printf("Shutting down\n")
				}
				return
			case MessageRequest:
				// Add to message list and write to network
				if l.Debug {
					fmt.Printf("Sending message %d\n", message_packet.MessageID)
				}
				l.chanResults[message_packet.MessageID] = message_packet.Channel
				buf := message_packet.Packet.Bytes()
				for len(buf) > 0 {
					n, err := l.conn.Write(buf)
					if err != nil {
						if l.Debug {
							fmt.Printf("Error Sending Message: %s\n", err.Error())
						}
						return
					}
					if n == len(buf) {
						break
					}
					buf = buf[n:]
				}
			case MessageResponse:
				// Pass back to waiting goroutine
				if l.Debug {
					fmt.Printf("Receiving message %d\n", message_packet.MessageID)
				}
				chanResult := l.chanResults[message_packet.MessageID]
				if chanResult == nil {
					fmt.Printf("Unexpected Message Result (possible Abandon): %d , MessageID: %d\n", message_id, message_packet.MessageID)
					// TODO: Noisy when abandoning connections, as server can still send.
					// Some sort of limited Abandon list?
					//ber.PrintPacket(message_packet.Packet)
				} else {
					packetCopy := message_packet.Packet
					go func() {
						chanResult <- packetCopy
					}()
				}
			case MessageFinish:
				// Remove from message list
				if l.Debug {
					fmt.Printf("Finished message %d\n", message_packet.MessageID)
				}
				delete(l.chanResults, message_packet.MessageID)
			}
		}
	}
}

func (l *LDAPConnection) closeAllChannels() {
	l.closeLock.Lock()
	defer l.closeLock.Unlock()

	for MessageID, Channel := range l.chanResults {
		if l.Debug {
			fmt.Printf("Closing channel for MessageID %d\n", MessageID)
		}
		close(Channel)
		delete(l.chanResults, MessageID)
	}
	close(l.chanMessageID)
	l.chanMessageID = nil

	close(l.chanProcessMessage)
	l.chanProcessMessage = nil
}

func (l *LDAPConnection) finishMessage(MessageID uint64) {
	message_packet := &messagePacket{Op: MessageFinish, MessageID: MessageID}
	l.sendProcessMessage(message_packet)
}

func (l *LDAPConnection) reader() {
	defer l.Close()
	//defer func() {
	//	if r := recover(); r != nil {
	//		// There was an issue with the reader still running
	//		// while the l.conn had been closed and nil'ed.
	//		// Catch here, while investigating better way of
	//		// handling.
	//		// go test -test.cpu=2 ldaptests
	//		fmt.Println("Recovered in reader", r)
	//		debug.PrintStack()
	//	}
	//}()
	for {
		p, err := ber.ReadPacket(l.conn)
		if err != nil {
			if l.Debug {
				fmt.Printf("ldap.reader: %s\n", err.Error())
			}
			return
		}

		addLDAPDescriptions(p)

		message_id := p.Children[0].Value.(uint64)
		message_packet := &messagePacket{Op: MessageResponse, MessageID: message_id, Packet: p}

		if l.chanProcessMessage != nil {
			l.sendProcessMessage(message_packet)
		} else {
			fmt.Printf("ldap.reader: Cannot return message\n")
			return
		}
	}
}

func (l *LDAPConnection) sendProcessMessage(message *messagePacket) {
	go func() {
		l.closeLock.RLock()
		defer l.closeLock.RUnlock()

		if l.chanProcessMessage != nil {
			l.chanProcessMessage <- message
		}
	}()
}
<commit_msg>Left runtime/debug in. Removed.<commit_after>// Copyright 2011 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.

// This package provides LDAP client functions.
package ldap

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/mavricknz/asn1-ber"
	"net"
	"sync"
	"time"
)

// Conn - LDAP Connection and also pre/post connect configuation
//	IsTLS bool // default false
//	IsSSL bool // default false
//	Debug bool // default false
//	ConnectTimeout time.Duration // default 0 no timeout (not available in 1.0)
//	ReadTimeout    time.Duration // default 0 no timeout
//	Network        string // default empty "tcp"
//	Addr           string // default empty
//
// A minimal connection...
//	conn := new(ldap.LDAPConnection)
//	conn.Network = "tcp"
//	conn.Addr    = "localhost:1389"
//
//	conn, err := ldap.DialUsingConn(conn) // returns the same conn passed but connected.
type LDAPConnection struct {
	IsTLS bool
	IsSSL bool
	Debug bool

	Addr                        string
	NetworkConnectTimeout       time.Duration
	ReadTimeout                 time.Duration
	AbandonMessageOnReadTimeout bool

	TlsConfig *tls.Config

	conn               net.Conn
	chanResults        map[uint64]chan *ber.Packet
	chanProcessMessage chan *messagePacket
	chanMessageID      chan uint64

	closeLock sync.RWMutex
}

// DialUsingConn connects to the given address on the given network using
// net.DialTimeout. SSL/startTLS can be enabled
// Conn should be populated with connection information.
func (l *LDAPConnection) Connect() *Error {
	if len(l.chanResults) > 0 || l.chanProcessMessage != nil || l.chanMessageID != nil {
		return NewError(ErrorInvalidArgument,
			errors.New("Connect: Connection already setup? Can't reuse."))
	}

	l.chanResults = map[uint64]chan *ber.Packet{}
	l.chanProcessMessage = make(chan *messagePacket)
	l.chanMessageID = make(chan uint64)

	if l.conn == nil {
		var c net.Conn
		var err error
		if l.NetworkConnectTimeout > 0 {
			c, err = net.DialTimeout("tcp", l.Addr, l.NetworkConnectTimeout)
		} else {
			c, err = net.Dial("tcp", l.Addr)
		}

		if err != nil {
			return NewError(ErrorNetwork, err)
		}

		if l.IsSSL {
			tlsConn := tls.Client(c, l.TlsConfig)
			err = tlsConn.Handshake()
			if err != nil {
				return NewError(ErrorNetwork, err)
			}
			l.conn = tlsConn
		} else {
			l.conn = c
		}
	}

	if l.IsTLS {
		err := l.startTLS()
		if err != nil {
			return NewError(ErrorNetwork, err)
		}
	} else {
		l.start()
	}

	return nil
}

// NewConn returns a new basic connection. Should start connection via
// Connect
func NewLDAPConnection(server string, port uint16) *LDAPConnection {
	return &LDAPConnection{
		Addr: fmt.Sprintf("%s:%d", server, port),
	}
}

func (l *LDAPConnection) start() {
	go l.reader()
	go l.processMessages()
}

// Close closes the connection.
func (l *LDAPConnection) Close() *Error {
	l.closeLock.Lock()
	defer l.closeLock.Unlock()

	l.sendProcessMessage(&messagePacket{Op: MessageQuit})

	if l.conn != nil {
		err := l.conn.Close()
		if err != nil {
			return NewError(ErrorNetwork, err)
		}
		// Don't nil conn, as reader() should be allowed to read
		// error. If nil, then panics as using nil struct.
		// l.conn = nil
	}
	return nil
}

// Returns the next available messageID
func (l *LDAPConnection) nextMessageID() (messageID uint64) {
	defer func() {
		if r := recover(); r != nil {
			messageID = 0
		}
	}()
	messageID = <-l.chanMessageID
	return
}

// StartTLS sends the command to start a TLS session and then creates a new TLS Client
func (l *LDAPConnection) startTLS() *Error {
	messageID := l.nextMessageID()

	if l.IsSSL {
		return NewError(ErrorNetwork, errors.New("Already encrypted"))
	}

	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimative, ber.TagInteger, messageID, "MessageID"))
	startTLS := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS")
	startTLS.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimative, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
	packet.AppendChild(startTLS)
	if l.Debug {
		ber.PrintPacket(packet)
	}

	_, err := l.conn.Write(packet.Bytes())
	if err != nil {
		return NewError(ErrorNetwork, err)
	}

	packet, err = ber.ReadPacket(l.conn)
	if err != nil {
		return NewError(ErrorNetwork, err)
	}

	if l.Debug {
		if err := addLDAPDescriptions(packet); err != nil {
			return NewError(ErrorDebugging, err)
		}
		ber.PrintPacket(packet)
	}

	if packet.Children[1].Children[0].Value.(uint64) == 0 {
		conn := tls.Client(l.conn, nil)
		l.IsSSL = true
		l.conn = conn
	}

	return nil
}

const (
	MessageQuit     = 0
	MessageRequest  = 1
	MessageResponse = 2
	MessageFinish   = 3
)

type messagePacket struct {
	Op        int
	MessageID uint64
	Packet    *ber.Packet
	Channel   chan *ber.Packet
}

func (l *LDAPConnection) sendMessage(p *ber.Packet) (out chan *ber.Packet, err *Error) {
	message_id := p.Children[0].Value.(uint64)
	out = make(chan *ber.Packet)

	if l.chanProcessMessage == nil {
		err = NewError(ErrorNetwork, errors.New("Connection closed"))
		return
	}
	message_packet := &messagePacket{Op: MessageRequest, MessageID: message_id, Packet: p, Channel: out}
	l.sendProcessMessage(message_packet)
	return
}

func (l *LDAPConnection) processMessages() {
	defer l.closeAllChannels()
	//defer func() {
	//	if r := recover(); r != nil {
	//		fmt.Println("Recovered in processMessages", r)
	//		debug.PrintStack()
	//	}
	//}()
	var message_id uint64 = 1
	var message_packet *messagePacket
	for {
		select {
		case l.chanMessageID <- message_id:
			if l.conn == nil {
				return
			}
			message_id++
		case message_packet = <-l.chanProcessMessage:
			if l.conn == nil {
				return
			}
			switch message_packet.Op {
			case MessageQuit:
				// Close all channels and quit
				if l.Debug {
					fmt.Printf("Shutting down\n")
				}
				return
			case MessageRequest:
				// Add to message list and write to network
				if l.Debug {
					fmt.Printf("Sending message %d\n", message_packet.MessageID)
				}
				l.chanResults[message_packet.MessageID] = message_packet.Channel
				buf := message_packet.Packet.Bytes()
				for len(buf) > 0 {
					n, err := l.conn.Write(buf)
					if err != nil {
						if l.Debug {
							fmt.Printf("Error Sending Message: %s\n", err.Error())
						}
						return
					}
					if n == len(buf) {
						break
					}
					buf = buf[n:]
				}
			case MessageResponse:
				// Pass back to waiting goroutine
				if l.Debug {
					fmt.Printf("Receiving message %d\n", message_packet.MessageID)
				}
				chanResult := l.chanResults[message_packet.MessageID]
				if chanResult == nil {
					fmt.Printf("Unexpected Message Result (possible Abandon): %d , MessageID: %d\n", message_id, message_packet.MessageID)
					// TODO: Noisy when abandoning connections, as server can still send.
					// Some sort of limited Abandon list?
					//ber.PrintPacket(message_packet.Packet)
				} else {
					packetCopy := message_packet.Packet
					go func() {
						chanResult <- packetCopy
					}()
				}
			case MessageFinish:
				// Remove from message list
				if l.Debug {
					fmt.Printf("Finished message %d\n", message_packet.MessageID)
				}
				delete(l.chanResults, message_packet.MessageID)
			}
		}
	}
}

func (l *LDAPConnection) closeAllChannels() {
	l.closeLock.Lock()
	defer l.closeLock.Unlock()

	for MessageID, Channel := range l.chanResults {
		if l.Debug {
			fmt.Printf("Closing channel for MessageID %d\n", MessageID)
		}
		close(Channel)
		delete(l.chanResults, MessageID)
	}
	close(l.chanMessageID)
	l.chanMessageID = nil

	close(l.chanProcessMessage)
	l.chanProcessMessage = nil
}

func (l *LDAPConnection) finishMessage(MessageID uint64) {
	message_packet := &messagePacket{Op: MessageFinish, MessageID: MessageID}
	l.sendProcessMessage(message_packet)
}

func (l *LDAPConnection) reader() {
	defer l.Close()
	//defer func() {
	//	if r := recover(); r != nil {
	//		// There was an issue with the reader still running
	//		// while the l.conn had been closed and nil'ed.
	//		// Catch here, while investigating better way of
	//		// handling.
	//		// go test -test.cpu=2 ldaptests
	//		fmt.Println("Recovered in reader", r)
	//		debug.PrintStack()
	//	}
	//}()
	for {
		p, err := ber.ReadPacket(l.conn)
		if err != nil {
			if l.Debug {
				fmt.Printf("ldap.reader: %s\n", err.Error())
			}
			return
		}

		addLDAPDescriptions(p)

		message_id := p.Children[0].Value.(uint64)
		message_packet := &messagePacket{Op: MessageResponse, MessageID: message_id, Packet: p}

		if l.chanProcessMessage != nil {
			l.sendProcessMessage(message_packet)
		} else {
			fmt.Printf("ldap.reader: Cannot return message\n")
			return
		}
	}
}

func (l *LDAPConnection) sendProcessMessage(message *messagePacket) {
	go func() {
		l.closeLock.RLock()
		defer l.closeLock.RUnlock()

		if l.chanProcessMessage != nil {
			l.chanProcessMessage <- message
		}
	}()
}
<|endoftext|>


<commit_before>package lightning

import (
  "net/http"
  "time"
  "io/ioutil"
  "encoding/json"
)

const API_ROOT = "https://api.spark.io/v1/"
const API_DEVICES = "devices/"

type Core struct {
  Id string `json:"id"`
  key string
  Name string `json:"name"`
  LastApp string `json:"last_app"`
  LastHeard time.Time `json:"last_heard"`
  Connected bool
  Functions [4]string `json:"functions"`
  Variables map[string]string `json:"variables"`
}

var client = &http.Client{}

func (c *Core) do (req *http.Request) (*http.Response, error) {
  req.Header.Add("Authorization", "Bearer " + c.key)

  return client.Do(req)
}

type fnArgs struct {
  Args []string `json:"args,omitempty"`
}

func (f fnArgs) Write(b []byte) (int, error) {
  if s, err := json.Marshal(f); err == nil {
     b = append(b, s...)
     return len(b), nil
  } else {
    return 0, err
  }
}

//Fn takes the name of the function to run and as many arguments as needed and will pass those arguments to the function
func (c *Core) Fn (fname string, fargs ...string) (string, error) {
  req, err1 := http.NewRequest("POST", API_ROOT + API_DEVICES + c.Id + "/" + fname, nil)
  if err1 != nil {
    return "", err1
  }

  args := fnArgs{fargs}

  err0 := req.Write(args)
  if err0 != nil {
    return "", err0
  }

  resp, err2 := c.do(req)

  if err2 != nil {
    return "", err2
  }

  if str, err := ioutil.ReadAll(resp.Body); err != nil {
    return "", err
  } else {
    return string(str), nil
  }
}

func NewCore(id, key string) (Core, error) {
  req, err1 := http.NewRequest("GET", API_ROOT + API_DEVICES + id, nil)

  //TODO: Abstract errors
  if err1 != nil {
    return Core{}, err1
  }

  c := Core{
    Id: id,
    key: key,
  }

  resp, err2 := c.do(req)

  if err2 != nil {
    return Core{}, err2
  }

  dec := json.NewDecoder(resp.Body)
  dec.Decode(&c)

  return c, nil
}

type Collection struct {
  Key string
}

func NewCollection(key string) (Collection, error) {
  c := Collection {
    Key: key,
  }

  return c, nil
}
<commit_msg>Working on a reader to send post with<commit_after>package lightning

import (
  "net/http"
  "time"
  "os"
  "io/ioutil"
  "encoding/json"
)

const API_ROOT = "https://api.spark.io/v1/"
const API_DEVICES = "devices/"

type Core struct {
  Id string `json:"id"`
  key string
  Name string `json:"name"`
  LastApp string `json:"last_app"`
  LastHeard time.Time `json:"last_heard"`
  Connected bool
  Functions [4]string `json:"functions"`
  Variables map[string]string `json:"variables"`
}

var client = &http.Client{}

func (c *Core) do (req *http.Request) (*http.Response, error) {
  req.Header.Add("Authorization", "Bearer " + c.key)
  req.Header.Add("Content-Type", "application/json")

  return client.Do(req)
}

type fnArgs struct {
  Args []string `json:"args,omitempty"`
}

func (f fnArgs) Read(b []byte) (l int, e error) {
  if j, err := json.Marshal(f); err == nil {
    lj := len(j)
    l = copy(b, j[:lj])
    return
  } else {
    e = err
    return
  }
}

//Fn takes the name of the function to run and as many arguments as needed and will pass those arguments to the function
func (c *Core) Fn (fname string, fargs ...string) (string, error) {
  args := fnArgs{fargs}

  req, err1 := http.NewRequest("POST", API_ROOT + API_DEVICES + c.Id + "/" + fname, args)
  if err1 != nil {
    return "", err1
  }

  req.Write(os.Stdout)

  resp, err2 := c.do(req)
  if err2 != nil {
    return "", err2
  }

  if str, err := ioutil.ReadAll(resp.Body); err != nil {
    return "", err
  } else {
    return string(str), nil
  }
}

func NewCore(id, key string) (Core, error) {
  req, err1 := http.NewRequest("GET", API_ROOT + API_DEVICES + id, nil)

  //TODO: Abstract errors
  if err1 != nil {
    return Core{}, err1
  }

  c := Core{
    Id: id,
    key: key,
  }

  resp, err2 := c.do(req)

  if err2 != nil {
    return Core{}, err2
  }

  dec := json.NewDecoder(resp.Body)
  dec.Decode(&c)

  return c, nil
}

type Collection struct {
  Key string
}

func NewCollection(key string) (Collection, error) {
  c := Collection {
    Key: key,
  }

  return c, nil
}
<|endoftext|>