<commit_before>package gosxalerter

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

type Sound string
type ActivationType string

const (
	SoundDefault Sound = "'default'"
	SoundBasso   Sound = "Basso"
	SoundBlow    Sound = "Blow"
	SoundBottle  Sound = "Bottle"
	SoundFrog    Sound = "Frog"
	SoundFunk    Sound = "Funk"
	SoundGlass   Sound = "Glass"
	SoundHero    Sound = "Hero"
	SoundMorse   Sound = "Morse"
	SoundPing    Sound = "Ping"
	SoundPop     Sound = "Pop"
	SoundPurr    Sound = "Purr"
	SoundSosumi  Sound = "Sosumi"
	SoundTink    Sound = "Tink"
)

const (
	ActivationTypeClosed          ActivationType = "Closed"
	ActivationTypeTimeOut         ActivationType = "timeout"
	ActivationTypeContentsClicked ActivationType = "contentsClicked"
	ActivationTypeActionClicked   ActivationType = "actionClicked"
	ActivationTypeReplied         ActivationType = "replied"
)

type Alert struct {
	Options    *Options
	Activation *Activation
}
type Options struct {
	Message       string   //required
	Title         string   //optional
	Subtitle      string   //optional
	Sound         Sound    //optional
	Link          string   //optional
	Sender        string   //optional
	Group         string   //optional
	AppIcon       string   //optional
	ContentImage  string   //optional
	Actions       []string //optional
	Reply         bool
	CloseLabel    string
	DropdownLabel string
	Timeout       int
}

type Activation struct {
	At          string         `json:"activationAt"`
	Type        ActivationType `json:"activationType"`
	Value       string         `json:"activationValue"`
	DeliveredAt string         `json:"deliveredAt"`
	ValueIndex  string         `json:"activationValueIndex"`
}

func New(message string) *Alert {
	opts := &Options{
		Message: message,
		Reply:   false,
		Timeout: 0,
	}

	a := &Alert{
		Options: opts,
	}
	return a
}

func (a *Alert) DeliverAndWait() (*Activation, error) {
	activationChan, err := a.Deliver()
	if err != nil {
		return nil, fmt.Errorf("can not deliver - %s", err.Error())
	}
	activation := <-activationChan
	return activation, nil
}

func (a *Alert) Deliver() (chan *Activation, error) {
	name, args, err := buildCommand(a)
	if err != nil {
		return nil, fmt.Errorf("error: %s", err)
	}

	cmd := exec.Command(name, args...)
	cmdOut, _ := cmd.StdoutPipe()
	if err := cmd.Start(); err != nil {
		return nil, err
	}

	activation := make(chan *Activation, 1)
	cmdBytes, _ := ioutil.ReadAll(cmdOut)
	cmd.Wait()
	act := &Activation{}

	json.Unmarshal(cmdBytes, &act)
	a.Activation = act
	activation <- act

	return activation, nil
}

func buildCommand(a *Alert) (name string, arg []string, err error) {
	commandTuples := make([]string, 0)

	//check required commands
	if a.Options.Message == "" {
		return "", nil, errors.New("Please specifiy a proper message argument.")
	} else {
		commandTuples = append(commandTuples, []string{"-message", a.Options.Message}...)
	}

	//add closeLabel if found
	if a.Options.CloseLabel != "" {
		commandTuples = append(commandTuples, []string{"-closeLabel", a.Options.CloseLabel}...)
	}

	//add dropdownLabel if found
	if a.Options.DropdownLabel != "" {
		commandTuples = append(commandTuples, []string{"-dropdownLabel", a.Options.DropdownLabel}...)
	}

	//add title if found
	if len(a.Options.Actions) > 0 {
		commandTuples = append(commandTuples, []string{"-actions"}...)
		commandTuples = append(commandTuples, strings.Join(a.Options.Actions, ","))
	}

	//add Reply if found
	if a.Options.Reply == true {
		commandTuples = append(commandTuples, []string{"-reply"}...)
	}

	//add Reply if found
	if a.Options.Timeout > 0 {
		commandTuples = append(commandTuples, []string{"-timeout", strconv.Itoa(a.Options.Timeout)}...)
	}

	//add title if found
	if a.Options.Title != "" {
		commandTuples = append(commandTuples, []string{"-title", a.Options.Title}...)
	}

	//add subtitle if found
	if a.Options.Subtitle != "" {
		commandTuples = append(commandTuples, []string{"-subtitle", a.Options.Subtitle}...)
	}

	//add sound if specified
	if a.Options.Sound != "" {
		commandTuples = append(commandTuples, []string{"-sound", string(a.Options.Sound)}...)
	}

	//add group if specified
	if a.Options.Group != "" {
		commandTuples = append(commandTuples, []string{"-group", a.Options.Group}...)
	}

	//add appIcon if specified
	if a.Options.AppIcon != "" {
		commandTuples = append(commandTuples, []string{"-appIcon", a.Options.AppIcon}...)
	}

	//add contentImage if specified
	if a.Options.ContentImage != "" {
		commandTuples = append(commandTuples, []string{"-contentImage", a.Options.ContentImage}...)
	}

	//add sender if specified
	if strings.HasPrefix(strings.ToLower(a.Options.Sender), "com.") {
		commandTuples = append(commandTuples, []string{"-sender", a.Options.Sender}...)
	}

	if len(commandTuples) == 0 {
		return "", nil, errors.New("Please provide a Message and Type at a minimum.")
	}

	return finalPath, commandTuples, nil
}

const (
	executableFilename = "alerter"
	tempDirSuffix      = "gosxalterter"
)

var (
	finalPath string
)

func init() {
	if runtime.GOOS == "darwin" {
		if err := installAlerter(); err != nil {
			log.Fatal(err.Error())
		} else {
			finalPath = filepath.Join(os.TempDir(), executableFilename)
		}
	}
}

func installAlerter() error {
	finalPath := filepath.Join(os.TempDir(), executableFilename)

	//if alerter already installed no-need to re-install
	if _, err := os.Stat(finalPath); false == os.IsNotExist(err) {
		return nil
	}

	alerterB, _ := alerterBytes()
	err := ioutil.WriteFile(finalPath, alerterB, 0700)
	if err != nil {
		return errors.New("could not write alerter file")
	}

	err = os.Chmod(finalPath, 0755)
	if err != nil {
		return errors.New("could not make alerter executable")
	}

	return nil
}
<commit_msg>comments<commit_after>package gosxalerter

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

type Sound string
type ActivationType string

const (
	SoundDefault Sound = "'default'"
	SoundBasso   Sound = "Basso"
	SoundBlow    Sound = "Blow"
	SoundBottle  Sound = "Bottle"
	SoundFrog    Sound = "Frog"
	SoundFunk    Sound = "Funk"
	SoundGlass   Sound = "Glass"
	SoundHero    Sound = "Hero"
	SoundMorse   Sound = "Morse"
	SoundPing    Sound = "Ping"
	SoundPop     Sound = "Pop"
	SoundPurr    Sound = "Purr"
	SoundSosumi  Sound = "Sosumi"
	SoundTink    Sound = "Tink"
)

const (
	ActivationTypeClosed          ActivationType = "Closed"
	ActivationTypeTimeOut         ActivationType = "timeout"
	ActivationTypeContentsClicked ActivationType = "contentsClicked"
	ActivationTypeActionClicked   ActivationType = "actionClicked"
	ActivationTypeReplied         ActivationType = "replied"
)

type Alert struct {
	Options    *Options
	Activation *Activation
}
type Options struct {
	Message       string   // required
	Title         string   // Title of the notification
	Subtitle      string   // Text under the title
	Sound         Sound    // Sound triggered when alert pops up
	Sender        string   // Send notification as a know osx app
	Group         string   // Group notification ID
	AppIcon       string   // Path or URL of image
	ContentImage  string   // Path or URL of image
	Actions       []string // One or more actions availables on the alert
	Reply         bool     // Reply type alert
	CloseLabel    string   // Change the Close button label
	DropdownLabel string   // When more than 1 action, you may customize the action dropdown label
	Timeout       int      // Autoclose notification avec X seconds
}

type Activation struct {
	Type        ActivationType `json:"activationType"`       // What kind of event dismissed the alert
	At          string         `json:"activationAt"`         // When did it happen ?
	Value       string         `json:"activationValue"`      // Value of activation
	DeliveredAt string         `json:"deliveredAt"`          // When displayed ?
	ValueIndex  string         `json:"activationValueIndex"` // When Dismissed ?
}

func New(message string) *Alert {
	opts := &Options{
		Message: message,
		Reply:   false,
		Timeout: 0,
	}

	a := &Alert{
		Options: opts,
	}
	return a
}

// DeliverAndWait display the alert, and returns an Activation when
// the user or the OS interacts with the notification.
func (a *Alert) DeliverAndWait() (*Activation, error) {
	activationChan, err := a.Deliver()
	if err != nil {
		return nil, fmt.Errorf("can not deliver - %s", err.Error())
	}
	activation := <-activationChan
	return activation, nil
}

// Deliver display the alert, and returns a chan that will be feeded later
// with Activation when user of OS interacts with the notification.
func (a *Alert) Deliver() (chan *Activation, error) {
	name, args, err := buildCommand(a)
	if err != nil {
		return nil, fmt.Errorf("error: %s", err)
	}

	cmd := exec.Command(name, args...)
	cmdOut, _ := cmd.StdoutPipe()
	if err := cmd.Start(); err != nil {
		return nil, err
	}

	activation := make(chan *Activation, 1)
	cmdBytes, _ := ioutil.ReadAll(cmdOut)
	cmd.Wait()
	act := &Activation{}

	json.Unmarshal(cmdBytes, &act)
	a.Activation = act
	activation <- act

	return activation, nil
}

func buildCommand(a *Alert) (name string, arg []string, err error) {
	commandTuples := make([]string, 0)

	//check required commands
	if a.Options.Message == "" {
		return "", nil, errors.New("Please specifiy a proper message argument.")
	} else {
		commandTuples = append(commandTuples, []string{"-message", a.Options.Message}...)
	}

	//add closeLabel if found
	if a.Options.CloseLabel != "" {
		commandTuples = append(commandTuples, []string{"-closeLabel", a.Options.CloseLabel}...)
	}

	//add dropdownLabel if found
	if a.Options.DropdownLabel != "" {
		commandTuples = append(commandTuples, []string{"-dropdownLabel", a.Options.DropdownLabel}...)
	}

	//add title if found
	if len(a.Options.Actions) > 0 {
		commandTuples = append(commandTuples, []string{"-actions"}...)
		commandTuples = append(commandTuples, strings.Join(a.Options.Actions, ","))
	}

	//add Reply if found
	if a.Options.Reply == true {
		commandTuples = append(commandTuples, []string{"-reply"}...)
	}

	//add Reply if found
	if a.Options.Timeout > 0 {
		commandTuples = append(commandTuples, []string{"-timeout", strconv.Itoa(a.Options.Timeout)}...)
	}

	//add title if found
	if a.Options.Title != "" {
		commandTuples = append(commandTuples, []string{"-title", a.Options.Title}...)
	}

	//add subtitle if found
	if a.Options.Subtitle != "" {
		commandTuples = append(commandTuples, []string{"-subtitle", a.Options.Subtitle}...)
	}

	//add sound if specified
	if a.Options.Sound != "" {
		commandTuples = append(commandTuples, []string{"-sound", string(a.Options.Sound)}...)
	}

	//add group if specified
	if a.Options.Group != "" {
		commandTuples = append(commandTuples, []string{"-group", a.Options.Group}...)
	}

	//add appIcon if specified
	if a.Options.AppIcon != "" {
		commandTuples = append(commandTuples, []string{"-appIcon", a.Options.AppIcon}...)
	}

	//add contentImage if specified
	if a.Options.ContentImage != "" {
		commandTuples = append(commandTuples, []string{"-contentImage", a.Options.ContentImage}...)
	}

	//add sender if specified
	if strings.HasPrefix(strings.ToLower(a.Options.Sender), "com.") {
		commandTuples = append(commandTuples, []string{"-sender", a.Options.Sender}...)
	}

	if len(commandTuples) == 0 {
		return "", nil, errors.New("Please provide a Message and Type at a minimum.")
	}

	return finalPath, commandTuples, nil
}

const (
	executableFilename = "alerter"
	tempDirSuffix      = "gosxalterter"
)

var (
	finalPath string
)

func init() {
	if runtime.GOOS == "darwin" {
		if err := installAlerter(); err != nil {
			log.Fatal(err.Error())
		} else {
			finalPath = filepath.Join(os.TempDir(), executableFilename)
		}
	}
}

