<commit_before>package shared

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

type ContainerState struct {
	Status     string                           `json:"status"`
	StatusCode StatusCode                       `json:"status_code"`
	CPU        ContainerStateCPU                `json:"cpu"`
	Disk       map[string]ContainerStateDisk    `json:"disk"`
	Memory     ContainerStateMemory             `json:"memory"`
	Network    map[string]ContainerStateNetwork `json:"network"`
	Pid        int64                            `json:"pid"`
	Processes  int64                            `json:"processes"`
}

type ContainerStateDisk struct {
	Usage int64 `json:"usage"`
}

type ContainerStateCPU struct {
	Usage int64 `json:"usage"`
}

type ContainerStateMemory struct {
	Usage         int64 `json:"usage"`
	UsagePeak     int64 `json:"usage_peak"`
	SwapUsage     int64 `json:"swap_usage"`
	SwapUsagePeak int64 `json:"swap_usage_peak"`
}

type ContainerStateNetwork struct {
	Addresses []ContainerStateNetworkAddress `json:"addresses"`
	Counters  ContainerStateNetworkCounters  `json:"counters"`
	Hwaddr    string                         `json:"hwaddr"`
	HostName  string                         `json:"host_name"`
	Mtu       int                            `json:"mtu"`
	State     string                         `json:"state"`
	Type      string                         `json:"type"`
}

type ContainerStateNetworkAddress struct {
	Family  string `json:"family"`
	Address string `json:"address"`
	Netmask string `json:"netmask"`
	Scope   string `json:"scope"`
}

type ContainerStateNetworkCounters struct {
	BytesReceived   int64 `json:"bytes_received"`
	BytesSent       int64 `json:"bytes_sent"`
	PacketsReceived int64 `json:"packets_received"`
	PacketsSent     int64 `json:"packets_sent"`
}

type ContainerExecControl struct {
	Command string            `json:"command"`
	Args    map[string]string `json:"args"`
}

type SnapshotInfo struct {
	Architecture    string            `json:"architecture"`
	Config          map[string]string `json:"config"`
	CreationDate    time.Time         `json:"created_at"`
	Devices         Devices           `json:"devices"`
	Ephemeral       bool              `json:"ephemeral"`
	ExpandedConfig  map[string]string `json:"expanded_config"`
	ExpandedDevices Devices           `json:"expanded_devices"`
	LastUsedDate    time.Time         `json:"last_used_at"`
	Name            string            `json:"name"`
	Profiles        []string          `json:"profiles"`
	Stateful        bool              `json:"stateful"`
}

type ContainerInfo struct {
	Architecture    string            `json:"architecture"`
	Config          map[string]string `json:"config"`
	CreationDate    time.Time         `json:"created_at"`
	Devices         Devices           `json:"devices"`
	Ephemeral       bool              `json:"ephemeral"`
	ExpandedConfig  map[string]string `json:"expanded_config"`
	ExpandedDevices Devices           `json:"expanded_devices"`
	LastUsedDate    time.Time         `json:"last_used_at"`
	Name            string            `json:"name"`
	Profiles        []string          `json:"profiles"`
	Stateful        bool              `json:"stateful"`
	Status          string            `json:"status"`
	StatusCode      StatusCode        `json:"status_code"`
}

func (c ContainerInfo) IsActive() bool {
	switch c.StatusCode {
	case Stopped:
		return false
	case Error:
		return false
	default:
		return true
	}
}

/*
 * BriefContainerState contains a subset of the fields in
 * ContainerState, namely those which a user may update
 */
type BriefContainerInfo struct {
	Name      string            `json:"name"`
	Profiles  []string          `json:"profiles"`
	Config    map[string]string `json:"config"`
	Devices   Devices           `json:"devices"`
	Ephemeral bool              `json:"ephemeral"`
}

func (c *ContainerInfo) Brief() BriefContainerInfo {
	retstate := BriefContainerInfo{Name: c.Name,
		Profiles:  c.Profiles,
		Config:    c.Config,
		Devices:   c.Devices,
		Ephemeral: c.Ephemeral}
	return retstate
}

func (c *ContainerInfo) BriefExpanded() BriefContainerInfo {
	retstate := BriefContainerInfo{Name: c.Name,
		Profiles:  c.Profiles,
		Config:    c.ExpandedConfig,
		Devices:   c.ExpandedDevices,
		Ephemeral: c.Ephemeral}
	return retstate
}

type ContainerAction string

const (
	Stop     ContainerAction = "stop"
	Start    ContainerAction = "start"
	Restart  ContainerAction = "restart"
	Freeze   ContainerAction = "freeze"
	Unfreeze ContainerAction = "unfreeze"
)

type ProfileConfig struct {
	Name        string            `json:"name"`
	Config      map[string]string `json:"config"`
	Description string            `json:"description"`
	Devices     Devices           `json:"devices"`
	UsedBy      []string          `json:"used_by"`
}

type NetworkConfig struct {
	Name    string            `json:"name"`
	Config  map[string]string `json:"config"`
	Managed bool              `json:"managed"`
	Type    string            `json:"type"`
	UsedBy  []string          `json:"used_by"`
}

func IsInt64(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	return nil
}

func IsPriority(value string) error {
	if value == "" {
		return nil
	}

	valueInt, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	if valueInt < 0 || valueInt > 10 {
		return fmt.Errorf("Invalid value for a limit '%s'. Must be between 0 and 10.", value)
	}

	return nil
}

func IsBool(value string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
		return fmt.Errorf("Invalid value for a boolean: %s", value)
	}

	return nil
}

func IsOneOf(value string, valid []string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(value, valid) {
		return fmt.Errorf("Invalid value: %s (not one of %s)", value, valid)
	}

	return nil
}

func IsAny(value string) error {
	return nil
}

// KnownContainerConfigKeys maps all fully defined, well-known config keys
// to an appropriate checker function, which validates whether or not a
// given value is syntactically legal.
var KnownContainerConfigKeys = map[string]func(value string) error{
	"boot.autostart":             IsBool,
	"boot.autostart.delay":       IsInt64,
	"boot.autostart.priority":    IsInt64,
	"boot.host_shutdown_timeout": IsInt64,

	"limits.cpu": IsAny,
	"limits.cpu.allowance": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			// Percentage based allocation
			_, err := strconv.Atoi(strings.TrimSuffix(value, "%"))
			if err != nil {
				return err
			}

			return nil
		}

		// Time based allocation
		fields := strings.SplitN(value, "/", 2)
		if len(fields) != 2 {
			return fmt.Errorf("Invalid allowance: %s", value)
		}

		_, err := strconv.Atoi(strings.TrimSuffix(fields[0], "ms"))
		if err != nil {
			return err
		}

		_, err = strconv.Atoi(strings.TrimSuffix(fields[1], "ms"))
		if err != nil {
			return err
		}

		return nil
	},
	"limits.cpu.priority": IsPriority,

	"limits.disk.priority": IsPriority,

	"limits.memory": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			_, err := strconv.ParseInt(strings.TrimSuffix(value, "%"), 10, 64)
			if err != nil {
				return err
			}

			return nil
		}

		_, err := ParseByteSizeString(value)
		if err != nil {
			return err
		}

		return nil
	},
	"limits.memory.enforce": func(value string) error {
		return IsOneOf(value, []string{"soft", "hard"})
	},
	"limits.memory.swap":          IsBool,
	"limits.memory.swap.priority": IsPriority,

	"limits.network.priority": IsPriority,

	"limits.processes": IsInt64,

	"linux.kernel_modules": IsAny,

	"security.nesting":    IsBool,
	"security.privileged": IsBool,

	"security.syscalls.blacklist_default": IsBool,
	"security.syscalls.blacklist_compat":  IsBool,
	"security.syscalls.blacklist":         IsAny,
	"security.syscalls.whitelist":         IsAny,

	// Caller is responsible for full validation of any raw.* value
	"raw.apparmor": IsAny,
	"raw.lxc":      IsAny,
	"raw.seccomp":  IsAny,

	"volatile.apply_template":   IsAny,
	"volatile.base_image":       IsAny,
	"volatile.last_state.idmap": IsAny,
	"volatile.last_state.power": IsAny,
}

// ConfigKeyChecker returns a function that will check whether or not
// a provide value is valid for the associate config key.  Returns an
// error if the key is not known.  The checker function only performs
// syntactic checking of the value, semantic and usage checking must
// be done by the caller.  User defined keys are always considered to
// be valid, e.g. user.* and environment.* keys.
func ConfigKeyChecker(key string) (func(value string) error, error) {
	if f, ok := KnownContainerConfigKeys[key]; ok {
		return f, nil
	}

	if strings.HasPrefix(key, "volatile.") {
		if strings.HasSuffix(key, ".hwaddr") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".name") {
			return IsAny, nil
		}
	}

	if strings.HasPrefix(key, "environment.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "user.") {
		return IsAny, nil
	}

	return nil, fmt.Errorf("Bad key: %s", key)
}
<commit_msg>shared/container: add Signal field to exec struct<commit_after>package shared

import (
	"fmt"
	"strconv"
	"strings"
	"syscall"
	"time"
)

type ContainerState struct {
	Status     string                           `json:"status"`
	StatusCode StatusCode                       `json:"status_code"`
	CPU        ContainerStateCPU                `json:"cpu"`
	Disk       map[string]ContainerStateDisk    `json:"disk"`
	Memory     ContainerStateMemory             `json:"memory"`
	Network    map[string]ContainerStateNetwork `json:"network"`
	Pid        int64                            `json:"pid"`
	Processes  int64                            `json:"processes"`
}

type ContainerStateDisk struct {
	Usage int64 `json:"usage"`
}

type ContainerStateCPU struct {
	Usage int64 `json:"usage"`
}

type ContainerStateMemory struct {
	Usage         int64 `json:"usage"`
	UsagePeak     int64 `json:"usage_peak"`
	SwapUsage     int64 `json:"swap_usage"`
	SwapUsagePeak int64 `json:"swap_usage_peak"`
}

type ContainerStateNetwork struct {
	Addresses []ContainerStateNetworkAddress `json:"addresses"`
	Counters  ContainerStateNetworkCounters  `json:"counters"`
	Hwaddr    string                         `json:"hwaddr"`
	HostName  string                         `json:"host_name"`
	Mtu       int                            `json:"mtu"`
	State     string                         `json:"state"`
	Type      string                         `json:"type"`
}

type ContainerStateNetworkAddress struct {
	Family  string `json:"family"`
	Address string `json:"address"`
	Netmask string `json:"netmask"`
	Scope   string `json:"scope"`
}

type ContainerStateNetworkCounters struct {
	BytesReceived   int64 `json:"bytes_received"`
	BytesSent       int64 `json:"bytes_sent"`
	PacketsReceived int64 `json:"packets_received"`
	PacketsSent     int64 `json:"packets_sent"`
}

type ContainerExecControl struct {
	Command string            `json:"command"`
	Args    map[string]string `json:"args"`
	Signal  syscall.Signal    `json:"signal"`
}

type SnapshotInfo struct {
	Architecture    string            `json:"architecture"`
	Config          map[string]string `json:"config"`
	CreationDate    time.Time         `json:"created_at"`
	Devices         Devices           `json:"devices"`
	Ephemeral       bool              `json:"ephemeral"`
	ExpandedConfig  map[string]string `json:"expanded_config"`
	ExpandedDevices Devices           `json:"expanded_devices"`
	LastUsedDate    time.Time         `json:"last_used_at"`
	Name            string            `json:"name"`
	Profiles        []string          `json:"profiles"`
	Stateful        bool              `json:"stateful"`
}

