<commit_before>package client

import (
	"fmt"
	"io"
	"net/url"
	"strings"

	"github.com/docker/engine-api/types"
	"github.com/hyperhq/libcompose/config"
	"golang.org/x/net/context"
)

type composeConfigWrapper struct {
	ServiceConfigs *config.ServiceConfigs           `json:"ServiceConfigs"`
	VolumeConfigs  map[string]*config.VolumeConfig  `json:"VolumeConfigs"`
	NetworkConfigs map[string]*config.NetworkConfig `json:"NetworkConfigs"`
	AuthConfigs    map[string]types.AuthConfig      `json:"auths"`
}

func (cli *Client) ComposeUp(project string, services []string, c *config.ServiceConfigs, vc map[string]*config.VolumeConfig, nc map[string]*config.NetworkConfig, auth map[string]types.AuthConfig, forcerecreate, norecreate bool) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	if forcerecreate {
		query.Set("forcerecreate", "true")
	}
	if norecreate {
		query.Set("norecreate", "true")
	}
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	body := composeConfigWrapper{
		ServiceConfigs: c,
		VolumeConfigs:  vc,
		NetworkConfigs: nc,
		AuthConfigs:    auth,
	}
	resp, err := cli.post(context.Background(), "/compose/up", query, body, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeDown(project string, services []string, rmi string, vol, rmorphans bool) (io.ReadCloser, error) {

	query := url.Values{}
	query.Set("project", project)
	if rmi != "" {
		query.Set("rmi", rmi)
	}
	if vol {
		query.Set("vol", "true")
	}
	if rmorphans {
		query.Set("rmorphans", "true")
	}
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/down", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeCreate(project string, services []string, c *config.ServiceConfigs, vc map[string]*config.VolumeConfig, nc map[string]*config.NetworkConfig, auth map[string]types.AuthConfig, forcerecreate, norecreate bool) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	if forcerecreate {
		query.Set("forcerecreate", "true")
	}
	if norecreate {
		query.Set("norecreate", "true")
	}
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	body := composeConfigWrapper{
		ServiceConfigs: c,
		VolumeConfigs:  vc,
		NetworkConfigs: nc,
		AuthConfigs:    auth,
	}
	resp, err := cli.post(context.Background(), "/compose/create", query, body, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeRm(project string, services []string, rmVol bool) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	if rmVol {
		query.Set("rmvol", "true")
	}
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/rm", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeStart(project string, services []string) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/start", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeStop(project string, services []string, timeout int) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	query.Set("seconds", fmt.Sprintf("%d", timeout))
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/stop", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeKill(project string, services []string, signal string) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	query.Set("signal", signal)
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/kill", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}
<commit_msg>compose down parameter is rmvol<commit_after>package client

import (
	"fmt"
	"io"
	"net/url"
	"strings"

	"github.com/docker/engine-api/types"
	"github.com/hyperhq/libcompose/config"
	"golang.org/x/net/context"
)

type composeConfigWrapper struct {
	ServiceConfigs *config.ServiceConfigs           `json:"ServiceConfigs"`
	VolumeConfigs  map[string]*config.VolumeConfig  `json:"VolumeConfigs"`
	NetworkConfigs map[string]*config.NetworkConfig `json:"NetworkConfigs"`
	AuthConfigs    map[string]types.AuthConfig      `json:"auths"`
}

func (cli *Client) ComposeUp(project string, services []string, c *config.ServiceConfigs, vc map[string]*config.VolumeConfig, nc map[string]*config.NetworkConfig, auth map[string]types.AuthConfig, forcerecreate, norecreate bool) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	if forcerecreate {
		query.Set("forcerecreate", "true")
	}
	if norecreate {
		query.Set("norecreate", "true")
	}
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	body := composeConfigWrapper{
		ServiceConfigs: c,
		VolumeConfigs:  vc,
		NetworkConfigs: nc,
		AuthConfigs:    auth,
	}
	resp, err := cli.post(context.Background(), "/compose/up", query, body, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeDown(project string, services []string, rmi string, vol, rmorphans bool) (io.ReadCloser, error) {

	query := url.Values{}
	query.Set("project", project)
	if rmi != "" {
		query.Set("rmi", rmi)
	}
	if vol {
		query.Set("rmvol", "true")
	}
	if rmorphans {
		query.Set("rmorphans", "true")
	}
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/down", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeCreate(project string, services []string, c *config.ServiceConfigs, vc map[string]*config.VolumeConfig, nc map[string]*config.NetworkConfig, auth map[string]types.AuthConfig, forcerecreate, norecreate bool) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	if forcerecreate {
		query.Set("forcerecreate", "true")
	}
	if norecreate {
		query.Set("norecreate", "true")
	}
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	body := composeConfigWrapper{
		ServiceConfigs: c,
		VolumeConfigs:  vc,
		NetworkConfigs: nc,
		AuthConfigs:    auth,
	}
	resp, err := cli.post(context.Background(), "/compose/create", query, body, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeRm(project string, services []string, rmVol bool) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	if rmVol {
		query.Set("rmvol", "true")
	}
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/rm", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeStart(project string, services []string) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/start", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeStop(project string, services []string, timeout int) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	query.Set("seconds", fmt.Sprintf("%d", timeout))
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/stop", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}

func (cli *Client) ComposeKill(project string, services []string, signal string) (io.ReadCloser, error) {
	query := url.Values{}
	query.Set("project", project)
	query.Set("signal", signal)
	if len(services) > 0 {
		query.Set("services", strings.Join(services, "}{"))
	}
	resp, err := cli.post(context.Background(), "/compose/kill", query, nil, nil)
	if err != nil {
		return nil, err
	}
	return resp.body, nil
}
<|endoftext|>


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

package save

import (
	"errors"
	"fmt"
	"log"
	"regexp"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/comeback/internal/state"
	"github.com/jacobsa/syncutil"
	"github.com/jacobsa/timeutil"
)

// Save a backup of the given directory, applying the supplied exclusions and
// using the supplied score map to avoid reading file content when possible.
// Return a score for the root of the backup.
func Save(
	ctx context.Context,
	dir string,
	exclusions []*regexp.Regexp,
	scoreMap state.ScoreMap,
	blobStore blob.Store,
	logger *log.Logger,
	clock timeutil.Clock) (score blob.Score, err error) {
	b := syncutil.NewBundle(ctx)

	// List the directory hierarchy, applying exclusions.
	listedNodes := make(chan *fsNode, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(listedNodes)
		err = listNodes(ctx, dir, exclusions, listedNodes)
		if err != nil {
			err = fmt.Errorf("listNodes: %v", err)
			return
		}

		return
	})

	// Fill in scores for files that don't appear to have changed since the last
	// run.
	postScoreMap := make(chan *fsNode, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(postScoreMap)
		err = consultScoreMap(ctx, scoreMap, clock, listedNodes, postScoreMap)
		if err != nil {
			err = fmt.Errorf("consultScoreMap: %v", err)
			return
		}

		return
	})

	// Fill in scores for those nodes that didn't hit in the cache. Do this
	// safely, respecting dependency order (children complete before parents
	// start).
	postDAGTraversal := make(chan *fsNode, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(postDAGTraversal)
		err = fillInScores(
			ctx,
			dir,
			blobStore,
			logger,
			postScoreMap,
			postDAGTraversal)

		if err != nil {
			err = fmt.Errorf("fillInScores: %v", err)
			return
		}

		return
	})

	// Update the score map with the results of the previous stage.
	postScoreMapUpdate := make(chan *fsNode, 100)
	{
		// Tee the channel; updateScoreMap doesn't give output, nor does it modify
		// nodes.
		tmp := make(chan *fsNode, 100)
		b.Add(func(ctx context.Context) (err error) {
			defer close(tmp)
			defer close(postScoreMapUpdate)

			err = teeNodes(ctx, postDAGTraversal, tmp, postScoreMapUpdate)
			return
		})

		// Run updateScoreMap.
		b.Add(func(ctx context.Context) (err error) {
			err = updateScoreMap(ctx, scoreMap, tmp)
			if err != nil {
				err = fmt.Errorf("updateScoreMap: %v", err)
				return
			}

			return
		})
	}

	// Find the root score.
	b.Add(func(ctx context.Context) (err error) {
		score, err = findRootScore(postScoreMapUpdate)
		if err != nil {
			err = fmt.Errorf("findRootScore: %v", err)
			return
		}

		return
	})

	err = b.Join()
	return
}

func findRootScore(nodes <-chan *fsNode) (score blob.Score, err error) {
	found := false
	for n := range nodes {
		// Skip non-root nodes.
		if n.Parent != nil {
			continue
		}

		// Is this a duplicate?
		if found {
			err = fmt.Errorf("Found a duplicate root node: %#v", n)
			return
		}

		found = true

		// We expect directory nodes to have exactly one score.
		if len(n.Info.Scores) != 1 {
			err = fmt.Errorf("Unexpected score count for rooT: %#v", n)
			return
		}

		score = n.Info.Scores[0]
	}

	if !found {
		err = errors.New("No root node found")
		return
	}

	return
}

func teeNodes(
	ctx context.Context,
	in <-chan *fsNode,
	out1 chan<- *fsNode,
	out2 chan<- *fsNode) (err error) {
	for n := range in {
		// Write to first output.
		select {
		case out1 <- n:
		case <-ctx.Done():
			err = ctx.Err()
			return
		}

		// And the second.
		select {
		case out2 <- n:
		case <-ctx.Done():
			err = ctx.Err()
			return
		}
	}

	return
}
<commit_msg>Fixed save.go.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package save

import (
	"errors"
	"fmt"
	"log"
	"regexp"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/blob"
	"github.com/jacobsa/comeback/internal/state"
	"github.com/jacobsa/syncutil"
	"github.com/jacobsa/timeutil"
)

// Save a backup of the given directory, applying the supplied exclusions and
// using the supplied score map to avoid reading file content when possible.
// Return a score for the root of the backup.
func Save(
	ctx context.Context,
	dir string,
	exclusions []*regexp.Regexp,
	scoreMap state.ScoreMap,
	blobStore blob.Store,
	logger *log.Logger,
	clock timeutil.Clock) (score blob.Score, err error) {
	b := syncutil.NewBundle(ctx)

	// Visit each node in the graph, writing the processed nodes to a channel.
	processedNodes := make(chan *fsNode, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(processedNodes)
		err = errors.New("TODO")
		return
	})

	// Find the root score.
	b.Add(func(ctx context.Context) (err error) {
		score, err = findRootScore(processedNodes)
		if err != nil {
			err = fmt.Errorf("findRootScore: %v", err)
			return
		}

		return
	})

	err = b.Join()
	return
}

func findRootScore(nodes <-chan *fsNode) (score blob.Score, err error) {
	found := false
	for n := range nodes {
		// Skip non-root nodes.
		if n.Parent != nil {
			continue
		}

		// Is this a duplicate?
		if found {
			err = fmt.Errorf("Found a duplicate root node: %#v", n)
			return
		}

		found = true

		// We expect directory nodes to have exactly one score.
		if len(n.Info.Scores) != 1 {
			err = fmt.Errorf("Unexpected score count for rooT: %#v", n)
			return
		}

		score = n.Info.Scores[0]
	}

	if !found {
		err = errors.New("No root node found")
		return
	}

	return
}
<|endoftext|>


<commit_before>package interpreter