func installAlerter() error {
	finalPath := filepath.Join(os.TempDir(), executableFilename)

	//if alerter already installed no-need to re-install
	if _, err := os.Stat(finalPath); false == os.IsNotExist(err) {
		return nil
	}

	alerterB, _ := alerterBytes()
	err := ioutil.WriteFile(finalPath, alerterB, 0700)
	if err != nil {
		return errors.New("could not write alerter file")
	}

	err = os.Chmod(finalPath, 0755)
	if err != nil {
		return errors.New("could not make alerter executable")
	}

	return nil
}
<|endoftext|>


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

package main

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var hashNameRe = regexp.MustCompile(`^[0-9a-f]{7,40}(\+[0-9a-f]{1,10})?$`)
var fullHashRe = regexp.MustCompile("^[0-9a-f]{40}$")
var hashPlusRe = regexp.MustCompile(`^[0-9a-f]{40}(\+[0-9a-f]{10})?$`)

// resolveName returns the path to the root of the named build and
// whether or not that path exists. It will log an error and exit if
// name is ambiguous. If the path does not exist, the returned path is
// where this build should be saved.
func resolveName(name string) (path string, ok bool) {
	// If the name exactly matches a saved version, return it.
	savePath := filepath.Join(*verDir, name)
	st, err := os.Stat(savePath)
	if err == nil && st.IsDir() {
		return savePath, true
	}

	// Otherwise, try to resolve it as an unambiguous hash prefix.
	if hashNameRe.MatchString(name) {
		nameParts := strings.SplitN(name, "+", 2)
		builds, err := listBuilds(0)
		if err != nil {
			log.Fatal(err)
		}

		var fullName string
		for _, b := range builds {
			if !strings.HasPrefix(b.commitHash, nameParts[0]) {
				continue
			}
			if (len(nameParts) == 1) != (b.deltaHash == "") {
				continue
			}
			if len(nameParts) > 1 && !strings.HasPrefix(b.deltaHash, nameParts[1]) {
				continue
			}

			// We found a match.
			if fullName != "" {
				log.Fatalf("ambiguous name `%s`", name)
			}
			fullName = b.fullName()
		}
		if fullName != "" {
			return filepath.Join(*verDir, fullName), true
		}
	}

	return savePath, false
}

type buildInfo struct {
	commitHash string
	deltaHash  string
	names      []string
	commit     *commit
}

func (i buildInfo) fullName() string {
	if i.deltaHash == "" {
		return i.commitHash
	}
	return i.commitHash + "+" + i.deltaHash
}

func (i buildInfo) shortName() string {
	// TODO: Print more than 7 characters if necessary.
	if i.deltaHash == "" {
		return i.commitHash[:7]
	}
	return i.commitHash[:7] + "+" + i.deltaHash
}

type listFlags int

const (
	listNames listFlags = 1 << iota
	listCommit
)

func listBuilds(flags listFlags) ([]*buildInfo, error) {
	files, err := ioutil.ReadDir(*verDir)
	if os.IsNotExist(err) {
		return nil, nil
	} else if err != nil {
		return nil, err
	}

	// Collect the saved builds.
	builds := []*buildInfo{}
	var baseMap map[string]*buildInfo
	if flags&listNames != 0 {
		baseMap = make(map[string]*buildInfo)
	}
	for _, file := range files {
		if !file.IsDir() || !hashPlusRe.MatchString(file.Name()) {
			continue
		}
		nameParts := strings.SplitN(file.Name(), "+", 2)
		info := &buildInfo{commitHash: nameParts[0]}
		if len(nameParts) > 1 {
			info.deltaHash = nameParts[1]
		}

		builds = append(builds, info)
		if baseMap != nil {
			baseMap[file.Name()] = info
		}

		if flags&listCommit != 0 {
			commit, err := ioutil.ReadFile(filepath.Join(*verDir, file.Name(), "commit"))
			if err != nil {
				if !os.IsNotExist(err) {
					log.Fatal(err)
				}
			} else {
				info.commit = parseCommit(commit)
			}
		}
	}

	// Collect the names for each build.
	if flags&listNames != 0 {
		for _, file := range files {
			if file.Mode()&os.ModeType == os.ModeSymlink {
				base, err := os.Readlink(filepath.Join(*verDir, file.Name()))
				if err != nil {
					continue
				}
				if info, ok := baseMap[base]; ok {
					info.names = append(info.names, file.Name())
				}
			}
		}
	}

	return builds, nil
}

type commit struct {
	authorDate time.Time
	topLine    string
}

func parseCommit(obj []byte) *commit {
	out := &commit{}
	lines := strings.Split(string(obj), "\n")
	for i, line := range lines {
		if strings.HasPrefix(line, "author ") {
			fs := strings.Fields(line)
			secs, err := strconv.ParseInt(fs[len(fs)-2], 10, 64)
			if err != nil {
				log.Fatalf("malformed author in commit: %s", err)
			}
			out.authorDate = time.Unix(secs, 0)
		}
		if len(line) == 0 {
			out.topLine = lines[i+1]
			break
		}
	}
	return out
}
<commit_msg>gover: require commit metadata<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var hashNameRe = regexp.MustCompile(`^[0-9a-f]{7,40}(\+[0-9a-f]{1,10})?$`)
var fullHashRe = regexp.MustCompile("^[0-9a-f]{40}$")
var hashPlusRe = regexp.MustCompile(`^[0-9a-f]{40}(\+[0-9a-f]{10})?$`)

// resolveName returns the path to the root of the named build and
// whether or not that path exists. It will log an error and exit if
// name is ambiguous. If the path does not exist, the returned path is
// where this build should be saved.
func resolveName(name string) (path string, ok bool) {
	// If the name exactly matches a saved version, return it.
	savePath := filepath.Join(*verDir, name)
	st, err := os.Stat(savePath)
	if err == nil && st.IsDir() {
		return savePath, true
	}

	// Otherwise, try to resolve it as an unambiguous hash prefix.
	if hashNameRe.MatchString(name) {
		nameParts := strings.SplitN(name, "+", 2)
		builds, err := listBuilds(0)
		if err != nil {
			log.Fatal(err)
		}

		var fullName string
		for _, b := range builds {
			if !strings.HasPrefix(b.commitHash, nameParts[0]) {
				continue
			}
			if (len(nameParts) == 1) != (b.deltaHash == "") {
				continue
			}
			if len(nameParts) > 1 && !strings.HasPrefix(b.deltaHash, nameParts[1]) {
				continue
			}

			// We found a match.
			if fullName != "" {
				log.Fatalf("ambiguous name `%s`", name)
			}
			fullName = b.fullName()
		}
		if fullName != "" {
			return filepath.Join(*verDir, fullName), true
		}
	}

	return savePath, false
}

type buildInfo struct {
	commitHash string
	deltaHash  string
	names      []string
	commit     *commit
}

func (i buildInfo) fullName() string {
	if i.deltaHash == "" {
		return i.commitHash
	}
	return i.commitHash + "+" + i.deltaHash
}

func (i buildInfo) shortName() string {
	// TODO: Print more than 7 characters if necessary.
	if i.deltaHash == "" {
		return i.commitHash[:7]
	}
	return i.commitHash[:7] + "+" + i.deltaHash
}

type listFlags int

const (
	listNames listFlags = 1 << iota
	listCommit
)

func listBuilds(flags listFlags) ([]*buildInfo, error) {
	files, err := ioutil.ReadDir(*verDir)
	if os.IsNotExist(err) {
		return nil, nil
	} else if err != nil {
		return nil, err
	}

	// Collect the saved builds.
	builds := []*buildInfo{}
	var baseMap map[string]*buildInfo
	if flags&listNames != 0 {
		baseMap = make(map[string]*buildInfo)
	}
	for _, file := range files {
		if !file.IsDir() || !hashPlusRe.MatchString(file.Name()) {
			continue
		}
		nameParts := strings.SplitN(file.Name(), "+", 2)
		info := &buildInfo{commitHash: nameParts[0]}
		if len(nameParts) > 1 {
			info.deltaHash = nameParts[1]
		}

		builds = append(builds, info)
		if baseMap != nil {
			baseMap[file.Name()] = info
		}

		if flags&listCommit != 0 {
			commit, err := ioutil.ReadFile(filepath.Join(*verDir, file.Name(), "commit"))
			if err != nil {
				log.Fatal(err)
			} else {
				info.commit = parseCommit(commit)
			}
		}
	}

	// Collect the names for each build.
	if flags&listNames != 0 {
		for _, file := range files {
			if file.Mode()&os.ModeType == os.ModeSymlink {
				base, err := os.Readlink(filepath.Join(*verDir, file.Name()))
				if err != nil {
					continue
				}
				if info, ok := baseMap[base]; ok {
					info.names = append(info.names, file.Name())
				}
			}
		}
	}

	return builds, nil
}

type commit struct {
	authorDate time.Time
	topLine    string
}

func parseCommit(obj []byte) *commit {
	out := &commit{}
	lines := strings.Split(string(obj), "\n")
	for i, line := range lines {
		if strings.HasPrefix(line, "author ") {
			fs := strings.Fields(line)
			secs, err := strconv.ParseInt(fs[len(fs)-2], 10, 64)
			if err != nil {
				log.Fatalf("malformed author in commit: %s", err)
			}
			out.authorDate = time.Unix(secs, 0)
		}
		if len(line) == 0 {
			out.topLine = lines[i+1]
			break
		}
	}
	return out
}
<|endoftext|>


<commit_before>package grift

import (
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strings"
	"sync"
	"time"
)

var CommandName = "grift"
var griftList = map[string]Grift{}
var descriptions = map[string]string{}
var lock = &sync.Mutex{}
var namespace string

type Grift func(c *Context) error

// Create a namespace. All tasks within the
// namespace will share the same prefix.
func Namespace(name string, s func()) error {
	defer func() {
		namespace = ""
	}()

	namespace = applyNamespace(name)
	s()
	return nil
}

func applyNamespace(name string) string {
	if namespace != "" {
		if strings.HasPrefix(name, ":") {
			return name[1:]
		}
		if name == "default" {
			return name
		}
		return fmt.Sprintf("%s:%s", namespace, name)
	}

	return name
}

// Add a grift. If there is already a grift
// with the given name the two grifts will
// be bundled together.
func Add(name string, grift Grift) error {
	lock.Lock()
	defer lock.Unlock()

	name = applyNamespace(name)

	if griftList[name] != nil {
		fn := griftList[name]
		griftList[name] = func(c *Context) error {
			err := fn(c)
			if err != nil {
				return err
			}
			return grift(c)
		}
	} else {
		griftList[name] = grift
	}
	return nil
}

// Set a grift. This is similar to `Add` but it will
// overwrite an existing grift with the same name.
func Set(name string, grift Grift) error {
	lock.Lock()
	defer lock.Unlock()
	name = applyNamespace(name)
	griftList[name] = grift
	return nil
}

// Rename a grift. Useful if you want to re-define
// an existing grift, but don't want to write over
// the original.
func Rename(old string, new string) error {
	lock.Lock()
	defer lock.Unlock()

	old = applyNamespace(old)
	new = applyNamespace(new)

	if griftList[old] == nil {
		return fmt.Errorf("No task named %s defined!", old)
	}
	griftList[new] = griftList[old]
	delete(griftList, old)
	return nil
}

// Remove a grift. Not incredibly useful, but here for
// completeness.
func Remove(name string) error {
	lock.Lock()
	defer lock.Unlock()

	name = applyNamespace(name)

	delete(griftList, name)
	delete(descriptions, name)
	return nil
}

// Desc sets a helpful descriptive text for a grift.
// This description will be shown when `grift list`
// is run.
func Desc(name string, description string) error {
	lock.Lock()
	defer lock.Unlock()

	name = applyNamespace(name)

	descriptions[name] = description
	return nil
}

// Run a grift. This allows for the chaining for grifts.
// One grift can Run another grift and so on.
func Run(name string, c *Context) error {
	name = applyNamespace(name)

	if griftList[name] == nil {
		return fmt.Errorf("No task named '%s' defined!", name)
	}
	if c.Verbose {
		defer func(start time.Time) {
			log.Printf("Completed task %s in %s\n", name, time.Now().Sub(start))
		}(time.Now())
		log.Printf("Starting task %s\n", name)
	}
	return griftList[name](c)
}

