<commit_before>package kubernetes

import (
	"errors"
	"fmt"
	"io"
	"time"

	"golang.org/x/net/context"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	"k8s.io/kubernetes/pkg/client/restclient"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	clientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"

	"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
)

func init() {
	clientcmd.DefaultCluster = clientcmdapi.Cluster{}
}

func getKubeClientConfig(config *common.KubernetesConfig) (*restclient.Config, error) {
	switch {
	case len(config.CertFile) > 0:
		if len(config.KeyFile) == 0 || len(config.CAFile) == 0 {
			return nil, fmt.Errorf("ca file, cert file and key file must be specified when using file based auth")
		}
		return &restclient.Config{
			Host: config.Host,
			TLSClientConfig: restclient.TLSClientConfig{
				CertFile: config.CertFile,
				KeyFile:  config.KeyFile,
				CAFile:   config.CAFile,
			},
		}, nil

	case len(config.Host) > 0:
		return &restclient.Config{
			Host: config.Host,
		}, nil

	default:
		config, err := clientcmd.NewDefaultClientConfigLoadingRules().Load()
		if err != nil {
			return nil, err
		}

		clientConfig := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{})
		return clientConfig.ClientConfig()
	}
}

func getKubeClient(config *common.KubernetesConfig) (*client.Client, error) {
	restConfig, err := getKubeClientConfig(config)
	if err != nil {
		return nil, err
	}

	return client.New(restConfig)
}

func isRunning(pod *api.Pod) (bool, error) {
	switch pod.Status.Phase {
	case api.PodRunning:
		return true, nil
	case api.PodSucceeded:
		return false, fmt.Errorf("pod already succeeded before it begins running")
	case api.PodFailed:
		return false, fmt.Errorf("pod status is failed")
	default:
		return false, nil
	}
}

type podPhaseResponse struct {
	done  bool
	phase api.PodPhase
	err   error
}

func getPodPhase(c *client.Client, pod *api.Pod, out io.Writer) podPhaseResponse {
	pod, err := c.Pods(pod.Namespace).Get(pod.Name)
	if err != nil {
		return podPhaseResponse{true, api.PodUnknown, err}
	}

	ready, err := isRunning(pod)

	if err != nil {
		return podPhaseResponse{true, pod.Status.Phase, err}
	}

	if ready {
		return podPhaseResponse{true, pod.Status.Phase, nil}
	}

	// check status of containers
	for _, container := range pod.Status.ContainerStatuses {
		if container.Ready {
			continue
		}
		if container.State.Waiting == nil {
			continue
		}

		switch container.State.Waiting.Reason {
		case "ErrImagePull", "ImagePullBackOff":
			return podPhaseResponse{true, api.PodUnknown, errors.New(container.State.Waiting.Message)}
		}
	}

	fmt.Fprintf(out, "Waiting for pod %s/%s to be running, status is %s\n", pod.Namespace, pod.Name, pod.Status.Phase)
	return podPhaseResponse{false, pod.Status.Phase, nil}

}

func triggerPodPhaseCheck(c *client.Client, pod *api.Pod, out io.Writer) <-chan podPhaseResponse {
	errc := make(chan podPhaseResponse)
	go func() {
		defer close(errc)
		errc <- getPodPhase(c, pod, out)
	}()
	return errc
}

// waitForPodRunning will use client c to detect when pod reaches the PodRunning
// state. It will check every second, and will return the final PodPhase once
// either PodRunning, PodSucceeded or PodFailed has been reached. In the case of
// PodRunning, it will also wait until all containers within the pod are also Ready
// Returns error if the call to retrieve pod details fails
func waitForPodRunning(ctx context.Context, c *client.Client, pod *api.Pod, out io.Writer) (api.PodPhase, error) {
	for i := 0; i < 60; i++ {
		select {
		case r := <-triggerPodPhaseCheck(c, pod, out):
			if r.done {
				return r.phase, r.err
			} else {
				time.Sleep(3 * time.Second)
			}
		case <-ctx.Done():
			return api.PodUnknown, ctx.Err()
		}
	}
	return api.PodUnknown, errors.New("timedout waiting for pod to start")
}

// limits takes a string representing CPU & memory limits,
// and returns a ResourceList with appropriately scaled Quantity
// values for Kubernetes. This allows users to write "500m" for CPU,
// and "50Mi" for memory (etc.)
func limits(cpu, memory string) (api.ResourceList, error) {
	var rCPU, rMem resource.Quantity
	var err error

	parse := func(s string) (resource.Quantity, error) {
		var q resource.Quantity
		if len(s) == 0 {
			return q, nil
		}
		if q, err = resource.ParseQuantity(s); err != nil {
			return q, fmt.Errorf("error parsing resource limit: %s", err.Error())
		}
		return q, nil
	}

	if rCPU, err = parse(cpu); err != nil {
		return api.ResourceList{}, nil
	}

	if rMem, err = parse(memory); err != nil {
		return api.ResourceList{}, nil
	}

	l := make(api.ResourceList)

	q := resource.Quantity{}
	if rCPU != q {
		l[api.ResourceLimitsCPU] = rCPU
	}
	if rMem != q {
		l[api.ResourceLimitsMemory] = rMem
	}

	return l, nil
}

// buildVariables converts a common.BuildVariables into a list of
// kubernetes EnvVar objects
func buildVariables(bv common.BuildVariables) []api.EnvVar {
	e := make([]api.EnvVar, len(bv))
	for i, b := range bv {
		e[i] = api.EnvVar{
			Name:  b.Key,
			Value: b.Value,
		}
	}
	return e
}
<commit_msg>Make lint happy<commit_after>package kubernetes

import (
	"errors"
	"fmt"
	"io"
	"time"

	"golang.org/x/net/context"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	"k8s.io/kubernetes/pkg/client/restclient"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	clientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"

	"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
)

func init() {
	clientcmd.DefaultCluster = clientcmdapi.Cluster{}
}

func getKubeClientConfig(config *common.KubernetesConfig) (*restclient.Config, error) {
	switch {
	case len(config.CertFile) > 0:
		if len(config.KeyFile) == 0 || len(config.CAFile) == 0 {
			return nil, fmt.Errorf("ca file, cert file and key file must be specified when using file based auth")
		}
		return &restclient.Config{
			Host: config.Host,
			TLSClientConfig: restclient.TLSClientConfig{
				CertFile: config.CertFile,
				KeyFile:  config.KeyFile,
				CAFile:   config.CAFile,
			},
		}, nil

	case len(config.Host) > 0:
		return &restclient.Config{
			Host: config.Host,
		}, nil

	default:
		config, err := clientcmd.NewDefaultClientConfigLoadingRules().Load()
		if err != nil {
			return nil, err
		}

		clientConfig := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{})
		return clientConfig.ClientConfig()
	}
}

func getKubeClient(config *common.KubernetesConfig) (*client.Client, error) {
	restConfig, err := getKubeClientConfig(config)
	if err != nil {
		return nil, err
	}

	return client.New(restConfig)
}

func isRunning(pod *api.Pod) (bool, error) {
	switch pod.Status.Phase {
	case api.PodRunning:
		return true, nil
	case api.PodSucceeded:
		return false, fmt.Errorf("pod already succeeded before it begins running")
	case api.PodFailed:
		return false, fmt.Errorf("pod status is failed")
	default:
		return false, nil
	}
}

type podPhaseResponse struct {
	done  bool
	phase api.PodPhase
	err   error
}

func getPodPhase(c *client.Client, pod *api.Pod, out io.Writer) podPhaseResponse {
	pod, err := c.Pods(pod.Namespace).Get(pod.Name)
	if err != nil {
		return podPhaseResponse{true, api.PodUnknown, err}
	}

	ready, err := isRunning(pod)

	if err != nil {
		return podPhaseResponse{true, pod.Status.Phase, err}
	}

	if ready {
		return podPhaseResponse{true, pod.Status.Phase, nil}
	}

	// check status of containers
	for _, container := range pod.Status.ContainerStatuses {
		if container.Ready {
			continue
		}
		if container.State.Waiting == nil {
			continue
		}

		switch container.State.Waiting.Reason {
		case "ErrImagePull", "ImagePullBackOff":
			return podPhaseResponse{true, api.PodUnknown, errors.New(container.State.Waiting.Message)}
		}
	}

	fmt.Fprintf(out, "Waiting for pod %s/%s to be running, status is %s\n", pod.Namespace, pod.Name, pod.Status.Phase)
	return podPhaseResponse{false, pod.Status.Phase, nil}

}

func triggerPodPhaseCheck(c *client.Client, pod *api.Pod, out io.Writer) <-chan podPhaseResponse {
	errc := make(chan podPhaseResponse)
	go func() {
		defer close(errc)
		errc <- getPodPhase(c, pod, out)
	}()
	return errc
}

// waitForPodRunning will use client c to detect when pod reaches the PodRunning
// state. It will check every second, and will return the final PodPhase once
// either PodRunning, PodSucceeded or PodFailed has been reached. In the case of
// PodRunning, it will also wait until all containers within the pod are also Ready
// Returns error if the call to retrieve pod details fails
func waitForPodRunning(ctx context.Context, c *client.Client, pod *api.Pod, out io.Writer) (api.PodPhase, error) {
	for i := 0; i < 60; i++ {
		select {
		case r := <-triggerPodPhaseCheck(c, pod, out):
			if !r.done {
				time.Sleep(3 * time.Second)
				continue
			}

			return r.phase, r.err
		case <-ctx.Done():
			return api.PodUnknown, ctx.Err()
		}
	}
	return api.PodUnknown, errors.New("timedout waiting for pod to start")
}

