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

package integration

import (
	"context"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/google/safehtml/template"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/fetch"
	"golang.org/x/pkgsite/internal/frontend"
	"golang.org/x/pkgsite/internal/middleware"
	"golang.org/x/pkgsite/internal/postgres"
	"golang.org/x/pkgsite/internal/proxy"
	"golang.org/x/pkgsite/internal/queue"
	"golang.org/x/pkgsite/internal/source"
	"golang.org/x/pkgsite/internal/testing/htmlcheck"
	"golang.org/x/pkgsite/internal/testing/testhelper"
)

var (
	in      = htmlcheck.In
	hasText = htmlcheck.HasText
)

func TestModulePackageDirectoryResolution(t *testing.T) {
	// The shared test state sets up the following scenario to exercise
	// what happens when a directory becomes a package,
	// and then becomes a directory again. Specifically:
	//  + at v1.2.3, github.com/golang/found/dir is a directory (containing dir/pkg)
	//  + at v1.2.4, github.com/golang/found/dir is a package
	//  + at v1.2.5, github.com/golang/found/dir is again just a directory
	versions := []*proxy.Module{
		{
			ModulePath: "github.com/golang/found",
			Version:    "v1.2.3",
			Files: map[string]string{
				"go.mod":         "module github.com/golang/found",
				"found.go":       "package found\nconst Value = 123",
				"dir/pkg/pkg.go": "package pkg\nconst Value = 321",
				"LICENSE":        testhelper.MITLicense,
			},
		},
		{
			ModulePath: "github.com/golang/found",
			Version:    "v1.2.4",
			Files: map[string]string{
				"go.mod":         "module github.com/golang/found",
				"found.go":       "package found\nconst Value = 124",
				"dir/pkg/pkg.go": "package pkg\nconst Value = 421",
				"dir/dir.go":     "package dir\nconst Value = \"I'm a package!\"",
				"LICENSE":        testhelper.MITLicense,
			},
		},
		{
			ModulePath: "github.com/golang/found",
			Version:    "v1.2.5",
			Files: map[string]string{
				"go.mod":         "module github.com/golang/found",
				"found.go":       "package found\nconst Value = 125",
				"dir/pkg/pkg.go": "package pkg\nconst Value = 521",
				"LICENSE":        testhelper.MITLicense,
			},
		},
	}

	tests := []struct {
		// Test description.
		desc string
		// URL Path (relative to the test server) to check
		urlPath string
		// The expected HTTP status code.
		wantCode int
		// If non-nil, used to verify the resulting page.
		want htmlcheck.Checker
	}{
		{
			desc:     "missing module",
			urlPath:  "/mod/github.com/golang/missing",
			wantCode: http.StatusNotFound,
		},
		{
			desc:     "latest module",
			urlPath:  "/mod/github.com/golang/found",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.5")),
				in(".DetailsHeader-badge", in(".DetailsHeader-badge--latest"))),
		},
		{
			desc:     "versioned module",
			urlPath:  "/mod/github.com/golang/found@v1.2.3",
			wantCode: http.StatusOK,
			want:     in(".DetailsHeader", hasText("v1.2.3")),
		},
		{
			desc:     "non-existent version",
			urlPath:  "/mod/github.com/golang/found@v1.1.3",
			wantCode: http.StatusNotFound,
			want:     in("h3.Fetch-message.js-fetchMessage", hasText("github.com/golang/found@v1.1.3")),
		},
		{
			desc:     "latest package",
			urlPath:  "/github.com/golang/found/dir/pkg",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.5")),
				in(".DetailsContent", hasText("521")),
				in(".DetailsHeader-badge", in(".DetailsHeader-badge--latest"))),
		},
		{
			desc:     "earlier package",
			urlPath:  "/github.com/golang/found@v1.2.3/dir/pkg",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.3")),
				in(".DetailsContent", hasText("321"))),
		},
		{
			desc:     "dir is initially a directory",
			urlPath:  "/github.com/golang/found@v1.2.3/dir",
			wantCode: http.StatusOK,
			want:     in(".Directories", hasText("pkg")),
		},
		{
			desc:     "dir becomes a package",
			urlPath:  "/github.com/golang/found@v1.2.4/dir",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.4")),
				in(".DetailsContent", hasText("I'm a package"))),
		},
		{
			desc:     "dir becomes a directory again",
			urlPath:  "/github.com/golang/found@v1.2.5/dir",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.5"))),
		},
		{
			desc:     "latest package for /dir",
			urlPath:  "/github.com/golang/found/dir",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.5"))),
		},
	}
	ctx := context.Background()
	ts := setupFrontend(ctx, t, nil)
	processVersions(ctx, t, versions)
	defer postgres.ResetTestDB(testDB, t)

	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			testURL := ts.URL + test.urlPath
			validateResponse(t, http.MethodGet, testURL, test.wantCode, test.want)
		})
	}
}

// TODO(https://github.com/golang/go/issues/40096): factor out this code reduce
// duplication
func setupFrontend(ctx context.Context, t *testing.T, q queue.Queue) *httptest.Server {
	t.Helper()
	s, err := frontend.NewServer(frontend.ServerConfig{
		DataSourceGetter:     func(context.Context) internal.DataSource { return testDB },
		TaskIDChangeInterval: 10 * time.Minute,
		StaticPath:           template.TrustedSourceFromConstant("../../../content/static"),
		ThirdPartyPath:       "../../../third_party",
		AppVersionLabel:      "",
		Queue:                q,
	})
	if err != nil {
		t.Fatal(err)
	}
	mux := http.NewServeMux()
	s.Install(mux.Handle, nil, nil)

	experimenter, err := middleware.NewExperimenter(ctx, 1*time.Minute, testDB.GetExperiments, nil)
	if err != nil {
		t.Fatal(err)
	}
	enableCSP := true
	mw := middleware.Chain(
		middleware.AcceptRequests(http.MethodGet, http.MethodPost),
		middleware.SecureHeaders(enableCSP),
		middleware.LatestVersions(s.GetLatestMinorVersion, s.GetLatestMajorVersion),
		middleware.Experiment(experimenter),
	)
	return httptest.NewServer(mw(mux))
}

// TODO(https://github.com/golang/go/issues/40098): factor out this code reduce
// duplication
func setupQueue(ctx context.Context, t *testing.T, proxyModules []*proxy.Module, experimentNames ...string) (queue.Queue, func()) {
	proxyClient, teardown := proxy.SetupTestClient(t, proxyModules)
	sourceClient := source.NewClient(1 * time.Second)
	q := queue.NewInMemory(ctx, 1, experimentNames,
		func(ctx context.Context, mpath, version string) (int, error) {
			return frontend.FetchAndUpdateState(ctx, mpath, version, proxyClient, sourceClient, testDB)
		})
	return q, func() {
		teardown()
	}
}

func processVersions(ctx context.Context, t *testing.T, testModules []*proxy.Module) {
	t.Helper()
	proxyClient, teardown := proxy.SetupTestClient(t, testModules)
	defer teardown()

	for _, tm := range testModules {
		fetchAndInsertModule(ctx, t, tm, proxyClient)
	}
}

func fetchAndInsertModule(ctx context.Context, t *testing.T, tm *proxy.Module, proxyClient *proxy.Client) {
	sourceClient := source.NewClient(1 * time.Second)
	res := fetch.FetchModule(ctx, tm.ModulePath, tm.Version, proxyClient, sourceClient)
	defer res.Defer()
	if res.Error != nil {
		t.Fatal(res.Error)
	}
	if err := testDB.InsertModule(ctx, res.Module); err != nil {
		t.Fatal(err)
	}
}

func validateResponse(t *testing.T, method, testURL string, wantCode int, wantHTML htmlcheck.Checker) {
	t.Helper()

	var (
		resp *http.Response
		err  error
	)
	if method == http.MethodPost {
		resp, err = http.Post(testURL, "text/plain", nil)
	} else {
		resp, err = http.Get(testURL)
	}
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != wantCode {
		t.Fatalf("%q request to %q returned status %d, want %d", method, testURL, resp.StatusCode, wantCode)
	}
	if wantHTML != nil {
		if err := htmlcheck.Run(resp.Body, wantHTML); err != nil {
			t.Fatal(err)
		}
	}
}
<commit_msg>internal/testing/integration: get frontend experiments from context<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package integration

import (
	"context"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/google/safehtml/template"
	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/experiment"
	"golang.org/x/pkgsite/internal/fetch"
	"golang.org/x/pkgsite/internal/frontend"
	"golang.org/x/pkgsite/internal/middleware"
	"golang.org/x/pkgsite/internal/postgres"
	"golang.org/x/pkgsite/internal/proxy"
	"golang.org/x/pkgsite/internal/queue"
	"golang.org/x/pkgsite/internal/source"
	"golang.org/x/pkgsite/internal/testing/htmlcheck"
	"golang.org/x/pkgsite/internal/testing/testhelper"
)

var (
	in      = htmlcheck.In
	hasText = htmlcheck.HasText
)

func TestModulePackageDirectoryResolution(t *testing.T) {
	// The shared test state sets up the following scenario to exercise
	// what happens when a directory becomes a package,
	// and then becomes a directory again. Specifically:
	//  + at v1.2.3, github.com/golang/found/dir is a directory (containing dir/pkg)
	//  + at v1.2.4, github.com/golang/found/dir is a package
	//  + at v1.2.5, github.com/golang/found/dir is again just a directory
	versions := []*proxy.Module{
		{
			ModulePath: "github.com/golang/found",
			Version:    "v1.2.3",
			Files: map[string]string{
				"go.mod":         "module github.com/golang/found",
				"found.go":       "package found\nconst Value = 123",
				"dir/pkg/pkg.go": "package pkg\nconst Value = 321",
				"LICENSE":        testhelper.MITLicense,
			},
		},
		{
			ModulePath: "github.com/golang/found",
			Version:    "v1.2.4",
			Files: map[string]string{
				"go.mod":         "module github.com/golang/found",
				"found.go":       "package found\nconst Value = 124",
				"dir/pkg/pkg.go": "package pkg\nconst Value = 421",
				"dir/dir.go":     "package dir\nconst Value = \"I'm a package!\"",
				"LICENSE":        testhelper.MITLicense,
			},
		},
		{
			ModulePath: "github.com/golang/found",
			Version:    "v1.2.5",
			Files: map[string]string{
				"go.mod":         "module github.com/golang/found",
				"found.go":       "package found\nconst Value = 125",
				"dir/pkg/pkg.go": "package pkg\nconst Value = 521",
				"LICENSE":        testhelper.MITLicense,
			},
		},
	}

	tests := []struct {
		// Test description.
		desc string
		// URL Path (relative to the test server) to check
		urlPath string
		// The expected HTTP status code.
		wantCode int
		// If non-nil, used to verify the resulting page.
		want htmlcheck.Checker
	}{
		{
			desc:     "missing module",
			urlPath:  "/mod/github.com/golang/missing",
			wantCode: http.StatusNotFound,
		},
		{
			desc:     "latest module",
			urlPath:  "/mod/github.com/golang/found",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.5")),
				in(".DetailsHeader-badge", in(".DetailsHeader-badge--latest"))),
		},
		{
			desc:     "versioned module",
			urlPath:  "/mod/github.com/golang/found@v1.2.3",
			wantCode: http.StatusOK,
			want:     in(".DetailsHeader", hasText("v1.2.3")),
		},
		{
			desc:     "non-existent version",
			urlPath:  "/mod/github.com/golang/found@v1.1.3",
			wantCode: http.StatusNotFound,
			want:     in("h3.Fetch-message.js-fetchMessage", hasText("github.com/golang/found@v1.1.3")),
		},
		{
			desc:     "latest package",
			urlPath:  "/github.com/golang/found/dir/pkg",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.5")),
				in(".DetailsContent", hasText("521")),
				in(".DetailsHeader-badge", in(".DetailsHeader-badge--latest"))),
		},
		{
			desc:     "earlier package",
			urlPath:  "/github.com/golang/found@v1.2.3/dir/pkg",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.3")),
				in(".DetailsContent", hasText("321"))),
		},
		{
			desc:     "dir is initially a directory",
			urlPath:  "/github.com/golang/found@v1.2.3/dir",
			wantCode: http.StatusOK,
			want:     in(".Directories", hasText("pkg")),
		},
		{
			desc:     "dir becomes a package",
			urlPath:  "/github.com/golang/found@v1.2.4/dir",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.4")),
				in(".DetailsContent", hasText("I'm a package"))),
		},
		{
			desc:     "dir becomes a directory again",
			urlPath:  "/github.com/golang/found@v1.2.5/dir",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.5"))),
		},
		{
			desc:     "latest package for /dir",
			urlPath:  "/github.com/golang/found/dir",
			wantCode: http.StatusOK,
			want: in("",
				in(".DetailsHeader", hasText("v1.2.5"))),
		},
	}
	ctx := context.Background()
	ts := setupFrontend(ctx, t, nil)
	processVersions(ctx, t, versions)
	defer postgres.ResetTestDB(testDB, t)

	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			testURL := ts.URL + test.urlPath
			validateResponse(t, http.MethodGet, testURL, test.wantCode, test.want)
		})
	}
}