type ContainerInfo struct {
	Architecture    string            `json:"architecture"`
	Config          map[string]string `json:"config"`
	CreationDate    time.Time         `json:"created_at"`
	Devices         Devices           `json:"devices"`
	Ephemeral       bool              `json:"ephemeral"`
	ExpandedConfig  map[string]string `json:"expanded_config"`
	ExpandedDevices Devices           `json:"expanded_devices"`
	LastUsedDate    time.Time         `json:"last_used_at"`
	Name            string            `json:"name"`
	Profiles        []string          `json:"profiles"`
	Stateful        bool              `json:"stateful"`
	Status          string            `json:"status"`
	StatusCode      StatusCode        `json:"status_code"`
}

func (c ContainerInfo) IsActive() bool {
	switch c.StatusCode {
	case Stopped:
		return false
	case Error:
		return false
	default:
		return true
	}
}

/*
 * BriefContainerState contains a subset of the fields in
 * ContainerState, namely those which a user may update
 */
type BriefContainerInfo struct {
	Name      string            `json:"name"`
	Profiles  []string          `json:"profiles"`
	Config    map[string]string `json:"config"`
	Devices   Devices           `json:"devices"`
	Ephemeral bool              `json:"ephemeral"`
}

func (c *ContainerInfo) Brief() BriefContainerInfo {
	retstate := BriefContainerInfo{Name: c.Name,
		Profiles:  c.Profiles,
		Config:    c.Config,
		Devices:   c.Devices,
		Ephemeral: c.Ephemeral}
	return retstate
}

func (c *ContainerInfo) BriefExpanded() BriefContainerInfo {
	retstate := BriefContainerInfo{Name: c.Name,
		Profiles:  c.Profiles,
		Config:    c.ExpandedConfig,
		Devices:   c.ExpandedDevices,
		Ephemeral: c.Ephemeral}
	return retstate
}

type ContainerAction string

const (
	Stop     ContainerAction = "stop"
	Start    ContainerAction = "start"
	Restart  ContainerAction = "restart"
	Freeze   ContainerAction = "freeze"
	Unfreeze ContainerAction = "unfreeze"
)

type ProfileConfig struct {
	Name        string            `json:"name"`
	Config      map[string]string `json:"config"`
	Description string            `json:"description"`
	Devices     Devices           `json:"devices"`
	UsedBy      []string          `json:"used_by"`
}

type NetworkConfig struct {
	Name    string            `json:"name"`
	Config  map[string]string `json:"config"`
	Managed bool              `json:"managed"`
	Type    string            `json:"type"`
	UsedBy  []string          `json:"used_by"`
}

func IsInt64(value string) error {
	if value == "" {
		return nil
	}

	_, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	return nil
}

func IsPriority(value string) error {
	if value == "" {
		return nil
	}

	valueInt, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return fmt.Errorf("Invalid value for an integer: %s", value)
	}

	if valueInt < 0 || valueInt > 10 {
		return fmt.Errorf("Invalid value for a limit '%s'. Must be between 0 and 10.", value)
	}

	return nil
}

func IsBool(value string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
		return fmt.Errorf("Invalid value for a boolean: %s", value)
	}

	return nil
}

func IsOneOf(value string, valid []string) error {
	if value == "" {
		return nil
	}

	if !StringInSlice(value, valid) {
		return fmt.Errorf("Invalid value: %s (not one of %s)", value, valid)
	}

	return nil
}

func IsAny(value string) error {
	return nil
}

// KnownContainerConfigKeys maps all fully defined, well-known config keys
// to an appropriate checker function, which validates whether or not a
// given value is syntactically legal.
var KnownContainerConfigKeys = map[string]func(value string) error{
	"boot.autostart":             IsBool,
	"boot.autostart.delay":       IsInt64,
	"boot.autostart.priority":    IsInt64,
	"boot.host_shutdown_timeout": IsInt64,

	"limits.cpu": IsAny,
	"limits.cpu.allowance": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			// Percentage based allocation
			_, err := strconv.Atoi(strings.TrimSuffix(value, "%"))
			if err != nil {
				return err
			}

			return nil
		}

		// Time based allocation
		fields := strings.SplitN(value, "/", 2)
		if len(fields) != 2 {
			return fmt.Errorf("Invalid allowance: %s", value)
		}

		_, err := strconv.Atoi(strings.TrimSuffix(fields[0], "ms"))
		if err != nil {
			return err
		}

		_, err = strconv.Atoi(strings.TrimSuffix(fields[1], "ms"))
		if err != nil {
			return err
		}

		return nil
	},
	"limits.cpu.priority": IsPriority,

	"limits.disk.priority": IsPriority,

	"limits.memory": func(value string) error {
		if value == "" {
			return nil
		}

		if strings.HasSuffix(value, "%") {
			_, err := strconv.ParseInt(strings.TrimSuffix(value, "%"), 10, 64)
			if err != nil {
				return err
			}

			return nil
		}

		_, err := ParseByteSizeString(value)
		if err != nil {
			return err
		}

		return nil
	},
	"limits.memory.enforce": func(value string) error {
		return IsOneOf(value, []string{"soft", "hard"})
	},
	"limits.memory.swap":          IsBool,
	"limits.memory.swap.priority": IsPriority,

	"limits.network.priority": IsPriority,

	"limits.processes": IsInt64,

	"linux.kernel_modules": IsAny,

	"security.nesting":    IsBool,
	"security.privileged": IsBool,

	"security.syscalls.blacklist_default": IsBool,
	"security.syscalls.blacklist_compat":  IsBool,
	"security.syscalls.blacklist":         IsAny,
	"security.syscalls.whitelist":         IsAny,

	// Caller is responsible for full validation of any raw.* value
	"raw.apparmor": IsAny,
	"raw.lxc":      IsAny,
	"raw.seccomp":  IsAny,

	"volatile.apply_template":   IsAny,
	"volatile.base_image":       IsAny,
	"volatile.last_state.idmap": IsAny,
	"volatile.last_state.power": IsAny,
}

// ConfigKeyChecker returns a function that will check whether or not
// a provide value is valid for the associate config key.  Returns an
// error if the key is not known.  The checker function only performs
// syntactic checking of the value, semantic and usage checking must
// be done by the caller.  User defined keys are always considered to
// be valid, e.g. user.* and environment.* keys.
func ConfigKeyChecker(key string) (func(value string) error, error) {
	if f, ok := KnownContainerConfigKeys[key]; ok {
		return f, nil
	}

	if strings.HasPrefix(key, "volatile.") {
		if strings.HasSuffix(key, ".hwaddr") {
			return IsAny, nil
		}

		if strings.HasSuffix(key, ".name") {
			return IsAny, nil
		}
	}

	if strings.HasPrefix(key, "environment.") {
		return IsAny, nil
	}

	if strings.HasPrefix(key, "user.") {
		return IsAny, nil
	}

	return nil, fmt.Errorf("Bad key: %s", key)
}
<|endoftext|>


<commit_before>/* This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GHTS 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>.

@author: UnHa Kim <unha.kim.ghts@gmail.com> */

package shared

import (
	zmq "github.com/pebbe/zmq4"
	"testing"
)

func TestF파이썬_프로세스_실행(테스트 *testing.T) {
	테스트_결과_회신_소켓, 에러 := zmq.NewSocket(zmq.REP)
	defer 테스트_결과_회신_소켓.Close()

	if 에러 != nil {
		F문자열_출력(에러.Error())
		테스트.Fail()
	}

	테스트_결과_회신_소켓.Bind(P테스트_결과_회신_주소)

	F파이썬_프로세스_실행("func_test.py", "exec_python_process", P테스트_결과_회신_주소)

	메시지, _ := 테스트_결과_회신_소켓.RecvMessage(0)
	구분 := 메시지[0]
	데이터 := 메시지[1]

	F테스트_같음(테스트, 구분, P메시지_구분_OK)
	F테스트_같음(테스트, 데이터, "")

	테스트_결과_회신_소켓.SendMessage([]string{P메시지_구분_OK, ""})
}
<commit_msg>컴파일 에러 1개 수정<commit_after>/* This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GHTS 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>.

@author: UnHa Kim <unha.kim.ghts@gmail.com> */

package shared

import (
	zmq "github.com/pebbe/zmq4"
	"testing"
)

func TestF파이썬_프로세스_실행(테스트 *testing.T) {
	테스트_결과_회신_소켓, 에러 := zmq.NewSocket(zmq.REP)
	defer 테스트_결과_회신_소켓.Close()

	if 에러 != nil {
		F문자열_출력(에러.Error())
		테스트.Fail()
	}

	테스트_결과_회신_소켓.Bind(P주소_테스트_결과_회신)

	F파이썬_프로세스_실행("func_test.py", "exec_python_process", P주소_테스트_결과_회신)

	메시지, _ := 테스트_결과_회신_소켓.RecvMessage(0)
	구분 := 메시지[0]
	데이터 := 메시지[1]

	F테스트_같음(테스트, 구분, P메시지_구분_OK)
	F테스트_같음(테스트, 데이터, "")

	테스트_결과_회신_소켓.SendMessage([]string{P메시지_구분_OK, ""})
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/segmentio/kafka-go"
)

type FaceResponse struct {
	Data        []Face `json:"datas"`
	FaceFeature []int8 `json:"face_feature"`
	Gender      int    `json:"gender"`
	Type        string `json:"type"`
	RetKeys     string `json:"retKeys"`
	Time        string `json:"time"`
	Operator    string `json:"operator"`
	Operation   string `json:"operation"`
}

type Face struct {
	Accessories    int    `json:"accessories"`
	Age            int    `json:"age"`
	FaceFeature    []int8 `json:"face_feature"`
	From_image_id  string `json:"from_image_id"`
	From_person_id string `json:"from_person_id"`
	Gender         int    `json:"gender"`
	ImageData      string `json:"image_data"`
	Indexed        int    `json:"indexed"`
	Json           string `json:"json"`
	Pose           string `json:"pose"`
	Quality        int    `json:"quality"`
	Race           int    `json:"race"`
	SourceId       string `json:"source_id"`
	SourceType     string `json:"source_type"`
	Tid            string `json:"tid"`
	Time           string `json:"time"`
	Version        int    `json:"version"`
}

func main() {
	address := "localhost:9092"
	conn, err := kafka.DialContext(context.Background(), "tcp", address)
	if err != nil {
		panic(err)
	}
	conn.Close()

	topic := "engine-alarm"
	fmt.Printf("success to connect %s topic %s\n", address, topic)
	// make a new reader that consumes from topic-A
	r := kafka.NewReader(kafka.ReaderConfig{
		Brokers:  []string{address},
		GroupID:  "monitor",
		Topic:    topic,
		MinBytes: 10e3, // 10KB
		MaxBytes: 10e6, // 10MB
	})

	var count int
	ctx := context.Background()
	for {
		m, err := r.FetchMessage(ctx)
		if err != nil {
			break
		}
		//fmt.Printf("message at topic/partition/offset %v/%v/%v: %s = %s\n", m.Topic, m.Partition, m.Offset, string(m.Key), string(m.Value))
		count++

		var resp FaceResponse
		if err := json.Unmarshal(m.Value, &resp); err != nil {
			panic(err)
		}
		fmt.Println(count, "==>", resp)

		r.CommitMessages(ctx, m)

		if count > 100 {
			break
		}
	}

	r.Close()
}
<commit_msg>fix topic name<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/segmentio/kafka-go"
)

type FaceResponse struct {
	Data        []Face `json:"datas"`
	FaceFeature []int8 `json:"face_feature"`
	Gender      int    `json:"gender"`
	Type        string `json:"type"`
	RetKeys     string `json:"retKeys"`
	Time        string `json:"time"`
	Operator    string `json:"operator"`
	Operation   string `json:"operation"`
}

type Face struct {
	Accessories    int    `json:"accessories"`
	Age            int    `json:"age"`
	FaceFeature    []int8 `json:"face_feature"`
	From_image_id  string `json:"from_image_id"`
	From_person_id string `json:"from_person_id"`
	Gender         int    `json:"gender"`
	ImageData      string `json:"image_data"`
	Indexed        int    `json:"indexed"`
	Json           string `json:"json"`
	Pose           string `json:"pose"`
	Quality        int    `json:"quality"`
	Race           int    `json:"race"`
	SourceId       string `json:"source_id"`
	SourceType     string `json:"source_type"`
	Tid            string `json:"tid"`
	Time           string `json:"time"`
	Version        int    `json:"version"`
}