// List of the names of the defined grifts.
func List() []string {
	keys := []string{}
	for k := range griftList {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}

// Exec the grift stack. This is the main "entry point" to
// the grift system.
func Exec(args []string, verbose bool) error {
	name := "default"
	if len(args) >= 1 {
		name = args[0]
	}
	switch name {
	case "list":
		var showAll bool
		if len(args) >= 2 && args[1] == "-a" {
			showAll = true
		}
		PrintGrifts(os.Stdout, showAll)
	default:
		c := NewContext(name)
		c.Verbose = verbose
		if len(args) >= 1 {
			c.Args = args[1:]
		}
		return Run(name, c)
	}
	return nil
}

// PrintGrifts to the screen, nice, sorted, and with descriptions,
// should they exist.
func PrintGrifts(w io.Writer, all bool) {
	cnLen := len(CommandName)
	maxLen := cnLen
	l := List()

	for _, k := range l {
		if !all && descriptions[k] == "" {
			continue
		}
		if (len(k) + cnLen) > maxLen {
			maxLen = len(k) + cnLen
		}
	}

	for _, k := range l {
		if !all && descriptions[k] == "" {
			continue
		}
		m := strings.Join([]string{CommandName, k}, " ")
		suffix := strings.Repeat(" ", (maxLen+3)-len(m)) + " #"

		fmt.Fprintln(w, strings.Join([]string{m, suffix, descriptions[k]}, " "))

	}
}
<commit_msg>Fix godoc and variable name<commit_after>package grift

import (
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strings"
	"sync"
	"time"
)

var CommandName = "grift"
var griftList = map[string]Grift{}
var descriptions = map[string]string{}
var lock = &sync.Mutex{}
var namespace string

type Grift func(c *Context) error

// Namespace will place all tasks within the given prefix.
func Namespace(name string, s func()) error {
	defer func() {
		namespace = ""
	}()

	namespace = applyNamespace(name)
	s()
	return nil
}

func applyNamespace(name string) string {
	if namespace != "" {
		if strings.HasPrefix(name, ":") {
			return name[1:]
		}
		if name == "default" {
			return name
		}
		return fmt.Sprintf("%s:%s", namespace, name)
	}

	return name
}

// Add a grift. If there is already a grift
// with the given name the two grifts will
// be bundled together.
func Add(name string, grift Grift) error {
	lock.Lock()
	defer lock.Unlock()

	name = applyNamespace(name)

	if griftList[name] != nil {
		fn := griftList[name]
		griftList[name] = func(c *Context) error {
			err := fn(c)
			if err != nil {
				return err
			}
			return grift(c)
		}
	} else {
		griftList[name] = grift
	}
	return nil
}

// Set a grift. This is similar to `Add` but it will
// overwrite an existing grift with the same name.
func Set(name string, grift Grift) error {
	lock.Lock()
	defer lock.Unlock()
	name = applyNamespace(name)
	griftList[name] = grift
	return nil
}

// Rename a grift. Useful if you want to re-define
// an existing grift, but don't want to write over
// the original.
func Rename(oldName string, newName string) error {
	lock.Lock()
	defer lock.Unlock()

	oldName = applyNamespace(oldName)
	newName = applyNamespace(newName)

	if griftList[oldName] == nil {
		return fmt.Errorf("No task named %s defined!", oldName)
	}
	griftList[newName] = griftList[oldName]
	delete(griftList, oldName)
	return nil
}

// Remove a grift. Not incredibly useful, but here for
// completeness.
func Remove(name string) error {
	lock.Lock()
	defer lock.Unlock()

	name = applyNamespace(name)

	delete(griftList, name)
	delete(descriptions, name)
	return nil
}

// Desc sets a helpful descriptive text for a grift.
// This description will be shown when `grift list`
// is run.
func Desc(name string, description string) error {
	lock.Lock()
	defer lock.Unlock()

	name = applyNamespace(name)

	descriptions[name] = description
	return nil
}

// Run a grift. This allows for the chaining for grifts.
// One grift can Run another grift and so on.
func Run(name string, c *Context) error {
	name = applyNamespace(name)

	if griftList[name] == nil {
		return fmt.Errorf("No task named '%s' defined!", name)
	}
	if c.Verbose {
		defer func(start time.Time) {
			log.Printf("Completed task %s in %s\n", name, time.Now().Sub(start))
		}(time.Now())
		log.Printf("Starting task %s\n", name)
	}
	return griftList[name](c)
}

// List of the names of the defined grifts.
func List() []string {
	keys := []string{}
	for k := range griftList {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}

// Exec the grift stack. This is the main "entry point" to
// the grift system.
func Exec(args []string, verbose bool) error {
	name := "default"
	if len(args) >= 1 {
		name = args[0]
	}
	switch name {
	case "list":
		var showAll bool
		if len(args) >= 2 && args[1] == "-a" {
			showAll = true
		}
		PrintGrifts(os.Stdout, showAll)
	default:
		c := NewContext(name)
		c.Verbose = verbose
		if len(args) >= 1 {
			c.Args = args[1:]
		}
		return Run(name, c)
	}
	return nil
}

// PrintGrifts to the screen, nice, sorted, and with descriptions,
// should they exist.
func PrintGrifts(w io.Writer, all bool) {
	cnLen := len(CommandName)
	maxLen := cnLen
	l := List()

	for _, k := range l {
		if !all && descriptions[k] == "" {
			continue
		}
		if (len(k) + cnLen) > maxLen {
			maxLen = len(k) + cnLen
		}
	}

	for _, k := range l {
		if !all && descriptions[k] == "" {
			continue
		}
		m := strings.Join([]string{CommandName, k}, " ")
		suffix := strings.Repeat(" ", (maxLen+3)-len(m)) + " #"

		fmt.Fprintln(w, strings.Join([]string{m, suffix, descriptions[k]}, " "))

	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"bufio"
	"regexp"
	"log"
	"fmt"
	"time"
	"flag"
	"strings"
	"path"

	"github.com/cheggaaa/pb"

	riakdata "github.com/likemindnetworks/riakdataportation/data"
	riakcli "github.com/likemindnetworks/riakdataportation/client"
)

const Version = "1.0.0"

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func printHelp() {
	fmt.Println("Usage: portation <export|import> <riak host with port>")
	flag.PrintDefaults()
}

func main() {
	log.Printf("Riak Data Portation Tool")

	var (
		numConnection = flag.Int("c", 20, "number of connections")
		appName = flag.String("a", "", "app name")
		dataVersion = flag.String("d", "", "data version")
		inputFile = flag.String("i", "", "input file for import")
		outputDir = flag.String("o", ".", "output folder for export result")
		printVersion = flag.Bool("v", false, "print version")
	)

	flag.Parse()

	if *printVersion {
		fmt.Println(Version)
		return
	}

	if len(flag.Args()) != 2 {
		printHelp()
		return
	}

	action := flag.Args()[0]
	host := flag.Args()[1]

	if action != "import" && action != "export" {
		printHelp()
		return
	}

	if strings.ContainsAny(*appName, "@:") ||
			strings.ContainsAny(*dataVersion, "@:") {
		panic("app name and data version should never contain @ or :")
	}

	// start
	startTime := time.Now()

	// "Riak-dev-ELB-749646943.us-east-1.elb.amazonaws.com:8087"
	var cli = riakcli.NewClient(host, *numConnection)

	err := cli.Connect()
	check(err)

	prog := make(chan float64, 100)
	quit := make(chan bool)
	started := false

	bar := pb.New(100)
	bar.SetWidth(80)
	bar.ShowCounters = false
	bar.ShowFinalTime = false

	go func() {
		for {
			pct, more := <-prog

			if !more {
				bar.Set(int(100))
				bar.Finish()
				cli.Close()
				quit <- true
				return
			} else {
				if !started {
					bar.Start()
					started = true
				}

				bar.Set(int(pct * 100))
			}
		}
	}()

	var byteCnt int

	switch action {
	case "import":
		byteCnt = runImport(cli, appName, dataVersion, inputFile, prog)
	case "export":
		byteCnt = runExport(cli, appName, dataVersion, outputDir, prog)
	}

	<-quit

	timeElapsed := time.Since(startTime)

	log.Printf("Bytes processed: %d", byteCnt)
	log.Printf("Time took: %s", timeElapsed)
	log.Printf(
		"Rate: %.4f Kb/s",
		float64(byteCnt) / 100 / float64(timeElapsed / time.Second),
	)
}

func runImport(
	cli *riakcli.Client,
	appName *string,
	dataVersion *string,
	inputFile *string,
	prog chan float64,
) int {
	log.Printf("Start Importing")

	bucketOverride := func(b []byte) []byte {
		bucket := string(b)
		colonIndex := strings.Index(bucket, ":")
		atIndex := strings.Index(bucket, "@")

		if (colonIndex < 0) {
			panic("cannot locate : to find app name prefix")
		}

		if atIndex >= 0 && atIndex > colonIndex {
			panic("@ is after :")
		}

		if atIndex + 1 == colonIndex {
			panic("Empty data version")
		}

		if strings.Count(bucket, ":") > 1 || strings.Count(bucket, "@") > 1 {
			panic("more than 1 : or @")
		}

		var origAppName string
		var origDataVersion string
		var bucketName string
		var resBucket string

		if atIndex >= 0 {
			// has data version in backup
			origAppName = bucket[0:atIndex]
			origDataVersion = bucket[atIndex + 1:colonIndex]
		} else {
			// no data version in backup
			origAppName = bucket[0:colonIndex]
		}

		bucketName = bucket[colonIndex + 1:]

		if len(*appName) == 0 {
			// no override for app name use original
			appName = &origAppName
		}
		resBucket = *appName

		if len(*dataVersion) > 0 {
			// has override for data version
			resBucket += "@" + *dataVersion
		} else if len(origDataVersion) > 0 {
			// has original data version either
			resBucket += "@" + origDataVersion
		}

		resBucket += ":" + bucketName

		return []byte(resBucket)
	}

	if len(*inputFile) == 0 {
		panic("missing input file")
	}

	f, err := os.Open(*inputFile)
	check(err)
	defer f.Close()
	input := bufio.NewReader(f)

	importation := riakdata.NewImport(cli, input, bucketOverride);

	cnt, err := importation.Run(prog)
	check(err)

	return cnt
}

func runExport(
	cli *riakcli.Client,
	appName *string,
	dataVersion *string,
	outputDir *string,
	prog chan float64,
) int {
	log.Printf("Start Exporting")

	if len(*appName) == 0 {
		panic("missing app name")
	}

	prefix := *appName

	if len(*dataVersion) > 0 {
		// has data version
		prefix += "@" + *dataVersion
	}

	f, err := os.Create(path.Join(
		*outputDir,
		prefix + "." + time.Now().Format(time.RFC3339) + ".bin",
	))
	check(err)
	defer f.Close()
	output := bufio.NewWriter(f)

	export := riakdata.NewExport(
		cli,
		func(k []byte) bool {
			match, err := regexp.MatchString("^" + prefix + ":.*$", string(k))

			return err == nil && match
		},
		[]string{"data"},
		[]string{"sets", "maps", "counters"},
		output,
	);

	cnt, err := export.Run(prog)
	check(err)

	return cnt
}
<commit_msg>confirm before import into a given appName<commit_after>package main

import (
	"os"
	"bufio"
	"regexp"
	"log"
	"fmt"
	"time"
	"flag"
	"strings"
	"path"

	"github.com/cheggaaa/pb"

	riakdata "github.com/likemindnetworks/riakdataportation/data"
	riakcli "github.com/likemindnetworks/riakdataportation/client"
)

const Version = "1.0.0"

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func printHelp() {
	fmt.Println("Usage: portation <export|import> <riak host with port>")
	flag.PrintDefaults()
}

func main() {
	log.Printf("Riak Data Portation Tool")

	var (
		numConnection = flag.Int("c", 20, "number of connections")
		appName = flag.String("a", "", "app name")
		dataVersion = flag.String("d", "", "data version")
		inputFile = flag.String("i", "", "input file for import")
		outputDir = flag.String("o", ".", "output folder for export result")
		printVersion = flag.Bool("v", false, "print version")
	)

	flag.Parse()

	if *printVersion {
		fmt.Println(Version)
		return
	}

	if len(flag.Args()) != 2 {
		printHelp()
		return
	}

	action := flag.Args()[0]
	host := flag.Args()[1]

	if action != "import" && action != "export" {
		printHelp()
		return
	}

	if strings.ContainsAny(*appName, "@:") ||
			strings.ContainsAny(*dataVersion, "@:") {
		panic("app name and data version should never contain @ or :")
	}

	// warn when try to override appName during import
	if action == "import" && len(*appName) > 0 {
		fmt.Println("Trying to override app name during import. Are you sure?");
		fmt.Printf("Type the full app name (%s) to confirm: ", *appName)

		readConfirm := bufio.NewReader(os.Stdin)
		text, _ := readConfirm.ReadString('\n')

		if text[:len(text) - 1] != *appName {
			panic("Not Confirmed. Abort import.")
		}
	}

	// start
	startTime := time.Now()

	var cli = riakcli.NewClient(host, *numConnection)

	err := cli.Connect()
	check(err)

	prog := make(chan float64, 100)
	quit := make(chan bool)
	started := false

	bar := pb.New(100)
	bar.SetWidth(80)
	bar.ShowCounters = false
	bar.ShowFinalTime = false

	go func() {
		for {
			pct, more := <-prog

			if !more {
				bar.Set(int(100))
				bar.Finish()
				cli.Close()
				quit <- true
				return
			} else {
				if !started {
					bar.Start()
					started = true
				}

				bar.Set(int(pct * 100))
			}
		}
	}()

	var byteCnt int

	switch action {
	case "import":
		byteCnt = runImport(cli, appName, dataVersion, inputFile, prog)
	case "export":
		byteCnt = runExport(cli, appName, dataVersion, outputDir, prog)
	}

	<-quit

	timeElapsed := time.Since(startTime)

	log.Printf("Bytes processed: %d", byteCnt)
	log.Printf("Time took: %s", timeElapsed)
	log.Printf(
		"Rate: %.4f Kb/s",
		float64(byteCnt) / 100 / float64(timeElapsed / time.Second),
	)
}

func runImport(
	cli *riakcli.Client,
	appName *string,
	dataVersion *string,
	inputFile *string,
	prog chan float64,
) int {
	log.Printf("Start Importing")

	bucketOverride := func(b []byte) []byte {
		bucket := string(b)
		colonIndex := strings.Index(bucket, ":")
		atIndex := strings.Index(bucket, "@")

		if (colonIndex < 0) {
			panic("cannot locate : to find app name prefix")
		}

		if atIndex >= 0 && atIndex > colonIndex {
			panic("@ is after :")
		}

		if atIndex + 1 == colonIndex {
			panic("Empty data version")
		}

		if strings.Count(bucket, ":") > 1 || strings.Count(bucket, "@") > 1 {
			panic("more than 1 : or @")
		}

		var origAppName string
		var origDataVersion string
		var bucketName string
		var resBucket string

		if atIndex >= 0 {
			// has data version in backup
			origAppName = bucket[0:atIndex]
			origDataVersion = bucket[atIndex + 1:colonIndex]
		} else {
			// no data version in backup
			origAppName = bucket[0:colonIndex]
		}

		bucketName = bucket[colonIndex + 1:]

		if len(*appName) == 0 {
			// no override for app name use original
			appName = &origAppName
		}
		resBucket = *appName

		if len(*dataVersion) > 0 {
			// has override for data version
			resBucket += "@" + *dataVersion
		} else if len(origDataVersion) > 0 {
			// has original data version either
			resBucket += "@" + origDataVersion
		}

		resBucket += ":" + bucketName

		return []byte(resBucket)
	}

	if len(*inputFile) == 0 {
		panic("missing input file")
	}

	f, err := os.Open(*inputFile)
	check(err)
	defer f.Close()
	input := bufio.NewReader(f)

	importation := riakdata.NewImport(cli, input, bucketOverride);

	cnt, err := importation.Run(prog)
	check(err)

	return cnt
}

func runExport(
	cli *riakcli.Client,
	appName *string,
	dataVersion *string,
	outputDir *string,
	prog chan float64,
) int {
	log.Printf("Start Exporting")

	if len(*appName) == 0 {
		panic("missing app name")
	}

	prefix := *appName

	if len(*dataVersion) > 0 {
		// has data version
		prefix += "@" + *dataVersion
	}

	f, err := os.Create(path.Join(
		*outputDir,
		prefix + "." + time.Now().Format(time.RFC3339) + ".bin",
	))
	check(err)
	defer f.Close()
	output := bufio.NewWriter(f)

	export := riakdata.NewExport(
		cli,
		func(k []byte) bool {
			match, err := regexp.MatchString("^" + prefix + ":.*$", string(k))

			return err == nil && match
		},
		[]string{"data"},
		[]string{"sets", "maps", "counters"},
		output,
	);

	cnt, err := export.Run(prog)
	check(err)

	return cnt
}
<|endoftext|>


<commit_before>package postman

import (
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/loadimpact/speedboat/lib"
	"golang.org/x/net/context"
	"strings"
)

type ErrorWithLineNumber struct {
	Wrapped error
	Line    int
}

func (e ErrorWithLineNumber) Error() string {
	return fmt.Sprintf("%s (line %d)", e.Wrapped.Error(), e.Line)
}

type Runner struct {
	Collection Collection
}

type VU struct {
	Runner *Runner
}

func New(source []byte) (*Runner, error) {
	var collection Collection
	if err := json.Unmarshal(source, &collection); err != nil {
		switch e := err.(type) {
		case *json.SyntaxError:
			src := string(source)
			line := strings.Count(src[:e.Offset], "\n") + 1
			return nil, ErrorWithLineNumber{Wrapped: e, Line: line}
		case *json.UnmarshalTypeError:
			src := string(source)
			line := strings.Count(src[:e.Offset], "\n") + 1
			return nil, ErrorWithLineNumber{Wrapped: e, Line: line}
		}
		return nil, err
	}

	return &Runner{
		Collection: collection,
	}, nil
}

func (r *Runner) NewVU() (lib.VU, error) {
	return &VU{Runner: r}, nil
}

func (u *VU) Reconfigure(id int64) error {
	return nil
}

func (u *VU) RunOnce(ctx context.Context) error {
	for _, item := range u.Runner.Collection.Item {
		if err := u.runItem(item, u.Runner.Collection.Auth); err != nil {
			return err
		}
	}

	return nil
}

func (u *VU) runItem(i Item, a Auth) error {
	if i.Auth.Type != "" {
		a = i.Auth
	}

	if i.Request.URL != "" {
		log.WithField("url", i.Request.URL).Info("Request!")
	}

	for _, item := range i.Item {
		if err := u.runItem(item, a); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>[feat] Postman runner can send reqs + log metrics<commit_after>package postman

import (
	"bytes"
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/loadimpact/speedboat/lib"
	"github.com/loadimpact/speedboat/stats"
	"golang.org/x/net/context"
	"io"
	"io/ioutil"
	"math"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"time"
)

var (
	mRequests = stats.Stat{Name: "requests", Type: stats.HistogramType, Intent: stats.TimeIntent}
	mErrors   = stats.Stat{Name: "errors", Type: stats.CounterType}
)

type ErrorWithLineNumber struct {
	Wrapped error
	Line    int
}

func (e ErrorWithLineNumber) Error() string {
	return fmt.Sprintf("%s (line %d)", e.Wrapped.Error(), e.Line)
}

type Runner struct {
	Collection Collection
}

type VU struct {
	Runner    *Runner
	Client    http.Client
	Collector *stats.Collector
}

func New(source []byte) (*Runner, error) {
	var collection Collection
	if err := json.Unmarshal(source, &collection); err != nil {
		switch e := err.(type) {
		case *json.SyntaxError:
			src := string(source)
			line := strings.Count(src[:e.Offset], "\n") + 1
			return nil, ErrorWithLineNumber{Wrapped: e, Line: line}
		case *json.UnmarshalTypeError:
			src := string(source)
			line := strings.Count(src[:e.Offset], "\n") + 1
			return nil, ErrorWithLineNumber{Wrapped: e, Line: line}
		}
		return nil, err
	}

	return &Runner{
		Collection: collection,
	}, nil
}

func (r *Runner) NewVU() (lib.VU, error) {
	return &VU{
		Runner: r,
		Client: http.Client{
			Transport: &http.Transport{
				MaxIdleConnsPerHost: math.MaxInt32,
			},
		},
		Collector: stats.NewCollector(),
	}, nil
}

func (u *VU) Reconfigure(id int64) error {
	return nil
}

func (u *VU) RunOnce(ctx context.Context) error {
	for _, item := range u.Runner.Collection.Item {
		if err := u.runItem(item, u.Runner.Collection.Auth); err != nil {
			return err
		}
	}

	return nil
}

func (u *VU) runItem(i Item, a Auth) error {
	if i.Auth.Type != "" {
		a = i.Auth
	}

	if i.Request.URL != "" {
		var buffer *bytes.Buffer
		switch i.Request.Body.Mode {
		case "raw":
			buffer = bytes.NewBufferString(i.Request.Body.Raw)
		case "formdata":
			buffer = &bytes.Buffer{}
			w := multipart.NewWriter(buffer)
			for _, field := range i.Request.Body.FormData {
				if !field.Enabled {
					continue
				}

				if err := w.WriteField(field.Key, field.Value); err != nil {
					return err
				}
			}
		case "urlencoded":
			v := make(url.Values)
			for _, field := range i.Request.Body.URLEncoded {
				if !field.Enabled {
					continue
				}
				v[field.Key] = append(v[field.Key], field.Value)
			}
			buffer = bytes.NewBufferString(v.Encode())
		}

		req, err := http.NewRequest(i.Request.Method, i.Request.URL, buffer)
		if err != nil {
			return err
		}

		startTime := time.Now()
		res, err := u.Client.Do(req)
		duration := time.Since(startTime)

		status := 0
		if err == nil {
			status = res.StatusCode
			io.Copy(ioutil.Discard, res.Body)
			res.Body.Close()
		}

		tags := stats.Tags{"method": i.Request.Method, "url": i.Request.URL, "status": status}
		u.Collector.Add(stats.Sample{
			Stat:   &mRequests,
			Tags:   tags,
			Values: stats.Values{"duration": float64(duration)},
		})

		if err != nil {
			log.WithError(err).Error("Request error")
			u.Collector.Add(stats.Sample{
				Stat:   &mErrors,
				Tags:   tags,
				Values: stats.Value(1),
			})
			return err
		}
	}

	for _, item := range i.Item {
		if err := u.runItem(item, a); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package premailer

import (
	"github.com/vanng822/css"
)

func copyRule(selector string, rule *css.CSSRule) *css.CSSRule {
	// copy rule for each selector
	copiedStyle := css.CSSStyleRule{SelectorText: selector, Styles: rule.Style.Styles}
	copiedRule := &css.CSSRule{Type: rule.Type, Style: copiedStyle}
	return copiedRule
}

func makeRuleImportant(rule *css.CSSRule) string {
	// this for using Text() which has nice sorted props
	for _, s := range rule.Style.Styles {
		s.Important = 1
	}
	return rule.Style.Text()
}<commit_msg>Copy rule entirely to avoid buggy in the future<commit_after>package premailer

import (
	"github.com/vanng822/css"
)

func copyRule(selector string, rule *css.CSSRule) *css.CSSRule {
	// copy rule for each selector
	styles := make(map[string]*css.CSSStyleDeclaration)
	for prop, s := range rule.Style.Styles {
		styles[prop] = css.NewCSSStyleDeclaration(s.Property, s.Value, s.Important)
	}
	copiedStyle := css.CSSStyleRule{SelectorText: selector, Styles: styles}
	copiedRule := &css.CSSRule{Type: rule.Type, Style: copiedStyle}
	return copiedRule
}

func makeRuleImportant(rule *css.CSSRule) string {
	// this for using Text() which has nice sorted props
	for _, s := range rule.Style.Styles {
		s.Important = 1
	}
	return rule.Style.Text()
}<|endoftext|>


<commit_before>/*
 * src/go/dnsresolvh.go
 * ============================================================================
 * DNS Resolver Daemon (dnsresolvd). Version 0.1
 * ============================================================================
 * A daemon that performs DNS lookups for the given hostname
 * passed in an HTTP request, with the focus on its implementation
 * using various programming languages. (net/http-boosted impl.)
 * ============================================================================
 * Copyright (C) 2017-2020 Radislav (Radicchio) Golubtsov
 *
 * (See the LICENSE file at the top of the source tree.)
 */

package main

import (
    "fmt"
    "log/syslog"
)

// Helper constants.
const (
    _EXIT_FAILURE     int    =    1 //    Failing exit status.
    _EXIT_SUCCESS     int    =    0 // Successful exit status.
    _EMPTY_STRING     string =   ""
    _COMMA_SPACE_SEP  string = ", "
    _NEW_LINE         string = "\n"
    _AMPER            string =  "&"
    _ONE_SPACE_STRING string =  " "
    _PRINT_BANNER_OPT string = "-V"
    _COLON            string =  ":"
)

// Common error messages.
const (
    _ERR_PORT_MUST_BE_POSITIVE_INT string = ": <port_number> must be "   +
                                            "a positive integer value, " +
                                            "in the range 1024-49151."
    _ERR_CANNOT_START_SERVER       string = ": FATAL: Cannot start server "
    _ERR_SRV_UNKNOWN_REASON        string = "for an unknown reason. Exiting..."
    _ERR_SRV_PORT_IS_IN_USE        string = "due to the port requested " +
                                            "is in use. Exiting..."
    _ERR_ADDR_ALREADY_IN_USE       string = "address already in use"
)

// Print this error message when there are no any args passed.
const (
    _ERR_MUST_BE_ONE_TWO_ARGS_1 string = ": There must be one or two args " +
                                         "passed: "
    _ERR_MUST_BE_ONE_TWO_ARGS_2 string = " args found"
)

// Print this usage info just after any inappropriate input.
const (
    _MSG_USAGE_TEMPLATE_1 string = "Usage: "
    _MSG_USAGE_TEMPLATE_2 string = " <port_number> [-V]"
)

// Constant: The minimum port number allowed.
const _MIN_PORT uint = 1024

// Constant: The maximum port number allowed.
const _MAX_PORT uint = 49151

// Common notification messages.
const _MSG_SERVER_STARTED_1 string = "Server started on port "
const _MSG_SERVER_STARTED_2 string = "=== Hit Ctrl+C to terminate it."

// HTTP request params.
const (
    _PRM_FMT_HTML string = "html"
    _PRM_FMT_JSON string = "json"
)

// HTTP response headers.
const (
    _HDR_CONTENT_TYPE_N      string = "Content-Type"
    _HDR_CONTENT_TYPE_V_HTML string = "text/html; charset=UTF-8"
    _HDR_CONTENT_TYPE_V_JSON string = "application/json"
    _HDR_CACHE_CONTROL_N     string = "Cache-Control"
    _HDR_CACHE_CONTROL_V     string = "no-cache, no-store, must-revalidate"
    _HDR_EXPIRES_N           string = "Expires"
    _HDR_EXPIRES_V           string = "Thu, 01 Dec 1994 16:00:00 GMT"
    _HDR_PRAGMA_N            string = "Pragma"
    _HDR_PRAGMA_V            string = "no-cache"
)

// Daemon name, version, and copyright banners.
const (
    _DMN_NAME        string = "DNS Resolver Daemon (dnsresolvd)"
    _DMN_DESCRIPTION string = "Performs DNS lookups for the given " +
                              "hostname passed in an HTTP request"
    _DMN_VERSION_S__ string = "Version"
    _DMN_VERSION     string = "0.1"
    _DMN_COPYRIGHT__ string = "Copyright (C) 2017-2020"
    _DMN_AUTHOR      string = "Radislav Golubtsov <ragolubtsov@my.com>"
)

// Constant: The default hostname to look up for.
const _DEF_HOSTNAME string = "openbsd.org"

// Helper function. Makes final buffer cleanups, closes streams, etc.
func _cleanups_fixate(log *syslog.Writer) {
    // Closing the system logger.
    log.Close()
}

// Helper function. Draws a horizontal separator banner.
func _separator_draw(banner_text string) {
    i := len(banner_text)

    for { fmt.Printf("%c", '='); i--; if (i == 0) { break } }; fmt.Println()
}

// vim:set nu et ts=4 sw=4:
<commit_msg>net/http/go: Add constants: response data names.<commit_after>/*
 * src/go/dnsresolvh.go
 * ============================================================================
 * DNS Resolver Daemon (dnsresolvd). Version 0.1
 * ============================================================================
 * A daemon that performs DNS lookups for the given hostname
 * passed in an HTTP request, with the focus on its implementation
 * using various programming languages. (net/http-boosted impl.)
 * ============================================================================
 * Copyright (C) 2017-2020 Radislav (Radicchio) Golubtsov
 *
 * (See the LICENSE file at the top of the source tree.)
 */

package main

import (
    "fmt"
    "log/syslog"
)

// Helper constants.
const (
    _EXIT_FAILURE     int    =    1 //    Failing exit status.
    _EXIT_SUCCESS     int    =    0 // Successful exit status.
    _EMPTY_STRING     string =   ""
    _COMMA_SPACE_SEP  string = ", "
    _NEW_LINE         string = "\n"
    _AMPER            string =  "&"
    _ONE_SPACE_STRING string =  " "
    _PRINT_BANNER_OPT string = "-V"
    _COLON            string =  ":"
)

// Common error messages.
const (
    _ERR_PREFIX                    string = "error"
    _ERR_PORT_MUST_BE_POSITIVE_INT string = ": <port_number> must be "   +
                                            "a positive integer value, " +
                                            "in the range 1024-49151."
    _ERR_CANNOT_START_SERVER       string = ": FATAL: Cannot start server "
    _ERR_SRV_UNKNOWN_REASON        string = "for an unknown reason. Exiting..."
    _ERR_SRV_PORT_IS_IN_USE        string = "due to the port requested " +
                                            "is in use. Exiting..."
    _ERR_ADDR_ALREADY_IN_USE       string = "address already in use"
)

// Print this error message when there are no any args passed.
const (
    _ERR_MUST_BE_ONE_TWO_ARGS_1 string = ": There must be one or two args " +
                                         "passed: "
    _ERR_MUST_BE_ONE_TWO_ARGS_2 string = " args found"
)

// Print this usage info just after any inappropriate input.
const (
    _MSG_USAGE_TEMPLATE_1 string = "Usage: "
    _MSG_USAGE_TEMPLATE_2 string = " <port_number> [-V]"
)

// Constant: The minimum port number allowed.
const _MIN_PORT uint = 1024

// Constant: The maximum port number allowed.
const _MAX_PORT uint = 49151

// Common notification messages.
const _MSG_SERVER_STARTED_1 string = "Server started on port "
const _MSG_SERVER_STARTED_2 string = "=== Hit Ctrl+C to terminate it."

// HTTP request params.
const (
    _PRM_FMT_HTML string = "html"
    _PRM_FMT_JSON string = "json"
)

// HTTP response headers.
const (
    _HDR_CONTENT_TYPE_N      string = "Content-Type"
    _HDR_CONTENT_TYPE_V_HTML string = "text/html; charset=UTF-8"
    _HDR_CONTENT_TYPE_V_JSON string = "application/json"
    _HDR_CACHE_CONTROL_N     string = "Cache-Control"
    _HDR_CACHE_CONTROL_V     string = "no-cache, no-store, must-revalidate"
    _HDR_EXPIRES_N           string = "Expires"
    _HDR_EXPIRES_V           string = "Thu, 01 Dec 1994 16:00:00 GMT"
    _HDR_PRAGMA_N            string = "Pragma"
    _HDR_PRAGMA_V            string = "no-cache"
)

// Response data names.
const (
    _DAT_HOSTNAME_N string = "hostname"
    _DAT_ADDRESS_N  string = "address"
    _DAT_VERSION_N  string = "version"
    _DAT_VERSION_V  string = "IPv"
)

// Daemon name, version, and copyright banners.
const (
    _DMN_NAME        string = "DNS Resolver Daemon (dnsresolvd)"
    _DMN_DESCRIPTION string = "Performs DNS lookups for the given " +
                              "hostname passed in an HTTP request"
    _DMN_VERSION_S__ string = "Version"
    _DMN_VERSION     string = "0.1"
    _DMN_COPYRIGHT__ string = "Copyright (C) 2017-2020"
    _DMN_AUTHOR      string = "Radislav Golubtsov <ragolubtsov@my.com>"
)

// Constant: The default hostname to look up for.
const _DEF_HOSTNAME string = "openbsd.org"

// Helper function. Makes final buffer cleanups, closes streams, etc.
func _cleanups_fixate(log *syslog.Writer) {
    // Closing the system logger.
    log.Close()
}

// Helper function. Draws a horizontal separator banner.
func _separator_draw(banner_text string) {
    i := len(banner_text)

    for { fmt.Printf("%c", '='); i--; if (i == 0) { break } }; fmt.Println()
}

// vim:set nu et ts=4 sw=4:
<|endoftext|>


<commit_before>package gumble

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"io"
	"net"
	"time"
)

type PingResponse struct {
	address        *net.UDPAddr
	version        Version
	connectedUsers int
	maximumUsers   int
	maximumBitrate int
}

// Address returns the the address of the pinged server.
func (pr *PingResponse) Address() *net.UDPAddr {
	return pr.address
}

// Version returns the server's version. Only the .Version() and
// .SemanticVersion() methods of the returned value will have valid values.
func (pr *PingResponse) Version() Version {
	return pr.version
}

// ConnectedUsers returns the number users currently connected to the server.
func (pr *PingResponse) ConnectedUsers() int {
	return pr.connectedUsers
}

// MaximumUsers returns the maximum number of users that can connect to the
// server.
func (pr *PingResponse) MaximumUsers() int {
	return pr.maximumUsers
}

// MaximumBitrate returns the maximum audio bitrate per user for the server.
func (pr *PingResponse) MaximumBitrate() int {
	return pr.maximumBitrate
}

// Ping sends a UDP ping packet to the given server. Returns a PingResponse and
// nil on success. The function will return nil and an error if a valid
// response is not received after the given timeout.
func Ping(address string, timeout time.Duration) (*PingResponse, error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, err
	}
	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, err
	}

	var packet bytes.Buffer
	var id [8]byte
	if err := binary.Write(&packet, binary.BigEndian, int32(0)); err != nil {
		return nil, err
	}
	if _, err := rand.Read(id[:]); err != nil {
		return nil, err
	}
	if _, err := packet.Write(id[:]); err != nil {
		return nil, err
	}
	if _, err := packet.WriteTo(conn); err != nil {
		return nil, err
	}

	conn.SetReadDeadline(time.Now().Add(timeout))
	for {
		var incoming [24]byte
		if _, err := io.ReadFull(conn, incoming[:]); err != nil {
			return nil, err
		}
		if !bytes.Equal(incoming[4:12], id[:]) {
			continue
		}

		return &PingResponse{
			address: addr,
			version: Version{
				version: binary.BigEndian.Uint32(incoming[0:]),
			},
			connectedUsers: int(binary.BigEndian.Uint32(incoming[12:])),
			maximumUsers:   int(binary.BigEndian.Uint32(incoming[16:])),
			maximumBitrate: int(binary.BigEndian.Uint32(incoming[20:])),
		}, nil
	}
}
<commit_msg>simplify Ping function<commit_after>package gumble

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"io"
	"net"
	"time"
)

type PingResponse struct {
	address        *net.UDPAddr
	version        Version
	connectedUsers int
	maximumUsers   int
	maximumBitrate int
}

// Address returns the the address of the pinged server.
func (pr *PingResponse) Address() *net.UDPAddr {
	return pr.address
}

// Version returns the server's version. Only the .Version() and
// .SemanticVersion() methods of the returned value will have valid values.
func (pr *PingResponse) Version() Version {
	return pr.version
}

// ConnectedUsers returns the number users currently connected to the server.
func (pr *PingResponse) ConnectedUsers() int {
	return pr.connectedUsers
}

// MaximumUsers returns the maximum number of users that can connect to the
// server.
func (pr *PingResponse) MaximumUsers() int {
	return pr.maximumUsers
}

// MaximumBitrate returns the maximum audio bitrate per user for the server.
func (pr *PingResponse) MaximumBitrate() int {
	return pr.maximumBitrate
}

// Ping sends a UDP ping packet to the given server. Returns a PingResponse and
// nil on success. The function will return nil and an error if a valid
// response is not received after the given timeout.
func Ping(address string, timeout time.Duration) (*PingResponse, error) {
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, err
	}
	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, err
	}

	var packet [12]byte
	if _, err := rand.Read(packet[4:]); err != nil {
		return nil, err
	}
	if _, err := conn.Write(packet[:]); err != nil {
		return nil, err
	}

	conn.SetReadDeadline(time.Now().Add(timeout))
	for {
		var incoming [24]byte
		if _, err := io.ReadFull(conn, incoming[:]); err != nil {
			return nil, err
		}
		if !bytes.Equal(incoming[4:12], packet[4:]) {
			continue
		}

		return &PingResponse{
			address: addr,
			version: Version{
				version: binary.BigEndian.Uint32(incoming[0:]),
			},
			connectedUsers: int(binary.BigEndian.Uint32(incoming[12:])),
			maximumUsers:   int(binary.BigEndian.Uint32(incoming[16:])),
			maximumBitrate: int(binary.BigEndian.Uint32(incoming[20:])),
		}, nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 FactomProject Authors. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package process

import (
	"errors"
	"github.com/FactomProject/FactomCode/common"
	"github.com/FactomProject/FactomCode/database"
	"github.com/FactomProject/FactomCode/util"
	"github.com/FactomProject/btcd/wire"
	"github.com/davecgh/go-spew/spew"
	"strconv"
	"time"
)

// processDirBlock validates dir block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processDirBlock(msg *wire.MsgDirBlock) error {

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	blk, _ := db.FetchDBlockByHeight(msg.DBlk.Header.DBHeight)
	if blk != nil {
		procLog.Info("DBlock already existing for height:" + string(msg.DBlk.Header.DBHeight))
		return nil
	}

	msg.DBlk.IsSealed = true
	dchain.AddDBlockToDChain(msg.DBlk)

	//Add it to mem pool before saving it in db
	fMemPool.addBlockMsg(msg, strconv.Itoa(int(msg.DBlk.Header.DBHeight))) // store in mempool with the height as the key

	procLog.Debugf("SyncUp: MsgDirBlock=%s\n", spew.Sdump(msg.DBlk))

	return nil
}

// processFBlock validates admin block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processFBlock(msg *wire.MsgFBlock) error {

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	key, _ := msg.SC.GetHash().MarshalText()
	//Add it to mem pool before saving it in db
	fMemPool.addBlockMsg(msg, string(key)) // stored in mem pool with the MR as the key

	procLog.Debugf("SyncUp: MsgFBlock=%s\n", spew.Sdump(msg.SC))

	return nil

}

// processABlock validates admin block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processABlock(msg *wire.MsgABlock) error {
	util.Trace()

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	//Add it to mem pool before saving it in db
	msg.ABlk.BuildABHash()
	fMemPool.addBlockMsg(msg, msg.ABlk.ABHash.String()) // store in mem pool with ABHash as key

	return nil
}

