<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 cmd

import (
	"bytes"
	"errors"
	"fmt"
	"net/url"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/spf13/cobra"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/kapi"
	"k8s.io/minikube/pkg/minikube/browser"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/mustload"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/reason"
	"k8s.io/minikube/pkg/minikube/service"
	"k8s.io/minikube/pkg/minikube/style"
	"k8s.io/minikube/pkg/minikube/tunnel/kic"
)

const defaultServiceFormatTemplate = "http://{{.IP}}:{{.Port}}"

var (
	namespace          string
	all                bool
	https              bool
	serviceURLMode     bool
	serviceURLFormat   string
	serviceURLTemplate *template.Template
	wait               int
	interval           int
)

// serviceCmd represents the service command
var serviceCmd = &cobra.Command{
	Use:   "service [flags] SERVICE",
	Short: "Returns a URL to connect to a service",
	Long:  `Returns the Kubernetes URL(s) for service(s) in your local cluster. In the case of multiple URLs they will be printed one at a time.`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		t, err := template.New("serviceURL").Parse(serviceURLFormat)
		if err != nil {
			exit.Error(reason.InternalFormatUsage, "The value passed to --format is invalid", err)
		}
		serviceURLTemplate = t

		RootCmd.PersistentPreRun(cmd, args)
	},
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 && !all || (len(args) > 0 && all) {
			exit.Message(reason.Usage, "You must specify service name(s) or --all")
		}

		svcArgs := make(map[string]bool)
		for _, v := range args {
			svcArgs[v] = true
		}

		cname := ClusterFlagValue()
		co := mustload.Healthy(cname)

		// Bail cleanly for qemu2 until implemented
		if driver.IsQEMU(co.Config.Driver) {
			exit.Message(reason.Unimplemented, "minikube service is not currently implemented with the qemu2 driver. See https://github.com/kubernetes/minikube/issues/14146 for details.")
		}

		var services service.URLs
		services, err := service.GetServiceURLs(co.API, co.Config.Name, namespace, serviceURLTemplate)
		if err != nil {
			out.FatalT("Failed to get service URL: {{.error}}", out.V{"error": err})
			out.ErrT(style.Notice, "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.")
			os.Exit(reason.ExSvcUnavailable)
		}

		if len(args) >= 1 {
			var newServices service.URLs
			for _, svc := range services {
				if _, ok := svcArgs[svc.Name]; ok {
					newServices = append(newServices, svc)
				}
			}
			services = newServices
		}

		if len(services) == 0 {
			exit.Message(reason.SvcNotFound, `Service '{{.service}}' was not found in '{{.namespace}}' namespace.
You may select another namespace by using 'minikube service {{.service}} -n <namespace>'. Or list out all the services using 'minikube service list'`, out.V{"service": args[0], "namespace": namespace})
		}

		var data [][]string
		for _, svc := range services {
			openUrls, err := service.WaitForService(co.API, co.Config.Name, namespace, svc.Name, serviceURLTemplate, serviceURLMode, https, wait, interval)

			if err != nil {
				var s *service.SVCNotFoundError
				if errors.As(err, &s) {
					exit.Message(reason.SvcNotFound, `Service '{{.service}}' was not found in '{{.namespace}}' namespace.
You may select another namespace by using 'minikube service {{.service}} -n <namespace>'. Or list out all the services using 'minikube service list'`, out.V{"service": svc, "namespace": namespace})
				}
				exit.Error(reason.SvcTimeout, "Error opening service", err)
			}

			if len(openUrls) == 0 {
				data = append(data, []string{svc.Namespace, svc.Name, "No node port"})
			} else {
				servicePortNames := strings.Join(svc.PortNames, "\n")
				serviceURLs := strings.Join(openUrls, "\n")

				// if we are running Docker on OSX we empty the internal service URLs
				if runtime.GOOS == "darwin" && co.Config.Driver == oci.Docker {
					serviceURLs = ""
				}

				data = append(data, []string{svc.Namespace, svc.Name, servicePortNames, serviceURLs})

				if serviceURLMode && !driver.NeedsPortForward(co.Config.Driver) {
					out.String(fmt.Sprintf("%s\n", serviceURLs))
				}
			}
		}

		if driver.NeedsPortForward(co.Config.Driver) && driver.IsKIC(co.Config.Driver) && services != nil {
			startKicServiceTunnel(services, cname, co.Config.Driver)
		} else if driver.NeedsPortForward(co.Config.Driver) && driver.IsQEMU(co.Config.Driver) && services != nil {
			startQemuServiceTunnel(services, cname, co.Config.Driver)
		} else if !serviceURLMode {
			openURLs(data)
		}
	},
}

func init() {
	serviceCmd.Flags().StringVarP(&namespace, "namespace", "n", "default", "The service namespace")
	serviceCmd.Flags().BoolVar(&serviceURLMode, "url", false, "Display the Kubernetes service URL in the CLI instead of opening it in the default browser")
	serviceCmd.Flags().BoolVar(&all, "all", false, "Forwards all services in a namespace (defaults to \"false\")")
	serviceCmd.Flags().BoolVar(&https, "https", false, "Open the service URL with https instead of http (defaults to \"false\")")
	serviceCmd.Flags().IntVar(&wait, "wait", service.DefaultWait, "Amount of time to wait for a service in seconds")
	serviceCmd.Flags().IntVar(&interval, "interval", service.DefaultInterval, "The initial time interval for each check that wait performs in seconds")

	serviceCmd.PersistentFlags().StringVar(&serviceURLFormat, "format", defaultServiceFormatTemplate, "Format to output service URL in. This format will be applied to each url individually and they will be printed one at a time.")
}

func startKicServiceTunnel(services service.URLs, configName, driverName string) {
	ctrlC := make(chan os.Signal, 1)
	signal.Notify(ctrlC, os.Interrupt)

	clientset, err := kapi.Client(configName)
	if err != nil {
		exit.Error(reason.InternalKubernetesClient, "error creating clientset", err)
	}

	var data [][]string
	for _, svc := range services {
		port, err := oci.ForwardedPort(driverName, configName, 22)
		if err != nil {
			exit.Error(reason.DrvPortForward, "error getting ssh port", err)
		}
		sshPort := strconv.Itoa(port)
		sshKey := filepath.Join(localpath.MiniPath(), "machines", configName, "id_rsa")

		serviceTunnel := kic.NewServiceTunnel(sshPort, sshKey, clientset.CoreV1(), serviceURLMode)
		urls, err := serviceTunnel.Start(svc.Name, namespace)

		if err != nil {
			exit.Error(reason.SvcTunnelStart, "error starting tunnel", err)
		}
		// mutate response urls to HTTPS if needed
		urls, err = mutateURLs(svc.Name, urls)

		if err != nil {
			exit.Error(reason.SvcTunnelStart, "error creatings urls", err)
		}

		defer serviceTunnel.Stop()
		svc.URLs = urls
		data = append(data, []string{namespace, svc.Name, "", strings.Join(urls, "\n")})
	}

	time.Sleep(1 * time.Second)

	if !serviceURLMode {
		service.PrintServiceList(os.Stdout, data)
	} else {
		for _, row := range data {
			out.String(fmt.Sprintf("%s\n", row[3]))
		}
	}

	if !serviceURLMode {
		openURLs(data)
	}

	out.WarningT("Because you are using a Docker driver on {{.operating_system}}, the terminal needs to be open to run it.", out.V{"operating_system": runtime.GOOS})

	<-ctrlC
}

func startQemuServiceTunnel(services service.URLs, configName, driverName string) {
}

func mutateURLs(serviceName string, urls []string) ([]string, error) {
	formattedUrls := make([]string, 0)
	for _, rawURL := range urls {
		var doc bytes.Buffer
		parsedURL, err := url.Parse(rawURL)
		if err != nil {
			exit.Error(reason.SvcTunnelStart, "No valid URL found for tunnel.", err)
		}
		port, err := strconv.Atoi(parsedURL.Port())
		if err != nil {
			exit.Error(reason.SvcTunnelStart, "No valid port found for tunnel.", err)
		}
		err = serviceURLTemplate.Execute(&doc, struct {
			IP   string
			Port int32
			Name string
		}{
			parsedURL.Hostname(),
			int32(port),
			serviceName,
		})

		if err != nil {
			return nil, err
		}

		httpsURL, _ := service.OptionallyHTTPSFormattedURLString(doc.String(), https)
		formattedUrls = append(formattedUrls, httpsURL)
	}

	return formattedUrls, nil
}

func openURLs(urls [][]string) {
	for _, u := range urls {

		if len(u) < 4{
			klog.Warning("No URL found")
			continue
		}

		_, err := url.Parse(u[3])
		if err != nil {
			klog.Warningf("failed to parse url %q: %v (will not open)", u[3], err)
			out.String(fmt.Sprintf("%s\n", u))
			continue
		}

		if serviceURLMode {
			out.String(fmt.Sprintf("%s\n", u))
			continue
		}

		out.Styled(style.Celebrate, "Opening service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": u[1]})
		if err := browser.OpenURL(u[3]); err != nil {
			exit.Error(reason.HostBrowser, fmt.Sprintf("open url failed: %s", u), err)
		}
	}
}
<commit_msg>fix linting<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 cmd

import (
	"bytes"
	"errors"
	"fmt"
	"net/url"
	"os"
	"os/signal"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/spf13/cobra"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/kapi"
	"k8s.io/minikube/pkg/minikube/browser"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/exit"
	"k8s.io/minikube/pkg/minikube/localpath"
	"k8s.io/minikube/pkg/minikube/mustload"
	"k8s.io/minikube/pkg/minikube/out"
	"k8s.io/minikube/pkg/minikube/reason"
	"k8s.io/minikube/pkg/minikube/service"
	"k8s.io/minikube/pkg/minikube/style"
	"k8s.io/minikube/pkg/minikube/tunnel/kic"
)

const defaultServiceFormatTemplate = "http://{{.IP}}:{{.Port}}"

var (
	namespace          string
	all                bool
	https              bool
	serviceURLMode     bool
	serviceURLFormat   string
	serviceURLTemplate *template.Template
	wait               int
	interval           int
)

// serviceCmd represents the service command
var serviceCmd = &cobra.Command{
	Use:   "service [flags] SERVICE",
	Short: "Returns a URL to connect to a service",
	Long:  `Returns the Kubernetes URL(s) for service(s) in your local cluster. In the case of multiple URLs they will be printed one at a time.`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		t, err := template.New("serviceURL").Parse(serviceURLFormat)
		if err != nil {
			exit.Error(reason.InternalFormatUsage, "The value passed to --format is invalid", err)
		}
		serviceURLTemplate = t

		RootCmd.PersistentPreRun(cmd, args)
	},
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 && !all || (len(args) > 0 && all) {
			exit.Message(reason.Usage, "You must specify service name(s) or --all")
		}

		svcArgs := make(map[string]bool)
		for _, v := range args {
			svcArgs[v] = true
		}

		cname := ClusterFlagValue()
		co := mustload.Healthy(cname)

		// Bail cleanly for qemu2 until implemented
		if driver.IsQEMU(co.Config.Driver) {
			exit.Message(reason.Unimplemented, "minikube service is not currently implemented with the qemu2 driver. See https://github.com/kubernetes/minikube/issues/14146 for details.")
		}

		var services service.URLs
		services, err := service.GetServiceURLs(co.API, co.Config.Name, namespace, serviceURLTemplate)
		if err != nil {
			out.FatalT("Failed to get service URL: {{.error}}", out.V{"error": err})
			out.ErrT(style.Notice, "Check that minikube is running and that you have specified the correct namespace (-n flag) if required.")
			os.Exit(reason.ExSvcUnavailable)
		}

		if len(args) >= 1 {
			var newServices service.URLs
			for _, svc := range services {
				if _, ok := svcArgs[svc.Name]; ok {
					newServices = append(newServices, svc)
				}
			}
			services = newServices
		}

		if len(services) == 0 {
			exit.Message(reason.SvcNotFound, `Service '{{.service}}' was not found in '{{.namespace}}' namespace.
You may select another namespace by using 'minikube service {{.service}} -n <namespace>'. Or list out all the services using 'minikube service list'`, out.V{"service": args[0], "namespace": namespace})
		}

		var data [][]string
		for _, svc := range services {
			openUrls, err := service.WaitForService(co.API, co.Config.Name, namespace, svc.Name, serviceURLTemplate, serviceURLMode, https, wait, interval)

			if err != nil {
				var s *service.SVCNotFoundError
				if errors.As(err, &s) {
					exit.Message(reason.SvcNotFound, `Service '{{.service}}' was not found in '{{.namespace}}' namespace.
You may select another namespace by using 'minikube service {{.service}} -n <namespace>'. Or list out all the services using 'minikube service list'`, out.V{"service": svc, "namespace": namespace})
				}
				exit.Error(reason.SvcTimeout, "Error opening service", err)
			}

			if len(openUrls) == 0 {
				data = append(data, []string{svc.Namespace, svc.Name, "No node port"})
			} else {
				servicePortNames := strings.Join(svc.PortNames, "\n")
				serviceURLs := strings.Join(openUrls, "\n")

				// if we are running Docker on OSX we empty the internal service URLs
				if runtime.GOOS == "darwin" && co.Config.Driver == oci.Docker {
					serviceURLs = ""
				}

				data = append(data, []string{svc.Namespace, svc.Name, servicePortNames, serviceURLs})

				if serviceURLMode && !driver.NeedsPortForward(co.Config.Driver) {
					out.String(fmt.Sprintf("%s\n", serviceURLs))
				}
			}
		}

		if driver.NeedsPortForward(co.Config.Driver) && driver.IsKIC(co.Config.Driver) && services != nil {
			startKicServiceTunnel(services, cname, co.Config.Driver)
		} else if driver.NeedsPortForward(co.Config.Driver) && driver.IsQEMU(co.Config.Driver) && services != nil {
			startQemuServiceTunnel(services, cname, co.Config.Driver)
		} else if !serviceURLMode {
			openURLs(data)
		}
	},
}