import (
	"bytes"
	"errors"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Argv     []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return ErrorLevel
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := ' '
	quote := false
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					return buffer.String()
				}
				if ch == '%' {
					break
				}
				nameBuf.WriteRune(ch)
			}
			nameStr := nameBuf.String()
			value := os.Getenv(nameStr)
			if value != "" {
				buffer.WriteString(value)
			} else if m := rxUnicode.FindStringSubmatch(nameStr); m != nil {
				ucode, _ := strconv.ParseInt(m[1], 16, 32)
				buffer.WriteRune(rune(ucode))
			} else if f, ok := PercentFunc[nameStr]; ok {
				buffer.WriteString(f())
			} else {
				buffer.WriteRune('%')
				buffer.WriteString(nameStr)
				buffer.WriteRune('%')
			}
			continue
		}
		if ch == '"' {
			quote = !quote
			if lastchar == '"' && quote {
				buffer.WriteRune('"')
				lastchar = '\000'
			}
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	return buffer.String()
}

func terminate(statements *[]StatementT,
	isRedirected *bool,
	redirect *[]*Redirecter,
	buffer *bytes.Buffer,
	argv *[]string,
	term string) {

	var statement1 StatementT
	if buffer.Len() > 0 {
		if *isRedirected && len(*redirect) > 0 {
			(*redirect)[len(*redirect)-1].SetPath(dequote(buffer))
			*isRedirected = false
			statement1.Argv = *argv
		} else {
			statement1.Argv = append(*argv, dequote(buffer))
		}
		buffer.Reset()
	} else if len(*argv) <= 0 {
		return
	} else {
		statement1.Argv = *argv
	}
	statement1.Redirect = *redirect
	*redirect = make([]*Redirecter, 0, 3)
	*argv = make([]string, 0)
	statement1.Term = term
	*statements = append(*statements, statement1)
}

func parse1(text string) ([]StatementT, error) {
	isQuoted := false
	statements := make([]StatementT, 0)
	argv := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if ch == '"' {
			isQuoted = !isQuoted
		}
		if isQuoted {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				if isNextRedirect && len(redirect) > 0 {
					redirect[len(redirect)-1].SetPath(dequote(&buffer))
				} else {
					argv = append(argv, dequote(&buffer))
				}
				buffer.Reset()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == ';' {
			terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, ";")
		} else if ch == '|' {
			if lastchar == '|' {
				statements[len(statements)-1].Term = "||"
			} else {
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "|")
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				statements[len(statements)-1].Term = "&&"
			case '|':
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "&")
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, " ")
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []StatementT) [][]StatementT {
	result := make([][]StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<commit_msg>Fix #57 - Miss a double-quotation after a percent<commit_after>package interpreter

import (
	"bytes"
	"errors"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Argv     []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return ErrorLevel
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

func dequote(source *bytes.Buffer) string {
	var buffer bytes.Buffer

	lastchar := ' '
	quote := false
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					return buffer.String()
				}
				if ch == '%' {
					nameStr := nameBuf.String()
					value := os.Getenv(nameStr)
					if value != "" {
						buffer.WriteString(value)
					} else if m := rxUnicode.FindStringSubmatch(nameStr); m != nil {
						ucode, _ := strconv.ParseInt(m[1], 16, 32)
						buffer.WriteRune(rune(ucode))
					} else if f, ok := PercentFunc[nameStr]; ok {
						buffer.WriteString(f())
					} else {
						buffer.WriteRune('%')
						buffer.WriteString(nameStr)
						buffer.WriteRune('%')
					}
					break
				}
				if !unicode.IsLower(ch) && !unicode.IsUpper(ch) && !unicode.IsNumber(ch) && ch != '_' {
					source.UnreadRune()
					buffer.WriteRune('%')
					buffer.WriteString(nameBuf.String())
					break
				}
				nameBuf.WriteRune(ch)
			}
			continue
		}
		if ch == '"' {
			quote = !quote
			if lastchar == '"' && quote {
				buffer.WriteRune('"')
				lastchar = '\000'
			}
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	return buffer.String()
}

func terminate(statements *[]StatementT,
	isRedirected *bool,
	redirect *[]*Redirecter,
	buffer *bytes.Buffer,
	argv *[]string,
	term string) {

	var statement1 StatementT
	if buffer.Len() > 0 {
		if *isRedirected && len(*redirect) > 0 {
			(*redirect)[len(*redirect)-1].SetPath(dequote(buffer))
			*isRedirected = false
			statement1.Argv = *argv
		} else {
			statement1.Argv = append(*argv, dequote(buffer))
		}
		buffer.Reset()
	} else if len(*argv) <= 0 {
		return
	} else {
		statement1.Argv = *argv
	}
	statement1.Redirect = *redirect
	*redirect = make([]*Redirecter, 0, 3)
	*argv = make([]string, 0)
	statement1.Term = term
	*statements = append(*statements, statement1)
}

func parse1(text string) ([]StatementT, error) {
	isQuoted := false
	statements := make([]StatementT, 0)
	argv := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if ch == '"' {
			isQuoted = !isQuoted
		}
		if isQuoted {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				if isNextRedirect && len(redirect) > 0 {
					redirect[len(redirect)-1].SetPath(dequote(&buffer))
				} else {
					argv = append(argv, dequote(&buffer))
				}
				buffer.Reset()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == ';' {
			terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, ";")
		} else if ch == '|' {
			if lastchar == '|' {
				statements[len(statements)-1].Term = "||"
			} else {
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "|")
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				statements[len(statements)-1].Term = "&&"
			case '|':
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, "&")
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		lastchar = ch
	}
	terminate(&statements, &isNextRedirect, &redirect, &buffer, &argv, " ")
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []StatementT) [][]StatementT {
	result := make([][]StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	mph "github.com/mackerelio/go-mackerel-plugin-helper"
	"net"
	"os"
	"strconv"
	"strings"
)

var graphdef map[string](mph.Graphs) = map[string](mph.Graphs){
	"memcached.connections": mph.Graphs{
		Label: "Memcached Connections",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "curr_connections", Label: "Connections", Diff: false},
		},
	},
	"memcached.cmd": mph.Graphs{
		Label: "Memcached Command",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "cmd_get", Label: "Get", Diff: true},
			mph.Metrics{Key: "cmd_set", Label: "Set", Diff: true},
			mph.Metrics{Key: "cmd_flush", Label: "Flush", Diff: true},
			mph.Metrics{Key: "cmd_touch", Label: "Touch", Diff: true},
		},
	},
	"memcached.hitmiss": mph.Graphs{
		Label: "Memcached Hits/Misses",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "get_hits", Label: "Get Hits", Diff: true},
			mph.Metrics{Key: "get_misses", Label: "Get Misses", Diff: true},
			mph.Metrics{Key: "delete_hits", Label: "Delete Hits", Diff: true},
			mph.Metrics{Key: "delete_misses", Label: "Delete Misses", Diff: true},
			mph.Metrics{Key: "incr_hits", Label: "Incr Hits", Diff: true},
			mph.Metrics{Key: "incr_misses", Label: "Incr Misses", Diff: true},
			mph.Metrics{Key: "cas_hits", Label: "Cas Hits", Diff: true},
			mph.Metrics{Key: "cas_misses", Label: "Cas Misses", Diff: true},
			mph.Metrics{Key: "touch_hits", Label: "Touch Hits", Diff: true},
			mph.Metrics{Key: "touch_misses", Label: "Touch Misses", Diff: true},
		},
	},
	"memcached.evictions": mph.Graphs{
		Label: "Memcached Evictions",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "evictions", Label: "Evictions", Diff: true},
		},
	},
	"memcached.unfetched": mph.Graphs{
		Label: "Memcached Unfetched",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "expired_unfetched", Label: "Expired unfetched", Diff: true},
			mph.Metrics{Key: "evicted_unfetched", Label: "Evicted unfetched", Diff: true},
		},
	},
	"memcached.rusage": mph.Graphs{
		Label: "Memcached Resouce Usage",
		Unit:  "float",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "rusage_user", Label: "User", Diff: true},
			mph.Metrics{Key: "rusage_system", Label: "System", Diff: true},
		},
	},
	"memcached.bytes": mph.Graphs{
		Label: "Memcached Traffics",
		Unit:  "bytes",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "bytes_read", Label: "Read", Diff: true},
			mph.Metrics{Key: "bytes_written", Label: "Write", Diff: true},
		},
	},
}

type MemcachedPlugin struct {
	Target   string
	Tempfile string
}

func (m MemcachedPlugin) FetchData() (map[string]float64, error) {
	conn, err := net.Dial("tcp", m.Target)
	if err != nil {
		return nil, err
	}
	fmt.Fprintln(conn, "stats")
	r := bufio.NewReader(conn)
	line, isPrefix, err := r.ReadLine()

	stat := make(map[string]float64)
	for err == nil && !isPrefix {
		s := string(line)
		if s == "END" {
			return stat, nil
		}
		res := strings.Split(s, " ")
		if res[0] == "STAT" {
			stat[res[1]], err = strconv.ParseFloat(res[2], 64)
			if err != nil {
				fmt.Fprintln(os.Stderr, "readStat:", err)
			}
		}
		line, isPrefix, err = r.ReadLine()
	}
	if isPrefix {
		return nil, errors.New("buffer size too small")
	}
	if err != nil {
		return nil, err
	}
	return nil, nil
}

func (m MemcachedPlugin) GetGraphDefinition() map[string](mph.Graphs) {
	return graphdef
}

func (m MemcachedPlugin) GetTempfilename() string {
	return m.Tempfile
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "11211", "Port")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var memcached MemcachedPlugin

	memcached.Target = fmt.Sprintf("%s:%s", *optHost, *optPort)
	if *optTempfile != "" {
		memcached.Tempfile = *optTempfile
	} else {
		memcached.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-memcached-%s-%s", *optHost, *optPort)
	}

	helper := mph.MackerelPluginHelper{memcached}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<commit_msg>use Scanner<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	mph "github.com/mackerelio/go-mackerel-plugin-helper"
	"net"
	"os"
	"strconv"
	"strings"
)

var graphdef map[string](mph.Graphs) = map[string](mph.Graphs){
	"memcached.connections": mph.Graphs{
		Label: "Memcached Connections",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "curr_connections", Label: "Connections", Diff: false},
		},
	},
	"memcached.cmd": mph.Graphs{
		Label: "Memcached Command",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "cmd_get", Label: "Get", Diff: true},
			mph.Metrics{Key: "cmd_set", Label: "Set", Diff: true},
			mph.Metrics{Key: "cmd_flush", Label: "Flush", Diff: true},
			mph.Metrics{Key: "cmd_touch", Label: "Touch", Diff: true},
		},
	},
	"memcached.hitmiss": mph.Graphs{
		Label: "Memcached Hits/Misses",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "get_hits", Label: "Get Hits", Diff: true},
			mph.Metrics{Key: "get_misses", Label: "Get Misses", Diff: true},
			mph.Metrics{Key: "delete_hits", Label: "Delete Hits", Diff: true},
			mph.Metrics{Key: "delete_misses", Label: "Delete Misses", Diff: true},
			mph.Metrics{Key: "incr_hits", Label: "Incr Hits", Diff: true},
			mph.Metrics{Key: "incr_misses", Label: "Incr Misses", Diff: true},
			mph.Metrics{Key: "cas_hits", Label: "Cas Hits", Diff: true},
			mph.Metrics{Key: "cas_misses", Label: "Cas Misses", Diff: true},
			mph.Metrics{Key: "touch_hits", Label: "Touch Hits", Diff: true},
			mph.Metrics{Key: "touch_misses", Label: "Touch Misses", Diff: true},
		},
	},
	"memcached.evictions": mph.Graphs{
		Label: "Memcached Evictions",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "evictions", Label: "Evictions", Diff: true},
		},
	},
	"memcached.unfetched": mph.Graphs{
		Label: "Memcached Unfetched",
		Unit:  "integer",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "expired_unfetched", Label: "Expired unfetched", Diff: true},
			mph.Metrics{Key: "evicted_unfetched", Label: "Evicted unfetched", Diff: true},
		},
	},
	"memcached.rusage": mph.Graphs{
		Label: "Memcached Resouce Usage",
		Unit:  "float",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "rusage_user", Label: "User", Diff: true},
			mph.Metrics{Key: "rusage_system", Label: "System", Diff: true},
		},
	},
	"memcached.bytes": mph.Graphs{
		Label: "Memcached Traffics",
		Unit:  "bytes",
		Metrics: [](mph.Metrics){
			mph.Metrics{Key: "bytes_read", Label: "Read", Diff: true},
			mph.Metrics{Key: "bytes_written", Label: "Write", Diff: true},
		},
	},
}