// TODO(https://github.com/golang/go/issues/40096): factor out this code reduce
// duplication
func setupFrontend(ctx context.Context, t *testing.T, q queue.Queue) *httptest.Server {
	t.Helper()
	s, err := frontend.NewServer(frontend.ServerConfig{
		DataSourceGetter:     func(context.Context) internal.DataSource { return testDB },
		TaskIDChangeInterval: 10 * time.Minute,
		StaticPath:           template.TrustedSourceFromConstant("../../../content/static"),
		ThirdPartyPath:       "../../../third_party",
		AppVersionLabel:      "",
		Queue:                q,
	})
	if err != nil {
		t.Fatal(err)
	}
	mux := http.NewServeMux()
	s.Install(mux.Handle, nil, nil)

	// Get experiments from the context. Fully roll them out.
	expNames := experiment.FromContext(ctx).Active()
	var exps []*internal.Experiment
	for _, n := range expNames {
		exps = append(exps, &internal.Experiment{
			Name:    n,
			Rollout: 100,
		})
	}
	getter := func(context.Context) ([]*internal.Experiment, error) {
		return exps, nil
	}

	experimenter, err := middleware.NewExperimenter(ctx, 1*time.Minute, getter, nil)
	if err != nil {
		t.Fatal(err)
	}
	enableCSP := true
	mw := middleware.Chain(
		middleware.AcceptRequests(http.MethodGet, http.MethodPost),
		middleware.SecureHeaders(enableCSP),
		middleware.LatestVersions(s.GetLatestMinorVersion, s.GetLatestMajorVersion),
		middleware.Experiment(experimenter),
	)
	return httptest.NewServer(mw(mux))
}

// TODO(https://github.com/golang/go/issues/40098): factor out this code reduce
// duplication
func setupQueue(ctx context.Context, t *testing.T, proxyModules []*proxy.Module, experimentNames ...string) (queue.Queue, func()) {
	proxyClient, teardown := proxy.SetupTestClient(t, proxyModules)
	sourceClient := source.NewClient(1 * time.Second)
	q := queue.NewInMemory(ctx, 1, experimentNames,
		func(ctx context.Context, mpath, version string) (int, error) {
			return frontend.FetchAndUpdateState(ctx, mpath, version, proxyClient, sourceClient, testDB)
		})
	return q, func() {
		teardown()
	}
}

func processVersions(ctx context.Context, t *testing.T, testModules []*proxy.Module) {
	t.Helper()
	proxyClient, teardown := proxy.SetupTestClient(t, testModules)
	defer teardown()

	for _, tm := range testModules {
		fetchAndInsertModule(ctx, t, tm, proxyClient)
	}
}

func fetchAndInsertModule(ctx context.Context, t *testing.T, tm *proxy.Module, proxyClient *proxy.Client) {
	sourceClient := source.NewClient(1 * time.Second)
	res := fetch.FetchModule(ctx, tm.ModulePath, tm.Version, proxyClient, sourceClient)
	defer res.Defer()
	if res.Error != nil {
		t.Fatal(res.Error)
	}
	if err := testDB.InsertModule(ctx, res.Module); err != nil {
		t.Fatal(err)
	}
}