func init() {
	serviceCmd.Flags().StringVarP(&namespace, "namespace", "n", "default", "The service namespace")
	serviceCmd.Flags().BoolVar(&serviceURLMode, "url", false, "Display the Kubernetes service URL in the CLI instead of opening it in the default browser")
	serviceCmd.Flags().BoolVar(&all, "all", false, "Forwards all services in a namespace (defaults to \"false\")")
	serviceCmd.Flags().BoolVar(&https, "https", false, "Open the service URL with https instead of http (defaults to \"false\")")
	serviceCmd.Flags().IntVar(&wait, "wait", service.DefaultWait, "Amount of time to wait for a service in seconds")
	serviceCmd.Flags().IntVar(&interval, "interval", service.DefaultInterval, "The initial time interval for each check that wait performs in seconds")

	serviceCmd.PersistentFlags().StringVar(&serviceURLFormat, "format", defaultServiceFormatTemplate, "Format to output service URL in. This format will be applied to each url individually and they will be printed one at a time.")
}

func startKicServiceTunnel(services service.URLs, configName, driverName string) {
	ctrlC := make(chan os.Signal, 1)
	signal.Notify(ctrlC, os.Interrupt)

	clientset, err := kapi.Client(configName)
	if err != nil {
		exit.Error(reason.InternalKubernetesClient, "error creating clientset", err)
	}

	var data [][]string
	for _, svc := range services {
		port, err := oci.ForwardedPort(driverName, configName, 22)
		if err != nil {
			exit.Error(reason.DrvPortForward, "error getting ssh port", err)
		}
		sshPort := strconv.Itoa(port)
		sshKey := filepath.Join(localpath.MiniPath(), "machines", configName, "id_rsa")

		serviceTunnel := kic.NewServiceTunnel(sshPort, sshKey, clientset.CoreV1(), serviceURLMode)
		urls, err := serviceTunnel.Start(svc.Name, namespace)

		if err != nil {
			exit.Error(reason.SvcTunnelStart, "error starting tunnel", err)
		}
		// mutate response urls to HTTPS if needed
		urls, err = mutateURLs(svc.Name, urls)

		if err != nil {
			exit.Error(reason.SvcTunnelStart, "error creatings urls", err)
		}

		defer serviceTunnel.Stop()
		svc.URLs = urls
		data = append(data, []string{namespace, svc.Name, "", strings.Join(urls, "\n")})
	}

	time.Sleep(1 * time.Second)

	if !serviceURLMode {
		service.PrintServiceList(os.Stdout, data)
	} else {
		for _, row := range data {
			out.String(fmt.Sprintf("%s\n", row[3]))
		}
	}

	if !serviceURLMode {
		openURLs(data)
	}

	out.WarningT("Because you are using a Docker driver on {{.operating_system}}, the terminal needs to be open to run it.", out.V{"operating_system": runtime.GOOS})

	<-ctrlC
}

func startQemuServiceTunnel(services service.URLs, configName, driverName string) {
}

func mutateURLs(serviceName string, urls []string) ([]string, error) {
	formattedUrls := make([]string, 0)
	for _, rawURL := range urls {
		var doc bytes.Buffer
		parsedURL, err := url.Parse(rawURL)
		if err != nil {
			exit.Error(reason.SvcTunnelStart, "No valid URL found for tunnel.", err)
		}
		port, err := strconv.Atoi(parsedURL.Port())
		if err != nil {
			exit.Error(reason.SvcTunnelStart, "No valid port found for tunnel.", err)
		}
		err = serviceURLTemplate.Execute(&doc, struct {
			IP   string
			Port int32
			Name string
		}{
			parsedURL.Hostname(),
			int32(port),
			serviceName,
		})

		if err != nil {
			return nil, err
		}

		httpsURL, _ := service.OptionallyHTTPSFormattedURLString(doc.String(), https)
		formattedUrls = append(formattedUrls, httpsURL)
	}

	return formattedUrls, nil
}

func openURLs(urls [][]string) {
	for _, u := range urls {

		if len(u) < 4 {
			klog.Warning("No URL found")
			continue
		}

		_, err := url.Parse(u[3])
		if err != nil {
			klog.Warningf("failed to parse url %q: %v (will not open)", u[3], err)
			out.String(fmt.Sprintf("%s\n", u))
			continue
		}

		if serviceURLMode {
			out.String(fmt.Sprintf("%s\n", u))
			continue
		}

		out.Styled(style.Celebrate, "Opening service {{.namespace_name}}/{{.service_name}} in default browser...", out.V{"namespace_name": namespace, "service_name": u[1]})
		if err := browser.OpenURL(u[3]); err != nil {
			exit.Error(reason.HostBrowser, fmt.Sprintf("open url failed: %s", u), err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/peteretelej/nasa"
)

var (
	random   = flag.Bool("random", true, "use random pictures, if false will only display today's APOD")
	interval = flag.Duration("interval", time.Minute*10, "interval to change wallpaper")
)

func main() {
	flag.Parse()
	nasaKey := os.Getenv("NASAKEY")
	if nasaKey == "" {
		nasaKey = "DEMO_KEY"
		fmt.Println(nasa.APIKEYMissing)
	}

	if !*random {
		if err := todaysAPOD(); err != nil {
			fmt.Printf("nasa-wallpapers: %v\n", err)
			os.Exit(1)
		}
		return // this return is just for-show, displayAPOD is long running
	}

	if err := randomAPOD(*interval); err != nil {
		fmt.Printf("nasa-wallpapers: %v\n", err)
		os.Exit(1)
	}
}

func todaysAPOD() error {
	defer cleanUp()
	return errors.New("TODO")
}

func randomAPOD(interval time.Duration) error {
	defer cleanUp()
	if interval < time.Second {
		return errors.New("interval set is too low")
	}

	fmt.Printf("nasa-wallpapers: resetting wallpaper to a random NASA APOD picture every %s\n", interval)
	for {
		fails := 0
		var err error
		for i := 0; i < 3; i++ {
			err = updateRandom()
			if err == nil {
				fails = 0
				break
			}
			fails++
		}
		if fails != 0 {
			log.Printf("nasa-wallpapers: unable to fetch wallpapers: %v", err)
		}
		time.Sleep(interval)
	}
}

func updateRandom() error {
	apod, err := nasa.RandomAPOD()
	if err != nil {
		return err
	}
	if apod.HDURL == "" {
		return errors.New("invalid response from NASA API")
	}
	req, err := http.NewRequest("GET", apod.HDURL, nil)
	if err != nil {
		return err
	}
	cl := &http.Client{Timeout: time.Second * 40}

	resp, err := cl.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("NASA API Response not OK: %d %s",
			resp.StatusCode, http.StatusText(resp.StatusCode))
	}
	defer func() { _ = resp.Body.Close() }()
	dat, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if len(dat) < 512 {
		return errors.New("invalid response from APOD image url")
	}
	switch http.DetectContentType(dat[:512]) {
	case "image/jpg", "image/jpeg", "image/png", "image/gif":
	default:
		return errors.New("Apod returned is not a valid image mimetype")
	}
	err = ioutil.WriteFile(tmpfile, dat, 0644)
	if err != nil {
		return err
	}
	cmdString := fmt.Sprintf("gsettings set org.gnome.desktop.background picture-uri file://%s", tmpfile)
	cmds := strings.Split(cmdString, " ")
	_, err = exec.Command(cmds[0], cmds[1:]...).Output()
	return err
}

var tmpfile string

func init() {
	tmp, err := ioutil.TempFile("", "nasa-wallpapers-pic.jpg")
	if err != nil {
		log.Fatalf("unable to get tempfile to work with: %v", err)
	}
	tmpfile = tmp.Name()
	if err := tmp.Close(); err != nil {
		log.Fatalf("unable to use tempfile %v", err)
	}
}
func cleanUp() {
	if tmpfile == "" {
		return
	}
	if _, err := os.Stat(tmpfile); err != nil {
		return
	}
	if err := os.Remove(tmpfile); err != nil {
		log.Printf("unable to clean up: %v", err)
	}
}
<commit_msg>Added parameter for background change command<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/peteretelej/nasa"
)

var (
	random    = flag.Bool("random", true, "use random pictures, if false will only display today's APOD")
	interval  = flag.Duration("interval", time.Minute*10, "interval to change wallpaper")
	cmdString = flag.String("cmd", "gsettings set org.gnome.desktop.background picture-uri file://%s", "command string to change the wallpaper")
)

func main() {
	flag.Parse()
	nasaKey := os.Getenv("NASAKEY")
	if nasaKey == "" {
		nasaKey = "DEMO_KEY"
		fmt.Println(nasa.APIKEYMissing)
	}

	if !*random {
		if err := todaysAPOD(); err != nil {
			fmt.Printf("nasa-wallpapers: %v\n", err)
			os.Exit(1)
		}
		return // this return is just for-show, displayAPOD is long running
	}

	if err := randomAPOD(*interval); err != nil {
		fmt.Printf("nasa-wallpapers: %v\n", err)
		os.Exit(1)
	}
}

func todaysAPOD() error {
	defer cleanUp()
	return errors.New("TODO")
}

func randomAPOD(interval time.Duration) error {
	defer cleanUp()
	if interval < time.Second {
		return errors.New("interval set is too low")
	}

	fmt.Printf("nasa-wallpapers: resetting wallpaper to a random NASA APOD picture every %s\n", interval)
	for {
		fails := 0
		var err error
		for i := 0; i < 3; i++ {
			err = updateRandom()
			if err == nil {
				fails = 0
				break
			}
			fails++
		}
		if fails != 0 {
			log.Printf("nasa-wallpapers: unable to fetch wallpapers: %v", err)
		}
		time.Sleep(interval)
	}
}

func updateRandom() error {
	apod, err := nasa.RandomAPOD()
	if err != nil {
		return err
	}
	if apod.HDURL == "" {
		return errors.New("invalid response from NASA API")
	}
	req, err := http.NewRequest("GET", apod.HDURL, nil)
	if err != nil {
		return err
	}
	cl := &http.Client{Timeout: time.Second * 40}

	resp, err := cl.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("NASA API Response not OK: %d %s",
			resp.StatusCode, http.StatusText(resp.StatusCode))
	}
	defer func() { _ = resp.Body.Close() }()
	dat, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if len(dat) < 512 {
		return errors.New("invalid response from APOD image url")
	}
	switch http.DetectContentType(dat[:512]) {
	case "image/jpg", "image/jpeg", "image/png", "image/gif":
	default:
		return errors.New("Apod returned is not a valid image mimetype")
	}
	err = ioutil.WriteFile(tmpfile, dat, 0644)
	if err != nil {
		return err
	}
	cmds := strings.Split(fmt.Sprintf(*cmdString, tmpfile), " ")
	_, err = exec.Command(cmds[0], cmds[1:]...).Output()
	return err
}

var tmpfile string