type MemcachedPlugin struct {
	Target   string
	Tempfile string
}

func (m MemcachedPlugin) FetchData() (map[string]float64, error) {
	conn, err := net.Dial("tcp", m.Target)
	if err != nil {
		return nil, err
	}
	fmt.Fprintln(conn, "stats")
	scanner := bufio.NewScanner(conn)
	stat := make(map[string]float64)

	for scanner.Scan() {
		line := scanner.Text()
		s := string(line)
		if s == "END" {
			return stat, nil
		}

		res := strings.Split(s, " ")
		if res[0] == "STAT" {
			stat[res[1]], err = strconv.ParseFloat(res[2], 64)
			if err != nil {
				fmt.Fprintln(os.Stderr, "readStat:", err)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return stat, err
	}
	return nil, nil
}

func (m MemcachedPlugin) GetGraphDefinition() map[string](mph.Graphs) {
	return graphdef
}

func (m MemcachedPlugin) GetTempfilename() string {
	return m.Tempfile
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "11211", "Port")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var memcached MemcachedPlugin

	memcached.Target = fmt.Sprintf("%s:%s", *optHost, *optPort)
	if *optTempfile != "" {
		memcached.Tempfile = *optTempfile
	} else {
		memcached.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-memcached-%s-%s", *optHost, *optPort)
	}

	helper := mph.MackerelPluginHelper{memcached}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<|endoftext|>


<commit_before>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.
package parse

import (
	"fmt"
	"github.com/demizer/go-elog"
	"unicode/utf8"
)

type itemElement int

const (
	itemEOF itemElement = iota
	itemError
	itemTitle
	itemSectionAdornment
	itemParagraph
	itemBlockquote
	itemLiteralBlock
	itemSystemMessage
	itemSpace
	itemBlankLine
)

var elements = [...]string{
	"itemEOF",
	"itemError",
	"itemTitle",
	"itemSectionAdornment",
	"itemParagraph",
	"itemBlockquote",
	"itemLiteralBlock",
	"itemSystemMessage",
	"itemSpace",
	"itemBlankLine",
}

func (t itemElement) String() string { return elements[t] }

var sectionAdornments = []rune{'!', '"', '#', '$', '\'', '%', '&', '(', ')', '*',
	'+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\',
	']', '^', '_', '`', '{', '|', '}', '~'}

const EOF rune = -1

type stateFn func(*lexer) stateFn

type item struct {
	ElementName string
	ElementType itemElement
	Position    Pos
	Line        int
	Value       interface{}
}

type systemMessageLevel int

const (
	levelInfo systemMessageLevel = iota
	levelWarning
	levelError
	levelSevere
)

var systemMessageLevels = [...]string{
	"INFO",
	"WARNING",
	"ERROR",
	"SEVERE",
}

func (s systemMessageLevel) String() string { return systemMessageLevels[s] }

type systemMessage struct {
	level  systemMessageLevel
	line   int
	source string
	items  []item
}

type lexer struct {
	name    string
	input   string
	state   stateFn
	pos     Pos
	start   Pos
	width   Pos
	lastPos Pos
	items   chan item
	line    int
}

func lex(name, input string) *lexer {
	l := &lexer{
		name:  name,
		input: input,
		line: 1,
		items: make(chan item),
	}
	go l.run()
	return l
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemElement) {
	l.items <- item{ElementType: t, ElementName: fmt.Sprint(t),
		Position: l.start, Line: l.line, Value: l.input[l.start:l.pos]}
	log.Debugf("%s: %q\n", t, l.input[l.start:l.pos])
	l.start = l.pos
}

func (l *lexer) backup() {
	l.pos -= l.width
}

func (l *lexer) current() rune {
	r, _ := utf8.DecodeRuneInString(l.input[l.pos:])
	return r
}

func (l *lexer) previous() rune {
	l.backup()
	r := l.current()
	l.next()
	return r
}

func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

func (l *lexer) skip() {
	l.start += 1
        l.next()
}

func (l *lexer) advance(to rune) {
	for {
		if l.next() == EOF || to == l.current() {
			break
		}
	}
}

func (l *lexer) next() rune {
	if int(l.pos) >= len(l.input) {
		log.Debugln("Reached EOF!")
		l.width = 0
		return EOF
	}
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	l.width = Pos(w)
	l.pos += l.width
	return r
}

// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
	item := <-l.items
	l.lastPos = item.Position
	return item

}

func (l *lexer) run() {
	for l.state = lexStart; l.state != nil; {
		l.state = l.state(l)
	}
}

// isWhiteSpace reports whether r is a space character.
func isWhiteSpace(r rune) bool {
	return r == ' ' || r == '\t' || r == '\n' || r == '\r'
}

// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
	return r == '\r' || r == '\n'
}

func lexStart(l *lexer) stateFn {
	log.Debugln("\nTransition...")
	for {
		if len(l.input) == 0 {
			l.emit(itemEOF)
			return nil
		}

		if l.pos > l.start {
			log.Debugf("%q, Current: %q, Start: %d, Pos: %d, Line: %d\n",
				l.input[l.start:l.pos], l.current(), l.start, l.pos, l.line)
		}

		isStartOfToken := l.start == l.pos-l.width

		switch r := l.current(); {
		case isStartOfToken:
			if isWhiteSpace(r) {
				lexWhiteSpace(l)
			}
			if isSection(l) {
				return lexSection
			}
			l.next()
		case isEndOfLine(r):
			if l.pos > l.start {
				l.emit(itemParagraph)
			}
			l.line += 1
			l.skip() // Skip the newline
		}
		if l.next() == EOF {
			break
		}
	}

	// Correctly reached EOF.
	if l.pos > l.start {
		l.emit(itemParagraph)
	}

	l.emit(itemEOF)
	return nil
}

func lexSection(l *lexer) stateFn {
	if len(l.input) > 0 {
		log.Debugf("\tlexSection: %q, Pos: %d\n",
			l.input[l.start:l.pos], l.pos)
	}

	if isEndOfLine(l.peek()) {
		l.emit(itemTitle)
		l.ignore()
	}

Loop:
	for {
		switch r := l.next(); {
		case isSectionAdornment(r):
			if len(l.input) > 0 {
				log.Debugf("\tlexSection: %q, Pos: %d\n",
					l.input[l.start:l.pos], l.pos)
			}
		case isEndOfLine(r):
			l.backup()
			l.emit(itemSectionAdornment)
			l.line += 1
			l.ignore()
			break Loop
		}
	}
	return lexStart
}

func isSectionAdornment(r rune) bool {
	for _, a := range sectionAdornments {
		if a == r {
			return true
		}
	}
	return false
}
<commit_msg>lex.go: Refactor lexSection()<commit_after>// go-rst - A reStructuredText parser for Go
// 2014 (c) The go-rst Authors
// MIT Licensed. See LICENSE for details.
package parse

import (
	"fmt"
	"github.com/demizer/go-elog"
	"unicode/utf8"
)

type itemElement int

const (
	itemEOF itemElement = iota
	itemError
	itemTitle
	itemSectionAdornment
	itemParagraph
	itemBlockquote
	itemLiteralBlock
	itemSystemMessage
	itemSpace
	itemBlankLine
)

var elements = [...]string{
	"itemEOF",
	"itemError",
	"itemTitle",
	"itemSectionAdornment",
	"itemParagraph",
	"itemBlockquote",
	"itemLiteralBlock",
	"itemSystemMessage",
	"itemSpace",
	"itemBlankLine",
}

func (t itemElement) String() string { return elements[t] }

var sectionAdornments = []rune{'!', '"', '#', '$', '\'', '%', '&', '(', ')', '*',
	'+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\',
	']', '^', '_', '`', '{', '|', '}', '~'}

const EOF rune = -1

type stateFn func(*lexer) stateFn

type item struct {
	ElementName string
	ElementType itemElement
	Position    Pos
	Line        int
	Value       interface{}
}

type systemMessageLevel int

const (
	levelInfo systemMessageLevel = iota
	levelWarning
	levelError
	levelSevere
)

var systemMessageLevels = [...]string{
	"INFO",
	"WARNING",
	"ERROR",
	"SEVERE",
}

func (s systemMessageLevel) String() string { return systemMessageLevels[s] }

type systemMessage struct {
	level  systemMessageLevel
	line   int
	source string
	items  []item
}

type lexer struct {
	name    string
	input   string
	state   stateFn
	pos     Pos
	start   Pos
	width   Pos
	lastPos Pos
	items   chan item
	line    int
}

func lex(name, input string) *lexer {
	l := &lexer{
		name:  name,
		input: input,
		line: 1,
		items: make(chan item),
	}
	go l.run()
	return l
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemElement) {
	l.items <- item{ElementType: t, ElementName: fmt.Sprint(t),
		Position: l.start, Line: l.line, Value: l.input[l.start:l.pos]}
	log.Debugf("%s: %q\n", t, l.input[l.start:l.pos])
	l.start = l.pos
}

func (l *lexer) backup() {
	l.pos -= l.width
}

func (l *lexer) current() rune {
	r, _ := utf8.DecodeRuneInString(l.input[l.pos:])
	return r
}

func (l *lexer) previous() rune {
	l.backup()
	r := l.current()
	l.next()
	return r
}

func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

func (l *lexer) skip() {
	l.start += 1
        l.next()
}

func (l *lexer) advance(to rune) {
	for {
		if l.next() == EOF || to == l.current() {
			break
		}
	}
}

func (l *lexer) next() rune {
	if int(l.pos) >= len(l.input) {
		log.Debugln("Reached EOF!")
		l.width = 0
		return EOF
	}
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	l.width = Pos(w)
	l.pos += l.width
	return r
}

// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
	item := <-l.items
	l.lastPos = item.Position
	return item

}

func (l *lexer) run() {
	for l.state = lexStart; l.state != nil; {
		l.state = l.state(l)
	}
}

// isWhiteSpace reports whether r is a space character.
func isWhiteSpace(r rune) bool {
	return r == ' ' || r == '\t' || r == '\n' || r == '\r'
}

// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
	return r == '\r' || r == '\n'
}