func validateResponse(t *testing.T, method, testURL string, wantCode int, wantHTML htmlcheck.Checker) {
	t.Helper()

	var (
		resp *http.Response
		err  error
	)
	if method == http.MethodPost {
		resp, err = http.Post(testURL, "text/plain", nil)
	} else {
		resp, err = http.Get(testURL)
	}
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != wantCode {
		t.Fatalf("%q request to %q returned status %d, want %d", method, testURL, resp.StatusCode, wantCode)
	}
	if wantHTML != nil {
		if err := htmlcheck.Run(resp.Body, wantHTML); err != nil {
			t.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.net/websocket"
	"github.com/ugorji/go-msgpack"
	"net/url"
	"reflect"
)

const MAX_USERS_PER_LOCATION = 20

var UserCount int

type User struct {
	Socket      *websocket.Conn
	UserId      int
	Nickname    string
	Location    *Location
	MouseIsDown bool
	PositionX   int
	PositionY   int
	ColorRed    int
	ColorGreen  int
	ColorBlue   int
	UsePen      bool
}

func NewUser(ws *websocket.Conn) *User {
	Log.Println("NewUser", "want Lock")
	GlobalLock.Lock()
	Log.Println("NewUser", "got Lock")
	defer func() {
		GlobalLock.Unlock()
		Log.Println("NewUser", "released Lock")
	}()
	UserCount += 1
	user := new(User)
	user.Socket = ws
	user.UserId = UserCount
	location_url, err := url.QueryUnescape(ws.Request().RequestURI[6:])
	if err != nil {
		panic(err)
	}
	user.Location = GetLocation(location_url)
	if len(user.Location.Users) >= MAX_USERS_PER_LOCATION {
		user.Error("Too much users at this location, try adding #something at the end of the URL.")
		return nil
	}
	user.Location.AddUser(user)
	user.UsePen = true
	user.OnOpen()
	return user
}

func EncodeEvent(event []interface{}) []byte {
	result, err := msgpack.Marshal(event)
	if err != nil {
		panic(err)
	}
	return result
}

func (user *User) SendEvent(event []interface{}) {
	//    fmt.Printf("sending %v\n", event)
	//    fmt.Printf("sending %v to %d %#v\n", EncodeEvent(event), user.UserId, user.Socket)
	err := websocket.Message.Send(user.Socket, EncodeEvent(event))
	if err != nil {
		Log.Printf("Couldn't send to %d: %v\n", user.UserId, err)
		user.Socket.Close()
	}
}

func (user *User) SendImage(bytes []byte) {
	if len(bytes) == 0 {
		return
	}
	err := websocket.Message.Send(user.Socket, bytes)
	if err != nil {
		Log.Printf("Couldn't send image to %d: %v\n", user.UserId, err)
		user.Socket.Close()
	}
}

func (user *User) Error(description string) {
	user.SendEvent([]interface{}{
		EventTypeError,
		description,
	})
	user.Socket.Close()
}

func (user *User) Broadcast(event []interface{}, include_myself bool) {
	//    fmt.Printf("users %v\n", user.Location.Users)
	// event.insert(1, user.UserId) ...
	event = append(event[:1], append([]interface{}{user.UserId}, event[1:]...)...)
	for _, other := range user.Location.Users {
		if other != nil {
			//            fmt.Printf("user %v\n", other)
			if !include_myself && other == user {
				continue
			}
			other.SendEvent(event)
		}
	}
}

func (user *User) MouseMove(x int, y int, duration int) {
	//    fmt.Printf("mouse move\n")
	if user.MouseIsDown {
		user.Location.DrawLine(
			user.PositionX, user.PositionY, // origin
			x, y, // destination
			duration,                                       // duration
			user.ColorRed, user.ColorGreen, user.ColorBlue, // color
			user.UsePen, // pen or eraser
		)
	}
	user.PositionX = x
	user.PositionY = y
}

func (user *User) MouseUp() {
	user.MouseIsDown = false
}

func (user *User) MouseDown() {
	user.MouseIsDown = true
}

func (user *User) ChangeTool(use_pen bool) {
	user.UsePen = use_pen
}

func (user *User) ChangeColor(red, green, blue int) {
	user.ColorRed = red
	user.ColorGreen = green
	user.ColorBlue = blue
}

func (user *User) ChangeNickname(nickname string) {
	user.Nickname = nickname
}

func ToInt(n interface{}) (result int) {
	switch n.(type) {
	case int, int8, int16, int32, int64:
		result = int(reflect.ValueOf(n).Int())
	case uint, uint8, uint16, uint32, uint64:
		result = int(reflect.ValueOf(n).Uint())
	default:
		Log.Printf("not an int: %#v ", n)
		panic("not an int!")
	}
	return result
}

func (user *User) GotMessage(event []interface{}) {
	event_type := ToInt(event[0])
	params := event[1:]
	switch event_type {
	case EventTypeMouseMove:
		user.MouseMove(ToInt(params[0]), ToInt(params[1]), ToInt(params[2]))
	case EventTypeMouseUp:
		user.MouseUp()
	case EventTypeMouseDown:
		user.MouseDown()
	case EventTypeChangeTool:
		user.ChangeTool(params[0].(int8) != 0)
	case EventTypeChangeColor:
		user.ChangeColor(ToInt(params[0]), ToInt(params[1]), ToInt(params[2]))
	case EventTypeChangeNickname:
		user.ChangeNickname(params[0].(string))
	}
	user.Broadcast(event, true)
}

func (user *User) OnOpen() {
	// Send the list of present users to this user:
	for _, other := range user.Location.Users {
		user.SendEvent([]interface{}{
			EventTypeJoin,
			other.UserId,
			[]interface{}{other.PositionX, other.PositionY},
			[]interface{}{other.ColorRed, other.ColorGreen, other.ColorBlue},
			other.MouseIsDown,
			false, // not you
			other.Nickname,
			other.UsePen,
		})
	}
	// Send the image without packing it in msgpack to the new user:
	user.SendImage(user.Location.GetImageBytes())
	// Send the delta between the image and now to the new user:
	user.SendEvent([]interface{}{
		EventTypeWelcome,
		user.Location.GetDelta(),
	})
	// Send this new user to other users:
	event := []interface{}{
		EventTypeJoin,
		[]interface{}{user.PositionX, user.PositionY},
		[]interface{}{user.ColorRed, user.ColorGreen, user.ColorBlue},
		user.MouseIsDown,
		false, // not you
		user.Nickname,
		user.UsePen,
	}
	user.Broadcast(event, false)
	// Send this user to himself
	event = []interface{}{
		EventTypeJoin,
		user.UserId,
		[]interface{}{user.PositionX, user.PositionY},
		[]interface{}{user.ColorRed, user.ColorGreen, user.ColorBlue},
		user.MouseIsDown,
		true, // you
		user.Nickname,
		user.UsePen,
	}
	user.SendEvent(event)
}

func (user *User) OnClose() {
	user.Broadcast([]interface{}{EventTypeLeave}, true)
	user.Location.RemoveUser(user)
}
<commit_msg>close connection when the data is not like it should be<commit_after>package main

import (
	"code.google.com/p/go.net/websocket"
	"github.com/ugorji/go-msgpack"
	"net/url"
	"reflect"
	"errors"
)

const MAX_USERS_PER_LOCATION = 20

var UserCount int

type User struct {
	Socket      *websocket.Conn
	UserId      int
	Nickname    string
	Location    *Location
	MouseIsDown bool
	PositionX   int
	PositionY   int
	ColorRed    int
	ColorGreen  int
	ColorBlue   int
	UsePen      bool
}

func NewUser(ws *websocket.Conn) *User {
	Log.Println("NewUser", "want Lock")
	GlobalLock.Lock()
	Log.Println("NewUser", "got Lock")
	defer func() {
		GlobalLock.Unlock()
		Log.Println("NewUser", "released Lock")
	}()
	Log.Println("NewUser", "1")
	UserCount += 1
	user := new(User)
	Log.Println("NewUser", "2")
	user.Socket = ws
	user.UserId = UserCount
	location_url, err := url.QueryUnescape(ws.Request().RequestURI[6:])
	Log.Println("NewUser", "3")
	if err != nil {
		Log.Println("NewUser", "panic")
		panic(err)
	}
	Log.Println("NewUser", "4")
	user.Location = GetLocation(location_url)
	Log.Println("NewUser", "5")
	if len(user.Location.Users) >= MAX_USERS_PER_LOCATION {
		Log.Println("NewUser", "too much")
		user.Error("Too much users at this location, try adding #something at the end of the URL.")
		return nil
	}
	Log.Println("NewUser", "6")
	user.Location.AddUser(user)
	Log.Println("NewUser", "7")
	user.UsePen = true
	Log.Println("NewUser", "8")
	user.OnOpen()
	Log.Println("NewUser", "9")
	return user
}

func EncodeEvent(event []interface{}) []byte {
	result, err := msgpack.Marshal(event)
	if err != nil {
		panic(err)
	}
	return result
}

func (user *User) SendEvent(event []interface{}) {
	//    fmt.Printf("sending %v\n", event)
	//    fmt.Printf("sending %v to %d %#v\n", EncodeEvent(event), user.UserId, user.Socket)
	err := websocket.Message.Send(user.Socket, EncodeEvent(event))
	if err != nil {
		Log.Printf("Couldn't send to %d: %v\n", user.UserId, err)
		user.Socket.Close()
	}
}

func (user *User) SendImage(bytes []byte) {
	if len(bytes) == 0 {
		return
	}
	err := websocket.Message.Send(user.Socket, bytes)
	if err != nil {
		Log.Printf("Couldn't send image to %d: %v\n", user.UserId, err)
		user.Socket.Close()
	}
}

func (user *User) Error(description string) {
	user.SendEvent([]interface{}{
		EventTypeError,
		description,
	})
	user.Socket.Close()
}

func (user *User) Broadcast(event []interface{}, include_myself bool) {
	//    fmt.Printf("users %v\n", user.Location.Users)
	// event.insert(1, user.UserId) ...
	event = append(event[:1], append([]interface{}{user.UserId}, event[1:]...)...)
	for _, other := range user.Location.Users {
		if other != nil {
			//            fmt.Printf("user %v\n", other)
			if !include_myself && other == user {
				continue
			}
			other.SendEvent(event)
		}
	}
}

func (user *User) MouseMove(x int, y int, duration int) {
	//    fmt.Printf("mouse move\n")
	if user.MouseIsDown {
		user.Location.DrawLine(
			user.PositionX, user.PositionY, // origin
			x, y, // destination
			duration,                                       // duration
			user.ColorRed, user.ColorGreen, user.ColorBlue, // color
			user.UsePen, // pen or eraser
		)
	}
	user.PositionX = x
	user.PositionY = y
}

func (user *User) MouseUp() {
	user.MouseIsDown = false
}

func (user *User) MouseDown() {
	user.MouseIsDown = true
}

func (user *User) ChangeTool(use_pen bool) {
	user.UsePen = use_pen
}

func (user *User) ChangeColor(red, green, blue int) {
	user.ColorRed = red
	user.ColorGreen = green
	user.ColorBlue = blue
}

func (user *User) ChangeNickname(nickname string) {
	user.Nickname = nickname
}

func ToInt(n interface{}) (result int, err error) {
	switch n.(type) {
	case int, int8, int16, int32, int64:
		result = int(reflect.ValueOf(n).Int())
	case uint, uint8, uint16, uint32, uint64:
		result = int(reflect.ValueOf(n).Uint())
	default:
		Log.Printf("ToInt, not an int: %#v ", n)
		err = errors.New("Not an int")
	}
	return result, err
}

func (user *User) GotMessage(event []interface{}) {
	event_type, err := ToInt(event[0])
	if err != nil{
		user.Socket.Close()
		return
	}
	params := event[1:]
	switch event_type {
	case EventTypeMouseMove:
		p0, err0 := ToInt(params[0])
		p1, err1 := ToInt(params[1])
		p2, err2 := ToInt(params[2])
		if err0 != nil || err1 != nil || err2 != nil {
			user.Socket.Close()
			return
		}
		user.MouseMove(p0, p1, p2)
	case EventTypeMouseUp:
		user.MouseUp()
	case EventTypeMouseDown:
		user.MouseDown()
	case EventTypeChangeTool:
		user.ChangeTool(params[0].(int8) != 0)
	case EventTypeChangeColor:
		p0, err0 := ToInt(params[0])
		p1, err1 := ToInt(params[1])
		p2, err2 := ToInt(params[2])
		if err0 != nil || err1 != nil || err2 != nil {
			user.Socket.Close()
			return
		}
		user.ChangeColor(p0, p1, p2)
	case EventTypeChangeNickname:
		user.ChangeNickname(params[0].(string))
	}
	user.Broadcast(event, true)
}

func (user *User) OnOpen() {
	// Send the list of present users to this user:
	for _, other := range user.Location.Users {
		user.SendEvent([]interface{}{
			EventTypeJoin,
			other.UserId,
			[]interface{}{other.PositionX, other.PositionY},
			[]interface{}{other.ColorRed, other.ColorGreen, other.ColorBlue},
			other.MouseIsDown,
			false, // not you
			other.Nickname,
			other.UsePen,
		})
	}
	// Send the image without packing it in msgpack to the new user:
	user.SendImage(user.Location.GetImageBytes())
	// Send the delta between the image and now to the new user:
	user.SendEvent([]interface{}{
		EventTypeWelcome,
		user.Location.GetDelta(),
	})
	// Send this new user to other users:
	event := []interface{}{
		EventTypeJoin,
		[]interface{}{user.PositionX, user.PositionY},
		[]interface{}{user.ColorRed, user.ColorGreen, user.ColorBlue},
		user.MouseIsDown,
		false, // not you
		user.Nickname,
		user.UsePen,
	}
	user.Broadcast(event, false)
	// Send this user to himself
	event = []interface{}{
		EventTypeJoin,
		user.UserId,
		[]interface{}{user.PositionX, user.PositionY},
		[]interface{}{user.ColorRed, user.ColorGreen, user.ColorBlue},
		user.MouseIsDown,
		true, // you
		user.Nickname,
		user.UsePen,
	}
	user.SendEvent(event)
}

func (user *User) OnClose() {
	user.Broadcast([]interface{}{EventTypeLeave}, true)
	user.Location.RemoveUser(user)
}
<|endoftext|>


<commit_before>package hue

import (
	"fmt"
	"time"
	"errors"
)

const ISO8601 = "2006-01-02T15:04:05"

type User struct {
	Bridge   *Bridge
	Username string
}

type Light struct {
	Id   string
	Name string
}

func (u *User) GetLights() ([]Light, error) {
	url := fmt.Sprintf("/api/%s/lights", u.Username)

	var lightsMap map[string]map[string]string
	err := u.Bridge.client.Get(url, &lightsMap)
	if err != nil {
		return nil, err
	}

	lights := make([]Light, 0, 10)
	for lightId, lightMap := range lightsMap {
		lights = append(lights, Light{Id: lightId, Name: lightMap["name"]})
	}

	return lights, nil
}

func (u *User) GetNewLights() ([]Light, time.Time, error) {
	url := fmt.Sprintf("/api/%s/lights/new", u.Username)

	var newLightsResponse map[string]interface{}
	err := u.Bridge.client.Get(url, &newLightsResponse)
	if err != nil {
		return nil, time.Time{}, err
	}

	lastScanString, ok := newLightsResponse["lastscan"].(string)
	if !ok {
		return nil, time.Time{}, errors.New("Error parsing lastscan")
	}
	lastScan, err := time.Parse(ISO8601, lastScanString)
	if err != nil {
		return nil, time.Time{}, err
	}
	delete(newLightsResponse, "lastscan")

	lights, err := parseLights(newLightsResponse)
	if err != nil {
		return nil, time.Time{}, err
	}

	return lights, lastScan, nil
}

func parseLights(newLightsResponse map[string]interface{}) ([]Light, error) {
	lights := make([]Light, 0, 10)

	for lightId, lightInterface := range newLightsResponse {
		lightMap, ok := lightInterface.(map[string]interface{})
		if !ok {
			// return nil, newParseError(reflect.TypeOf(map[string]interface{}), lightInterface, "lights map")
			return nil, newParseError()
		}
		name, ok := lightMap["name"].(string)
		if !ok {
			// return nil, newParseError(string, lightMap["name"], "light name")
			return nil, newParseError()
		}
		lights = append(lights, Light{Id: lightId, Name: name})
	}

	return lights, nil
}

// func newParseError(expected reflect.Type, actual interface{}, context string) error {
func newParseError() error {
	return errors.New("Error parsing api response.")
}
<commit_msg>shared parse lights<commit_after>package hue

import (
	"fmt"
	"time"
	"errors"
)

const ISO8601 = "2006-01-02T15:04:05"

type User struct {
	Bridge   *Bridge
	Username string
}

type Light struct {
	Id   string
	Name string
}

func (u *User) GetLights() ([]Light, error) {
	url := fmt.Sprintf("/api/%s/lights", u.Username)

	var lightsMap map[string]interface{}
	err := u.Bridge.client.Get(url, &lightsMap)
	if err != nil {
		return nil, err
	}

	lights, err := parseLights(lightsMap)
	if err != nil {
		return nil, err
	}

	return lights, nil
}

func (u *User) GetNewLights() ([]Light, time.Time, error) {
	url := fmt.Sprintf("/api/%s/lights/new", u.Username)

	var lightsMap map[string]interface{}
	err := u.Bridge.client.Get(url, &lightsMap)
	if err != nil {
		return nil, time.Time{}, err
	}

	lastScanString, ok := lightsMap["lastscan"].(string)
	if !ok {
		return nil, time.Time{}, errors.New("Error parsing lastscan")
	}
	lastScan, err := time.Parse(ISO8601, lastScanString)
	if err != nil {
		return nil, time.Time{}, err
	}
	delete(lightsMap, "lastscan")

	lights, err := parseLights(lightsMap)
	if err != nil {
		return nil, time.Time{}, err
	}

	return lights, lastScan, nil
}

func parseLights(lightsMap map[string]interface{}) ([]Light, error) {
	lights := make([]Light, 0, 10)

	for lightId, lightInterface := range lightsMap {
		lightMap, ok := lightInterface.(map[string]interface{})
		if !ok {
			return nil, &ApiParseError{
				Expected: "map[string]interface{}",
				Actual: lightInterface,
				Context: "lights map",
			}
		}
		name, ok := lightMap["name"].(string)
		if !ok {
			return nil, &ApiParseError{
				Expected: "string",
				Actual: lightMap["name"],
				Context: "lights name",
			}
		}
		lights = append(lights, Light{Id: lightId, Name: name})
	}

	return lights, nil
}

type ApiParseError struct {
	Expected string
	Actual interface{}
	Context string
}

func (e *ApiParseError) Error() string {
	return fmt.Sprintf("Parsing error: expected type '%s' but received '%T' for %s.",
		e.Expected, e.Actual, e.Context)
}
<|endoftext|>


<commit_before>package irckit

import (
	"net"
	"strings"
	"sync"

	"github.com/sorcix/irc"
)

// NewUser creates a *User, wrapping a connection with metadata we need for our server.
func NewUser(c Conn) *User {
	return &User{
		Conn:     c,
		Host:     "*",
		channels: map[Channel]struct{}{},
	}
}

// NewUserNet creates a *User from a net.Conn connection.
func NewUserNet(c net.Conn) *User {
	return NewUser(&conn{
		Conn:    c,
		Encoder: irc.NewEncoder(c),
		Decoder: irc.NewDecoder(c),
	})
}

const defaultCloseMsg = "Closed."

type User struct {
	Conn

	sync.RWMutex
	Nick string // From NICK command
	User string // From USER command
	Real string // From USER command
	Host string

	channels map[Channel]struct{}

	MmInfo
}

func (u *User) ID() string {
	return strings.ToLower(u.Nick)
}

func (u *User) Prefix() *irc.Prefix {
	return &irc.Prefix{
		Name: u.Nick,
		User: u.User,
		Host: u.Host,
	}
}

func (u *User) Close() error {
	for ch := range u.channels {
		ch.Part(u, defaultCloseMsg)
	}
	return u.Conn.Close()
}

func (u *User) String() string {
	return u.Prefix().String()
}

func (u *User) NumChannels() int {
	u.RLock()
	defer u.RUnlock()
	return len(u.channels)
}

func (u *User) Channels() []Channel {
	u.RLock()
	channels := make([]Channel, 0, len(u.channels))
	for ch := range u.channels {
		channels = append(channels, ch)
	}
	u.RUnlock()
	return channels
}

func (u *User) VisibleTo() []*User {
	seen := map[*User]struct{}{}
	seen[u] = struct{}{}

	channels := u.Channels()
	num := 0
	for _, ch := range channels {
		// Don't include self
		num += ch.Len()
	}

	// Pre-allocate
	users := make([]*User, 0, num)
	if num == 0 {
		return users
	}

	// Get all unique users
	for _, ch := range channels {
		for _, other := range ch.Users() {
			if _, dupe := seen[other]; dupe {
				continue
			}
			seen[other] = struct{}{}
			// TODO: Check visibility (once it's implemented)
			users = append(users, other)
		}
	}
	return users
}

// Encode and send each msg until an error occurs, then returns.
func (user *User) Encode(msgs ...*irc.Message) (err error) {
	if user.MmGhostUser {
		return nil
	}
	for _, msg := range msgs {
		logger.Debugf("-> %s", msg)
		err := user.Conn.Encode(msg)
		if err != nil {
			return err
		}
	}
	return nil
}

// Decode will receive and return a decoded message, or an error.
func (user *User) Decode() (*irc.Message, error) {
	if user.MmGhostUser {
		// block
		c := make(chan struct{})
		<-c
	}
	msg, err := user.Conn.Decode()
	if err == nil && msg != nil {
		logger.Debugf("<- %s", msg)
	}
	return msg, err
}
<commit_msg>Add Roles to user<commit_after>package irckit

import (
	"net"
	"strings"
	"sync"

	"github.com/sorcix/irc"
)

// NewUser creates a *User, wrapping a connection with metadata we need for our server.
func NewUser(c Conn) *User {
	return &User{
		Conn:     c,
		Host:     "*",
		channels: map[Channel]struct{}{},
	}
}

// NewUserNet creates a *User from a net.Conn connection.
func NewUserNet(c net.Conn) *User {
	return NewUser(&conn{
		Conn:    c,
		Encoder: irc.NewEncoder(c),
		Decoder: irc.NewDecoder(c),
	})
}

const defaultCloseMsg = "Closed."

type User struct {
	Conn

	sync.RWMutex
	Nick  string // From NICK command
	User  string // From USER command
	Real  string // From USER command
	Host  string
	Roles string

	channels map[Channel]struct{}

	MmInfo
}

func (u *User) ID() string {
	return strings.ToLower(u.Nick)
}

func (u *User) Prefix() *irc.Prefix {
	return &irc.Prefix{
		Name: u.Nick,
		User: u.User,
		Host: u.Host,
	}
}

func (u *User) Close() error {
	for ch := range u.channels {
		ch.Part(u, defaultCloseMsg)
	}
	return u.Conn.Close()
}

func (u *User) String() string {
	return u.Prefix().String()
}

func (u *User) NumChannels() int {
	u.RLock()
	defer u.RUnlock()
	return len(u.channels)
}

func (u *User) Channels() []Channel {
	u.RLock()
	channels := make([]Channel, 0, len(u.channels))
	for ch := range u.channels {
		channels = append(channels, ch)
	}
	u.RUnlock()
	return channels
}

func (u *User) VisibleTo() []*User {
	seen := map[*User]struct{}{}
	seen[u] = struct{}{}

	channels := u.Channels()
	num := 0
	for _, ch := range channels {
		// Don't include self
		num += ch.Len()
	}

	// Pre-allocate
	users := make([]*User, 0, num)
	if num == 0 {
		return users
	}

	// Get all unique users
	for _, ch := range channels {
		for _, other := range ch.Users() {
			if _, dupe := seen[other]; dupe {
				continue
			}
			seen[other] = struct{}{}
			// TODO: Check visibility (once it's implemented)
			users = append(users, other)
		}
	}
	return users
}

// Encode and send each msg until an error occurs, then returns.
func (user *User) Encode(msgs ...*irc.Message) (err error) {
	if user.MmGhostUser {
		return nil
	}
	for _, msg := range msgs {
		logger.Debugf("-> %s", msg)
		err := user.Conn.Encode(msg)
		if err != nil {
			return err
		}
	}
	return nil
}

// Decode will receive and return a decoded message, or an error.
func (user *User) Decode() (*irc.Message, error) {
	if user.MmGhostUser {
		// block
		c := make(chan struct{})
		<-c
	}
	msg, err := user.Conn.Decode()
	if err == nil && msg != nil {
		logger.Debugf("<- %s", msg)
	}
	return msg, err
}
<|endoftext|>


<commit_before>package sqlread

import (
	"log"
	"unicode"
)

func startState(l *lexer) state {
	l.accept(sep)

	if l.hasPrefix("--") {
		return doubleDashCommentState(l, startState)
	}

	if l.hasPrefix("/*") {
		return blockCommentState(l, startState)
	}

	if l.hasPrefix("DROP TABLE ") {
		return dropTableState(l)
	}

	if l.hasPrefix("LOCK TABLES ") {
		return lockTableState(l)
	}

	if l.hasPrefix("UNLOCK TABLES") {
		return unlockTableState(l)
	}

	if l.hasPrefix("CREATE TABLE ") {
		return createTableState(l)
	}

	if l.hasPrefix("INSERT INTO ") {
		return insertIntoTableState(l)
	}

	_, p := l.peak(100)
	log.Println("peak ahead", string(p))

	return nil
}

func insertIntoTableState(l *lexer) state {
	l.start = l.pos
	l.pos += 12

	l.emit(TInsertInto)
	return identifierStateAction(insertValuesState)
}

func insertValuesState(l *lexer) state {
	l.start = l.pos
	l.accept(whitespace)
	if l.hasPrefix("VALUES ") {
		l.pos += 7
		l.emit(TInsertValues)
	} else {
		l.emit(TIllegal)
		return nil
	}

	l.accept(whitespace)

	return insertRowsState
}

func insertRowsState(l *lexer) state {
	l.start = l.pos

	e := l.next()
	if e == lprn {
		return insertRowState
	}
	// _, pp := l.peak(5)
	// log.Println(string(e), string(pp))
	if e == coma && l.hasPrefix("(") {
		l.pos++
		return insertRowState
	}

	if e == semi {
		l.emit(TSemi)
		return startState
	}

	l.emit(TIllegal)
	return nil
}

func insertRowState(l *lexer) state {
	l.start = l.pos
	for {
		c := l.next()

		if in(c, numbers) {
			l.rewind()
			if eatNumber(l) {
				l.emit(TNumber)
				break
			}
		} else if c == sq || c == dq {
			l.rewind()
			if eatString(l) {
				l.emit(TString)
				break
			}
		} else if c == letN && l.hasPrefix("ULL") {
			l.pos += 3
			l.emit(TNull)
			break
		}

		l.emit(TIllegal)
		return nil
	}

	d := l.next()

	if d == rprn {
		// l.rewind()
		// l.emit(TInsertRow)
		return insertRowsState
	}

	if d == coma {
		return insertRowState
	}

	l.emit(TIllegal)
	return nil
}

func doubleDashCommentState(l *lexer, ret state) state {
	l.start = l.pos + 3
	s := ""
	for {
		b := l.next()
		if b == eof || b == lf {
			break
		}
		s += string(b)
	}

	l.emit(TComment)

	return ret
}

func blockCommentState(l *lexer, ret state) state {
	l.pos += 2
	l.start = l.pos

	for {
		if l.hasPrefix("*/") {
			l.emit(TComment)
			l.pos += 2
			break
		}
		l.next()
	}

	return ret
}

func dropTableState(l *lexer) state {
	l.start = l.pos

	if l.until(semi) {
		l.rewind()
		l.emit(TDropTableFullStmt)
		l.start = l.pos
		l.pos++
		l.emit(TSemi)
	} else {
		l.emit(TIllegal)
		return nil
	}

	return startState
}

func lockTableState(l *lexer) state {
	l.start = l.pos

	if l.until(semi) {
		l.rewind()
		l.emit(TLockTableFullStmt)
		l.start = l.pos
		l.pos++
		l.emit(TSemi)
	} else {
		l.emit(TIllegal)
		return nil
	}

	return startState
}

func unlockTableState(l *lexer) state {
	l.start = l.pos

	if l.until(semi) {
		l.rewind()
		l.emit(TUnlockTablesFullStmt)
		l.start = l.pos
		l.pos++
		l.emit(TSemi)
	} else {
		l.emit(TIllegal)
		return nil
	}

	return startState
}

func createTableState(l *lexer) state {
	l.start = l.pos
	l.pos += 13
	l.emit(TCreateTable)

	return identifierStateAction(createTableParamsState)
}

func createTableParamsState(l *lexer) state {
	l.start = l.pos
	l.accept(whitespace)

	if l.next() == lprn {
		l.emit(TLParen)
		return createTableParamState
	}

	l.emit(TIllegal)
	return nil
}

func createTableParamState(l *lexer) state {
	l.accept(whitespace)

	if l.hasPrefix("PRIMARY KEY ") || l.hasPrefix("KEY ") || l.hasPrefix("CONSTRAINT ") || l.hasPrefix("UNIQUE KEY") {
		return createTableDetailState
	}

	return identifierStateAction(createTableParamTypeState)
}

func createTableDetailState(l *lexer) state {
	l.start = l.pos
	o := 0

	for {
		c := l.next()

		if c == lprn {
			o++
		} else if c == rprn {
			o--
		}

		if o < 0 {
			l.rewind()
			l.emit(TCreateTableDetail)

			l.start = l.pos

			l.next()
			l.emit(TRParen)
			return createTableExtra
		}
	}
}

func createTableExtra(l *lexer) state {
	l.start = l.pos
	for {
		c := l.next()

		if l.hasPrefix("COMMENT=") {
			l.pos += 8
			if !eatString(l) {
				l.emit(TIllegal)
				return nil
			}
		}

		if c == semi {
			return startState
		}
	}
}

func createTableParamTypeState(l *lexer) state {
	l.accept(whitespace)
	l.start = l.pos

	for {
		c := l.next()
		r := rune(c)

		if !unicode.IsLetter(r) {
			if l.start == l.pos-1 {
				l.emit(TIllegal)
				return nil
			}

			l.rewind()
			li := l.emit(TColumnType)

			if c == lprn {
				if li.Val == "enum" {
					return createTableParamTypeEnumValuesState
				}

				return createTableParamTypeSizeState
			}

			return createTableParamDetailsState
		}
	}
}

func createTableParamTypeEnumValuesState(l *lexer) state {
	if l.accept([]byte{lprn}) != 1 {
		l.emit(TIllegal)
		return nil
	}

	l.start = l.pos
	for {
		if eatString(l) {
			l.emit(TColumnEnumVal)
		}

		c := l.next()
		if c == rprn {
			return createTableParamDetailsState
		}

		if c != coma {
			l.emit(TIllegal)
			return nil
		}

		l.start = l.pos
	}
}

func createTableParamTypeSizeState(l *lexer) state {
	if l.accept([]byte{lprn}) != 1 {
		l.emit(TIllegal)
		return nil
	}

	l.start = l.pos

	for {
		c := l.next()

		if c == rprn {
			if l.start == l.pos-1 {
				l.emit(TIllegal)
				return nil
			}

			l.rewind()
			l.emit(TColumnSize)
			l.next()

			return createTableParamDetailsState
		}

		if c != coma {
			r := rune(c)

			if !unicode.IsNumber(r) {
				l.emit(TIllegal)
				return nil
			}
		}
	}
}

func createTableParamDetailsState(l *lexer) state {
	l.start = l.pos
	for {
		c := l.next()

		if c == coma || c == rprn {
			l.rewind()
			l.emit(TColumnDetails)
			l.next()
			return createTableParamState
		}

		if l.hasPrefix("COMMENT ") {
			l.pos += 7
			l.accept(whitespace)
			if !eatString(l) {
				l.emit(TIllegal)
				return nil
			}
		}

		if l.hasPrefix("DEFAULT ") {
			l.pos += 7
			l.accept(whitespace)
			eatString(l)
		}
	}
	return nil
}

func eatNumber(l *lexer) bool {
	n1 := l.accept(numbers)
	n2 := 0

	p := l.next()
	if p == dot {
		n2 = l.accept(numbers)
		if n2 == 0 {
			return false
		}
	} else {
		l.rewind()
	}

	return n1+n2 > 0
}

func eatString(l *lexer) bool {
	delim := l.next()
	l.rewind()

	if delim != sq && delim != dq {
		return false
	}

	return eatDelimStr(l, delim)
}

func eatDelimStr(l *lexer, delim byte) bool {
	first := l.next()
	if first != delim {
		l.rewind()
		return false
	}

	for {
		c := l.next()

		if c == eof {
			return false
		}

		if c == bs {
			_, p := l.peak(1)
			if p[0] == delim || p[0] == bs {
				l.next()
			}
		} else if c == delim {
			_, p := l.peak(1)
			if p[0] == delim {
				l.next()
			} else {
				break
			}

		}
	}

	return true
}

func eatIdentifier(l *lexer) bool {
	b := l.next()
	l.rewind()

	if b == bt {
		return eatDelimStr(l, bt)
	}

	log.Fatal("non-backtick identifiers not implemented yet")

	return false
}

func identifierStateAction(ret state) state {
	return func(l *lexer) state {
		l.start = l.pos

		if eatIdentifier(l) {
			l.start++
			l.pos--
			l.emit(TIdentifier)
			l.pos++
		} else {
			l.emit(TIllegal)
		}

		return ret
	}
}
<commit_msg>Add pos to log.Fatal<commit_after>package sqlread

import (
	"log"
	"unicode"
)

func startState(l *lexer) state {
	l.accept(sep)

	if l.hasPrefix("--") {
		return doubleDashCommentState(l, startState)
	}

	if l.hasPrefix("/*") {
		return blockCommentState(l, startState)
	}

	if l.hasPrefix("DROP TABLE ") {
		return dropTableState(l)
	}

	if l.hasPrefix("LOCK TABLES ") {
		return lockTableState(l)
	}

	if l.hasPrefix("UNLOCK TABLES") {
		return unlockTableState(l)
	}

	if l.hasPrefix("CREATE TABLE ") {
		return createTableState(l)
	}

	if l.hasPrefix("INSERT INTO ") {
		return insertIntoTableState(l)
	}

	_, p := l.peak(100)
	log.Println("peak ahead", string(p))

	return nil
}

func insertIntoTableState(l *lexer) state {
	l.start = l.pos
	l.pos += 12

	l.emit(TInsertInto)
	return identifierStateAction(insertValuesState)
}

func insertValuesState(l *lexer) state {
	l.start = l.pos
	l.accept(whitespace)
	if l.hasPrefix("VALUES ") {
		l.pos += 7
		l.emit(TInsertValues)
	} else {
		l.emit(TIllegal)
		return nil
	}

	l.accept(whitespace)

	return insertRowsState
}

func insertRowsState(l *lexer) state {
	l.start = l.pos

	e := l.next()
	if e == lprn {
		return insertRowState
	}
	// _, pp := l.peak(5)
	// log.Println(string(e), string(pp))
	if e == coma && l.hasPrefix("(") {
		l.pos++
		return insertRowState
	}

	if e == semi {
		l.emit(TSemi)
		return startState
	}

	l.emit(TIllegal)
	return nil
}

func insertRowState(l *lexer) state {
	l.start = l.pos
	for {
		c := l.next()

		if in(c, numbers) {
			l.rewind()
			if eatNumber(l) {
				l.emit(TNumber)
				break
			}
		} else if c == sq || c == dq {
			l.rewind()
			if eatString(l) {
				l.emit(TString)
				break
			}
		} else if c == letN && l.hasPrefix("ULL") {
			l.pos += 3
			l.emit(TNull)
			break
		}

		l.emit(TIllegal)
		return nil
	}

	d := l.next()

	if d == rprn {
		// l.rewind()
		// l.emit(TInsertRow)
		return insertRowsState
	}

	if d == coma {
		return insertRowState
	}

	l.emit(TIllegal)
	return nil
}

func doubleDashCommentState(l *lexer, ret state) state {
	l.start = l.pos + 3
	s := ""
	for {
		b := l.next()
		if b == eof || b == lf {
			break
		}
		s += string(b)
	}

	l.emit(TComment)

	return ret
}

func blockCommentState(l *lexer, ret state) state {
	l.pos += 2
	l.start = l.pos

	for {
		if l.hasPrefix("*/") {
			l.emit(TComment)
			l.pos += 2
			break
		}
		l.next()
	}

	return ret
}

func dropTableState(l *lexer) state {
	l.start = l.pos

	if l.until(semi) {
		l.rewind()
		l.emit(TDropTableFullStmt)
		l.start = l.pos
		l.pos++
		l.emit(TSemi)
	} else {
		l.emit(TIllegal)
		return nil
	}

	return startState
}

func lockTableState(l *lexer) state {
	l.start = l.pos

	if l.until(semi) {
		l.rewind()
		l.emit(TLockTableFullStmt)
		l.start = l.pos
		l.pos++
		l.emit(TSemi)
	} else {
		l.emit(TIllegal)
		return nil
	}

	return startState
}

func unlockTableState(l *lexer) state {
	l.start = l.pos

	if l.until(semi) {
		l.rewind()
		l.emit(TUnlockTablesFullStmt)
		l.start = l.pos
		l.pos++
		l.emit(TSemi)
	} else {
		l.emit(TIllegal)
		return nil
	}

	return startState
}

func createTableState(l *lexer) state {
	l.start = l.pos
	l.pos += 13
	l.emit(TCreateTable)

	return identifierStateAction(createTableParamsState)
}

func createTableParamsState(l *lexer) state {
	l.start = l.pos
	l.accept(whitespace)

	if l.next() == lprn {
		l.emit(TLParen)
		return createTableParamState
	}

	l.emit(TIllegal)
	return nil
}

func createTableParamState(l *lexer) state {
	l.accept(whitespace)

	if l.hasPrefix("PRIMARY KEY ") || l.hasPrefix("KEY ") || l.hasPrefix("CONSTRAINT ") || l.hasPrefix("UNIQUE KEY") {
		return createTableDetailState
	}

	return identifierStateAction(createTableParamTypeState)
}

func createTableDetailState(l *lexer) state {
	l.start = l.pos
	o := 0

	for {
		c := l.next()

		if c == lprn {
			o++
		} else if c == rprn {
			o--
		}

		if o < 0 {
			l.rewind()
			l.emit(TCreateTableDetail)

			l.start = l.pos

			l.next()
			l.emit(TRParen)
			return createTableExtra
		}
	}
}

func createTableExtra(l *lexer) state {
	l.start = l.pos
	for {
		c := l.next()

		if l.hasPrefix("COMMENT=") {
			l.pos += 8
			if !eatString(l) {
				l.emit(TIllegal)
				return nil
			}
		}

		if c == semi {
			return startState
		}
	}
}

func createTableParamTypeState(l *lexer) state {
	l.accept(whitespace)
	l.start = l.pos

	for {
		c := l.next()
		r := rune(c)

		if !unicode.IsLetter(r) {
			if l.start == l.pos-1 {
				l.emit(TIllegal)
				return nil
			}

			l.rewind()
			li := l.emit(TColumnType)

			if c == lprn {
				if li.Val == "enum" {
					return createTableParamTypeEnumValuesState
				}

				return createTableParamTypeSizeState
			}

			return createTableParamDetailsState
		}
	}
}

func createTableParamTypeEnumValuesState(l *lexer) state {
	if l.accept([]byte{lprn}) != 1 {
		l.emit(TIllegal)
		return nil
	}

	l.start = l.pos
	for {
		if eatString(l) {
			l.emit(TColumnEnumVal)
		}

		c := l.next()
		if c == rprn {
			return createTableParamDetailsState
		}

		if c != coma {
			l.emit(TIllegal)
			return nil
		}

		l.start = l.pos
	}
}

func createTableParamTypeSizeState(l *lexer) state {
	if l.accept([]byte{lprn}) != 1 {
		l.emit(TIllegal)
		return nil
	}

	l.start = l.pos

	for {
		c := l.next()

		if c == rprn {
			if l.start == l.pos-1 {
				l.emit(TIllegal)
				return nil
			}

			l.rewind()
			l.emit(TColumnSize)
			l.next()

			return createTableParamDetailsState
		}

		if c != coma {
			r := rune(c)

			if !unicode.IsNumber(r) {
				l.emit(TIllegal)
				return nil
			}
		}
	}
}

func createTableParamDetailsState(l *lexer) state {
	l.start = l.pos
	for {
		c := l.next()

		if c == coma || c == rprn {
			l.rewind()
			l.emit(TColumnDetails)
			l.next()
			return createTableParamState
		}

		if l.hasPrefix("COMMENT ") {
			l.pos += 7
			l.accept(whitespace)
			if !eatString(l) {
				l.emit(TIllegal)
				return nil
			}
		}

		if l.hasPrefix("DEFAULT ") {
			l.pos += 7
			l.accept(whitespace)
			eatString(l)
		}
	}
	return nil
}

func eatNumber(l *lexer) bool {
	n1 := l.accept(numbers)
	n2 := 0

	p := l.next()
	if p == dot {
		n2 = l.accept(numbers)
		if n2 == 0 {
			return false
		}
	} else {
		l.rewind()
	}

	return n1+n2 > 0
}

func eatString(l *lexer) bool {
	delim := l.next()
	l.rewind()

	if delim != sq && delim != dq {
		return false
	}

	return eatDelimStr(l, delim)
}

func eatDelimStr(l *lexer, delim byte) bool {
	first := l.next()
	if first != delim {
		l.rewind()
		return false
	}

	for {
		c := l.next()

		if c == eof {
			return false
		}

		if c == bs {
			_, p := l.peak(1)
			if p[0] == delim || p[0] == bs {
				l.next()
			}
		} else if c == delim {
			_, p := l.peak(1)
			if p[0] == delim {
				l.next()
			} else {
				break
			}

		}
	}

	return true
}

func eatIdentifier(l *lexer) bool {
	b := l.next()
	l.rewind()

	if b == bt {
		return eatDelimStr(l, bt)
	}

	log.Fatal("non-backtick identifiers not implemented yet at:", l.pos)

	return false
}

func identifierStateAction(ret state) state {
	return func(l *lexer) state {
		l.start = l.pos

		if eatIdentifier(l) {
			l.start++
			l.pos--
			l.emit(TIdentifier)
			l.pos++
		} else {
			l.emit(TIllegal)
		}

		return ret
	}
}
<|endoftext|>


<commit_before>package gojistatic

import (
	"log"
	"net/http"
	"path"
	"strings"
)

// StaticOptions is a struct for specifiying configuration options for the goji-static middleware.
type StaticOptions struct {
	// Prefix is the optional prefix used to serve the static directory content
	Prefix string
	// Indexfile is teh name of the index file that should be served if it exists
	IndexFile string
	// SkipLogging will disable [Static] log messages when a static file is served
	SkipLogging bool
	// Expires defines which user-defined function to use for producing a HTTP Expires Header
	// https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
	Expires func() string
}

// prepareOptions prepares the configuration options and sets some default options.
func prepareOptions(options []StaticOptions) StaticOptions {
	var opt StaticOptions
	if len(options) > 0 {
		opt = options[0]
	}

	if len(opt.IndexFile) == 0 {
		opt.IndexFile = "index.html"
	}

	if len(opt.Prefix) != 0 {
		// ensure we have a leading "/"
		if opt.Prefix[0] != '/' {
			opt.Prefix = "/" + opt.Prefix
		}

		// remove all trailing "/"
		opt.Prefix = strings.TrimRight(opt.Prefix, "/")
	}

	return opt
}

func Static(directory string, options ...StaticOptions) func(http.Handler) http.Handler {
	dir := http.Dir(directory)
	opt := prepareOptions(options)

	return func(h http.Handler) http.Handler {
		fn := func(w http.ResponseWriter, req *http.Request) {
			if req.Method != "GET" && req.Method != "HEAD" {
				h.ServeHTTP(w, req)
				return
			}
			// Get the file name from the path
			file := req.URL.Path

			// if we have a prefix, filter requests by stripping the prefix
			if opt.Prefix != "" {
				if !strings.HasPrefix(file, opt.Prefix) {
					h.ServeHTTP(w, req)
					return
				}
				file = file[len(opt.Prefix):]
				if file != "" && file[0] != '/' {
					h.ServeHTTP(w, req)
					return
				}
			}

			// Open the file and get the stats
			f, err := dir.Open(file)
			if err != nil {
				h.ServeHTTP(w, req)
				return
			}
			defer f.Close()

			fs, err := f.Stat()
			if err != nil {
				h.ServeHTTP(w, req)
				return
			}

			// if the requested resource is a directory, try to serve the index file
			if fs.IsDir() {
				// redirect if trailling "/"" is missing
				if !strings.HasSuffix(req.URL.Path, "/") {
					http.Redirect(w, req, req.URL.Path+"/", http.StatusFound)
					return
				}

				file = path.Join(file, opt.IndexFile)
				f, err = dir.Open(file)
				if err != nil {
					h.ServeHTTP(w, req)
					return
				}
				defer f.Close()
				fs, err = f.Stat()
				if err != nil || fs.IsDir() {
					h.ServeHTTP(w, req)
					return
				}
			}

			if !opt.SkipLogging {
				log.Println("[Static] Serving " + file)
			}

			// Add an Expires header to the static content
			if opt.Expires != nil {
				w.Header().Set("Expires", opt.Expires())
			}

			http.ServeContent(w, req, file, fs.ModTime(), f)
		}

		return http.HandlerFunc(fn)
	}
}
<commit_msg>Add option to disable browser's caching of served files<commit_after>package gojistatic

import (
	"log"
	"net/http"
	"path"
	"strings"
	"time"
)

// StaticOptions is a struct for specifiying configuration options for the goji-static middleware.
type StaticOptions struct {
	// Prefix is the optional prefix used to serve the static directory content
	Prefix string
	// Indexfile is teh name of the index file that should be served if it exists
	IndexFile string
	// SkipLogging will disable [Static] log messages when a static file is served
	SkipLogging bool
	// Disable browser caching for files
	DisableCaching bool
	// Expires defines which user-defined function to use for producing a HTTP Expires Header
	// https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
	Expires func() string
}

// prepareOptions prepares the configuration options and sets some default options.
func prepareOptions(options []StaticOptions) StaticOptions {
	var opt StaticOptions
	if len(options) > 0 {
		opt = options[0]
	}

	if len(opt.IndexFile) == 0 {
		opt.IndexFile = "index.html"
	}

	if len(opt.Prefix) != 0 {
		// ensure we have a leading "/"
		if opt.Prefix[0] != '/' {
			opt.Prefix = "/" + opt.Prefix
		}

		// remove all trailing "/"
		opt.Prefix = strings.TrimRight(opt.Prefix, "/")
	}

	return opt
}

func Static(directory string, options ...StaticOptions) func(http.Handler) http.Handler {
	dir := http.Dir(directory)
	opt := prepareOptions(options)

	return func(h http.Handler) http.Handler {
		fn := func(w http.ResponseWriter, req *http.Request) {
			if req.Method != "GET" && req.Method != "HEAD" {
				h.ServeHTTP(w, req)
				return
			}
			// Get the file name from the path
			file := req.URL.Path

			// if we have a prefix, filter requests by stripping the prefix
			if opt.Prefix != "" {
				if !strings.HasPrefix(file, opt.Prefix) {
					h.ServeHTTP(w, req)
					return
				}
				file = file[len(opt.Prefix):]
				if file != "" && file[0] != '/' {
					h.ServeHTTP(w, req)
					return
				}
			}

			// Open the file and get the stats
			f, err := dir.Open(file)
			if err != nil {
				h.ServeHTTP(w, req)
				return
			}
			defer f.Close()

			fs, err := f.Stat()
			if err != nil {
				h.ServeHTTP(w, req)
				return
			}

			// if the requested resource is a directory, try to serve the index file
			if fs.IsDir() {
				// redirect if trailling "/"" is missing
				if !strings.HasSuffix(req.URL.Path, "/") {
					http.Redirect(w, req, req.URL.Path+"/", http.StatusFound)
					return
				}

				file = path.Join(file, opt.IndexFile)
				f, err = dir.Open(file)
				if err != nil {
					h.ServeHTTP(w, req)
					return
				}
				defer f.Close()
				fs, err = f.Stat()
				if err != nil || fs.IsDir() {
					h.ServeHTTP(w, req)
					return
				}
			}

			if !opt.SkipLogging {
				log.Println("[Static] Serving " + file)
			}

			if opt.DisableCaching {
				now := time.Now().UTC()
				expires := now.Add(-time.Second)
				w.Header().Set("Date", now.Format(http.TimeFormat))
				w.Header().Set("Expires", expires.Format(http.TimeFormat))
				w.Header().Set("Cache-Control", "no-cache")
			}

			// Add an Expires header to the static content
			if opt.Expires != nil {
				w.Header().Set("Expires", opt.Expires())
			}

			http.ServeContent(w, req, file, fs.ModTime(), f)
		}

		return http.HandlerFunc(fn)
	}
}
<|endoftext|>


<commit_before>package statsd

import (
	"net"
	"strconv"
	"time"
)

var Address = "localhost:8125"

// Cache the conn for perf.
var conn net.Conn

func Timer() timer {
	return timer{time.Now()}
}

// Timer
type timer struct {
	start time.Time
}

func (t *timer) Reset() {
	t.start = time.Now()
}

func (t *timer) Send(names ...interface{}) (took time.Duration) {
	took = time.Since(t.start)

	value := ":" + strconv.FormatUint(uint64(took.Nanoseconds()/1e6), 10) + "|ms"

	if err := getConnection(); err != nil {
		return
	}

	for _, name := range names {
		conn.Write([]byte(name.(string) + value))
	}

	return
}

// Inc is a simple counter adding one to a given metric.
func Inc(name string) {
	if err := getConnection(); err != nil {
		return
	}

    conn.Write([]byte(name + ":1|c"))

    return
}

func getConnection() (err error) {
	// If we don't have a conn, make one.
	if conn == nil {
		if conn, err = net.Dial("udp", Address); err != nil {
			conn = nil
			return
		}
	}

	return
}
<commit_msg>go fmt<commit_after>package statsd

import (
	"net"
	"strconv"
	"time"
)

var Address = "localhost:8125"

// Cache the conn for perf.
var conn net.Conn

func Timer() timer {
	return timer{time.Now()}
}

// Timer
type timer struct {
	start time.Time
}

func (t *timer) Reset() {
	t.start = time.Now()
}

func (t *timer) Send(names ...interface{}) (took time.Duration) {
	took = time.Since(t.start)

	value := ":" + strconv.FormatUint(uint64(took.Nanoseconds()/1e6), 10) + "|ms"

	if err := getConnection(); err != nil {
		return
	}

	for _, name := range names {
		conn.Write([]byte(name.(string) + value))
	}

	return
}

// Inc is a simple counter adding one to a given metric.
func Inc(name string) {
	if err := getConnection(); err != nil {
		return
	}

	conn.Write([]byte(name + ":1|c"))

	return
}

func getConnection() (err error) {
	// If we don't have a conn, make one.
	if conn == nil {
		if conn, err = net.Dial("udp", Address); err != nil {
			conn = nil
			return
		}
	}

	return
}
<|endoftext|>


<commit_before>package sse

// Status groups together a ready state and possible error associated
// with it. Useful to notify changes and why they happened.
type Status struct {
	ReadyState ReadyState
	Error      error
}
<commit_msg>status: implement error interface<commit_after>package sse

// Status groups together a ready state and possible error associated
// with it. Useful to notify changes and why they happened.
type Status struct {
	ReadyState ReadyState
	Err        error
}

func (s *Status) Error() string {
	return s.Err.Error()
}
<|endoftext|>


<commit_before>package metadata

import (
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/rancher/external-dns/config"
	"github.com/rancher/external-dns/utils"
	"github.com/rancher/go-rancher-metadata/metadata"
)

const (
	metadataUrl = "http://rancher-metadata/2015-12-19"
)

type MetadataClient struct {
	MetadataClient  metadata.Client
	EnvironmentName string
	EnvironmentUUID string
}

func getEnvironment(m metadata.Client) (string, string, error) {
	timeout := 30 * time.Second
	var err error
	var stack metadata.Stack
	for i := 1 * time.Second; i < timeout; i *= time.Duration(2) {
		stack, err = m.GetSelfStack()
		if err != nil {
			logrus.Errorf("Error reading stack info: %v...will retry", err)
			time.Sleep(i)
		} else {
			return stack.EnvironmentName, stack.EnvironmentUUID, nil
		}
	}
	return "", "", fmt.Errorf("Error reading stack info: %v", err)
}

func NewMetadataClient() (*MetadataClient, error) {
	m, err := metadata.NewClientAndWait(metadataUrl)
	if err != nil {
		logrus.Fatalf("Failed to configure rancher-metadata: %v", err)
	}

	envName, envUUID, err := getEnvironment(m)
	if err != nil {
		logrus.Fatalf("Error reading stack info: %v", err)
	}

	return &MetadataClient{
		MetadataClient:  m,
		EnvironmentName: envName,
		EnvironmentUUID: envUUID,
	}, nil
}

func (m *MetadataClient) GetVersion() (string, error) {
	return m.MetadataClient.GetVersion()
}

func (m *MetadataClient) GetMetadataDnsRecords() (map[string]utils.DnsRecord, error) {
	dnsEntries := make(map[string]utils.DnsRecord)
	err := m.getContainersDnsRecords(dnsEntries)
	if err != nil {
		return dnsEntries, err
	}
	return dnsEntries, nil
}

func (m *MetadataClient) getContainersDnsRecords(dnsEntries map[string]utils.DnsRecord) error {
	services, err := m.MetadataClient.GetServices()
	if err != nil {
		return err
	}

	ourFqdns := make(map[string]struct{})
	hostMeta := make(map[string]metadata.Host)
	for _, service := range services {

		// Check for Service Label: io.rancher.service.external_dns
		// Accepts 'always', 'auto' (default), or 'never'
		policy, ok := service.Labels["io.rancher.service.external_dns"]
		if !ok {
			policy = "auto"
		} else if policy == "never" {
			logrus.Debugf("Service %v is Disabled", service.Name)
			continue
		}

		if service.Kind != "service" && service.Kind != "loadBalancerService" {
			continue
		}

		for _, container := range service.Containers {

			if (len(container.Ports) == 0 && policy != "always") || !containerStateOK(container) {
				continue
			}

			hostUUID := container.HostUUID
			if len(hostUUID) == 0 {
				logrus.Debugf("Container's %v host_uuid is empty", container.Name)
				continue
			}

			var host metadata.Host
			if _, ok := hostMeta[hostUUID]; ok {
				host = hostMeta[hostUUID]
			} else {
				host, err = m.MetadataClient.GetHost(hostUUID)
				if err != nil {
					logrus.Warnf("Failed to get host metadata: %v", err)
					continue
				}
				hostMeta[hostUUID] = host
			}

			// Check for Host Label: io.rancher.host.external_dns
			// Accepts 'true' (default) or 'false'
			if label, ok := host.Labels["io.rancher.host.external_dns"]; ok {
				if label == "false" {
					logrus.Debugf("Container %v Host %s is Disabled", container.Name, host.Name)
					continue
				}
			}

			var externalIP string
			if ip, ok := host.Labels["io.rancher.host.external_dns_ip"]; ok && len(ip) > 0 {
				externalIP = ip
			} else if len(container.Ports) > 0 {
				if ip, ok := parsePortToIP(container.Ports[0]); ok {
					externalIP = ip
				}
			}

			// fallback to host agent IP
			if len(externalIP) == 0 {
				logrus.Debugf("Fallback to host.AgentIP %s for container %s", host.AgentIP, container.Name)
				externalIP = host.AgentIP
			}

			if net.ParseIP(externalIP) == nil {
				logrus.Errorf("Skipping container %s: Invalid IP address %s", container.Name, externalIP)
			}

			nameTemplate, ok := service.Labels["io.rancher.service.external_dns_name_template"]
			if !ok {
				nameTemplate = config.NameTemplate
			}

			fqdn := utils.FqdnFromTemplate(nameTemplate, container.ServiceName, container.StackName,
				m.EnvironmentName, config.RootDomainName)

			addToDnsEntries(fqdn, externalIP, dnsEntries)
			ourFqdns[fqdn] = struct{}{}
		}
	}

	if len(ourFqdns) > 0 {
		stateFqdn := utils.StateFqdn(m.EnvironmentUUID, config.RootDomainName)
		stateRec := utils.StateRecord(stateFqdn, config.TTL, ourFqdns)
		dnsEntries[stateFqdn] = stateRec
	}

	return nil
}

func addToDnsEntries(fqdn, ip string, dnsEntries map[string]utils.DnsRecord) {
	var records []string
	if _, ok := dnsEntries[fqdn]; !ok {
		records = []string{ip}
	} else {
		records = dnsEntries[fqdn].Records
		// skip if the records already have that IP
		for _, val := range records {
			if val == ip {
				return
			}
		}
		records = append(records, ip)
	}

	dnsEntries[fqdn] = utils.DnsRecord{fqdn, records, "A", config.TTL}
}

func containerStateOK(container metadata.Container) bool {
	switch container.State {
	case "running":
	default:
		return false
	}

	switch container.HealthState {
	case "healthy":
	case "updating-healthy":
	case "":
	default:
		return false
	}

	return true
}

// expects port string as 'ip:publicPort:privatePort'
// returns usable ip address
func parsePortToIP(port string) (string, bool) {
	parts := strings.Split(port, ":")
	if len(parts) == 3 {
		ip := parts[0]
		if len(ip) > 0 && ip != "0.0.0.0" {
			return ip, true
		}
	}

	return "", false
}
<commit_msg>Use fqdn for metadata service<commit_after>package metadata

import (
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/rancher/external-dns/config"
	"github.com/rancher/external-dns/utils"
	"github.com/rancher/go-rancher-metadata/metadata"
)

const (
	metadataUrl = "http://rancher-metadata.rancher.internal/2015-12-19"
)

type MetadataClient struct {
	MetadataClient  metadata.Client
	EnvironmentName string
	EnvironmentUUID string
}

func getEnvironment(m metadata.Client) (string, string, error) {
	timeout := 30 * time.Second
	var err error
	var stack metadata.Stack
	for i := 1 * time.Second; i < timeout; i *= time.Duration(2) {
		stack, err = m.GetSelfStack()
		if err != nil {
			logrus.Errorf("Error reading stack info: %v...will retry", err)
			time.Sleep(i)
		} else {
			return stack.EnvironmentName, stack.EnvironmentUUID, nil
		}
	}
	return "", "", fmt.Errorf("Error reading stack info: %v", err)
}

func NewMetadataClient() (*MetadataClient, error) {
	m, err := metadata.NewClientAndWait(metadataUrl)
	if err != nil {
		logrus.Fatalf("Failed to configure rancher-metadata: %v", err)
	}

	envName, envUUID, err := getEnvironment(m)
	if err != nil {
		logrus.Fatalf("Error reading stack info: %v", err)
	}

	return &MetadataClient{
		MetadataClient:  m,
		EnvironmentName: envName,
		EnvironmentUUID: envUUID,
	}, nil
}

func (m *MetadataClient) GetVersion() (string, error) {
	return m.MetadataClient.GetVersion()
}

func (m *MetadataClient) GetMetadataDnsRecords() (map[string]utils.DnsRecord, error) {
	dnsEntries := make(map[string]utils.DnsRecord)
	err := m.getContainersDnsRecords(dnsEntries)
	if err != nil {
		return dnsEntries, err
	}
	return dnsEntries, nil
}

func (m *MetadataClient) getContainersDnsRecords(dnsEntries map[string]utils.DnsRecord) error {
	services, err := m.MetadataClient.GetServices()
	if err != nil {
		return err
	}

	ourFqdns := make(map[string]struct{})
	hostMeta := make(map[string]metadata.Host)
	for _, service := range services {

		// Check for Service Label: io.rancher.service.external_dns
		// Accepts 'always', 'auto' (default), or 'never'
		policy, ok := service.Labels["io.rancher.service.external_dns"]
		if !ok {
			policy = "auto"
		} else if policy == "never" {
			logrus.Debugf("Service %v is Disabled", service.Name)
			continue
		}

		if service.Kind != "service" && service.Kind != "loadBalancerService" {
			continue
		}

		for _, container := range service.Containers {

			if (len(container.Ports) == 0 && policy != "always") || !containerStateOK(container) {
				continue
			}

			hostUUID := container.HostUUID
			if len(hostUUID) == 0 {
				logrus.Debugf("Container's %v host_uuid is empty", container.Name)
				continue
			}

			var host metadata.Host
			if _, ok := hostMeta[hostUUID]; ok {
				host = hostMeta[hostUUID]
			} else {
				host, err = m.MetadataClient.GetHost(hostUUID)
				if err != nil {
					logrus.Warnf("Failed to get host metadata: %v", err)
					continue
				}
				hostMeta[hostUUID] = host
			}

			// Check for Host Label: io.rancher.host.external_dns
			// Accepts 'true' (default) or 'false'
			if label, ok := host.Labels["io.rancher.host.external_dns"]; ok {
				if label == "false" {
					logrus.Debugf("Container %v Host %s is Disabled", container.Name, host.Name)
					continue
				}
			}

			var externalIP string
			if ip, ok := host.Labels["io.rancher.host.external_dns_ip"]; ok && len(ip) > 0 {
				externalIP = ip
			} else if len(container.Ports) > 0 {
				if ip, ok := parsePortToIP(container.Ports[0]); ok {
					externalIP = ip
				}
			}

			// fallback to host agent IP
			if len(externalIP) == 0 {
				logrus.Debugf("Fallback to host.AgentIP %s for container %s", host.AgentIP, container.Name)
				externalIP = host.AgentIP
			}

			if net.ParseIP(externalIP) == nil {
				logrus.Errorf("Skipping container %s: Invalid IP address %s", container.Name, externalIP)
			}

			nameTemplate, ok := service.Labels["io.rancher.service.external_dns_name_template"]
			if !ok {
				nameTemplate = config.NameTemplate
			}

			fqdn := utils.FqdnFromTemplate(nameTemplate, container.ServiceName, container.StackName,
				m.EnvironmentName, config.RootDomainName)

			addToDnsEntries(fqdn, externalIP, dnsEntries)
			ourFqdns[fqdn] = struct{}{}
		}
	}

	if len(ourFqdns) > 0 {
		stateFqdn := utils.StateFqdn(m.EnvironmentUUID, config.RootDomainName)
		stateRec := utils.StateRecord(stateFqdn, config.TTL, ourFqdns)
		dnsEntries[stateFqdn] = stateRec
	}

	return nil
}

func addToDnsEntries(fqdn, ip string, dnsEntries map[string]utils.DnsRecord) {
	var records []string
	if _, ok := dnsEntries[fqdn]; !ok {
		records = []string{ip}
	} else {
		records = dnsEntries[fqdn].Records
		// skip if the records already have that IP
		for _, val := range records {
			if val == ip {
				return
			}
		}
		records = append(records, ip)
	}

	dnsEntries[fqdn] = utils.DnsRecord{fqdn, records, "A", config.TTL}
}

func containerStateOK(container metadata.Container) bool {
	switch container.State {
	case "running":
	default:
		return false
	}

	switch container.HealthState {
	case "healthy":
	case "updating-healthy":
	case "":
	default:
		return false
	}

	return true
}

// expects port string as 'ip:publicPort:privatePort'
// returns usable ip address
func parsePortToIP(port string) (string, bool) {
	parts := strings.Split(port, ":")
	if len(parts) == 3 {
		ip := parts[0]
		if len(ip) > 0 && ip != "0.0.0.0" {
			return ip, true
		}
	}

	return "", false
}
<|endoftext|>


<commit_before>package metadata

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"time"

	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
)

var logger = logging.GetLogger("metadata")

// Generator generates metadata
type Generator struct {
	Name         string
	Config       *config.MetadataPlugin
	Tempfile     string
	PrevMetadata interface{}
}

// Fetch invokes the command and returns the result
func (g *Generator) Fetch() (interface{}, error) {
	message, stderr, exitCode, err := g.Config.Run()

	if err != nil {
		logger.Warningf("Error occurred while executing a metadata plugin %q: %v", g.Name, err)
		return nil, err
	}

	if stderr != "" {
		logger.Warningf("metadata plugin %q outputs stderr: %s", g.Name, stderr)
		// metadata plugin can output message to stderr for debugging and json to stdout
	}

	if exitCode != 0 {
		return nil, fmt.Errorf("exits with: %d", exitCode)
	}

	var metadata interface{}
	if err := json.Unmarshal([]byte(message), &metadata); err != nil {
		return nil, fmt.Errorf("outputs invalid JSON: %v", message)
	}

	return metadata, nil
}

// IsChanged returns whether the metadata has been changed or not
func (g *Generator) IsChanged(metadata interface{}) bool {
	if g.PrevMetadata == nil {
		g.LoadFromFile()
	}
	return !reflect.DeepEqual(g.PrevMetadata, metadata)
}

// LoadFromFile loads the previous metadata from file
func (g *Generator) LoadFromFile() {
	data, err := ioutil.ReadFile(g.Tempfile)
	if err != nil { // maybe initial state
		return
	}
	var metadata interface{}
	if err := json.Unmarshal(data, &metadata); err != nil {
		logger.Warningf("metadata plugin %q detected a invalid json in temporary file: %s", g.Name, string(data))
		// ignore errors, the file will be overwritten by Save()
		return
	}
	g.PrevMetadata = metadata
}

// Save stores the metadata locally
func (g *Generator) Save(metadata interface{}) error {
	g.PrevMetadata = metadata
	data, err := json.Marshal(metadata)
	if err != nil {
		return fmt.Errorf("failed to marshal the metadata to json: %v", err)
	}
	if g.Tempfile == "" {
		return fmt.Errorf("specify the name of temporary file")
	}
	if err := os.MkdirAll(filepath.Dir(g.Tempfile), 0755); err != nil {
		return err
	}
	if err := writeFileAtomically(g.Tempfile, data); err != nil {
		return fmt.Errorf("failed to write the metadata to temporary file: %v", err)
	}
	return nil
}

// Clear destroys the metadata cache
func (g *Generator) Clear() error {
	g.PrevMetadata = nil
	return os.Remove(g.Tempfile)
}

// writeFileAtomically writes contents to the file atomically
func writeFileAtomically(f string, contents []byte) error {
	tmpf, err := ioutil.TempFile("", "")
	if err != nil {
		return err
	}
	// os.Remove here works successfully when tmpf.Write fails or os.Rename fails.
	// In successful case, os.Remove fails because the temporary file is already renamed.
	defer os.Remove(tmpf.Name())
	_, err = tmpf.Write(contents)
	tmpf.Close() // should be called before rename
	if err != nil {
		return err
	}
	return os.Rename(tmpf.Name(), f)
}

const defaultExecutionInterval = 10 * time.Minute

// Interval calculates the time interval of command execution
func (g *Generator) Interval() time.Duration {
	if g.Config.ExecutionInterval == nil {
		return defaultExecutionInterval
	}
	interval := time.Duration(*g.Config.ExecutionInterval) * time.Minute
	if interval < defaultExecutionInterval {
		return defaultExecutionInterval
	}
	return interval
}
<commit_msg>write the temporary file for atomic write in the same directory of the target file<commit_after>package metadata

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"time"

	"github.com/mackerelio/mackerel-agent/config"
	"github.com/mackerelio/mackerel-agent/logging"
)