func main() {
	address := "localhost:9092"
	conn, err := kafka.DialContext(context.Background(), "tcp", address)
	if err != nil {
		panic(err)
	}
	conn.Close()

	topic := "engine-face"
	fmt.Printf("success to connect %s topic %s\n", address, topic)
	// make a new reader that consumes from topic-A
	r := kafka.NewReader(kafka.ReaderConfig{
		Brokers:  []string{address},
		GroupID:  "monitor",
		Topic:    topic,
		MinBytes: 10e3, // 10KB
		MaxBytes: 10e6, // 10MB
	})

	var count int
	ctx := context.Background()
	for {
		m, err := r.FetchMessage(ctx)
		if err != nil {
			break
		}
		//fmt.Printf("message at topic/partition/offset %v/%v/%v: %s = %s\n", m.Topic, m.Partition, m.Offset, string(m.Key), string(m.Value))
		count++

		var resp FaceResponse
		if err := json.Unmarshal(m.Value, &resp); err != nil {
			panic(err)
		}
		fmt.Println(count, "==>", resp)

		r.CommitMessages(ctx, m)

		if count > 100 {
			break
		}
	}

	r.Close()
}
<|endoftext|>


<commit_before>package commands

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

	"github.com/mesosphere/dcos-commons/cli/client"
	"github.com/mesosphere/dcos-commons/cli/config"
	"gopkg.in/alecthomas/kingpin.v3-unstable"
)

type describeHandler struct{}

type describeRequest struct {
	AppID string `json:"appId"`
}

func checkError(err error, responseBytes []byte) {
	if err != nil {
		reportErrorAndExit(err, responseBytes)
	}
}

func reportErrorAndExit(err error, responseBytes []byte) {
	client.PrintMessage("Failed to unmarshal response. Error: %s", err)
	client.PrintMessage("Original data follows:")
	client.PrintMessageAndExit(string(responseBytes))
}

func describe() {
	// TODO: figure out KingPin's error handling
	requestContent, err := json.Marshal(describeRequest{config.ServiceName})
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	responseBytes, err := client.HTTPCosmosPostJSON("describe", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	// This attempts to retrieve resolvedOptions from the response. This field is only provided by
	// Cosmos running on Enterprise DC/OS 1.10 clusters or later.
	resolvedOptionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "resolvedOptions")
	checkError(err, responseBytes)
	if resolvedOptionsBytes != nil {
		client.PrintJSONBytes(resolvedOptionsBytes)
	} else {
		client.PrintMessage("Package configuration is not available for service %s.", config.ServiceName)
		client.PrintMessage("dcos %s %s is only available for packages installed with Enterprise DC/OS 1.10 or newer.", config.ModuleName, config.Command)
	}
}

func (cmd *describeHandler) handleDescribe(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	describe()
	return nil
}

// HandleDescribe adds the describe subcommand to the passed in kingpin.Application.
func HandleDescribe(app *kingpin.Application) {
	cmd := &describeHandler{}
	app.Command("describe", "View the package configuration for this DC/OS service").Action(cmd.handleDescribe)
}

type updateHandler struct {
	UpdateName     string
	OptionsFile    string
	PackageVersion string
	ViewStatus     bool
}

type updateRequest struct {
	AppID          string                 `json:"appId"`
	PackageVersion string                 `json:"packageVersion,omitempty"`
	OptionsJSON    map[string]interface{} `json:"options,omitempty"`
}

func printPackageVersions() {
	// TODO: figure out KingPin's error handling
	requestContent, _ := json.Marshal(describeRequest{config.ServiceName})
	responseBytes, err := client.HTTPCosmosPostJSON("describe", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	packageBytes, err := client.GetValueFromJSONResponse(responseBytes, "package")
	checkError(err, responseBytes)
	currentVersionBytes, err := client.GetValueFromJSONResponse(packageBytes, "version")
	checkError(err, responseBytes)
	downgradeVersionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "downgradesTo")
	checkError(err, responseBytes)
	downgradeVersions, err := client.JSONBytesToArray(downgradeVersionsBytes)
	checkError(err, responseBytes)
	upgradeVersionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "upgradesTo")
	checkError(err, responseBytes)
	updateVersions, err := client.JSONBytesToArray(upgradeVersionsBytes)
	checkError(err, responseBytes)
	client.PrintMessage("Current package version is: %s", currentVersionBytes)
	if len(downgradeVersions) > 0 {
		client.PrintMessage("Package can be downgraded to: %s", client.PrettyPrintSlice(downgradeVersions))
	} else {
		client.PrintMessage("No valid package downgrade versions.")
	}
	if len(updateVersions) > 0 {
		client.PrintMessage("Package can be upgraded to: %s", client.PrettyPrintSlice(updateVersions))
	} else {
		client.PrintMessage("No valid package upgrade versions.")
	}
}

func (cmd *updateHandler) ViewPackageVersions(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	printPackageVersions()
	return nil
}

func readJSONFile(filename string) (map[string]interface{}, error) {
	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return client.UnmarshalJSON(fileBytes)
}

func parseUpdateResponse(responseBytes []byte) (string, error) {
	responseJSON, err := client.UnmarshalJSON(responseBytes)
	if err != nil {
		return "", err
	}
	return string(responseJSON["marathonDeploymentId"].(string)), nil
}