// limits takes a string representing CPU & memory limits,
// and returns a ResourceList with appropriately scaled Quantity
// values for Kubernetes. This allows users to write "500m" for CPU,
// and "50Mi" for memory (etc.)
func limits(cpu, memory string) (api.ResourceList, error) {
	var rCPU, rMem resource.Quantity
	var err error

	parse := func(s string) (resource.Quantity, error) {
		var q resource.Quantity
		if len(s) == 0 {
			return q, nil
		}
		if q, err = resource.ParseQuantity(s); err != nil {
			return q, fmt.Errorf("error parsing resource limit: %s", err.Error())
		}
		return q, nil
	}

	if rCPU, err = parse(cpu); err != nil {
		return api.ResourceList{}, nil
	}

	if rMem, err = parse(memory); err != nil {
		return api.ResourceList{}, nil
	}

	l := make(api.ResourceList)

	q := resource.Quantity{}
	if rCPU != q {
		l[api.ResourceLimitsCPU] = rCPU
	}
	if rMem != q {
		l[api.ResourceLimitsMemory] = rMem
	}

	return l, nil
}

// buildVariables converts a common.BuildVariables into a list of
// kubernetes EnvVar objects
func buildVariables(bv common.BuildVariables) []api.EnvVar {
	e := make([]api.EnvVar, len(bv))
	for i, b := range bv {
		e[i] = api.EnvVar{
			Name:  b.Key,
			Value: b.Value,
		}
	}
	return e
}
<|endoftext|>


<commit_before>package riemann

import (
	"errors"
	"fmt"
	"os"

	"github.com/amir/raidman"
	"github.com/influxdb/influxdb/client/v2"
	"github.com/influxdb/telegraf/outputs"
)

type Riemann struct {
	URL       string
	Transport string

	client *raidman.Client
}

var sampleConfig = `
  # URL of server
  url = "localhost:5555"
  # transport protocol to use either tcp or udp
  transport = "tcp"
`

func (r *Riemann) Connect() error {
	c, err := raidman.Dial(r.Transport, r.URL)

	if err != nil {
		return err
	}

	r.client = c
	return nil
}

func (r *Riemann) Close() error {
	r.client.Close()
	return nil
}

func (r *Riemann) SampleConfig() string {
	return sampleConfig
}

func (r *Riemann) Description() string {
	return "Configuration for the Riemann server to send metrics to"
}

func (r *Riemann) Write(points []*client.Point) error {
	if len(points) == 0 {
		return nil
	}

	var events []*raidman.Event
	for _, p := range points {
		ev := buildEvent(p)
		events = append(events, ev)
	}

	var senderr = r.client.SendMulti(events)
	if senderr != nil {
		return errors.New(fmt.Sprintf("FAILED to send riemann message: %s\n",
			senderr))
	}

	return nil
}

func buildEvent(p *client.Point) *raidman.Event {
	host, ok := p.Tags()["host"]
	if !ok {
		hostname, err := os.Hostname()
		if err != nil {
			host = "unknown"
		} else {
			host = hostname
		}
	}

	var event = &raidman.Event{
		Host:    host,
		Service: p.Name(),
		Metric:  p.Fields()["value"],
	}

	return event
}

func init() {
	outputs.Add("riemann", func() outputs.Output {
		return &Riemann{}
	})
}
<commit_msg>0.3.0 outputs: riemann<commit_after>package riemann

import (
	"errors"
	"fmt"
	"os"

	"github.com/amir/raidman"
	"github.com/influxdb/influxdb/client/v2"
	"github.com/influxdb/telegraf/outputs"
)

type Riemann struct {
	URL       string
	Transport string

	client *raidman.Client
}

var sampleConfig = `
  # URL of server
  url = "localhost:5555"
  # transport protocol to use either tcp or udp
  transport = "tcp"
`

func (r *Riemann) Connect() error {
	c, err := raidman.Dial(r.Transport, r.URL)

	if err != nil {
		return err
	}

	r.client = c
	return nil
}

func (r *Riemann) Close() error {
	r.client.Close()
	return nil
}

func (r *Riemann) SampleConfig() string {
	return sampleConfig
}

func (r *Riemann) Description() string {
	return "Configuration for the Riemann server to send metrics to"
}

func (r *Riemann) Write(points []*client.Point) error {
	if len(points) == 0 {
		return nil
	}

	var events []*raidman.Event
	for _, p := range points {
		evs := buildEvents(p)
		for _, ev := range evs {
			events = append(events, ev)
		}
	}

	var senderr = r.client.SendMulti(events)
	if senderr != nil {
		return errors.New(fmt.Sprintf("FAILED to send riemann message: %s\n",
			senderr))
	}

	return nil
}

func buildEvents(p *client.Point) []*raidman.Event {
	events := []*raidman.Event{}
	for fieldName, value := range p.Fields() {
		host, ok := p.Tags()["host"]
		if !ok {
			hostname, err := os.Hostname()
			if err != nil {
				host = "unknown"
			} else {
				host = hostname
			}
		}

		event := &raidman.Event{
			Host:    host,
			Service: p.Name() + "_" + fieldName,
			Metric:  value,
		}
		events = append(events, event)
	}

	return events
}

func init() {
	outputs.Add("riemann", func() outputs.Output {
		return &Riemann{}
	})
}
<|endoftext|>


<commit_before><commit_msg>some code adjustment<commit_after><|endoftext|>


<commit_before>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

/*
 Monsti is a simple and resource efficient CMS.

 This package implements the data service.
*/
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sync"
	"launchpad.net/goyaml"

	"pkg.monsti.org/service"
	"pkg.monsti.org/util"
)

// DataService implements RPC methods for the Data service.
type DataService struct {
	Info     *service.InfoClient
	Settings settings
}

// getNode looks up the given node.
// If no such node exists, return nil.
// It adds a path attribute with the given path.
func getNode(root, path string) (node []byte, err error) {
	node_path := filepath.Join(root, path[1:], "node.json")
	node, err = ioutil.ReadFile(node_path)
	if err != nil {
		return
	}
	pathJSON := fmt.Sprintf(`{"path":%q,`, path)
	node = bytes.Replace(node, []byte("{"), []byte(pathJSON), 1)
	return
}

type GetNodeArgs struct{ Site, Path string }

func (i *DataService) GetNode(args *GetNodeArgs,
	reply *[]byte) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	fmt.Println(site, args.Path)
	node, err := getNode(site, args.Path)
	if err != nil {
		reply = nil
		return err
	}
	*reply = node
	return nil
}

// getChildren looks up child nodes of the given node.
func getChildren(root, path string) (nodes [][]byte, err error) {
	files, err := ioutil.ReadDir(filepath.Join(root, path))
	if err != nil {
		return
	}
	for _, file := range files {
		node, _ := getNode(root, filepath.Join(path, file.Name()))
		if node != nil {
			nodes = append(nodes, node)
		}
	}
	return
}

type GetChildrenArgs struct {
	Site, Path string
}

func (i *DataService) GetChildren(args GetChildrenArgs,
	reply *[][]byte) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	ret, err := getChildren(site, args.Path)
	*reply = ret
	return err
}

type GetNodeDataArgs struct{ Site, Path, File string }

func (i *DataService) GetNodeData(args *GetNodeDataArgs,
	reply *[]byte) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	path := filepath.Join(site, args.Path[1:], args.File)
	ret, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		*reply = nil
		return nil
	}
	*reply = ret
	return err
}

type WriteNodeDataArgs struct {
	Site, Path, File, Content string
}

func (i *DataService) WriteNodeData(args *WriteNodeDataArgs,
	reply *int) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	path := filepath.Join(site, args.Path[1:], args.File)
	err := ioutil.WriteFile(path, []byte(args.Content), 0600)
	return err
}

type UpdateNodeArgs struct {
	Site string
	Node service.NodeInfo
}

func (i *DataService) UpdateNode(args *UpdateNodeArgs, reply *int) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	return writeNode(args.Node, site)
}

type RemoveNodeArgs struct {
	Site, Node string
}

func (i *DataService) RemoveNode(args *RemoveNodeArgs, reply *int) error {
	root := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	nodePath := filepath.Join(root, args.Node[1:])
	if err := os.RemoveAll(nodePath); err != nil {
		return fmt.Errorf("Can't remove node: %v", err)
	}
	return nil
}

// writeNode writes the given node to the data directory located at the given
// root.
func writeNode(reqnode service.NodeInfo, root string) error {
	path := reqnode.Path
	reqnode.Path = ""
	content, err := goyaml.Marshal(&reqnode)
	if err != nil {
		return err
	}
	node_path := filepath.Join(root, path[1:],
		"node.json")
	if err := os.Mkdir(filepath.Dir(node_path), 0700); err != nil {
		if !os.IsExist(err) {
			panic("Can't create directory for new node: " + err.Error())
		}
	}
	return ioutil.WriteFile(node_path, content, 0600)
}

type settings struct {
	Monsti util.MonstiSettings
}

func main() {
	logger := log.New(os.Stderr, "data ", log.LstdFlags)

	// Load configuration
	flag.Parse()
	cfgPath := util.GetConfigPath(flag.Arg(0))
	var settings settings
	if err := util.LoadModuleSettings("data", cfgPath, &settings); err != nil {
		logger.Fatal("Could not load settings: ", err)
	}

	// Connect to Info service
	info, err := service.NewInfoConnection(settings.Monsti.GetServicePath(
		service.Info.String()))
	if err != nil {
		logger.Fatalf("Could not connect to Info service: %v", err)
	}

	// Start own Data service
	var waitGroup sync.WaitGroup
	logger.Println("Starting Data service")
	waitGroup.Add(1)
	dataPath := settings.Monsti.GetServicePath(service.Data.String())
	go func() {
		defer waitGroup.Done()
		var provider service.Provider
		var data_ DataService
		data_.Info = info
		data_.Settings = settings
		provider.Logger = logger
		if err := provider.Serve(dataPath, "Data", &data_); err != nil {
			logger.Fatalf("Could not start Data service: %v", err)
		}
	}()

	if err := info.PublishService("Data", dataPath); err != nil {
		logger.Fatalf("Could not publish Data service: %v", err)
	}

	waitGroup.Wait()
}
<commit_msg>Remove info client reference, not needed.<commit_after>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