var logger = logging.GetLogger("metadata")

// Generator generates metadata
type Generator struct {
	Name         string
	Config       *config.MetadataPlugin
	Tempfile     string
	PrevMetadata interface{}
}

// Fetch invokes the command and returns the result
func (g *Generator) Fetch() (interface{}, error) {
	message, stderr, exitCode, err := g.Config.Run()

	if err != nil {
		logger.Warningf("Error occurred while executing a metadata plugin %q: %v", g.Name, err)
		return nil, err
	}

	if stderr != "" {
		logger.Warningf("metadata plugin %q outputs stderr: %s", g.Name, stderr)
		// metadata plugin can output message to stderr for debugging and json to stdout
	}

	if exitCode != 0 {
		return nil, fmt.Errorf("exits with: %d", exitCode)
	}

	var metadata interface{}
	if err := json.Unmarshal([]byte(message), &metadata); err != nil {
		return nil, fmt.Errorf("outputs invalid JSON: %v", message)
	}

	return metadata, nil
}

// IsChanged returns whether the metadata has been changed or not
func (g *Generator) IsChanged(metadata interface{}) bool {
	if g.PrevMetadata == nil {
		g.LoadFromFile()
	}
	return !reflect.DeepEqual(g.PrevMetadata, metadata)
}

// LoadFromFile loads the previous metadata from file
func (g *Generator) LoadFromFile() {
	data, err := ioutil.ReadFile(g.Tempfile)
	if err != nil { // maybe initial state
		return
	}
	var metadata interface{}
	if err := json.Unmarshal(data, &metadata); err != nil {
		logger.Warningf("metadata plugin %q detected a invalid json in temporary file: %s", g.Name, string(data))
		// ignore errors, the file will be overwritten by Save()
		return
	}
	g.PrevMetadata = metadata
}