func init() {
	tmp, err := ioutil.TempFile("", "nasa-wallpapers-pic.jpg")
	if err != nil {
		log.Fatalf("unable to get tempfile to work with: %v", err)
	}
	tmpfile = tmp.Name()
	if err := tmp.Close(); err != nil {
		log.Fatalf("unable to use tempfile %v", err)
	}
}
func cleanUp() {
	if tmpfile == "" {
		return
	}
	if _, err := os.Stat(tmpfile); err != nil {
		return
	}
	if err := os.Remove(tmpfile); err != nil {
		log.Printf("unable to clean up: %v", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"net/http"

	"github.com/thijzert/speeldoos"
	rand "github.com/thijzert/speeldoos/lib/properrandom"
	"github.com/thijzert/speeldoos/lib/wavreader"
)

func server_main(args []string) {
	ln, err := net.Listen("tcp", ":11884")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Listening on *:11884")

	mux := http.NewServeMux()
	var srv http.Server

	mux.HandleFunc("/", indexHandler)
	mux.HandleFunc("/stream.mp3", syncStreamHandler)

	srv.Handler = mux
	log.Fatal(srv.Serve(ln))
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=UTF-8")
	fmt.Fprintf(w, "<!DOCTYPE html>\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "<audio controls><source src=\"stream.mp3\" type=\"audio/mpeg\" /></audio>\n")
	fmt.Fprintf(w, "\n")
}

func syncStreamHandler(w http.ResponseWriter, r *http.Request) {
	playlist := make(chan playlistItem)

	l := speeldoos.NewLibrary(Config.LibraryDir)
	l.WAVConf = Config.WAVConf
	l.Refresh()

	pfii := make([]speeldoos.Performance, 0, 50)

	for _, car := range l.Carriers {
		for _, pf := range car.Carrier.Performances {
			pfii = append(pfii, pf)
		}
	}

	if len(pfii) == 0 {
		log.Fatal("No performances found in your library.")
	}

	go func() {
		// Stopgap measure: stop after playing 10 performances.
		for k := 0; k < 10; k++ {
			i := rand.Intn(len(pfii))
			w, err := l.GetWAV(pfii[i])
			if err != nil {
				log.Printf("%v", err)
				continue
			}
			playlist <- playlistItem{Performance: pfii[i], Wav: w}
		}
	}()

	w.Header().Set("Content-Type", "audio/mpeg")

	format := wavreader.StreamFormat{
		Format:   1,
		Channels: 2,
		Rate:     48000,
		Bits:     16,
	}
	output, err := Config.WAVConf.ToMP3(w, format)
	if err != nil {
		log.Fatal(err)
	}

	for item := range playlist {
		log.Printf("Now playing: %s - %s", item.Performance.Work.Composer.Name, item.Performance.Work.Title[0].Title)
		_, err = io.Copy(output, item.Wav)

		if err != nil {
			log.Fatal(err)
		}
		item.Wav.Close()
	}

	output.Close()
}
<commit_msg>Don't Slow Loris connected browsers<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"net/http"

	"github.com/thijzert/speeldoos"
	rand "github.com/thijzert/speeldoos/lib/properrandom"
	"github.com/thijzert/speeldoos/lib/wavreader"
)

func server_main(args []string) {
	ln, err := net.Listen("tcp", ":11884")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Listening on *:11884")

	mux := http.NewServeMux()
	var srv http.Server

	mux.HandleFunc("/", indexHandler)
	mux.HandleFunc("/stream.mp3", syncStreamHandler)

	srv.Handler = mux
	log.Fatal(srv.Serve(ln))
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=UTF-8")
	fmt.Fprintf(w, "<!DOCTYPE html>\n")
	fmt.Fprintf(w, "\n")
	fmt.Fprintf(w, "<audio controls><source src=\"stream.mp3\" type=\"audio/mpeg\" /></audio>\n")
	fmt.Fprintf(w, "\n")
}

func syncStreamHandler(w http.ResponseWriter, r *http.Request) {
	playlist := make(chan playlistItem)

	l := speeldoos.NewLibrary(Config.LibraryDir)
	l.WAVConf = Config.WAVConf
	l.Refresh()

	pfii := make([]speeldoos.Performance, 0, 50)

	for _, car := range l.Carriers {
		for _, pf := range car.Carrier.Performances {
			pfii = append(pfii, pf)
		}
	}

	if len(pfii) == 0 {
		log.Fatal("No performances found in your library.")
	}

	go func() {
		// Stopgap measure: stop after playing 10 performances.
		for k := 0; k < 10; k++ {
			i := rand.Intn(len(pfii))
			w, err := l.GetWAV(pfii[i])
			if err != nil {
				log.Printf("%v", err)
				continue
			}
			playlist <- playlistItem{Performance: pfii[i], Wav: w}
		}
		close(playlist)
	}()

	w.Header().Set("Content-Type", "audio/mpeg")

	format := wavreader.StreamFormat{
		Format:   1,
		Channels: 2,
		Rate:     48000,
		Bits:     16,
	}
	output, err := Config.WAVConf.ToMP3(w, format)
	if err != nil {
		log.Fatal(err)
	}

	for item := range playlist {
		log.Printf("Now playing: %s - %s", item.Performance.Work.Composer.Name, item.Performance.Work.Title[0].Title)
		_, err = io.Copy(output, item.Wav)

		if err != nil {
			log.Fatal(err)
		}
		item.Wav.Close()
	}

	output.Close()
}
<|endoftext|>


<commit_before>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"errors"
	"math/rand"
	"model"
	"net/url"
	"strconv"
	"strings"
	"time"
	"util"

	. "db"

	"github.com/PuerkitoBio/goquery"
	"github.com/lunny/html2md"
	"github.com/polaris1119/logger"
	"golang.org/x/net/context"
)

type ProjectLogic struct{}

var DefaultProject = ProjectLogic{}

func (self ProjectLogic) Publish(ctx context.Context, user *model.Me, form url.Values) (err error) {
	objLog := GetLogger(ctx)

	id := form.Get("id")
	isModify := id != ""

	project := &model.OpenProject{}

	if isModify {
		_, err = MasterDB.Id(id).Get(project)
		if err != nil {
			objLog.Errorln("Publish Project find error:", err)
			return
		}
		if project.Username != user.Username && !user.IsAdmin {
			err = NotModifyAuthorityErr
			return
		}

		err = schemaDecoder.Decode(project, form)
		if err != nil {
			objLog.Errorln("Publish Project schema decode error:", err)
			return
		}
	} else {
		err = schemaDecoder.Decode(project, form)
		if err != nil {
			objLog.Errorln("Publish Project schema decode error:", err)
			return
		}

		project.Username = user.Username
	}
	if project.Uri == "" {
		project.Uri = strings.Replace(project.Name, " ", "-", -1)
	}
	project.Uri = strings.ToLower(project.Uri)

	if !isModify && self.UriExists(ctx, form.Get("uri")) {
		err = errors.New("项目已存在")
		return
	}

	github := "github.com"
	pos := strings.Index(project.Src, github)
	if pos != -1 {
		project.Repo = project.Src[pos+len(github)+1:]
	}

	var affected int64
	if !isModify {
		affected, err = MasterDB.Insert(project)
	} else {
		affected, err = MasterDB.Update(project)
	}

	if err != nil {
		objLog.Errorln("Publish Project error:", err)
		return
	}

	if affected == 0 {
		return
	}

	// 发布项目，活跃度+10
	weight := 10
	if isModify {
		weight = 2
	}
	go DefaultUser.IncrUserWeight("uid", user.Uid, weight)

	return
}

// UriExists 通过 uri 是否存在 project
func (ProjectLogic) UriExists(ctx context.Context, uri string) bool {
	total, err := MasterDB.Where("uri=?", uri).Count(new(model.OpenProject))
	if err != nil || total == 0 {
		return false
	}

	return true
}

// Total 开源项目总数
func (ProjectLogic) Total() int64 {
	total, err := MasterDB.Count(new(model.OpenProject))
	if err != nil {
		logger.Errorln("ProjectLogic Total error:", err)
	}
	return total
}

// FindBy 获取开源项目列表（分页）
func (ProjectLogic) FindBy(ctx context.Context, limit int, lastIds ...int) []*model.OpenProject {
	objLog := GetLogger(ctx)

	dbSession := MasterDB.Where("status IN(?,?)", model.ProjectStatusNew, model.ProjectStatusOnline)
	if len(lastIds) > 0 && lastIds[0] > 0 {
		dbSession.And("id<?", lastIds[0])
	}

	projectList := make([]*model.OpenProject, 0)
	err := dbSession.OrderBy("id DESC").Limit(limit).Find(&projectList)
	if err != nil {
		objLog.Errorln("ProjectLogic FindBy Error:", err)
		return nil
	}

	return projectList
}

// FindByIds 获取多个项目详细信息
func (ProjectLogic) FindByIds(ids []int) []*model.OpenProject {
	if len(ids) == 0 {
		return nil
	}

	projects := make([]*model.OpenProject, 0)
	err := MasterDB.In("id", ids).Find(&projects)
	if err != nil {
		logger.Errorln("ProjectLogic FindByIds error:", err)
		return nil
	}
	return projects
}

// findByIds 获取多个项目详细信息 包内使用
func (ProjectLogic) findByIds(ids []int) map[int]*model.OpenProject {
	if len(ids) == 0 {
		return nil
	}

	projects := make(map[int]*model.OpenProject)
	err := MasterDB.In("id", ids).Find(&projects)
	if err != nil {
		logger.Errorln("ProjectLogic FindByIds error:", err)
		return nil
	}
	return projects
}

// FindOne 获取单个项目
func (ProjectLogic) FindOne(ctx context.Context, val interface{}) *model.OpenProject {
	objLog := GetLogger(ctx)

	field := "id"
	_, ok := val.(int)
	if !ok {
		val := val.(string)
		if _, err := strconv.Atoi(val); err != nil {
			field = "uri"
		}
	}

	project := &model.OpenProject{}
	_, err := MasterDB.Where(field+"=? AND status IN(?,?)", val, model.ProjectStatusNew, model.ProjectStatusOnline).Get(project)
	if err != nil {
		objLog.Errorln("project service FindProject error:", err)
		return nil
	}

	return project
}

// FindRecent 获得某个用户最近发布的开源项目
func (ProjectLogic) FindRecent(ctx context.Context, username string) []*model.OpenProject {
	projectList := make([]*model.OpenProject, 0)
	err := MasterDB.Where("username=?", username).Limit(5).OrderBy("id DESC").Find(&projectList)
	if err != nil {
		logger.Errorln("project logic FindRecent error:", err)
		return nil
	}
	return projectList
}

// getOwner 通过objid获得 project 的所有者
func (ProjectLogic) getOwner(ctx context.Context, id int) int {
	project := &model.OpenProject{}
	_, err := MasterDB.Id(id).Get(project)
	if err != nil {
		logger.Errorln("project logic getOwner Error:", err)
		return 0
	}

	user := DefaultUser.FindOne(ctx, "username", project.Username)
	return user.Uid
}

// ParseProjectList 解析其他网站的开源项目
func (self ProjectLogic) ParseProjectList(pUrl string) error {
	pUrl = strings.TrimSpace(pUrl)
	if !strings.HasPrefix(pUrl, "http") {
		pUrl = "http://" + pUrl
	}

	logger.Infoln("parse project url:", pUrl)

	var (
		doc *goquery.Document
		err error
	)

	if doc, err = goquery.NewDocument(pUrl); err != nil {
		logger.Errorln("goquery opensource project newdocument error:", err)
		return err
	}

	// 最后面的先入库处理
	projectsSelection := doc.Find(".news-list .box")

	for i := projectsSelection.Length() - 1; i >= 0; i-- {

		contentSelection := goquery.NewDocumentFromNode(projectsSelection.Get(i)).Selection
		projectUrl, ok := contentSelection.Find(".box-aw a").Attr("href")

		if !ok || projectUrl == "" {
			logger.Errorln("project url is empty")
			continue
		}
		go func() {
			err := self.ParseOneProject(projectUrl)

			if err != nil {
				logger.Errorln(err)
			}
		}()
	}

	return err
}

const OsChinaDomain = "http://www.oschina.net"

// ProjectLogoPrefix 开源项目 logo 前缀
const ProjectLogoPrefix = "plogo"

var PresetUsernames = []string{"polaris", "blov", "agolangf", "xuanbao"}

// ParseOneProject 处理单个 project
func (ProjectLogic) ParseOneProject(projectUrl string) error {
	if !strings.HasPrefix(projectUrl, "http") {
		projectUrl = OsChinaDomain + projectUrl
	}

	var (
		doc *goquery.Document
		err error
	)

	// 加上 ?fromerr=xfwefs，否则页面有 js 重定向
	if doc, err = goquery.NewDocument(projectUrl + "?fromerr=xfwefs"); err != nil {
		return errors.New("goquery fetch " + projectUrl + " error:" + err.Error())
	}

	// 标题
	category := strings.TrimSpace(doc.Find("#v-header header .box-aw h1").Text())
	name := strings.TrimSpace(doc.Find("#v-header header .box-aw h1 span").Text())
	if category == "" && name == "" {
		return errors.New("projectUrl:" + projectUrl + " category and name are empty")
	}

	tmpIndex := strings.LastIndex(category, name)
	if tmpIndex != -1 {
		category = category[:tmpIndex]
	}

	// uri
	uri := projectUrl[strings.LastIndex(projectUrl, "/")+1:]

	project := &model.OpenProject{}

	_, err = MasterDB.Where("uri=?", uri).Get(project)
	// 已经存在
	if project.Id != 0 {
		logger.Infoln("url", projectUrl, "has exists!")
		return nil
	}

	logoSelection := doc.Find("#v-header header .logo img")
	if logoSelection.AttrOr("title", "") != "" {
		project.Logo = logoSelection.AttrOr("src", "")

		if !strings.HasPrefix(project.Logo, "http") {
			// project.Logo = OsChinaDomain + project.Logo
			// 默认图
			project.Logo = ""
		} else {
			project.Logo, err = DefaultUploader.TransferUrl(nil, project.Logo, ProjectLogoPrefix)
			if err != nil {
				logger.Errorln("project logo upload error:", err)
			}
		}
	}

	// 获取项目相关链接
	doc.Find("#v-details .urls a").Each(func(i int, aSelection *goquery.Selection) {
		uri := util.FetchRealUrl(OsChinaDomain + aSelection.AttrOr("href", ""))
		switch aSelection.Find("span").Text() {
		case "软件首页":
			project.Home = uri
		case "软件文档":
			project.Doc = uri
		case "软件下载":
			project.Download = uri
		}
	})

	doc.Find("#v-basic .list .box").Each(func(i int, liSelection *goquery.Selection) {
		aSelection := liSelection.Find("span")
		txt := strings.TrimSpace(aSelection.Text())
		if i == 0 {
			project.Licence = txt
			if txt == "未知" {
				project.Licence = "其他"
			}
		} else if i == 1 {
			project.Lang = txt
		} else if i == 2 {
			project.Os = txt
		}
	})

	project.Name = name
	project.Category = strings.TrimSpace(category)
	project.Uri = uri
	project.Repo = strings.TrimSpace(doc.Find("#v-details .github-widget").AttrOr("data-repo", ""))
	project.Src = "https://github.com/" + project.Repo

	pos := strings.Index(project.Repo, "/")
	if pos > -1 {
		project.Author = project.Repo[:pos]
	} else {
		project.Author = "网友"
	}

	if project.Doc == "" {
		// TODO：暂时认为一定是 Go 语言
		project.Doc = "https://godoc.org/" + project.Src[8:]
	}

	desc := ""
	doc.Find("#v-details .detail").Find("p").Each(func(i int, domSelection *goquery.Selection) {
		doc.FindSelection(domSelection).WrapHtml(`<div id="tmp` + strconv.Itoa(i) + `"></div>`)
		domHtml, _ := doc.Find("#tmp" + strconv.Itoa(i)).Html()
		if domSelection.Is("pre") {
			desc += domHtml + "\n\n"
		} else {
			desc += html2md.Convert(domHtml) + "\n\n"
		}
	})

	project.Desc = strings.TrimSpace(desc)
	project.Username = PresetUsernames[rand.Intn(4)]
	project.Status = model.ProjectStatusOnline
	project.Ctime = model.OftenTime(time.Now())

	_, err = MasterDB.Insert(project)
	if err != nil {
		return errors.New("insert into open project error:" + err.Error())
	}

	return nil
}