func doUpdate(optionsFile, packageVersion string) {
	// TODO: figure out KingPin's error handling
	request := updateRequest{AppID: config.ServiceName}
	if len(packageVersion) == 0 && len(optionsFile) == 0 {
		client.PrintMessage("Either --options and/or --package-version must be specified. See --help.")
		return
	}
	if len(packageVersion) > 0 {
		request.PackageVersion = packageVersion
	}
	if len(optionsFile) > 0 {
		fileBytes, err := ioutil.ReadFile(optionsFile)
		if err != nil {
			client.PrintMessage("Failed to load specified options file %s: %s", optionsFile, err)
			return
		}
		optionsJSON, err := client.UnmarshalJSON(fileBytes)
		if err != nil {
			client.PrintMessage("Failed to parse JSON in specified options file %s: %s", optionsFile, err)
			return
		}
		request.OptionsJSON = optionsJSON
	}
	requestContent, _ := json.Marshal(request)
	responseBytes, err := client.HTTPCosmosPostJSON("update", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	_, err = client.UnmarshalJSON(responseBytes)
	checkError(err, responseBytes)
	client.PrintMessage(fmt.Sprintf("Update started. Please use `dcos %s --name=%s update status` to view progress.", config.ModuleName, config.ServiceName))
}

func (cmd *updateHandler) UpdateConfiguration(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	doUpdate(cmd.OptionsFile, cmd.PackageVersion)
	return nil
}

// HandleUpdateSection adds the update subcommand to the passed in kingpin.Application.
func HandleUpdateSection(app *kingpin.Application) {
	cmd := &updateHandler{}
	update := app.Command("update", "Updates the package version or configuration for this DC/OS service")

	start := update.Command("start", "Launches an update operation").Action(cmd.UpdateConfiguration)
	start.Flag("options", "Path to a JSON file that contains customized package installation options").StringVar(&cmd.OptionsFile)
	start.Flag("package-version", "The desired package version").StringVar(&cmd.PackageVersion)

	planCmd := &planHandler{}

	forceComplete := update.Command("force-complete", "Force complete a specific step in the provided phase").Alias("force").Action(planCmd.handleForceComplete)
	forceComplete.Arg("phase", "Name or UUID of the phase containing the provided step").Required().StringVar(&planCmd.Phase)
	forceComplete.Arg("step", "Name or UUID of step to be restarted").Required().StringVar(&planCmd.Step)

	forceRestart := update.Command("force-restart", "Restart update plan, or specific step in the provided phase").Alias("restart").Action(planCmd.handleForceRestart)
	forceRestart.Arg("phase", "Name or UUID of the phase containing the provided step").StringVar(&planCmd.Phase)
	forceRestart.Arg("step", "Name or UUID of step to be restarted").StringVar(&planCmd.Step)

	update.Command("package-versions", "View a list of available package versions to downgrade or upgrade to").Action(cmd.ViewPackageVersions)

	update.Command("pause", "Pause update plan").Alias("interrupt").Action(planCmd.handlePause)

	update.Command("resume", "Resume update plan").Alias("continue").Action(planCmd.handleResume)

	status := update.Command("status", "View status of a running update").Alias("show").Action(planCmd.handleStatus)
	status.Flag("json", "Show raw JSON response instead of user-friendly tree").BoolVar(&planCmd.RawJSON)
}
<commit_msg>[INFINITY-1912] Add dcos-commons cli support for update hash merge semantics (#1310)<commit_after>package commands

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

	"github.com/mesosphere/dcos-commons/cli/client"
	"github.com/mesosphere/dcos-commons/cli/config"
	"gopkg.in/alecthomas/kingpin.v3-unstable"
)

type describeHandler struct{}

type describeRequest struct {
	AppID string `json:"appId"`
}

func checkError(err error, responseBytes []byte) {
	if err != nil {
		reportErrorAndExit(err, responseBytes)
	}
}

func reportErrorAndExit(err error, responseBytes []byte) {
	client.PrintMessage("Failed to unmarshal response. Error: %s", err)
	client.PrintMessage("Original data follows:")
	client.PrintMessageAndExit(string(responseBytes))
}

func describe() {
	// TODO: figure out KingPin's error handling
	requestContent, err := json.Marshal(describeRequest{config.ServiceName})
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	responseBytes, err := client.HTTPCosmosPostJSON("describe", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	// This attempts to retrieve resolvedOptions from the response. This field is only provided by
	// Cosmos running on Enterprise DC/OS 1.10 clusters or later.
	resolvedOptionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "resolvedOptions")
	checkError(err, responseBytes)
	if resolvedOptionsBytes != nil {
		client.PrintJSONBytes(resolvedOptionsBytes)
	} else {
		client.PrintMessage("Package configuration is not available for service %s.", config.ServiceName)
		client.PrintMessage("dcos %s %s is only available for packages installed with Enterprise DC/OS 1.10 or newer.", config.ModuleName, config.Command)
	}
}

func (cmd *describeHandler) handleDescribe(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	describe()
	return nil
}

// HandleDescribe adds the describe subcommand to the passed in kingpin.Application.
func HandleDescribe(app *kingpin.Application) {
	cmd := &describeHandler{}
	app.Command("describe", "View the package configuration for this DC/OS service").Action(cmd.handleDescribe)
}

type updateHandler struct {
	UpdateName     string
	OptionsFile    string
	PackageVersion string
	ViewStatus     bool
}

type updateRequest struct {
	AppID          string                 `json:"appId"`
	PackageVersion string                 `json:"packageVersion,omitempty"`
	OptionsJSON    map[string]interface{} `json:"options,omitempty"`
	Replace        bool                   `json:"replace"`
}

func printPackageVersions() {
	// TODO: figure out KingPin's error handling
	requestContent, _ := json.Marshal(describeRequest{config.ServiceName})
	responseBytes, err := client.HTTPCosmosPostJSON("describe", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	packageBytes, err := client.GetValueFromJSONResponse(responseBytes, "package")
	checkError(err, responseBytes)
	currentVersionBytes, err := client.GetValueFromJSONResponse(packageBytes, "version")
	checkError(err, responseBytes)
	downgradeVersionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "downgradesTo")
	checkError(err, responseBytes)
	downgradeVersions, err := client.JSONBytesToArray(downgradeVersionsBytes)
	checkError(err, responseBytes)
	upgradeVersionsBytes, err := client.GetValueFromJSONResponse(responseBytes, "upgradesTo")
	checkError(err, responseBytes)
	updateVersions, err := client.JSONBytesToArray(upgradeVersionsBytes)
	checkError(err, responseBytes)
	client.PrintMessage("Current package version is: %s", currentVersionBytes)
	if len(downgradeVersions) > 0 {
		client.PrintMessage("Package can be downgraded to: %s", client.PrettyPrintSlice(downgradeVersions))
	} else {
		client.PrintMessage("No valid package downgrade versions.")
	}
	if len(updateVersions) > 0 {
		client.PrintMessage("Package can be upgraded to: %s", client.PrettyPrintSlice(updateVersions))
	} else {
		client.PrintMessage("No valid package upgrade versions.")
	}
}

func (cmd *updateHandler) ViewPackageVersions(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	printPackageVersions()
	return nil
}

func readJSONFile(filename string) (map[string]interface{}, error) {
	fileBytes, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return client.UnmarshalJSON(fileBytes)
}

func parseUpdateResponse(responseBytes []byte) (string, error) {
	responseJSON, err := client.UnmarshalJSON(responseBytes)
	if err != nil {
		return "", err
	}
	return string(responseJSON["marathonDeploymentId"].(string)), nil
}

func doUpdate(optionsFile, packageVersion string) {
	// TODO: figure out KingPin's error handling
	request := updateRequest{AppID: config.ServiceName, Replace: false}
	if len(packageVersion) == 0 && len(optionsFile) == 0 {
		client.PrintMessage("Either --options and/or --package-version must be specified. See --help.")
		return
	}
	if len(packageVersion) > 0 {
		request.PackageVersion = packageVersion
	}
	if len(optionsFile) > 0 {
		fileBytes, err := ioutil.ReadFile(optionsFile)
		if err != nil {
			client.PrintMessage("Failed to load specified options file %s: %s", optionsFile, err)
			return
		}
		optionsJSON, err := client.UnmarshalJSON(fileBytes)
		if err != nil {
			client.PrintMessage("Failed to parse JSON in specified options file %s: %s", optionsFile, err)
			return
		}
		request.OptionsJSON = optionsJSON
	}
	requestContent, _ := json.Marshal(request)
	responseBytes, err := client.HTTPCosmosPostJSON("update", string(requestContent))
	if err != nil {
		client.PrintMessageAndExit(err.Error())
	}
	_, err = client.UnmarshalJSON(responseBytes)
	checkError(err, responseBytes)
	client.PrintMessage(fmt.Sprintf("Update started. Please use `dcos %s --name=%s update status` to view progress.", config.ModuleName, config.ServiceName))
}

func (cmd *updateHandler) UpdateConfiguration(a *kingpin.Application, e *kingpin.ParseElement, c *kingpin.ParseContext) error {
	config.Command = c.SelectedCommand.FullCommand()
	doUpdate(cmd.OptionsFile, cmd.PackageVersion)
	return nil
}

// HandleUpdateSection adds the update subcommand to the passed in kingpin.Application.
func HandleUpdateSection(app *kingpin.Application) {
	cmd := &updateHandler{}
	update := app.Command("update", "Updates the package version or configuration for this DC/OS service")

	start := update.Command("start", "Launches an update operation").Action(cmd.UpdateConfiguration)
	start.Flag("options", "Path to a JSON file that contains customized package installation options").StringVar(&cmd.OptionsFile)
	start.Flag("package-version", "The desired package version").StringVar(&cmd.PackageVersion)

	planCmd := &planHandler{}

	forceComplete := update.Command("force-complete", "Force complete a specific step in the provided phase").Alias("force").Action(planCmd.handleForceComplete)
	forceComplete.Arg("phase", "Name or UUID of the phase containing the provided step").Required().StringVar(&planCmd.Phase)
	forceComplete.Arg("step", "Name or UUID of step to be restarted").Required().StringVar(&planCmd.Step)

	forceRestart := update.Command("force-restart", "Restart update plan, or specific step in the provided phase").Alias("restart").Action(planCmd.handleForceRestart)
	forceRestart.Arg("phase", "Name or UUID of the phase containing the provided step").StringVar(&planCmd.Phase)
	forceRestart.Arg("step", "Name or UUID of step to be restarted").StringVar(&planCmd.Step)

	update.Command("package-versions", "View a list of available package versions to downgrade or upgrade to").Action(cmd.ViewPackageVersions)

	update.Command("pause", "Pause update plan").Alias("interrupt").Action(planCmd.handlePause)

	update.Command("resume", "Resume update plan").Alias("continue").Action(planCmd.handleResume)

	status := update.Command("status", "View status of a running update").Alias("show").Action(planCmd.handleStatus)
	status.Flag("json", "Show raw JSON response instead of user-friendly tree").BoolVar(&planCmd.RawJSON)
}
<|endoftext|>


<commit_before>package main

import "net/http"
import "log"
import "io"
import "os"
import "time"
import "encoding/json"
import "strconv"
import "io/ioutil"
import "flag"
import "encoding/binary"
import "net/url"

const (
	secret_token_file string = "secret_token"
	msg_offset_file   string = "msg_offset"
)

func readSecreteToken() string {
	token, err := ioutil.ReadFile(secret_token_file)
	if err != nil {
		panic(err)
	}

	return string(token)
}

func readInt64File(fname string) int64 {
	f, err := os.OpenFile(fname, os.O_CREATE|os.O_RDONLY, 0600)
	if err != nil {
		log.Fatal("os.OpenFile", err)
	}
	var offset int64
	// ignore error offset = 0
	binary.Read(f, binary.LittleEndian, &offset)
	f.Close()
	return offset
}

func writeInt64File(fname string, offset int64) {
	f, err := os.OpenFile(fname, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		log.Fatal("os.OpenFile", err)
	}
	// ignore irrelevant error
	binary.Write(f, binary.LittleEndian, offset)
	f.Close()
}

var apiSecretToken = readSecreteToken()

func makeApiUrl(cmd string, args url.Values) string {
	u := url.URL{}
	u.Scheme = "https"
	u.Host = "api.telegram.org"
	u.Path = "bot" + apiSecretToken + "/" + cmd
	u.RawQuery = args.Encode()
	return u.String()
}

func main() {
	var dumpFlag = flag.Bool("d", false, "dump json response into file json_dump")
	flag.Parse()

	var dumpFile *os.File
	var err error
	if *dumpFlag {
		dumpFile, err = os.OpenFile("json_dump", os.O_CREATE|os.O_RDWR, 0600)
		if err != nil {
			log.Fatal("os.OpenFile", err)
		}
	}
	defer dumpFile.Close()

	var msg_offset int64 = readInt64File(msg_offset_file)

	for {
		reqestArgs := url.Values{}
		reqestArgs.Add("offset", strconv.FormatInt(msg_offset, 10))
		resp, err := http.Get(makeApiUrl("getUpdates", reqestArgs))
		if err != nil {
			log.Fatal("http.Get", err)
		}

		var respReader io.Reader
		if *dumpFlag {
			respReader = io.TeeReader(resp.Body, dumpFile)
		} else {
			respReader = resp.Body
		}

		updates, err := UnmarshalResponse(respReader)
		if err != nil {
			log.Fatal("UnmarshalResponse", err)
		}

		for _, u := range updates {
			log.Printf("%#v\n", u)
			msg_offset = int64(u.update_id) + 1

			reqestArgs = url.Values{}
			reqestArgs.Add("chat_id", strconv.FormatInt(u.chat_id, 10))
			reqestArgs.Add("text", "This bot just do nothing for you.\n"+
				"Simply send anything you want and read this message =)\n\n"+
				"You can use any command to get results, it doesn't help ^_____^")
			_, err := http.Get(makeApiUrl("sendMessage", reqestArgs))
			if err != nil {
				log.Fatal("http.Get", err)
			}
			writeInt64File(msg_offset_file, msg_offset)
		}

		resp.Body.Close()
		time.Sleep(10 * time.Second)
	}
}

type UpdatePayload struct {
	update_id int64
	chat_id   int64
	text      string
}

func UnmarshalResponse(respReader io.Reader) ([]UpdatePayload, error) {
	dec := json.NewDecoder(respReader)
	var resp map[string]interface{}
	var err error
	var updates []UpdatePayload
	for {
		err = dec.Decode(&resp)
		if err != nil {
			if err == io.EOF {
				err = nil
				break
			}
			return nil, err
		}

		for _, u := range resp["result"].([]interface{}) {
			updates = append(updates, UpdatePayload{})
			updates[len(updates)-1].update_id = int64(u.(map[string]interface{})["update_id"].(float64))
			updates[len(updates)-1].text = u.(map[string]interface{})["message"].(map[string]interface{})["text"].(string)
			updates[len(updates)-1].chat_id = int64(u.(map[string]interface{})["message"].(map[string]interface{})["chat"].(map[string]interface{})["id"].(float64))
		}
	}
	return updates, err
}
<commit_msg>simplify logger flags to filename and line<commit_after>package main

import "net/http"
import "log"
import "io"
import "os"
import "time"
import "encoding/json"
import "strconv"
import "io/ioutil"
import "flag"
import "encoding/binary"
import "net/url"

const (
	secret_token_file string = "secret_token"
	msg_offset_file   string = "msg_offset"
)

func readSecreteToken() string {
	token, err := ioutil.ReadFile(secret_token_file)
	if err != nil {
		panic(err)
	}

	return string(token)
}

func readInt64File(fname string) int64 {
	f, err := os.OpenFile(fname, os.O_CREATE|os.O_RDONLY, 0600)
	if err != nil {
		log.Fatal("os.OpenFile", err)
	}
	var offset int64
	// ignore error offset = 0
	binary.Read(f, binary.LittleEndian, &offset)
	f.Close()
	return offset
}

func writeInt64File(fname string, offset int64) {
	f, err := os.OpenFile(fname, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		log.Fatal("os.OpenFile", err)
	}
	// ignore irrelevant error
	binary.Write(f, binary.LittleEndian, offset)
	f.Close()
}

var apiSecretToken = readSecreteToken()

func makeApiUrl(cmd string, args url.Values) string {
	u := url.URL{}
	u.Scheme = "https"
	u.Host = "api.telegram.org"
	u.Path = "bot" + apiSecretToken + "/" + cmd
	u.RawQuery = args.Encode()
	return u.String()
}

func main() {
	// config standard logger
	log.SetFlags(log.Lshortfile)

	var dumpFlag = flag.Bool("d", false, "dump json response into file json_dump")
	flag.Parse()

	var dumpFile *os.File
	var err error
	if *dumpFlag {
		dumpFile, err = os.OpenFile("json_dump", os.O_CREATE|os.O_RDWR, 0600)
		if err != nil {
			log.Fatal("os.OpenFile", err)
		}
	}
	defer dumpFile.Close()

	var msg_offset int64 = readInt64File(msg_offset_file)

	for {
		reqestArgs := url.Values{}
		reqestArgs.Add("offset", strconv.FormatInt(msg_offset, 10))
		resp, err := http.Get(makeApiUrl("getUpdates", reqestArgs))
		if err != nil {
			log.Fatal("http.Get", err)
		}

		var respReader io.Reader
		if *dumpFlag {
			respReader = io.TeeReader(resp.Body, dumpFile)
		} else {
			respReader = resp.Body
		}

		updates, err := UnmarshalResponse(respReader)
		if err != nil {
			log.Fatal("UnmarshalResponse", err)
		}

		for _, u := range updates {
			log.Printf("%#v\n", u)
			msg_offset = int64(u.update_id) + 1

			reqestArgs = url.Values{}
			reqestArgs.Add("chat_id", strconv.FormatInt(u.chat_id, 10))
			reqestArgs.Add("text", "This bot just do nothing for you.\n"+
				"Simply send anything you want and read this message =)\n\n"+
				"You can use any command to get results, it doesn't help ^_____^")
			_, err := http.Get(makeApiUrl("sendMessage", reqestArgs))
			if err != nil {
				log.Fatal("http.Get", err)
			}
			writeInt64File(msg_offset_file, msg_offset)
		}

		resp.Body.Close()
		time.Sleep(10 * time.Second)
	}
}

type UpdatePayload struct {
	update_id int64
	chat_id   int64
	text      string
}

func UnmarshalResponse(respReader io.Reader) ([]UpdatePayload, error) {
	dec := json.NewDecoder(respReader)
	var resp map[string]interface{}
	var err error
	var updates []UpdatePayload
	for {
		err = dec.Decode(&resp)
		if err != nil {
			if err == io.EOF {
				err = nil
				break
			}
			return nil, err
		}

		for _, u := range resp["result"].([]interface{}) {
			updates = append(updates, UpdatePayload{})
			updates[len(updates)-1].update_id = int64(u.(map[string]interface{})["update_id"].(float64))
			updates[len(updates)-1].text = u.(map[string]interface{})["message"].(map[string]interface{})["text"].(string)
			updates[len(updates)-1].chat_id = int64(u.(map[string]interface{})["message"].(map[string]interface{})["chat"].(map[string]interface{})["id"].(float64))
		}
	}
	return updates, err
}
<|endoftext|>


<commit_before>package semaphore

import (
	"log"

	api "github.com/armon/consul-api"
	lock "github.com/ryanschneider/consul-semaphore/lock"
)

// Semaphore represents a Consul-backed semaphore.
// Based off of the etcd semaphore used in CoreOS' Locksmith,
// Semaphore can be used to coordiate a set of workers around
// a Consul KV.  For exmple, restarting services in a consul-template
// action in a controlled manner.
type Semaphore struct {
	Path   string
	Holder string
	lock   *lock.Lock
}

// New creates and returns a new Semaphore.
func New(path string, holder string) (s *Semaphore, err error) {
	apiClient, err := api.NewClient(api.DefaultConfig())
	if err != nil {
		return nil, err
	}

	client, err := lock.NewConsulLockClient(apiClient)
	if err != nil {
		return nil, err
	}

	lock, err := lock.New(path, holder, client)
	if err != nil {
		return nil, err
	}

	return &Semaphore{path, holder, lock}, nil
}

// SetMax sets the maximum number of concurrent holders of a Semaphore.
// If the max is raised, multiple Acquirers may be signalled.  If the max is
// lowered below the current number of holders, no one will be signalled until
// the number of holders drops below max.
func (s *Semaphore) SetMax(max uint) (oldMax uint, err error) {
	_, iOldMax, err := s.lock.SetMax(int(max))
	if err != nil {
		return 0, err
	}

	oldMax = uint(iOldMax)
	return oldMax, nil
}

// Acquire acquires a portion of the Semaphore, optionally waiting if the
// Semaphore is currently maxed out.
func (s *Semaphore) Acquire(wait bool) (err error) {
	for {
		err = s.lock.Lock()
		if err == nil {
			return nil
		}

		// only go again if we are waiting
		if !wait {
			return err
		}

		_, isExhausted := err.(lock.SemaphoreExhaustedErr)
		casFailed := (err == lock.CheckAndSetFailedErr)

		switch {
		case isExhausted:
			log.Printf("Exhausted, trying again")
		case casFailed:
			log.Printf("CAS failed, trying again")
		default:
			return err
		}

		changed, err := s.lock.Watch()
		if err != nil {
			return err
		}

		log.Printf("Watch woke up, changed: %v", changed)
		if changed {
			// TODO: add some random sleep here to avoid
			// too many CAS errors on thundering herd
			continue
		}
	}

	return err
}

// Releases releases a portion of the Semaphore.
// Releasing allows waiting Acquirers to be signalled.
// Note: In a highly contentious Semaphore, there may be CheckAndSet (CAS)
// errors writing to the semaphore.  These are handled inside Release, which
// may lead to Release blocking while it attempts to cleanly write to the KV.
func (s *Semaphore) Release() (err error) {
	for {
		err = s.lock.Unlock()
		if err == lock.CheckAndSetFailedErr {
			// TODO: add some sleep here to avoid
			// too many CAS errors on thundering herd
			continue
		}
		return
	}
}
<commit_msg>Backoff up to 1s to prevent thundering on the CAS Operations.<commit_after>package semaphore

import (
	"log"
	"math/rand"
	"time"

	api "github.com/armon/consul-api"
	lock "github.com/ryanschneider/consul-semaphore/lock"
)

// Semaphore represents a Consul-backed semaphore.
// Based off of the etcd semaphore used in CoreOS' Locksmith,
// Semaphore can be used to coordiate a set of workers around
// a Consul KV.  For exmple, restarting services in a consul-template
// action in a controlled manner.
type Semaphore struct {
	Path   string
	Holder string
	lock   *lock.Lock
}

// New creates and returns a new Semaphore.
func New(path string, holder string) (s *Semaphore, err error) {
	apiClient, err := api.NewClient(api.DefaultConfig())
	if err != nil {
		return nil, err
	}

	client, err := lock.NewConsulLockClient(apiClient)
	if err != nil {
		return nil, err
	}

	lock, err := lock.New(path, holder, client)
	if err != nil {
		return nil, err
	}

	return &Semaphore{path, holder, lock}, nil
}

// SetMax sets the maximum number of concurrent holders of a Semaphore.
// If the max is raised, multiple Acquirers may be signalled.  If the max is
// lowered below the current number of holders, no one will be signalled until
// the number of holders drops below max.
func (s *Semaphore) SetMax(max uint) (oldMax uint, err error) {
	_, iOldMax, err := s.lock.SetMax(int(max))
	if err != nil {
		return 0, err
	}

	oldMax = uint(iOldMax)
	return oldMax, nil
}

// Acquire acquires a portion of the Semaphore, optionally waiting if the
// Semaphore is currently maxed out.
func (s *Semaphore) Acquire(wait bool) (err error) {
	for {
		log.Printf("Holder %v: acquiring..", s.Holder)
		err = s.lock.Lock()
		if err == nil {
			return nil
		}

		// only go again if we are waiting
		if !wait {
			return err
		}

		_, isExhausted := err.(lock.SemaphoreExhaustedErr)
		casFailed := (err == lock.CheckAndSetFailedErr)

		switch {
		case isExhausted:
			log.Printf("Holder %v: Semaphore exhausted, trying again", s.Holder)
		case casFailed:
			log.Printf("Holder %v: CheckAndSet failed, trying again", s.Holder)
		default:
			return err
		}

		changed, err := s.lock.Watch()
		if err != nil {
			return err
		}

		log.Printf("Holder %v: Watch woke up, changed: %v", s.Holder, changed)
		if changed {
			// Sleep here to avoid too many CAS errors on thundering herd
			r := rand.New(rand.NewSource(time.Now().UnixNano()))
			time.Sleep(time.Duration(r.Intn(1000)) * time.Millisecond)
			continue
		}
	}

	return err
}

// Releases releases a portion of the Semaphore.
// Releasing allows waiting Acquirers to be signalled.
// Note: In a highly contentious Semaphore, there may be CheckAndSet (CAS)
// errors writing to the semaphore.  These are handled inside Release, which
// may lead to Release blocking while it attempts to cleanly write to the KV.
func (s *Semaphore) Release() (err error) {
	for {
		err = s.lock.Unlock()
		if err == lock.CheckAndSetFailedErr {
			// Sleep here to avoid too many CAS errors on thundering herd
			r := rand.New(rand.NewSource(time.Now().UnixNano()))
			time.Sleep(time.Duration(r.Intn(1000)) * time.Millisecond)
			continue
		}
		return
	}
}
<|endoftext|>


<commit_before>package cors

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/volatile/core"
)