// Save stores the metadata locally
func (g *Generator) Save(metadata interface{}) error {
	g.PrevMetadata = metadata
	data, err := json.Marshal(metadata)
	if err != nil {
		return fmt.Errorf("failed to marshal the metadata to json: %v", err)
	}
	if g.Tempfile == "" {
		return fmt.Errorf("specify the name of temporary file")
	}
	if err := os.MkdirAll(filepath.Dir(g.Tempfile), 0755); err != nil {
		return err
	}
	if err := writeFileAtomically(g.Tempfile, data); err != nil {
		return fmt.Errorf("failed to write the metadata to temporary file: %v", err)
	}
	return nil
}

// Clear destroys the metadata cache
func (g *Generator) Clear() error {
	g.PrevMetadata = nil
	return os.Remove(g.Tempfile)
}

// writeFileAtomically writes contents to the file atomically
func writeFileAtomically(f string, contents []byte) error {
	// MUST be located on same disk partition
	tmpf, err := ioutil.TempFile(filepath.Dir(f), "tmp")
	if err != nil {
		return err
	}
	// os.Remove here works successfully when tmpf.Write fails or os.Rename fails.
	// In successful case, os.Remove fails because the temporary file is already renamed.
	defer os.Remove(tmpf.Name())
	_, err = tmpf.Write(contents)
	tmpf.Close() // should be called before rename
	if err != nil {
		return err
	}
	return os.Rename(tmpf.Name(), f)
}