func lexStart(l *lexer) stateFn {
	log.Debugln("\nTransition...")
	for {
		if len(l.input) == 0 {
			l.emit(itemEOF)
			return nil
		}

		if l.pos > l.start {
			log.Debugf("%q, Current: %q, Start: %d, Pos: %d, Line: %d\n",
				l.input[l.start:l.pos], l.current(), l.start, l.pos, l.line)
		}

		isStartOfToken := l.start == l.pos-l.width

		switch r := l.current(); {
		case isStartOfToken:
			if isWhiteSpace(r) {
				lexWhiteSpace(l)
			}
			if isSection(l) {
				return lexSection
			}
			l.next()
		case isEndOfLine(r):
			if l.pos > l.start {
				l.emit(itemParagraph)
			}
			l.line += 1
			l.skip() // Skip the newline
		}
		if l.next() == EOF {
			break
		}
	}

	// Correctly reached EOF.
	if l.pos > l.start {
		l.emit(itemParagraph)
	}

	l.emit(itemEOF)
	return nil
}

func lexSection(l *lexer) stateFn {
	var lexTitle bool
	log.Debugln("\nTransition...")

	if !isSectionAdornment(l.current()) {
		lexTitle = true
	}

	for {
		if len(l.input) > 0 {
			log.Debugf("%q, Start: %d, Pos: %d\n", l.input[l.start:l.pos], l.start, l.pos)
		}
		switch r := l.next(); {
		case isEndOfLine(r):
			l.backup()
			if lexTitle {
				l.emit(itemTitle)
				lexTitle = false
				l.line += 1
				l.skip()
				l.next()
				continue
			} else {
				l.emit(itemSectionAdornment)
				return lexStart
			}
		case isWhiteSpace(r):
			lexWhiteSpace(l)
		}
	}
	return lexStart
}

func isSectionAdornment(r rune) bool {
	for _, a := range sectionAdornments {
		if a == r {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package processors

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
	"text/template"

	"github.com/fsouza/go-dockerclient"
	"github.com/rehabstudio/oneill/oneill"
)

const (
	nginxTemplate string = `
        upstream {{.Subdomain}} {
          server localhost:{{.Port}};
        }

        server {
          listen *:80;
          server_name {{.Subdomain}}.{{.ServingDomain}};
          return 301 https://$server_name$request_uri;
        }

        server {
          listen 443;
          server_name {{.Subdomain}}.{{.ServingDomain}};

          ssl on;
          ssl_certificate /etc/ssl/certs/labs-server.crt;
          ssl_certificate_key /etc/ssl/private/labs-server.pem;
          ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
          ssl_session_timeout 5m;
          ssl_session_cache shared:SSL:5m;

          client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads

          # required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
          chunked_transfer_encoding on;

          location / {
            proxy_pass                       http://{{.Subdomain}};
            proxy_set_header  Host           $http_host;   # required for docker client's sake
            proxy_set_header  X-Real-IP      $remote_addr; # pass on real client's IP
            proxy_read_timeout               900;
          }

        }
`
)

func ensureFreshOutputDir() {
	oneill.LogDebug("Recreating empty configuration directory")
	outDir := oneill.Config.NginxConfigDirectory
	err := os.RemoveAll(outDir)
	if err != nil {
		panic(err)
	}
	err = os.Mkdir(outDir, 0755)
	if err != nil {
		panic(err)
	}
}

func reloadNginxConfig() error {
	runCmd := exec.Command("service", "nginx", "reload")
	return runCmd.Run()
}

type templateContext struct {
	Subdomain     string
	ServingDomain string
	Port          int64
}

func writeTemplateToDisk(siteConfig *oneill.SiteConfig, container docker.APIContainers) {
	tmpl, err := template.New("nginx-config").Parse(nginxTemplate)
	if err != nil {
		oneill.LogWarning(fmt.Sprintf("Unable to load nginx config template: %s", siteConfig.Subdomain))
		return
	}

	var b bytes.Buffer
	context := templateContext{
		Subdomain:     siteConfig.Subdomain,
		ServingDomain: oneill.Config.ServingDomain,
		Port:          container.Ports[0].PublicPort,
	}
	err = tmpl.Execute(&b, context)
	if err != nil {
		oneill.LogWarning(fmt.Sprintf("Unable to execute nginx config template: %s", siteConfig.Subdomain))
		return
	}
	outDir := oneill.Config.NginxConfigDirectory
	outFile := path.Join(outDir, fmt.Sprintf("%s.conf", siteConfig.Subdomain))
	err = ioutil.WriteFile(outFile, b.Bytes(), 0644)
	if err != nil {
		oneill.LogWarning(fmt.Sprintf("Unable to write nginx config template: %s", siteConfig.Subdomain))
		return
	}
}

func ConfigureNginx(siteConfigs []*oneill.SiteConfig) []*oneill.SiteConfig {
	oneill.LogInfo("## Configuring Nginx")

	ensureFreshOutputDir()

	for _, sc := range siteConfigs {
		for _, container := range oneill.ListContainers() {
			containerName := strings.TrimPrefix(container.Names[0], "/")
			if containerName == sc.Subdomain {
				writeTemplateToDisk(sc, container)
				oneill.LogDebug(fmt.Sprintf("Configured nginx proxy for container: %s", sc.Subdomain))
				break
			}
		}
	}
	if err := reloadNginxConfig(); err != nil {
		oneill.LogWarning("Unable to reload nginx configuration")
		return siteConfigs
	}
	oneill.LogDebug("Reloaded nginx configuration")
	return siteConfigs
}
<commit_msg>update nginx config so that websocket connections will work<commit_after>package processors

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
	"text/template"

	"github.com/fsouza/go-dockerclient"
	"github.com/rehabstudio/oneill/oneill"
)

const (
	nginxTemplate string = `
        upstream {{.Subdomain}} {
          server localhost:{{.Port}};
        }

        map $http_upgrade $connection_upgrade {
            default upgrade;
            ''      close;
        }

        server {
          listen *:80;
          server_name {{.Subdomain}}.{{.ServingDomain}};
          return 301 https://$server_name$request_uri;
        }

        server {
          listen 443;
          server_name {{.Subdomain}}.{{.ServingDomain}};

          ssl on;
          ssl_certificate /etc/ssl/certs/labs-server.crt;
          ssl_certificate_key /etc/ssl/private/labs-server.pem;
          ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
          ssl_session_timeout 5m;
          ssl_session_cache shared:SSL:5m;

          client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads

          # required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
          chunked_transfer_encoding on;

          location / {
            proxy_pass                       http://{{.Subdomain}};
            proxy_set_header  Host           $http_host;   # required for docker client's sake
            proxy_set_header  X-Real-IP      $remote_addr; # pass on real client's IP
            proxy_read_timeout               900;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
          }

        }
`
)

func ensureFreshOutputDir() {
	oneill.LogDebug("Recreating empty configuration directory")
	outDir := oneill.Config.NginxConfigDirectory
	err := os.RemoveAll(outDir)
	if err != nil {
		panic(err)
	}
	err = os.Mkdir(outDir, 0755)
	if err != nil {
		panic(err)
	}
}

func reloadNginxConfig() error {
	runCmd := exec.Command("service", "nginx", "reload")
	return runCmd.Run()
}

type templateContext struct {
	Subdomain     string
	ServingDomain string
	Port          int64
}

func writeTemplateToDisk(siteConfig *oneill.SiteConfig, container docker.APIContainers) {
	tmpl, err := template.New("nginx-config").Parse(nginxTemplate)
	if err != nil {
		oneill.LogWarning(fmt.Sprintf("Unable to load nginx config template: %s", siteConfig.Subdomain))
		return
	}

	var b bytes.Buffer
	context := templateContext{
		Subdomain:     siteConfig.Subdomain,
		ServingDomain: oneill.Config.ServingDomain,
		Port:          container.Ports[0].PublicPort,
	}
	err = tmpl.Execute(&b, context)
	if err != nil {
		oneill.LogWarning(fmt.Sprintf("Unable to execute nginx config template: %s", siteConfig.Subdomain))
		return
	}
	outDir := oneill.Config.NginxConfigDirectory
	outFile := path.Join(outDir, fmt.Sprintf("%s.conf", siteConfig.Subdomain))
	err = ioutil.WriteFile(outFile, b.Bytes(), 0644)
	if err != nil {
		oneill.LogWarning(fmt.Sprintf("Unable to write nginx config template: %s", siteConfig.Subdomain))
		return
	}
}

func ConfigureNginx(siteConfigs []*oneill.SiteConfig) []*oneill.SiteConfig {
	oneill.LogInfo("## Configuring Nginx")

	ensureFreshOutputDir()

	for _, sc := range siteConfigs {
		for _, container := range oneill.ListContainers() {
			containerName := strings.TrimPrefix(container.Names[0], "/")
			if containerName == sc.Subdomain {
				writeTemplateToDisk(sc, container)
				oneill.LogDebug(fmt.Sprintf("Configured nginx proxy for container: %s", sc.Subdomain))
				break
			}
		}
	}
	if err := reloadNginxConfig(); err != nil {
		oneill.LogWarning("Unable to reload nginx configuration")
		return siteConfigs
	}
	oneill.LogDebug("Reloaded nginx configuration")
	return siteConfigs
}
<|endoftext|>


<commit_before>package doozer

import (
	"doozer/client"
	"github.com/bmizerany/assert"
	"net"
	"runtime"
	"testing"
)

// Upper bound on number of leaked goroutines.
// Our goal is to reduce this to zero.
const leaked = 23

func mustListen() net.Listener {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		panic(err)
	}
	return l
}

func mustListenPacket(addr string) net.PacketConn {
	c, err := net.ListenPacket("udp", addr)
	if err != nil {
		panic(err)
	}
	return c
}

func TestDoozerSimple(t *testing.T) {
	l := mustListen()
	defer l.Close()
	u := mustListenPacket(l.Addr().String())
	defer u.Close()

	go Main("a", "", u, l, nil)

	cl, err := client.Dial(l.Addr().String())
	assert.Equal(t, nil, err)
	assert.Equal(t, nil, cl.Noop())
}

func TestDoozerWatchSimple(t *testing.T) {
	l := mustListen()
	defer l.Close()
	u := mustListenPacket(l.Addr().String())
	defer u.Close()

	go Main("a", "", u, l, nil)

	cl, err := client.Dial(l.Addr().String())
	assert.Equal(t, nil, err)

	ch, err := cl.Watch("/test/**")
	assert.Equal(t, nil, err, err)
	defer close(ch)

	cl.Set("/test/foo", "bar", "")
	ev := <-ch
	assert.Equal(t, "/test/foo", ev.Path)
	assert.Equal(t, "bar", ev.Body)
	assert.NotEqual(t, "", ev.Cas)

	cl.Set("/test/fun", "house", "")
	ev = <-ch
	assert.Equal(t, "/test/fun", ev.Path)
	assert.Equal(t, "house", ev.Body)
	assert.NotEqual(t, "", ev.Cas)
}

func TestDoozerGoroutines(t *testing.T) {
	gs := runtime.Goroutines()

	func() {
		l := mustListen()
		defer l.Close()
		u := mustListenPacket(l.Addr().String())
		defer u.Close()

		go Main("a", "", u, l, nil)

		cl, err := client.Dial(l.Addr().String())
		assert.Equal(t, nil, err)
		cl.Noop()
	}()

	assert.T(t, gs+leaked >= runtime.Goroutines(), gs+leaked)
}
<commit_msg>add a basic benchmark<commit_after>package doozer

import (
	"doozer/client"
	"doozer/store"
	"github.com/bmizerany/assert"
	"net"
	"runtime"
	"testing"
)

// Upper bound on number of leaked goroutines.
// Our goal is to reduce this to zero.
const leaked = 23

func mustListen() net.Listener {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		panic(err)
	}
	return l
}