// procesFBlock validates entry credit block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func procesECBlock(msg *wire.MsgECBlock) error {
	util.Trace()

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	//Add it to mem pool before saving it in db
	fMemPool.addBlockMsg(msg, msg.ECBlock.Header.KeyMR().String())

	// for debugging??
	procLog.Debugf("SyncUp: msg.ECBlock.Header.KeyMR().String()=%s\n", msg.ECBlock.Header.KeyMR().String())
	procLog.Debugf("SyncUp: MsgCBlock=%s\n", spew.Sdump(msg.ECBlock))

	return nil
}

// processEBlock validates entry block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processEBlock(msg *wire.MsgEBlock) error {
	util.Trace()

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}
	/*
		if msg.EBlk.Header.DBHeight >= dchain.NextBlockHeight || msg.EBlk.Header.DBHeight < 0 {
			return errors.New("MsgEBlock has an invalid DBHeight:" + strconv.Itoa(int(msg.EBlk.Header.DBHeight)))
		}
	*/
	//Add it to mem pool before saving it in db
	msg.EBlk.BuildMerkleRoot()
	fMemPool.addBlockMsg(msg, msg.EBlk.MerkleRoot.String()) // store it in mem pool with MR as the key

	// for debugging??
	procLog.Debugf("SyncUp: MsgEBlock=%s\n", spew.Sdump(msg.EBlk))

	return nil
}