// 项目评论
type ProjectComment struct{}

// 更新该项目的评论信息
// cid：评论id；objid：被评论对象id；uid：评论者；cmttime：评论时间
func (self ProjectComment) UpdateComment(cid, objid, uid int, cmttime time.Time) {
	// 更新评论数（TODO：暂时每次都更新表）
	_, err := MasterDB.Id(objid).Incr("cmtnum", 1).Update(new(model.OpenProject))
	if err != nil {
		logger.Errorln("更新项目评论数失败：", err)
	}
}

func (self ProjectComment) String() string {
	return "project"
}

// 实现 CommentObjecter 接口
func (self ProjectComment) SetObjinfo(ids []int, commentMap map[int][]*model.Comment) {
	projects := DefaultProject.FindByIds(ids)
	if len(projects) == 0 {
		return
	}

	for _, project := range projects {
		objinfo := make(map[string]interface{})
		objinfo["title"] = project.Category + project.Name
		objinfo["uri"] = model.PathUrlMap[model.TypeProject]
		objinfo["type_name"] = model.TypeNameMap[model.TypeProject]

		for _, comment := range commentMap[project.Id] {
			comment.Objinfo = objinfo
		}
	}
}

// 项目喜欢
type ProjectLike struct{}

// 更新该项目的喜欢数
// objid：被喜欢对象id；num: 喜欢数(负数表示取消喜欢)
func (self ProjectLike) UpdateLike(objid, num int) {
	// 更新喜欢数（TODO：暂时每次都更新表）
	_, err := MasterDB.Id(objid).Incr("likenum", num).Update(new(model.OpenProject))
	if err != nil {
		logger.Errorln("更新项目喜欢数失败：", err)
	}
}

func (self ProjectLike) String() string {
	return "project"
}
<commit_msg>项目 bugfix<commit_after>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"errors"
	"math/rand"
	"model"
	"net/url"
	"strconv"
	"strings"
	"time"
	"util"

	. "db"

	"github.com/PuerkitoBio/goquery"
	"github.com/lunny/html2md"
	"github.com/polaris1119/logger"
	"golang.org/x/net/context"
)

type ProjectLogic struct{}

var DefaultProject = ProjectLogic{}

func (self ProjectLogic) Publish(ctx context.Context, user *model.Me, form url.Values) (err error) {
	objLog := GetLogger(ctx)

	id := form.Get("id")
	isModify := id != ""

	project := &model.OpenProject{}

	if isModify {
		_, err = MasterDB.Id(id).Get(project)
		if err != nil {
			objLog.Errorln("Publish Project find error:", err)
			return
		}
		if project.Username != user.Username && !user.IsAdmin {
			err = NotModifyAuthorityErr
			return
		}

		err = schemaDecoder.Decode(project, form)
		if err != nil {
			objLog.Errorln("Publish Project schema decode error:", err)
			return
		}
	} else {
		err = schemaDecoder.Decode(project, form)
		if err != nil {
			objLog.Errorln("Publish Project schema decode error:", err)
			return
		}

		project.Username = user.Username
	}
	if project.Uri == "" {
		project.Uri = strings.Replace(project.Name, " ", "-", -1)
	}
	project.Uri = strings.ToLower(project.Uri)

	if !isModify && self.UriExists(ctx, form.Get("uri")) {
		err = errors.New("项目已存在")
		return
	}

	github := "github.com"
	pos := strings.Index(project.Src, github)
	if pos != -1 {
		project.Repo = project.Src[pos+len(github)+1:]
	}

	var affected int64
	if !isModify {
		affected, err = MasterDB.Insert(project)
	} else {
		affected, err = MasterDB.Update(project)
	}

	if err != nil {
		objLog.Errorln("Publish Project error:", err)
		return
	}

	if affected == 0 {
		return
	}

	// 发布项目，活跃度+10
	weight := 10
	if isModify {
		weight = 2
	}
	go DefaultUser.IncrUserWeight("uid", user.Uid, weight)

	return
}

// UriExists 通过 uri 是否存在 project
func (ProjectLogic) UriExists(ctx context.Context, uri string) bool {
	total, err := MasterDB.Where("uri=?", uri).Count(new(model.OpenProject))
	if err != nil || total == 0 {
		return false
	}

	return true
}

// Total 开源项目总数
func (ProjectLogic) Total() int64 {
	total, err := MasterDB.Count(new(model.OpenProject))
	if err != nil {
		logger.Errorln("ProjectLogic Total error:", err)
	}
	return total
}

// FindBy 获取开源项目列表（分页）
func (ProjectLogic) FindBy(ctx context.Context, limit int, lastIds ...int) []*model.OpenProject {
	objLog := GetLogger(ctx)

	dbSession := MasterDB.Where("status IN(?,?)", model.ProjectStatusNew, model.ProjectStatusOnline)
	if len(lastIds) > 0 && lastIds[0] > 0 {
		dbSession.And("id<?", lastIds[0])
	}

	projectList := make([]*model.OpenProject, 0)
	err := dbSession.OrderBy("id DESC").Limit(limit).Find(&projectList)
	if err != nil {
		objLog.Errorln("ProjectLogic FindBy Error:", err)
		return nil
	}

	return projectList
}

// FindByIds 获取多个项目详细信息
func (ProjectLogic) FindByIds(ids []int) []*model.OpenProject {
	if len(ids) == 0 {
		return nil
	}

	projects := make([]*model.OpenProject, 0)
	err := MasterDB.In("id", ids).Find(&projects)
	if err != nil {
		logger.Errorln("ProjectLogic FindByIds error:", err)
		return nil
	}
	return projects
}

// findByIds 获取多个项目详细信息 包内使用
func (ProjectLogic) findByIds(ids []int) map[int]*model.OpenProject {
	if len(ids) == 0 {
		return nil
	}

	projects := make(map[int]*model.OpenProject)
	err := MasterDB.In("id", ids).Find(&projects)
	if err != nil {
		logger.Errorln("ProjectLogic FindByIds error:", err)
		return nil
	}
	return projects
}

// FindOne 获取单个项目
func (ProjectLogic) FindOne(ctx context.Context, val interface{}) *model.OpenProject {
	objLog := GetLogger(ctx)

	field := "id"
	_, ok := val.(int)
	if !ok {
		val := val.(string)
		if _, err := strconv.Atoi(val); err != nil {
			field = "uri"
		}
	}

	project := &model.OpenProject{}
	_, err := MasterDB.Where(field+"=? AND status IN(?,?)", val, model.ProjectStatusNew, model.ProjectStatusOnline).Get(project)
	if err != nil {
		objLog.Errorln("project service FindProject error:", err)
		return nil
	}

	return project
}

// FindRecent 获得某个用户最近发布的开源项目
func (ProjectLogic) FindRecent(ctx context.Context, username string) []*model.OpenProject {
	projectList := make([]*model.OpenProject, 0)
	err := MasterDB.Where("username=?", username).Limit(5).OrderBy("id DESC").Find(&projectList)
	if err != nil {
		logger.Errorln("project logic FindRecent error:", err)
		return nil
	}
	return projectList
}

// getOwner 通过objid获得 project 的所有者
func (ProjectLogic) getOwner(ctx context.Context, id int) int {
	project := &model.OpenProject{}
	_, err := MasterDB.Id(id).Get(project)
	if err != nil {
		logger.Errorln("project logic getOwner Error:", err)
		return 0
	}

	user := DefaultUser.FindOne(ctx, "username", project.Username)
	return user.Uid
}

// ParseProjectList 解析其他网站的开源项目
func (self ProjectLogic) ParseProjectList(pUrl string) error {
	pUrl = strings.TrimSpace(pUrl)
	if !strings.HasPrefix(pUrl, "http") {
		pUrl = "http://" + pUrl
	}

	var (
		doc *goquery.Document
		err error
	)

	if doc, err = goquery.NewDocument(pUrl); err != nil {
		logger.Errorln("goquery opensource project newdocument error:", err)
		return err
	}

	// 最后面的先入库处理
	projectsSelection := doc.Find(".news-list").Children()

	for i := projectsSelection.Length() - 1; i >= 0; i-- {

		contentSelection := goquery.NewDocumentFromNode(projectsSelection.Get(i)).Selection
		projectUrl, ok := contentSelection.Find(".box-aw a").First().Attr("href")

		if !ok || projectUrl == "" {
			logger.Errorln("project url is empty")
			continue
		}
		go func(projectUrl string) {
			err := self.ParseOneProject(projectUrl)

			if err != nil {
				logger.Errorln(err)
			}
		}(projectUrl)
	}

	return err
}

const OsChinaDomain = "http://www.oschina.net"

// ProjectLogoPrefix 开源项目 logo 前缀
const ProjectLogoPrefix = "plogo"

var PresetUsernames = []string{"polaris", "blov", "agolangf", "xuanbao"}

// ParseOneProject 处理单个 project
func (ProjectLogic) ParseOneProject(projectUrl string) error {
	if !strings.HasPrefix(projectUrl, "http") {
		projectUrl = OsChinaDomain + projectUrl
	}

	var (
		doc *goquery.Document
		err error
	)

	// 加上 ?fromerr=xfwefs，否则页面有 js 重定向
	if doc, err = goquery.NewDocument(projectUrl + "?fromerr=xfwefs"); err != nil {
		return errors.New("goquery fetch " + projectUrl + " error:" + err.Error())
	}

	// 标题
	category := strings.TrimSpace(doc.Find("#v-header header .box-aw h1").Text())
	name := strings.TrimSpace(doc.Find("#v-header header .box-aw h1 span").Text())
	if category == "" && name == "" {
		return errors.New("projectUrl:" + projectUrl + " category and name are empty")
	}

	tmpIndex := strings.LastIndex(category, name)
	if tmpIndex != -1 {
		category = category[:tmpIndex]
	}

	// uri
	uri := projectUrl[strings.LastIndex(projectUrl, "/")+1:]

	project := &model.OpenProject{}

	_, err = MasterDB.Where("uri=?", uri).Get(project)
	// 已经存在
	if project.Id != 0 {
		logger.Infoln("url", projectUrl, "has exists!")
		return nil
	}

	logoSelection := doc.Find("#v-header header .logo img")
	if logoSelection.AttrOr("title", "") != "" {
		project.Logo = logoSelection.AttrOr("src", "")

		if !strings.HasPrefix(project.Logo, "http") {
			// project.Logo = OsChinaDomain + project.Logo
			// 默认图
			project.Logo = ""
		} else {
			project.Logo, err = DefaultUploader.TransferUrl(nil, project.Logo, ProjectLogoPrefix)
			if err != nil {
				logger.Errorln("project logo upload error:", err)
			}
		}
	}

	// 获取项目相关链接
	doc.Find("#v-details .urls a").Each(func(i int, aSelection *goquery.Selection) {
		uri := util.FetchRealUrl(OsChinaDomain + aSelection.AttrOr("href", ""))
		switch aSelection.Find("span").Text() {
		case "软件首页":
			project.Home = uri
		case "软件文档":
			project.Doc = uri
		case "软件下载":
			project.Download = uri
		}
	})

	doc.Find("#v-basic .list .box").Each(func(i int, liSelection *goquery.Selection) {
		aSelection := liSelection.Find("span")
		txt := strings.TrimSpace(aSelection.Text())
		if i == 0 {
			project.Licence = txt
			if txt == "未知" {
				project.Licence = "其他"
			}
		} else if i == 1 {
			project.Lang = txt
		} else if i == 2 {
			project.Os = txt
		}
	})

	project.Name = name
	project.Category = strings.TrimSpace(category)
	project.Uri = uri
	project.Repo = strings.TrimSpace(doc.Find("#v-details .github-widget").AttrOr("data-repo", ""))
	project.Src = "https://github.com/" + project.Repo

	pos := strings.Index(project.Repo, "/")
	if pos > -1 {
		project.Author = project.Repo[:pos]
	} else {
		project.Author = "网友"
	}

	if project.Doc == "" {
		// TODO：暂时认为一定是 Go 语言
		project.Doc = "https://godoc.org/" + project.Src[8:]
	}

	desc := ""
	doc.Find("#v-details .detail").Find("p").Each(func(i int, domSelection *goquery.Selection) {
		doc.FindSelection(domSelection).WrapHtml(`<div id="tmp` + strconv.Itoa(i) + `"></div>`)
		domHtml, _ := doc.Find("#tmp" + strconv.Itoa(i)).Html()
		if domSelection.Is("pre") {
			desc += domHtml + "\n\n"
		} else {
			desc += html2md.Convert(domHtml) + "\n\n"
		}
	})

	project.Desc = strings.TrimSpace(desc)
	project.Username = PresetUsernames[rand.Intn(4)]
	project.Status = model.ProjectStatusOnline
	project.Ctime = model.OftenTime(time.Now())

	_, err = MasterDB.Insert(project)
	if err != nil {
		return errors.New("insert into open project error:" + err.Error())
	}

	return nil
}

// 项目评论
type ProjectComment struct{}