const (
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	headerRequestHeaders = "Access-Control-Request-Headers"
	headerRequestMethod  = "Access-Control-Request-Method"
)

// Options represents access control options.
type Options struct {
	AllowedHeaders     []string      // AllowedHeaders indicates, in the case of a preflight request, which headers can be used during the actual request.
	AllowedMethods     []string      // AllowedMethods indicates, in the case of a preflight request, which methods can be used during the actual request.
	AllowedOrigins     []string      // AllowedOrigins indicates which origins can make a request.
	CredentialsAllowed bool          // CredentialsAllowed indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.
	ExposedHeaders     []string      // ExposedHeaders whitelists headers that browsers are allowed to access.
	MaxAge             time.Duration // MaxAge indicates how long the results of a preflight request can be cached.
}

type formattedOptions struct {
	AllowedHeaders     string
	AllowedMethods     string
	AllowedOrigins     string
	CredentialsAllowed string
	ExposedHeaders     string
	MaxAge             string
}

// Use tells the core to use this handler with the provided options.
func Use(options *Options) {
	fmtOpt := formatCORS(options)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOpt, func() {
			c.Next()
		})
	})
}

// LocalUse allows to set CORS locally, for a single handler.
// Remember you can't set headers after Write or WriteHeader has been called.
func LocalUse(c *core.Context, options *Options, handler func()) {
	setCORS(c, formatCORS(options), handler)
}

func formatCORS(opt *Options) *formattedOptions {
	if opt == nil {
		opt = new(Options)
	}

	fmtOpt := &formattedOptions{
		AllowedHeaders:     strings.Join(opt.AllowedHeaders, ", "),
		AllowedMethods:     strings.Join(opt.AllowedMethods, ", "),
		CredentialsAllowed: strconv.FormatBool(opt.CredentialsAllowed),
		ExposedHeaders:     strings.Join(opt.ExposedHeaders, ", "),
		MaxAge:             fmt.Sprintf("%.f", opt.MaxAge.Seconds()),
	}

	// If no allowed origins set, all origins are allowed.
	if opt.AllowedOrigins == nil {
		fmtOpt.AllowedOrigins = "*"
	} else {
		fmtOpt.AllowedOrigins = strings.Join(opt.AllowedOrigins, ", ")
	}

	return fmtOpt
}

// setCORS set the response headers and continue if it's not a preflight request.
func setCORS(c *core.Context, fmtOpt *formattedOptions, handler func()) {
	c.ResponseWriter.Header().Set(headerAllowOrigin, fmtOpt.AllowedOrigins)
	if fmtOpt.AllowedOrigins != "*" {
		c.ResponseWriter.Header().Set("Vary", "Origin")
	}

	if fmtOpt.CredentialsAllowed == "true" {
		c.ResponseWriter.Header().Set(headerAllowCredentials, fmtOpt.CredentialsAllowed)
	}

	if fmtOpt.ExposedHeaders != "" {
		c.ResponseWriter.Header().Set(headerExposeHeaders, fmtOpt.ExposedHeaders)
	}

	if fmtOpt.MaxAge != "0" {
		c.ResponseWriter.Header().Set(headerMaxAge, fmtOpt.MaxAge)
	}

	// OPTIONS is used for preflight requests.
	// For that, only the CORS handler must respond, so the handlers chain is broken.
	if c.Request.Method == "OPTIONS" {
		// If no allowed headers are set, accept all from the real request.
		if fmtOpt.AllowedHeaders == "" {
			c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
		} else {
			c.ResponseWriter.Header().Set(headerAllowHeaders, fmtOpt.AllowedHeaders)
		}

		// If no allowed methods are set, accept the method of the real request.
		if fmtOpt.AllowedMethods == "" {
			c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
		} else {
			c.ResponseWriter.Header().Set(headerAllowMethods, fmtOpt.AllowedMethods)
		}

		c.ResponseWriter.WriteHeader(http.StatusOK)
	} else {
		handler()
	}
}
<commit_msg>Rewrite CORS<commit_after>package cors

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/volatile/core"
)

const (
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	headerRequestHeaders = "Access-Control-Request-Headers"
	headerRequestMethod  = "Access-Control-Request-Method"
)