// processEntry validates entry and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processEntry(msg *wire.MsgEntry) error {
	util.Trace()

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	// store the entry in mem pool
	h := msg.Entry.Hash()
	fMemPool.addBlockMsg(msg, h.String()) // store it in mem pool with hash as the key

	procLog.Debugf("SyncUp: MsgEntry=%s\n", spew.Sdump(msg.Entry))

	return nil
}

// Validate the new blocks in mem pool and store them in db
func validateAndStoreBlocks(fMemPool *ftmMemPool, db database.Db, dchain *common.DChain, outCtlMsgQ chan wire.FtmInternalMsg) {
	var myDBHeight int64
	var sleeptime int
	var dblk *common.DirectoryBlock

	for true {
		dblk = nil
		_, myDBHeight, _ = db.FetchBlockHeightCache()

		adj := (len(dchain.Blocks) - int(myDBHeight))
		if adj <= 0 {
			adj = 1
		}
		// in milliseconds
		sleeptime = 100 + 1000/adj

		if len(dchain.Blocks) > int(myDBHeight+1) {
			dblk = dchain.Blocks[myDBHeight+1]
		}
		if dblk != nil {
			if validateBlocksFromMemPool(dblk, fMemPool, db) {
				err := storeBlocksFromMemPool(dblk, fMemPool, db)
				if err == nil {
					deleteBlocksFromMemPool(dblk, fMemPool)
				} else {
					panic("error in deleteBlocksFromMemPool.")
				}
			} else {
				time.Sleep(time.Duration(sleeptime * 1000000)) // Nanoseconds for duration
			}
		} else {
			time.Sleep(time.Duration(sleeptime * 1000000)) // Nanoseconds for duration

			//send an internal msg to sync up with peers
			// ??
		}

	}

}