/*
 Monsti is a simple and resource efficient CMS.

 This package implements the data service.
*/
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sync"
	"launchpad.net/goyaml"

	"pkg.monsti.org/service"
	"pkg.monsti.org/util"
)

// DataService implements RPC methods for the Data service.
type DataService struct {
	Settings settings
}

// getNode looks up the given node.
// If no such node exists, return nil.
// It adds a path attribute with the given path.
func getNode(root, path string) (node []byte, err error) {
	node_path := filepath.Join(root, path[1:], "node.json")
	node, err = ioutil.ReadFile(node_path)
	if err != nil {
		return
	}
	pathJSON := fmt.Sprintf(`{"path":%q,`, path)
	node = bytes.Replace(node, []byte("{"), []byte(pathJSON), 1)
	return
}

type GetNodeArgs struct{ Site, Path string }

func (i *DataService) GetNode(args *GetNodeArgs,
	reply *[]byte) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	fmt.Println(site, args.Path)
	node, err := getNode(site, args.Path)
	if err != nil {
		reply = nil
		return err
	}
	*reply = node
	return nil
}

// getChildren looks up child nodes of the given node.
func getChildren(root, path string) (nodes [][]byte, err error) {
	files, err := ioutil.ReadDir(filepath.Join(root, path))
	if err != nil {
		return
	}
	for _, file := range files {
		node, _ := getNode(root, filepath.Join(path, file.Name()))
		if node != nil {
			nodes = append(nodes, node)
		}
	}
	return
}

type GetChildrenArgs struct {
	Site, Path string
}

func (i *DataService) GetChildren(args GetChildrenArgs,
	reply *[][]byte) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	ret, err := getChildren(site, args.Path)
	*reply = ret
	return err
}

type GetNodeDataArgs struct{ Site, Path, File string }

func (i *DataService) GetNodeData(args *GetNodeDataArgs,
	reply *[]byte) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	path := filepath.Join(site, args.Path[1:], args.File)
	ret, err := ioutil.ReadFile(path)
	if os.IsNotExist(err) {
		*reply = nil
		return nil
	}
	*reply = ret
	return err
}

type WriteNodeDataArgs struct {
	Site, Path, File, Content string
}

func (i *DataService) WriteNodeData(args *WriteNodeDataArgs,
	reply *int) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	path := filepath.Join(site, args.Path[1:], args.File)
	err := ioutil.WriteFile(path, []byte(args.Content), 0600)
	return err
}

type UpdateNodeArgs struct {
	Site string
	Node service.NodeInfo
}

func (i *DataService) UpdateNode(args *UpdateNodeArgs, reply *int) error {
	site := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	return writeNode(args.Node, site)
}

type RemoveNodeArgs struct {
	Site, Node string
}

func (i *DataService) RemoveNode(args *RemoveNodeArgs, reply *int) error {
	root := i.Settings.Monsti.GetSiteNodesPath(args.Site)
	nodePath := filepath.Join(root, args.Node[1:])
	if err := os.RemoveAll(nodePath); err != nil {
		return fmt.Errorf("Can't remove node: %v", err)
	}
	return nil
}

// writeNode writes the given node to the data directory located at the given
// root.
func writeNode(reqnode service.NodeInfo, root string) error {
	path := reqnode.Path
	reqnode.Path = ""
	content, err := goyaml.Marshal(&reqnode)
	if err != nil {
		return err
	}
	node_path := filepath.Join(root, path[1:],
		"node.json")
	if err := os.Mkdir(filepath.Dir(node_path), 0700); err != nil {
		if !os.IsExist(err) {
			panic("Can't create directory for new node: " + err.Error())
		}
	}
	return ioutil.WriteFile(node_path, content, 0600)
}

type settings struct {
	Monsti util.MonstiSettings
}

func main() {
	logger := log.New(os.Stderr, "data ", log.LstdFlags)

	// Load configuration
	flag.Parse()
	cfgPath := util.GetConfigPath(flag.Arg(0))
	var settings settings
	if err := util.LoadModuleSettings("data", cfgPath, &settings); err != nil {
		logger.Fatal("Could not load settings: ", err)
	}

	// Connect to Info service
	info, err := service.NewInfoConnection(settings.Monsti.GetServicePath(
		service.Info.String()))
	if err != nil {
		logger.Fatalf("Could not connect to Info service: %v", err)
	}

	// Start own Data service
	var waitGroup sync.WaitGroup
	logger.Println("Starting Data service")
	waitGroup.Add(1)
	dataPath := settings.Monsti.GetServicePath(service.Data.String())
	go func() {
		defer waitGroup.Done()
		var provider service.Provider
		var data_ DataService
		data_.Settings = settings
		provider.Logger = logger
		if err := provider.Serve(dataPath, "Data", &data_); err != nil {
			logger.Fatalf("Could not start Data service: %v", err)
		}
	}()

	if err := info.PublishService("Data", dataPath); err != nil {
		logger.Fatalf("Could not publish Data service: %v", err)
	}

	waitGroup.Wait()
}
<|endoftext|>


<commit_before>package types

import (
	"encoding/json"
	"io/ioutil"
	"time"

	"github.com/pkg/errors"

	crypto "github.com/tendermint/go-crypto"
	cmn "github.com/tendermint/tmlibs/common"
)

//------------------------------------------------------------
// core types for a genesis definition

// GenesisValidator is an initial validator.
type GenesisValidator struct {
	PubKey crypto.PubKey `json:"pub_key"`
	Power  int64         `json:"power"`
	Name   string        `json:"name"`
}

// GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set.
type GenesisDoc struct {
	GenesisTime     time.Time          `json:"genesis_time"`
	ChainID         string             `json:"chain_id"`
	ConsensusParams *ConsensusParams   `json:"consensus_params,omitempty"`
	Validators      []GenesisValidator `json:"validators"`
	AppHash         cmn.HexBytes       `json:"app_hash"`
	AppOptions      interface{}        `json:"app_options,omitempty"`
}

// SaveAs is a utility method for saving GenensisDoc as a JSON file.
func (genDoc *GenesisDoc) SaveAs(file string) error {
	genDocBytes, err := json.Marshal(genDoc)
	if err != nil {
		return err
	}
	return cmn.WriteFile(file, genDocBytes, 0644)
}

// ValidatorHash returns the hash of the validator set contained in the GenesisDoc
func (genDoc *GenesisDoc) ValidatorHash() []byte {
	vals := make([]*Validator, len(genDoc.Validators))
	for i, v := range genDoc.Validators {
		vals[i] = NewValidator(v.PubKey, v.Power)
	}
	vset := NewValidatorSet(vals)
	return vset.Hash()
}

// ValidateAndComplete checks that all necessary fields are present
// and fills in defaults for optional fields left empty
func (genDoc *GenesisDoc) ValidateAndComplete() error {

	if genDoc.ChainID == "" {
		return errors.Errorf("Genesis doc must include non-empty chain_id")
	}

	if genDoc.ConsensusParams == nil {
		genDoc.ConsensusParams = DefaultConsensusParams()
	} else {
		if err := genDoc.ConsensusParams.Validate(); err != nil {
			return err
		}
	}

	if len(genDoc.Validators) == 0 {
		return errors.Errorf("The genesis file must have at least one validator")
	}

	for _, v := range genDoc.Validators {
		if v.Power == 0 {
			return errors.Errorf("The genesis file cannot contain validators with no voting power: %v", v)
		}
	}

	if genDoc.GenesisTime.IsZero() {
		genDoc.GenesisTime = time.Now()
	}

	return nil
}

//------------------------------------------------------------
// Make genesis state from file

// GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc.
func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) {
	genDoc := GenesisDoc{}
	err := json.Unmarshal(jsonBlob, &genDoc)
	if err != nil {
		return nil, err
	}

	if err := genDoc.ValidateAndComplete(); err != nil {
		return nil, err
	}

	return &genDoc, err
}

// GenesisDocFromFile reads JSON data from a file and unmarshalls it into a GenesisDoc.
func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) {
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		return nil, errors.Wrap(err, "Couldn't read GenesisDoc file")
	}
	genDoc, err := GenesisDocFromJSON(jsonBlob)
	if err != nil {
		return nil, errors.Wrap(err, cmn.Fmt("Error reading GenesisDoc at %v", genDocFile))
	}
	return genDoc, nil
}
<commit_msg>AppOptions -> AppStateJSON<commit_after>package types

import (
	"encoding/json"
	"io/ioutil"
	"time"

	"github.com/pkg/errors"

	crypto "github.com/tendermint/go-crypto"
	cmn "github.com/tendermint/tmlibs/common"
)

//------------------------------------------------------------
// core types for a genesis definition

// GenesisValidator is an initial validator.
type GenesisValidator struct {
	PubKey crypto.PubKey `json:"pub_key"`
	Power  int64         `json:"power"`
	Name   string        `json:"name"`
}

// GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set.
type GenesisDoc struct {
	GenesisTime     time.Time          `json:"genesis_time"`
	ChainID         string             `json:"chain_id"`
	ConsensusParams *ConsensusParams   `json:"consensus_params,omitempty"`
	Validators      []GenesisValidator `json:"validators"`
	AppHash         cmn.HexBytes       `json:"app_hash"`
	AppState        json.RawMessage    `json:"app_state,omitempty"`
}