// Options represents access control options for an origin.
type Options struct {
	// AllowedHeaders indicates, in the case of a preflight request,
	// which headers can be used during the actual request.
	AllowedHeaders []string

	// AllowedMethods indicates, in the case of a preflight request,
	// which methods can be used during the actual request.
	//
	// If len(ExposedHeaders) == 0, all methods will be allowed.
	AllowedMethods []string

	// CredentialsAllowed indicates whether the request can include user
	// credentials like cookies, HTTP authentication or client side SSL certificates.
	CredentialsAllowed bool

	// ExposedHeaders whitelists headers that browsers are allowed to access.
	//
	// If len(ExposedHeaders) == 0, all headers on preflight requests will be exposed.
	ExposedHeaders []string

	// MaxAge indicates how long the results of a preflight request can be cached.
	MaxAge time.Duration
}

type formattedOptions struct {
	AllowedHeaders,
	AllowedMethods,
	CredentialsAllowed,
	ExposedHeaders,
	MaxAge *string
}

// Use tells the core to use this handler with the provided options.
func Use(options map[string]Options) {
	fmtOpt := formatCORS(options)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOpt, c.Next)
	})
}

// LocalUse allows to set CORS locally, for a single handler.
// Remember you can't set headers after Write or WriteHeader has been called.
func LocalUse(c *core.Context, options map[string]Options, handler func()) {
	setCORS(c, formatCORS(options), handler)
}

func formatCORS(opt map[string]Options) (fmtOpt map[string]formattedOptions) {
	// For requests without credentials, the server may specify "*" as a wildcard,
	// thereby allowing any origin to access the resource.
	if wild, ok := opt["*"]; ok && wild.CredentialsAllowed {
		panic("sending credentials via CORS is not permitted for wildcarded origins")
	}

	fmtOpt = make(map[string]formattedOptions, len(opt))

	for origin, item := range opt {
		result := formattedOptions{}
		if len(item.AllowedHeaders) > 0 {
			*result.AllowedHeaders = strings.Join(item.AllowedHeaders, ", ")
		}
		if len(item.AllowedMethods) > 0 {
			*result.AllowedMethods = strings.Join(item.AllowedMethods, ", ")
		}
		if item.CredentialsAllowed {
			*result.CredentialsAllowed = strconv.FormatBool(item.CredentialsAllowed)
		}
		if len(item.ExposedHeaders) > 0 {
			*result.ExposedHeaders = strings.Join(item.ExposedHeaders, ", ")
		}
		if item.MaxAge.Seconds() > 0.5 {
			*result.MaxAge = fmt.Sprintf("%.f", item.MaxAge.Seconds())
		}

		fmtOpt[origin] = result
	}

	return
}

// setCORS set the response headers and continue if it's not a preflight request.
func setCORS(c *core.Context, fmtOpts map[string]formattedOptions, handler func()) {
	origin := c.Request.Header.Get("Origin")

	// Request does not have an Origin header, therefore it is no CORS request
	// and control is passed to the handler.
	if origin == "" {
		handler()
		return
	}

	fmtOpt, ok := fmtOpts[origin]
	wildcard = false

	// Source origin is not known by name, so check for wildcard.
	if !ok {
		fmtOpt, wildcard = fmtOpts["*"]
	}

	// No origin matched and wildcard not accepted, reject the request.
	if !ok && !wildcard {
		http.Error(c.ResponseWriter, "Invalid CORS request", http.StatusForbidden)
		return
	}

	c.ResponseWriter.Header().Set(headerAllowOrigin, origin)

	if wildcard {
		c.ResponseWriter.Header().Set("Vary", "Origin")
	}

	if fmtOpt.CredentialsAllowed != nil {
		c.ResponseWriter.Header().Set(headerAllowCredentials, *fmtOpt.CredentialsAllowed)
	}

	if fmtOpt.ExposedHeaders != nil {
		c.ResponseWriter.Header().Set(headerExposeHeaders, *fmtOpt.ExposedHeaders)
	}

	if fmtOpt.MaxAge != nil {
		c.ResponseWriter.Header().Set(headerMaxAge, *fmtOpt.MaxAge)
	}

	if c.Request.Method != "OPTIONS" {
		handler()
		return
	}

	// OPTIONS is used for preflight requests.
	// For that, only the CORS handler must respond, so the handlers chain is broken.

	// If no allowed headers are set, accept all from the real request.
	if fmtOpt.AllowedHeaders == nil {
		c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
	} else {
		c.ResponseWriter.Header().Set(headerAllowHeaders, *fmtOpt.AllowedHeaders)
	}

	// If no allowed methods are set, accept the method of the real request.
	if fmtOpt.AllowedMethods == nil {
		c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
	} else {
		c.ResponseWriter.Header().Set(headerAllowMethods, *fmtOpt.AllowedMethods)
	}

	c.ResponseWriter.WriteHeader(http.StatusOK)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"unicode"

	"github.com/gorilla/mux"
)

const (
	linkSentMsg     = "An account verification email has been sent."
	subscribedMsg   = "Your account is now active!"
	scoreUpdatedMsg = "Your settings have been successfully updated!"
	unsubscribedMsg = "You have been successfully unsubscribed."

	minScoreNoKeywords = 200
)

var (
	errInvalidEmail    = errors.New("Error: The email address is not valid!")
	errInvalidScore    = errors.New("Error: The score field must be a number!")
	errInvalidLink     = errors.New("Error: The link is not valid.")
	errInvalidKeywords = errors.New("Error: Invalid keywords. Keywords must be space-separated, alphanumeric strings")
	errNotFound        = errors.New("Error: The email address you provided is not subscribed to this service!")
	errMinScore        = errors.New("Error: You must either add some keywords or select a minimum score of 200 points!")
)

// errInternal represents an internal server error.
type errInternal struct{ error }

// errMessage represents a meaningful error message, that will be sent to the user.
type errMessage struct{ error }

// Context carries http session information. It will be passed to all HTTP handlers.
// TODO: Include user information, simplifying authentication management.
type Context struct {
	db *Database
}

// newContext creates a new Context, ready to be passed to a HTTP handler.
func newContext() *Context {
	return &Context{
		db: newDatabase(),
	}
}

// handler wraps a custom handler function returning a standard HandlerFunc closure.
func handler(f func(ctx *Context, w http.ResponseWriter, r *http.Request) error) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		Logger.Printf("%s %s %s\n", r.Method, r.URL.Path, r.URL.RawQuery)
		ctx := newContext()
		defer ctx.db.close()

		err := f(ctx, w, r)

		if err == nil {
			return
		}

		// Log the error, and depending on the type, display it to the user.
		Logger.Println(err)
		switch err.(type) {
		case errMessage:
			w.WriteHeader(http.StatusBadRequest)
			writeMessage(err.Error(), w)
		case errInternal:
		default:
			w.WriteHeader(http.StatusInternalServerError)
			writeMessage("Oops! An error ocurred.", w)
		}
	}
}

// setupHandlers registers the HTTP handlers of the app.
func setupHandlers() {
	router := mux.NewRouter()
	router.HandleFunc("/subscribe", handler(SubscribeHandler)).
		Methods("POST")
	router.HandleFunc("/activate", handler(ActivateHandler)).
		Methods("GET")
	router.HandleFunc("/unsubscribe", handler(UnsubscribeHandler)).
		Methods("GET", "POST")

	// serve settings.html static file. index.html works the same way,
	// though it's automatically handled by the root file server handler.
	router.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "./public/settings.html")
	})

	router.PathPrefix("/").Handler(http.FileServer(http.Dir("./public/")))
	http.Handle("/", router)
}

// SubscribeHandler is the HTTP handler for managing new subscriptions; It handles '/subscribe'.
func SubscribeHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	email, ok := parseEmail(r)
	if !ok {
		return errMessage{errInvalidEmail}
	}
	keywords, ok := parseKeywords(r)
	if !ok {
		return errMessage{errInvalidKeywords}
	}
	score, ok := parseScore(r)
	if !ok {
		return errMessage{errInvalidScore}
	} else if len(keywords) == 0 && score < minScoreNoKeywords {
		return errMessage{errMinScore}
	}
	Logger.Printf("Settings -> Score:%d, Keywords:%v\n", score, keywords)

	q := url.Values{} // Link query parameters.
	u, ok := ctx.db.findUser(email)
	if ok {
		// The user already exists. Settings will be added to the query.
		q.Set("score", strconv.Itoa(score))
		q.Set("keywords", strings.Join(keywords, " ")) // FIXME: Should we just forward whatever we got in the initial request?
		u.Token = newToken()                           // reset user token.
		if err := ctx.db.updateToken(u.Id, u.Token); err != nil {
			return errInternal{err}
		}
	} else {
		u = newUser(email, score, keywords)
		if err := ctx.db.upsertUser(u); err != nil {
			return errInternal{err}
		}
	}

	q.Set("email", u.Email)
	q.Set("token", u.Token)
	link := config.Url + "/activate?" + q.Encode()
	go sendVerification(email, link)

	return writeMessage(linkSentMsg, w)
}

// ActivateHandler is the HTTP handler for managing account activations; It handles '/activate'.
// On registered users, it also handles setting updates.
func ActivateHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	email, token := r.FormValue("email"), r.FormValue("token")

	// Attempt to read score and keywords preferences, in case of setting updates.
	score, sOK := parseScore(r)
	keywords, kOK := parseKeywords(r)
	if sOK && kOK {
		// Update settings
		if ctx.db.updateUser(email, token, score, keywords) {
			return writeMessage(scoreUpdatedMsg, w)
		}
		return errMessage{errInvalidLink}
	}

	if ctx.db.activate(email, token) {
		return writeMessage(subscribedMsg, w)
	}
	return errMessage{errInvalidLink}
}

// ActivateHandler is the HTTP handler for managing account unsubscriptions; It handles '/unsubscribe'.
func UnsubscribeHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	switch r.Method {
	case "POST":
		email, ok := parseEmail(r)
		u, found := ctx.db.findUser(email)
		if !ok || !found {
			return errMessage{errNotFound}
		}

		u.Token = newToken() // reset user token.
		if err := ctx.db.updateToken(u.Id, u.Token); err != nil {
			return errInternal{err}
		}

		q := url.Values{}
		q.Set("email", u.Email)
		q.Set("token", u.Token)
		link := config.Url + "/unsubscribe?" + q.Encode()
		go sendUnsubscription(email, link)

		return writeMessage(linkSentMsg, w)
	case "GET":
		email, token := r.FormValue("email"), r.FormValue("token")
		if ctx.db.unsubscribe(email, token) {
			return writeMessage(unsubscribedMsg, w)
		}
		return errMessage{errInvalidLink}
	}
	return nil
}

// writeMessage renders a message in the default 'info' template.
func writeMessage(msg string, w http.ResponseWriter) error {
	return useTemplate("info", msg, w)
}

// parseEmail gets the email attribute from the request.
func parseEmail(r *http.Request) (string, bool) {
	email := r.FormValue("email")
	return email, validateAddress(email)
}

// parseScore gets the score attribute from the request.
func parseScore(r *http.Request) (int, bool) {
	score, err := strconv.Atoi(r.FormValue("score"))
	return score, err == nil
}

// parseKeywords reads the keywords attribute from the request.
func parseKeywords(r *http.Request) ([]string, bool) {
	text := r.FormValue("keywords")
	for _, r := range text {
		if !unicode.IsLetter(r) && !unicode.IsNumber(r) && !unicode.IsSpace(r) {
			return nil, false
		}
	}
	return Keywords(text), true
}
<commit_msg>Fix typos<commit_after>package main

import (
	"errors"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"unicode"

	"github.com/gorilla/mux"
)

const (
	linkSentMsg     = "An account verification email has been sent."
	subscribedMsg   = "Your account is now active!"
	scoreUpdatedMsg = "Your settings have been successfully updated!"
	unsubscribedMsg = "You have been successfully unsubscribed."

	minScoreNoKeywords = 200
)