// Validate the new blocks in mem pool and store them in db
func validateBlocksFromMemPool(b *common.DirectoryBlock, fMemPool *ftmMemPool, db database.Db) bool {

	// Validate the genesis block
	if b.Header.DBHeight == 0 {
		h, _ := common.CreateHash(b)
		if h.String() != common.GENESIS_DIR_BLOCK_HASH {
			// panic for milestone 1
			panic("Genesis dir block is not as expected: " + h.String())
		}
	}

	for _, dbEntry := range b.DBEntries {
		switch dbEntry.ChainID.String() {
		case ecchain.ChainID.String():
			if _, ok := fMemPool.blockpool[dbEntry.KeyMR.String()]; !ok {
				return false
			}
		case achain.ChainID.String():
			if msg, ok := fMemPool.blockpool[dbEntry.KeyMR.String()]; !ok {
				return false
			} else {
				// validate signature of the previous dir block
				aBlkMsg, _ := msg.(*wire.MsgABlock)
				if !validateDBSignature(aBlkMsg.ABlk, dchain) {
					return false
				}
			}
		case fchain.ChainID.String():
			if _, ok := fMemPool.blockpool[dbEntry.KeyMR.String()]; !ok {
				return false
			}
		default:
			if msg, ok := fMemPool.blockpool[dbEntry.KeyMR.String()]; !ok {
				return false
			} else {
				eBlkMsg, _ := msg.(*wire.MsgEBlock)
				// validate every entry in EBlock
				for _, ebEntry := range eBlkMsg.EBlk.EBEntries {
					if _, foundInMemPool := fMemPool.blockpool[ebEntry.EntryHash.String()]; !foundInMemPool {
						// continue if the entry arleady exists in db
						entry, _ := db.FetchEntryByHash(ebEntry.EntryHash)
						if entry == nil {
							return false
						}
					}
				}
			}
		}
	}

	return true
}

// Validate the new blocks in mem pool and store them in db
// Need to make a batch insert in db in milestone 2
func storeBlocksFromMemPool(b *common.DirectoryBlock, fMemPool *ftmMemPool, db database.Db) error {

	for _, dbEntry := range b.DBEntries {
		switch dbEntry.ChainID.String() {
		case ecchain.ChainID.String():
			ecBlkMsg := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgECBlock)
			err := db.ProcessECBlockBatch(ecBlkMsg.ECBlock)
			if err != nil {
				return err
			}
			// needs to be improved??
			initializeECreditMap(ecBlkMsg.ECBlock)
			// for debugging
			exportECBlock(ecBlkMsg.ECBlock)
		case achain.ChainID.String():
			aBlkMsg := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgABlock)
			err := db.ProcessABlockBatch(aBlkMsg.ABlk)
			if err != nil {
				return err
			}
			// for debugging
			exportABlock(aBlkMsg.ABlk)
		case fchain.ChainID.String():
			fBlkMsg := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgFBlock)
			err := db.ProcessFBlockBatch(fBlkMsg.SC)
			if err != nil {
				return err
			}
			// Initialize the Factoid State
			err = common.FactoidState.AddTransactionBlock(fBlkMsg.SC)
			if err != nil {
				panic("Failed to rebuild factoid state: " + err.Error())
			}

			// for debugging
			exportFctBlock(fBlkMsg.SC)
		default:
			// handle Entry Block
			eBlkMsg, _ := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgEBlock)
			// store entry in db first
			for _, ebEntry := range eBlkMsg.EBlk.EBEntries {
				if msg, foundInMemPool := fMemPool.blockpool[ebEntry.EntryHash.String()]; foundInMemPool {
					err := db.InsertEntry(msg.(*wire.MsgEntry).Entry)
					if err != nil {
						return err
					}
				}
			}
			// Store Entry Block in db
			err := db.ProcessEBlockBatch(eBlkMsg.EBlk)
			if err != nil {
				return err
			}

			// create a chain in db if it's not existing
			chain := chainIDMap[eBlkMsg.EBlk.Header.ChainID.String()]
			if chain == nil {
				chain = new(common.EChain)
				chain.ChainID = eBlkMsg.EBlk.Header.ChainID
				if eBlkMsg.EBlk.Header.EBHeight == 0 {
					chain.FirstEntry, _ = db.FetchEntryByHash(eBlkMsg.EBlk.EBEntries[0].EntryHash)
				}
				db.InsertChain(chain)
				chainIDMap[chain.ChainID.String()] = chain
			} else if chain.FirstEntry == nil && eBlkMsg.EBlk.Header.EBHeight == 0 {
				chain.FirstEntry, _ = db.FetchEntryByHash(eBlkMsg.EBlk.EBEntries[0].EntryHash)
				db.InsertChain(chain)
			}

			// for debugging
			exportEBlock(eBlkMsg.EBlk)
		}
	}

	// Store the dir block
	err := db.ProcessDBlockBatch(b)
	if err != nil {
		return err
	}

	// Update dir block height cache in db
	commonHash, _ := common.CreateHash(b)
	db.UpdateBlockHeightCache(b.Header.DBHeight, commonHash)

	// for debugging
	exportDBlock(b)

	return nil
}

// Validate the new blocks in mem pool and store them in db
func deleteBlocksFromMemPool(b *common.DirectoryBlock, fMemPool *ftmMemPool) error {

	for _, dbEntry := range b.DBEntries {
		switch dbEntry.ChainID.String() {
		case ecchain.ChainID.String():
			delete(fMemPool.blockpool, dbEntry.KeyMR.String())
		case achain.ChainID.String():
			delete(fMemPool.blockpool, dbEntry.KeyMR.String())
		case fchain.ChainID.String():
			delete(fMemPool.blockpool, dbEntry.KeyMR.String())
		default:
			eBlkMsg, _ := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgEBlock)
			for _, ebEntry := range eBlkMsg.EBlk.EBEntries {
				delete(fMemPool.blockpool, ebEntry.EntryHash.String())
			}
			delete(fMemPool.blockpool, dbEntry.KeyMR.String())
		}
	}
	delete(fMemPool.blockpool, strconv.Itoa(int(b.Header.DBHeight)))

	return nil
}

func validateDBSignature(aBlock *common.AdminBlock, dchain *common.DChain) bool {

	dbSigEntry := aBlock.GetDBSignature()
	if dbSigEntry == nil {
		if aBlock.Header.DBHeight == 0 {
			return true
		} else {
			return false
		}
	} else {
		dbSig := dbSigEntry.(*common.DBSignatureEntry)
		if serverPubKey.String() != dbSig.PubKey.String() {
			return false
		} else {
			// obtain the previous directory block
			dblk := dchain.Blocks[aBlock.Header.DBHeight-1]
			if dblk == nil {
				return false
			} else {
				// validatet the signature
				bHeader, _ := dblk.Header.MarshalBinary()
				if !serverPubKey.Verify(bHeader, dbSig.PrevDBSig) {
					procLog.Infof("No valid signature found in Admin Block = %s\n", spew.Sdump(aBlock))
					return false
				}
			}
		}
	}

	return true
}
<commit_msg>Small bug fix<commit_after>// Copyright 2015 FactomProject Authors. All rights reserved.
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package process

import (
	"errors"
	"github.com/FactomProject/FactomCode/common"
	"github.com/FactomProject/FactomCode/database"
	"github.com/FactomProject/FactomCode/util"
	"github.com/FactomProject/btcd/wire"
	"github.com/davecgh/go-spew/spew"
	"strconv"
	"time"
)

// processDirBlock validates dir block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processDirBlock(msg *wire.MsgDirBlock) error {

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	blk, _ := db.FetchDBlockByHeight(msg.DBlk.Header.DBHeight)
	if blk != nil {
		procLog.Info("DBlock already existing for height:" + string(msg.DBlk.Header.DBHeight))
		return nil
	}

	msg.DBlk.IsSealed = true
	dchain.AddDBlockToDChain(msg.DBlk)

	//Add it to mem pool before saving it in db
	fMemPool.addBlockMsg(msg, strconv.Itoa(int(msg.DBlk.Header.DBHeight))) // store in mempool with the height as the key

	procLog.Debugf("SyncUp: MsgDirBlock=%s\n", spew.Sdump(msg.DBlk))

	return nil
}