func mustListenPacket(addr string) net.PacketConn {
	c, err := net.ListenPacket("udp", addr)
	if err != nil {
		panic(err)
	}
	return c
}

func TestDoozerSimple(t *testing.T) {
	l := mustListen()
	defer l.Close()
	u := mustListenPacket(l.Addr().String())
	defer u.Close()

	go Main("a", "", u, l, nil)

	cl, err := client.Dial(l.Addr().String())
	assert.Equal(t, nil, err)
	assert.Equal(t, nil, cl.Noop())
}

func TestDoozerWatchSimple(t *testing.T) {
	l := mustListen()
	defer l.Close()
	u := mustListenPacket(l.Addr().String())
	defer u.Close()

	go Main("a", "", u, l, nil)

	cl, err := client.Dial(l.Addr().String())
	assert.Equal(t, nil, err)

	ch, err := cl.Watch("/test/**")
	assert.Equal(t, nil, err, err)
	defer close(ch)

	cl.Set("/test/foo", "bar", "")
	ev := <-ch
	assert.Equal(t, "/test/foo", ev.Path)
	assert.Equal(t, "bar", ev.Body)
	assert.NotEqual(t, "", ev.Cas)

	cl.Set("/test/fun", "house", "")
	ev = <-ch
	assert.Equal(t, "/test/fun", ev.Path)
	assert.Equal(t, "house", ev.Body)
	assert.NotEqual(t, "", ev.Cas)
}

func TestDoozerGoroutines(t *testing.T) {
	gs := runtime.Goroutines()

	func() {
		l := mustListen()
		defer l.Close()
		u := mustListenPacket(l.Addr().String())
		defer u.Close()

		go Main("a", "", u, l, nil)

		cl, err := client.Dial(l.Addr().String())
		assert.Equal(t, nil, err)
		cl.Noop()
	}()

	assert.T(t, gs+leaked >= runtime.Goroutines(), gs+leaked)
}

func BenchmarkDoozerClientSet(b *testing.B) {
	b.StopTimer()
	l := mustListen()
	defer l.Close()
	a := l.Addr().String()
	u := mustListenPacket(a)
	defer u.Close()

	go Main("a", "", u, l, nil)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil)
	go Main("a", a, mustListenPacket(":0"), mustListen(), nil)

	cl, err := client.Dial(l.Addr().String())
	if err != nil {
		panic(err)
	}

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		cl.Set("/test", "", store.Clobber)
	}
}
<|endoftext|>


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

// Primitive HTTP client. See RFC 2616.

package http

import (
	"bufio"
	"encoding/base64"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
)

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Used in Send to implement io.ReadCloser by bundling together the
// io.BufReader through which we read the response, and the underlying
// network connection.
type readClose struct {
	io.Reader
	io.Closer
}

// Send issues an HTTP request.  Caller should close resp.Body when done reading it.
//
// TODO: support persistent connections (multiple requests on a single connection).
// send() method is nonpublic because, when we refactor the code for persistent
// connections, it may no longer make sense to have a method with this signature.
func send(req *Request) (resp *Response, err os.Error) {
	if req.URL.Scheme != "http" {
		return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
	}

	addr := req.URL.Host
	if !hasPort(addr) {
		addr += ":http"
	}
	info := req.URL.Userinfo
	if len(info) > 0 {
		enc := base64.URLEncoding
		encoded := make([]byte, enc.EncodedLen(len(info)))
		enc.Encode(encoded, strings.Bytes(info))
		if req.Header == nil {
			req.Header = make(map[string]string)
		}
		req.Header["Authorization"] = "Basic " + string(encoded)
	}
	conn, err := net.Dial("tcp", "", addr)
	if err != nil {
		return nil, err
	}

	err = req.Write(conn)
	if err != nil {
		conn.Close()
		return nil, err
	}

	reader := bufio.NewReader(conn)
	resp, err = ReadResponse(reader, req.Method)
	if err != nil {
		conn.Close()
		return nil, err
	}

	resp.Body = readClose{resp.Body, conn}

	return
}

// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
func shouldRedirect(statusCode int) bool {
	switch statusCode {
	case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
		return true
	}
	return false
}

// Get issues a GET to the specified URL.  If the response is one of the following
// redirect codes, it follows the redirect, up to a maximum of 10 redirects:
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// finalURL is the URL from which the response was fetched -- identical to the
// input URL unless redirects were followed.
//
// Caller should close r.Body when done reading it.
func Get(url string) (r *Response, finalURL string, err os.Error) {
	// TODO: if/when we add cookie support, the redirected request shouldn't
	// necessarily supply the same cookies as the original.
	// TODO: set referrer header on redirects.
	for redirect := 0; ; redirect++ {
		if redirect >= 10 {
			err = os.ErrorString("stopped after 10 redirects")
			break
		}

		var req Request
		if req.URL, err = ParseURL(url); err != nil {
			break
		}
		if r, err = send(&req); err != nil {
			break
		}
		if shouldRedirect(r.StatusCode) {
			r.Body.Close()
			if url = r.GetHeader("Location"); url == "" {
				err = os.ErrorString(fmt.Sprintf("%d response missing Location header", r.StatusCode))
				break
			}
			continue
		}
		finalURL = url
		return
	}

	err = &URLError{"Get", url, err}
	return
}

// Post issues a POST to the specified URL.
//
// Caller should close r.Body when done reading it.
func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
	var req Request
	req.Method = "POST"
	req.Body = nopCloser{body}
	req.Header = map[string]string{
		"Content-Type": bodyType,
		"Transfer-Encoding": "chunked",
	}

	req.URL, err = ParseURL(url)
	if err != nil {
		return nil, err
	}

	return send(&req)
}

type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() os.Error { return nil }
<commit_msg>http: fix bug in Post<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Primitive HTTP client. See RFC 2616.

package http

import (
	"bufio"
	"encoding/base64"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
)

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Used in Send to implement io.ReadCloser by bundling together the
// io.BufReader through which we read the response, and the underlying
// network connection.
type readClose struct {
	io.Reader
	io.Closer
}

// Send issues an HTTP request.  Caller should close resp.Body when done reading it.
//
// TODO: support persistent connections (multiple requests on a single connection).
// send() method is nonpublic because, when we refactor the code for persistent
// connections, it may no longer make sense to have a method with this signature.
func send(req *Request) (resp *Response, err os.Error) {
	if req.URL.Scheme != "http" {
		return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
	}

	addr := req.URL.Host
	if !hasPort(addr) {
		addr += ":http"
	}
	info := req.URL.Userinfo
	if len(info) > 0 {
		enc := base64.URLEncoding
		encoded := make([]byte, enc.EncodedLen(len(info)))
		enc.Encode(encoded, strings.Bytes(info))
		if req.Header == nil {
			req.Header = make(map[string]string)
		}
		req.Header["Authorization"] = "Basic " + string(encoded)
	}
	conn, err := net.Dial("tcp", "", addr)
	if err != nil {
		return nil, err
	}

	err = req.Write(conn)
	if err != nil {
		conn.Close()
		return nil, err
	}

	reader := bufio.NewReader(conn)
	resp, err = ReadResponse(reader, req.Method)
	if err != nil {
		conn.Close()
		return nil, err
	}

	resp.Body = readClose{resp.Body, conn}

	return
}

// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
func shouldRedirect(statusCode int) bool {
	switch statusCode {
	case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
		return true
	}
	return false
}

// Get issues a GET to the specified URL.  If the response is one of the following
// redirect codes, it follows the redirect, up to a maximum of 10 redirects:
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// finalURL is the URL from which the response was fetched -- identical to the
// input URL unless redirects were followed.
//
// Caller should close r.Body when done reading it.
func Get(url string) (r *Response, finalURL string, err os.Error) {
	// TODO: if/when we add cookie support, the redirected request shouldn't
	// necessarily supply the same cookies as the original.
	// TODO: set referrer header on redirects.
	for redirect := 0; ; redirect++ {
		if redirect >= 10 {
			err = os.ErrorString("stopped after 10 redirects")
			break
		}

		var req Request
		if req.URL, err = ParseURL(url); err != nil {
			break
		}
		if r, err = send(&req); err != nil {
			break
		}
		if shouldRedirect(r.StatusCode) {
			r.Body.Close()
			if url = r.GetHeader("Location"); url == "" {
				err = os.ErrorString(fmt.Sprintf("%d response missing Location header", r.StatusCode))
				break
			}
			continue
		}
		finalURL = url
		return
	}

	err = &URLError{"Get", url, err}
	return
}

// Post issues a POST to the specified URL.
//
// Caller should close r.Body when done reading it.
func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
	var req Request
	req.Method = "POST"
	req.ProtoMajor = 1
	req.ProtoMinor = 1
	req.Body = nopCloser{body}
	req.Header = map[string]string{
		"Content-Type": bodyType,
	}
	req.TransferEncoding = []string{"chunked"}

	req.URL, err = ParseURL(url)
	if err != nil {
		return nil, err
	}

	return send(&req)
}

type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() os.Error { return nil }
<|endoftext|>


<commit_before>// Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package caps

// Capability represents an optional feature that a client may request from the server.
type Capability string

const (
	// AccountNotify is this IRCv3 capability: http://ircv3.net/specs/extensions/account-notify-3.1.html
	AccountNotify Capability = "account-notify"
	// AccountTag is this IRCv3 capability: http://ircv3.net/specs/extensions/account-tag-3.2.html
	AccountTag Capability = "account-tag"
	// AwayNotify is this IRCv3 capability: http://ircv3.net/specs/extensions/away-notify-3.1.html
	AwayNotify Capability = "away-notify"
	// Batch is this IRCv3 capability: http://ircv3.net/specs/extensions/batch-3.2.html
	Batch Capability = "batch"
	// CapNotify is this IRCv3 capability: http://ircv3.net/specs/extensions/cap-notify-3.2.html
	CapNotify Capability = "cap-notify"
	// ChgHost is this IRCv3 capability: http://ircv3.net/specs/extensions/chghost-3.2.html
	ChgHost Capability = "chghost"
	// EchoMessage is this IRCv3 capability: http://ircv3.net/specs/extensions/echo-message-3.2.html
	EchoMessage Capability = "echo-message"
	// ExtendedJoin is this IRCv3 capability: http://ircv3.net/specs/extensions/extended-join-3.1.html
	ExtendedJoin Capability = "extended-join"
	// InviteNotify is this IRCv3 capability: http://ircv3.net/specs/extensions/invite-notify-3.2.html
	InviteNotify Capability = "invite-notify"
	// LabeledResponse is this draft IRCv3 capability: http://ircv3.net/specs/extensions/labeled-response.html
	LabeledResponse Capability = "draft/labeled-response"
	// MaxLine is this proposed capability: https://github.com/DanielOaks/ircv3-specifications/blob/master+line-lengths/extensions/line-lengths.md
	MaxLine Capability = "draft/maxline"
	// MessageTags is this draft IRCv3 capability: http://ircv3.net/specs/core/message-tags-3.3.html
	MessageTags Capability = "draft/message-tags-0.2"
	// MultiPrefix is this IRCv3 capability: http://ircv3.net/specs/extensions/multi-prefix-3.1.html
	MultiPrefix Capability = "multi-prefix"
	// Rename is this proposed capability: https://github.com/SaberUK/ircv3-specifications/blob/rename/extensions/rename.md
	Rename Capability = "draft/rename"
	// SASL is this IRCv3 capability: http://ircv3.net/specs/extensions/sasl-3.2.html
	SASL Capability = "sasl"
	// ServerTime is this IRCv3 capability: http://ircv3.net/specs/extensions/server-time-3.2.html
	ServerTime Capability = "server-time"
	// STS is this draft IRCv3 capability: http://ircv3.net/specs/core/sts-3.3.html
	STS Capability = "draft/sts"
	// UserhostInNames is this IRCv3 capability: http://ircv3.net/specs/extensions/userhost-in-names-3.2.html
	UserhostInNames Capability = "userhost-in-names"
)