// 更新该项目的评论信息
// cid：评论id；objid：被评论对象id；uid：评论者；cmttime：评论时间
func (self ProjectComment) UpdateComment(cid, objid, uid int, cmttime time.Time) {
	// 更新评论数（TODO：暂时每次都更新表）
	_, err := MasterDB.Id(objid).Incr("cmtnum", 1).Update(new(model.OpenProject))
	if err != nil {
		logger.Errorln("更新项目评论数失败：", err)
	}
}

func (self ProjectComment) String() string {
	return "project"
}

// 实现 CommentObjecter 接口
func (self ProjectComment) SetObjinfo(ids []int, commentMap map[int][]*model.Comment) {
	projects := DefaultProject.FindByIds(ids)
	if len(projects) == 0 {
		return
	}

	for _, project := range projects {
		objinfo := make(map[string]interface{})
		objinfo["title"] = project.Category + project.Name
		objinfo["uri"] = model.PathUrlMap[model.TypeProject]
		objinfo["type_name"] = model.TypeNameMap[model.TypeProject]

		for _, comment := range commentMap[project.Id] {
			comment.Objinfo = objinfo
		}
	}
}

// 项目喜欢
type ProjectLike struct{}

// 更新该项目的喜欢数
// objid：被喜欢对象id；num: 喜欢数(负数表示取消喜欢)
func (self ProjectLike) UpdateLike(objid, num int) {
	// 更新喜欢数（TODO：暂时每次都更新表）
	_, err := MasterDB.Id(objid).Incr("likenum", num).Update(new(model.OpenProject))
	if err != nil {
		logger.Errorln("更新项目喜欢数失败：", err)
	}
}

func (self ProjectLike) String() string {
	return "project"
}
<|endoftext|>


<commit_before>/*
This command is the client portion of the pipeline plotter.

Lineprobe supports two modes. By default, it reads values from Stdin. Each line
is a single value. If the argument `-command` is used, rather than reading from
Stdin, lineprobe runs the given command and reads its output. The command
should return a single value and exit with status 0. The command will be
executed every 'interval' seconds.

Options

These are command-line options.

    -hostname=localhost:3131  Host and port of view server.
    -interval=1.0             How often to run command.
    -command=                 Command to run every second.
    -axis=default             Name of axis to associate this line.
    -xlabel=                  X-Label for axis.
    -ylabel=                  Y-Label for axis.
    -label=                   Line label name on axis.
    -color=                   Color of line as "#ffffff". Chosen automatically by default.
    -q                        Silence the echo of values sent to the server.
    -exit                     Signal the lineviewer to exit. Useful when profiling the server.
		-operation=<kind>,<samples>[,<percentile>]
                              Perform an optional operation on collected samples
                              before sending to lineviewer.
                              <kind> is one of "avg", "stdev", "perc".
                              <samples> is an integer specifying the number of samples to operate over.
                              <percentile> if kind=perc, then this is the percentile to
                                 report from the last <samples> values.
    -debug                    Print extra debug information.

Examples

Reading data from stdin:
    while /usr/bin/true; do ps ax | wc -l ; sleep 1 ; done | lineprobe

Reading data from command output:
    lineprobe --command "ps ax | wc -l" --interval 2.0

*/
package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math"
	"os"
	"os/exec"
	"sort"
	"strconv"
	"time"

	// third-party
	"github.com/stephen-soltesz/go/lineserver"
)

var (
	hostname    = flag.String("hostname", "localhost:3131", "Host and port of view server.")
	command     = flag.String("command", "", "Command to run every second.")
	interval    = flag.Float64("interval", 1.0, "How often to run command.")
	axisName    = flag.String("axis", "default", "Name of axis to associate this line.")
	xlabelName  = flag.String("xlabel", "", "X-Label for axis.")
	ylabelName  = flag.String("ylabel", "", "Y-Label for axis.")
	ylimitValue = flag.String("ylimit", "", "Y-limits for axis 'ymin:ymax'.")
	yaxisScale  = flag.Bool("ylog", false, "Y-axis should be log scale.")
	lineName    = flag.String("label", "", "Line label name on axis.")
	lineColor   = flag.String("color", "", "Color of line. Chosen automatically by default.")
	quietOutput = flag.Bool("q", false, "Whether to echo values sent to server.")
	exitServer  = flag.Bool("exit", false, "Tell the lineviewer to exit.")

	calcOperations operationSlice

	debug         = flag.Bool("debug", false, "Enable debug messages on stderr.")
	debugLogger   *log.Logger
)

type sampleSet struct {
	samples []float64
	pos int
}

type ValueWriter struct {
	writer *bufio.ReadWriter
	server *lineserver.Server
	op *Operation
	samples *sampleSet
}

type ValueReader struct {
	reader *bufio.Reader
	cmd *exec.Cmd
}

func initFlags() {
	flag.Var(&calcOperations, "operation", "An operation to run on samples.")
}

func checkFlags() {
	if *debug {
		debugLogger = log.New(os.Stderr, "", 0)
	} else {
		debugLogger = log.New(ioutil.Discard, "", 0)
	}
	if len(calcOperations) == 0 {
		calcOperations = append(calcOperations, &Operation{OpPercentile, 1, 50})
	}
}

func printAllFlagsAndValues() {
	flag.VisitAll(func(f *flag.Flag) {
		debugLogger.Printf("%-30s %s\n", fmt.Sprintf("-%s=%s", f.Name, f.Value), f.Usage)
	})
}

func newSampleSet(sampleCount int64) *sampleSet {
	return &sampleSet{make([]float64, sampleCount), 0}
}

func (ss *sampleSet) Count() int {
	if ss.Full() {
		return len(ss.samples)
	} else {
		return ss.pos
	}
}

func (ss *sampleSet) Full() bool {
	return ss.pos >= len(ss.samples)-1
}

func (ss *sampleSet) Append(val float64) {
	ss.samples[ss.pos%len(ss.samples)] = val
	ss.pos += 1
}

func (ss *sampleSet) Mean() float64 {
	count := ss.Count()
	var s float64 = 0
	for i := 0; i < count; i++ {
		s += ss.samples[i]
	}
	avg := s/float64(count)
	debugLogger.Println("avg:", avg)
	return avg
}

func (ss *sampleSet) Stdev() float64 {
	count := ss.Count()
	avg := ss.Mean()
	var variance float64 = 0

	for i := 0; i < count; i++ {
		variance += math.Pow((ss.samples[i] - avg), 2)
	}
	stdev := math.Sqrt(variance/float64(count))
	debugLogger.Println("stdev:", stdev)
	return stdev
}

func (ss *sampleSet) Percentile(pct int64) float64 {
	count := ss.Count()
	var calc = make([]float64, count)
	copy(calc, ss.samples)

	i := int(math.Ceil(float64(count-1) * float64(pct)/101.0))
	sort.Float64s(calc)
	debugLogger.Println("size", count, "i", i)
	debugLogger.Println("pct:", calc[i])
	return calc[i]
}

func NewValueWriter(op *Operation) *ValueWriter {
	var err error
	writer := ValueWriter{}
	writer.op = op
	fmt.Printf("%#v\n", op)
	writer.samples = newSampleSet(op.samples)
	writer.server = lineserver.NewServer(*hostname)
	if writer.writer, err = writer.server.Connect(); err != nil {
		log.Fatal(err)
	}
	return &writer
}

func SetupValueWriters() []*ValueWriter {
	var writers []*ValueWriter
	for _, op := range calcOperations {
		writer := NewValueWriter(op)
		if err := writer.sendClientSettings(); err != nil {
			log.Fatal(err)
		}
		writers = append(writers, writer)
	}
	return writers
}

func (w *ValueWriter) SendValue(val float64) error {
	var f float64
	w.samples.Append(val)
	if w.op.operation == OpMean {
		f = w.samples.Mean()
	} else if w.op.operation == OpStdev {
		f = w.samples.Stdev()
	} else if w.op.operation == OpPercentile {
		f = w.samples.Percentile(w.op.percentile)
	}

	out := formatFloat(f)
	fmt.Printf("Sending: %s", string(out))
	_, err := w.writer.Write(out)
	if err != nil {
		return err
	}
	w.writer.Flush()
	return nil
}

func (w *ValueWriter) sendClientSettings() error {
	var err error
	if *exitServer {
		fmt.Println("Sending exit")
		_, err := w.writer.WriteString("EXIT\n")
		if err != nil {
			return err
		}
		w.writer.Flush()
		// create new connection to trigger exit.
		time.Sleep(time.Second)
		NewValueWriter(&Operation{OpNone, 0, 0})
		os.Exit(0)
	}
	axisSetting := "axis:" + *axisName + ":" + *xlabelName + ":" + *ylabelName + "\n"
	if _, err = w.writer.WriteString(axisSetting); err != nil {
		return err
	}

	if *ylimitValue != "" {
		limitSetting := "limit:" + *ylimitValue + "\n"
		if _, err := w.writer.WriteString(limitSetting); err != nil {
			return err
		}
	}

	if *yaxisScale {
		yaxisScale := "yaxisscale:log\n"
		if _, err := w.writer.WriteString(yaxisScale); err != nil {
			return err
		}
	}

	name := ""
	if w.op.operation > OpNone {
		name = fmt.Sprintf("%s-", w.op)
	}

	if *lineName != "" {
		name = "label:" + *lineName + "\n"
	} else if *command != "" {
		name = "label:" + name + *command + "\n"
	} else {
		name = "label:" + name + fmt.Sprintf("Thread-%d\n", os.Getpid())
	}

	if _, err = w.writer.WriteString(name); err != nil {
		return err
	}

	if *lineColor != "" {
		colorSetting := fmt.Sprintf("color:%s\n", *lineColor)
		if _, err := w.writer.WriteString(colorSetting); err != nil {
			return err
		}
	}
	return nil
}

func NewValueReader() *ValueReader {
	reader := ValueReader{}
	reader.Setup()
	return &reader
}

func (r *ValueReader) Setup() error {
	var fromStdin bool = ("" == *command)

	if fromStdin {
		r.reader = bufio.NewReader(os.Stdin)
	} else {
		r.cmd = exec.Command("bash", "-c", *command)
		output, err := r.cmd.StdoutPipe()
		if err != nil {
			return err
		}
		if err := r.cmd.Start(); err != nil {
			return err
		}
		r.reader = bufio.NewReader(output)
	}
	return nil
}

func (r *ValueReader) ReadValue() (float64, error) {
	var err error
	var lineTooLong bool
	var out []byte

	out, lineTooLong, err = r.reader.ReadLine()
	if lineTooLong {
		return 0.0, errors.New("line too long")
	} else if err == io.EOF {
		r.cmd.Wait()	// must run after EOF to avoid races with Read()
		time.Sleep(time.Duration(*interval) * time.Second)
		err := r.Setup()
		if err != nil {
			fmt.Println("reader setup failed:", err)
			return 0.0, err
		}
		return r.ReadValue()
	} else if err != nil {
		fmt.Println("unknown failure:", err)
		return 0.0, err
	}

	f, err := strconv.ParseFloat(string(out), 64)
	if err != nil {
		return 0.0, err
	}
	if !*quietOutput {
		fmt.Printf("Received: %f\n", f)
	}
	return f, nil
}

func parseFloat(val []byte) float64 {
	fmt.Println("%v", val)
	f, err := strconv.ParseFloat(string(val), 64)
	if err != nil {
		debugLogger.Println(err)
		return 0.0
	}
	return f
}

func formatFloat(f float64) []byte {
	return []byte(fmt.Sprintf("%f\n", f))
}

func main() {
	var err error
	var val float64

	initFlags()
	flag.Parse()
	checkFlags()

	if !*quietOutput {
		printAllFlagsAndValues()
	}

	// allocate value reader and writers.
	reader := NewValueReader()
	writers := SetupValueWriters()

	for {
		if val, err = reader.ReadValue(); err != nil {
			log.Println(err)
			break
		}
		for _, writer := range writers {
			if err = writer.SendValue(val); err != nil {
				log.Fatal(err)
			}
		}
	}
	return
}
<commit_msg>update documentation and EOF handling from commands.<commit_after>/*
This command is the client portion of the pipeline plotter.

Lineprobe supports two modes. By default, it reads values from Stdin. As an
alternate to reading from Stdin, lineprobe can execute a given command and read
the command's Stdout. Each line is interprted as a single floating point value.

If execting a given command, the command may output a single value, or
multiple. If the command exits with status 0, it will be executed again after a
configurable interval. If the command exists non-zero, then lineprobe exits
also.

Command Options

Each section below describes the lineprobe options.

Typically, the lineprobe and lineviewer will run on the same system. When this
is the case, default values work well. However, lineprobe can target a
collector running on any system. To specify an alternate collector use:

   -hostname=localhost:3131

The default is for lineprobe to read from Stdin. However, lineprobe can execute
a given command and read from the command's Stdout instead. After the command
exits with a zero status, lineprobe waits an interval before re-executing the
command. If the command exists non-zero, lineprobe also exits.

   -interval=1.0
   -command=

By default, lineprobe reports on the values it receives and sends. To silence
this, use:

   -quiet

For additional debug logging:

   -debug

Data Options

The default operation for lineprobe is to forward values from a command to the
lineviewer collector. Lineprobe also has the capability to perform simple
operations on the raw data. Operations have this form:

 -operation=<type>,<samples>[,<percentile>]

Only the following operation types are supported: "avg", "stdev", "perc".
"avg" and "stdev" take a single argument; the number of samples to operate over.

The "perc", or percentile, type takes a second argument. The second argument is
the percentile to report on from the collected samples.

Plot Options

Lineprobe can send line and plot style hints to the lineviewer.

If not given, a line color is chosen automatically by the lineviewer. To
specify the line color use a format like:

   -color="#ffffff"

Lines are given a default name based on the command executed or current PID and
operation. To specify a custom name use:

   -label=<name>

To name the X and Y axis use:

   -xlabel=<label>
   -ylabel=<label>

By default, all lines are added to a single axis. To create multiple axes
specify a distinct axis name. Other lineprobes can use the same name to add
lines to the same axis. The axis name is not user visible.

   -axis=default

For a given axis name, you can specify the the minimum and maximum values for
the Y axis.

   -ylimit=<ymin>:<ymax>

Or, plot the Y-axis on a log scale.

   -ylog

Server Options

Normally the lineviewer runs indefinitely. When profiling the server, it is
necessary to cause the server to exit normally. This option tells the
lineviewer to exit.

   -exit

Examples

Reading data from stdin:
 while /usr/bin/true; do ps ax | wc -l ; sleep 1 ; done | lineprobe

Reading data from command output:
    lineprobe --command "ps ax | wc -l" --interval 2.0

*/
package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math"
	"os"
	"os/exec"
	"sort"
	"strconv"
	"time"

	// third-party
	"github.com/stephen-soltesz/go/lineserver"
)