// processFBlock validates admin block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processFBlock(msg *wire.MsgFBlock) error {

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	key, _ := msg.SC.GetHash().MarshalText()
	//Add it to mem pool before saving it in db
	fMemPool.addBlockMsg(msg, string(key)) // stored in mem pool with the MR as the key

	procLog.Debugf("SyncUp: MsgFBlock=%s\n", spew.Sdump(msg.SC))

	return nil

}

// processABlock validates admin block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processABlock(msg *wire.MsgABlock) error {
	util.Trace()

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	//Add it to mem pool before saving it in db
	msg.ABlk.BuildABHash()
	fMemPool.addBlockMsg(msg, msg.ABlk.ABHash.String()) // store in mem pool with ABHash as key

	return nil
}

// procesFBlock validates entry credit block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func procesECBlock(msg *wire.MsgECBlock) error {
	util.Trace()

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	//Add it to mem pool before saving it in db
	fMemPool.addBlockMsg(msg, msg.ECBlock.Header.Hash().String())

	// for debugging??
	procLog.Debugf("SyncUp: msg.ECBlock.Header.Hash().String()=%s\n", msg.ECBlock.Header.Hash().String())
	procLog.Debugf("SyncUp: MsgCBlock=%s\n", spew.Sdump(msg.ECBlock))

	return nil
}

// processEBlock validates entry block and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processEBlock(msg *wire.MsgEBlock) error {
	util.Trace()

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}
	/*
		if msg.EBlk.Header.DBHeight >= dchain.NextBlockHeight || msg.EBlk.Header.DBHeight < 0 {
			return errors.New("MsgEBlock has an invalid DBHeight:" + strconv.Itoa(int(msg.EBlk.Header.DBHeight)))
		}
	*/
	//Add it to mem pool before saving it in db
	msg.EBlk.BuildMerkleRoot()
	fMemPool.addBlockMsg(msg, msg.EBlk.MerkleRoot.String()) // store it in mem pool with MR as the key

	// for debugging??
	procLog.Debugf("SyncUp: MsgEBlock=%s\n", spew.Sdump(msg.EBlk))

	return nil
}

// processEntry validates entry and save it to factom db.
// similar to blockChain.BC_ProcessBlock
func processEntry(msg *wire.MsgEntry) error {
	util.Trace()

	// Error condiftion for Milestone 1
	if nodeMode == common.SERVER_NODE {
		return errors.New("Server received msg:" + msg.Command())
	}

	// store the entry in mem pool
	h := msg.Entry.Hash()
	fMemPool.addBlockMsg(msg, h.String()) // store it in mem pool with hash as the key

	procLog.Debugf("SyncUp: MsgEntry=%s\n", spew.Sdump(msg.Entry))

	return nil
}

// Validate the new blocks in mem pool and store them in db
func validateAndStoreBlocks(fMemPool *ftmMemPool, db database.Db, dchain *common.DChain, outCtlMsgQ chan wire.FtmInternalMsg) {
	var myDBHeight int64
	var sleeptime int
	var dblk *common.DirectoryBlock

	for true {
		dblk = nil
		_, myDBHeight, _ = db.FetchBlockHeightCache()

		adj := (len(dchain.Blocks) - int(myDBHeight))
		if adj <= 0 {
			adj = 1
		}
		// in milliseconds
		sleeptime = 100 + 1000/adj

		if len(dchain.Blocks) > int(myDBHeight+1) {
			dblk = dchain.Blocks[myDBHeight+1]
		}
		if dblk != nil {
			if validateBlocksFromMemPool(dblk, fMemPool, db) {
				err := storeBlocksFromMemPool(dblk, fMemPool, db)
				if err == nil {
					deleteBlocksFromMemPool(dblk, fMemPool)
				} else {
					panic("error in deleteBlocksFromMemPool.")
				}
			} else {
				time.Sleep(time.Duration(sleeptime * 1000000)) // Nanoseconds for duration
			}
		} else {
			time.Sleep(time.Duration(sleeptime * 1000000)) // Nanoseconds for duration

			//send an internal msg to sync up with peers
			// ??
		}

	}

}

// Validate the new blocks in mem pool and store them in db
func validateBlocksFromMemPool(b *common.DirectoryBlock, fMemPool *ftmMemPool, db database.Db) bool {

	// Validate the genesis block
	if b.Header.DBHeight == 0 {
		h, _ := common.CreateHash(b)
		if h.String() != common.GENESIS_DIR_BLOCK_HASH {
			// panic for milestone 1
			panic("Genesis dir block is not as expected: " + h.String())
		}
	}

	for _, dbEntry := range b.DBEntries {
		switch dbEntry.ChainID.String() {
		case ecchain.ChainID.String():
			if _, ok := fMemPool.blockpool[dbEntry.KeyMR.String()]; !ok {
				return false
			}
		case achain.ChainID.String():
			if msg, ok := fMemPool.blockpool[dbEntry.KeyMR.String()]; !ok {
				return false
			} else {
				// validate signature of the previous dir block
				aBlkMsg, _ := msg.(*wire.MsgABlock)
				if !validateDBSignature(aBlkMsg.ABlk, dchain) {
					return false
				}
			}
		case fchain.ChainID.String():
			if _, ok := fMemPool.blockpool[dbEntry.KeyMR.String()]; !ok {
				return false
			}
		default:
			if msg, ok := fMemPool.blockpool[dbEntry.KeyMR.String()]; !ok {
				return false
			} else {
				eBlkMsg, _ := msg.(*wire.MsgEBlock)
				// validate every entry in EBlock
				for _, ebEntry := range eBlkMsg.EBlk.EBEntries {
					if _, foundInMemPool := fMemPool.blockpool[ebEntry.EntryHash.String()]; !foundInMemPool {
						// continue if the entry arleady exists in db
						entry, _ := db.FetchEntryByHash(ebEntry.EntryHash)
						if entry == nil {
							return false
						}
					}
				}
			}
		}
	}

	return true
}

// Validate the new blocks in mem pool and store them in db
// Need to make a batch insert in db in milestone 2
func storeBlocksFromMemPool(b *common.DirectoryBlock, fMemPool *ftmMemPool, db database.Db) error {

	for _, dbEntry := range b.DBEntries {
		switch dbEntry.ChainID.String() {
		case ecchain.ChainID.String():
			ecBlkMsg := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgECBlock)
			err := db.ProcessECBlockBatch(ecBlkMsg.ECBlock)
			if err != nil {
				return err
			}
			// needs to be improved??
			initializeECreditMap(ecBlkMsg.ECBlock)
			// for debugging
			exportECBlock(ecBlkMsg.ECBlock)
		case achain.ChainID.String():
			aBlkMsg := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgABlock)
			err := db.ProcessABlockBatch(aBlkMsg.ABlk)
			if err != nil {
				return err
			}
			// for debugging
			exportABlock(aBlkMsg.ABlk)
		case fchain.ChainID.String():
			fBlkMsg := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgFBlock)
			err := db.ProcessFBlockBatch(fBlkMsg.SC)
			if err != nil {
				return err
			}
			// Initialize the Factoid State
			err = common.FactoidState.AddTransactionBlock(fBlkMsg.SC)
			if err != nil {
				panic("Failed to rebuild factoid state: " + err.Error())
			}

			// for debugging
			exportFctBlock(fBlkMsg.SC)
		default:
			// handle Entry Block
			eBlkMsg, _ := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgEBlock)
			// store entry in db first
			for _, ebEntry := range eBlkMsg.EBlk.EBEntries {
				if msg, foundInMemPool := fMemPool.blockpool[ebEntry.EntryHash.String()]; foundInMemPool {
					err := db.InsertEntry(msg.(*wire.MsgEntry).Entry)
					if err != nil {
						return err
					}
				}
			}
			// Store Entry Block in db
			err := db.ProcessEBlockBatch(eBlkMsg.EBlk)
			if err != nil {
				return err
			}

			// create a chain in db if it's not existing
			chain := chainIDMap[eBlkMsg.EBlk.Header.ChainID.String()]
			if chain == nil {
				chain = new(common.EChain)
				chain.ChainID = eBlkMsg.EBlk.Header.ChainID
				if eBlkMsg.EBlk.Header.EBHeight == 0 {
					chain.FirstEntry, _ = db.FetchEntryByHash(eBlkMsg.EBlk.EBEntries[0].EntryHash)
				}
				db.InsertChain(chain)
				chainIDMap[chain.ChainID.String()] = chain
			} else if chain.FirstEntry == nil && eBlkMsg.EBlk.Header.EBHeight == 0 {
				chain.FirstEntry, _ = db.FetchEntryByHash(eBlkMsg.EBlk.EBEntries[0].EntryHash)
				db.InsertChain(chain)
			}

			// for debugging
			exportEBlock(eBlkMsg.EBlk)
		}
	}

	// Store the dir block
	err := db.ProcessDBlockBatch(b)
	if err != nil {
		return err
	}

	// Update dir block height cache in db
	commonHash, _ := common.CreateHash(b)
	db.UpdateBlockHeightCache(b.Header.DBHeight, commonHash)

	// for debugging
	exportDBlock(b)

	return nil
}

// Validate the new blocks in mem pool and store them in db
func deleteBlocksFromMemPool(b *common.DirectoryBlock, fMemPool *ftmMemPool) error {

	for _, dbEntry := range b.DBEntries {
		switch dbEntry.ChainID.String() {
		case ecchain.ChainID.String():
			delete(fMemPool.blockpool, dbEntry.KeyMR.String())
		case achain.ChainID.String():
			delete(fMemPool.blockpool, dbEntry.KeyMR.String())
		case fchain.ChainID.String():
			delete(fMemPool.blockpool, dbEntry.KeyMR.String())
		default:
			eBlkMsg, _ := fMemPool.blockpool[dbEntry.KeyMR.String()].(*wire.MsgEBlock)
			for _, ebEntry := range eBlkMsg.EBlk.EBEntries {
				delete(fMemPool.blockpool, ebEntry.EntryHash.String())
			}
			delete(fMemPool.blockpool, dbEntry.KeyMR.String())
		}
	}
	delete(fMemPool.blockpool, strconv.Itoa(int(b.Header.DBHeight)))

	return nil
}

func validateDBSignature(aBlock *common.AdminBlock, dchain *common.DChain) bool {

	dbSigEntry := aBlock.GetDBSignature()
	if dbSigEntry == nil {
		if aBlock.Header.DBHeight == 0 {
			return true
		} else {
			return false
		}
	} else {
		dbSig := dbSigEntry.(*common.DBSignatureEntry)
		if serverPubKey.String() != dbSig.PubKey.String() {
			return false
		} else {
			// obtain the previous directory block
			dblk := dchain.Blocks[aBlock.Header.DBHeight-1]
			if dblk == nil {
				return false
			} else {
				// validatet the signature
				bHeader, _ := dblk.Header.MarshalBinary()
				if !serverPubKey.Verify(bHeader, dbSig.PrevDBSig) {
					procLog.Infof("No valid signature found in Admin Block = %s\n", spew.Sdump(aBlock))
					return false
				}
			}
		}
	}

	return true
}
<|endoftext|>


<commit_before>package prepare

import (
	"fmt"
	"io"
	"path"

	uploadClient "github.com/webx-top/client/upload"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/code"
	"github.com/webx-top/echo/middleware/tplfunc"

	"github.com/admpub/nging/v4/application/library/common"
	modelFile "github.com/admpub/nging/v4/application/model/file"
	storerUtils "github.com/admpub/nging/v4/application/model/file/storer"
	"github.com/admpub/nging/v4/application/registry/upload"
	"github.com/admpub/nging/v4/application/registry/upload/checker"
	"github.com/admpub/nging/v4/application/registry/upload/dbsaver"
	"github.com/admpub/nging/v4/application/registry/upload/driver"
	"github.com/admpub/nging/v4/application/registry/upload/thumb"
)

type PrepareData struct {
	newStorer  driver.Constructor
	storer     driver.Storer
	StorerInfo storerUtils.Info
	DBSaver    dbsaver.DBSaver
	Checker    uploadClient.Checker
	Checkin    checker.Checker
	Subdir     string
	FileType   string
}

func (p *PrepareData) Storer(ctx echo.Context) (driver.Storer, error) {
	var err error
	if p.storer == nil {
		p.storer, err = p.newStorer(ctx, p.Subdir)
	}
	return p.storer, err
}