// Name returns the name of the given capability.
func (capability Capability) Name() string {
	return string(capability)
}

// Version is used to select which max version of CAP the client supports.
type Version uint

const (
	// Cap301 refers to the base CAP spec.
	Cap301 Version = 301
	// Cap302 refers to the IRCv3.2 CAP spec.
	Cap302 Version = 302
)
<commit_msg>draft/sts -> sts<commit_after>// Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license

package caps

// Capability represents an optional feature that a client may request from the server.
type Capability string

const (
	// AccountNotify is this IRCv3 capability: http://ircv3.net/specs/extensions/account-notify-3.1.html
	AccountNotify Capability = "account-notify"
	// AccountTag is this IRCv3 capability: http://ircv3.net/specs/extensions/account-tag-3.2.html
	AccountTag Capability = "account-tag"
	// AwayNotify is this IRCv3 capability: http://ircv3.net/specs/extensions/away-notify-3.1.html
	AwayNotify Capability = "away-notify"
	// Batch is this IRCv3 capability: http://ircv3.net/specs/extensions/batch-3.2.html
	Batch Capability = "batch"
	// CapNotify is this IRCv3 capability: http://ircv3.net/specs/extensions/cap-notify-3.2.html
	CapNotify Capability = "cap-notify"
	// ChgHost is this IRCv3 capability: http://ircv3.net/specs/extensions/chghost-3.2.html
	ChgHost Capability = "chghost"
	// EchoMessage is this IRCv3 capability: http://ircv3.net/specs/extensions/echo-message-3.2.html
	EchoMessage Capability = "echo-message"
	// ExtendedJoin is this IRCv3 capability: http://ircv3.net/specs/extensions/extended-join-3.1.html
	ExtendedJoin Capability = "extended-join"
	// InviteNotify is this IRCv3 capability: http://ircv3.net/specs/extensions/invite-notify-3.2.html
	InviteNotify Capability = "invite-notify"
	// LabeledResponse is this draft IRCv3 capability: http://ircv3.net/specs/extensions/labeled-response.html
	LabeledResponse Capability = "draft/labeled-response"
	// MaxLine is this proposed capability: https://github.com/DanielOaks/ircv3-specifications/blob/master+line-lengths/extensions/line-lengths.md
	MaxLine Capability = "draft/maxline"
	// MessageTags is this draft IRCv3 capability: http://ircv3.net/specs/core/message-tags-3.3.html
	MessageTags Capability = "draft/message-tags-0.2"
	// MultiPrefix is this IRCv3 capability: http://ircv3.net/specs/extensions/multi-prefix-3.1.html
	MultiPrefix Capability = "multi-prefix"
	// Rename is this proposed capability: https://github.com/SaberUK/ircv3-specifications/blob/rename/extensions/rename.md
	Rename Capability = "draft/rename"
	// SASL is this IRCv3 capability: http://ircv3.net/specs/extensions/sasl-3.2.html
	SASL Capability = "sasl"
	// ServerTime is this IRCv3 capability: http://ircv3.net/specs/extensions/server-time-3.2.html
	ServerTime Capability = "server-time"
	// STS is this IRCv3 capability: http://ircv3.net/specs/extensions/sts.html
	STS Capability = "sts"
	// UserhostInNames is this IRCv3 capability: http://ircv3.net/specs/extensions/userhost-in-names-3.2.html
	UserhostInNames Capability = "userhost-in-names"
)

// Name returns the name of the given capability.
func (capability Capability) Name() string {
	return string(capability)
}

// Version is used to select which max version of CAP the client supports.
type Version uint

const (
	// Cap301 refers to the base CAP spec.
	Cap301 Version = 301
	// Cap302 refers to the IRCv3.2 CAP spec.
	Cap302 Version = 302
)
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/brutella/hc/db"
	"github.com/brutella/hc/hap"
	"github.com/brutella/hc/hap/pair"
	"io"
	"log"
	"net/http"
)

func pairSetup(b io.Reader) (io.Reader, error) {
	return sendTLV8(b, "pair-setup")
}

func pairVerify(b io.Reader) (io.Reader, error) {
	return sendTLV8(b, "pair-verify")
}

func sendTLV8(b io.Reader, endpoint string) (io.Reader, error) {
	url := fmt.Sprintf("http://127.0.0.1:64521/%s", endpoint)
	resp, err := http.Post(url, hap.HTTPContentTypePairingTLV8, b)
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Invalid status code %v", resp.StatusCode)
	}
	return resp.Body, err
}

func main() {
	database, _ := db.NewDatabase("./data")
	c, _ := hap.NewDevice("Golang Client", database)
	client := pair.NewSetupClientController("336-02-620", c, database)
	pairStartRequest := client.InitialPairingRequest()

	pairStartResponse, err := pairSetup(pairStartRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 2) S -> C
	pairVerifyRequest, err := pair.HandleReaderForHandler(pairStartResponse, client)
	if err != nil {
		log.Info.Panic(err)
	}

	// 3) C -> S
	pairVerifyResponse, err := pairSetup(pairVerifyRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 4) S -> C
	pairKeyRequest, err := pair.HandleReaderForHandler(pairVerifyResponse, client)
	if err != nil {
		log.Info.Panic(err)
	}

	// 5) C -> S
	pairKeyRespond, err := pairSetup(pairKeyRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 6) S -> C
	request, err := pair.HandleReaderForHandler(pairKeyRespond, client)
	if err != nil {
		log.Info.Panic(err)
	}

	if request != nil {
		log.Println(request)
	}

	log.Println("*** Pairing done ***")

	verify := pair.NewVerifyClientController(c, database)

	verifyStartRequest := verify.InitialKeyVerifyRequest()
	// 1) C -> S
	verifyStartResponse, err := pairVerify(verifyStartRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 2) S -> C
	verifyFinishRequest, err := pair.HandleReaderForHandler(verifyStartResponse, verify)
	if err != nil {
		log.Info.Panic(err)
	}

	// 3) C -> S
	verifyFinishResponse, err := pairVerify(verifyFinishRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 4) S -> C
	last_request, err := pair.HandleReaderForHandler(verifyFinishResponse, verify)
	if err != nil {
		log.Info.Panic(err)
	}

	if last_request != nil {
		log.Println(last_request)
	}

	log.Println("*** Key Verification done ***")
}
<commit_msg>Use local log package<commit_after>package main

import (
	"fmt"
	"github.com/brutella/hc/db"
	"github.com/brutella/hc/hap"
	"github.com/brutella/hc/hap/pair"
	"github.com/brutella/hc/log"
	"io"
	"net/http"
)

func pairSetup(b io.Reader) (io.Reader, error) {
	return sendTLV8(b, "pair-setup")
}

func pairVerify(b io.Reader) (io.Reader, error) {
	return sendTLV8(b, "pair-verify")
}

func sendTLV8(b io.Reader, endpoint string) (io.Reader, error) {
	url := fmt.Sprintf("http://127.0.0.1:64521/%s", endpoint)
	resp, err := http.Post(url, hap.HTTPContentTypePairingTLV8, b)
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Invalid status code %v", resp.StatusCode)
	}
	return resp.Body, err
}

func main() {
	database, _ := db.NewDatabase("./data")
	c, _ := hap.NewDevice("Golang Client", database)
	client := pair.NewSetupClientController("336-02-620", c, database)
	pairStartRequest := client.InitialPairingRequest()

	pairStartResponse, err := pairSetup(pairStartRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 2) S -> C
	pairVerifyRequest, err := pair.HandleReaderForHandler(pairStartResponse, client)
	if err != nil {
		log.Info.Panic(err)
	}

	// 3) C -> S
	pairVerifyResponse, err := pairSetup(pairVerifyRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 4) S -> C
	pairKeyRequest, err := pair.HandleReaderForHandler(pairVerifyResponse, client)
	if err != nil {
		log.Info.Panic(err)
	}

	// 5) C -> S
	pairKeyRespond, err := pairSetup(pairKeyRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 6) S -> C
	request, err := pair.HandleReaderForHandler(pairKeyRespond, client)
	if err != nil {
		log.Info.Panic(err)
	}

	if request != nil {
		log.Info.Println(request)
	}

	log.Info.Println("*** Pairing done ***")

	verify := pair.NewVerifyClientController(c, database)

	verifyStartRequest := verify.InitialKeyVerifyRequest()
	// 1) C -> S
	verifyStartResponse, err := pairVerify(verifyStartRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 2) S -> C
	verifyFinishRequest, err := pair.HandleReaderForHandler(verifyStartResponse, verify)
	if err != nil {
		log.Info.Panic(err)
	}

	// 3) C -> S
	verifyFinishResponse, err := pairVerify(verifyFinishRequest)
	if err != nil {
		log.Info.Panic(err)
	}

	// 4) S -> C
	last_request, err := pair.HandleReaderForHandler(verifyFinishResponse, verify)
	if err != nil {
		log.Info.Panic(err)
	}

	if last_request != nil {
		log.Info.Println(last_request)
	}

	log.Info.Println("*** Key Verification done ***")
}
<|endoftext|>


<commit_before>package ircserver

import (
	"log"
	"strings"

	"github.com/sorcix/irc"
)

// Each handler function must have the “cmd” prefix and return either a single
// *irc.Message or a slice of *irc.Message.

func init() {
	// Keep this list ordered the same way the functions below are ordered.
	commands["PING"] = &ircCommand{Func: cmdPing}
	commands["NICK"] = &ircCommand{
		Func: cmdNick,
		Interesting: func(s *Session, msg *irc.Message) bool {
			// TODO(secure): does it make sense to restrict this to Sessions which
			// have a channel in common? noting this because it doesn’t handle the
			// query-only use-case. if there’s no downside (except for the privacy
			// aspect), perhaps leave it as-is?
			return true
		},
	}
	commands["USER"] = &ircCommand{Func: cmdUser, MinParams: 3}
	commands["JOIN"] = &ircCommand{
		Func:        cmdJoin,
		MinParams:   1,
		Interesting: interestJoin,
	}
	commands["PART"] = &ircCommand{
		Func:        cmdPart,
		MinParams:   1,
		Interesting: interestPart,
	}
	commands["QUIT"] = &ircCommand{
		Func: cmdQuit,
		Interesting: func(s *Session, msg *irc.Message) bool {
			// TODO(secure): does it make sense to restrict this to Sessions which
			// have a channel in common? noting this because it doesn’t handle the
			// query-only use-case. if there’s no downside (except for the privacy
			// aspect), perhaps leave it as-is?
			return true
		},
	}
	commands["PRIVMSG"] = &ircCommand{Func: cmdPrivmsg, Interesting: interestPrivmsg}
	commands["MODE"] = &ircCommand{
		Func:        cmdMode,
		MinParams:   1,
		Interesting: commonChannelOrDirect,
	}
	commands["WHO"] = &ircCommand{Func: cmdWho}
	commands["OPER"] = &ircCommand{Func: cmdOper, MinParams: 2}
	commands["KILL"] = &ircCommand{Func: cmdKill, MinParams: 1}
}