var (
	hostname    = flag.String("hostname", "localhost:3131", "Host and port of view server.")
	command     = flag.String("command", "", "Command to run every second.")
	interval    = flag.Float64("interval", 1.0, "How often to run command.")
	axisName    = flag.String("axis", "default", "Name of axis to associate this line.")
	xlabelName  = flag.String("xlabel", "", "X-Label for axis.")
	ylabelName  = flag.String("ylabel", "", "Y-Label for axis.")
	ylimitValue = flag.String("ylimit", "", "Y-limits for axis 'ymin:ymax'.")
	yaxisScale  = flag.Bool("ylog", false, "Y-axis should be log scale.")
	lineName    = flag.String("label", "", "Line label name on axis.")
	lineColor   = flag.String("color", "", "Color of line. Chosen automatically by default.")
	quietOutput = flag.Bool("quiet", false, "Whether to echo values sent to server.")
	exitServer  = flag.Bool("exit", false, "Tell the lineviewer to exit.")

	calcOperations operationSlice

	debug       = flag.Bool("debug", false, "Enable debug messages on stderr.")
	debugLogger *log.Logger
)

type sampleSet struct {
	samples []float64
	pos     int
}

type ValueWriter struct {
	writer  *bufio.ReadWriter
	server  *lineserver.Server
	op      *Operation
	samples *sampleSet
}

type ValueReader struct {
	reader    *bufio.Reader
	cmd       *exec.Cmd
	fromStdin bool
}

func initFlags() {
	flag.Var(&calcOperations, "operation", "An operation to run on samples.")
}

func checkFlags() {
	if *debug {
		debugLogger = log.New(os.Stderr, "", 0)
	} else {
		debugLogger = log.New(ioutil.Discard, "", 0)
	}
	if len(calcOperations) == 0 {
		calcOperations = append(calcOperations, &Operation{OpPercentile, 1, 50})
	}
}

func printAllFlagsAndValues() {
	flag.VisitAll(func(f *flag.Flag) {
		debugLogger.Printf("%-30s %s\n", fmt.Sprintf("-%s=%s", f.Name, f.Value), f.Usage)
	})
}

func newSampleSet(sampleCount int64) *sampleSet {
	return &sampleSet{make([]float64, sampleCount), 0}
}

func (ss *sampleSet) Count() int {
	if ss.Full() {
		return len(ss.samples)
	} else {
		return ss.pos
	}
}

func (ss *sampleSet) Full() bool {
	return ss.pos >= len(ss.samples)-1
}

func (ss *sampleSet) Append(val float64) {
	ss.samples[ss.pos%len(ss.samples)] = val
	ss.pos += 1
}

func (ss *sampleSet) Mean() float64 {
	count := ss.Count()
	var s float64 = 0
	for i := 0; i < count; i++ {
		s += ss.samples[i]
	}
	avg := s / float64(count)
	debugLogger.Println("avg:", avg)
	return avg
}

func (ss *sampleSet) Stdev() float64 {
	count := ss.Count()
	avg := ss.Mean()
	var variance float64 = 0

	for i := 0; i < count; i++ {
		variance += math.Pow((ss.samples[i] - avg), 2)
	}
	stdev := math.Sqrt(variance / float64(count))
	debugLogger.Println("stdev:", stdev)
	return stdev
}

func (ss *sampleSet) Percentile(pct int64) float64 {
	count := ss.Count()
	var calc = make([]float64, count)
	copy(calc, ss.samples)

	i := int(math.Ceil(float64(count-1) * float64(pct) / 101.0))
	sort.Float64s(calc)
	debugLogger.Println("size", count, "i", i)
	debugLogger.Println("pct:", calc[i])
	return calc[i]
}

func NewValueWriter(op *Operation) *ValueWriter {
	var err error
	writer := ValueWriter{}
	writer.op = op
	debugLogger.Printf("%#v\n", op)
	writer.samples = newSampleSet(op.samples)
	writer.server = lineserver.NewServer(*hostname)
	if writer.writer, err = writer.server.Connect(); err != nil {
		log.Fatal(err)
	}
	return &writer
}

func SetupValueWriters() []*ValueWriter {
	var writers []*ValueWriter
	for _, op := range calcOperations {
		writer := NewValueWriter(op)
		if err := writer.sendClientSettings(); err != nil {
			log.Fatal(err)
		}
		writers = append(writers, writer)
	}
	return writers
}

func (w *ValueWriter) SendValue(val float64) error {
	var f float64
	w.samples.Append(val)
	if w.op.operation == OpMean {
		f = w.samples.Mean()
	} else if w.op.operation == OpStdev {
		// TODO: create +/- stdev around mean.
		f = w.samples.Stdev()
	} else if w.op.operation == OpPercentile {
		f = w.samples.Percentile(w.op.percentile)
	}

	out := formatFloat(f)
	fmt.Printf("Sending: %s", string(out))
	_, err := w.writer.Write(out)
	if err != nil {
		return err
	}
	w.writer.Flush()
	return nil
}

func (w *ValueWriter) sendClientSettings() error {
	var err error
	if *exitServer {
		debugLogger.Println("Sending exit")
		_, err := w.writer.WriteString("EXIT\n")
		if err != nil {
			return err
		}
		w.writer.Flush()
		// create new connection to trigger exit.
		time.Sleep(time.Second)
		NewValueWriter(&Operation{OpNone, 0, 0})
		os.Exit(0)
	}
	axisSetting := "axis:" + *axisName + ":" + *xlabelName + ":" + *ylabelName + "\n"
	if _, err = w.writer.WriteString(axisSetting); err != nil {
		return err
	}

	if *ylimitValue != "" {
		limitSetting := "limit:" + *ylimitValue + "\n"
		if _, err := w.writer.WriteString(limitSetting); err != nil {
			return err
		}
	}

	if *yaxisScale {
		yaxisScale := "yaxisscale:log\n"
		if _, err := w.writer.WriteString(yaxisScale); err != nil {
			return err
		}
	}

	name := ""
	if w.op.operation > OpNone {
		name = fmt.Sprintf("%s-", w.op)
	}

	if *lineName != "" {
		name = "label:" + *lineName + "\n"
	} else if *command != "" {
		name = "label:" + name + *command + "\n"
	} else {
		name = "label:" + name + fmt.Sprintf("Thread-%d\n", os.Getpid())
	}

	if _, err = w.writer.WriteString(name); err != nil {
		return err
	}

	if *lineColor != "" {
		colorSetting := fmt.Sprintf("color:%s\n", *lineColor)
		if _, err := w.writer.WriteString(colorSetting); err != nil {
			return err
		}
	}
	return nil
}

func NewValueReader() *ValueReader {
	reader := ValueReader{}
	reader.Setup()
	return &reader
}

func (r *ValueReader) HandleEOF() error {
	if r.fromStdin {
		// just pass along EOF if reading from Stdin.
		return io.EOF
	}

	// reading from a command.
	r.cmd.Wait() // must run Wait() after EOF to avoid races with Read().
	if !r.cmd.ProcessState.Success() {
		fmt.Println("Non-zero exit value from: " + *command)
		// TODO: exit with child exit status.
		os.Exit(1)
	}

	// pause before re-executing command.
	time.Sleep(time.Duration(*interval) * time.Second)
	return r.Setup()
}

func (r *ValueReader) Setup() error {
	r.fromStdin = ("" == *command)

	if r.fromStdin {
		r.reader = bufio.NewReader(os.Stdin)
	} else {
		r.cmd = exec.Command("bash", "-c", *command)
		output, err := r.cmd.StdoutPipe()
		if err != nil {
			debugLogger.Println("failed StdoutPipe", err)
			return err
		}
		if err := r.cmd.Start(); err != nil {
			debugLogger.Println("failed to Start command", err)
			return err
		}
		r.reader = bufio.NewReader(output)
	}
	return nil
}

func (r *ValueReader) ReadValue() (float64, error) {
	var err error
	var lineTooLong bool
	var out []byte

	out, lineTooLong, err = r.reader.ReadLine()
	if lineTooLong {
		return 0.0, errors.New("Line too long.")
	} else if err == io.EOF {
		err := r.HandleEOF()
		if err != nil {
			return 0.0, err
		}
		return r.ReadValue()
	} else if err != nil {
		fmt.Println("Unknown failure:", err)
		return 0.0, err
	}

	f, err := strconv.ParseFloat(string(out), 64)
	if err != nil {
		return 0.0, err
	}
	if !*quietOutput {
		fmt.Printf("Received: %f\n", f)
	}
	return f, nil
}

func parseFloat(val []byte) float64 {
	fmt.Println("%v", val)
	f, err := strconv.ParseFloat(string(val), 64)
	if err != nil {
		debugLogger.Println(err)
		return 0.0
	}
	return f
}

func formatFloat(f float64) []byte {
	return []byte(fmt.Sprintf("%f\n", f))
}