const defaultExecutionInterval = 10 * time.Minute

// Interval calculates the time interval of command execution
func (g *Generator) Interval() time.Duration {
	if g.Config.ExecutionInterval == nil {
		return defaultExecutionInterval
	}
	interval := time.Duration(*g.Config.ExecutionInterval) * time.Minute
	if interval < defaultExecutionInterval {
		return defaultExecutionInterval
	}
	return interval
}
<|endoftext|>


<commit_before>package dns

import (
	"crypto/ecdsa"
	"crypto/rsa"
	"io"
	"math/big"
	"strings"
)

func (k *RR_DNSKEY) NewPrivateKey(s string) (PrivateKey, error) {
	if s[len(s)-1] != '\n' { // We need a closing newline
		return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
	}
	return k.ReadPrivateKey(strings.NewReader(s), "")
}

// NewPrivateKey reads a private key from the io.Reader q. The public key must be
// known, because some cryptographics algorithms embed the public inside the privatekey.
func (k *RR_DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) {
	m, e := parseKey(q, file)
	if m == nil {
		return nil, e
	}
	if _, ok := m["private-key-format"]; !ok {
		return nil, ErrPrivKey
	}
	if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
		return nil, ErrPrivKey
	}
	// TODO(mg): check if the pubkey matches the private key
	switch m["algorithm"] {
	case "1 (RSAMD5)":
		fallthrough
	case "5 (RSASHA1)":
		fallthrough
	case "8 (RSASHA256)":
		fallthrough
	case "10 (RSASHA512)":
		fallthrough
	case "7 (RSASHA1NSEC3SHA1)":
		p, e := readPrivateKeyRSA(m)
		if e != nil {
			if !k.setPublicKeyInPrivate(p) {
				return nil, ErrPrivKey
			}
		}
		return p, e
	case "13 (ECDSAP256SHA256)":
		fallthrough
	case "14 (ECDSAP384SHA384)":
		p, e := readPrivateKeyECDSA(m)
		if e != nil {
			if !k.setPublicKeyInPrivate(p) {
				return nil, ErrPrivKey
			}
		}
		return p, e
	}
	return nil, ErrPrivKey
}