// commonChannelOrDirect returns true when msg’s first parameter is a channel
// name of 's' or when the first parameter is the nickname of 's'.
func commonChannelOrDirect(s *Session, msg *irc.Message) bool {
	return s.Channels[msg.Params[0]] || msg.Params[0] == s.Nick
}

func cmdPing(s *Session, msg *irc.Message) interface{} {
	if len(msg.Params) < 1 {
		return &irc.Message{
			Command:  irc.ERR_NOORIGIN,
			Params:   []string{s.Nick},
			Trailing: "No origin specified",
		}
	}
	return &irc.Message{
		Command: irc.PONG,
		Params:  []string{msg.Params[0]},
	}
}

func cmdNick(s *Session, msg *irc.Message) interface{} {
	oldPrefix := s.ircPrefix

	if len(msg.Params) < 1 {
		return &irc.Message{
			Command:  irc.ERR_NONICKNAMEGIVEN,
			Trailing: "No nickname given",
		}
	}

	if !IsValidNickname(msg.Params[0]) {
		return &irc.Message{
			Command:  irc.ERR_ERRONEUSNICKNAME,
			Params:   []string{"*", msg.Params[0]},
			Trailing: "Erroneus nickname.",
		}
	}

	inuse := false
	for _, session := range Sessions {
		if NickToLower(session.Nick) == NickToLower(msg.Params[0]) {
			inuse = true
			break
		}
	}
	if inuse {
		return &irc.Message{
			Command:  irc.ERR_NICKNAMEINUSE,
			Params:   []string{"*", msg.Params[0]},
			Trailing: "Nickname is already in use.",
		}
	}
	s.Nick = msg.Params[0]
	s.updateIrcPrefix()
	log.Printf("nickname now: %+v\n", s)
	if oldPrefix.String() != "" {
		return &irc.Message{
			Prefix:   &oldPrefix,
			Command:  irc.NICK,
			Trailing: msg.Params[0],
		}
	}

	var replies []*irc.Message

	// TODO(secure): send 002, 003, 004, 251, 252, 254, 255, 265, 266, [motd = 375, 372, 376]
	replies = append(replies, &irc.Message{
		Command:  irc.RPL_WELCOME,
		Params:   []string{s.Nick},
		Trailing: "Welcome to RobustIRC!",
	})

	replies = append(replies, &irc.Message{
		Command:  irc.RPL_YOURHOST,
		Params:   []string{s.Nick},
		Trailing: "Your host is " + ServerPrefix.Name,
	})

	replies = append(replies, &irc.Message{
		Command:  irc.RPL_CREATED,
		Params:   []string{s.Nick},
		Trailing: "This server was created " + serverCreation.String(),
	})

	replies = append(replies, &irc.Message{
		Command: irc.RPL_MYINFO,
		Params:  []string{s.Nick},
		// TODO(secure): actually support these modes.
		Trailing: ServerPrefix.Name + " v1 i nst",
	})

	// send ISUPPORT as per http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
	replies = append(replies, &irc.Message{
		Command: "005",
		Params: []string{
			"CHANTYPES=#",
			"CHANNELLEN=" + maxChannelLen,
			"NICKLEN=" + maxNickLen,
			"MODES=1",
			"PREFIX=",
		},
		Trailing: "are supported by this server",
	})

	return replies
}

func cmdUser(s *Session, msg *irc.Message) interface{} {
	// We don’t need any information from the USER message.
	return []*irc.Message{}
}

func interestJoin(s *Session, msg *irc.Message) bool {
	return s.Channels[msg.Trailing]
}

func cmdJoin(s *Session, msg *irc.Message) interface{} {
	// TODO(secure): strictly speaking, RFC1459 says one can join multiple channels at once.
	channel := msg.Params[0]
	if !IsValidChannel(channel) {
		return &irc.Message{
			Command:  irc.ERR_NOSUCHCHANNEL,
			Params:   []string{s.Nick, channel},
			Trailing: "No such channel",
		}
	}
	s.Channels[channel] = true
	var nicks []string
	// TODO(secure): a separate map for quick lookup may be worthwhile for big channels.
	for _, session := range Sessions {
		if !session.Channels[channel] {
			continue
		}
		nicks = append(nicks, session.Nick)
	}

	var replies []*irc.Message

	replies = append(replies, &irc.Message{
		Prefix:   &s.ircPrefix,
		Command:  irc.JOIN,
		Trailing: channel,
	})
	//replies = append(replies, irc.Message{
	//	Prefix:  &irc.Prefix{Name: *network},
	//	Command: irc.RPL_NOTOPIC,
	//	Params:  []string{channel},
	//})
	// TODO(secure): why the = param?
	replies = append(replies, &irc.Message{
		Command:  irc.RPL_NAMREPLY,
		Params:   []string{s.Nick, "=", channel},
		Trailing: strings.Join(nicks, " "),
	})
	replies = append(replies, &irc.Message{
		Command:  irc.RPL_ENDOFNAMES,
		Params:   []string{s.Nick, channel},
		Trailing: "End of /NAMES list.",
	})

	return replies
}

func interestPart(s *Session, msg *irc.Message) bool {
	// Do send PART messages back to the sender (who, by now, is not in the
	// channel anymore).
	return s.ircPrefix == *msg.Prefix || s.Channels[msg.Params[0]]
}

func cmdPart(s *Session, msg *irc.Message) interface{} {
	// TODO(secure): strictly speaking, RFC1459 says one can join multiple channels at once.
	channel := msg.Params[0]
	delete(s.Channels, channel)
	return &irc.Message{
		Prefix:  &s.ircPrefix,
		Command: irc.PART,
		Params:  []string{channel},
	}
}

func cmdQuit(s *Session, msg *irc.Message) interface{} {
	return &irc.Message{
		Prefix:   &s.ircPrefix,
		Command:  irc.QUIT,
		Trailing: msg.Trailing,
	}
}

func interestPrivmsg(s *Session, msg *irc.Message) bool {
	// Don’t send messages back to the sender.
	if s.ircPrefix == *msg.Prefix {
		return false
	}

	return commonChannelOrDirect(s, msg)
}

func cmdPrivmsg(s *Session, msg *irc.Message) interface{} {
	if len(msg.Params) < 1 {
		return &irc.Message{
			Command:  irc.ERR_NORECIPIENT,
			Params:   []string{s.Nick},
			Trailing: "No recipient given (PRIVMSG)",
		}
	}

	if msg.Trailing == "" {
		return &irc.Message{
			Command:  irc.ERR_NOTEXTTOSEND,
			Params:   []string{s.Nick},
			Trailing: "No text to send",
		}
	}

	return &irc.Message{
		Prefix:   &s.ircPrefix,
		Command:  irc.PRIVMSG,
		Params:   []string{msg.Params[0]},
		Trailing: msg.Trailing,
	}
}

func cmdMode(s *Session, msg *irc.Message) interface{} {
	channel := msg.Params[0]
	// TODO(secure): properly distinguish between users and channels
	if s.Channels[channel] {
		if len(msg.Params) > 1 && msg.Params[1] == "b" {
			return &irc.Message{
				Command:  irc.RPL_ENDOFBANLIST,
				Params:   []string{s.Nick, channel},
				Trailing: "End of Channel Ban List",
			}
		} else {
			return &irc.Message{
				Command: irc.RPL_CHANNELMODEIS,
				Params:  []string{s.Nick, channel, "+"},
			}
		}
	} else {
		if channel == s.Nick {
			return &irc.Message{
				Prefix:   &s.ircPrefix,
				Command:  irc.MODE,
				Params:   []string{s.Nick},
				Trailing: "+",
			}
		} else {
			return &irc.Message{
				Command:  irc.ERR_NOTONCHANNEL,
				Params:   []string{s.Nick, channel},
				Trailing: "You're not on that channel",
			}
		}
	}
}

func cmdWho(s *Session, msg *irc.Message) interface{} {
	if len(msg.Params) < 1 {
		return &irc.Message{
			Command:  irc.RPL_ENDOFWHO,
			Params:   []string{s.Nick},
			Trailing: "End of /WHO list",
		}
	}

	var replies []*irc.Message

	channel := msg.Params[0]
	// TODO(secure): a separate map for quick lookup may be worthwhile for big channels.
	for _, session := range Sessions {
		if !session.Channels[channel] {
			continue
		}
		prefix := session.ircPrefix
		replies = append(replies, &irc.Message{
			Command:  irc.RPL_WHOREPLY,
			Params:   []string{s.Nick, channel, prefix.User, prefix.Host, ServerPrefix.Name, prefix.Name, "H"},
			Trailing: "0 Unknown",
		})
	}

	replies = append(replies, &irc.Message{
		Command:  irc.RPL_ENDOFWHO,
		Params:   []string{s.Nick, channel},
		Trailing: "End of /WHO list",
	})

	return replies
}

func cmdOper(s *Session, msg *irc.Message) interface{} {
	// TODO(secure): implement restriction to certain hosts once we have a
	// configuration file. (ERR_NOOPERHOST)

	if msg.Params[1] != NetworkPassword {
		return &irc.Message{
			Command:  irc.ERR_PASSWDMISMATCH,
			Params:   []string{s.Nick},
			Trailing: "Password incorrect",
		}
	}

	s.Operator = true

	return &irc.Message{
		Command:  irc.RPL_YOUREOPER,
		Params:   []string{s.Nick},
		Trailing: "You are now an IRC operator",
	}
}

func cmdKill(s *Session, msg *irc.Message) interface{} {
	if strings.TrimSpace(msg.Trailing) == "" {
		return &irc.Message{
			Command:  irc.ERR_NEEDMOREPARAMS,
			Params:   []string{s.Nick, msg.Command},
			Trailing: "Not enough parameters",
		}
	}

	if !s.Operator {
		return &irc.Message{
			Command:  irc.ERR_NOPRIVILEGES,
			Params:   []string{s.Nick},
			Trailing: "Permission Denied - You're not an IRC operator",
		}
	}

	for _, session := range Sessions {
		if NickToLower(session.Nick) != NickToLower(msg.Params[0]) {
			continue
		}

		prefix := session.ircPrefix
		DeleteSession(session.Id)
		return &irc.Message{
			Prefix:   &prefix,
			Command:  irc.QUIT,
			Trailing: "Killed by " + s.Nick + ": " + msg.Trailing,
		}
	}

	return &irc.Message{
		Command:  irc.ERR_NOSUCHNICK,
		Params:   []string{s.Nick, msg.Params[0]},
		Trailing: "No such nick/channel",
	}
}
<commit_msg>return directly<commit_after>package ircserver

import (
	"log"
	"strings"

	"github.com/sorcix/irc"
)

// Each handler function must have the “cmd” prefix and return either a single
// *irc.Message or a slice of *irc.Message.