func main() {
	var err error
	var val float64

	initFlags()
	flag.Parse()
	checkFlags()

	if !*quietOutput {
		printAllFlagsAndValues()
	}

	// allocate value reader and writers.
	reader := NewValueReader()
	writers := SetupValueWriters()

	for {
		// read one value.
		if val, err = reader.ReadValue(); err != nil {
			if err != io.EOF {
				// only report non-EOF.
				log.Println(err)
			}
			break
		}
		// and, send value to all writers.
		for _, writer := range writers {
			if err = writer.SendValue(val); err != nil {
				log.Fatal(err)
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package auth

import (
	. "github.com/eynstudio/gobreak"
	"github.com/eynstudio/gobreak/di"
	"github.com/eynstudio/gow/auth/domains"
	. "github.com/eynstudio/gow/auth/models"
	"github.com/eynstudio/gow/auth/repo"
)

func Init() {
	repoRes := NewResRepo()
	di.MapAs(repoRes, (*repo.IResRepo)(nil)).Apply(repoRes.MgoRepo)

	repoRole := NewRoleRepo()
	di.MapAs(repoRole, (*repo.IRoleRepo)(nil)).Apply(repoRole.MgoRepo)

	repoOrg := NewOrgRepo()
	di.MapAs(repoOrg, (*repo.IOrgRepo)(nil)).Apply(repoOrg.MgoRepo)

	repoUser := NewUserRepo()
	di.MapAs(repoUser, (*repo.IUserRepo)(nil)).Apply(repoUser.MgoRepo)

	di.ApplyAndMap(&AuthCtx{})
}

type AuthCtx struct {
	repo.IResRepo  `di`
	repo.IOrgRepo  `di`
	repo.IRoleRepo `di`
	repo.IUserRepo `di`
	OnLogin        func(login *Login) (*LoginOk, bool)
	OnLoginByToken func(token string) (*LoginOk, bool)
}

func (p *AuthCtx) LoginByToken(token string) (*LoginOk, bool) { return p.OnLoginByToken(token) }
func (p *AuthCtx) Login(login *Login) (*LoginOk, bool)        { return p.OnLogin(login) }

func (p *AuthCtx) GetResTree() []*TreeNode {
	return BuildTree(p.IResRepo.All())
}

func (p *AuthCtx) GetOrgTree() []*TreeNode {
	return BuildTree(p.IOrgRepo.All())
}

func (p *AuthCtx) GetRoleTree() []*TreeNode {
	return BuildTree(p.IRoleRepo.All())
}

func (p *AuthCtx) GetUserPage() T {
	return p.IUserRepo.All()
}

func (p *AuthCtx) SaveOrg(m Org) {
	p.IOrgRepo.Save(m.Id, m)
}

func (p *AuthCtx) SaveRes(m Res) {
	p.IResRepo.Save(m.Id, m)
}
func (p *AuthCtx) SaveRole(m Role) {
	p.IRoleRepo.Save(m.Id, m)
}
func (p *AuthCtx) SaveUser(m User) {
	p.IUserRepo.Save(m.Id, m)
}

func (p *AuthCtx) GetRes(id GUID) (m *domains.ResAgg) {
	m = domains.NewResAgg(id)
	return
}

func (p *AuthCtx) GetRole(id GUID) (m *domains.RoleAgg) {
	m = domains.NewRoleAgg(id)
	return
}

func (p *AuthCtx) GetOrg(id GUID) (m *domains.OrgAgg) {
	m = domains.NewOrgAgg(id)
	return
}

func (p *AuthCtx) GetUser(id GUID) (m *domains.UserAgg) {
	m = domains.NewUserAgg(id)
	return
}

func (p *AuthCtx) GetNcImg(id GUID) (nc string, img string) {
	return p.GetUser(id).GetNcImg()
}

func (p *AuthCtx) CheckThenUpdatePwd(id GUID, pwd0, pwd1 string) Status {
	u := p.GetUser(id)
	u.CheckThenUpdatePwd(SaltPwd(pwd0), SaltPwd(pwd1))
	if u.NotErr() {
		return OkStatus
	}
	return u.GetStatus()
}

func (p *AuthCtx) UpdatePwd(id GUID, pwd string) Status {
	u := p.GetUser(id)
	u.UpdatePwd(SaltPwd(pwd))
	if u.NotErr() {
		return OkStatus
	}
	return u.GetStatus()
}
<commit_msg>update<commit_after>package auth

import (
	. "github.com/eynstudio/gobreak"
	"github.com/eynstudio/gobreak/di"
	"github.com/eynstudio/gow/auth/domains"
	. "github.com/eynstudio/gow/auth/models"
	"github.com/eynstudio/gow/auth/repo"
)

func Init() {
	repoRes := NewResRepo()
	di.MapAs(repoRes, (*repo.IResRepo)(nil)).Apply(repoRes.MgoRepo)

	repoRole := NewRoleRepo()
	di.MapAs(repoRole, (*repo.IRoleRepo)(nil)).Apply(repoRole.MgoRepo)

	repoOrg := NewOrgRepo()
	di.MapAs(repoOrg, (*repo.IOrgRepo)(nil)).Apply(repoOrg.MgoRepo)

	repoUser := NewUserRepo()
	di.MapAs(repoUser, (*repo.IUserRepo)(nil)).Apply(repoUser.MgoRepo)

	di.ApplyAndMap(&AuthCtx{})
}

type AuthCtx struct {
	repo.IResRepo  `di`
	repo.IOrgRepo  `di`
	repo.IRoleRepo `di`
	repo.IUserRepo `di`
	OnLogin        func(login *Login) (*LoginOk, bool)
	OnLoginByToken func(token string) (*LoginOk, bool)
}

func (p *AuthCtx) LoginByToken(token string) (*LoginOk, bool) { return p.OnLoginByToken(token) }
func (p *AuthCtx) Login(login *Login) (*LoginOk, bool)        { return p.OnLogin(login) }

func (p *AuthCtx) GetResTree() []*TreeNode {
	return BuildTree(p.IResRepo.All())
}

func (p *AuthCtx) GetOrgTree() []*TreeNode {
	return BuildTree(p.IOrgRepo.All())
}

func (p *AuthCtx) GetRoleTree() []*TreeNode {
	return BuildTree(p.IRoleRepo.All())
}

func (p *AuthCtx) GetUserPage() T {
	return p.IUserRepo.All()
}

func (p *AuthCtx) SaveOrg(m Org) {
	p.IOrgRepo.Save(m.Id, m)
}

func (p *AuthCtx) SaveRes(m Res) {
	p.IResRepo.Save(m.Id, m)
}
func (p *AuthCtx) SaveRole(m Role) {
	p.IRoleRepo.Save(m.Id, m)
}
func (p *AuthCtx) SaveUser(m User) {
	p.IUserRepo.Save(m.Id, m)
}

func (p *AuthCtx) GetRes(id GUID) (m *domains.ResAgg) {
	m = domains.NewResAgg(id)
	return
}

func (p *AuthCtx) GetRole(id GUID) (m *domains.RoleAgg) {
	m = domains.NewRoleAgg(id)
	return
}

func (p *AuthCtx) GetOrg(id GUID) (m *domains.OrgAgg) {
	m = domains.NewOrgAgg(id)
	return
}

func (p *AuthCtx) GetUser(id GUID) (m *domains.UserAgg) {
	m = domains.NewUserAgg(id)
	return
}

func (p *AuthCtx) GetNcImg(id GUID) (nc string, img string) {
	return p.GetUser(id).GetNcImg()
}

func (p *AuthCtx) CheckThenUpdatePwd(id GUID, pwd0, pwd1 string) Status {
	u := p.GetUser(id)
	u.CheckThenUpdatePwd(SaltPwd(pwd0), SaltPwd(pwd1))
	if u.NotErr() {

		return OkStatus
	}
	return u.GetStatus()
}

func (p *AuthCtx) UpdatePwd(id GUID, pwd string) Status {
	u := p.GetUser(id)
	u.UpdatePwd(SaltPwd(pwd))
	if u.NotErr() {
		return OkStatus
	}
	return u.GetStatus()
}
<|endoftext|>


<commit_before>package auth

import (
	"crypto/rsa"
	"fmt"
	"net/http"
	"strings"

	"github.com/LUSHDigital/microservice-core-golang/response"
	jwt "github.com/dgrijalva/jwt-go"
)

const (
	authHeader       = "Authorization"
	authHeaderPrefix = "Bearer "

	errorMessageMalformed     = "token malformed"
	errorMessageExpired       = "token expired or not yet valid"
	errorMessageInvalid       = "invalid token"
	errorMessageClaimsInvalid = "invalid token claims"
)

// ConsumerFor derives the Consumer from the JWT claims
func ConsumerFor(token *jwt.Token) *Consumer {
	return &token.Claims.(*JWTClaims).Consumer
}

// JWTClaims represents the claims within the JWT.
type JWTClaims struct {
	Consumer Consumer `json:"consumer"`
	jwt.StandardClaims
}

// JWTResponder defines the behaviour of validating a JWT
type JWTResponder interface {
	OnUnauthorizedErr(err error)
	OnComplete(token *jwt.Token)
}

type jwtHTTPResponder struct {
	w    http.ResponseWriter
	r    *http.Request
	next http.HandlerFunc
}

func (r *jwtHTTPResponder) OnUnauthorizedErr(err error) {
	response.New(http.StatusUnauthorized, err.Error(), nil).WriteTo(r.w)
}

func (r *jwtHTTPResponder) OnComplete(token *jwt.Token) {
	ctx := ContextWithConsumer(r.r.Context(), token.Claims.(*JWTClaims).Consumer)
	r.next.ServeHTTP(r.w, r.r.WithContext(ctx))
}

// ValidateJWT takes the raw JWT and the public RSA key
func ValidateJWT(raw string, publicKey *rsa.PublicKey, responder JWTResponder) {
	// Parse the JWT token
	token, err := jwt.ParseWithClaims(raw, &JWTClaims{}, func(t *jwt.Token) (interface{}, error) {
		// Ensure the signing method was not changed
		if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
		}
		return publicKey, nil
	})

	// Bail out if the token could not be parsed
	if err != nil {
		if _, ok := err.(*jwt.ValidationError); ok {
			// Handle any token specific errors
			var errorMessage string
			if err.(*jwt.ValidationError).Errors&jwt.ValidationErrorMalformed != 0 {
				errorMessage = errorMessageMalformed
			} else if err.(*jwt.ValidationError).Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
				errorMessage = errorMessageExpired
			} else {
				errorMessage = errorMessageInvalid
			}
			responder.OnUnauthorizedErr(fmt.Errorf(errorMessage))
			return
		}
		responder.OnUnauthorizedErr(fmt.Errorf(errorMessageInvalid))
		return
	}

	// Check the claims and token are valid
	if _, ok := token.Claims.(*JWTClaims); !ok || !token.Valid {
		responder.OnUnauthorizedErr(fmt.Errorf(errorMessageClaimsInvalid))
		return
	}

	responder.OnComplete(token)
}

// HandlerValidateJWT takes a JWT from the request headers, attempts validation and returns a http handler.
func HandlerValidateJWT(publicKey *rsa.PublicKey, next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := strings.TrimPrefix(r.Header.Get(authHeader), authHeaderPrefix)
		responder := &jwtHTTPResponder{w, r, next}
		ValidateJWT(token, publicKey, responder)
	})
}
<commit_msg>fix: assert that type check works when asserting JWT claims<commit_after>package auth

import (
	"crypto/rsa"
	"fmt"
	"net/http"
	"strings"

	"github.com/LUSHDigital/microservice-core-golang/response"
	jwt "github.com/dgrijalva/jwt-go"
)

const (
	authHeader       = "Authorization"
	authHeaderPrefix = "Bearer "

	errorMessageMalformed     = "token malformed"
	errorMessageExpired       = "token expired or not yet valid"
	errorMessageInvalid       = "invalid token"
	errorMessageClaimsInvalid = "invalid token claims"
)

// ConsumerFor derives the Consumer from the JWT claims
func ConsumerFor(token *jwt.Token) (*Consumer, error) {
	if claims, ok := token.Claims.(*JWTClaims); ok {
		return &claims.Consumer, nil
	}
	return nil, fmt.Errorf("cannot assert claims for type %T", token.Claims)
}

// JWTClaims represents the claims within the JWT.
type JWTClaims struct {
	Consumer Consumer `json:"consumer"`
	jwt.StandardClaims
}

// JWTResponder defines the behaviour of validating a JWT
type JWTResponder interface {
	OnUnauthorizedErr(err error)
	OnComplete(token *jwt.Token)
}

type jwtHTTPResponder struct {
	w    http.ResponseWriter
	r    *http.Request
	next http.HandlerFunc
}

func (r *jwtHTTPResponder) OnUnauthorizedErr(err error) {
	response.New(http.StatusUnauthorized, err.Error(), nil).WriteTo(r.w)
}

func (r *jwtHTTPResponder) OnComplete(token *jwt.Token) {
	ctx := ContextWithConsumer(r.r.Context(), token.Claims.(*JWTClaims).Consumer)
	r.next.ServeHTTP(r.w, r.r.WithContext(ctx))
}

// ValidateJWT takes the raw JWT and the public RSA key
func ValidateJWT(raw string, publicKey *rsa.PublicKey, responder JWTResponder) {
	// Parse the JWT token
	token, err := jwt.ParseWithClaims(raw, &JWTClaims{}, func(t *jwt.Token) (interface{}, error) {
		// Ensure the signing method was not changed
		if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
		}
		return publicKey, nil
	})

	// Bail out if the token could not be parsed
	if err != nil {
		if _, ok := err.(*jwt.ValidationError); ok {
			// Handle any token specific errors
			var errorMessage string
			if err.(*jwt.ValidationError).Errors&jwt.ValidationErrorMalformed != 0 {
				errorMessage = errorMessageMalformed
			} else if err.(*jwt.ValidationError).Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
				errorMessage = errorMessageExpired
			} else {
				errorMessage = errorMessageInvalid
			}
			responder.OnUnauthorizedErr(fmt.Errorf(errorMessage))
			return
		}
		responder.OnUnauthorizedErr(fmt.Errorf(errorMessageInvalid))
		return
	}

	// Check the claims and token are valid
	if _, ok := token.Claims.(*JWTClaims); !ok || !token.Valid {
		responder.OnUnauthorizedErr(fmt.Errorf(errorMessageClaimsInvalid))
		return
	}

	responder.OnComplete(token)
}

// HandlerValidateJWT takes a JWT from the request headers, attempts validation and returns a http handler.
func HandlerValidateJWT(publicKey *rsa.PublicKey, next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token := strings.TrimPrefix(r.Header.Get(authHeader), authHeaderPrefix)
		responder := &jwtHTTPResponder{w, r, next}
		ValidateJWT(token, publicKey, responder)
	})
}
<|endoftext|>


<commit_before>/*
Package authboss is a modular authentication system for the web. It tries to
remove as much boilerplate and "hard things" as possible so that each time you
start a new web project in Go, you can plug it in, configure and be off to the
races without having to think about how to store passwords or remember tokens.
*/
package authboss // import "gopkg.in/authboss.v0"

import (
	"database/sql"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strings"

	"golang.org/x/crypto/bcrypt"
)

// Authboss contains a configuration and other details for running.
type Authboss struct {
	Config
	Callbacks *Callbacks

	loadedModules    map[string]Modularizer
	moduleAttributes AttributeMeta
	mux              *http.ServeMux
}

// New makes a new instance of authboss with a default
// configuration.
func New() *Authboss {
	ab := &Authboss{
		Callbacks:        NewCallbacks(),
		loadedModules:    make(map[string]Modularizer),
		moduleAttributes: make(AttributeMeta),
	}
	ab.Config.Defaults()
	return ab
}

// Init authboss and the requested modules. modulesToLoad is left empty
// all registered modules will be loaded.
func (a *Authboss) Init(modulesToLoad ...string) error {
	if len(modulesToLoad) == 0 {
		modulesToLoad = RegisteredModules()
	}

	for _, name := range modulesToLoad {
		fmt.Fprintf(a.LogWriter, "%-10s Loading\n", "["+name+"]")
		if err := a.loadModule(name); err != nil {
			return fmt.Errorf("[%s] Error Initializing: %v", name, err)
		}
	}

	for _, mod := range a.loadedModules {
		for k, v := range mod.Storage() {
			a.moduleAttributes[k] = v
		}
	}

	return nil
}

// CurrentUser retrieves the current user from the session and the database.
func (a *Authboss) CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) {
	return a.currentUser(a.InitContext(w, r), w, r)
}

func (a *Authboss) currentUser(ctx *Context, w http.ResponseWriter, r *http.Request) (interface{}, error) {
	_, err := a.Callbacks.FireBefore(EventGetUserSession, ctx)
	if err != nil {
		return nil, err
	}

	key, ok := ctx.SessionStorer.Get(SessionKey)
	if !ok {
		return nil, nil
	}

	_, err = a.Callbacks.FireBefore(EventGetUser, ctx)
	if err != nil {
		return nil, err
	}

	var user interface{}

	if index := strings.IndexByte(key, ';'); index > 0 {
		user, err = a.OAuth2Storer.GetOAuth(key[:index], key[index+1:])
	} else {
		user, err = a.Storer.Get(key)
	}

	if err != nil {
		return nil, err
	}

	ctx.User = Unbind(user)

	err = a.Callbacks.FireAfter(EventGetUser, ctx)
	if err != nil {
		return nil, err
	}

	return user, err
}