// SaveAs is a utility method for saving GenensisDoc as a JSON file.
func (genDoc *GenesisDoc) SaveAs(file string) error {
	genDocBytes, err := json.Marshal(genDoc)
	if err != nil {
		return err
	}
	return cmn.WriteFile(file, genDocBytes, 0644)
}

// ValidatorHash returns the hash of the validator set contained in the GenesisDoc
func (genDoc *GenesisDoc) ValidatorHash() []byte {
	vals := make([]*Validator, len(genDoc.Validators))
	for i, v := range genDoc.Validators {
		vals[i] = NewValidator(v.PubKey, v.Power)
	}
	vset := NewValidatorSet(vals)
	return vset.Hash()
}

// ValidateAndComplete checks that all necessary fields are present
// and fills in defaults for optional fields left empty
func (genDoc *GenesisDoc) ValidateAndComplete() error {

	if genDoc.ChainID == "" {
		return errors.Errorf("Genesis doc must include non-empty chain_id")
	}

	if genDoc.ConsensusParams == nil {
		genDoc.ConsensusParams = DefaultConsensusParams()
	} else {
		if err := genDoc.ConsensusParams.Validate(); err != nil {
			return err
		}
	}

	if len(genDoc.Validators) == 0 {
		return errors.Errorf("The genesis file must have at least one validator")
	}

	for _, v := range genDoc.Validators {
		if v.Power == 0 {
			return errors.Errorf("The genesis file cannot contain validators with no voting power: %v", v)
		}
	}

	if genDoc.GenesisTime.IsZero() {
		genDoc.GenesisTime = time.Now()
	}

	return nil
}

//------------------------------------------------------------
// Make genesis state from file

// GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc.
func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) {
	genDoc := GenesisDoc{}
	err := json.Unmarshal(jsonBlob, &genDoc)
	if err != nil {
		return nil, err
	}

	if err := genDoc.ValidateAndComplete(); err != nil {
		return nil, err
	}

	return &genDoc, err
}

// GenesisDocFromFile reads JSON data from a file and unmarshalls it into a GenesisDoc.
func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) {
	jsonBlob, err := ioutil.ReadFile(genDocFile)
	if err != nil {
		return nil, errors.Wrap(err, "Couldn't read GenesisDoc file")
	}
	genDoc, err := GenesisDocFromJSON(jsonBlob)
	if err != nil {
		return nil, errors.Wrap(err, cmn.Fmt("Error reading GenesisDoc at %v", genDocFile))
	}
	return genDoc, nil
}
<|endoftext|>


<commit_before>package main

import (
  "./turbo-wookie"
  "flag"
  "log"
  "os"
  "os/signal"
  "bufio"
  "net"
  "fmt"
  "strings"
)

func main() {
  // Parse out our flags
  serveDart := flag.Bool("dart", false, "Include to serve dart code.")
  noStartMPD := flag.Bool("nompd", false, "Include to not start MPD.")
  configFile := flag.String("config", "config.yaml", "Location of a Turbo Wookie configuration file.")
  portOverride := flag.Int("port", 9000, "Force override Turbo Wookie's port.")

  flag.Parse()

  // create a new Turbo Wookie Handler, using our flags.
  h, err := turbowookie.NewTWHandler(*configFile, *serveDart, *noStartMPD, *portOverride)
  if err != nil {
    log.Fatal(err)
  }

  // This waits for SIGINT (Signal Interrupt) to come in, when a SIGINT is
  // received (typically through CTRL+C) we tell our MPDClient to kill the
  // MPD instance we started up, and we exit the program, status 1 ("A-OK!").
  if *noStartMPD {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    go func() {
      for _ = range c {
        h.MpdClient.KillMpd()
        os.Exit(1)
      }
    }()
  }

  go talkToMPD()

  // Listen for and serve HTTP requests
  if err := h.ListenAndServe(); err != nil {
    log.Println(err)
  }
}

func talkToMPD() {
  bio := bufio.NewReader(os.Stdin)
  requestBytes, _, _ := bio.ReadLine()
  request := string(requestBytes)

  conn, err := net.Dial("tcp", "localhost:6600")
  checkErr(err)

  fmt.Fprintf(conn, request + "\n")

  reader := bufio.NewReader(conn)

  for {
    response, err := reader.ReadString('\n')
    checkErr(err)

    fmt.Print(response)

    if strings.HasSuffix(response, "OK\n") {
      break
    } else if strings.HasPrefix(response, "ACK [") {
      break
    }
  }
  fmt.Println("")
  talkToMPD()
}

func checkErr(err error) {
  if err != nil {
    log.Println("Error: %s", err.Error())
    os.Exit(1)
  }
}<commit_msg>Added comments.<commit_after>package main

import (
  "./turbo-wookie"
  "flag"
  "log"
  "os"
  "os/signal"
  "bufio"
  "net"
  "fmt"
  "strings"
)

func main() {
  // Parse out our flags
  serveDart := flag.Bool("dart", false, "Include to serve dart code.")
  noStartMPD := flag.Bool("nompd", false, "Include to not start MPD.")
  configFile := flag.String("config", "config.yaml", "Location of a Turbo Wookie configuration file.")
  portOverride := flag.Int("port", 9000, "Force override Turbo Wookie's port.")

  flag.Parse()

  // create a new Turbo Wookie Handler, using our flags.
  h, err := turbowookie.NewTWHandler(*configFile, *serveDart, *noStartMPD, *portOverride)
  if err != nil {
    log.Fatal(err)
  }

  // This waits for SIGINT (Signal Interrupt) to come in, when a SIGINT is
  // received (typically through CTRL+C) we tell our MPDClient to kill the
  // MPD instance we started up, and we exit the program, status 1 ("A-OK!").
  if *noStartMPD {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    go func() {
      for _ = range c {
        h.MpdClient.KillMpd()
        os.Exit(1)
      }
    }()
  }

  go talkToMPD()

  // Listen for and serve HTTP requests
  if err := h.ListenAndServe(); err != nil {
    log.Println(err)
  }
}

func talkToMPD() {
  // Read from the stdin.
  clientReader := bufio.NewReader(os.Stdin)
  requestBytes, _, _ := clientReader.ReadLine()
  request := string(requestBytes)

  // Once we have read, create a connection to MPD.
  conn, err := net.Dial("tcp", "localhost:6600")
  checkErr(err)

  // Write our response to MPD.
  fmt.Fprintf(conn, request + "\n")

  // Read from MPD.
  reader := bufio.NewReader(conn)
  for {
    response, err := reader.ReadString('\n')
    checkErr(err)

    // Using fmt to print a response because we don't care about time.
    fmt.Print(response)

    // If the request was good, MPD's response will end with "OK\n"
    // Otherwise, the response will start with "ACK [<num>@<num>] <error>"
    if strings.HasSuffix(response, "OK\n") {
      break
    } else if strings.HasPrefix(response, "ACK [") {
      break
    }
  }

  // Just print a blank line to make it look prettier.
  fmt.Println("")

  // Talk to MPD again.
  talkToMPD()
}

// I got sick of checking for errors after every function call, so I did this.
func checkErr(err error) {
  if err != nil {
    log.Println("Error: %s", err.Error())
    os.Exit(1)
  }
}<|endoftext|>