// Read a private key (file) string and create a public key. Return the private key.
func readPrivateKeyRSA(m map[string]string) (PrivateKey, error) {
	p := new(rsa.PrivateKey)
	p.Primes = []*big.Int{nil, nil}
	for k, v := range m {
		switch k {
		case "modulus", "publicexponent", "privateexponent", "prime1", "prime2":
			v1, err := packBase64([]byte(v))
			if err != nil {
				return nil, err
			}
			switch k {
			case "modulus":
				p.PublicKey.N = big.NewInt(0)
				p.PublicKey.N.SetBytes(v1)
			case "publicexponent":
				i := big.NewInt(0)
				i.SetBytes(v1)
				p.PublicKey.E = int(i.Int64()) // int64 should be large enough
			case "privateexponent":
				p.D = big.NewInt(0)
				p.D.SetBytes(v1)
			case "prime1":
				p.Primes[0] = big.NewInt(0)
				p.Primes[0].SetBytes(v1)
			case "prime2":
				p.Primes[1] = big.NewInt(0)
				p.Primes[1].SetBytes(v1)
			}
		case "exponent1", "exponent2", "coefficient":
			// not used in Go (yet)
		case "created", "publish", "activate":
			// not used in Go (yet)
		}
	}
	return p, nil
}

func readPrivateKeyECDSA(m map[string]string) (PrivateKey, error) {
	p := new(ecdsa.PrivateKey)
	p.D = big.NewInt(0)
	// Need to check if we have everything
	for k, v := range m {
		switch k {
		case "privatekey:":
			v1, err := packBase64([]byte(v))
			if err != nil {
				return nil, err
			}
			p.D.SetBytes(v1)
		case "created:", "publish:", "activate:":
			/* not used in Go (yet) */
		}
	}
	return p, nil
}