// CurrentUserP retrieves the current user but panics if it's not available for
// any reason.
func (a *Authboss) CurrentUserP(w http.ResponseWriter, r *http.Request) interface{} {
	i, err := a.CurrentUser(w, r)
	if err != nil {
		panic(err.Error())
	}
	return i
}

/*
UpdatePassword should be called to recalculate hashes and do any cleanup
that should occur on password resets. Updater should return an error if the
update to the user failed (for reasons say like validation, duplicate
primary key, etc...). In that case the cleanup will not be performed.

The w and r parameters are for establishing session and cookie storers.

The ptPassword parameter is the new password to update to. updater is called
regardless if this is empty or not, but if it is empty, it will not set a new
password before calling updater.

The user parameter is the user struct which will have it's
Password string/sql.NullString value set to the new bcrypted password. Therefore
it must be passed in as a pointer with the Password field exported or an error
will be returned.

The error returned is returned either from the updater if that produced an error
or from the cleanup routines.
*/
func (a *Authboss) UpdatePassword(w http.ResponseWriter, r *http.Request,
	ptPassword string, user interface{}, updater func() error) error {

	updatePwd := len(ptPassword) > 0

	if updatePwd {
		pass, err := bcrypt.GenerateFromPassword([]byte(ptPassword), a.BCryptCost)
		if err != nil {
			return err
		}

		val := reflect.ValueOf(user).Elem()
		field := val.FieldByName("Password")
		if !field.CanSet() {
			return errors.New("authboss: UpdatePassword called without a modifyable user struct")
		}
		fieldPtr := field.Addr()

		if scanner, ok := fieldPtr.Interface().(sql.Scanner); ok {
			if err := scanner.Scan(string(pass)); err != nil {
				return err
			}
		} else {
			field.SetString(string(pass))
		}
	}

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

	if !updatePwd {
		return nil
	}

	return a.Callbacks.FireAfter(EventPasswordReset, a.InitContext(w, r))
}
<commit_msg>Authboss.moduleAttributes -> Authboss.ModuleAttributes<commit_after>/*
Package authboss is a modular authentication system for the web. It tries to
remove as much boilerplate and "hard things" as possible so that each time you
start a new web project in Go, you can plug it in, configure and be off to the
races without having to think about how to store passwords or remember tokens.
*/
package authboss // import "gopkg.in/authboss.v0"

import (
	"database/sql"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strings"

	"golang.org/x/crypto/bcrypt"
)

// Authboss contains a configuration and other details for running.
type Authboss struct {
	Config
	Callbacks *Callbacks

	loadedModules    map[string]Modularizer
	ModuleAttributes AttributeMeta
	mux              *http.ServeMux
}

// New makes a new instance of authboss with a default
// configuration.
func New() *Authboss {
	ab := &Authboss{
		Callbacks:        NewCallbacks(),
		loadedModules:    make(map[string]Modularizer),
		ModuleAttributes: make(AttributeMeta),
	}
	ab.Config.Defaults()
	return ab
}

// Init authboss and the requested modules. modulesToLoad is left empty
// all registered modules will be loaded.
func (a *Authboss) Init(modulesToLoad ...string) error {
	if len(modulesToLoad) == 0 {
		modulesToLoad = RegisteredModules()
	}

	for _, name := range modulesToLoad {
		fmt.Fprintf(a.LogWriter, "%-10s Loading\n", "["+name+"]")
		if err := a.loadModule(name); err != nil {
			return fmt.Errorf("[%s] Error Initializing: %v", name, err)
		}
	}

	for _, mod := range a.loadedModules {
		for k, v := range mod.Storage() {
			a.ModuleAttributes[k] = v
		}
	}

	return nil
}

// CurrentUser retrieves the current user from the session and the database.
func (a *Authboss) CurrentUser(w http.ResponseWriter, r *http.Request) (interface{}, error) {
	return a.currentUser(a.InitContext(w, r), w, r)
}

func (a *Authboss) currentUser(ctx *Context, w http.ResponseWriter, r *http.Request) (interface{}, error) {
	_, err := a.Callbacks.FireBefore(EventGetUserSession, ctx)
	if err != nil {
		return nil, err
	}

	key, ok := ctx.SessionStorer.Get(SessionKey)
	if !ok {
		return nil, nil
	}

	_, err = a.Callbacks.FireBefore(EventGetUser, ctx)
	if err != nil {
		return nil, err
	}

	var user interface{}

	if index := strings.IndexByte(key, ';'); index > 0 {
		user, err = a.OAuth2Storer.GetOAuth(key[:index], key[index+1:])
	} else {
		user, err = a.Storer.Get(key)
	}

	if err != nil {
		return nil, err
	}

	ctx.User = Unbind(user)

	err = a.Callbacks.FireAfter(EventGetUser, ctx)
	if err != nil {
		return nil, err
	}

	return user, err
}

// CurrentUserP retrieves the current user but panics if it's not available for
// any reason.
func (a *Authboss) CurrentUserP(w http.ResponseWriter, r *http.Request) interface{} {
	i, err := a.CurrentUser(w, r)
	if err != nil {
		panic(err.Error())
	}
	return i
}

/*
UpdatePassword should be called to recalculate hashes and do any cleanup
that should occur on password resets. Updater should return an error if the
update to the user failed (for reasons say like validation, duplicate
primary key, etc...). In that case the cleanup will not be performed.

The w and r parameters are for establishing session and cookie storers.

The ptPassword parameter is the new password to update to. updater is called
regardless if this is empty or not, but if it is empty, it will not set a new
password before calling updater.

The user parameter is the user struct which will have it's
Password string/sql.NullString value set to the new bcrypted password. Therefore
it must be passed in as a pointer with the Password field exported or an error
will be returned.

The error returned is returned either from the updater if that produced an error
or from the cleanup routines.
*/
func (a *Authboss) UpdatePassword(w http.ResponseWriter, r *http.Request,
	ptPassword string, user interface{}, updater func() error) error {

	updatePwd := len(ptPassword) > 0

	if updatePwd {
		pass, err := bcrypt.GenerateFromPassword([]byte(ptPassword), a.BCryptCost)
		if err != nil {
			return err
		}

		val := reflect.ValueOf(user).Elem()
		field := val.FieldByName("Password")
		if !field.CanSet() {
			return errors.New("authboss: UpdatePassword called without a modifyable user struct")
		}
		fieldPtr := field.Addr()

		if scanner, ok := fieldPtr.Interface().(sql.Scanner); ok {
			if err := scanner.Scan(string(pass)); err != nil {
				return err
			}
		} else {
			field.SetString(string(pass))
		}
	}

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

	if !updatePwd {
		return nil
	}

	return a.Callbacks.FireAfter(EventPasswordReset, a.InitContext(w, r))
}
<|endoftext|>


<commit_before>package video

import "errors"
import "image"

var ErrInvalidReservedBits = errors.New("invalid reserved bits")

type Slice struct {
	slice_start_code                  StartCode // 32 bslbf
	slice_vertical_position_extension uint32    // 3 uimsbf
	priority_breakpoint               uint32    // 7 uimsbf
	quantiser_scale_code              uint32    // 5 uimsbf
	intra_slice_flag                  bool      // 1 bslbf
	intra_slice                       bool      // 1 uimsbf
	extra_information                 []uint8
}

func (br *VideoSequence) slice(frame *image.YCbCr) error {

	code, err := br.Read32(32)
	if err != nil {
		return err
	}

	if StartCode(code).isSlice() == false {
		return ErrUnexpectedStartCode
	}

	br.resetDCPredictors()

	// Reset motion vector predictors: Start if each slice (7.6.4.3)
	br.pMV.reset()

	s := Slice{}
	s.slice_start_code = StartCode(code)

	mb_row := int((code & 0xFF) - 1)

	frameSlice := &image.YCbCr{
		Y:       frame.Y[16*mb_row*frame.YStride:],
		Cb:      frame.Cb[8*mb_row*frame.CStride:],
		Cr:      frame.Cr[8*mb_row*frame.CStride:],
		YStride: frame.YStride,
		CStride: frame.CStride,
		Rect:    image.Rect(frame.Rect.Min.X, mb_row*16, frame.Rect.Max.X, mb_row*16+16),
	}

	if br.SequenceHeader.vertical_size_value > 2800 {
		s.slice_vertical_position_extension, err = br.Read32(3)
		if err != nil {
			return err
		}
	}

	// TODO(jh): sequence_scalable support
	if false /* (<sequence_scalable_extension() is present in the bitstream>) */ {
		if false /* (scalable_mode == “data partitioning” ) */ {
			s.priority_breakpoint, err = br.Read32(7)
			if err != nil {
				return err
			}
		}
	}

	if qsc, err := br.Read32(5); err != nil {
		return err
	} else {
		s.quantiser_scale_code = qsc
		br.currentQSC = qsc
	}

	if nextbits, err := br.Peek32(1); err != nil {
		return err
	} else if nextbits == 1 {
		s.intra_slice_flag, err = br.ReadBit()
		if err != nil {
			return err
		}

		s.intra_slice, err = br.ReadBit()
		if err != nil {
			return err
		}

		if reserved_bits, err := br.Read32(7); err != nil {
			return err
		} else if reserved_bits != 0 {
			return ErrInvalidReservedBits
		}

		for {
			if nextbits, err := br.Peek32(1); err != nil {
				return err
			} else if nextbits != 1 {
				break
			}

			if err = br.Trash(1); err != nil {
				return err
			}

			if data, err := br.Read32(8); err != nil {
				return err
			} else {
				s.extra_information = append(s.extra_information, uint8(data))
			}
		}
	}

	if err := br.Trash(1); err != nil {
		return err
	}

	var mb_address int = -1
	for {
		mb_address, err = br.macroblock(mb_address, mb_row, frameSlice)
		if err != nil {
			return err
		}

		if nextbits, err := br.Peek32(23); err != nil {
			return err
		} else if nextbits == 0 {
			break
		}
	}

	return next_start_code(br)
}
<commit_msg>compressing error handling a bit<commit_after>package video

import "errors"
import "image"

var ErrInvalidReservedBits = errors.New("invalid reserved bits")

type Slice struct {
	slice_start_code                  StartCode // 32 bslbf
	slice_vertical_position_extension uint32    // 3 uimsbf
	priority_breakpoint               uint32    // 7 uimsbf
	quantiser_scale_code              uint32    // 5 uimsbf
	intra_slice_flag                  bool      // 1 bslbf
	intra_slice                       bool      // 1 uimsbf
	extra_information                 []uint8
}

func (br *VideoSequence) slice(frame *image.YCbCr) error {

	code, err := br.Read32(32)
	if err != nil {
		return err
	}

	if StartCode(code).isSlice() == false {
		return ErrUnexpectedStartCode
	}

	br.resetDCPredictors()

	// Reset motion vector predictors: Start if each slice (7.6.4.3)
	br.pMV.reset()

	s := Slice{}
	s.slice_start_code = StartCode(code)

	mb_row := int((code & 0xFF) - 1)

	frameSlice := &image.YCbCr{
		Y:       frame.Y[16*mb_row*frame.YStride:],
		Cb:      frame.Cb[8*mb_row*frame.CStride:],
		Cr:      frame.Cr[8*mb_row*frame.CStride:],
		YStride: frame.YStride,
		CStride: frame.CStride,
		Rect:    image.Rect(frame.Rect.Min.X, mb_row*16, frame.Rect.Max.X, mb_row*16+16),
	}

	if br.SequenceHeader.vertical_size_value > 2800 {
		s.slice_vertical_position_extension, err = br.Read32(3)
		if err != nil {
			return err
		}
	}

	// TODO(jh): sequence_scalable support
	if false /* (<sequence_scalable_extension() is present in the bitstream>) */ {
		if false /* (scalable_mode == “data partitioning” ) */ {
			s.priority_breakpoint, err = br.Read32(7)
			if err != nil {
				return err
			}
		}
	}

	if qsc, err := br.Read32(5); err != nil {
		return err
	} else {
		s.quantiser_scale_code = qsc
		br.currentQSC = qsc
	}

	if nextbits, err := br.Peek32(1); err != nil {
		return err
	} else if nextbits == 1 {
		s.intra_slice_flag, err = br.ReadBit()
		if err != nil {
			return err
		}

		s.intra_slice, err = br.ReadBit()
		if err != nil {
			return err
		}

		if reserved_bits, err := br.Read32(7); err != nil {
			return err
		} else if reserved_bits != 0 {
			return ErrInvalidReservedBits
		}

		for {
			if nextbits, err := br.Peek32(1); err != nil {
				return err
			} else if nextbits != 1 {
				break
			} else if err := br.Trash(1); err != nil {
				return err
			}

			if data, err := br.Read32(8); err != nil {
				return err
			} else {
				s.extra_information = append(s.extra_information, uint8(data))
			}
		}
	}

	if err := br.Trash(1); err != nil {
		return err
	}

	var mb_address int = -1
	for {
		mb_address, err = br.macroblock(mb_address, mb_row, frameSlice)
		if err != nil {
			return err
		}

		if nextbits, err := br.Peek32(23); err != nil {
			return err
		} else if nextbits == 0 {
			break
		}
	}

	return next_start_code(br)
}
<|endoftext|>