var (
	errInvalidEmail    = errors.New("Error: The email address is not valid!")
	errInvalidScore    = errors.New("Error: The score field must be a number!")
	errInvalidLink     = errors.New("Error: The link is not valid.")
	errInvalidKeywords = errors.New("Error: Invalid keywords. Keywords must be space-separated, alphanumeric strings")
	errNotFound        = errors.New("Error: The email address you provided is not subscribed to this service!")
	errMinScore        = errors.New("Error: You must either add some keywords or select a minimum score of 200 points!")
)

// errInternal represents an internal server error.
type errInternal struct{ error }

// errMessage represents a meaningful error message, that will be sent to the user.
type errMessage struct{ error }

// Context carries http session information. It will be passed to all HTTP handlers.
// TODO: Include user information, simplifying authentication management.
type Context struct {
	db *Database
}

// newContext creates a new Context, ready to be passed to a HTTP handler.
func newContext() *Context {
	return &Context{
		db: newDatabase(),
	}
}

// handler wraps a custom handler function returning a standard HandlerFunc closure.
func handler(f func(ctx *Context, w http.ResponseWriter, r *http.Request) error) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		Logger.Printf("%s %s %s\n", r.Method, r.URL.Path, r.URL.RawQuery)
		ctx := newContext()
		defer ctx.db.close()

		err := f(ctx, w, r)

		if err == nil {
			return
		}

		// Log the error, and depending on the type, display it to the user.
		Logger.Println(err)
		switch err.(type) {
		case errMessage:
			w.WriteHeader(http.StatusBadRequest)
			writeMessage(err.Error(), w)
		case errInternal:
		default:
			w.WriteHeader(http.StatusInternalServerError)
			writeMessage("Oops! An error occurred.", w)
		}
	}
}

// setupHandlers registers the HTTP handlers of the app.
func setupHandlers() {
	router := mux.NewRouter()
	router.HandleFunc("/subscribe", handler(SubscribeHandler)).
		Methods("POST")
	router.HandleFunc("/activate", handler(ActivateHandler)).
		Methods("GET")
	router.HandleFunc("/unsubscribe", handler(UnsubscribeHandler)).
		Methods("GET", "POST")

	// serve settings.html static file. index.html works the same way,
	// though it's automatically handled by the root file server handler.
	router.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "./public/settings.html")
	})

	router.PathPrefix("/").Handler(http.FileServer(http.Dir("./public/")))
	http.Handle("/", router)
}

// SubscribeHandler is the HTTP handler for managing new subscriptions; It handles '/subscribe'.
func SubscribeHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	email, ok := parseEmail(r)
	if !ok {
		return errMessage{errInvalidEmail}
	}
	keywords, ok := parseKeywords(r)
	if !ok {
		return errMessage{errInvalidKeywords}
	}
	score, ok := parseScore(r)
	if !ok {
		return errMessage{errInvalidScore}
	} else if len(keywords) == 0 && score < minScoreNoKeywords {
		return errMessage{errMinScore}
	}
	Logger.Printf("Settings -> Score:%d, Keywords:%v\n", score, keywords)

	q := url.Values{} // Link query parameters.
	u, ok := ctx.db.findUser(email)
	if ok {
		// The user already exists. Settings will be added to the query.
		q.Set("score", strconv.Itoa(score))
		q.Set("keywords", strings.Join(keywords, " ")) // FIXME: Should we just forward whatever we got in the initial request?
		u.Token = newToken()                           // reset user token.
		if err := ctx.db.updateToken(u.Id, u.Token); err != nil {
			return errInternal{err}
		}
	} else {
		u = newUser(email, score, keywords)
		if err := ctx.db.upsertUser(u); err != nil {
			return errInternal{err}
		}
	}

	q.Set("email", u.Email)
	q.Set("token", u.Token)
	link := config.Url + "/activate?" + q.Encode()
	go sendVerification(email, link)

	return writeMessage(linkSentMsg, w)
}

// ActivateHandler is the HTTP handler for managing account activations; It handles '/activate'.
// On registered users, it also handles setting updates.
func ActivateHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	email, token := r.FormValue("email"), r.FormValue("token")

	// Attempt to read score and keywords preferences, in case of setting updates.
	score, sOK := parseScore(r)
	keywords, kOK := parseKeywords(r)
	if sOK && kOK {
		// Update settings
		if ctx.db.updateUser(email, token, score, keywords) {
			return writeMessage(scoreUpdatedMsg, w)
		}
		return errMessage{errInvalidLink}
	}

	if ctx.db.activate(email, token) {
		return writeMessage(subscribedMsg, w)
	}
	return errMessage{errInvalidLink}
}

// ActivateHandler is the HTTP handler for managing account unsubscriptions; It handles '/unsubscribe'.
func UnsubscribeHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	switch r.Method {
	case "POST":
		email, ok := parseEmail(r)
		u, found := ctx.db.findUser(email)
		if !ok || !found {
			return errMessage{errNotFound}
		}

		u.Token = newToken() // reset user token.
		if err := ctx.db.updateToken(u.Id, u.Token); err != nil {
			return errInternal{err}
		}

		q := url.Values{}
		q.Set("email", u.Email)
		q.Set("token", u.Token)
		link := config.Url + "/unsubscribe?" + q.Encode()
		go sendUnsubscription(email, link)

		return writeMessage(linkSentMsg, w)
	case "GET":
		email, token := r.FormValue("email"), r.FormValue("token")
		if ctx.db.unsubscribe(email, token) {
			return writeMessage(unsubscribedMsg, w)
		}
		return errMessage{errInvalidLink}
	}
	return nil
}

// writeMessage renders a message in the default 'info' template.
func writeMessage(msg string, w http.ResponseWriter) error {
	return useTemplate("info", msg, w)
}

// parseEmail gets the email attribute from the request.
func parseEmail(r *http.Request) (string, bool) {
	email := r.FormValue("email")
	return email, validateAddress(email)
}

// parseScore gets the score attribute from the request.
func parseScore(r *http.Request) (int, bool) {
	score, err := strconv.Atoi(r.FormValue("score"))
	return score, err == nil
}

// parseKeywords reads the keywords attribute from the request.
func parseKeywords(r *http.Request) ([]string, bool) {
	text := r.FormValue("keywords")
	for _, r := range text {
		if !unicode.IsLetter(r) && !unicode.IsNumber(r) && !unicode.IsSpace(r) {
			return nil, false
		}
	}
	return Keywords(text), true
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os/exec"
	"strings"
	"time"
)

type Reminder struct {
	*App
	DefaultTime string
	ReminderMsg string
	SuccessMsg  string
	ThenDay     string
	ThenTime    string
}

func CreateReminder(app *App) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		reminder := newReminder(app)
		switch r.Method {
		case "GET":
			renderTemplate(w, reminder)
			return
		case "POST":
			submitReminder(w, r, reminder)
			return
		}
	})
}

func newReminder(app *App) *Reminder {
	thirtyMinutesFromNow := time.Now().Add(time.Duration(30) * time.Minute)
	return &Reminder{
		App:         app,
		SuccessMsg:  "",
		DefaultTime: thirtyMinutesFromNow.Format("15:04"),
	}
}

func renderTemplate(w http.ResponseWriter, rem *Reminder) {
	templateFile := rem.DocumentRoot + "/" + rem.Path + "/create.html"
	t, err := template.ParseFiles(templateFile)
	die("error when rendering template: %v", err)
	w.WriteHeader(http.StatusOK)
	t.Execute(w, rem)
}

func submitReminder(w http.ResponseWriter, r *http.Request, rem *Reminder) {
	rem.ThenDay = r.FormValue("day")
	rem.ThenTime = r.FormValue("time")
	rem.ReminderMsg = r.FormValue("message")
	delay, err := timeToSleepFor(rem)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	go func(time.Duration, *Reminder) {
		select {
		case <-time.After(delay):
			execute(rem)
		}
	}(delay, rem)

	thenDateTime := time.Now().Add(delay).Format("2006-01-02 15:04:05-07:00")
	rem.SuccessMsg = "Thank you! Your reminder will be sent at " + thenDateTime
	renderTemplate(w, rem)
	log.Printf("The reminder '%v' will be sent at %v", rem.ReminderMsg, thenDateTime)
}

func timeToSleepFor(rem *Reminder) (time.Duration, error) {
	thenDate, err := thenDate(rem)
	if err != nil {
		return 0, err
	}
	return thenDate.Sub(time.Now()), nil
}

func thenDate(rem *Reminder) (time.Time, error) {
	cmd := exec.Command("date", "--rfc-3339=seconds", "--date",
		rem.ThenDay+" "+rem.ThenTime)
	var cmdResult bytes.Buffer
	cmd.Stdout = &cmdResult
	err := cmd.Run()
	if err != nil {
		log.Printf("error: %v", cmd)
		return time.Now(), err
	}
	then, err := time.Parse("2006-01-02 15:04:05-07:00",
		strings.TrimSuffix(cmdResult.String(), "\n"))
	if err != nil {
		return time.Now(), err
	}
	return then, nil
}

func execute(rem *Reminder) {
	resp, err := http.PostForm(rem.NotificationApi,
		url.Values{"token": {rem.ApiToken},
			"user":    {rem.ApiUser},
			"message": {rem.ReminderMsg}})
	die("error when using Notification API: %v", err)
	defer resp.Body.Close()
	if resp.Status != "200 OK" {
		die("error when using Notification API: %v",
			errors.New(resp.Status))
	}
	return
}
<commit_msg>Remove DefaultTime from handler<commit_after>package main

import (
	"bytes"
	"errors"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"os/exec"
	"strings"
	"time"
)

type Reminder struct {
	*App
	ReminderMsg string
	SuccessMsg  string
	ThenDay     string
	ThenTime    string
}

func CreateReminder(app *App) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		reminder := newReminder(app)
		switch r.Method {
		case "GET":
			renderTemplate(w, reminder)
			return
		case "POST":
			submitReminder(w, r, reminder)
			return
		}
	})
}

func newReminder(app *App) *Reminder {
	return &Reminder{
		App:        app,
		SuccessMsg: "",
	}
}

func renderTemplate(w http.ResponseWriter, rem *Reminder) {
	templateFile := rem.DocumentRoot + "/" + rem.Path + "/create.html"
	t, err := template.ParseFiles(templateFile)
	die("error when rendering template: %v", err)
	w.WriteHeader(http.StatusOK)
	t.Execute(w, rem)
}

func submitReminder(w http.ResponseWriter, r *http.Request, rem *Reminder) {
	rem.ThenDay = r.FormValue("day")
	rem.ThenTime = r.FormValue("time")
	rem.ReminderMsg = r.FormValue("message")
	delay, err := timeToSleepFor(rem)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	go func(time.Duration, *Reminder) {
		select {
		case <-time.After(delay):
			execute(rem)
		}
	}(delay, rem)

	thenDateTime := time.Now().Add(delay).Format("2006-01-02 15:04:05-07:00")
	rem.SuccessMsg = "Thank you! Your reminder will be sent at " + thenDateTime
	renderTemplate(w, rem)
	log.Printf("The reminder '%v' will be sent at %v", rem.ReminderMsg, thenDateTime)
}

func timeToSleepFor(rem *Reminder) (time.Duration, error) {
	thenDate, err := thenDate(rem)
	if err != nil {
		return 0, err
	}
	return thenDate.Sub(time.Now()), nil
}

func thenDate(rem *Reminder) (time.Time, error) {
	cmd := exec.Command("date", "--rfc-3339=seconds", "--date",
		rem.ThenDay+" "+rem.ThenTime)
	var cmdResult bytes.Buffer
	cmd.Stdout = &cmdResult
	err := cmd.Run()
	if err != nil {
		log.Printf("error: %v", cmd)
		return time.Now(), err
	}
	then, err := time.Parse("2006-01-02 15:04:05-07:00",
		strings.TrimSuffix(cmdResult.String(), "\n"))
	if err != nil {
		return time.Now(), err
	}
	return then, nil
}