func init() {
	// Keep this list ordered the same way the functions below are ordered.
	commands["PING"] = &ircCommand{Func: cmdPing}
	commands["NICK"] = &ircCommand{
		Func: cmdNick,
		Interesting: func(s *Session, msg *irc.Message) bool {
			// TODO(secure): does it make sense to restrict this to Sessions which
			// have a channel in common? noting this because it doesn’t handle the
			// query-only use-case. if there’s no downside (except for the privacy
			// aspect), perhaps leave it as-is?
			return true
		},
	}
	commands["USER"] = &ircCommand{Func: cmdUser, MinParams: 3}
	commands["JOIN"] = &ircCommand{
		Func:        cmdJoin,
		MinParams:   1,
		Interesting: interestJoin,
	}
	commands["PART"] = &ircCommand{
		Func:        cmdPart,
		MinParams:   1,
		Interesting: interestPart,
	}
	commands["QUIT"] = &ircCommand{
		Func: cmdQuit,
		Interesting: func(s *Session, msg *irc.Message) bool {
			// TODO(secure): does it make sense to restrict this to Sessions which
			// have a channel in common? noting this because it doesn’t handle the
			// query-only use-case. if there’s no downside (except for the privacy
			// aspect), perhaps leave it as-is?
			return true
		},
	}
	commands["PRIVMSG"] = &ircCommand{Func: cmdPrivmsg, Interesting: interestPrivmsg}
	commands["MODE"] = &ircCommand{
		Func:        cmdMode,
		MinParams:   1,
		Interesting: commonChannelOrDirect,
	}
	commands["WHO"] = &ircCommand{Func: cmdWho}
	commands["OPER"] = &ircCommand{Func: cmdOper, MinParams: 2}
	commands["KILL"] = &ircCommand{Func: cmdKill, MinParams: 1}
}

// commonChannelOrDirect returns true when msg’s first parameter is a channel
// name of 's' or when the first parameter is the nickname of 's'.
func commonChannelOrDirect(s *Session, msg *irc.Message) bool {
	return s.Channels[msg.Params[0]] || msg.Params[0] == s.Nick
}

func cmdPing(s *Session, msg *irc.Message) interface{} {
	if len(msg.Params) < 1 {
		return &irc.Message{
			Command:  irc.ERR_NOORIGIN,
			Params:   []string{s.Nick},
			Trailing: "No origin specified",
		}
	}
	return &irc.Message{
		Command: irc.PONG,
		Params:  []string{msg.Params[0]},
	}
}

func cmdNick(s *Session, msg *irc.Message) interface{} {
	oldPrefix := s.ircPrefix

	if len(msg.Params) < 1 {
		return &irc.Message{
			Command:  irc.ERR_NONICKNAMEGIVEN,
			Trailing: "No nickname given",
		}
	}

	if !IsValidNickname(msg.Params[0]) {
		return &irc.Message{
			Command:  irc.ERR_ERRONEUSNICKNAME,
			Params:   []string{"*", msg.Params[0]},
			Trailing: "Erroneus nickname.",
		}
	}

	for _, session := range Sessions {
		if NickToLower(session.Nick) == NickToLower(msg.Params[0]) {
			return &irc.Message{
				Command:  irc.ERR_NICKNAMEINUSE,
				Params:   []string{"*", msg.Params[0]},
				Trailing: "Nickname is already in use.",
			}
		}
	}
	s.Nick = msg.Params[0]
	s.updateIrcPrefix()
	log.Printf("nickname now: %+v\n", s)
	if oldPrefix.String() != "" {
		return &irc.Message{
			Prefix:   &oldPrefix,
			Command:  irc.NICK,
			Trailing: msg.Params[0],
		}
	}

	var replies []*irc.Message

	// TODO(secure): send 002, 003, 004, 251, 252, 254, 255, 265, 266, [motd = 375, 372, 376]
	replies = append(replies, &irc.Message{
		Command:  irc.RPL_WELCOME,
		Params:   []string{s.Nick},
		Trailing: "Welcome to RobustIRC!",
	})

	replies = append(replies, &irc.Message{
		Command:  irc.RPL_YOURHOST,
		Params:   []string{s.Nick},
		Trailing: "Your host is " + ServerPrefix.Name,
	})

	replies = append(replies, &irc.Message{
		Command:  irc.RPL_CREATED,
		Params:   []string{s.Nick},
		Trailing: "This server was created " + serverCreation.String(),
	})

	replies = append(replies, &irc.Message{
		Command: irc.RPL_MYINFO,
		Params:  []string{s.Nick},
		// TODO(secure): actually support these modes.
		Trailing: ServerPrefix.Name + " v1 i nst",
	})

	// send ISUPPORT as per http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
	replies = append(replies, &irc.Message{
		Command: "005",
		Params: []string{
			"CHANTYPES=#",
			"CHANNELLEN=" + maxChannelLen,
			"NICKLEN=" + maxNickLen,
			"MODES=1",
			"PREFIX=",
		},
		Trailing: "are supported by this server",
	})

	return replies
}

func cmdUser(s *Session, msg *irc.Message) interface{} {
	// We don’t need any information from the USER message.
	return []*irc.Message{}
}

func interestJoin(s *Session, msg *irc.Message) bool {
	return s.Channels[msg.Trailing]
}

func cmdJoin(s *Session, msg *irc.Message) interface{} {
	// TODO(secure): strictly speaking, RFC1459 says one can join multiple channels at once.
	channel := msg.Params[0]
	if !IsValidChannel(channel) {
		return &irc.Message{
			Command:  irc.ERR_NOSUCHCHANNEL,
			Params:   []string{s.Nick, channel},
			Trailing: "No such channel",
		}
	}
	s.Channels[channel] = true
	var nicks []string
	// TODO(secure): a separate map for quick lookup may be worthwhile for big channels.
	for _, session := range Sessions {
		if !session.Channels[channel] {
			continue
		}
		nicks = append(nicks, session.Nick)
	}

	var replies []*irc.Message

	replies = append(replies, &irc.Message{
		Prefix:   &s.ircPrefix,
		Command:  irc.JOIN,
		Trailing: channel,
	})
	//replies = append(replies, irc.Message{
	//	Prefix:  &irc.Prefix{Name: *network},
	//	Command: irc.RPL_NOTOPIC,
	//	Params:  []string{channel},
	//})
	// TODO(secure): why the = param?
	replies = append(replies, &irc.Message{
		Command:  irc.RPL_NAMREPLY,
		Params:   []string{s.Nick, "=", channel},
		Trailing: strings.Join(nicks, " "),
	})
	replies = append(replies, &irc.Message{
		Command:  irc.RPL_ENDOFNAMES,
		Params:   []string{s.Nick, channel},
		Trailing: "End of /NAMES list.",
	})

	return replies
}

func interestPart(s *Session, msg *irc.Message) bool {
	// Do send PART messages back to the sender (who, by now, is not in the
	// channel anymore).
	return s.ircPrefix == *msg.Prefix || s.Channels[msg.Params[0]]
}

func cmdPart(s *Session, msg *irc.Message) interface{} {
	// TODO(secure): strictly speaking, RFC1459 says one can join multiple channels at once.
	channel := msg.Params[0]
	delete(s.Channels, channel)
	return &irc.Message{
		Prefix:  &s.ircPrefix,
		Command: irc.PART,
		Params:  []string{channel},
	}
}

func cmdQuit(s *Session, msg *irc.Message) interface{} {
	return &irc.Message{
		Prefix:   &s.ircPrefix,
		Command:  irc.QUIT,
		Trailing: msg.Trailing,
	}
}

func interestPrivmsg(s *Session, msg *irc.Message) bool {
	// Don’t send messages back to the sender.
	if s.ircPrefix == *msg.Prefix {
		return false
	}

	return commonChannelOrDirect(s, msg)
}

func cmdPrivmsg(s *Session, msg *irc.Message) interface{} {
	if len(msg.Params) < 1 {
		return &irc.Message{
			Command:  irc.ERR_NORECIPIENT,
			Params:   []string{s.Nick},
			Trailing: "No recipient given (PRIVMSG)",
		}
	}

	if msg.Trailing == "" {
		return &irc.Message{
			Command:  irc.ERR_NOTEXTTOSEND,
			Params:   []string{s.Nick},
			Trailing: "No text to send",
		}
	}

	return &irc.Message{
		Prefix:   &s.ircPrefix,
		Command:  irc.PRIVMSG,
		Params:   []string{msg.Params[0]},
		Trailing: msg.Trailing,
	}
}

func cmdMode(s *Session, msg *irc.Message) interface{} {
	channel := msg.Params[0]
	// TODO(secure): properly distinguish between users and channels
	if s.Channels[channel] {
		if len(msg.Params) > 1 && msg.Params[1] == "b" {
			return &irc.Message{
				Command:  irc.RPL_ENDOFBANLIST,
				Params:   []string{s.Nick, channel},
				Trailing: "End of Channel Ban List",
			}
		} else {
			return &irc.Message{
				Command: irc.RPL_CHANNELMODEIS,
				Params:  []string{s.Nick, channel, "+"},
			}
		}
	} else {
		if channel == s.Nick {
			return &irc.Message{
				Prefix:   &s.ircPrefix,
				Command:  irc.MODE,
				Params:   []string{s.Nick},
				Trailing: "+",
			}
		} else {
			return &irc.Message{
				Command:  irc.ERR_NOTONCHANNEL,
				Params:   []string{s.Nick, channel},
				Trailing: "You're not on that channel",
			}
		}
	}
}

func cmdWho(s *Session, msg *irc.Message) interface{} {
	if len(msg.Params) < 1 {
		return &irc.Message{
			Command:  irc.RPL_ENDOFWHO,
			Params:   []string{s.Nick},
			Trailing: "End of /WHO list",
		}
	}

	var replies []*irc.Message

	channel := msg.Params[0]
	// TODO(secure): a separate map for quick lookup may be worthwhile for big channels.
	for _, session := range Sessions {
		if !session.Channels[channel] {
			continue
		}
		prefix := session.ircPrefix
		replies = append(replies, &irc.Message{
			Command:  irc.RPL_WHOREPLY,
			Params:   []string{s.Nick, channel, prefix.User, prefix.Host, ServerPrefix.Name, prefix.Name, "H"},
			Trailing: "0 Unknown",
		})
	}

	replies = append(replies, &irc.Message{
		Command:  irc.RPL_ENDOFWHO,
		Params:   []string{s.Nick, channel},
		Trailing: "End of /WHO list",
	})

	return replies
}

func cmdOper(s *Session, msg *irc.Message) interface{} {
	// TODO(secure): implement restriction to certain hosts once we have a
	// configuration file. (ERR_NOOPERHOST)

	if msg.Params[1] != NetworkPassword {
		return &irc.Message{
			Command:  irc.ERR_PASSWDMISMATCH,
			Params:   []string{s.Nick},
			Trailing: "Password incorrect",
		}
	}

	s.Operator = true

	return &irc.Message{
		Command:  irc.RPL_YOUREOPER,
		Params:   []string{s.Nick},
		Trailing: "You are now an IRC operator",
	}
}

func cmdKill(s *Session, msg *irc.Message) interface{} {
	if strings.TrimSpace(msg.Trailing) == "" {
		return &irc.Message{
			Command:  irc.ERR_NEEDMOREPARAMS,
			Params:   []string{s.Nick, msg.Command},
			Trailing: "Not enough parameters",
		}
	}

	if !s.Operator {
		return &irc.Message{
			Command:  irc.ERR_NOPRIVILEGES,
			Params:   []string{s.Nick},
			Trailing: "Permission Denied - You're not an IRC operator",
		}
	}

	for _, session := range Sessions {
		if NickToLower(session.Nick) != NickToLower(msg.Params[0]) {
			continue
		}

		prefix := session.ircPrefix
		DeleteSession(session.Id)
		return &irc.Message{
			Prefix:   &prefix,
			Command:  irc.QUIT,
			Trailing: "Killed by " + s.Nick + ": " + msg.Trailing,
		}
	}

	return &irc.Message{
		Command:  irc.ERR_NOSUCHNICK,
		Params:   []string{s.Nick, msg.Params[0]},
		Trailing: "No such nick/channel",
	}
}
<|endoftext|>