<commit_before>/*
Copyright 2018 The Skaffold Authors

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 runner

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/bazel"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/gcb"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/kaniko"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/local"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
	kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/watch"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const PollInterval = 1000 * time.Millisecond

// ErrorConfigurationChanged is a special error that's returned when the skaffold configuration was changed.
var ErrorConfigurationChanged = errors.New("configuration changed")

// SkaffoldRunner is responsible for running the skaffold build and deploy pipeline.
type SkaffoldRunner struct {
	build.Builder
	deploy.Deployer
	tag.Tagger

	opts         *config.SkaffoldOptions
	watchFactory watch.Factory
	builds       []build.Artifact
}

// NewForConfig returns a new SkaffoldRunner for a SkaffoldConfig
func NewForConfig(opts *config.SkaffoldOptions, cfg *config.SkaffoldConfig) (*SkaffoldRunner, error) {
	kubeContext, err := kubectx.CurrentContext()
	if err != nil {
		return nil, errors.Wrap(err, "getting current cluster context")
	}
	logrus.Infof("Using kubectl context: %s", kubeContext)

	tagger, err := getTagger(cfg.Build.TagPolicy, opts.CustomTag)
	if err != nil {
		return nil, errors.Wrap(err, "parsing skaffold tag config")
	}

	builder, err := getBuilder(&cfg.Build, kubeContext)
	if err != nil {
		return nil, errors.Wrap(err, "parsing skaffold build config")
	}

	deployer, err := getDeployer(&cfg.Deploy, kubeContext, opts.Namespace)
	if err != nil {
		return nil, errors.Wrap(err, "parsing skaffold deploy config")
	}

	deployer = deploy.WithLabels(deployer, opts, builder, deployer, tagger)
	builder, deployer = WithTimings(builder, deployer)
	if opts.Notification {
		deployer = WithNotification(deployer)
	}

	return &SkaffoldRunner{
		Builder:      builder,
		Deployer:     deployer,
		Tagger:       tagger,
		opts:         opts,
		watchFactory: watch.NewWatcher,
	}, nil
}

func getBuilder(cfg *v1alpha2.BuildConfig, kubeContext string) (build.Builder, error) {
	switch {
	case cfg.LocalBuild != nil:
		logrus.Debugf("Using builder: local")
		return local.NewBuilder(cfg.LocalBuild, kubeContext)

	case cfg.GoogleCloudBuild != nil:
		logrus.Debugf("Using builder: google cloud")
		return gcb.NewBuilder(cfg.GoogleCloudBuild), nil

	case cfg.KanikoBuild != nil:
		logrus.Debugf("Using builder: kaniko")
		return kaniko.NewBuilder(cfg.KanikoBuild), nil

	default:
		return nil, fmt.Errorf("Unknown builder for config %+v", cfg)
	}
}

func getDeployer(cfg *v1alpha2.DeployConfig, kubeContext string, namespace string) (deploy.Deployer, error) {
	switch {
	case cfg.KubectlDeploy != nil:
		// TODO(dgageot): this should be the folder containing skaffold.yaml. Should also be moved elsewhere.
		cwd, err := os.Getwd()
		if err != nil {
			return nil, errors.Wrap(err, "finding current directory")
		}
		return deploy.NewKubectlDeployer(cwd, cfg.KubectlDeploy, kubeContext, namespace), nil

	case cfg.HelmDeploy != nil:
		return deploy.NewHelmDeployer(cfg.HelmDeploy, kubeContext, namespace), nil

	case cfg.KustomizeDeploy != nil:
		return deploy.NewKustomizeDeployer(cfg.KustomizeDeploy, kubeContext, namespace), nil

	default:
		return nil, fmt.Errorf("Unknown deployer for config %+v", cfg)
	}
}

func getTagger(t v1alpha2.TagPolicy, customTag string) (tag.Tagger, error) {
	switch {
	case customTag != "":
		return &tag.CustomTag{
			Tag: customTag,
		}, nil

	case t.EnvTemplateTagger != nil:
		return tag.NewEnvTemplateTagger(t.EnvTemplateTagger.Template)

	case t.ShaTagger != nil:
		return &tag.ChecksumTagger{}, nil

	case t.GitTagger != nil:
		return &tag.GitCommit{}, nil

	case t.DateTimeTagger != nil:
		return tag.NewDateTimeTagger(t.DateTimeTagger.Format, t.DateTimeTagger.TimeZone), nil

	default:
		return nil, fmt.Errorf("Unknown tagger for strategy %+v", t)
	}
}

// Run builds artifacts ad then deploys them.
func (r *SkaffoldRunner) Run(ctx context.Context, out io.Writer, artifacts []*v1alpha2.Artifact) error {
	bRes, err := r.Build(ctx, out, r.Tagger, artifacts)
	if err != nil {
		return errors.Wrap(err, "build step")
	}

	_, err = r.Deploy(ctx, out, bRes)
	if err != nil {
		return errors.Wrap(err, "deploy step")
	}

	return r.TailLogs(ctx, out, artifacts, bRes)
}

// TailLogs prints the logs for deployed artifacts.
func (r *SkaffoldRunner) TailLogs(ctx context.Context, out io.Writer, artifacts []*v1alpha2.Artifact, bRes []build.Artifact) error {
	if !r.opts.Tail {
		return nil
	}

	imageList := kubernetes.NewImageList()
	for _, b := range bRes {
		imageList.Add(b.Tag)
	}

	colorPicker := kubernetes.NewColorPicker(artifacts)
	logger := kubernetes.NewLogAggregator(out, imageList, colorPicker)
	if err := logger.Start(ctx); err != nil {
		return errors.Wrap(err, "starting logger")
	}

	<-ctx.Done()
	return nil
}

// Dev watches for changes and runs the skaffold build and deploy
// pipeline until interrrupted by the user.
func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*v1alpha2.Artifact) ([]build.Artifact, error) {
	imageList := kubernetes.NewImageList()
	colorPicker := kubernetes.NewColorPicker(artifacts)
	logger := kubernetes.NewLogAggregator(out, imageList, colorPicker)

	// Create watcher and register artifacts to build current state of files.
	changed := changes{}
	onChange := func() error {
		logger.Mute()

		var err error

		switch {
		case changed.needsReload:
			err = ErrorConfigurationChanged
		case len(changed.dirtyArtifacts) > 0:
			err = r.buildAndDeploy(ctx, out, changed.dirtyArtifacts, imageList)
		case changed.needsRedeploy:
			if _, err := r.Deploy(ctx, out, r.builds); err != nil {
				logrus.Warnln("Skipping Deploy due to error:", err)
			}
		}

		color.Default.Fprintln(out, "Watching for changes...")
		changed.reset()
		logger.Unmute()

		return err
	}

	watcher := r.watchFactory()

	// Watch artifacts
	for i := range artifacts {
		artifact := artifacts[i]

		if err := watcher.Register(
			func() ([]string, error) { return dependenciesForArtifact(artifact) },
			func() { changed.Add(artifact) },
		); err != nil {
			return nil, errors.Wrapf(err, "watching files for artifact %s", artifact.ImageName)
		}
	}

	// Watch deployment configuration
	if err := watcher.Register(
		func() ([]string, error) { return r.Dependencies() },
		func() { changed.needsRedeploy = true },
	); err != nil {
		return nil, errors.Wrap(err, "watching files for deployer")
	}

	// Watch Skaffold configuration
	if err := watcher.Register(
		func() ([]string, error) { return []string{r.opts.ConfigurationFile}, nil },
		func() { changed.needsReload = true },
	); err != nil {
		return nil, errors.Wrapf(err, "watching skaffold configuration %s", r.opts.ConfigurationFile)
	}

	// First run
	if err := r.buildAndDeploy(ctx, out, artifacts, imageList); err != nil {
		return nil, errors.Wrap(err, "first run")
	}

	// Start logs
	if err := logger.Start(ctx); err != nil {
		return nil, errors.Wrap(err, "starting logger")
	}

	return nil, watcher.Run(ctx, PollInterval, onChange)
}

// buildAndDeploy builds a subset of the artifacts and deploys everything.
func (r *SkaffoldRunner) buildAndDeploy(ctx context.Context, out io.Writer, artifacts []*v1alpha2.Artifact, images *kubernetes.ImageList) error {
	firstRun := r.builds == nil

	bRes, err := r.Build(ctx, out, r.Tagger, artifacts)
	if err != nil {
		if firstRun {
			return errors.Wrap(err, "exiting dev mode because the first build failed")
		}

		logrus.Warnln("Skipping Deploy due to build error:", err)
		return nil
	}

	// Update which images are logged.
	for _, build := range bRes {
		images.Add(build.Tag)
	}

	// Make sure all artifacts are redeployed. Not only those that were just rebuilt.
	r.builds = mergeWithPreviousBuilds(bRes, r.builds)

	_, err = r.Deploy(ctx, out, r.builds)
	if err != nil {
		if firstRun {
			return errors.Wrap(err, "exiting dev mode because the first deploy failed")
		}

		logrus.Warnln("Skipping Deploy due to error:", err)
		return nil
	}

	return nil
}

func mergeWithPreviousBuilds(builds, previous []build.Artifact) []build.Artifact {
	updatedBuilds := map[string]bool{}
	for _, build := range builds {
		updatedBuilds[build.ImageName] = true
	}

	var merged []build.Artifact
	merged = append(merged, builds...)

	for _, b := range previous {
		if !updatedBuilds[b.ImageName] {
			merged = append(merged, b)
		}
	}

	return merged
}

func dependenciesForArtifact(a *v1alpha2.Artifact) ([]string, error) {
	var (
		paths []string
		err   error
	)

	switch {
	case a.DockerArtifact != nil:
		paths, err = docker.GetDependencies(a.Workspace, a.DockerArtifact)

	case a.BazelArtifact != nil:
		paths, err = bazel.GetDependencies(a.Workspace, a.BazelArtifact)

	default:
		return nil, fmt.Errorf("undefined artifact type: %+v", a.ArtifactType)
	}

	if err != nil {
		return nil, err
	}

	var p []string
	for _, path := range paths {
		p = append(p, filepath.Join(a.Workspace, path))
	}
	return p, nil
}
<commit_msg>Fix typo<commit_after>/*
Copyright 2018 The Skaffold Authors

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 runner

import (
	"context"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/bazel"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/gcb"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/kaniko"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/local"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
	kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/watch"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const PollInterval = 1000 * time.Millisecond

// ErrorConfigurationChanged is a special error that's returned when the skaffold configuration was changed.
var ErrorConfigurationChanged = errors.New("configuration changed")

// SkaffoldRunner is responsible for running the skaffold build and deploy pipeline.
type SkaffoldRunner struct {
	build.Builder
	deploy.Deployer
	tag.Tagger

	opts         *config.SkaffoldOptions
	watchFactory watch.Factory
	builds       []build.Artifact
}

// NewForConfig returns a new SkaffoldRunner for a SkaffoldConfig
func NewForConfig(opts *config.SkaffoldOptions, cfg *config.SkaffoldConfig) (*SkaffoldRunner, error) {
	kubeContext, err := kubectx.CurrentContext()
	if err != nil {
		return nil, errors.Wrap(err, "getting current cluster context")
	}
	logrus.Infof("Using kubectl context: %s", kubeContext)

	tagger, err := getTagger(cfg.Build.TagPolicy, opts.CustomTag)
	if err != nil {
		return nil, errors.Wrap(err, "parsing skaffold tag config")
	}

	builder, err := getBuilder(&cfg.Build, kubeContext)
	if err != nil {
		return nil, errors.Wrap(err, "parsing skaffold build config")
	}

	deployer, err := getDeployer(&cfg.Deploy, kubeContext, opts.Namespace)
	if err != nil {
		return nil, errors.Wrap(err, "parsing skaffold deploy config")
	}

	deployer = deploy.WithLabels(deployer, opts, builder, deployer, tagger)
	builder, deployer = WithTimings(builder, deployer)
	if opts.Notification {
		deployer = WithNotification(deployer)
	}

	return &SkaffoldRunner{
		Builder:      builder,
		Deployer:     deployer,
		Tagger:       tagger,
		opts:         opts,
		watchFactory: watch.NewWatcher,
	}, nil
}

func getBuilder(cfg *v1alpha2.BuildConfig, kubeContext string) (build.Builder, error) {
	switch {
	case cfg.LocalBuild != nil:
		logrus.Debugf("Using builder: local")
		return local.NewBuilder(cfg.LocalBuild, kubeContext)

	case cfg.GoogleCloudBuild != nil:
		logrus.Debugf("Using builder: google cloud")
		return gcb.NewBuilder(cfg.GoogleCloudBuild), nil

	case cfg.KanikoBuild != nil:
		logrus.Debugf("Using builder: kaniko")
		return kaniko.NewBuilder(cfg.KanikoBuild), nil

	default:
		return nil, fmt.Errorf("Unknown builder for config %+v", cfg)
	}
}

func getDeployer(cfg *v1alpha2.DeployConfig, kubeContext string, namespace string) (deploy.Deployer, error) {
	switch {
	case cfg.KubectlDeploy != nil:
		// TODO(dgageot): this should be the folder containing skaffold.yaml. Should also be moved elsewhere.
		cwd, err := os.Getwd()
		if err != nil {
			return nil, errors.Wrap(err, "finding current directory")
		}
		return deploy.NewKubectlDeployer(cwd, cfg.KubectlDeploy, kubeContext, namespace), nil

	case cfg.HelmDeploy != nil:
		return deploy.NewHelmDeployer(cfg.HelmDeploy, kubeContext, namespace), nil

	case cfg.KustomizeDeploy != nil:
		return deploy.NewKustomizeDeployer(cfg.KustomizeDeploy, kubeContext, namespace), nil

	default:
		return nil, fmt.Errorf("Unknown deployer for config %+v", cfg)
	}
}

func getTagger(t v1alpha2.TagPolicy, customTag string) (tag.Tagger, error) {
	switch {
	case customTag != "":
		return &tag.CustomTag{
			Tag: customTag,
		}, nil

	case t.EnvTemplateTagger != nil:
		return tag.NewEnvTemplateTagger(t.EnvTemplateTagger.Template)

	case t.ShaTagger != nil:
		return &tag.ChecksumTagger{}, nil

	case t.GitTagger != nil:
		return &tag.GitCommit{}, nil

	case t.DateTimeTagger != nil:
		return tag.NewDateTimeTagger(t.DateTimeTagger.Format, t.DateTimeTagger.TimeZone), nil

	default:
		return nil, fmt.Errorf("Unknown tagger for strategy %+v", t)
	}
}

// Run builds artifacts and then deploys them.
func (r *SkaffoldRunner) Run(ctx context.Context, out io.Writer, artifacts []*v1alpha2.Artifact) error {
	bRes, err := r.Build(ctx, out, r.Tagger, artifacts)
	if err != nil {
		return errors.Wrap(err, "build step")
	}

	_, err = r.Deploy(ctx, out, bRes)
	if err != nil {
		return errors.Wrap(err, "deploy step")
	}

	return r.TailLogs(ctx, out, artifacts, bRes)
}

// TailLogs prints the logs for deployed artifacts.
func (r *SkaffoldRunner) TailLogs(ctx context.Context, out io.Writer, artifacts []*v1alpha2.Artifact, bRes []build.Artifact) error {
	if !r.opts.Tail {
		return nil
	}

	imageList := kubernetes.NewImageList()
	for _, b := range bRes {
		imageList.Add(b.Tag)
	}

	colorPicker := kubernetes.NewColorPicker(artifacts)
	logger := kubernetes.NewLogAggregator(out, imageList, colorPicker)
	if err := logger.Start(ctx); err != nil {
		return errors.Wrap(err, "starting logger")
	}

	<-ctx.Done()
	return nil
}

// Dev watches for changes and runs the skaffold build and deploy
// pipeline until interrrupted by the user.
func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*v1alpha2.Artifact) ([]build.Artifact, error) {
	imageList := kubernetes.NewImageList()
	colorPicker := kubernetes.NewColorPicker(artifacts)
	logger := kubernetes.NewLogAggregator(out, imageList, colorPicker)

	// Create watcher and register artifacts to build current state of files.
	changed := changes{}
	onChange := func() error {
		logger.Mute()

		var err error

		switch {
		case changed.needsReload:
			err = ErrorConfigurationChanged
		case len(changed.dirtyArtifacts) > 0:
			err = r.buildAndDeploy(ctx, out, changed.dirtyArtifacts, imageList)
		case changed.needsRedeploy:
			if _, err := r.Deploy(ctx, out, r.builds); err != nil {
				logrus.Warnln("Skipping Deploy due to error:", err)
			}
		}

		color.Default.Fprintln(out, "Watching for changes...")
		changed.reset()
		logger.Unmute()

		return err
	}

	watcher := r.watchFactory()

	// Watch artifacts
	for i := range artifacts {
		artifact := artifacts[i]

		if err := watcher.Register(
			func() ([]string, error) { return dependenciesForArtifact(artifact) },
			func() { changed.Add(artifact) },
		); err != nil {
			return nil, errors.Wrapf(err, "watching files for artifact %s", artifact.ImageName)
		}
	}

	// Watch deployment configuration
	if err := watcher.Register(
		func() ([]string, error) { return r.Dependencies() },
		func() { changed.needsRedeploy = true },
	); err != nil {
		return nil, errors.Wrap(err, "watching files for deployer")
	}

	// Watch Skaffold configuration
	if err := watcher.Register(
		func() ([]string, error) { return []string{r.opts.ConfigurationFile}, nil },
		func() { changed.needsReload = true },
	); err != nil {
		return nil, errors.Wrapf(err, "watching skaffold configuration %s", r.opts.ConfigurationFile)
	}

	// First run
	if err := r.buildAndDeploy(ctx, out, artifacts, imageList); err != nil {
		return nil, errors.Wrap(err, "first run")
	}

	// Start logs
	if err := logger.Start(ctx); err != nil {
		return nil, errors.Wrap(err, "starting logger")
	}

	return nil, watcher.Run(ctx, PollInterval, onChange)
}

// buildAndDeploy builds a subset of the artifacts and deploys everything.
func (r *SkaffoldRunner) buildAndDeploy(ctx context.Context, out io.Writer, artifacts []*v1alpha2.Artifact, images *kubernetes.ImageList) error {
	firstRun := r.builds == nil

	bRes, err := r.Build(ctx, out, r.Tagger, artifacts)
	if err != nil {
		if firstRun {
			return errors.Wrap(err, "exiting dev mode because the first build failed")
		}

		logrus.Warnln("Skipping Deploy due to build error:", err)
		return nil
	}

	// Update which images are logged.
	for _, build := range bRes {
		images.Add(build.Tag)
	}

	// Make sure all artifacts are redeployed. Not only those that were just rebuilt.
	r.builds = mergeWithPreviousBuilds(bRes, r.builds)

	_, err = r.Deploy(ctx, out, r.builds)
	if err != nil {
		if firstRun {
			return errors.Wrap(err, "exiting dev mode because the first deploy failed")
		}

		logrus.Warnln("Skipping Deploy due to error:", err)
		return nil
	}

	return nil
}

func mergeWithPreviousBuilds(builds, previous []build.Artifact) []build.Artifact {
	updatedBuilds := map[string]bool{}
	for _, build := range builds {
		updatedBuilds[build.ImageName] = true
	}

	var merged []build.Artifact
	merged = append(merged, builds...)

	for _, b := range previous {
		if !updatedBuilds[b.ImageName] {
			merged = append(merged, b)
		}
	}

	return merged
}

func dependenciesForArtifact(a *v1alpha2.Artifact) ([]string, error) {
	var (
		paths []string
		err   error
	)

	switch {
	case a.DockerArtifact != nil:
		paths, err = docker.GetDependencies(a.Workspace, a.DockerArtifact)

	case a.BazelArtifact != nil:
		paths, err = bazel.GetDependencies(a.Workspace, a.BazelArtifact)

	default:
		return nil, fmt.Errorf("undefined artifact type: %+v", a.ArtifactType)
	}

	if err != nil {
		return nil, err
	}

	var p []string
	for _, path := range paths {
		p = append(p, filepath.Join(a.Workspace, path))
	}
	return p, nil
}
<|endoftext|>


<commit_before>package adapter

import (
	crand "crypto/rand"
	"encoding/base64"
	"encoding/gob"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/context"
	"github.com/gorilla/sessions"
	"github.com/sirupsen/logrus"
)

// ErrProviderConfigNotFound is the error which provider configure is not found.
var ErrProviderConfigNotFound = errors.New("shogo82148/go-nginx-oauth2-adapter: provider configure not found")

// ErrForbidden is the error which the access is forbidden.
var ErrForbidden = errors.New("shogo82148/go-nginx-oauth2-adapter/provider: access forbidden")

// fallback for "crypto/rand"
var mu sync.Mutex
var myRand = rand.New(rand.NewSource(time.Now().UnixNano()))

// Server is the go-nginx-oauth2-adapter server.
type Server struct {
	Config             Config
	DefaultPrivider    string
	ProviderConfigs    map[string]ProviderConfig
	SessionStore       sessions.Store
	AppRefreshInterval time.Duration
}

func init() {
	gob.Register(time.Time{})
}

// NewServer returns a new go-nginx-oauth2-adapter server.
func NewServer(config Config) (*Server, error) {
	s := &Server{
		Config:          config,
		ProviderConfigs: map[string]ProviderConfig{},
	}

	for name, provider := range providers {
		var conf map[string]interface{}
		var ok bool
		if config.Providers == nil {
			continue
		}
		conf, ok = config.Providers[name]
		if !ok {
			continue
		}
		providerConfig, err := provider.ParseConfig(conf)
		if err != nil && err != ErrProviderConfigNotFound {
			return nil, err
		}
		s.ProviderConfigs[name] = providerConfig
		if s.DefaultPrivider != "" {
			return nil, errors.New("shogo82148/go-nginx-oauth2-adapter: multiple providers are not supported")
		}
		s.DefaultPrivider = name
	}

	if s.DefaultPrivider == "" {
		return nil, ErrProviderConfigNotFound
	}

	// Decode secrets and set up the session store.
	secrets := make([][]byte, len(s.Config.Secrets))
	for i, secret := range s.Config.Secrets {
		isAuthKey := i%2 == 0
		if secret == nil {
			if isAuthKey {
				logrus.Warn("The session authentication key is empty. you should set secure random hex string.")
				secrets[i] = []byte("dummy-session-authentication-key")
				if s.Config.ConfigTest {
					return nil, errors.New("shogo82148/go-nginx-oauth2-adapter: the session authentication key is empty")
				}
			}
			continue
		}
		var err error
		if isAuthKey {
			switch len(*secret) {
			case 32, 64:
				secrets[i] = []byte(*secret)
			case 128:
				secrets[i], err = hex.DecodeString(*secret)
			default:
				err = errors.New("shogo82148/go-nginx-oauth2-adapter: invalid session authentication key length")
			}
		} else {
			switch len(*secret) {
			case 16, 24, 32:
				secrets[i] = []byte(*secret)
			case 48, 64:
				secrets[i], err = hex.DecodeString(*secret)
			default:
				err = errors.New("shogo82148/go-nginx-oauth2-adapter: invalid session authentication key length")
			}
		}
		if err != nil {
			return nil, err
		}
	}
	if len(secrets) == 0 {
		logrus.Warn("The session authentication key is empty. you should set secure random hex string.")
		secrets = append(secrets, []byte("dummy-session-authentication-key"))
		if s.Config.ConfigTest {
			return nil, errors.New("adapter: the session authentication key is empty")
		}
	}
	store := sessions.NewCookieStore(secrets...)
	store.Options = config.Cookie.Options()
	s.SessionStore = store

	if s.Config.AppRefreshInterval == "" {
		s.AppRefreshInterval = 24 * time.Hour
	} else {
		var err error
		s.AppRefreshInterval, err = time.ParseDuration(s.Config.AppRefreshInterval)
		if err != nil {
			return nil, err
		}
	}

	return s, nil
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer context.Clear(r)

	switch r.URL.Path {
	case "/test":
		s.HandlerTest(w, r)
	case "/initiate":
		s.HandlerInitiate(w, r)
	case "/callback":
		s.HandlerCallback(w, r)
	default:
		http.NotFound(w, r)
	}
}

// HandlerTest validates the session.
func (s *Server) HandlerTest(w http.ResponseWriter, r *http.Request) {
	session, err := s.SessionStore.Get(r, s.Config.SessionName)
	if err != nil {
		// session is broken. retrigger authorization for fix it
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Info("session is broken. trigger reauthorization for fix it.")

		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}

	// check when the user has started the session.
	var val interface{}
	var ok bool
	var loggedInAt time.Time
	val = session.Values["logged_in_at"]
	if loggedInAt, ok = val.(time.Time); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "logged_in_at is not found",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}

	if time.Now().Sub(loggedInAt) > s.AppRefreshInterval {
		logrus.Info("session is expired. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}

	// send the user information to the application server.
	var provider string
	val = session.Values["provider"]
	if provider, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "provider is not found",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}
	w.Header().Add("x-ngx-omniauth-provider", provider)

	var uid string
	val = session.Values["uid"]
	if uid, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "uid is not found",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}
	w.Header().Add("x-ngx-omniauth-user", uid)

	var info string
	val = session.Values["info"]
	if info, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "info is not found",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}
	w.Header().Add("x-ngx-omniauth-info", info)

	var b []byte
	b, err = base64.StdEncoding.DecodeString(info)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": "info is not base64 encoded",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}

	var j map[string]interface{}
	if err := json.Unmarshal(b, &j); err != nil {
		logrus.WithFields(logrus.Fields{
			"err": "info is invalid json",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}
	if email, ok := j["email"]; ok {
		if email, ok := email.(string); ok {
			w.Header().Add("x-ngx-omniauth-email", email)
		}
	}

	fmt.Fprint(w, "")
}

// HandlerInitiate redirects to authorization page.
func (s *Server) HandlerInitiate(w http.ResponseWriter, r *http.Request) {
	// ignore error bacause we don't need privious session values.
	session, _ := s.SessionStore.Get(r, s.Config.SessionName)

	conf := s.ProviderConfigs[s.DefaultPrivider].Config()
	callback := r.Header.Get("x-ngx-omniauth-initiate-callback")
	next := r.Header.Get("x-ngx-omniauth-initiate-back-to")
	state := generateNewState()

	conf.RedirectURL = callback
	session.Values = map[interface{}]interface{}{}
	session.Values["provider"] = s.DefaultPrivider
	session.Values["callback"] = callback
	session.Values["next"] = next
	session.Values["state"] = state
	if err := session.Save(r, w); err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Error("failed to save session")
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	http.Redirect(w, r, conf.AuthCodeURL(state), http.StatusFound)
}

// HandlerCallback validates the user infomation, set to cookie
func (s *Server) HandlerCallback(w http.ResponseWriter, r *http.Request) {
	session, err := s.SessionStore.Get(r, s.Config.SessionName)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Error("failed to get session")
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	var val interface{}
	var ok bool

	var provider string
	val = session.Values["provider"]
	if provider, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "provider is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	var callback string
	val = session.Values["callback"]
	if callback, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "callback is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	var next string
	val = session.Values["next"]
	if next, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "next is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	var state string
	val = session.Values["state"]
	if state, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "state is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	providerConfig, ok := s.ProviderConfigs[provider]
	if !ok {
		logrus.WithFields(logrus.Fields{
			"err": "provider is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	conf := providerConfig.Config()
	conf.RedirectURL = callback

	query := r.URL.Query()

	if state != query.Get("state") {
		logrus.WithFields(logrus.Fields{
			"err": "state is not correct",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	code := query.Get("code")
	t, err := conf.Exchange(r.Context(), code)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Info("oauth verification faild")
		http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
		return
	}

	var uid string
	var info map[string]interface{}
	if infoctx, ok := providerConfig.(ProviderInfoContext); ok {
		uid, info, err = infoctx.InfoContext(r.Context(), &conf, t)
	} else {
		uid, info, err = providerConfig.Info(&conf, t)
	}
	if err != nil {
		if err == ErrForbidden {
			logrus.WithFields(logrus.Fields{
				"err": err.Error(),
			}).Warn("access forbidden")
			http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
		} else {
			logrus.WithFields(logrus.Fields{
				"err": err.Error(),
			}).Warn("user info cannot get")
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return
	}
	jsonInfo, encodedInfo, err := encodeInfo(info)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Info("failed encoding info")
	}

	session.Values["uid"] = uid
	session.Values["info"] = encodedInfo
	session.Values["logged_in_at"] = time.Now()

	if err := session.Save(r, w); err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Error("failed to save session")
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	logrus.WithFields(logrus.Fields{
		"user": uid,
		"info": jsonInfo,
	}).Info("user login")

	http.Redirect(w, r, next, http.StatusFound)
}

// generateNewState generate secure random state
func generateNewState() string {
	data := make([]byte, 32)
	if n, err := crand.Read(data); err != nil || n != len(data) {
		f := logrus.Fields{}
		if err != nil {
			f["err"] = err.Error()
		}
		logrus.WithFields(f).Warn("failed to generate secure random state. fallback to insecure pseudo random.")

		mu.Lock()
		defer mu.Unlock()
		// fallback insecure pseudo random
		for i := range data {
			data[i] = byte(myRand.Intn(256))
		}
	}
	return base64.URLEncoding.EncodeToString(data)
}

// encodeInfo encodes the user information for embeding to http header.
func encodeInfo(info map[string]interface{}) (string, string, error) {
	data, err := json.Marshal(info)
	if err != nil {
		return "", "", err
	}
	return string(data), base64.StdEncoding.EncodeToString(data), nil
}
<commit_msg>fix typo: provider<commit_after>package adapter

import (
	crand "crypto/rand"
	"encoding/base64"
	"encoding/gob"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/context"
	"github.com/gorilla/sessions"
	"github.com/sirupsen/logrus"
)

// ErrProviderConfigNotFound is the error which provider configure is not found.
var ErrProviderConfigNotFound = errors.New("shogo82148/go-nginx-oauth2-adapter: provider configure not found")

// ErrForbidden is the error which the access is forbidden.
var ErrForbidden = errors.New("shogo82148/go-nginx-oauth2-adapter/provider: access forbidden")

// fallback for "crypto/rand"
var mu sync.Mutex
var myRand = rand.New(rand.NewSource(time.Now().UnixNano()))

// Server is the go-nginx-oauth2-adapter server.
type Server struct {
	Config             Config
	DefaultProvider    string
	ProviderConfigs    map[string]ProviderConfig
	SessionStore       sessions.Store
	AppRefreshInterval time.Duration
}

func init() {
	gob.Register(time.Time{})
}

// NewServer returns a new go-nginx-oauth2-adapter server.
func NewServer(config Config) (*Server, error) {
	s := &Server{
		Config:          config,
		ProviderConfigs: map[string]ProviderConfig{},
	}

	for name, provider := range providers {
		var conf map[string]interface{}
		var ok bool
		if config.Providers == nil {
			continue
		}
		conf, ok = config.Providers[name]
		if !ok {
			continue
		}
		providerConfig, err := provider.ParseConfig(conf)
		if err != nil && err != ErrProviderConfigNotFound {
			return nil, err
		}
		s.ProviderConfigs[name] = providerConfig
		if s.DefaultProvider != "" {
			return nil, errors.New("shogo82148/go-nginx-oauth2-adapter: multiple providers are not supported")
		}
		s.DefaultProvider = name
	}

	if s.DefaultProvider == "" {
		return nil, ErrProviderConfigNotFound
	}

	// Decode secrets and set up the session store.
	secrets := make([][]byte, len(s.Config.Secrets))
	for i, secret := range s.Config.Secrets {
		isAuthKey := i%2 == 0
		if secret == nil {
			if isAuthKey {
				logrus.Warn("The session authentication key is empty. you should set secure random hex string.")
				secrets[i] = []byte("dummy-session-authentication-key")
				if s.Config.ConfigTest {
					return nil, errors.New("shogo82148/go-nginx-oauth2-adapter: the session authentication key is empty")
				}
			}
			continue
		}
		var err error
		if isAuthKey {
			switch len(*secret) {
			case 32, 64:
				secrets[i] = []byte(*secret)
			case 128:
				secrets[i], err = hex.DecodeString(*secret)
			default:
				err = errors.New("shogo82148/go-nginx-oauth2-adapter: invalid session authentication key length")
			}
		} else {
			switch len(*secret) {
			case 16, 24, 32:
				secrets[i] = []byte(*secret)
			case 48, 64:
				secrets[i], err = hex.DecodeString(*secret)
			default:
				err = errors.New("shogo82148/go-nginx-oauth2-adapter: invalid session authentication key length")
			}
		}
		if err != nil {
			return nil, err
		}
	}
	if len(secrets) == 0 {
		logrus.Warn("The session authentication key is empty. you should set secure random hex string.")
		secrets = append(secrets, []byte("dummy-session-authentication-key"))
		if s.Config.ConfigTest {
			return nil, errors.New("adapter: the session authentication key is empty")
		}
	}
	store := sessions.NewCookieStore(secrets...)
	store.Options = config.Cookie.Options()
	s.SessionStore = store

	if s.Config.AppRefreshInterval == "" {
		s.AppRefreshInterval = 24 * time.Hour
	} else {
		var err error
		s.AppRefreshInterval, err = time.ParseDuration(s.Config.AppRefreshInterval)
		if err != nil {
			return nil, err
		}
	}

	return s, nil
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer context.Clear(r)

	switch r.URL.Path {
	case "/test":
		s.HandlerTest(w, r)
	case "/initiate":
		s.HandlerInitiate(w, r)
	case "/callback":
		s.HandlerCallback(w, r)
	default:
		http.NotFound(w, r)
	}
}

// HandlerTest validates the session.
func (s *Server) HandlerTest(w http.ResponseWriter, r *http.Request) {
	session, err := s.SessionStore.Get(r, s.Config.SessionName)
	if err != nil {
		// session is broken. retrigger authorization for fix it
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Info("session is broken. trigger reauthorization for fix it.")

		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}

	// check when the user has started the session.
	var val interface{}
	var ok bool
	var loggedInAt time.Time
	val = session.Values["logged_in_at"]
	if loggedInAt, ok = val.(time.Time); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "logged_in_at is not found",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}

	if time.Now().Sub(loggedInAt) > s.AppRefreshInterval {
		logrus.Info("session is expired. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}

	// send the user information to the application server.
	var provider string
	val = session.Values["provider"]
	if provider, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "provider is not found",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}
	w.Header().Add("x-ngx-omniauth-provider", provider)

	var uid string
	val = session.Values["uid"]
	if uid, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "uid is not found",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}
	w.Header().Add("x-ngx-omniauth-user", uid)

	var info string
	val = session.Values["info"]
	if info, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "info is not found",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}
	w.Header().Add("x-ngx-omniauth-info", info)

	var b []byte
	b, err = base64.StdEncoding.DecodeString(info)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": "info is not base64 encoded",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}

	var j map[string]interface{}
	if err := json.Unmarshal(b, &j); err != nil {
		logrus.WithFields(logrus.Fields{
			"err": "info is invalid json",
		}).Info("session is broken. trigger reauthorization for fix it.")
		http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}
	if email, ok := j["email"]; ok {
		if email, ok := email.(string); ok {
			w.Header().Add("x-ngx-omniauth-email", email)
		}
	}

	fmt.Fprint(w, "")
}

// HandlerInitiate redirects to authorization page.
func (s *Server) HandlerInitiate(w http.ResponseWriter, r *http.Request) {
	// ignore error bacause we don't need privious session values.
	session, _ := s.SessionStore.Get(r, s.Config.SessionName)

	conf := s.ProviderConfigs[s.DefaultProvider].Config()
	callback := r.Header.Get("x-ngx-omniauth-initiate-callback")
	next := r.Header.Get("x-ngx-omniauth-initiate-back-to")
	state := generateNewState()

	conf.RedirectURL = callback
	session.Values = map[interface{}]interface{}{}
	session.Values["provider"] = s.DefaultProvider
	session.Values["callback"] = callback
	session.Values["next"] = next
	session.Values["state"] = state
	if err := session.Save(r, w); err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Error("failed to save session")
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	http.Redirect(w, r, conf.AuthCodeURL(state), http.StatusFound)
}

// HandlerCallback validates the user infomation, set to cookie
func (s *Server) HandlerCallback(w http.ResponseWriter, r *http.Request) {
	session, err := s.SessionStore.Get(r, s.Config.SessionName)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Error("failed to get session")
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	var val interface{}
	var ok bool

	var provider string
	val = session.Values["provider"]
	if provider, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "provider is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	var callback string
	val = session.Values["callback"]
	if callback, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "callback is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	var next string
	val = session.Values["next"]
	if next, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "next is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	var state string
	val = session.Values["state"]
	if state, ok = val.(string); !ok {
		logrus.WithFields(logrus.Fields{
			"err": "state is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	providerConfig, ok := s.ProviderConfigs[provider]
	if !ok {
		logrus.WithFields(logrus.Fields{
			"err": "provider is not found",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	conf := providerConfig.Config()
	conf.RedirectURL = callback

	query := r.URL.Query()

	if state != query.Get("state") {
		logrus.WithFields(logrus.Fields{
			"err": "state is not correct",
		}).Info("session is broken.")
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	code := query.Get("code")
	t, err := conf.Exchange(r.Context(), code)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Info("oauth verification faild")
		http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
		return
	}

	var uid string
	var info map[string]interface{}
	if infoctx, ok := providerConfig.(ProviderInfoContext); ok {
		uid, info, err = infoctx.InfoContext(r.Context(), &conf, t)
	} else {
		uid, info, err = providerConfig.Info(&conf, t)
	}
	if err != nil {
		if err == ErrForbidden {
			logrus.WithFields(logrus.Fields{
				"err": err.Error(),
			}).Warn("access forbidden")
			http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
		} else {
			logrus.WithFields(logrus.Fields{
				"err": err.Error(),
			}).Warn("user info cannot get")
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return
	}
	jsonInfo, encodedInfo, err := encodeInfo(info)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Info("failed encoding info")
	}

	session.Values["uid"] = uid
	session.Values["info"] = encodedInfo
	session.Values["logged_in_at"] = time.Now()

	if err := session.Save(r, w); err != nil {
		logrus.WithFields(logrus.Fields{
			"err": err.Error(),
		}).Error("failed to save session")
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}

	logrus.WithFields(logrus.Fields{
		"user": uid,
		"info": jsonInfo,
	}).Info("user login")

	http.Redirect(w, r, next, http.StatusFound)
}

// generateNewState generate secure random state
func generateNewState() string {
	data := make([]byte, 32)
	if n, err := crand.Read(data); err != nil || n != len(data) {
		f := logrus.Fields{}
		if err != nil {
			f["err"] = err.Error()
		}
		logrus.WithFields(f).Warn("failed to generate secure random state. fallback to insecure pseudo random.")

		mu.Lock()
		defer mu.Unlock()
		// fallback insecure pseudo random
		for i := range data {
			data[i] = byte(myRand.Intn(256))
		}
	}
	return base64.URLEncoding.EncodeToString(data)
}

// encodeInfo encodes the user information for embeding to http header.
func encodeInfo(info map[string]interface{}) (string, string, error) {
	data, err := json.Marshal(info)
	if err != nil {
		return "", "", err
	}
	return string(data), base64.StdEncoding.EncodeToString(data), nil
}
<|endoftext|>


<commit_before>// Package listmap implements an ordered doubly linked list map.
package listmap

import (
	"bytes"
	"errors"
	"os"
	"sync"
	"syscall"
	"unsafe"
)

var (
	ErrKeyNotFound       = errors.New("listmap: key not found")
	ErrKeyPresent        = errors.New("listmap: key already present")
	ErrFileTruncateError = errors.New("listmap: file truncate error")
	ErrUnknown           = errors.New("listmap: unknown error")
)

const (
	rootLength   = unsafe.Sizeof(root{})
	recordLength = unsafe.Sizeof(record{})

	constTruncateResize = 1 << 12
)

// Listmap represents an ordered doubly linked list map.
type Listmap struct {
	file     *os.File
	fileSize int64
	lock     *sync.Mutex
	root     *root
	mapped   []byte
}

type root struct {
	first        uint32
	last         uint32
	lastInserted uint32
}

type record struct {
	prev    uint32
	next    uint32
	keylen  uint16
	vallen  uint16
	removed bool
}

// NewListmap returns a pointer to an initialized list backed by file
// or nil in the case of an error. file will be truncated.
func NewListmap(file string) *Listmap {
	f, err := os.Create(file)
	if err != nil {
		return nil
	}

	f.Truncate(int64(rootLength))
	stat, err := f.Stat()
	if err != nil {
		f.Close()
		return nil
	}

	sl, err := syscall.Mmap(int(f.Fd()), 0, int(stat.Size()),
		syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)

	if err != nil {
		f.Close()
		return nil
	}

	l := &Listmap{
		file:     f,
		lock:     &sync.Mutex{},
		mapped:   sl,
		fileSize: stat.Size(),
	}

	l.root = (*root)(unsafe.Pointer(&l.mapped[0]))
	return l
}

// OpenListmap returns a pointer to an existing Listmap
// backed by file or nil in the case of an error.
func OpenListmap(file string) *Listmap {
	f, err := os.OpenFile(file, os.O_RDWR, 0666)
	if err != nil {
		return nil
	}
	stat, err := f.Stat()
	if err != nil {
		f.Close()
		return nil
	}

	sl, err := syscall.Mmap(int(f.Fd()), 0, int(stat.Size()),
		syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)

	if err != nil {
		f.Close()
		return nil
	}

	l := &Listmap{
		file:     f,
		lock:     &sync.Mutex{},
		mapped:   sl,
		fileSize: stat.Size(),
	}

	l.root = (*root)(unsafe.Pointer(&l.mapped[0]))
	return l
}

// Close closes an initialized Listmap.
func (l *Listmap) Close() {
	syscall.Munmap(l.mapped)
	l.file.Close()
}

// Destroy closes an initialized Listmap and
// removes its associated file.
func (l *Listmap) Destroy() {
	syscall.Munmap(l.mapped)
	l.file.Close()
	os.Remove(l.file.Name())
}

// Set writes a key-value pair to a Listmap. Records are
// kept in lexicographical order.
func (l *Listmap) Set(key, value []byte) error {
	l.lock.Lock()

	if int64(l.root.lastInserted)+constTruncateResize > int64(len(l.mapped)) {
		syscall.Munmap(l.mapped)
		err := l.file.Truncate(l.fileSize + constTruncateResize)
		if err != nil {
			l.mapped, _ = syscall.Mmap(int(l.file.Fd()), 0, int(l.fileSize),
				syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
			l.root = (*root)(unsafe.Pointer(&l.mapped[0]))
			return ErrFileTruncateError
		}
		l.mapped, _ = syscall.Mmap(int(l.file.Fd()), 0, int(l.fileSize+constTruncateResize),
			syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
		l.root = (*root)(unsafe.Pointer(&l.mapped[0]))

		l.fileSize += constTruncateResize
	}

	// First record
	if l.root.lastInserted == 0 {
		r := (*record)(unsafe.Pointer(&l.mapped[rootLength]))
		r.keylen = uint16(len(key))
		r.vallen = uint16(len(value))
		copy(l.mapped[rootLength+recordLength:], append(key, value...))

		l.root.first = uint32(rootLength)
		l.root.last = uint32(rootLength)
		l.root.lastInserted = uint32(rootLength)
		l.lock.Unlock()
		return nil
	}

	cursor := l.NewCursor().seek(int(l.root.lastInserted))

	// New records always go to the end
	currentIndex := int(l.root.lastInserted) + int(recordLength) + int(cursor.r.keylen+cursor.r.vallen)
	l.root.lastInserted = uint32(currentIndex)
	r := (*record)(unsafe.Pointer(&l.mapped[currentIndex]))
	r.keylen = uint16(len(key))
	r.vallen = uint16(len(value))
	copy(l.mapped[currentIndex+int(recordLength):], append(key, value...))

	l.lock.Unlock()

	cursor = cursor.seek(int(l.root.last))

	lastKey := cursor.Key()

	// Sequential insert
	if cmp := bytes.Compare(lastKey, key); cmp < 0 || (cmp == 0 && cursor.r.removed) {
		cursor.r.next = uint32(currentIndex)
		r.prev = l.root.last
		l.root.last = cursor.r.next
		return nil
	} else {
		// find first greater than
		cursor = cursor.seek(int(l.root.first))

		for cursor != nil {
			if bytes.Compare(cursor.Key(), key) == 0 &&
				!cursor.r.removed {
				return ErrKeyPresent
			}

			if bytes.Compare(cursor.Key(), key) > 0 {
				if cursor.index == int(l.root.first) {
					// inserting before first
					cursor.r.prev = uint32(currentIndex)
					r.next = uint32(cursor.index)
					l.root.first = uint32(currentIndex)
					return nil
				} else {
					nextRecord := cursor.r
					nextRecordIndex := cursor.index
					previousRecord := cursor.Prev().r
					previousRecordIndex := cursor.index

					r.next = uint32(nextRecordIndex)
					r.prev = uint32(previousRecordIndex)

					previousRecord.next = uint32(currentIndex)
					nextRecord.prev = uint32(currentIndex)

					return nil
				}
			} else {
				cursor = cursor.Next()
			}
		}
	}

	return ErrUnknown
}

// Get returns the value in the Listmap associated with key.
func (l *Listmap) Get(key []byte) ([]byte, error) {
	for c := l.NewCursor(); c != nil; c = c.Next() {
		cKey := c.Key()
		if bytes.Compare(cKey, key) > 0 {
			return nil, ErrKeyNotFound
		}

		if bytes.Compare(cKey, key) == 0 {
			if !c.r.removed {
				return c.Value(), nil
			}
		}
	}
	return nil, ErrKeyNotFound
}

func (l *Listmap) Remove(key []byte) {
	for c := l.NewCursor(); c != nil; c = c.Next() {
		cKey := c.Key()
		if bytes.Compare(cKey, key) > 0 {
			return
		}

		if bytes.Compare(cKey, key) == 0 {
			c.r.removed = true
		}
	}
}
<commit_msg>Add comment above Remove<commit_after>// Package listmap implements an ordered doubly linked list map.
package listmap

import (
	"bytes"
	"errors"
	"os"
	"sync"
	"syscall"
	"unsafe"
)

var (
	ErrKeyNotFound       = errors.New("listmap: key not found")
	ErrKeyPresent        = errors.New("listmap: key already present")
	ErrFileTruncateError = errors.New("listmap: file truncate error")
	ErrUnknown           = errors.New("listmap: unknown error")
)

const (
	rootLength   = unsafe.Sizeof(root{})
	recordLength = unsafe.Sizeof(record{})

	constTruncateResize = 1 << 12
)

// Listmap represents an ordered doubly linked list map.
type Listmap struct {
	file     *os.File
	fileSize int64
	lock     *sync.Mutex
	root     *root
	mapped   []byte
}

type root struct {
	first        uint32
	last         uint32
	lastInserted uint32
}

type record struct {
	prev    uint32
	next    uint32
	keylen  uint16
	vallen  uint16
	removed bool
}

// NewListmap returns a pointer to an initialized list backed by file
// or nil in the case of an error. file will be truncated.
func NewListmap(file string) *Listmap {
	f, err := os.Create(file)
	if err != nil {
		return nil
	}

	f.Truncate(int64(rootLength))
	stat, err := f.Stat()
	if err != nil {
		f.Close()
		return nil
	}

	sl, err := syscall.Mmap(int(f.Fd()), 0, int(stat.Size()),
		syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)

	if err != nil {
		f.Close()
		return nil
	}

	l := &Listmap{
		file:     f,
		lock:     &sync.Mutex{},
		mapped:   sl,
		fileSize: stat.Size(),
	}

	l.root = (*root)(unsafe.Pointer(&l.mapped[0]))
	return l
}

// OpenListmap returns a pointer to an existing Listmap
// backed by file or nil in the case of an error.
func OpenListmap(file string) *Listmap {
	f, err := os.OpenFile(file, os.O_RDWR, 0666)
	if err != nil {
		return nil
	}
	stat, err := f.Stat()
	if err != nil {
		f.Close()
		return nil
	}

	sl, err := syscall.Mmap(int(f.Fd()), 0, int(stat.Size()),
		syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)

	if err != nil {
		f.Close()
		return nil
	}

	l := &Listmap{
		file:     f,
		lock:     &sync.Mutex{},
		mapped:   sl,
		fileSize: stat.Size(),
	}

	l.root = (*root)(unsafe.Pointer(&l.mapped[0]))
	return l
}

// Close closes an initialized Listmap.
func (l *Listmap) Close() {
	syscall.Munmap(l.mapped)
	l.file.Close()
}

// Destroy closes an initialized Listmap and
// removes its associated file.
func (l *Listmap) Destroy() {
	syscall.Munmap(l.mapped)
	l.file.Close()
	os.Remove(l.file.Name())
}

// Set writes a key-value pair to a Listmap. Records are
// kept in lexicographical order.
func (l *Listmap) Set(key, value []byte) error {
	l.lock.Lock()

	if int64(l.root.lastInserted)+constTruncateResize > int64(len(l.mapped)) {
		syscall.Munmap(l.mapped)
		err := l.file.Truncate(l.fileSize + constTruncateResize)
		if err != nil {
			l.mapped, _ = syscall.Mmap(int(l.file.Fd()), 0, int(l.fileSize),
				syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
			l.root = (*root)(unsafe.Pointer(&l.mapped[0]))
			return ErrFileTruncateError
		}
		l.mapped, _ = syscall.Mmap(int(l.file.Fd()), 0, int(l.fileSize+constTruncateResize),
			syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
		l.root = (*root)(unsafe.Pointer(&l.mapped[0]))

		l.fileSize += constTruncateResize
	}

	// First record
	if l.root.lastInserted == 0 {
		r := (*record)(unsafe.Pointer(&l.mapped[rootLength]))
		r.keylen = uint16(len(key))
		r.vallen = uint16(len(value))
		copy(l.mapped[rootLength+recordLength:], append(key, value...))

		l.root.first = uint32(rootLength)
		l.root.last = uint32(rootLength)
		l.root.lastInserted = uint32(rootLength)
		l.lock.Unlock()
		return nil
	}

	cursor := l.NewCursor().seek(int(l.root.lastInserted))

	// New records always go to the end
	currentIndex := int(l.root.lastInserted) + int(recordLength) + int(cursor.r.keylen+cursor.r.vallen)
	l.root.lastInserted = uint32(currentIndex)
	r := (*record)(unsafe.Pointer(&l.mapped[currentIndex]))
	r.keylen = uint16(len(key))
	r.vallen = uint16(len(value))
	copy(l.mapped[currentIndex+int(recordLength):], append(key, value...))

	l.lock.Unlock()

	cursor = cursor.seek(int(l.root.last))

	lastKey := cursor.Key()

	// Sequential insert
	if cmp := bytes.Compare(lastKey, key); cmp < 0 || (cmp == 0 && cursor.r.removed) {
		cursor.r.next = uint32(currentIndex)
		r.prev = l.root.last
		l.root.last = cursor.r.next
		return nil
	} else {
		// find first greater than
		cursor = cursor.seek(int(l.root.first))

		for cursor != nil {
			if bytes.Compare(cursor.Key(), key) == 0 &&
				!cursor.r.removed {
				return ErrKeyPresent
			}

			if bytes.Compare(cursor.Key(), key) > 0 {
				if cursor.index == int(l.root.first) {
					// inserting before first
					cursor.r.prev = uint32(currentIndex)
					r.next = uint32(cursor.index)
					l.root.first = uint32(currentIndex)
					return nil
				} else {
					nextRecord := cursor.r
					nextRecordIndex := cursor.index
					previousRecord := cursor.Prev().r
					previousRecordIndex := cursor.index

					r.next = uint32(nextRecordIndex)
					r.prev = uint32(previousRecordIndex)

					previousRecord.next = uint32(currentIndex)
					nextRecord.prev = uint32(currentIndex)

					return nil
				}
			} else {
				cursor = cursor.Next()
			}
		}
	}

	return ErrUnknown
}

// Get returns the value in the Listmap associated with key.
func (l *Listmap) Get(key []byte) ([]byte, error) {
	for c := l.NewCursor(); c != nil; c = c.Next() {
		cKey := c.Key()
		if bytes.Compare(cKey, key) > 0 {
			return nil, ErrKeyNotFound
		}

		if bytes.Compare(cKey, key) == 0 {
			if !c.r.removed {
				return c.Value(), nil
			}
		}
	}
	return nil, ErrKeyNotFound
}

// Remove marks a key as removed
func (l *Listmap) Remove(key []byte) {
	for c := l.NewCursor(); c != nil; c = c.Next() {
		cKey := c.Key()
		if bytes.Compare(cKey, key) > 0 {
			return
		}

		if bytes.Compare(cKey, key) == 0 {
			c.r.removed = true
		}
	}
}
<|endoftext|>


<commit_before>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package api

import (
	"fmt"
	"net/url"

	"github.com/google/go-querystring/query"
)

// LDAPSettings holds Domain LDAP settings
type LDAPSettings struct {
	ID                int        `json:"id,omitempty" url:"id,omitempty"`
	Basedn            string     `json:"basedn" url:"basedn"`
	NameAttribute     string     `json:"nameattribute" url:"nameattribute"`
	EmailAttribute    string     `json:"emailattribute" url:"emailattribute"`
	BindDN            string     `json:"binddn" url:"binddn"`
	BindPw            string     `json:"bindpw" url:"bindpw"`
	UseTLS            bool       `json:"usetls" url:"usetls"`
	UseSearch         bool       `json:"usesearch" url:"usesearch"`
	SearchFilter      string     `json:"searchfilter" url:"searchfilter"`
	SearchScope       string     `json:"search_scope" url:"search_scope"`
	EmailSearchFilter string     `json:"emailsearchfilter" url:"emailsearchfilter"`
	EmailSearchScope  string     `json:"emailsearch_scope" url:"emailsearch_scope"`
	AuthServer        SettingsAS `json:"authserver" url:"authserver"`
}

// GetLDAPSettings returns a domain LDAP settings
// https://www.baruwa.com/docs/api/#retrieve-ad-ldap-settings
func (c *Client) GetLDAPSettings(domainID, serverID, settingsID int) (settings *LDAPSettings, err error) {
	if domainID <= 0 {
		err = fmt.Errorf(domainIDError)
		return
	}

	if serverID <= 0 {
		err = fmt.Errorf(serverIDError)
		return
	}

	if settingsID <= 0 {
		err = fmt.Errorf(settingsIDError)
		return
	}

	settings = &LDAPSettings{}

	err = c.get(fmt.Sprintf("ldapsettings/%d/%d/%d", domainID, serverID, settingsID), nil, settings)

	return
}

// CreateLDAPSettings creates a domain LDAP settings
// https://www.baruwa.com/docs/api/#create-ad-ldap-settings
func (c *Client) CreateLDAPSettings(domainID, serverID int, settings *LDAPSettings) (err error) {
	var v url.Values

	if domainID <= 0 {
		err = fmt.Errorf(domainIDError)
		return
	}

	if serverID <= 0 {
		err = fmt.Errorf(serverIDError)
		return
	}

	if settings == nil {
		err = fmt.Errorf(settingsParamError)
		return
	}

	if v, err = query.Values(settings); err != nil {
		return
	}

	err = c.post(fmt.Sprintf("ldapsettings/%d/%d", domainID, serverID), v, settings)

	return
}

// UpdateLDAPSettings updates a domain LDAP settings
// https://www.baruwa.com/docs/api/#update-ad-ldap-settings
func (c *Client) UpdateLDAPSettings(domainID, serverID int, settings *LDAPSettings) (err error) {
	var v url.Values

	if domainID <= 0 {
		err = fmt.Errorf(domainIDError)
		return
	}

	if serverID <= 0 {
		err = fmt.Errorf(serverIDError)
		return
	}

	if settings == nil {
		err = fmt.Errorf(settingsParamError)
		return
	}

	if settings.ID <= 0 {
		err = fmt.Errorf(settingsSIDError)
		return
	}

	if v, err = query.Values(settings); err != nil {
		return
	}

	err = c.put(fmt.Sprintf("ldapsettings/%d/%d/%d", domainID, serverID, settings.ID), v, nil)

	return
}

// DeleteLDAPSettings deletes a domain LDAP settings
// https://www.baruwa.com/docs/api/#delete-ad-ldap-settings
func (c *Client) DeleteLDAPSettings(domainID, serverID int, settings *LDAPSettings) (err error) {
	var v url.Values

	if domainID <= 0 {
		err = fmt.Errorf(domainIDError)
		return
	}

	if serverID <= 0 {
		err = fmt.Errorf(serverIDError)
		return
	}

	if settings == nil {
		err = fmt.Errorf(settingsParamError)
		return
	}

	if settings.ID <= 0 {
		err = fmt.Errorf(settingsSIDError)
		return
	}

	if v, err = query.Values(settings); err != nil {
		return
	}

	err = c.delete(fmt.Sprintf("ldapsettings/%d/%d/%d", domainID, serverID, settings.ID), v)

	return
}
<commit_msg>FIX: Omit some ldap settings when blank<commit_after>// BaruwaAPI Golang bindings for Baruwa REST API
// Copyright (C) 2019 Andrew Colin Kissa <andrew@topdog.za.net>

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

package api

import (
	"fmt"
	"net/url"

	"github.com/google/go-querystring/query"
)

// LDAPSettings holds Domain LDAP settings
type LDAPSettings struct {
	ID                int        `json:"id,omitempty" url:"id,omitempty"`
	Basedn            string     `json:"basedn" url:"basedn"`
	NameAttribute     string     `json:"nameattribute" url:"nameattribute"`
	EmailAttribute    string     `json:"emailattribute" url:"emailattribute"`
	BindDN            string     `json:"binddn" url:"binddn"`
	BindPw            string     `json:"bindpw,omitempty" url:"bindpw,omitempty"`
	UseTLS            bool       `json:"usetls" url:"usetls"`
	UseSearch         bool       `json:"usesearch" url:"usesearch"`
	SearchFilter      string     `json:"searchfilter" url:"searchfilter"`
	SearchScope       string     `json:"search_scope" url:"search_scope"`
	EmailSearchFilter string     `json:"emailsearchfilter" url:"emailsearchfilter"`
	EmailSearchScope  string     `json:"emailsearch_scope" url:"emailsearch_scope"`
	AuthServer        SettingsAS `json:"authserver,omitempty" url:"authserver,omitempty"`
}

// GetLDAPSettings returns a domain LDAP settings
// https://www.baruwa.com/docs/api/#retrieve-ad-ldap-settings
func (c *Client) GetLDAPSettings(domainID, serverID, settingsID int) (settings *LDAPSettings, err error) {
	if domainID <= 0 {
		err = fmt.Errorf(domainIDError)
		return
	}

	if serverID <= 0 {
		err = fmt.Errorf(serverIDError)
		return
	}

	if settingsID <= 0 {
		err = fmt.Errorf(settingsIDError)
		return
	}

	settings = &LDAPSettings{}

	err = c.get(fmt.Sprintf("ldapsettings/%d/%d/%d", domainID, serverID, settingsID), nil, settings)

	return
}

// CreateLDAPSettings creates a domain LDAP settings
// https://www.baruwa.com/docs/api/#create-ad-ldap-settings
func (c *Client) CreateLDAPSettings(domainID, serverID int, settings *LDAPSettings) (err error) {
	var v url.Values

	if domainID <= 0 {
		err = fmt.Errorf(domainIDError)
		return
	}

	if serverID <= 0 {
		err = fmt.Errorf(serverIDError)
		return
	}

	if settings == nil {
		err = fmt.Errorf(settingsParamError)
		return
	}

	if v, err = query.Values(settings); err != nil {
		return
	}

	err = c.post(fmt.Sprintf("ldapsettings/%d/%d", domainID, serverID), v, settings)

	return
}

// UpdateLDAPSettings updates a domain LDAP settings
// https://www.baruwa.com/docs/api/#update-ad-ldap-settings
func (c *Client) UpdateLDAPSettings(domainID, serverID int, settings *LDAPSettings) (err error) {
	var v url.Values

	if domainID <= 0 {
		err = fmt.Errorf(domainIDError)
		return
	}

	if serverID <= 0 {
		err = fmt.Errorf(serverIDError)
		return
	}

	if settings == nil {
		err = fmt.Errorf(settingsParamError)
		return
	}

	if settings.ID <= 0 {
		err = fmt.Errorf(settingsSIDError)
		return
	}

	if v, err = query.Values(settings); err != nil {
		return
	}

	err = c.put(fmt.Sprintf("ldapsettings/%d/%d/%d", domainID, serverID, settings.ID), v, nil)

	return
}

// DeleteLDAPSettings deletes a domain LDAP settings
// https://www.baruwa.com/docs/api/#delete-ad-ldap-settings
func (c *Client) DeleteLDAPSettings(domainID, serverID int, settings *LDAPSettings) (err error) {
	var v url.Values

	if domainID <= 0 {
		err = fmt.Errorf(domainIDError)
		return
	}

	if serverID <= 0 {
		err = fmt.Errorf(serverIDError)
		return
	}

	if settings == nil {
		err = fmt.Errorf(settingsParamError)
		return
	}

	if settings.ID <= 0 {
		err = fmt.Errorf(settingsSIDError)
		return
	}

	if v, err = query.Values(settings); err != nil {
		return
	}

	err = c.delete(fmt.Sprintf("ldapsettings/%d/%d/%d", domainID, serverID, settings.ID), v)

	return
}
<|endoftext|>


<commit_before>package log

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path"
	"strings"
	"time"
)

// levels
const (
	debugLevel   = 0
	releaseLevel = 1
	errorLevel   = 2
	fatalLevel   = 3
)

const (
	printDebugLevel   = "[debug  ] "
	printReleaseLevel = "[release] "
	printErrorLevel   = "[error  ] "
	printFatalLevel   = "[fatal  ] "
)

type Logger struct {
	level      int
	baseLogger *log.Logger
	baseFile   *os.File
}

func New(strLevel string, pathname string) (*Logger, error) {
	// level
	var level int
	switch strings.ToLower(strLevel) {
	case "debug":
		level = debugLevel
	case "release":
		level = releaseLevel
	case "error":
		level = errorLevel
	case "fatal":
		level = fatalLevel
	default:
		return nil, errors.New("unknown level: " + strLevel)
	}

	// logger
	var baseLogger *log.Logger
	var baseFile *os.File
	if pathname != "" {
		now := time.Now()

		filename := fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d.log",
			now.Year(),
			now.Month(),
			now.Day(),
			now.Hour(),
			now.Minute(),
			now.Second())

		file, err := os.Create(path.Join(pathname, filename))
		if err != nil {
			return nil, err
		}

		baseLogger = log.New(file, "", log.LstdFlags)
		baseFile = file
	} else {
		baseLogger = log.New(os.Stdout, "", log.LstdFlags)
	}

	// new
	logger := new(Logger)
	logger.level = level
	logger.baseLogger = baseLogger
	logger.baseFile = baseFile

	return logger, nil
}

// It's dangerous to call the method on logging
func (logger *Logger) Close() {
	if logger.baseFile != nil {
		logger.baseFile.Close()
	}

	logger.baseLogger = nil
	logger.baseFile = nil
}

func (logger *Logger) doPrintf(level int, printLevel string, format string, a ...interface{}) {
	if level < logger.level {
		return
	}
	if logger.baseLogger == nil {
		panic("logger closed")
	}

	format = printLevel + format
	logger.baseLogger.Printf(format, a...)

	if level == fatalLevel {
		os.Exit(1)
	}
}

func (logger *Logger) Debug(format string, a ...interface{}) {
	logger.doPrintf(debugLevel, printDebugLevel, format, a...)
}

func (logger *Logger) Release(format string, a ...interface{}) {
	logger.doPrintf(releaseLevel, printReleaseLevel, format, a...)
}

func (logger *Logger) Error(format string, a ...interface{}) {
	logger.doPrintf(errorLevel, printErrorLevel, format, a...)
}

func (logger *Logger) Fatal(format string, a ...interface{}) {
	logger.doPrintf(fatalLevel, printFatalLevel, format, a...)
}

var gLogger, _ = New("debug", "")

// It's dangerous to call the method on logging
func Export(logger *Logger) {
	if logger != nil {
		gLogger = logger
	}
}

func Debug(format string, a ...interface{}) {
	gLogger.Debug(format, a...)
}

func Release(format string, a ...interface{}) {
	gLogger.Release(format, a...)
}

func Error(format string, a ...interface{}) {
	gLogger.Error(format, a...)
}

func Fatal(format string, a ...interface{}) {
	gLogger.Fatal(format, a...)
}

func Close() {
	gLogger.Close()
}
<commit_msg>log calling src and line number<commit_after>package log

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

// levels
const (
	debugLevel   = 0
	releaseLevel = 1
	errorLevel   = 2
	fatalLevel   = 3
)

const (
	printDebugLevel   = "[debug  ] "
	printReleaseLevel = "[release] "
	printErrorLevel   = "[error  ] "
	printFatalLevel   = "[fatal  ] "
)

type Logger struct {
	level      int
	baseLogger *log.Logger
	baseFile   *os.File
}

func New(strLevel string, pathname string) (*Logger, error) {
	// level
	var level int
	switch strings.ToLower(strLevel) {
	case "debug":
		level = debugLevel
	case "release":
		level = releaseLevel
	case "error":
		level = errorLevel
	case "fatal":
		level = fatalLevel
	default:
		return nil, errors.New("unknown level: " + strLevel)
	}

	// logger
	var baseLogger *log.Logger
	var baseFile *os.File
	if pathname != "" {
		now := time.Now()

		filename := fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d.log",
			now.Year(),
			now.Month(),
			now.Day(),
			now.Hour(),
			now.Minute(),
			now.Second())

		file, err := os.Create(path.Join(pathname, filename))
		if err != nil {
			return nil, err
		}

		baseLogger = log.New(file, "", log.LstdFlags)
		baseFile = file
	} else {
		baseLogger = log.New(os.Stdout, "", log.LstdFlags)
	}

	// new
	logger := new(Logger)
	logger.level = level
	logger.baseLogger = baseLogger
	logger.baseFile = baseFile

	return logger, nil
}

// It's dangerous to call the method on logging
func (logger *Logger) Close() {
	if logger.baseFile != nil {
		logger.baseFile.Close()
	}

	logger.baseLogger = nil
	logger.baseFile = nil
}

func (logger *Logger) doPrintf(level int, printLevel string, format string, a ...interface{}) {
	if level < logger.level {
		return
	}
	if logger.baseLogger == nil {
		panic("logger closed")
	}

	format = printLevel + format
	logger.baseLogger.Printf(format, a...)

	if level == fatalLevel {
		os.Exit(1)
	}
}

func (logger *Logger) Debug(format string, a ...interface{}) {
	// Determine caller func
	pc, _, lineno, ok := runtime.Caller(2)
	src := ""
	if ok {
		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
	}
	str := fmt.Sprintf(format, a...)

	logger.doPrintf(debugLevel, printDebugLevel, "[%s]%s", src, str)
}

func (logger *Logger) Release(format string, a ...interface{}) {
	// Determine caller func
	pc, _, lineno, ok := runtime.Caller(2)
	src := ""
	if ok {
		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
	}
	str := fmt.Sprintf(format, a...)
	logger.doPrintf(releaseLevel, printReleaseLevel, "[%s]%s", src, str)
}

func (logger *Logger) Error(format string, a ...interface{}) {
	// Determine caller func
	pc, _, lineno, ok := runtime.Caller(2)
	src := ""
	if ok {
		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
	}
	str := fmt.Sprintf(format, a...)
	logger.doPrintf(errorLevel, printErrorLevel, "[%s]%s", src, str)
}

func (logger *Logger) Fatal(format string, a ...interface{}) {
	// Determine caller func
	pc, _, lineno, ok := runtime.Caller(2)
	src := ""
	if ok {
		src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
	}
	str := fmt.Sprintf(format, a...)
	logger.doPrintf(fatalLevel, printFatalLevel, "[%s]%s", src, str)
}

var gLogger, _ = New("debug", "")

// It's dangerous to call the method on logging
func Export(logger *Logger) {
	if logger != nil {
		gLogger = logger
	}
}

func Debug(format string, a ...interface{}) {
	gLogger.Debug(format, a...)
}

func Release(format string, a ...interface{}) {
	gLogger.Release(format, a...)
}

func Error(format string, a ...interface{}) {
	gLogger.Error(format, a...)
}

func Fatal(format string, a ...interface{}) {
	gLogger.Fatal(format, a...)
}

func Close() {
	gLogger.Close()
}
<|endoftext|>


<commit_before>/* Copyright (c) 2016 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package log

import (
	"fmt"
	"github.com/mattn/go-isatty"
	"os"
	"path/filepath"
	"runtime"
	"time"
)

type LogLevel int

const (
	ERROR LogLevel = iota
	WARNING
	NOTICE
	INFO
	DEBUG
)

var logLevel LogLevel = INFO

const (
	GREEN   = "\x1b[32m"
	BLUE    = "\x1b[34m"
	YELLOW  = "\x1b[33m"
	YELLOWB = "\x1b[1;33m"
	RED     = "\x1b[31m"
	//REDB    = "\x1b[1;31m"
	ORANGE = "\x1b[38;5;208m"
	RESET  = "\x1b[0m"
)

type ColorPrinter func(v interface{}) string

var Green ColorPrinter = noColorPrinter
var Blue ColorPrinter = noColorPrinter
var Red ColorPrinter = noColorPrinter
var Yellow ColorPrinter = noColorPrinter
var YellowB ColorPrinter = noColorPrinter
var Orange ColorPrinter = noColorPrinter

func init() {
	doColor := false
	if runtime.GOOS == "windows" {
		if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
			doColor = true
		}
	} else if isatty.IsTerminal(os.Stdout.Fd()) {
		doColor = true
	}

	if doColor {
		// Register color printers.
		Green = _Green
		Blue = _Blue
		Yellow = _Yellow
		YellowB = _YellowB
		Red = _Red
		Orange = _Orange
	}
}

func noColorPrinter(v interface{}) string {
	return fmt.Sprintf("%v", v)
}

func _Green(v interface{}) string {
	return fmt.Sprintf("%s%v%s", GREEN, v, RESET)
}

func _Blue(v interface{}) string {
	return fmt.Sprintf("%s%v%s", BLUE, v, RESET)
}

func _Yellow(v interface{}) string {
	return fmt.Sprintf("%s%v%s", YELLOW, v, RESET)
}

func _YellowB(v interface{}) string {
	return fmt.Sprintf("%s%v%s", YELLOWB, v, RESET)
}

func _Red(v interface{}) string {
	return fmt.Sprintf("%s%v%s", RED, v, RESET)
}

func _Orange(v interface{}) string {
	return fmt.Sprintf("%s%v%s", ORANGE, v, RESET)
}

func Timestamp() string {
	now := time.Now()
	return now.Format("2006-01-02 15:04:05")
}

func SetLevel(level LogLevel) {
	logLevel = level
}

func GetLevel() LogLevel {
	return logLevel
}

func doLog(calldepth int, level LogLevel, format string, v ...interface{}) {

	if level > logLevel {
		return
	}

	_, filename, line, _ := runtime.Caller(calldepth)

	if level == ERROR {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			Red("Error"),
			Red(fmt.Sprintf(format, v...)))
	}

	if level == WARNING {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			Orange("Warning"),
			Orange(fmt.Sprintf(format, v...)))
	}

	if level == NOTICE {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			YellowB("Notice"),
			fmt.Sprintf(format, v...))
	}

	if level == INFO {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			Blue("Info"),
			fmt.Sprintf(format, v...))
	}

	if level == DEBUG {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			Yellow("Debug"),
			fmt.Sprintf(format, v...))
	}
}

func Error(format string, v ...interface{}) {
	doLog(2, ERROR, format, v...)
}

func Warning(format string, v ...interface{}) {
	doLog(2, WARNING, format, v...)
}

func Notice(format string, v ...interface{}) {
	doLog(2, NOTICE, format, v...)
}

func Info(format string, v ...interface{}) {
	doLog(2, INFO, format, v...)
}

func Debug(format string, v ...interface{}) {
	doLog(2, DEBUG, format, v...)
}

// Promote to info...
func Println(v ...interface{}) {
	doLog(2, INFO, "%s", fmt.Sprint(v...))
}

// To be compatible with standard logging, promote to info.
func Printf(format string, v ...interface{}) {
	doLog(2, INFO, format, v...)
}

func Fatal(v ...interface{}) {
	doLog(2, ERROR, "%s", fmt.Sprint(v...))
	os.Exit(1)
}

func Fatalf(format string, v ...interface{}) {
	doLog(2, ERROR, format, v...)
	os.Exit(1)
}
<commit_msg>logging: InfoWithFields... takes a map of fields<commit_after>/* Copyright (c) 2016 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package log

import (
	"fmt"
	"github.com/mattn/go-isatty"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

type LogLevel int

const (
	ERROR LogLevel = iota
	WARNING
	NOTICE
	INFO
	DEBUG
)

type Fields map[string]interface{}

var logLevel LogLevel = INFO

const (
	GREEN   = "\x1b[32m"
	BLUE    = "\x1b[34m"
	YELLOW  = "\x1b[33m"
	YELLOWB = "\x1b[1;33m"
	RED     = "\x1b[31m"
	//REDB    = "\x1b[1;31m"
	ORANGE = "\x1b[38;5;208m"
	RESET  = "\x1b[0m"
)

type ColorPrinter func(v interface{}) string

var Green ColorPrinter = noColorPrinter
var Blue ColorPrinter = noColorPrinter
var Red ColorPrinter = noColorPrinter
var Yellow ColorPrinter = noColorPrinter
var YellowB ColorPrinter = noColorPrinter
var Orange ColorPrinter = noColorPrinter

func init() {
	doColor := false
	if runtime.GOOS == "windows" {
		if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
			doColor = true
		}
	} else if isatty.IsTerminal(os.Stdout.Fd()) {
		doColor = true
	}

	if doColor {
		// Register color printers.
		Green = _Green
		Blue = _Blue
		Yellow = _Yellow
		YellowB = _YellowB
		Red = _Red
		Orange = _Orange
	}
}

func noColorPrinter(v interface{}) string {
	return fmt.Sprintf("%v", v)
}

func _Green(v interface{}) string {
	return fmt.Sprintf("%s%v%s", GREEN, v, RESET)
}

func _Blue(v interface{}) string {
	return fmt.Sprintf("%s%v%s", BLUE, v, RESET)
}

func _Yellow(v interface{}) string {
	return fmt.Sprintf("%s%v%s", YELLOW, v, RESET)
}

func _YellowB(v interface{}) string {
	return fmt.Sprintf("%s%v%s", YELLOWB, v, RESET)
}

func _Red(v interface{}) string {
	return fmt.Sprintf("%s%v%s", RED, v, RESET)
}

func _Orange(v interface{}) string {
	return fmt.Sprintf("%s%v%s", ORANGE, v, RESET)
}

func Timestamp() string {
	now := time.Now()
	return now.Format("2006-01-02 15:04:05")
}

func SetLevel(level LogLevel) {
	logLevel = level
}

func GetLevel() LogLevel {
	return logLevel
}

func doLog(calldepth int, level LogLevel, format string, v ...interface{}) {

	if level > logLevel {
		return
	}

	_, filename, line, _ := runtime.Caller(calldepth)

	if level == ERROR {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			Red("Error"),
			Red(fmt.Sprintf(format, v...)))
	}

	if level == WARNING {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			Orange("Warning"),
			Orange(fmt.Sprintf(format, v...)))
	}

	if level == NOTICE {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			YellowB("Notice"),
			fmt.Sprintf(format, v...))
	}

	if level == INFO {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			Blue("Info"),
			fmt.Sprintf(format, v...))
	}

	if level == DEBUG {
		fmt.Fprintf(os.Stderr, "%s (%s:%s) <%s> -- %s\n",
			Green(Timestamp()),
			Blue(filepath.Base(filename)),
			Green(line),
			Yellow("Debug"),
			fmt.Sprintf(format, v...))
	}
}

func doLogWithFields(calldepth int, level LogLevel, fields Fields, format string, v ...interface{}) {
	if level > logLevel {
		return
	}
	msg := fmt.Sprintf(format, v...)
	doLog(calldepth, level, "%s -- %s", msg, formatFields(fields))
}

func Error(format string, v ...interface{}) {
	doLog(2, ERROR, format, v...)
}

func Warning(format string, v ...interface{}) {
	doLog(2, WARNING, format, v...)
}

func Notice(format string, v ...interface{}) {
	doLog(2, NOTICE, format, v...)
}

func Info(format string, v ...interface{}) {
	doLog(2, INFO, format, v...)
}

func InfoWithFields(fields Fields, format string, v ...interface{}) {
	doLogWithFields(3, INFO, fields, format, v...)
}

func formatFields(fields Fields) string {
	parts := []string{}
	for key, val := range fields {
		valstr := fmt.Sprintf("%v", val)
		if strings.Index(valstr, " ") > -1 {
			parts = append(parts,
				fmt.Sprintf("%s=\"%s\"", key, val))
		} else {
			parts = append(parts,
				fmt.Sprintf("%s=%s", key, val))
		}
	}
	return strings.Join(parts, " ")
}

func Debug(format string, v ...interface{}) {
	doLog(2, DEBUG, format, v...)
}

// Promote to info...
func Println(v ...interface{}) {
	doLog(2, INFO, "%s", fmt.Sprint(v...))
}

// To be compatible with standard logging, promote to info.
func Printf(format string, v ...interface{}) {
	doLog(2, INFO, format, v...)
}

func Fatal(v ...interface{}) {
	doLog(2, ERROR, "%s", fmt.Sprint(v...))
	os.Exit(1)
}

func Fatalf(format string, v ...interface{}) {
	doLog(2, ERROR, format, v...)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	Unknown = iota
	GO_1_0
	GO_1_1
)

var (
	regexes = map[int]*regexp.Regexp{
		GO_1_0: regexp.MustCompile(`gc(\d+)\((\d+)\):\s(\d+)\+(\d+)\+(\d+)\s\w+\s(\d+)\s->\s(\d+)\s\w+\s(\d+)\s->\s(\d+)\s\((\d+)-(\d+)\)\sobjects\s(\d+)\shandoff`),
		GO_1_1: regexp.MustCompile(`gc(\d+)\((\d+)\):\s(\d+)\+(\d+)\+(\d+)\s\w+,\s(\d+)\s->\s(\d+)\s\w+\s(\d+)\s->\s(\d+)\s\((\d+)-(\d+)\)\sobjects,\s(\d+)\((\d+)\)\shandoff,\s(\d+)\((\d+)\)\ssteal,\s(\d+)\/(\d+)\/(\d+)\syields`),
	}
	header = map[int]string{
		GO_1_0: "numgc,nproc,mark,sweep,cleanup,heap0,heap1,obj0,obj1,nmalloc,nfree,nhandoff",
		GO_1_1: "numgc,nproc,mark,sweep,cleanup,heap0,heap1,obj0,obj1,nmalloc,nfree,nhandoff,nhandoffcnt,nsteal,nstealcnt,nprocyield,nosyield,nsleep",
	}

	errVersionNotFound = errors.New("can't detected version")

	inputFile, outputFile string
	timestamp             bool
	isStdin               = false
)

func init() {
	flag.StringVar(&inputFile, "i", "", "The input file (default: Stdin)")
	flag.StringVar(&outputFile, "o", "", "The output file")
	flag.BoolVar(&timestamp, "t", false, "Add timestamp at line head(Stdin input only)")

	flag.Usage = func() {
		fmt.Println("Usage1: log2csv -i gc.log -o gc.csv")
		fmt.Println("Usage2: GOGCTRACE=1 your-go-program 2>&1 | log2csv -o gc.csv")
		flag.PrintDefaults()
		fmt.Println("  -h   : show help usage")
	}
}

func detectLogVersion(line string) (version int, err error) {
	for version, regexp := range regexes {
		if regexp.MatchString(line) {
			return version, nil
		}
	}

	err = errVersionNotFound
	return
}

func convert(input string, version int) (output string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprintf("unmatched uint string: %s => %s", input, e))
		}
	}()

	if matched := regexes[version].FindStringSubmatch(input); matched == nil {
		err = errors.New(fmt.Sprintf("unmatched string: %s", input))
	} else {
		output = strings.Join(matched[1:], ",")
	}
	return
}

func process(reader io.Reader, writer io.Writer) {
	bufReader := bufio.NewReader(reader)
	bufWriter := bufio.NewWriter(writer)

	currentLogVersion := Unknown
	for {

		filtered := false
		if line, err := bufReader.ReadString('\n'); err != nil {
			break
		} else {
			if currentLogVersion == Unknown {
				if version, err := detectLogVersion(line); err == nil {
					currentLogVersion = version
					writeHeader(bufWriter, currentLogVersion)
				}

			}

			if currentLogVersion != Unknown {
				if output, err := convert(line, currentLogVersion); err == nil {
					writeBody(bufWriter, output)
					filtered = true
				}
			}

			if isStdin && filtered == false {
				fmt.Print(line)
			}
		}

	}
	bufWriter.Flush()
}

func writeHeader(writer *bufio.Writer, version int) {
	prefix := ""
	if isStdin && timestamp {
		prefix = "unixtime,"
	}
	writer.WriteString(prefix + header[version] + "\n")
}

func writeBody(writer *bufio.Writer, output string) {
	prefix := ""
	if isStdin && timestamp {
		prefix = fmtFrac(time.Now(), 6) + ","
	}

	writer.WriteString(prefix + output + "\n")
}

func fmtFrac(t time.Time, prec int) string {
	unixNano := t.UnixNano()
	fmtStr := "%." + strconv.Itoa(prec) + "f"
	return fmt.Sprintf(fmtStr, float64(unixNano)/10e8)
}

func getReader(file string) (reader io.Reader, err error) {
	if file == "" {
		reader = os.Stdin
		isStdin = true
	} else {
		reader, err = os.Open(file)
	}
	return
}

func getWriter(file string) (writer io.Writer, err error) {
	if file == "" {
		err = errors.New("required output file parameter")
	} else {
		writer, err = os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	}
	return
}

func checkError(err error) {
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		flag.Usage()
		os.Exit(-1)
	}
}

func main() {
	flag.Parse()

	reader, err := getReader(inputFile)
	checkError(err)
	defer reader.(*os.File).Close()

	writer, err := getWriter(outputFile)
	checkError(err)
	defer writer.(*os.File).Close()

	process(reader, writer)
}
<commit_msg>fixed unchecked errors<commit_after>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	Unknown = iota
	GO_1_0
	GO_1_1
)

var (
	regexes = map[int]*regexp.Regexp{
		GO_1_0: regexp.MustCompile(`gc(\d+)\((\d+)\):\s(\d+)\+(\d+)\+(\d+)\s\w+\s(\d+)\s->\s(\d+)\s\w+\s(\d+)\s->\s(\d+)\s\((\d+)-(\d+)\)\sobjects\s(\d+)\shandoff`),
		GO_1_1: regexp.MustCompile(`gc(\d+)\((\d+)\):\s(\d+)\+(\d+)\+(\d+)\s\w+,\s(\d+)\s->\s(\d+)\s\w+\s(\d+)\s->\s(\d+)\s\((\d+)-(\d+)\)\sobjects,\s(\d+)\((\d+)\)\shandoff,\s(\d+)\((\d+)\)\ssteal,\s(\d+)\/(\d+)\/(\d+)\syields`),
	}
	header = map[int]string{
		GO_1_0: "numgc,nproc,mark,sweep,cleanup,heap0,heap1,obj0,obj1,nmalloc,nfree,nhandoff",
		GO_1_1: "numgc,nproc,mark,sweep,cleanup,heap0,heap1,obj0,obj1,nmalloc,nfree,nhandoff,nhandoffcnt,nsteal,nstealcnt,nprocyield,nosyield,nsleep",
	}

	errVersionNotFound = errors.New("can't detected version")

	inputFile, outputFile string
	timestamp             bool
	isStdin               = false
)

func init() {
	flag.StringVar(&inputFile, "i", "", "The input file (default: Stdin)")
	flag.StringVar(&outputFile, "o", "", "The output file")
	flag.BoolVar(&timestamp, "t", false, "Add timestamp at line head(Stdin input only)")

	flag.Usage = func() {
		fmt.Println("Usage1: log2csv -i gc.log -o gc.csv")
		fmt.Println("Usage2: GOGCTRACE=1 your-go-program 2>&1 | log2csv -o gc.csv")
		flag.PrintDefaults()
		fmt.Println("  -h   : show help usage")
	}
}

func detectLogVersion(line string) (version int, err error) {
	for version, regexp := range regexes {
		if regexp.MatchString(line) {
			return version, nil
		}
	}

	err = errVersionNotFound
	return
}

func convert(input string, version int) (output string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprintf("unmatched uint string: %s => %s", input, e))
		}
	}()

	if matched := regexes[version].FindStringSubmatch(input); matched == nil {
		err = errors.New(fmt.Sprintf("unmatched string: %s", input))
	} else {
		output = strings.Join(matched[1:], ",")
	}
	return
}

func process(reader io.Reader, writer io.Writer) {
	bufReader := bufio.NewReader(reader)
	bufWriter := bufio.NewWriter(writer)

	currentLogVersion := Unknown
	for {

		filtered := false
		if line, err := bufReader.ReadString('\n'); err != nil {
			break
		} else {
			if currentLogVersion == Unknown {
				if version, err := detectLogVersion(line); err == nil {
					currentLogVersion = version
					err := writeHeader(bufWriter, currentLogVersion)
					checkError(err)
				}

			}

			if currentLogVersion != Unknown {
				if output, err := convert(line, currentLogVersion); err == nil {
					err := writeBody(bufWriter, output)
					checkError(err)
					filtered = true
				}
			}

			if isStdin && filtered == false {
				fmt.Print(line)
			}
		}

	}
	err := bufWriter.Flush()
	checkError(err)
}

func writeHeader(writer *bufio.Writer, version int) (err error) {
	prefix := ""
	if isStdin && timestamp {
		prefix = "unixtime,"
	}
	_, err = writer.WriteString(prefix + header[version] + "\n")
	return
}

func writeBody(writer *bufio.Writer, output string) (err error) {
	prefix := ""
	if isStdin && timestamp {
		prefix = fmtFrac(time.Now(), 6) + ","
	}

	_, err = writer.WriteString(prefix + output + "\n")
	return
}

func fmtFrac(t time.Time, prec int) string {
	unixNano := t.UnixNano()
	fmtStr := "%." + strconv.Itoa(prec) + "f"
	return fmt.Sprintf(fmtStr, float64(unixNano)/10e8)
}

func getReader(file string) (reader io.Reader, err error) {
	if file == "" {
		reader = os.Stdin
		isStdin = true
	} else {
		reader, err = os.Open(file)
	}
	return
}

func getWriter(file string) (writer io.Writer, err error) {
	if file == "" {
		err = errors.New("required output file parameter")
	} else {
		writer, err = os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	}
	return
}

func checkError(err error) {
	if err != nil {
		fmt.Fprint(os.Stderr, err)
		os.Exit(-1)
	}
}

func main() {
	flag.Parse()

	reader, err := getReader(inputFile)
	checkError(err)
	defer reader.(*os.File).Close()

	writer, err := getWriter(outputFile)
	checkError(err)
	defer writer.(*os.File).Close()

	process(reader, writer)
}
<|endoftext|>


<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to discordgo package logging

package discordgo

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"runtime"
	"strings"
)

const (
	LogError int = iota
	LogWarning
	LogNotice
	LogDebug
)

// logs messages to stderr
func msglog(cfgL, msgL int, format string, a ...interface{}) {

	if msgL > cfgL {
		return
	}

	pc, file, line, _ := runtime.Caller(1)

	files := strings.Split(file, "/")
	file = files[len(files)-1]

	name := runtime.FuncForPC(pc).Name()
	fns := strings.Split(name, ".")
	name = fns[len(fns)-1]

	msg := fmt.Sprintf(format, a...)

	log.Printf("%s:%d:%s %s\n", file, line, name, msg)
}

// helper function that wraps msglog for the Session struct
func (s *Session) log(msgL int, format string, a ...interface{}) {

	if s.Debug { // Deprecated
		s.LogLevel = LogDebug
	}
	msglog(s.LogLevel, msgL, format, a...)
}

// helper function that wraps msglog for the VoiceConnection struct
func (v *VoiceConnection) log(msgL int, format string, a ...interface{}) {

	if v.Debug { // Deprecated
		v.LogLevel = LogDebug
	}

	msglog(v.LogLevel, msgL, format, a...)
}

// printEvent prints out a WSAPI event.
func printEvent(e *Event) {
	log.Println(fmt.Sprintf("Event. Type: %s, State: %d Operation: %d Direction: %d", e.Type, e.State, e.Operation, e.Direction))
	printJSON(e.RawData)
}

// printJSON is a helper function to display JSON data in a easy to read format.
func printJSON(body []byte) {
	var prettyJSON bytes.Buffer
	error := json.Indent(&prettyJSON, body, "", "\t")
	if error != nil {
		log.Print("JSON parse error: ", error)
	}
	log.Println(string(prettyJSON.Bytes()))
}
<commit_msg>Comment log levels.<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to discordgo package logging

package discordgo

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"runtime"
	"strings"
)

const (

	// Logs critical errors that can lead to data loss or panic
	LogError int = iota

	// Logs very abnormal events
	LogWarning

	// Logs normal basic activity like connect/disconnects
	LogInformational

	// Logs detailed activity including all HTTP/Websocket packets.
	LogDebug
)

// logs messages to stderr
func msglog(cfgL, msgL int, format string, a ...interface{}) {

	if msgL > cfgL {
		return
	}

	pc, file, line, _ := runtime.Caller(1)

	files := strings.Split(file, "/")
	file = files[len(files)-1]

	name := runtime.FuncForPC(pc).Name()
	fns := strings.Split(name, ".")
	name = fns[len(fns)-1]

	msg := fmt.Sprintf(format, a...)

	log.Printf("%s:%d:%s %s\n", file, line, name, msg)
}

// helper function that wraps msglog for the Session struct
func (s *Session) log(msgL int, format string, a ...interface{}) {

	if s.Debug { // Deprecated
		s.LogLevel = LogDebug
	}
	msglog(s.LogLevel, msgL, format, a...)
}

// helper function that wraps msglog for the VoiceConnection struct
func (v *VoiceConnection) log(msgL int, format string, a ...interface{}) {

	if v.Debug { // Deprecated
		v.LogLevel = LogDebug
	}

	msglog(v.LogLevel, msgL, format, a...)
}

// printEvent prints out a WSAPI event.
func printEvent(e *Event) {
	log.Println(fmt.Sprintf("Event. Type: %s, State: %d Operation: %d Direction: %d", e.Type, e.State, e.Operation, e.Direction))
	printJSON(e.RawData)
}

// printJSON is a helper function to display JSON data in a easy to read format.
func printJSON(body []byte) {
	var prettyJSON bytes.Buffer
	error := json.Indent(&prettyJSON, body, "", "\t")
	if error != nil {
		log.Print("JSON parse error: ", error)
	}
	log.Println(string(prettyJSON.Bytes()))
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/go-kit/kit/log"

	"github.com/marcusolsson/pathfinder/path"
)

type loggingService struct {
	logger log.Logger
	PathService
}

func (s loggingService) ShortestPath(origin, destination string) (paths []path.TransitPath, err error) {
	defer func(begin time.Time) {
		_ = mw.logger.Log(
			"method", "shortest_path",
			"origin", origin,
			"destination", destination,
			"took", time.Since(begin),
			"err", err,
		)
	}(time.Now())
	return s.PathService.ShortestPath(origin, destination)
}
<commit_msg>Fix failing build<commit_after>package main

import (
	"time"

	"github.com/go-kit/kit/log"

	"github.com/marcusolsson/pathfinder/path"
)

type loggingService struct {
	logger log.Logger
	PathService
}

func (s loggingService) ShortestPath(origin, destination string) (paths []path.TransitPath, err error) {
	defer func(begin time.Time) {
		_ = s.logger.Log(
			"method", "shortest_path",
			"origin", origin,
			"destination", destination,
			"took", time.Since(begin),
			"err", err,
		)
	}(time.Now())
	return s.PathService.ShortestPath(origin, destination)
}
<|endoftext|>