<commit_before>/*
 Copyright (c) 2012, Chris Rushton

 Permission to use, copy, modify, and distribute this software for any
 purpose with or without fee is hereby granted, provided that the above
 copyright notice and this permission notice appear in all copies.

 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package main

import (
    "os"
    "net"
    "fmt"
    "flag"
    "strings"
)

var buf [4096]byte

var address = flag.String("a", "", "Set the listen ip address")
var port = flag.String("p", "2500", "Set the listen port (default 2500)")
var siplogonly = flag.Bool("siplog", false, "If true will filter sipmsg.log messages only")
var local = ""
var searchfor = ""

var Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
    flag.PrintDefaults()
    fmt.Printf("hi")
    os.Exit(1)
}

func parseMessage(data string, addr net.Addr) {
    // splits the log message
    //   d[0] = logname@cpu/core
    //   d[1] = the log message
    var d = strings.Split(data, ":", 2)

    // splits the logname and cpu/core
    //  f[0] = logfile name
    //  f[1] = the cpu/core
    var f = strings.Split(d[0], "@", 2)

    // handles siplog flag
    if *siplogonly {
      if strings.Contains(d[0], "sipmsg.log") {
        if len(searchfor) > 0 {
          if strings.Contains(data, searchfor) {
            fmt.Printf("From: %v, Log: %v, Cpu/Core: %v\n", addr, f[0], f[1])
            fmt.Printf("%v\n", strings.TrimSpace(d[1]))
          }
        } else {
          fmt.Printf("From: %v, Log: %v, Cpu/Core: %v\n", addr, f[0], f[1])
          fmt.Printf("%v\n", strings.TrimSpace(d[1]))
        }
      }
    } else {
      if len(searchfor) > 0 {
        if strings.Contains(data, searchfor) {
          fmt.Printf("%v: %v\n", addr, string(data))
        }
      } else {
        fmt.Printf("%v: %v\n", addr, string(data))
      }
    }
}

func main() {
  flag.Parse()
  if flag.NArg() == 1 {
    searchfor = flag.Arg(0)
  }

  if flag.NArg() > 1 {
      fmt.Printf("Too many arguments, only 1 search arg is supported at this time.\n")
      os.Exit(1)
  }

  if len(*address) > 0 {
    l := net.ParseIP(*address).To4()
    if l == nil {
      fmt.Printf("Invalid IPv4 Address: %v\n", *address)
      os.Exit(1)
    }
    local = l.String()
  }

  laddr := local + ":" + *port
  c, err := net.ListenPacket("udp", laddr )
  if err != nil {
    fmt.Printf("Cannot bind...%v\nError: %s\n", laddr, err)
    os.Exit(1)
  }
  fmt.Printf("Listening on: %v\n", c.LocalAddr())

  for {
    nr, addr, err := c.ReadFrom(buf[0:])
    if err != nil {
      panic(err.String())
    }
    data := buf[0:nr]
    parseMessage(string(data), addr)
  }

}
<commit_msg>updated udplog to golang 1.0<commit_after>/*
Copyright (c) 2012, Chris Rushton

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package main

import (
    "os"
    "net"
    "fmt"
    "flag"
    "strings"
)

var buf [4096]byte

var address = flag.String("a", "", "Set the listen ip address")
var port = flag.String("p", "2500", "Set the listen port (default 2500)")
var siplogonly = flag.Bool("siplog", false, "If true will filter sipmsg.log messages only")
var local = ""
var searchfor = ""

var Usage = func() {
    fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
    flag.PrintDefaults()
    fmt.Printf("hi")
    os.Exit(1)
}

func parseMessage(data string, addr net.Addr) {
    // splits the log message
    //   d[0] = logname@cpu/core
    //   d[1] = the log message
    var d = strings.SplitN(data, ":", 2)

    // splits the logname and cpu/core
    //  f[0] = logfile name
    //  f[1] = the cpu/core
    var f = strings.SplitN(d[0], "@", 2)

    // handles siplog flag
    if *siplogonly {
      if strings.Contains(d[0], "sipmsg.log") {
        if len(searchfor) > 0 {
          if strings.Contains(data, searchfor) {
            fmt.Printf("From: %v, Log: %v, Cpu/Core: %v\n", addr, f[0], f[1])
            fmt.Printf("%v\n", strings.TrimSpace(d[1]))
          }
        } else {
          fmt.Printf("From: %v, Log: %v, Cpu/Core: %v\n", addr, f[0], f[1])
          fmt.Printf("%v\n", strings.TrimSpace(d[1]))
        }
      }
    } else {
      if len(searchfor) > 0 {
        if strings.Contains(data, searchfor) {
          fmt.Printf("%v: %v\n", addr, string(data))
        }
      } else {
        fmt.Printf("%v: %v\n", addr, string(data))
      }
    }
}

func main() {
  flag.Parse()
  if flag.NArg() == 1 {
    searchfor = flag.Arg(0)
  }

  if flag.NArg() > 1 {
      fmt.Printf("Too many arguments, only 1 search arg is supported at this time.\n")
      os.Exit(1)
  }

  if len(*address) > 0 {
    l := net.ParseIP(*address).To4()
    if l == nil {
      fmt.Printf("Invalid IPv4 Address: %v\n", *address)
      os.Exit(1)
    }
    local = l.String()
  }

  laddr := local + ":" + *port
  c, err := net.ListenPacket("udp", laddr )
  if err != nil {
    fmt.Printf("Cannot bind...%v\nError: %s\n", laddr, err)
    os.Exit(1)
  }
  fmt.Printf("Listening on: %v\n", c.LocalAddr())

  for {
    nr, addr, err := c.ReadFrom(buf[0:])
    if err != nil {
      panic(err)
    }
    data := buf[0:nr]
    parseMessage(string(data), addr)
  }

}
<|endoftext|>


<commit_before>package persist

import (
	"crypto/rand"
	"encoding/base32"
	"errors"
	"os"
)

const (
	// persistDir defines the folder that is used for testing the persist
	// package.
	persistDir = "persist"
)

var (
	// ErrBadVersion indicates that the version number of the file is not
	// compatible with the current codebase.
	ErrBadVersion = errors.New("incompatible version")

	// ErrBadHeader indicates that the file opened is not the file that was
	// expected.
	ErrBadHeader = errors.New("wrong header")
)

// Metadata contains the header and version of the data being stored.
type Metadata struct {
	Header, Version string
}

// RandomSuffix returns a 20 character base32 suffix for a filename. There are
// 100 bits of entropy, and a very low probability of colliding with existing
// files unintentionally.
func RandomSuffix() string {
	randBytes := make([]byte, 20)
	rand.Read(randBytes)
	str := base32.StdEncoding.EncodeToString(randBytes)
	return str[:20]
}

// A safeFile is a file that is stored under a temporary filename. When Commit
// is called, the file is renamed to its "final" filename. This allows for
// atomic updating of files; otherwise, an unexpected shutdown could leave a
// valuable file in a corrupted state. Callers must still Close the file handle
// as usual.
type safeFile struct {
	*os.File
	finalName string
}

// Commit renames the file to the intended final filename.
func (sf *safeFile) Commit() error {
	return os.Rename(sf.finalName+"_temp", sf.finalName)
}

// NewSafeFile returns a file that can atomically be written to disk,
// minimizing the risk of corruption.
func NewSafeFile(filename string) (*safeFile, error) {
	file, err := os.Create(filename + "_temp")
	if err != nil {
		return nil, err
	}
	return &safeFile{file, filename}, nil
}
<commit_msg>Use the absolute path of the filename in finalName<commit_after>package persist

import (
	"crypto/rand"
	"encoding/base32"
	"errors"
	"os"
	"path/filepath"
)

const (
	// persistDir defines the folder that is used for testing the persist
	// package.
	persistDir = "persist"
)

var (
	// ErrBadVersion indicates that the version number of the file is not
	// compatible with the current codebase.
	ErrBadVersion = errors.New("incompatible version")

	// ErrBadHeader indicates that the file opened is not the file that was
	// expected.
	ErrBadHeader = errors.New("wrong header")
)

// Metadata contains the header and version of the data being stored.
type Metadata struct {
	Header, Version string
}

// RandomSuffix returns a 20 character base32 suffix for a filename. There are
// 100 bits of entropy, and a very low probability of colliding with existing
// files unintentionally.
func RandomSuffix() string {
	randBytes := make([]byte, 20)
	rand.Read(randBytes)
	str := base32.StdEncoding.EncodeToString(randBytes)
	return str[:20]
}

// A safeFile is a file that is stored under a temporary filename. When Commit
// is called, the file is renamed to its "final" filename. This allows for
// atomic updating of files; otherwise, an unexpected shutdown could leave a
// valuable file in a corrupted state. Callers must still Close the file handle
// as usual.
type safeFile struct {
	*os.File
	finalName string
}

// Commit renames the file to the intended final filename.
func (sf *safeFile) Commit() error {
	return os.Rename(sf.finalName+"_temp", sf.finalName)
}

// NewSafeFile returns a file that can atomically be written to disk,
// minimizing the risk of corruption.
func NewSafeFile(filename string) (*safeFile, error) {
	file, err := os.Create(filename + "_temp")
	if err != nil {
		return nil, err
	}

	// Get the absolute path of the filename so that calling os.Chdir in
	// between calling NewSafeFile and calling safeFile.Commit does not change
	// the final file path.
	absFilename, err := filepath.Abs(filename)
	if err != nil {
		return nil, err
	}

	return &safeFile{file, absFilename}, nil
}
<|endoftext|>


<commit_before>//
// Copyright 2015, Sander van Harmelen
//
// 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 multisyncer

type syncer chan cmdData

type cmdData struct {
	action cmdAction
	token  interface{}
	done   chan struct{}
}

type cmdAction int

const (
	lock cmdAction = iota
	unlock
)

// MultiSyncer synchronizes access based on a given token
type MultiSyncer interface {
	Lock(interface{})
	Unlock(interface{})
}

// New returns a new MultiSyncer
func New() MultiSyncer {
	s := make(syncer)
	go s.run()
	return s
}

func (s syncer) run() {
	store := make(map[interface{}]chan struct{})

	for cmd := range s {
		go func(cmd cmdData) {
			switch cmd.action {
			case lock:
				if c, ok := store[cmd.token]; ok {
					c <- struct{}{}
				} else {
					c := make(chan struct{}, 1)
					c <- struct{}{}
					store[cmd.token] = c
				}
			case unlock:
				if c, ok := store[cmd.token]; ok {
					<-c
				}
			}

			cmd.done <- struct{}{}
		}(cmd)
	}
}

// Lock implements the MultiSyncer interface
func (s syncer) Lock(token interface{}) {
	done := make(chan struct{})
	s <- cmdData{action: lock, token: token, done: done}
	<-done
}

// Unlock implements the MultiSyncer interface
func (s syncer) Unlock(token interface{}) {
	done := make(chan struct{})
	s <- cmdData{action: unlock, token: token, done: done}
	<-done
}
<commit_msg>Make sure the store is accessed from the main goroutine<commit_after>//
// Copyright 2015, Sander van Harmelen
//
// 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 multisyncer

type syncer chan cmdData

type cmdData struct {
	action cmdAction
	token  interface{}
	done   chan struct{}
}

type cmdAction int

const (
	lock cmdAction = iota
	unlock
)

// MultiSyncer synchronizes access based on a given token
type MultiSyncer interface {
	Lock(interface{})
	Unlock(interface{})
}

// New returns a new MultiSyncer
func New() MultiSyncer {
	s := make(syncer)
	go s.run()
	return s
}

func (s syncer) run() {
	store := make(map[interface{}]chan struct{})

	for cmd := range s {
		l, ok := store[cmd.token]

		if !ok && cmd.action == lock {
			l = make(chan struct{}, 1)
			store[cmd.token] = l
		}

		go func(cmd cmdData) {
			switch cmd.action {
			case lock:
				l <- struct{}{}
			case unlock:
				if ok {
					<-l
				}
			}

			cmd.done <- struct{}{}
		}(cmd)
	}
}

// Lock implements the MultiSyncer interface
func (s syncer) Lock(token interface{}) {
	done := make(chan struct{})
	s <- cmdData{action: lock, token: token, done: done}
	<-done
}

// Unlock implements the MultiSyncer interface
func (s syncer) Unlock(token interface{}) {
	done := make(chan struct{})
	s <- cmdData{action: unlock, token: token, done: done}
	<-done
}
<|endoftext|>


<commit_before>package persist

import (
	"fmt"
	"io"
	"log"
	"os"
	"sync"

	"github.com/NebulousLabs/Sia/build"
)

// Logger is a wrapper for the standard library logger that enforces logging
// with the Sia-standard settings. It also supports a Close method, which
// attempts to close the underlying io.Writer.
type Logger struct {
	*log.Logger
	w io.Writer
}

// Close logs a shutdown message and closes the Logger's underlying io.Writer,
// if it is also an io.Closer.
func (l *Logger) Close() error {
	l.Output(2, "SHUTDOWN: Logging has terminated.")
	if c, ok := l.w.(io.Closer); ok {
		return c.Close()
	}
	return nil
}

// Critical logs a message with a CRITICAL prefix that guides the user to the
// Sia github tracker. If debug mode is enabled, it will also write the message
// to os.Stderr and panic. Critical should only be called if there has been a
// developer error, otherwise Severe should be called.
func (l *Logger) Critical(v ...interface{}) {
	l.Output(2, "CRITICAL: "+fmt.Sprintln(v...))
	build.Critical(v...)
}

// Debug is equivalent to Logger.Print when build.DEBUG is true. Otherwise it
// is a no-op.
func (l *Logger) Debug(v ...interface{}) {
	if build.DEBUG {
		l.Output(2, fmt.Sprint(v...))
	}
}

// Debugf is equivalent to Logger.Printf when build.DEBUG is true. Otherwise it
// is a no-op.
func (l *Logger) Debugf(format string, v ...interface{}) {
	if build.DEBUG {
		l.Output(2, fmt.Sprintf(format, v...))
	}
}

// Debugln is equivalent to Logger.Println when build.DEBUG is true. Otherwise
// it is a no-op.
func (l *Logger) Debugln(v ...interface{}) {
	if build.DEBUG {
		l.Output(2, "[DEBUG] "+fmt.Sprintln(v...))
	}
}

// Severe logs a message with a SEVERE prefix. If debug mode is enabled, it
// will also write the message to os.Stderr and panic. Severe should be called
// if there is a severe problem with the user's machine or setup that should be
// addressed ASAP but does not necessarily require that the machine crash or
// exit.
func (l *Logger) Severe(v ...interface{}) {
	l.Output(2, "SEVERE: "+fmt.Sprintln(v...))
	build.Severe(v...)
}

// NewLogger returns a logger that can be closed. Calls should not be made to
// the logger after 'Close' has been called.
func NewLogger(w io.Writer) *Logger {
	l := log.New(w, "", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile|log.LUTC)
	l.Output(3, "STARTUP: Logging has started.") // Call depth is 3 because NewLogger is usually called by NewFileLogger
	return &Logger{l, w}
}

// closeableFile wraps an os.File to perform sanity checks on its Write and
// Close methods. When the checks are enabled, calls to Write or Close will
// panic if they are called after the file has already been closed.
type closeableFile struct {
	*os.File
	closed bool
	mu     sync.RWMutex
}

// Close closes the file and sets the closed flag.
func (cf *closeableFile) Close() error {
	cf.mu.Lock()
	defer cf.mu.Unlock()
	// Sanity check - close should not have been called yet.
	if cf.closed {
		build.Critical("cannot close the file; already closed")
	}

	// Ensure that all data has actually hit the disk.
	if err := cf.Sync(); err != nil {
		return err
	}
	cf.closed = true
	return cf.File.Close()
}

// Write takes the input data and writes it to the file.
func (cf *closeableFile) Write(b []byte) (int, error) {
	cf.mu.RLock()
	defer cf.mu.RUnlock()
	// Sanity check - close should not have been called yet.
	if cf.closed {
		build.Critical("cannot write to the file after it has been closed")
	}
	return cf.File.Write(b)
}

// NewFileLogger returns a logger that logs to logFilename. The file is opened
// in append mode, and created if it does not exist.
func NewFileLogger(logFilename string) (*Logger, error) {
	logFile, err := os.OpenFile(logFilename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0660)
	if err != nil {
		return nil, err
	}
	cf := &closeableFile{File: logFile}
	return NewLogger(cf), nil
}
<commit_msg>undo patch in master for logging<commit_after>package persist

import (
	"fmt"
	"io"
	"log"
	"os"
	"sync"
	"path/filepath"

	"github.com/NebulousLabs/Sia/build"
)

// Logger is a wrapper for the standard library logger that enforces logging
// with the Sia-standard settings. It also supports a Close method, which
// attempts to close the underlying io.Writer.
type Logger struct {
	*log.Logger
	w io.Writer
}

// Close logs a shutdown message and closes the Logger's underlying io.Writer,
// if it is also an io.Closer.
func (l *Logger) Close() error {
	l.Output(2, "SHUTDOWN: Logging has terminated.")
	if c, ok := l.w.(io.Closer); ok {
		return c.Close()
	}
	return nil
}

// Critical logs a message with a CRITICAL prefix that guides the user to the
// Sia github tracker. If debug mode is enabled, it will also write the message
// to os.Stderr and panic. Critical should only be called if there has been a
// developer error, otherwise Severe should be called.
func (l *Logger) Critical(v ...interface{}) {
	l.Output(2, "CRITICAL: "+fmt.Sprintln(v...))
	build.Critical(v...)
}

// Debug is equivalent to Logger.Print when build.DEBUG is true. Otherwise it
// is a no-op.
func (l *Logger) Debug(v ...interface{}) {
	if build.DEBUG {
		l.Output(2, fmt.Sprint(v...))
	}
}

// Debugf is equivalent to Logger.Printf when build.DEBUG is true. Otherwise it
// is a no-op.
func (l *Logger) Debugf(format string, v ...interface{}) {
	if build.DEBUG {
		l.Output(2, fmt.Sprintf(format, v...))
	}
}

// Debugln is equivalent to Logger.Println when build.DEBUG is true. Otherwise
// it is a no-op.
func (l *Logger) Debugln(v ...interface{}) {
	if build.DEBUG {
		l.Output(2, "[DEBUG] "+fmt.Sprintln(v...))
	}
}

// Severe logs a message with a SEVERE prefix. If debug mode is enabled, it
// will also write the message to os.Stderr and panic. Severe should be called
// if there is a severe problem with the user's machine or setup that should be
// addressed ASAP but does not necessarily require that the machine crash or
// exit.
func (l *Logger) Severe(v ...interface{}) {
	l.Output(2, "SEVERE: "+fmt.Sprintln(v...))
	build.Severe(v...)
}

// NewLogger returns a logger that can be closed. Calls should not be made to
// the logger after 'Close' has been called.
func NewLogger(w io.Writer) *Logger {
	l := log.New(w, "", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile|log.LUTC)
	l.Output(3, "STARTUP: Logging has started.") // Call depth is 3 because NewLogger is usually called by NewFileLogger
	return &Logger{l, w}
}

// closeableFile wraps an os.File to perform sanity checks on its Write and
// Close methods. When the checks are enabled, calls to Write or Close will
// panic if they are called after the file has already been closed.
type closeableFile struct {
	*os.File
	closed bool
	mu     sync.RWMutex
}

// Close closes the file and sets the closed flag.
func (cf *closeableFile) Close() error {
	cf.mu.Lock()
	defer cf.mu.Unlock()
	// Sanity check - close should not have been called yet.
	if cf.closed {
		build.Critical("cannot close the file; already closed")
	}

	// Ensure that all data has actually hit the disk.
	if err := cf.Sync(); err != nil {
		return err
	}
	cf.closed = true
	return cf.File.Close()
}

// Write takes the input data and writes it to the file.
func (cf *closeableFile) Write(b []byte) (int, error) {
	cf.mu.RLock()
	defer cf.mu.RUnlock()
	// Sanity check - close should not have been called yet.
	if cf.closed {
		build.Critical("cannot write to the file after it has been closed")
	}
	return cf.File.Write(b)
}

// NewFileLogger returns a logger that logs to logFilename. The file is opened
// in append mode, and created if it does not exist.
func NewFileLogger(logFilename string) (*Logger, error) {
	logFile, err := os.OpenFile(filepath.Join("/var/log/sia", filepath.Base(logFilename)), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664)
	if err != nil {
		return nil, err
	}
	cf := &closeableFile{File: logFile}
	return NewLogger(cf), nil
}
<|endoftext|>


<commit_before>package main

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

	"github.com/frustra/bbcode"
	"github.com/microcosm-cc/bluemonday"
)

var bbcodeCompiler = func() bbcode.Compiler {
	compiler := bbcode.NewCompiler(true, true)
	compiler.SetTag("list", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "ul"
		style := node.GetOpeningTag().Value
		switch style {
		case "a":
			out.Attrs["style"] = "list-style-type: lower-alpha;"
		case "A":
			out.Attrs["style"] = "list-style-type: upper-alpha;"
		case "i":
			out.Attrs["style"] = "list-style-type: lower-roman;"
		case "I":
			out.Attrs["style"] = "list-style-type: upper-roman;"
		case "1":
			out.Attrs["style"] = "list-style-type: decimal;"
		default:
			out.Attrs["style"] = "list-style-type: disc;"
		}

		if len(node.Children) == 0 {
			out.AppendChild(bbcode.NewHTMLTag(""))
		} else {
			node.Info = []*bbcode.HTMLTag{out, out}
			tags := node.Info.([]*bbcode.HTMLTag)
			for _, child := range node.Children {
				curr := tags[1]
				curr.AppendChild(node.Compiler.CompileTree(child))
			}
			if len(tags[1].Children) > 0 {
				last := tags[1].Children[len(tags[1].Children)-1]
				if len(last.Children) > 0 && last.Children[len(last.Children)-1].Name == "br" {
					last.Children[len(last.Children)-1] = bbcode.NewHTMLTag("")
				}
			} else {
				tags[1].AppendChild(bbcode.NewHTMLTag(""))
			}
		}
		return out, false
	})

	compiler.SetTag("*", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		parent := node.Parent
		for parent != nil {
			if parent.ID == bbcode.OPENING_TAG && parent.GetOpeningTag().Name == "list" {
				out := bbcode.NewHTMLTag("")
				out.Name = "li"
				tags := parent.Info.([]*bbcode.HTMLTag)
				if len(tags[1].Children) > 0 {
					last := tags[1].Children[len(tags[1].Children)-1]
					if len(last.Children) > 0 && last.Children[len(last.Children)-1].Name == "br" {
						last.Children[len(last.Children)-1] = bbcode.NewHTMLTag("")
					}
				} else {
					tags[1].AppendChild(bbcode.NewHTMLTag(""))
				}
				tags[1] = out
				tags[0].AppendChild(out)

				if len(parent.Children) == 0 {
					out.AppendChild(bbcode.NewHTMLTag(""))
				} else {
					for _, child := range node.Children {
						curr := tags[1]
						curr.AppendChild(node.Compiler.CompileTree(child))
					}
				}
				if node.ClosingTag != nil {
					tag := bbcode.NewHTMLTag(node.ClosingTag.Raw)
					bbcode.InsertNewlines(tag)
					out.AppendChild(tag)
				}
				return nil, false
			}
			parent = parent.Parent
		}
		return bbcode.DefaultTagCompiler(node)
	})

	compiler.SetTag("youtube", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		var youtubeID string

		content := bbcode.CompileText(node)
		youtubeLink, err := url.Parse(content)
		if err != nil {
			youtubeID = content
		} else {
			youtubeID = youtubeLink.Query().Get("v")
			if youtubeID == "" {
				youtubeID = content
			}
		}

		tag := bbcode.NewHTMLTag("")
		tag.Name = "iframe"
		tag.Attrs = map[string]string{
			"style":           "width: 100%; max-height: 100%;",
			"src":             "https://www.youtube.com/embed/" + youtubeID,
			"frameborder":     "0",
			"allowfullscreen": "",
		}
		tag.AppendChild(nil)

		container := bbcode.NewHTMLTag("")
		container.Name = "div"
		container.Attrs["class"] = "youtube video container"
		container.AppendChild(tag)

		return container, false
	})

	compiler.SetTag("left", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "div"
		out.Attrs["style"] = "text-align: left;"
		return out, true
	})
	compiler.SetTag("right", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "div"
		out.Attrs["style"] = "text-align: right;"
		return out, true
	})

	compiler.SetTag("container", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		args := node.GetOpeningTag().Args
		out := bbcode.NewHTMLTag("")
		out.Name = "div"
		out.Attrs["style"] = ""
		if _, err := strconv.Atoi(args["width"]); err == nil {
			out.Attrs["style"] += "width: " + args["width"] + "px;"
		}
		return out, true
	})

	compiler.SetTag("hr", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "div"
		out.Attrs["class"] = "ui divider"
		out.AppendChild(nil)
		return out, false
	})

	compiler.SetTag("email", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "a"
		val := node.GetOpeningTag().Value
		if val == "" {
			out.Attrs["href"] = "mailto:" + bbcode.CompileText(node)
			out.AppendChild(bbcode.NewHTMLTag(bbcode.CompileText(node)))
			return out, false
		}
		out.Attrs["href"] = "mailto:" + val
		return out, true
	})

	compiler.SetTag("size", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "span"
		if size, err := strconv.Atoi(node.GetOpeningTag().Value); err == nil && size > 0 {
			if size > 15 {
				size = 15
			}
			out.Attrs["style"] = fmt.Sprintf("font-size: %dpt; line-height: %[1]dpt;", size*6)
		}
		return out, true
	})

	return compiler
}()

var emojis = []string{
	"peppy",
	"barney",
	"akerino",
	"foka",
	"kappy",
	"creepypeppy",
	"peppyfiero",
	"djpeppy",
	"kappa",
}
var emojiReplacer = func() *strings.Replacer {
	var list []string
	for _, e := range emojis {
		list = append(list, ":"+e+":", "[img=/static/emotes/"+e+".png]:"+e+":[/img]")
	}
	return strings.NewReplacer(list...)
}()

func compileBBCode(s string) string {
	s = emojiReplacer.Replace(s)
	s = strings.TrimSpace(s)
	return mondaySanitise(bbcodeCompiler.Compile(s))
}

var policy = func() *bluemonday.Policy {
	p := bluemonday.UGCPolicy()
	p.AllowAttrs("style", "class").Globally()
	return p
}()

func mondaySanitise(source string) string {
	return policy.Sanitize(source)
}
<commit_msg>Fix youtube tag not working<commit_after>package main

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

	"github.com/frustra/bbcode"
	"github.com/microcosm-cc/bluemonday"
)

var bbcodeCompiler = func() bbcode.Compiler {
	compiler := bbcode.NewCompiler(true, true)
	compiler.SetTag("list", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "ul"
		style := node.GetOpeningTag().Value
		switch style {
		case "a":
			out.Attrs["style"] = "list-style-type: lower-alpha;"
		case "A":
			out.Attrs["style"] = "list-style-type: upper-alpha;"
		case "i":
			out.Attrs["style"] = "list-style-type: lower-roman;"
		case "I":
			out.Attrs["style"] = "list-style-type: upper-roman;"
		case "1":
			out.Attrs["style"] = "list-style-type: decimal;"
		default:
			out.Attrs["style"] = "list-style-type: disc;"
		}

		if len(node.Children) == 0 {
			out.AppendChild(bbcode.NewHTMLTag(""))
		} else {
			node.Info = []*bbcode.HTMLTag{out, out}
			tags := node.Info.([]*bbcode.HTMLTag)
			for _, child := range node.Children {
				curr := tags[1]
				curr.AppendChild(node.Compiler.CompileTree(child))
			}
			if len(tags[1].Children) > 0 {
				last := tags[1].Children[len(tags[1].Children)-1]
				if len(last.Children) > 0 && last.Children[len(last.Children)-1].Name == "br" {
					last.Children[len(last.Children)-1] = bbcode.NewHTMLTag("")
				}
			} else {
				tags[1].AppendChild(bbcode.NewHTMLTag(""))
			}
		}
		return out, false
	})

	compiler.SetTag("*", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		parent := node.Parent
		for parent != nil {
			if parent.ID == bbcode.OPENING_TAG && parent.GetOpeningTag().Name == "list" {
				out := bbcode.NewHTMLTag("")
				out.Name = "li"
				tags := parent.Info.([]*bbcode.HTMLTag)
				if len(tags[1].Children) > 0 {
					last := tags[1].Children[len(tags[1].Children)-1]
					if len(last.Children) > 0 && last.Children[len(last.Children)-1].Name == "br" {
						last.Children[len(last.Children)-1] = bbcode.NewHTMLTag("")
					}
				} else {
					tags[1].AppendChild(bbcode.NewHTMLTag(""))
				}
				tags[1] = out
				tags[0].AppendChild(out)

				if len(parent.Children) == 0 {
					out.AppendChild(bbcode.NewHTMLTag(""))
				} else {
					for _, child := range node.Children {
						curr := tags[1]
						curr.AppendChild(node.Compiler.CompileTree(child))
					}
				}
				if node.ClosingTag != nil {
					tag := bbcode.NewHTMLTag(node.ClosingTag.Raw)
					bbcode.InsertNewlines(tag)
					out.AppendChild(tag)
				}
				return nil, false
			}
			parent = parent.Parent
		}
		return bbcode.DefaultTagCompiler(node)
	})

	compiler.SetTag("youtube", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		var youtubeID string

		content := bbcode.CompileText(node)
		youtubeLink, err := url.Parse(content)
		if err != nil {
			youtubeID = content
		} else {
			youtubeID = youtubeLink.Query().Get("v")
			if youtubeID == "" {
				youtubeID = content
			}
		}

		tag := bbcode.NewHTMLTag("")
		tag.Name = "iframe"
		tag.Attrs = map[string]string{
			"style":           "width: 100%; max-height: 100%;",
			"src":             "https://www.youtube.com/embed/" + youtubeID,
			"frameborder":     "0",
			"allowfullscreen": "",
		}
		tag.AppendChild(nil)

		container := bbcode.NewHTMLTag("")
		container.Name = "div"
		container.Attrs["class"] = "youtube video container"
		container.AppendChild(tag)

		return container, false
	})

	compiler.SetTag("left", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "div"
		out.Attrs["style"] = "text-align: left;"
		return out, true
	})
	compiler.SetTag("right", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "div"
		out.Attrs["style"] = "text-align: right;"
		return out, true
	})

	compiler.SetTag("container", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		args := node.GetOpeningTag().Args
		out := bbcode.NewHTMLTag("")
		out.Name = "div"
		out.Attrs["style"] = ""
		if _, err := strconv.Atoi(args["width"]); err == nil {
			out.Attrs["style"] += "width: " + args["width"] + "px;"
		}
		return out, true
	})

	compiler.SetTag("hr", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "div"
		out.Attrs["class"] = "ui divider"
		out.AppendChild(nil)
		return out, false
	})

	compiler.SetTag("email", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "a"
		val := node.GetOpeningTag().Value
		if val == "" {
			out.Attrs["href"] = "mailto:" + bbcode.CompileText(node)
			out.AppendChild(bbcode.NewHTMLTag(bbcode.CompileText(node)))
			return out, false
		}
		out.Attrs["href"] = "mailto:" + val
		return out, true
	})

	compiler.SetTag("size", func(node *bbcode.BBCodeNode) (*bbcode.HTMLTag, bool) {
		out := bbcode.NewHTMLTag("")
		out.Name = "span"
		if size, err := strconv.Atoi(node.GetOpeningTag().Value); err == nil && size > 0 {
			if size > 15 {
				size = 15
			}
			out.Attrs["style"] = fmt.Sprintf("font-size: %dpt; line-height: %[1]dpt;", size*6)
		}
		return out, true
	})

	return compiler
}()

var emojis = []string{
	"peppy",
	"barney",
	"akerino",
	"foka",
	"kappy",
	"creepypeppy",
	"peppyfiero",
	"djpeppy",
	"kappa",
}
var emojiReplacer = func() *strings.Replacer {
	var list []string
	for _, e := range emojis {
		list = append(list, ":"+e+":", "[img=/static/emotes/"+e+".png]:"+e+":[/img]")
	}
	return strings.NewReplacer(list...)
}()

func compileBBCode(s string) string {
	s = emojiReplacer.Replace(s)
	s = strings.TrimSpace(s)
	return mondaySanitise(bbcodeCompiler.Compile(s))
}

var policy = func() *bluemonday.Policy {
	p := bluemonday.UGCPolicy()
	p.AllowAttrs("style", "class").Globally()
	p.AllowElements("iframe")
	p.AllowAttrs("style", "src", "frameborder", "allowfullscreen").OnElements("iframe")
	return p
}()

func mondaySanitise(source string) string {
	return policy.Sanitize(source)
}
<|endoftext|>


<commit_before>//Package phone abstracts away sending various message types to a phone.
package phone

import (
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"

	"github.com/mfresonke/ngrokker"
)

const (
	// callback path for twilio requests
	twilioCallbackPath = "/callback"
	// the prefix used before hosting files. For more info see the "file" type.
	filePrefixPath = "/file"
)

//Sender holds the necessary values for sending supported data to a phone.
//
//Must be initalized with the NewSender func.
type Sender struct {
	verbose bool
	port    int
	tunnel  ngrokker.Tunneler
	config  TwilioConfig
}

//NewSender creates a new sender object with the specified options. Utilizes the
// ngrokker pkg, and in turn, ngrok, for its introspective tunneling purposes,
// and Twilio for its MMS sending purposes. If you'd like to override the tunneling
// service with something else, use NewSenderTunnel.
//
//A valid Twilio Configuration is needed to be able to properly send a message.
//
//Users must accept the ngrok ToS before sending anything.
//
//Port will be used to create a local webserver and introspective tunnel, if
// necessary. The port must not be currently in use by another process.
//
//Verbose prints diagnostic information to stderr.
func NewSender(
	config TwilioConfig,
	acceptedNGROKTOS bool,
	port int,
	verbose bool,
) *Sender {
	tunnel := ngrokker.NewHTTPTunnel(acceptedNGROKTOS, verbose)
	return NewSenderTunnel(tunnel, config, port, verbose)
}

//NewSenderTunnel is similar to NewSender, except that it allows you to
// override the introspective tunneling service with your own.
func NewSenderTunnel(
	tunnel ngrokker.Tunneler,
	config TwilioConfig,
	port int,
	verbose bool,
) *Sender {
	return &Sender{
		config:  config,
		tunnel:  tunnel,
		port:    port,
		verbose: verbose,
	}
}

//SendFile sends a file to the specified phone number.
//
//Currently, it only supports photos, but support for additional files
// is planned.
func (s Sender) SendFile(phoneNumber, filePath string) error {
	// check that the given file exists and is not a directory
	if fileInfo, err := os.Stat(filePath); os.IsNotExist(err) {
		return ErrFileDoesNotExist
	} else if fileInfo.IsDir() {
		return ErrFileIsDirectory
	}

	// check the extension of the given file to make sure it is compatible with twilio
	fileExt := filepath.Ext(filePath)
	if ok := isValidPhotoExt(fileExt); !ok {
		return ErrFiletypeNotSupported
	}

	// start the go webserver to serve the image
	webserverErrChan := make(chan error, 1)
	go serveFile(webserverErrChan, s.port, filePath)

	//open the introspective tunnel
	_, err := s.tunnel.Open(s.port)
	if err != nil {
		return err
	}

	// at some point check the channels for errors
	select {
	case _ = <-webserverErrChan:
		//do something useful
	}

	return nil
}

//sendableFile represents a valid, sendable input file based on its path.
type sendableFile string

// func newFile(filePath string) file {
//
// }

func (f sendableFile) publicURL(baseURL string) string {
	_, fileName := path.Split(string(f))
	urlFileName := url.QueryEscape(fileName)
	return baseURL + filePrefixPath + "/" + urlFileName
}

var isValidPhotoExtRegex = regexp.MustCompile(".*(.jpg|.jpeg|.gif|.png|.bmp)")

func isValidPhotoExt(fileExtension string) bool {
	return isValidPhotoExtRegex.MatchString(fileExtension)
}

func serveFile(errorChan chan error, port int, filePath string) {
	http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
		// implement twilio callback parsing here.
	})
	http.HandleFunc("/file", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filePath)
	})
	bindStr := ":" + strconv.Itoa(port)
	errorChan <- http.ListenAndServe(bindStr, nil)
}
<commit_msg>Improved file-serving logic and moved file-checks to a helper type<commit_after>//Package phone abstracts away sending various message types to a phone.
package phone

import (
	"log"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"

	"github.com/mfresonke/ngrokker"
)

const (
	// callback path for twilio requests
	twilioCallbackPath = "/callback"
	// the prefix used before hosting files. For more info see the "file" type.
	filePrefixPath = "/file"
)

//Sender holds the necessary values for sending supported data to a phone.
//
//Must be initalized with the NewSender func.
type Sender struct {
	verbose bool
	port    int
	tunnel  ngrokker.Tunneler
	config  TwilioConfig
}

//NewSender creates a new sender object with the specified options. Utilizes the
// ngrokker pkg, and in turn, ngrok, for its introspective tunneling purposes,
// and Twilio for its MMS sending purposes. If you'd like to override the tunneling
// service with something else, use NewSenderTunnel.
//
//A valid Twilio Configuration is needed to be able to properly send a message.
//
//Users must accept the ngrok ToS before sending anything.
//
//Port will be used to create a local webserver and introspective tunnel, if
// necessary. The port must not be currently in use by another process.
//
//Verbose prints diagnostic information to stderr.
func NewSender(
	config TwilioConfig,
	acceptedNGROKTOS bool,
	port int,
	verbose bool,
) *Sender {
	tunnel := ngrokker.NewHTTPTunnel(acceptedNGROKTOS, verbose)
	return NewSenderTunnel(tunnel, config, port, verbose)
}

//NewSenderTunnel is similar to NewSender, except that it allows you to
// override the introspective tunneling service with your own.
func NewSenderTunnel(
	tunnel ngrokker.Tunneler,
	config TwilioConfig,
	port int,
	verbose bool,
) *Sender {
	return &Sender{
		config:  config,
		tunnel:  tunnel,
		port:    port,
		verbose: verbose,
	}
}

//SendFile sends a file to the specified phone number.
//
//Currently, it only supports photos, but support for additional files
// is planned.
func (s Sender) SendFile(phoneNumber, filePath string) error {
	// validate the input file.
	file, err := newSendableFile(filePath)
	if err != nil {
		return err
	}

	// start the go webserver to serve the image
	webserverErrChan := make(chan error, 1)
	go serveFile(webserverErrChan, s.port, file, s.verbose)

	//open the introspective tunnel
	_, err = s.tunnel.Open(s.port)
	if err != nil {
		return err
	}

	//send the image!
	//makeTwilioRequest()

	// at some point check the channels for errors
	select {
	case _ = <-webserverErrChan:
		//do something useful
	}

	return nil
}

//sendableFile represents a valid, sendable input file based on its path.
type sendableFile string

func newSendableFile(filePath string) (sendableFile, error) {
	// check that the given file exists and is not a directory
	if fileInfo, err := os.Stat(filePath); os.IsNotExist(err) {
		return "", ErrFileDoesNotExist
	} else if fileInfo.IsDir() {
		return "", ErrFileIsDirectory
	}

	// check the extension of the given file to make sure it is compatible with twilio
	fileExt := filepath.Ext(filePath)
	if ok := isValidPhotoExt(fileExt); !ok {
		return "", ErrFiletypeNotSupported
	}
	// if all looks good, return a valid file object!
	return sendableFile(filePath), nil
}

func (f sendableFile) path() string {
	return string(f)
}

func (f sendableFile) name() string {
	_, fileName := path.Split(string(f))
	return fileName
}

func (f sendableFile) publicURL(baseURL string) string {
	fileName := f.name()
	urlFileName := url.QueryEscape(fileName)
	return baseURL + filePrefixPath + "/" + urlFileName
}

var isValidPhotoExtRegex = regexp.MustCompile(".*(.jpg|.jpeg|.gif|.png|.bmp)")

func isValidPhotoExt(fileExtension string) bool {
	return isValidPhotoExtRegex.MatchString(fileExtension)
}

func serveFile(errorChan chan error, port int, file sendableFile, verbose bool) {
	http.HandleFunc(twilioCallbackPath, func(w http.ResponseWriter, r *http.Request) {
		// implement twilio callback parsing here.
	})
	http.HandleFunc(filePrefixPath, func(w http.ResponseWriter, r *http.Request) {
		fileName := r.URL.Path[len(filePrefixPath)+1:]
		if fileName != file.name() {
			if verbose {
				log.Println(
					"unable to serve file, as fileName and file.name() differ. fileName:",
					fileName,
					"file.name():",
					file.name(),
				)
			}
			return
		}
		http.ServeFile(w, r, file.path())
	})
	bindStr := ":" + strconv.Itoa(port)
	errorChan <- http.ListenAndServe(bindStr, nil)
}
<|endoftext|>