func execute(rem *Reminder) {
	resp, err := http.PostForm(rem.NotificationApi,
		url.Values{"token": {rem.ApiToken},
			"user":    {rem.ApiUser},
			"message": {rem.ReminderMsg}})
	die("error when using Notification API: %v", err)
	defer resp.Body.Close()
	if resp.Status != "200 OK" {
		die("error when using Notification API: %v",
			errors.New(resp.Status))
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/NebulousLabs/Andromeda/network"
	"github.com/NebulousLabs/Andromeda/siacore"
)

// Environment is the struct that serves as the state for siad. It contains a
// pointer to the state, as things like a wallet, a friend list, etc. Each
// environment should have its own state.
type Environment struct {
	state *siacore.State

	server       *network.TCPServer
	host         *Host
	hostDatabase *HostDatabase
	renter       *Renter
	wallet       *Wallet

	friends map[string]siacore.CoinAddress

	// Channels for incoming blocks and transactions to be processed
	blockChan       chan siacore.Block
	transactionChan chan siacore.Transaction

	// Mining variables. The mining variables are protected by the miningLock.
	// Any time that you read from or write to any of the mining variables, you
	// need to be under a lock.
	mining        bool         // true when mining
	miningThreads int          // number of processes mining at once
	miningLock    sync.RWMutex // prevents benign race conditions
}

// createEnvironment creates a server, host, miner, renter and wallet and
// puts it all in a single environment struct that's used as the state for the
// main package.
func CreateEnvironment(rpcPort uint16, apiPort uint16, nobootstrap bool) (e *Environment, err error) {
	e = &Environment{
		state:           siacore.CreateGenesisState(),
		friends:         make(map[string]siacore.CoinAddress),
		blockChan:       make(chan siacore.Block, 100),
		transactionChan: make(chan siacore.Transaction, 100),
	}

	// Initialize all internal structures.
	e.hostDatabase = CreateHostDatabase()
	e.host = CreateHost()
	e.renter = CreateRenter()
	e.wallet = CreateWallet(e.state)

	// Bootstrap to the network.
	err = e.initializeNetwork(rpcPort, nobootstrap)
	if err != nil {
		return
	}
	e.host.Settings.IPAddress = e.server.NetAddress()

	// Begin listening for requests on the api.
	e.setUpHandlers(apiPort)

	return
}

// Close does any finishing maintenence before the environment can be garbage
// collected. Right now that just means closing the server.
func (e *Environment) Close() {
	e.server.Close()
}

// initializeNetwork registers the rpcs and bootstraps to the network,
// downlading all of the blocks and establishing a peer list.
func (e *Environment) initializeNetwork(rpcPort uint16, nobootstrap bool) (err error) {
	e.server, err = network.NewTCPServer(rpcPort)
	if err != nil {
		return
	}

	e.server.Register("AcceptBlock", e.AcceptBlock)
	e.server.Register("AcceptTransaction", e.AcceptTransaction)
	e.server.Register("SendBlocks", e.SendBlocks)
	e.server.Register("NegotiateContract", e.NegotiateContract)
	e.server.Register("RetrieveFile", e.RetrieveFile)

	if nobootstrap {
		go e.listen()
		return
	}

	// establish an initial peer list
	if err = e.server.Bootstrap(); err != nil {
		return
	}

	// Download the blockchain, getting blocks one batch at a time until an
	// empty batch is sent.
	go func() {
		// Catch up the first time.
		if err := e.CatchUp(e.RandomPeer()); err != nil {
			fmt.Println("Error during CatchUp:", err)
		}

		// Every 2 minutes call CatchUp() on a random peer. This will help to
		// resolve synchronization issues and keep everybody on the same page
		// with regards to the longest chain. It's a bit of a hack but will
		// make the network substantially more robust.
		for {
			time.Sleep(time.Minute * 2)
			e.CatchUp(e.RandomPeer())
		}
	}()

	go e.listen()

	return nil
}

// AcceptBlock sends the input block down a channel, where it will be dealt
// with by the Environment's listener.
func (e *Environment) AcceptBlock(b siacore.Block) error {
	e.blockChan <- b
	return nil
}

// AcceptTransaction sends the input transaction down a channel, where it will
// be dealt with by the Environment's listener.
func (e *Environment) AcceptTransaction(t siacore.Transaction) error {
	e.transactionChan <- t
	return nil
}

// processBlock is called by the environment's listener.
func (e *Environment) processBlock(b siacore.Block) {
	e.state.Lock()
	e.hostDatabase.Lock()
	e.host.Lock()
	defer e.state.Unlock()
	defer e.hostDatabase.Unlock()
	defer e.host.Unlock()

	initialStateHeight := e.state.Height()
	rewoundBlocks, appliedBlocks, err := e.state.AcceptBlock(b)

	// Perform error handling.
	if err == siacore.BlockKnownErr {
		return
	} else if err != nil {
		// Call CatchUp() if an unknown orphan is sent.
		if err == siacore.UnknownOrphanErr {
			err = e.CatchUp(e.server.RandomPeer())
			if err != nil {
				fmt.Println("Error while calling catchup: ", err)
			}
		} else if err != siacore.KnownOrphanErr {
			fmt.Println("AcceptBlock Error: ", err)
		}
		return
	}

	e.updateHostDB(rewoundBlocks, appliedBlocks)
	e.storageProofMaintenance(stateHeight, rewoundBlocks, appliedBlocks)

	// Broadcast all valid blocks.
	go e.server.Broadcast("AcceptBlock", b, nil)
}

// processTransaction sends a transaction to the state.
func (e *Environment) processTransaction(t siacore.Transaction) {
	e.state.Lock()
	defer e.state.Unlock()

	err := e.state.AcceptTransaction(t)
	if err != nil {
		if err != siacore.ConflictingTransactionErr {
			// TODO: Change this println to a logging statement.
			fmt.Println("AcceptTransaction Error:", err)
		}
		return
	}

	go e.server.Broadcast("AcceptTransaction", t, nil)
}

// listen waits until a new block or transaction arrives, then attempts to
// process and rebroadcast it.
func (e *Environment) listen() {
	for {
		select {
		case b := <-e.blockChan:
			e.processBlock(b)

		case t := <-e.transactionChan:
			e.processTransaction(t)
		}
	}
}
<commit_msg>restore compiling<commit_after>package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/NebulousLabs/Andromeda/network"
	"github.com/NebulousLabs/Andromeda/siacore"
)

// Environment is the struct that serves as the state for siad. It contains a
// pointer to the state, as things like a wallet, a friend list, etc. Each
// environment should have its own state.
type Environment struct {
	state *siacore.State

	server       *network.TCPServer
	host         *Host
	hostDatabase *HostDatabase
	renter       *Renter
	wallet       *Wallet

	friends map[string]siacore.CoinAddress

	// Channels for incoming blocks and transactions to be processed
	blockChan       chan siacore.Block
	transactionChan chan siacore.Transaction

	// Mining variables. The mining variables are protected by the miningLock.
	// Any time that you read from or write to any of the mining variables, you
	// need to be under a lock.
	mining        bool         // true when mining
	miningThreads int          // number of processes mining at once
	miningLock    sync.RWMutex // prevents benign race conditions
}

// createEnvironment creates a server, host, miner, renter and wallet and
// puts it all in a single environment struct that's used as the state for the
// main package.
func CreateEnvironment(rpcPort uint16, apiPort uint16, nobootstrap bool) (e *Environment, err error) {
	e = &Environment{
		state:           siacore.CreateGenesisState(),
		friends:         make(map[string]siacore.CoinAddress),
		blockChan:       make(chan siacore.Block, 100),
		transactionChan: make(chan siacore.Transaction, 100),
	}

	// Initialize all internal structures.
	e.hostDatabase = CreateHostDatabase()
	e.host = CreateHost()
	e.renter = CreateRenter()
	e.wallet = CreateWallet(e.state)

	// Bootstrap to the network.
	err = e.initializeNetwork(rpcPort, nobootstrap)
	if err != nil {
		return
	}
	e.host.Settings.IPAddress = e.server.NetAddress()

	// Begin listening for requests on the api.
	e.setUpHandlers(apiPort)

	return
}

// Close does any finishing maintenence before the environment can be garbage
// collected. Right now that just means closing the server.
func (e *Environment) Close() {
	e.server.Close()
}

// initializeNetwork registers the rpcs and bootstraps to the network,
// downlading all of the blocks and establishing a peer list.
func (e *Environment) initializeNetwork(rpcPort uint16, nobootstrap bool) (err error) {
	e.server, err = network.NewTCPServer(rpcPort)
	if err != nil {
		return
	}

	e.server.Register("AcceptBlock", e.AcceptBlock)
	e.server.Register("AcceptTransaction", e.AcceptTransaction)
	e.server.Register("SendBlocks", e.SendBlocks)
	e.server.Register("NegotiateContract", e.NegotiateContract)
	e.server.Register("RetrieveFile", e.RetrieveFile)

	if nobootstrap {
		go e.listen()
		return
	}

	// establish an initial peer list
	if err = e.server.Bootstrap(); err != nil {
		return
	}

	// Download the blockchain, getting blocks one batch at a time until an
	// empty batch is sent.
	go func() {
		// Catch up the first time.
		if err := e.CatchUp(e.RandomPeer()); err != nil {
			fmt.Println("Error during CatchUp:", err)
		}

		// Every 2 minutes call CatchUp() on a random peer. This will help to
		// resolve synchronization issues and keep everybody on the same page
		// with regards to the longest chain. It's a bit of a hack but will
		// make the network substantially more robust.
		for {
			time.Sleep(time.Minute * 2)
			e.CatchUp(e.RandomPeer())
		}
	}()

	go e.listen()

	return nil
}

// AcceptBlock sends the input block down a channel, where it will be dealt
// with by the Environment's listener.
func (e *Environment) AcceptBlock(b siacore.Block) error {
	e.blockChan <- b
	return nil
}

// AcceptTransaction sends the input transaction down a channel, where it will
// be dealt with by the Environment's listener.
func (e *Environment) AcceptTransaction(t siacore.Transaction) error {
	e.transactionChan <- t
	return nil
}

// processBlock is called by the environment's listener.
func (e *Environment) processBlock(b siacore.Block) {
	e.state.Lock()
	e.hostDatabase.Lock()
	e.host.Lock()
	defer e.state.Unlock()
	defer e.hostDatabase.Unlock()
	defer e.host.Unlock()

	initialStateHeight := e.state.Height()
	rewoundBlocks, appliedBlocks, err := e.state.AcceptBlock(b)

	// Perform error handling.
	if err == siacore.BlockKnownErr || siacore.KnownOrphanErr {
		return
	} else if err != nil {
		// Call CatchUp() if an unknown orphan is sent.
		if err == siacore.UnknownOrphanErr {
			go e.CatchUp(e.server.RandomPeer())
		}
		return
	}

	e.updateHostDB(rewoundBlocks, appliedBlocks)
	e.storageProofMaintenance(initialStateHeight, rewoundBlocks, appliedBlocks)

	// Broadcast all valid blocks.
	go e.server.Broadcast("AcceptBlock", b, nil)
}

// processTransaction sends a transaction to the state.
func (e *Environment) processTransaction(t siacore.Transaction) {
	e.state.Lock()
	defer e.state.Unlock()

	err := e.state.AcceptTransaction(t)
	if err != nil {
		if err != siacore.ConflictingTransactionErr {
			// TODO: Change this println to a logging statement.
			fmt.Println("AcceptTransaction Error:", err)
		}
		return
	}

	go e.server.Broadcast("AcceptTransaction", t, nil)
}

// listen waits until a new block or transaction arrives, then attempts to
// process and rebroadcast it.
func (e *Environment) listen() {
	for {
		select {
		case b := <-e.blockChan:
			e.processBlock(b)

		case t := <-e.transactionChan:
			e.processTransaction(t)
		}
	}
}
<|endoftext|>