// parseKey reads a private key from r. It returns a map[string]string,
// with the key-value pairs, or an error when the file is not correct.
func parseKey(r io.Reader, file string) (map[string]string, error) {
	s := scanInit(r)
	m := make(map[string]string)
	c := make(chan lex)
	k := ""
	// Start the lexer
	go klexer(s, c)
	for l := range c {
		// It should alternate
		switch l.value {
		case _KEY:
			k = l.token
		case _VALUE:
			if k == "" {
				return nil, &ParseError{file, "no private key seen", l}
			}
			//println("Setting", strings.ToLower(k), "to", l.token, "b")
			m[strings.ToLower(k)] = l.token
			k = ""
		}
	}
	return m, nil
}

// klexer scans the sourcefile and returns tokens on the channel c.
func klexer(s *scan, c chan lex) {
	var l lex
	str := "" // Hold the current read text
	commt := false
	key := true
	x, err := s.tokenText()
	defer close(c)
	for err == nil {
		l.column = s.position.Column
		l.line = s.position.Line
		switch x {
		case ':':
			if commt {
				break
			}
			l.token = str
			if key {
				l.value = _KEY
				c <- l
				// Next token is a space, eat it
				s.tokenText()
				key = false
				str = ""
			} else {
				l.value = _VALUE
			}
		case ';':
			commt = true
		case '\n':
			if commt {
				// Reset a comment
				commt = false
			}
			l.value = _VALUE
			l.token = str
			c <- l
			str = ""
			commt = false
			key = true
		default:
			if commt {
				break
			}
			str += string(x)
		}
		x, err = s.tokenText()
	}
	if len(str) > 0 {
		// Send remainder
		l.token = str
		l.value = _VALUE
		c <- l
	}
}
<commit_msg>Fix crash<commit_after>package dns

import (
	"crypto/ecdsa"
	"crypto/rsa"
	"io"
	"math/big"
	"strings"
)

func (k *RR_DNSKEY) NewPrivateKey(s string) (PrivateKey, error) {
	if s[len(s)-1] != '\n' { // We need a closing newline
		return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
	}
	return k.ReadPrivateKey(strings.NewReader(s), "")
}

// NewPrivateKey reads a private key from the io.Reader q. The public key must be
// known, because some cryptographics algorithms embed the public inside the privatekey.
func (k *RR_DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) {
	m, e := parseKey(q, file)
	if m == nil {
		return nil, e
	}
	if _, ok := m["private-key-format"]; !ok {
		return nil, ErrPrivKey
	}
	if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
		return nil, ErrPrivKey
	}
	// TODO(mg): check if the pubkey matches the private key
	switch m["algorithm"] {
	case "1 (RSAMD5)":
		fallthrough
	case "5 (RSASHA1)":
		fallthrough
	case "8 (RSASHA256)":
		fallthrough
	case "10 (RSASHA512)":
		fallthrough
	case "7 (RSASHA1NSEC3SHA1)":
		p, e := readPrivateKeyRSA(m)
		if e != nil {
			return nil, e
		}
		if !k.setPublicKeyInPrivate(p) {
			return nil, ErrPrivKey
		}
		return p, e
	case "13 (ECDSAP256SHA256)":
		fallthrough
	case "14 (ECDSAP384SHA384)":
		p, e := readPrivateKeyECDSA(m)
		if e != nil {
			return nil, e
		}
		if !k.setPublicKeyInPrivate(p) {
			return nil, ErrPrivKey
		}
		return p, e
	}
	return nil, ErrPrivKey
}

// Read a private key (file) string and create a public key. Return the private key.
func readPrivateKeyRSA(m map[string]string) (PrivateKey, error) {
	p := new(rsa.PrivateKey)
	p.Primes = []*big.Int{nil, nil}
	for k, v := range m {
		switch k {
		case "modulus", "publicexponent", "privateexponent", "prime1", "prime2":
			v1, err := packBase64([]byte(v))
			if err != nil {
				return nil, err
			}
			switch k {
			case "modulus":
				p.PublicKey.N = big.NewInt(0)
				p.PublicKey.N.SetBytes(v1)
			case "publicexponent":
				i := big.NewInt(0)
				i.SetBytes(v1)
				p.PublicKey.E = int(i.Int64()) // int64 should be large enough
			case "privateexponent":
				p.D = big.NewInt(0)
				p.D.SetBytes(v1)
			case "prime1":
				p.Primes[0] = big.NewInt(0)
				p.Primes[0].SetBytes(v1)
			case "prime2":
				p.Primes[1] = big.NewInt(0)
				p.Primes[1].SetBytes(v1)
			}
		case "exponent1", "exponent2", "coefficient":
			// not used in Go (yet)
		case "created", "publish", "activate":
			// not used in Go (yet)
		}
	}
	return p, nil
}

func readPrivateKeyECDSA(m map[string]string) (PrivateKey, error) {
	p := new(ecdsa.PrivateKey)
	p.D = big.NewInt(0)
	// Need to check if we have everything
	for k, v := range m {
		switch k {
		case "privatekey:":
			v1, err := packBase64([]byte(v))
			if err != nil {
				return nil, err
			}
			p.D.SetBytes(v1)
		case "created:", "publish:", "activate:":
			/* not used in Go (yet) */
		}
	}
	return p, nil
}

// parseKey reads a private key from r. It returns a map[string]string,
// with the key-value pairs, or an error when the file is not correct.
func parseKey(r io.Reader, file string) (map[string]string, error) {
	s := scanInit(r)
	m := make(map[string]string)
	c := make(chan lex)
	k := ""
	// Start the lexer
	go klexer(s, c)
	for l := range c {
		// It should alternate
		switch l.value {
		case _KEY:
			k = l.token
		case _VALUE:
			if k == "" {
				return nil, &ParseError{file, "no private key seen", l}
			}
			//println("Setting", strings.ToLower(k), "to", l.token, "b")
			m[strings.ToLower(k)] = l.token
			k = ""
		}
	}
	return m, nil
}

// klexer scans the sourcefile and returns tokens on the channel c.
func klexer(s *scan, c chan lex) {
	var l lex
	str := "" // Hold the current read text
	commt := false
	key := true
	x, err := s.tokenText()
	defer close(c)
	for err == nil {
		l.column = s.position.Column
		l.line = s.position.Line
		switch x {
		case ':':
			if commt {
				break
			}
			l.token = str
			if key {
				l.value = _KEY
				c <- l
				// Next token is a space, eat it
				s.tokenText()
				key = false
				str = ""
			} else {
				l.value = _VALUE
			}
		case ';':
			commt = true
		case '\n':
			if commt {
				// Reset a comment
				commt = false
			}
			l.value = _VALUE
			l.token = str
			c <- l
			str = ""
			commt = false
			key = true
		default:
			if commt {
				break
			}
			str += string(x)
		}
		x, err = s.tokenText()
	}
	if len(str) > 0 {
		// Send remainder
		l.token = str
		l.value = _VALUE
		c <- l
	}
}
<|endoftext|>