func (p *PrepareData) Close() error {
	if p.storer == nil {
		return nil
	}
	return p.storer.Close()
}

func (p *PrepareData) MakeModel(ctx echo.Context, ownerType string, ownerID uint64) *modelFile.File {
	fileM := modelFile.NewFile(ctx)
	fileM.StorerName = p.StorerInfo.Name
	fileM.StorerId = p.StorerInfo.ID
	fileM.OwnerId = ownerID
	fileM.OwnerType = ownerType
	fileM.Type = p.FileType
	fileM.Subdir = p.Subdir
	return fileM
}

func (p *PrepareData) MakeCallback(fileM *modelFile.File, storer driver.Storer, subdir string) func(*uploadClient.Result, io.Reader, io.Reader) error {
	ctx := fileM.Context()
	callback := func(result *uploadClient.Result, originalReader io.Reader, _ io.Reader) error {
		fileM.Id = 0
		fileM.SetByUploadResult(result)
		if err := ctx.Begin(); err != nil {
			return err
		}
		fileM.Use(common.Tx(ctx))
		err := p.DBSaver(fileM, result, originalReader)
		if err != nil {
			ctx.Rollback()
			return err
		}
		if result.FileType.String() != `image` {
			ctx.Commit()
			return nil
		}
		thumbSizes := thumb.Registry.Get(subdir).AutoCrop()
		if len(thumbSizes) > 0 {
			thumbM := modelFile.NewThumb(ctx)
			thumbM.CPAFrom(fileM.NgingFile)
			for _, thumbSize := range thumbSizes {
				thumbM.Reset()
				if seek, ok := originalReader.(io.Seeker); ok {
					seek.Seek(0, 0)
				}
				thumbURL := tplfunc.AddSuffix(result.FileURL, fmt.Sprintf(`_%v_%v`, thumbSize.Width, thumbSize.Height))
				cropOpt := &modelFile.CropOptions{
					Options:          modelFile.ImageOptions(thumbSize.Width, thumbSize.Height),
					File:             fileM.NgingFile,
					SrcReader:        originalReader,
					Storer:           storer,
					DestFile:         storer.URLToFile(thumbURL),
					FileMD5:          ``,
					WatermarkOptions: storerUtils.GetWatermarkOptions(),
				}
				err = thumbM.Crop(cropOpt)
				if err != nil {
					ctx.Rollback()
					return err
				}
			}
		}
		ctx.Commit()
		return nil
	}
	return callback
}

func (p *PrepareData) Save(fileM *modelFile.File, clientName string, clients ...uploadClient.Client) (client uploadClient.Client, err error) {
	ctx := fileM.Context()
	var result *uploadClient.Result
	if len(clients) == 0 || clients[0] == nil {
		result = &uploadClient.Result{
			FileType: uploadClient.FileType(p.FileType),
			FileName: ``,
		}
		client = NewClientWithModel(fileM, clientName, result)
	} else {
		result = client.GetUploadResult()
		if len(result.FileType) == 0 {
			result.FileType = uploadClient.FileType(p.FileType)
		}
	}
	var (
		subdir string
		name   string
		storer driver.Storer
	)
	subdir, name, err = p.Checkin(ctx)
	if err != nil {
		return
	}
	result.SetFileNameGenerator(func(filename string) (string, error) {
		return storerUtils.SaveFilename(subdir, name, filename)
	})
	storer, err = p.Storer(ctx)
	if err != nil {
		return
	}

	callback := p.MakeCallback(fileM, storer, subdir)

	optionsSetters := []uploadClient.OptionsSetter{
		uploadClient.OptClientName(clientName),
		uploadClient.OptResult(result),
		uploadClient.OptStorer(storer),
		uploadClient.OptWatermarkOptions(storerUtils.GetWatermarkOptions()),
		uploadClient.OptChecker(p.Checker),
		uploadClient.OptCallback(callback),
	}
	if clientName == `default` {
		client.BatchUpload(optionsSetters...)
	} else {
		client.Upload(optionsSetters...)
	}
	return
}

// Prepare 上传前的环境准备
func Prepare(ctx echo.Context, subdir string, fileType string, storerInfos ...storerUtils.Info) (*PrepareData, error) {
	if len(subdir) == 0 {
		subdir = `default`
	}
	if !upload.AllowedSubdir(subdir) {
		return nil, ctx.NewError(code.InvalidParameter, `subdir参数值“%s”未被登记`, subdir)
	}
	var storerInfo storerUtils.Info
	if len(storerInfos) > 0 {
		storerInfo = storerInfos[0]
	} else {
		storerInfo = storerUtils.Get()
	}
	//echo.Dump(ctx.Forms())
	newStore := driver.Get(storerInfo.Name)
	if newStore == nil {
		return nil, ctx.NewError(code.InvalidParameter, `存储引擎“%s”未被登记`, storerInfo.Name)
	}
	dbSaverFn := dbsaver.Get(subdir)
	checkerFn := func(r *uploadClient.Result) error {
		extension := path.Ext(r.FileName)
		if len(r.FileType) > 0 {
			if !uploadClient.CheckTypeExtension(fileType, extension) {
				return ctx.NewError(code.InvalidParameter, `不支持将扩展名为“%v”的文件作为“%v”类型的文件来进行上传`, extension, fileType)
			}
		} else {
			r.FileType = uploadClient.FileType(uploadClient.DetectType(extension))
		}
		return NopChecker(r)
	}
	data := &PrepareData{
		newStorer:  newStore,
		StorerInfo: storerInfo,
		DBSaver:    dbSaverFn,
		Checker:    checkerFn,
		Checkin:    checker.DefaultWithVerify,
		Subdir:     subdir,
		FileType:   fileType,
	}
	return data, nil
}
<commit_msg>update<commit_after>package prepare

import (
	"fmt"
	"io"
	"path"

	uploadClient "github.com/webx-top/client/upload"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/code"
	"github.com/webx-top/echo/middleware/tplfunc"

	"github.com/admpub/nging/v4/application/library/common"
	modelFile "github.com/admpub/nging/v4/application/model/file"
	storerUtils "github.com/admpub/nging/v4/application/model/file/storer"
	"github.com/admpub/nging/v4/application/registry/upload"
	"github.com/admpub/nging/v4/application/registry/upload/checker"
	"github.com/admpub/nging/v4/application/registry/upload/dbsaver"
	"github.com/admpub/nging/v4/application/registry/upload/driver"
	"github.com/admpub/nging/v4/application/registry/upload/thumb"
)

type PrepareData struct {
	newStorer  driver.Constructor
	storer     driver.Storer
	StorerInfo storerUtils.Info
	DBSaver    dbsaver.DBSaver
	Checker    uploadClient.Checker
	Checkin    checker.Checker
	Subdir     string
	FileType   string
}

func (p *PrepareData) Storer(ctx echo.Context) (driver.Storer, error) {
	var err error
	if p.storer == nil {
		p.storer, err = p.newStorer(ctx, p.Subdir)
	}
	return p.storer, err
}

func (p *PrepareData) Close() error {
	if p.storer == nil {
		return nil
	}
	return p.storer.Close()
}

func (p *PrepareData) MakeModel(ctx echo.Context, ownerType string, ownerID uint64) *modelFile.File {
	fileM := modelFile.NewFile(ctx)
	fileM.StorerName = p.StorerInfo.Name
	fileM.StorerId = p.StorerInfo.ID
	fileM.OwnerId = ownerID
	fileM.OwnerType = ownerType
	fileM.Type = p.FileType
	fileM.Subdir = p.Subdir
	return fileM
}

func (p *PrepareData) MakeCallback(fileM *modelFile.File, storer driver.Storer, subdir string) func(*uploadClient.Result, io.Reader, io.Reader) error {
	ctx := fileM.Context()
	callback := func(result *uploadClient.Result, originalReader io.Reader, _ io.Reader) error {
		fileM.Id = 0
		fileM.SetByUploadResult(result)
		if err := ctx.Begin(); err != nil {
			return err
		}
		fileM.Use(common.Tx(ctx))
		err := p.DBSaver(fileM, result, originalReader)
		if err != nil {
			ctx.Rollback()
			return err
		}
		if result.FileType.String() != `image` {
			ctx.Commit()
			return nil
		}
		thumbSizes := thumb.Registry.Get(subdir).AutoCrop()
		if len(thumbSizes) > 0 {
			thumbM := modelFile.NewThumb(ctx)
			thumbM.CPAFrom(fileM.NgingFile)
			for _, thumbSize := range thumbSizes {
				thumbM.Reset()
				if seek, ok := originalReader.(io.Seeker); ok {
					seek.Seek(0, 0)
				}
				thumbURL := tplfunc.AddSuffix(result.FileURL, fmt.Sprintf(`_%v_%v`, thumbSize.Width, thumbSize.Height))
				cropOpt := &modelFile.CropOptions{
					Options:          modelFile.ImageOptions(thumbSize.Width, thumbSize.Height),
					File:             fileM.NgingFile,
					SrcReader:        originalReader,
					Storer:           storer,
					DestFile:         storer.URLToFile(thumbURL),
					FileMD5:          ``,
					WatermarkOptions: storerUtils.GetWatermarkOptions(),
				}
				err = thumbM.Crop(cropOpt)
				if err != nil {
					ctx.Rollback()
					return err
				}
			}
		}
		ctx.Commit()
		return nil
	}
	return callback
}

func (p *PrepareData) Save(fileM *modelFile.File, clientName string, clients ...uploadClient.Client) (client uploadClient.Client, err error) {
	ctx := fileM.Context()
	var result *uploadClient.Result
	if len(clients) == 0 || clients[0] == nil {
		result = &uploadClient.Result{
			FileType: uploadClient.FileType(p.FileType),
			FileName: ``,
		}
		client = NewClientWithModel(fileM, clientName, result)
	} else {
		client = clients[0]
		result = client.GetUploadResult()
		if len(result.FileType) == 0 {
			result.FileType = uploadClient.FileType(p.FileType)
		}
	}
	var (
		subdir string
		name   string
		storer driver.Storer
	)
	subdir, name, err = p.Checkin(ctx)
	if err != nil {
		return
	}
	result.SetFileNameGenerator(func(filename string) (string, error) {
		return storerUtils.SaveFilename(subdir, name, filename)
	})
	storer, err = p.Storer(ctx)
	if err != nil {
		return
	}

	callback := p.MakeCallback(fileM, storer, subdir)

	optionsSetters := []uploadClient.OptionsSetter{
		uploadClient.OptClientName(clientName),
		uploadClient.OptResult(result),
		uploadClient.OptStorer(storer),
		uploadClient.OptWatermarkOptions(storerUtils.GetWatermarkOptions()),
		uploadClient.OptChecker(p.Checker),
		uploadClient.OptCallback(callback),
	}
	if clientName == `default` {
		client.BatchUpload(optionsSetters...)
	} else {
		client.Upload(optionsSetters...)
	}
	return
}

// Prepare 上传前的环境准备
func Prepare(ctx echo.Context, subdir string, fileType string, storerInfos ...storerUtils.Info) (*PrepareData, error) {
	if len(subdir) == 0 {
		subdir = `default`
	}
	if !upload.AllowedSubdir(subdir) {
		return nil, ctx.NewError(code.InvalidParameter, `subdir参数值“%s”未被登记`, subdir)
	}
	var storerInfo storerUtils.Info
	if len(storerInfos) > 0 {
		storerInfo = storerInfos[0]
	} else {
		storerInfo = storerUtils.Get()
	}
	//echo.Dump(ctx.Forms())
	newStore := driver.Get(storerInfo.Name)
	if newStore == nil {
		return nil, ctx.NewError(code.InvalidParameter, `存储引擎“%s”未被登记`, storerInfo.Name)
	}
	dbSaverFn := dbsaver.Get(subdir)
	checkerFn := func(r *uploadClient.Result) error {
		extension := path.Ext(r.FileName)
		if len(r.FileType) > 0 {
			if !uploadClient.CheckTypeExtension(fileType, extension) {
				return ctx.NewError(code.InvalidParameter, `不支持将扩展名为“%v”的文件作为“%v”类型的文件来进行上传`, extension, fileType)
			}
		} else {
			r.FileType = uploadClient.FileType(uploadClient.DetectType(extension))
		}
		return NopChecker(r)
	}
	data := &PrepareData{
		newStorer:  newStore,
		StorerInfo: storerInfo,
		DBSaver:    dbSaverFn,
		Checker:    checkerFn,
		Checkin:    checker.DefaultWithVerify,
		Subdir:     subdir,
		FileType:   fileType,
	}
	return data, nil
}
<|endoftext|>