<commit_before>// Copyright 2016 Sam Dukhovni
//
// Adapted from clyde.pl by cat@mit.edu
//
// Licensed under the MIT License
// (https://opensource.org/licenses/MIT)
//
//
// behavior.go defines combinator functions for writing chatbot
// behaviors, and provides a set of behaviors for Clyde.

package clyde

import (
	"log"
	"fmt"
	"strings"
	"strconv"
	"regexp"
	"math/rand"
	"bufio"
	"os"
	"path"
	"github.com/zephyr-im/zephyr-go"
	"github.com/sdukhovni/clyde-go/stringutil"
)

// Behavior represents a zephyrbot behavior. A Behavior takes a Clyde
// instance and an incoming zephyr, and either returns false to
// indicate that the behavior was not triggered by the message, or
// performs some action (possibly using or modifying the Clyde) and
// returns true to indicate that the behavior was triggered.
type Behavior func(*Clyde, zephyr.MessageReaderResult) bool

// StandardBehavior generates a Behavior following a standard pattern
// of triggering based on a case-insensitive regular expression in a
// zephyr body, reading some named capturing groups from the regexp
// match, possibly performing some action, and replying with a single
// zephyr (possibly generated using the markov chainer) either on the
// same class and instance as the incoming zephyr or on Clyde's home
// class.
func StandardBehavior(pattern string, keys []string, chain bool, resp func(*Clyde, zephyr.MessageReaderResult, map[string]string) string) Behavior {
	return func(c *Clyde, r zephyr.MessageReaderResult) bool {
		body := strings.Join(strings.Fields(r.Message.Body[1]), " ") // normalize spacing for regexp matches
		insPattern := fmt.Sprint("(?i)", pattern)
		rex := regexp.MustCompile(insPattern)
		match := rex.FindStringSubmatchIndex(body)
		if match == nil {
			return false
		}

		keyvals := make(map[string]string)
		for _, key := range keys {
			keyvals[key] = string(rex.ExpandString([]byte(""), fmt.Sprint("$", key), body, match))
		}

		response := resp(c, r, keyvals)
		if chain {
			response = c.Chain.Generate(response, sentenceCounts[rand.Intn(len(sentenceCounts))], maxWords)
		}

		class := r.Message.Header.Class
		instance := r.Message.Header.Instance
		if class != homeClass || instance != homeInstance {
			switch c.subs[class] {
			case 0, LISTEN:
				return true
			case REPLYHOME:
				if !strings.Contains(strings.ToLower(r.Message.Body[1]), "clyde") {
					class = homeClass
					instance = homeInstance
				}
			}
		}

		c.Send(class, instance, stringutil.BreakLines(response, stringutil.MaxLine))

		return true
	}
}

// maxWords is the maximum number of words that a behavior should
// generate using the markov chainer.
const maxWords = 100

// sentenceCounts is a set of sentence counts to request from the
// chainer; a number is chosen randomly from this list each time a
// number of sentences is needed.
var sentenceCounts = []int{1, 1, 1, 2, 2, 3}

// shortSender returns just the kerberos principal (with no realm) of
// the sender of a zephyr.
func shortSender(r zephyr.MessageReaderResult) string {
	return strings.Split(r.Message.Header.Sender, "@")[0]
}

// allLines returns a list of non-empty lines in a file in Clyde's
// home directory.
func allLines(c *Clyde, filename string) ([]string, error) {
	filepath := c.Path(filename)

	f, err := os.Open(filepath)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	defer f.Close()

	var lines []string
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		if line != "" {
			lines = append(lines, line)
		}
	}

	return lines, nil
}

// randomLine returns a random non-empty line from a file in Clyde's
// home directory.
func randomLine(c *Clyde, filename string) (string, error) {
	lines, err := allLines(c, filename)
	if err != nil {
		return "", err
	}
	return lines[rand.Intn(len(lines))], nil
}

// addLine adds a line to a file in Clyde's home directory.
func addLine(c *Clyde, filename, line string) error {
	filepath := c.Path(filename)

	f, err := os.OpenFile(filepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		log.Println(err)
		return err
	}
	defer f.Close()

	fmt.Fprintln(f, line)
	return nil
}

// Behaviors is a list of behaviors to be attempted in the order
// given.
var Behaviors = []Behavior{
	addActLike,
	actLike,
	addSub,
	checkSub,
	learnJob,
	story,
	fight,
	dice,
	chat,
}

var addActLike = StandardBehavior("clyde.? (?P<person>.+) says,? (\"(?P<phrase>[^\"]+)\".?|'(?P<phrase>[^']+)'.?|(?P<phrase>[^\"']+)|(?P<phrase>.+[\"'].+))$",
	[]string{"person", "phrase"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		alDir := c.Path("al")
		os.MkdirAll(alDir, 0755)
		filename := path.Join("al", stringutil.Escape(strings.ToLower(kvs["person"])))
		addLine(c, filename, kvs["phrase"])
		return "Ok!"
	})

var actLike = StandardBehavior("clyde.? ((please )?act like (?P<person>.*[^\\.\\?!])(?P<punc>.*?)$|what does (?P<person>.+) say)",
	[]string{"person", "punc"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		filename := path.Join("al", stringutil.Escape(strings.ToLower(kvs["person"])))
		phrase, err := randomLine(c, filename)
		if err != nil {
			filename = path.Join("al", stringutil.Escape(strings.ToLower(fmt.Sprint(kvs["person"], kvs["punc"]))))
			phrase, err = randomLine(c, filename)
			if err != nil {
				return fmt.Sprintf("I don't know how to act like %s.", kvs["person"])
			}
		}
		return phrase
	})

var addSub = StandardBehavior("clyde.*sub(scribe)? to (me|my class|(-c )?(?P<class>[^ !\\?]+[^ !\\?\\.]))",
	[]string{"class"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		class := kvs["class"]
		if class == "" {
			class = shortSender(r)
		}

		if r.Message.Header.Class != homeClass || r.Message.Header.Instance != homeInstance {
			return "I'm subbed to a lot of classes right now; maybe another time..."
		}

		if c.subs[class] != 0 {
			return fmt.Sprintf("I'm already subbed to -c %s!", class)
		}

		if r.AuthStatus != zephyr.AuthYes {
			return "You look sketchy, I don't trust you..."
		}

		c.Subscribe(class, REPLYHOME)
		return fmt.Sprintf("-c %s sounds awesome! Thanks for the invitation :)", class)
	})

var checkSub = StandardBehavior("are you (on|sub(scri)?bed to) (me|my class|(-c )?(?P<class>[^ !\\?]+[^ !\\?\\.]))",
	[]string{"class"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		class := kvs["class"]
		if class == "" {
			class = shortSender(r)
		}

		if c.subs[class] == 0 {
			return fmt.Sprintf("I'm not subbed to -c %s.", class)
		} else {
			return fmt.Sprintf("Yup, I'm subbed to -c %s! It's my favorite class :)", class)
		}
	})

var learnJob = StandardBehavior("clyde.? (?P<job>.+) is an? (job|profession|occupation)",
	[]string{"job"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		addLine(c, "jobs", kvs["job"])
		return "That's what I wanna be when I grow up!"
	})

var story = StandardBehavior("tell me a story",
	nil,
	true,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		job, _ := randomLine(c, "jobs")
		return fmt.Sprintf("Once upon a time, there was %s %s named %s who", stringutil.Article(job), job, shortSender(r))
	})

var fight = StandardBehavior("if (?P<fight1>.+) and (?P<fight2>.+) (fought|duell?ed|got in|were in|had)|(who|which|what) .* between (?P<fight1>.+) and (?P<fight2>.+[^,\\?])(\\?|$)|between (?P<fight1>.+) and (?P<fight2>.+[^,\\?]),? (who|which|what)",
	[]string{"fight1", "fight2"},
	true,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		var winner string
		switch rand.Intn(2) {
		case 0:
			winner = "fight1"
		case 1:
			winner = "fight2"
		}
		return fmt.Sprintf("I think %s would win, because", kvs[winner])
	})

var dice = StandardBehavior("( |^)(?P<count>[0-9]*)d(?P<faces>[0-9]+)",
	[]string{"count", "faces"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		count, _ := strconv.Atoi(kvs["count"])
		faces, _ := strconv.Atoi(kvs["faces"])
		total := 0
		for i := 0; i < count; i++ {
			total += rand.Intn(faces) + 1
		}
		return strconv.Itoa(total)
	})

var chat = StandardBehavior("clyde, (?P<topic>[^ ]+)",
	[]string{"topic"},
	true,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		return stringutil.Capitalize(kvs["topic"])
	})
<commit_msg>Add fortune-telling<commit_after>// Copyright 2016 Sam Dukhovni
//
// Adapted from clyde.pl by cat@mit.edu
//
// Licensed under the MIT License
// (https://opensource.org/licenses/MIT)
//
//
// behavior.go defines combinator functions for writing chatbot
// behaviors, and provides a set of behaviors for Clyde.

package clyde

import (
	"log"
	"fmt"
	"strings"
	"strconv"
	"regexp"
	"math/rand"
	"bufio"
	"os"
	"path"
	"github.com/zephyr-im/zephyr-go"
	"github.com/sdukhovni/clyde-go/stringutil"
)

// Behavior represents a zephyrbot behavior. A Behavior takes a Clyde
// instance and an incoming zephyr, and either returns false to
// indicate that the behavior was not triggered by the message, or
// performs some action (possibly using or modifying the Clyde) and
// returns true to indicate that the behavior was triggered.
type Behavior func(*Clyde, zephyr.MessageReaderResult) bool

// StandardBehavior generates a Behavior following a standard pattern
// of triggering based on a case-insensitive regular expression in a
// zephyr body, reading some named capturing groups from the regexp
// match, possibly performing some action, and replying with a single
// zephyr (possibly generated using the markov chainer) either on the
// same class and instance as the incoming zephyr or on Clyde's home
// class.
func StandardBehavior(pattern string, keys []string, chain bool, resp func(*Clyde, zephyr.MessageReaderResult, map[string]string) string) Behavior {
	return func(c *Clyde, r zephyr.MessageReaderResult) bool {
		body := strings.Join(strings.Fields(r.Message.Body[1]), " ") // normalize spacing for regexp matches
		insPattern := fmt.Sprint("(?i)", pattern)
		rex := regexp.MustCompile(insPattern)
		match := rex.FindStringSubmatchIndex(body)
		if match == nil {
			return false
		}

		keyvals := make(map[string]string)
		for _, key := range keys {
			keyvals[key] = string(rex.ExpandString([]byte(""), fmt.Sprint("$", key), body, match))
		}

		response := resp(c, r, keyvals)
		if chain {
			response = c.Chain.Generate(response, sentenceCounts[rand.Intn(len(sentenceCounts))], maxWords)
		}

		class := r.Message.Header.Class
		instance := r.Message.Header.Instance
		if class != homeClass || instance != homeInstance {
			switch c.subs[class] {
			case 0, LISTEN:
				return true
			case REPLYHOME:
				if !strings.Contains(strings.ToLower(r.Message.Body[1]), "clyde") {
					class = homeClass
					instance = homeInstance
				}
			}
		}

		c.Send(class, instance, stringutil.BreakLines(response, stringutil.MaxLine))

		return true
	}
}

// maxWords is the maximum number of words that a behavior should
// generate using the markov chainer.
const maxWords = 100

// sentenceCounts is a set of sentence counts to request from the
// chainer; a number is chosen randomly from this list each time a
// number of sentences is needed.
var sentenceCounts = []int{1, 1, 1, 2, 2, 3}

// shortSender returns just the kerberos principal (with no realm) of
// the sender of a zephyr.
func shortSender(r zephyr.MessageReaderResult) string {
	return strings.Split(r.Message.Header.Sender, "@")[0]
}

// allLines returns a list of non-empty lines in a file in Clyde's
// home directory.
func allLines(c *Clyde, filename string) ([]string, error) {
	filepath := c.Path(filename)

	f, err := os.Open(filepath)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	defer f.Close()

	var lines []string
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		if line != "" {
			lines = append(lines, line)
		}
	}

	return lines, nil
}

// randomLine returns a random non-empty line from a file in Clyde's
// home directory.
func randomLine(c *Clyde, filename string) (string, error) {
	lines, err := allLines(c, filename)
	if err != nil {
		return "", err
	}
	return lines[rand.Intn(len(lines))], nil
}

// addLine adds a line to a file in Clyde's home directory.
func addLine(c *Clyde, filename, line string) error {
	filepath := c.Path(filename)

	f, err := os.OpenFile(filepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		log.Println(err)
		return err
	}
	defer f.Close()

	fmt.Fprintln(f, line)
	return nil
}

// Behaviors is a list of behaviors to be attempted in the order
// given.
var Behaviors = []Behavior{
	addActLike,
	actLike,
	addSub,
	checkSub,
	learnJob,
	story,
	fight,
	fortune,
	dice,
	chat,
}

var addActLike = StandardBehavior("clyde.? (?P<person>.+) says,? (\"(?P<phrase>[^\"]+)\".?|'(?P<phrase>[^']+)'.?|(?P<phrase>[^\"']+)|(?P<phrase>.+[\"'].+))$",
	[]string{"person", "phrase"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		alDir := c.Path("al")
		os.MkdirAll(alDir, 0755)
		filename := path.Join("al", stringutil.Escape(strings.ToLower(kvs["person"])))
		addLine(c, filename, kvs["phrase"])
		return "Ok!"
	})

var actLike = StandardBehavior("clyde.? ((please )?act like (?P<person>.*[^\\.\\?!])(?P<punc>.*?)$|what does (?P<person>.+) say)",
	[]string{"person", "punc"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		filename := path.Join("al", stringutil.Escape(strings.ToLower(kvs["person"])))
		phrase, err := randomLine(c, filename)
		if err != nil {
			filename = path.Join("al", stringutil.Escape(strings.ToLower(fmt.Sprint(kvs["person"], kvs["punc"]))))
			phrase, err = randomLine(c, filename)
			if err != nil {
				return fmt.Sprintf("I don't know how to act like %s.", kvs["person"])
			}
		}
		return phrase
	})

var addSub = StandardBehavior("clyde.*sub(scribe)? to (me|my class|(-c )?(?P<class>[^ !\\?]+[^ !\\?\\.]))",
	[]string{"class"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		class := kvs["class"]
		if class == "" {
			class = shortSender(r)
		}

		if r.Message.Header.Class != homeClass || r.Message.Header.Instance != homeInstance {
			return "I'm subbed to a lot of classes right now; maybe another time..."
		}

		if c.subs[class] != 0 {
			return fmt.Sprintf("I'm already subbed to -c %s!", class)
		}

		if r.AuthStatus != zephyr.AuthYes {
			return "You look sketchy, I don't trust you..."
		}

		c.Subscribe(class, REPLYHOME)
		return fmt.Sprintf("-c %s sounds awesome! Thanks for the invitation :)", class)
	})

var checkSub = StandardBehavior("are you (on|sub(scri)?bed to) (me|my class|(-c )?(?P<class>[^ !\\?]+[^ !\\?\\.]))",
	[]string{"class"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		class := kvs["class"]
		if class == "" {
			class = shortSender(r)
		}

		if c.subs[class] == 0 {
			return fmt.Sprintf("I'm not subbed to -c %s.", class)
		} else {
			return fmt.Sprintf("Yup, I'm subbed to -c %s! It's my favorite class :)", class)
		}
	})

var learnJob = StandardBehavior("clyde.? (?P<job>.+) is an? (job|profession|occupation)",
	[]string{"job"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		addLine(c, "jobs", kvs["job"])
		return "That's what I wanna be when I grow up!"
	})

var story = StandardBehavior("tell me a story",
	nil,
	true,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		job, _ := randomLine(c, "jobs")
		return fmt.Sprintf("Once upon a time, there was %s %s named %s who", stringutil.Article(job), job, shortSender(r))
	})

var fight = StandardBehavior("if (?P<fight1>.+) and (?P<fight2>.+) (fought|duell?ed|got in|were in|had)|(who|which|what) .* between (?P<fight1>.+) and (?P<fight2>.+[^,\\?])(\\?|$)|between (?P<fight1>.+) and (?P<fight2>.+[^,\\?]),? (who|which|what)",
	[]string{"fight1", "fight2"},
	true,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		var winner string
		switch rand.Intn(2) {
		case 0:
			winner = "fight1"
		case 1:
			winner = "fight2"
		}
		return fmt.Sprintf("I think %s would win, because", kvs[winner])
	})

var fortune = StandardBehavior("fortune", []string{}, false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		var intros []string
		switch rand.Intn(3) {
		case 0:
			intros = []string{
				fmt.Sprintf("%s, yesterday you were", shortSender(r)),
				"Today you will",
				"Tomorrow you should",
			}
		case 1:
			planet, _ := randomLine(c, "planets")
			intros = []string{
				"The stars say",
				fmt.Sprintf("%s is aligned, so expect", planet),
				"Be careful of",
			}
		case 2:
			intros = []string{
				fmt.Sprintf("%s, in love, you will", shortSender(r)),
				"In work, you will",
				"For yourself, you should",
			}
		}
		var response []string
		for _, intro := range intros {
			response = append(response, c.Chain.Generate(intro, 1, maxWords))
		}
		return strings.Join(response, " ")
	})

var dice = StandardBehavior("( |^)(?P<count>[0-9]*)d(?P<faces>[0-9]+)",
	[]string{"count", "faces"},
	false,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		count, _ := strconv.Atoi(kvs["count"])
		faces, _ := strconv.Atoi(kvs["faces"])
		total := 0
		for i := 0; i < count; i++ {
			total += rand.Intn(faces) + 1
		}
		return strconv.Itoa(total)
	})

var chat = StandardBehavior("clyde, (?P<topic>[^ ]+)",
	[]string{"topic"},
	true,
	func(c *Clyde, r zephyr.MessageReaderResult, kvs map[string]string) string {
		return stringutil.Capitalize(kvs["topic"])
	})
<|endoftext|>


<commit_before>package core

/*TODO: make everything private*/

/*Model todo*/
type Model struct {
	pathexisted map[string]bool
}

/*Objectinfo todo*/
type Objectinfo struct {
	directory      bool
	Identification string
	Name           string
	Path           string
	Shadow         bool
	Version        map[string]int
	// Objects        []*Objectinfo `json:",omitempty"`
	Content string `json:",omitempty"`
}

/*LoadModel todo*/
func LoadModel(path string) (*Model, error) {
	if !IsTinzenite(path) {
		return nil, ErrNotTinzenite
	}
	m := &Model{}

	return m, nil
}

/*Update todo*/
func (m *Model) Update() (bool, error) {
	/*TODO*/
	return false, ErrUnsupported
}

/*Register todo*/
func (m *Model) Register(v chan Objectinfo) {
	/*TODO*/
}

/*Read todo*/
func (m *Model) Read() (*Objectinfo, error) {
	/*TODO*/
	return nil, ErrUnsupported
}
<commit_msg>model basic runs<commit_after>package core

import (
	"os"
	"path/filepath"
)

/*TODO: make everything private*/

/*Model todo*/
type Model struct {
	root    string
	tracked map[string]bool
}

/*Objectinfo todo*/
type Objectinfo struct {
	directory      bool
	Identification string
	Name           string
	Path           string
	Shadow         bool
	Version        map[string]int
	// Objects        []*Objectinfo `json:",omitempty"`
	Content string `json:",omitempty"`
}

/*LoadModel todo*/
func LoadModel(path string) (*Model, error) {
	if !IsTinzenite(path) {
		return nil, ErrNotTinzenite
	}
	m := &Model{
		root:    path,
		tracked: make(map[string]bool)}
	return m, m.populate()
}

/*Update todo*/
func (m *Model) Update() (bool, error) {
	/*TODO*/
	return false, ErrUnsupported
}

/*Register todo*/
func (m *Model) Register(v chan Objectinfo) {
	/*TODO*/
}

/*Read todo*/
func (m *Model) Read() (*Objectinfo, error) {
	/*TODO*/
	return nil, ErrUnsupported
}

func (m *Model) store() error {
	return ErrUnsupported
}

func (m *Model) populate() error {
	filepath.Walk(m.root, func(subpath string, stat os.FileInfo, inerr error) error {
		m.tracked[subpath] = true
		return nil
	})
	return nil
}

func (m *Model) String() string {
	var list string
	for path := range m.tracked {
		list += path + "\n"
	}
	return list
}
<|endoftext|>


<commit_before>package event

import (
	"fmt"
	"time"

	"github.com/Cepave/fe/model/uic"
	"github.com/astaxie/beego/orm"
)

func GetEvent(startTime int64, endTime int64, priority int, status string) (result []Event, err error) {
	q := orm.NewOrm()
	q.Using("falcon_portal")
	flag := false
	query_tmp := ""
	if startTime != 0 && endTime != 0 {
		flag = true
		query_tmp = fmt.Sprintf(" %v timestamp >= %d and  update_at >= %d", query_tmp, startTime, endTime)
	}
	if priority != -1 {
		if flag {
			query_tmp = fmt.Sprintf("%v and priority = %d", query_tmp, priority)
		} else {
			flag = true
			query_tmp = fmt.Sprintf("%v priority = %d", query_tmp, priority)
		}
	}
	if status != "ALL" {
		if flag {
			query_tmp = fmt.Sprintf("%v and status = '%s'", query_tmp, status)
		} else {
			flag = true
			query_tmp = fmt.Sprintf("%v status = '%s'", query_tmp, status)
		}
	}
	if query_tmp != "" {
		_, err = q.Raw(fmt.Sprintf("select * from event where %v", query_tmp)).QueryRows(&result)
	} else {
		_, err = q.Raw("select * from event").QueryRows(&result)
	}
	return
}

func CloseEvent(username string, id string) (err error) {
	q := orm.NewOrm()
	q.Using("falcon_portal")
	userid := uic.ReadUserIdByName(username)
	_, err = q.Raw("Update event SET user_modified = ?, closed_at = ?, status = ? WHERE id = ?", userid, time.Now(), "SOLVED", id).Exec()
	return
}

func CountNumOfTlp() (c int, err error) {
	var h []Tpl
	q := orm.NewOrm()
	q.Using("falcon_portal")
	_, err = q.Raw("select * from `tpl`").QueryRows(&h)
	c = len(h)
	return
}
<commit_msg>fixed null value retrun of alert case<commit_after>package event

import (
	"fmt"
	"time"

	"github.com/Cepave/fe/model/uic"
	"github.com/astaxie/beego/orm"
)

func GetEvent(startTime int64, endTime int64, priority int, status string) (result []Event, err error) {
	q := orm.NewOrm()
	q.Using("falcon_portal")
	flag := false
	query_tmp := ""
	if startTime != 0 && endTime != 0 {
		flag = true
		query_tmp = fmt.Sprintf(" %v timestamp >= %d and  update_at >= %d", query_tmp, startTime, endTime)
	}
	if priority != -1 {
		if flag {
			query_tmp = fmt.Sprintf("%v and priority = %d", query_tmp, priority)
		} else {
			flag = true
			query_tmp = fmt.Sprintf("%v priority = %d", query_tmp, priority)
		}
	}
	if status != "ALL" {
		if flag {
			query_tmp = fmt.Sprintf("%v and status = '%s'", query_tmp, status)
		} else {
			flag = true
			query_tmp = fmt.Sprintf("%v status = '%s'", query_tmp, status)
		}
	}
	if query_tmp != "" {
		_, err = q.Raw(fmt.Sprintf("select * from event where %v", query_tmp)).QueryRows(&result)
	} else {
		_, err = q.Raw("select * from event").QueryRows(&result)
	}

	if len(result) == 0 {
		result = []Event{}
	}

	return
}

func CloseEvent(username string, id string) (err error) {
	q := orm.NewOrm()
	q.Using("falcon_portal")
	userid := uic.ReadUserIdByName(username)
	_, err = q.Raw("Update event SET user_modified = ?, closed_at = ?, status = ? WHERE id = ?", userid, time.Now(), "SOLVED", id).Exec()
	return
}

func CountNumOfTlp() (c int, err error) {
	var h []Tpl
	q := orm.NewOrm()
	q.Using("falcon_portal")
	_, err = q.Raw("select * from `tpl`").QueryRows(&h)
	c = len(h)
	return
}
<|endoftext|>


<commit_before>package mockengine

import (
	"net/http"

	"github.com/Sirupsen/logrus"
	schematypes "github.com/taskcluster/go-schematypes"
	"github.com/taskcluster/taskcluster-worker/engines"
	"github.com/taskcluster/taskcluster-worker/engines/extpoints"
	"github.com/taskcluster/taskcluster-worker/runtime"
)

type engine struct {
	engines.EngineBase
	Log *logrus.Entry
}

type engineProvider struct {
	extpoints.EngineProviderBase
}

func init() {
	// Register the mock engine as an import side-effect
	extpoints.EngineProviders.Register(engineProvider{}, "mock")
}

func (e engineProvider) NewEngine(options extpoints.EngineOptions) (engines.Engine, error) {
	return engine{Log: options.Log}, nil
}

// mock config contains no fields
func (e engineProvider) ConfigSchema() runtime.CompositeSchema {
	return runtime.NewEmptyCompositeSchema()
}

func (e engine) PayloadSchema() schematypes.Object {
	return payloadSchema
}

func (e engine) NewSandboxBuilder(options engines.SandboxOptions) (engines.SandboxBuilder, error) {
	e.Log.Debug("Building Sandbox")

	var p payloadType
	err := e.PayloadSchema().Map(options.Payload, &p)
	if err == schematypes.ErrTypeMismatch {
		// This should pretty much either always happen or never happen.
		// So while this runtime error is bad we're pretty sure it'll get caught
		// during testing.
		panic("TypeMismatch: PayloadSchema doesn't work with payloadType")
	}
	if err != nil {
		// TODO: Write to some sort of log if the type assertion fails
		return nil, engines.ErrContractViolation
	}
	return &sandbox{
		payload: p,
		context: options.TaskContext,
		mounts:  make(map[string]*mount),
		proxies: make(map[string]http.Handler),
		env:     make(map[string]string),
		files:   make(map[string][]byte),
	}, nil
}

func (engine) NewCacheFolder() (engines.Volume, error) {
	// Create a new cache folder
	return &volume{}, nil
}
<commit_msg>mock builds again, tests not working<commit_after>package mockengine

import (
	"net/http"

	"github.com/Sirupsen/logrus"
	schematypes "github.com/taskcluster/go-schematypes"
	"github.com/taskcluster/taskcluster-worker/engines"
)

type engine struct {
	engines.EngineBase
	Log *logrus.Entry
}

type engineProvider struct {
	engines.EngineProviderBase
}

func init() {
	// Register the mock engine as an import side-effect
	engines.RegisterEngine("mock", engineProvider{})
}

func (e engineProvider) NewEngine(options engines.EngineOptions) (engines.Engine, error) {
	return engine{Log: options.Log}, nil
}

// mock config contains no fields
func (e engineProvider) ConfigSchema() schematypes.Schema {
	return schematypes.Object{}
}

func (e engine) PayloadSchema() schematypes.Object {
	return payloadSchema
}

func (e engine) NewSandboxBuilder(options engines.SandboxOptions) (engines.SandboxBuilder, error) {
	e.Log.Debug("Building Sandbox")

	var p payloadType
	err := e.PayloadSchema().Map(options.Payload, &p)
	if err == schematypes.ErrTypeMismatch {
		// This should pretty much either always happen or never happen.
		// So while this runtime error is bad we're pretty sure it'll get caught
		// during testing.
		panic("TypeMismatch: PayloadSchema doesn't work with payloadType")
	}
	if err != nil {
		// TODO: Write to some sort of log if the type assertion fails
		return nil, engines.ErrContractViolation
	}
	return &sandbox{
		payload: p,
		context: options.TaskContext,
		mounts:  make(map[string]*mount),
		proxies: make(map[string]http.Handler),
		env:     make(map[string]string),
		files:   make(map[string][]byte),
	}, nil
}

func (engine) NewCacheFolder() (engines.Volume, error) {
	// Create a new cache folder
	return &volume{}, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package options

import (
	"fmt"
	"time"

	"github.com/spf13/pflag"
	"k8s.io/klog"

	"k8s.io/apiserver/pkg/authorization/authorizer"
	"k8s.io/apiserver/pkg/authorization/authorizerfactory"
	"k8s.io/apiserver/pkg/authorization/path"
	"k8s.io/apiserver/pkg/authorization/union"
	"k8s.io/apiserver/pkg/server"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

// DelegatingAuthorizationOptions provides an easy way for composing API servers to delegate their authorization to
// the root kube API server.
// WARNING: never assume that every authenticated incoming request already does authorization.
//          The aggregator in the kube API server does this today, but this behaviour is not
//          guaranteed in the future.
type DelegatingAuthorizationOptions struct {
	// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
	// SubjectAccessReview.authorization.k8s.io endpoint for checking tokens.
	RemoteKubeConfigFile string
	// RemoteKubeConfigFileOptional is specifying whether not specifying the kubeconfig or
	// a missing in-cluster config will be fatal.
	RemoteKubeConfigFileOptional bool

	// AllowCacheTTL is the length of time that a successful authorization response will be cached
	AllowCacheTTL time.Duration

	// DenyCacheTTL is the length of time that an unsuccessful authorization response will be cached.
	// You generally want more responsive, "deny, try again" flows.
	DenyCacheTTL time.Duration

	// AlwaysAllowPaths are HTTP paths which are excluded from authorization. They can be plain
	// paths or end in * in which case prefix-match is applied. A leading / is optional.
	AlwaysAllowPaths []string

	// AlwaysAllowGroups are groups which are allowed to take any actions.  In kube, this is system:masters.
	AlwaysAllowGroups []string
}

func NewDelegatingAuthorizationOptions() *DelegatingAuthorizationOptions {
	return &DelegatingAuthorizationOptions{
		// very low for responsiveness, but high enough to handle storms
		AllowCacheTTL: 10 * time.Second,
		DenyCacheTTL:  10 * time.Second,
	}
}

// WithAlwaysAllowGroups appends the list of paths to AlwaysAllowGroups
func (s *DelegatingAuthorizationOptions) WithAlwaysAllowGroups(groups ...string) *DelegatingAuthorizationOptions {
	s.AlwaysAllowGroups = append(s.AlwaysAllowGroups, groups...)
	return s
}

// WithAlwaysAllowPaths appends the list of paths to AlwaysAllowPaths
func (s *DelegatingAuthorizationOptions) WithAlwaysAllowPaths(paths ...string) *DelegatingAuthorizationOptions {
	s.AlwaysAllowPaths = append(s.AlwaysAllowPaths, paths...)
	return s
}

func (s *DelegatingAuthorizationOptions) Validate() []error {
	allErrors := []error{}
	return allErrors
}

func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
	if s == nil {
		return
	}

	var optionalKubeConfigSentence string
	if s.RemoteKubeConfigFileOptional {
		optionalKubeConfigSentence = " This is optional. If empty, all requests not skipped by authorization are forbidden."
	}
	fs.StringVar(&s.RemoteKubeConfigFile, "authorization-kubeconfig", s.RemoteKubeConfigFile,
		"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
			"subjectaccessreviews.authorization.k8s.io."+optionalKubeConfigSentence)

	fs.DurationVar(&s.AllowCacheTTL, "authorization-webhook-cache-authorized-ttl",
		s.AllowCacheTTL,
		"The duration to cache 'authorized' responses from the webhook authorizer.")

	fs.DurationVar(&s.DenyCacheTTL,
		"authorization-webhook-cache-unauthorized-ttl", s.DenyCacheTTL,
		"The duration to cache 'unauthorized' responses from the webhook authorizer.")

	fs.StringSliceVar(&s.AlwaysAllowPaths, "authorization-always-allow-paths", s.AlwaysAllowPaths,
		"A list of HTTP paths to skip during authorization, i.e. these are authorized without "+
			"contacting the 'core' kubernetes server.")
}

func (s *DelegatingAuthorizationOptions) ApplyTo(c *server.AuthorizationInfo) error {
	if s == nil {
		c.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
		return nil
	}

	client, err := s.getClient()
	if err != nil {
		return err
	}

	c.Authorizer, err = s.toAuthorizer(client)
	return err
}

func (s *DelegatingAuthorizationOptions) toAuthorizer(client kubernetes.Interface) (authorizer.Authorizer, error) {
	var authorizers []authorizer.Authorizer

	if len(s.AlwaysAllowGroups) > 0 {
		authorizers = append(authorizers, authorizerfactory.NewPrivilegedGroups(s.AlwaysAllowGroups...))
	}

	if len(s.AlwaysAllowPaths) > 0 {
		a, err := path.NewAuthorizer(s.AlwaysAllowPaths)
		if err != nil {
			return nil, err
		}
		authorizers = append(authorizers, a)
	}

	if client == nil {
		klog.Warningf("No authorization-kubeconfig provided, so SubjectAccessReview of authorization tokens won't work.")
	} else {
		cfg := authorizerfactory.DelegatingAuthorizerConfig{
			SubjectAccessReviewClient: client.AuthorizationV1().SubjectAccessReviews(),
			AllowCacheTTL:             s.AllowCacheTTL,
			DenyCacheTTL:              s.DenyCacheTTL,
		}
		delegatedAuthorizer, err := cfg.New()
		if err != nil {
			return nil, err
		}
		authorizers = append(authorizers, delegatedAuthorizer)
	}

	return union.New(authorizers...), nil
}

func (s *DelegatingAuthorizationOptions) getClient() (kubernetes.Interface, error) {
	var clientConfig *rest.Config
	var err error
	if len(s.RemoteKubeConfigFile) > 0 {
		loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
		loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})

		clientConfig, err = loader.ClientConfig()
	} else {
		// without the remote kubeconfig file, try to use the in-cluster config.  Most addon API servers will
		// use this path. If it is optional, ignore errors.
		clientConfig, err = rest.InClusterConfig()
		if err != nil && s.RemoteKubeConfigFileOptional {
			if err != rest.ErrNotInCluster {
				klog.Warningf("failed to read in-cluster kubeconfig for delegated authorization: %v", err)
			}
			return nil, nil
		}
	}
	if err != nil {
		return nil, fmt.Errorf("failed to get delegated authorization kubeconfig: %v", err)
	}

	// set high qps/burst limits since this will effectively limit API server responsiveness
	clientConfig.QPS = 200
	clientConfig.Burst = 400

	return kubernetes.NewForConfig(clientConfig)
}
<commit_msg>UPSTREAM: 74956: apiserver: switch authorization to use protobuf client<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package options

import (
	"fmt"
	"time"

	"github.com/spf13/pflag"
	"k8s.io/klog"

	"k8s.io/apiserver/pkg/authorization/authorizer"
	"k8s.io/apiserver/pkg/authorization/authorizerfactory"
	"k8s.io/apiserver/pkg/authorization/path"
	"k8s.io/apiserver/pkg/authorization/union"
	"k8s.io/apiserver/pkg/server"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

// DelegatingAuthorizationOptions provides an easy way for composing API servers to delegate their authorization to
// the root kube API server.
// WARNING: never assume that every authenticated incoming request already does authorization.
//          The aggregator in the kube API server does this today, but this behaviour is not
//          guaranteed in the future.
type DelegatingAuthorizationOptions struct {
	// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
	// SubjectAccessReview.authorization.k8s.io endpoint for checking tokens.
	RemoteKubeConfigFile string
	// RemoteKubeConfigFileOptional is specifying whether not specifying the kubeconfig or
	// a missing in-cluster config will be fatal.
	RemoteKubeConfigFileOptional bool

	// AllowCacheTTL is the length of time that a successful authorization response will be cached
	AllowCacheTTL time.Duration

	// DenyCacheTTL is the length of time that an unsuccessful authorization response will be cached.
	// You generally want more responsive, "deny, try again" flows.
	DenyCacheTTL time.Duration

	// AlwaysAllowPaths are HTTP paths which are excluded from authorization. They can be plain
	// paths or end in * in which case prefix-match is applied. A leading / is optional.
	AlwaysAllowPaths []string

	// AlwaysAllowGroups are groups which are allowed to take any actions.  In kube, this is system:masters.
	AlwaysAllowGroups []string
}

func NewDelegatingAuthorizationOptions() *DelegatingAuthorizationOptions {
	return &DelegatingAuthorizationOptions{
		// very low for responsiveness, but high enough to handle storms
		AllowCacheTTL: 10 * time.Second,
		DenyCacheTTL:  10 * time.Second,
	}
}

// WithAlwaysAllowGroups appends the list of paths to AlwaysAllowGroups
func (s *DelegatingAuthorizationOptions) WithAlwaysAllowGroups(groups ...string) *DelegatingAuthorizationOptions {
	s.AlwaysAllowGroups = append(s.AlwaysAllowGroups, groups...)
	return s
}

// WithAlwaysAllowPaths appends the list of paths to AlwaysAllowPaths
func (s *DelegatingAuthorizationOptions) WithAlwaysAllowPaths(paths ...string) *DelegatingAuthorizationOptions {
	s.AlwaysAllowPaths = append(s.AlwaysAllowPaths, paths...)
	return s
}

func (s *DelegatingAuthorizationOptions) Validate() []error {
	allErrors := []error{}
	return allErrors
}

func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
	if s == nil {
		return
	}

	var optionalKubeConfigSentence string
	if s.RemoteKubeConfigFileOptional {
		optionalKubeConfigSentence = " This is optional. If empty, all requests not skipped by authorization are forbidden."
	}
	fs.StringVar(&s.RemoteKubeConfigFile, "authorization-kubeconfig", s.RemoteKubeConfigFile,
		"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
			"subjectaccessreviews.authorization.k8s.io."+optionalKubeConfigSentence)

	fs.DurationVar(&s.AllowCacheTTL, "authorization-webhook-cache-authorized-ttl",
		s.AllowCacheTTL,
		"The duration to cache 'authorized' responses from the webhook authorizer.")

	fs.DurationVar(&s.DenyCacheTTL,
		"authorization-webhook-cache-unauthorized-ttl", s.DenyCacheTTL,
		"The duration to cache 'unauthorized' responses from the webhook authorizer.")

	fs.StringSliceVar(&s.AlwaysAllowPaths, "authorization-always-allow-paths", s.AlwaysAllowPaths,
		"A list of HTTP paths to skip during authorization, i.e. these are authorized without "+
			"contacting the 'core' kubernetes server.")
}

func (s *DelegatingAuthorizationOptions) ApplyTo(c *server.AuthorizationInfo) error {
	if s == nil {
		c.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
		return nil
	}

	client, err := s.getClient()
	if err != nil {
		return err
	}

	c.Authorizer, err = s.toAuthorizer(client)
	return err
}

func (s *DelegatingAuthorizationOptions) toAuthorizer(client kubernetes.Interface) (authorizer.Authorizer, error) {
	var authorizers []authorizer.Authorizer

	if len(s.AlwaysAllowGroups) > 0 {
		authorizers = append(authorizers, authorizerfactory.NewPrivilegedGroups(s.AlwaysAllowGroups...))
	}

	if len(s.AlwaysAllowPaths) > 0 {
		a, err := path.NewAuthorizer(s.AlwaysAllowPaths)
		if err != nil {
			return nil, err
		}
		authorizers = append(authorizers, a)
	}

	if client == nil {
		klog.Warningf("No authorization-kubeconfig provided, so SubjectAccessReview of authorization tokens won't work.")
	} else {
		cfg := authorizerfactory.DelegatingAuthorizerConfig{
			SubjectAccessReviewClient: client.AuthorizationV1().SubjectAccessReviews(),
			AllowCacheTTL:             s.AllowCacheTTL,
			DenyCacheTTL:              s.DenyCacheTTL,
		}
		delegatedAuthorizer, err := cfg.New()
		if err != nil {
			return nil, err
		}
		authorizers = append(authorizers, delegatedAuthorizer)
	}

	return union.New(authorizers...), nil
}

func (s *DelegatingAuthorizationOptions) getClient() (kubernetes.Interface, error) {
	var clientConfig *rest.Config
	var err error
	if len(s.RemoteKubeConfigFile) > 0 {
		loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
		loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})

		clientConfig, err = loader.ClientConfig()
	} else {
		// without the remote kubeconfig file, try to use the in-cluster config.  Most addon API servers will
		// use this path. If it is optional, ignore errors.
		clientConfig, err = rest.InClusterConfig()
		if err != nil && s.RemoteKubeConfigFileOptional {
			if err != rest.ErrNotInCluster {
				klog.Warningf("failed to read in-cluster kubeconfig for delegated authorization: %v", err)
			}
			return nil, nil
		}
	}
	if err != nil {
		return nil, fmt.Errorf("failed to get delegated authorization kubeconfig: %v", err)
	}

	// set high qps/burst limits since this will effectively limit API server responsiveness
	clientConfig.QPS = 200
	clientConfig.Burst = 400

	// make the client use protobuf
	protoConfig := rest.CopyConfig(clientConfig)
	protoConfig.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
	protoConfig.ContentType = "application/vnd.kubernetes.protobuf"

	return kubernetes.NewForConfig(protoConfig)
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/codegangsta/cli"
	"github.com/hashicorp/errwrap"

	"github.com/timeglass/glass/model"
)

type Start struct {
	*command
}

func NewStart() *Start {
	return &Start{newCommand()}
}

func (c *Start) Name() string {
	return "start"
}

func (c *Start) Description() string {
	return fmt.Sprintf("Starts the daemon that is reposible for tracking time for the current repository, if the daemon is already running this operations is a no-op.")
}

func (c *Start) Usage() string {
	return "Manually start timer for the current repository"
}

func (c *Start) Flags() []cli.Flag {
	return []cli.Flag{}
}

func (c *Start) Action() func(ctx *cli.Context) {
	return c.command.Action(c.Run)
}

func (c *Start) Run(ctx *cli.Context) error {
	dir, err := os.Getwd()
	if err != nil {
		return errwrap.Wrapf("Failed to fetch current working dir: {{err}}", err)
	}

	m := model.New(dir)
	info, err := m.ReadDaemonInfo()
	if err != nil {
		return errwrap.Wrapf(fmt.Sprintf("Failed to get Daemon address: {{err}}"), err)
	}

	client := NewClient(info)
	err = client.Call("timer.start")
	if err != nil {
		if err != ErrDaemonDown {
			return err
		}

		cmd := exec.Command("glass-daemon")
		err := cmd.Start()
		if err != nil {
			return errwrap.Wrapf(fmt.Sprintf("Failed to start Daemon: {{err}}"), err)
		}
	}

	fmt.Println("Timeglass: timer started")
	return nil
}
<commit_msg>Fixed a typo in the description for the 'start' command<commit_after>package command

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/codegangsta/cli"
	"github.com/hashicorp/errwrap"

	"github.com/timeglass/glass/model"
)

type Start struct {
	*command
}

func NewStart() *Start {
	return &Start{newCommand()}
}

func (c *Start) Name() string {
	return "start"
}

func (c *Start) Description() string {
	return fmt.Sprintf("Starts the daemon that is reposible for tracking time for the current repository. If the daemon is already running, this operation is a no-op.")
}

func (c *Start) Usage() string {
	return "Manually start timer for the current repository"
}

func (c *Start) Flags() []cli.Flag {
	return []cli.Flag{}
}

func (c *Start) Action() func(ctx *cli.Context) {
	return c.command.Action(c.Run)
}

func (c *Start) Run(ctx *cli.Context) error {
	dir, err := os.Getwd()
	if err != nil {
		return errwrap.Wrapf("Failed to fetch current working dir: {{err}}", err)
	}

	m := model.New(dir)
	info, err := m.ReadDaemonInfo()
	if err != nil {
		return errwrap.Wrapf(fmt.Sprintf("Failed to get Daemon address: {{err}}"), err)
	}

	client := NewClient(info)
	err = client.Call("timer.start")
	if err != nil {
		if err != ErrDaemonDown {
			return err
		}

		cmd := exec.Command("glass-daemon")
		err := cmd.Start()
		if err != nil {
			return errwrap.Wrapf(fmt.Sprintf("Failed to start Daemon: {{err}}"), err)
		}
	}

	fmt.Println("Timeglass: timer started")
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package amass

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/OWASP/Amass/amass/core"
	"github.com/OWASP/Amass/amass/utils"
	"github.com/fatih/color"
)

// Banner is the ASCII art logo used within help output.
const Banner = `

        .+++:.            :                             .+++.
      +W@@@@@@8        &+W@#               o8W8:      +W@@@@@@#.   oW@@@W#+
     &@#+   .o@##.    .@@@o@W.o@@o       :@@#&W8o    .@#:  .:oW+  .@#+++&#&
    +@&        &@&     #@8 +@W@&8@+     :@W.   +@8   +@:          .@8
    8@          @@     8@o  8@8  WW    .@W      W@+  .@W.          o@#:
    WW          &@o    &@:  o@+  o@+   #@.      8@o   +W@#+.        +W@8:
    #@          :@W    &@+  &@+   @8  :@o       o@o     oW@@W+        oW@8
    o@+          @@&   &@+  &@+   #@  &@.      .W@W       .+#@&         o@W.
     WW         +@W@8. &@+  :&    o@+ #@      :@W&@&         &@:  ..     :@o
     :@W:      o@# +Wo &@+        :W: +@W&o++o@W. &@&  8@#o+&@W.  #@:    o@+
      :W@@WWWW@@8       +              :&W@@@@&    &W  .o#@@W&.   :W@WWW@@&
        +o&&&&+.                                                    +oooo.

`

const (
	// Version is used to display the current version of Amass.
	Version = "3.0.9"

	// Author is used to display the Amass Project Team.
	Author = "OWASP Amass Project - @owaspamass"
)

var (
	// Colors used to ease the reading of program output
	y      = color.New(color.FgHiYellow)
	g      = color.New(color.FgHiGreen)
	r      = color.New(color.FgHiRed)
	b      = color.New(color.FgHiBlue)
	fgR    = color.New(color.FgRed)
	fgY    = color.New(color.FgYellow)
	yellow = color.New(color.FgHiYellow).SprintFunc()
	green  = color.New(color.FgHiGreen).SprintFunc()
	blue   = color.New(color.FgHiBlue).SprintFunc()
)

// TrustedTag returns true when the tag parameter is of a type that should be trusted even
// facing DNS wildcards.
func TrustedTag(tag string) bool {
	if tag == core.DNS || tag == core.CERT || tag == core.ARCHIVE || tag == core.AXFR {
		return true
	}
	return false
}

// ASNSummaryData stores information related to discovered ASs and netblocks.
type ASNSummaryData struct {
	Name      string
	Netblocks map[string]int
}

// UpdateSummaryData updates the summary maps using the provided core.Output data.
func UpdateSummaryData(output *core.Output, tags map[string]int, asns map[int]*ASNSummaryData) {
	tags[output.Tag]++

	for _, addr := range output.Addresses {
		data, found := asns[addr.ASN]
		if !found {
			asns[addr.ASN] = &ASNSummaryData{
				Name:      addr.Description,
				Netblocks: make(map[string]int),
			}
			data = asns[addr.ASN]
		}
		// Increment how many IPs were in this netblock
		data.Netblocks[addr.Netblock.String()]++
	}
}

// PrintEnumerationSummary outputs the summary information utilized by the command-line tools.
func PrintEnumerationSummary(total int, tags map[string]int, asns map[int]*ASNSummaryData, demo bool) {
	pad := func(num int, chr string) {
		for i := 0; i < num; i++ {
			b.Fprint(color.Error, chr)
		}
	}

	fmt.Fprintln(color.Error)
	// Print the header information
	title := "OWASP Amass v"
	site := "https://github.com/OWASP/Amass"
	b.Fprint(color.Error, title+Version)
	num := 80 - (len(title) + len(Version) + len(site))
	pad(num, " ")
	b.Fprintf(color.Error, "%s\n", site)
	pad(8, "----------")
	fmt.Fprintf(color.Error, "\n%s%s", yellow(strconv.Itoa(total)), green(" names discovered - "))
	// Print the stats using tag information
	num, length := 1, len(tags)
	for k, v := range tags {
		fmt.Fprintf(color.Error, "%s: %s", green(k), yellow(strconv.Itoa(v)))
		if num < length {
			g.Fprint(color.Error, ", ")
		}
		num++
	}
	fmt.Fprintln(color.Error)

	if len(asns) == 0 {
		return
	}
	// Another line gets printed
	pad(8, "----------")
	fmt.Fprintln(color.Error)
	// Print the ASN and netblock information
	for asn, data := range asns {
		asnstr := strconv.Itoa(asn)
		datastr := data.Name

		if demo && asn > 0 {
			asnstr = censorString(asnstr, 0, len(asnstr))
			datastr = censorString(datastr, 0, len(datastr))
		}

		fmt.Fprintf(color.Error, "%s%s %s %s\n",
			blue("ASN: "), yellow(asnstr), green("-"), green(datastr))

		for cidr, ips := range data.Netblocks {
			countstr := strconv.Itoa(ips)
			cidrstr := cidr

			if demo {
				cidrstr = censorNetBlock(cidrstr)
			}

			countstr = fmt.Sprintf("\t%-4s", countstr)
			cidrstr = fmt.Sprintf("\t%-18s", cidrstr)

			fmt.Fprintf(color.Error, "%s%s %s\n",
				yellow(cidrstr), yellow(countstr), blue("Subdomain Name(s)"))
		}
	}
}

// PrintBanner outputs the Amass banner the same for all tools.
func PrintBanner() {
	y := color.New(color.FgHiYellow)
	r := color.New(color.FgHiRed)
	rightmost := 76
	version := "Version " + Version
	desc := "In-depth DNS Enumeration and Network Mapping"

	pad := func(num int) {
		for i := 0; i < num; i++ {
			fmt.Fprint(color.Error, " ")
		}
	}
	r.Fprintln(color.Error, Banner)
	pad(rightmost - len(version))
	y.Fprintln(color.Error, version)
	pad(rightmost - len(Author))
	y.Fprintln(color.Error, Author)
	pad(rightmost - len(desc))
	y.Fprintf(color.Error, "%s\n\n\n", desc)
}

func censorDomain(input string) string {
	return censorString(input, strings.Index(input, "."), len(input))
}

func censorIP(input string) string {
	return censorString(input, 0, strings.LastIndex(input, "."))
}

func censorNetBlock(input string) string {
	return censorString(input, 0, strings.Index(input, "/"))
}

func censorString(input string, start, end int) string {
	runes := []rune(input)
	for i := start; i < end; i++ {
		if runes[i] == '.' ||
			runes[i] == '/' ||
			runes[i] == '-' ||
			runes[i] == ' ' {
			continue
		}
		runes[i] = 'x'
	}
	return string(runes)
}

// OutputLineParts returns the parts of a line to be printed for a core.Output.
func OutputLineParts(out *core.Output, src, addrs, demo bool) (source, name, ips string) {
	if src {
		source = fmt.Sprintf("%-18s", "["+out.Source+"] ")
	}
	if addrs {
		for i, a := range out.Addresses {
			if i != 0 {
				ips += ","
			}
			if demo {
				ips += censorIP(a.Address.String())
			} else {
				ips += a.Address.String()
			}
		}
		if ips == "" {
			ips = "N/A"
		}
	}
	name = out.Name
	if demo {
		name = censorDomain(name)
	}
	return
}

// DesiredAddrTypes removes undesired address types from the AddressInfo slice.
func DesiredAddrTypes(addrs []core.AddressInfo, ipv4, ipv6 bool) []core.AddressInfo {
	if !ipv4 && !ipv6 {
		return addrs
	}

	var keep []core.AddressInfo
	for _, addr := range addrs {
		if utils.IsIPv4(addr.Address) && !ipv4 {
			continue
		} else if utils.IsIPv6(addr.Address) && !ipv6 {
			continue
		}
		keep = append(keep, addr)
	}
	return keep
}
<commit_msg>version 3.0.10 release<commit_after>// Copyright 2017 Jeff Foley. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package amass

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/OWASP/Amass/amass/core"
	"github.com/OWASP/Amass/amass/utils"
	"github.com/fatih/color"
)

// Banner is the ASCII art logo used within help output.
const Banner = `

        .+++:.            :                             .+++.
      +W@@@@@@8        &+W@#               o8W8:      +W@@@@@@#.   oW@@@W#+
     &@#+   .o@##.    .@@@o@W.o@@o       :@@#&W8o    .@#:  .:oW+  .@#+++&#&
    +@&        &@&     #@8 +@W@&8@+     :@W.   +@8   +@:          .@8
    8@          @@     8@o  8@8  WW    .@W      W@+  .@W.          o@#:
    WW          &@o    &@:  o@+  o@+   #@.      8@o   +W@#+.        +W@8:
    #@          :@W    &@+  &@+   @8  :@o       o@o     oW@@W+        oW@8
    o@+          @@&   &@+  &@+   #@  &@.      .W@W       .+#@&         o@W.
     WW         +@W@8. &@+  :&    o@+ #@      :@W&@&         &@:  ..     :@o
     :@W:      o@# +Wo &@+        :W: +@W&o++o@W. &@&  8@#o+&@W.  #@:    o@+
      :W@@WWWW@@8       +              :&W@@@@&    &W  .o#@@W&.   :W@WWW@@&
        +o&&&&+.                                                    +oooo.

`

const (
	// Version is used to display the current version of Amass.
	Version = "3.0.10"

	// Author is used to display the Amass Project Team.
	Author = "OWASP Amass Project - @owaspamass"
)

var (
	// Colors used to ease the reading of program output
	y      = color.New(color.FgHiYellow)
	g      = color.New(color.FgHiGreen)
	r      = color.New(color.FgHiRed)
	b      = color.New(color.FgHiBlue)
	fgR    = color.New(color.FgRed)
	fgY    = color.New(color.FgYellow)
	yellow = color.New(color.FgHiYellow).SprintFunc()
	green  = color.New(color.FgHiGreen).SprintFunc()
	blue   = color.New(color.FgHiBlue).SprintFunc()
)

// TrustedTag returns true when the tag parameter is of a type that should be trusted even
// facing DNS wildcards.
func TrustedTag(tag string) bool {
	if tag == core.DNS || tag == core.CERT || tag == core.ARCHIVE || tag == core.AXFR {
		return true
	}
	return false
}

// ASNSummaryData stores information related to discovered ASs and netblocks.
type ASNSummaryData struct {
	Name      string
	Netblocks map[string]int
}

// UpdateSummaryData updates the summary maps using the provided core.Output data.
func UpdateSummaryData(output *core.Output, tags map[string]int, asns map[int]*ASNSummaryData) {
	tags[output.Tag]++

	for _, addr := range output.Addresses {
		data, found := asns[addr.ASN]
		if !found {
			asns[addr.ASN] = &ASNSummaryData{
				Name:      addr.Description,
				Netblocks: make(map[string]int),
			}
			data = asns[addr.ASN]
		}
		// Increment how many IPs were in this netblock
		data.Netblocks[addr.Netblock.String()]++
	}
}

// PrintEnumerationSummary outputs the summary information utilized by the command-line tools.
func PrintEnumerationSummary(total int, tags map[string]int, asns map[int]*ASNSummaryData, demo bool) {
	pad := func(num int, chr string) {
		for i := 0; i < num; i++ {
			b.Fprint(color.Error, chr)
		}
	}

	fmt.Fprintln(color.Error)
	// Print the header information
	title := "OWASP Amass v"
	site := "https://github.com/OWASP/Amass"
	b.Fprint(color.Error, title+Version)
	num := 80 - (len(title) + len(Version) + len(site))
	pad(num, " ")
	b.Fprintf(color.Error, "%s\n", site)
	pad(8, "----------")
	fmt.Fprintf(color.Error, "\n%s%s", yellow(strconv.Itoa(total)), green(" names discovered - "))
	// Print the stats using tag information
	num, length := 1, len(tags)
	for k, v := range tags {
		fmt.Fprintf(color.Error, "%s: %s", green(k), yellow(strconv.Itoa(v)))
		if num < length {
			g.Fprint(color.Error, ", ")
		}
		num++
	}
	fmt.Fprintln(color.Error)

	if len(asns) == 0 {
		return
	}
	// Another line gets printed
	pad(8, "----------")
	fmt.Fprintln(color.Error)
	// Print the ASN and netblock information
	for asn, data := range asns {
		asnstr := strconv.Itoa(asn)
		datastr := data.Name

		if demo && asn > 0 {
			asnstr = censorString(asnstr, 0, len(asnstr))
			datastr = censorString(datastr, 0, len(datastr))
		}

		fmt.Fprintf(color.Error, "%s%s %s %s\n",
			blue("ASN: "), yellow(asnstr), green("-"), green(datastr))

		for cidr, ips := range data.Netblocks {
			countstr := strconv.Itoa(ips)
			cidrstr := cidr

			if demo {
				cidrstr = censorNetBlock(cidrstr)
			}

			countstr = fmt.Sprintf("\t%-4s", countstr)
			cidrstr = fmt.Sprintf("\t%-18s", cidrstr)

			fmt.Fprintf(color.Error, "%s%s %s\n",
				yellow(cidrstr), yellow(countstr), blue("Subdomain Name(s)"))
		}
	}
}

// PrintBanner outputs the Amass banner the same for all tools.
func PrintBanner() {
	y := color.New(color.FgHiYellow)
	r := color.New(color.FgHiRed)
	rightmost := 76
	version := "Version " + Version
	desc := "In-depth DNS Enumeration and Network Mapping"

	pad := func(num int) {
		for i := 0; i < num; i++ {
			fmt.Fprint(color.Error, " ")
		}
	}
	r.Fprintln(color.Error, Banner)
	pad(rightmost - len(version))
	y.Fprintln(color.Error, version)
	pad(rightmost - len(Author))
	y.Fprintln(color.Error, Author)
	pad(rightmost - len(desc))
	y.Fprintf(color.Error, "%s\n\n\n", desc)
}

func censorDomain(input string) string {
	return censorString(input, strings.Index(input, "."), len(input))
}

func censorIP(input string) string {
	return censorString(input, 0, strings.LastIndex(input, "."))
}

func censorNetBlock(input string) string {
	return censorString(input, 0, strings.Index(input, "/"))
}

func censorString(input string, start, end int) string {
	runes := []rune(input)
	for i := start; i < end; i++ {
		if runes[i] == '.' ||
			runes[i] == '/' ||
			runes[i] == '-' ||
			runes[i] == ' ' {
			continue
		}
		runes[i] = 'x'
	}
	return string(runes)
}

// OutputLineParts returns the parts of a line to be printed for a core.Output.
func OutputLineParts(out *core.Output, src, addrs, demo bool) (source, name, ips string) {
	if src {
		source = fmt.Sprintf("%-18s", "["+out.Source+"] ")
	}
	if addrs {
		for i, a := range out.Addresses {
			if i != 0 {
				ips += ","
			}
			if demo {
				ips += censorIP(a.Address.String())
			} else {
				ips += a.Address.String()
			}
		}
		if ips == "" {
			ips = "N/A"
		}
	}
	name = out.Name
	if demo {
		name = censorDomain(name)
	}
	return
}

// DesiredAddrTypes removes undesired address types from the AddressInfo slice.
func DesiredAddrTypes(addrs []core.AddressInfo, ipv4, ipv6 bool) []core.AddressInfo {
	if !ipv4 && !ipv6 {
		return addrs
	}

	var keep []core.AddressInfo
	for _, addr := range addrs {
		if utils.IsIPv4(addr.Address) && !ipv4 {
			continue
		} else if utils.IsIPv6(addr.Address) && !ipv6 {
			continue
		}
		keep = append(keep, addr)
	}
	return keep
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Mirantis

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package integration

var (
	imageCirrosUrl     = "localhost/cirros-0.3.4-x86_64-disk.img"
	imageCopyCirrosUrl = "localhost/copy/cirros-0.3.4-x86_64-disk.img"
	imageCirrosId      = "cirros-0.3.4-x86_64-disk.img"
	cirrosVolumeSize   = 41126400
	imageCirrosUrl2    = "localhost/cirros-0.3.3-x86_64-disk.img"
)
<commit_msg>tests: Update tests for new image id<commit_after>/*
Copyright 2016 Mirantis

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package integration

var (
	imageCirrosUrl     = "localhost/cirros-0.3.4-x86_64-disk.img"
	imageCopyCirrosUrl = "localhost/copy/cirros-0.3.4-x86_64-disk.img"
	imageCirrosId      = "localhost/cirros-0.3.4-x86_64-disk.img"
	cirrosVolumeSize   = 41126400
	imageCirrosUrl2    = "localhost/cirros-0.3.3-x86_64-disk.img"
)
<|endoftext|>


<commit_before>package sshexec

import "encoding/binary"

// Opcode represents a PTY mode opcode
type Opcode byte

// List of PTY mode opcodes
const (
	TTYOPEND    Opcode = iota // 0     TTY_OP_END  Indicates end of options.
	VINTR                     // 1     VINTR       Interrupt character; 255 if none.
	VQUIT                     // 2     VQUIT       The quit character (sends SIGQUIT signal on POSIX systems).
	VERASE                    // 3     VERASE      Erase the character to left of the cursor.
	VKILL                     // 4     VKILL       Kill the current input line.
	VEOF                      // 5     VEOF        End-of-file character (sends EOF from the terminal).
	VEOL                      // 6     VEOL        End-of-line character in addition to carriage return and/or linefeed.
	VEOL2                     // 7     VEOL2       Additional end-of-line character.
	VSTART                    // 8     VSTART      Continues paused output (normally control-Q).
	VSTOP                     // 9     VSTOP       Pauses output (normally control-S).
	VSUSP                     // 10    VSUSP       Suspends the current program.
	VDSUSP                    // 11    VDSUSP      Another suspend character.
	VREPRINT                  // 12    VREPRINT    Reprints the current input line.
	VWERASE                   // 13    VWERASE     Erases a word left of cursor.
	VLNEXT                    // 14    VLNEXT      Enter the next character typed literally, even if it is a special character
	VFLUSH                    // 15    VFLUSH      Character to flush output.
	VSWTCH                    // 16    VSWTCH      Switch to a different shell layer.
	VSTATUS                   // 17    VSTATUS     Prints system status line (load, command, pid, etc).
	VDISCARD                  // 18    VDISCARD    Toggles the flushing of terminal output.
	IGNPAR      = 30          // 30    IGNPAR      The ignore parity flag.
	PARMRK                    // 31    PARMRK      Mark parity and framing errors.
	INPCK                     // 32    INPCK       Enable checking of parity errors.
	ISTRIP                    // 33    ISTRIP      Strip 8th bit off characters.
	INLCR                     // 34    INLCR       Map NL into CR on input.
	IGNCR                     // 35    IGNCR       Ignore CR on input.
	ICRNL                     // 36    ICRNL       Map CR to NL on input.
	IUCLC                     // 37    IUCLC       Translate uppercase characters to lowercase.
	IXON                      // 38    IXON        Enable output flow control.
	IXANY                     // 39    IXANY       Any char will restart after stop.
	IXOFF                     // 40    IXOFF       Enable input flow control.
	IMAXBEL                   // 41    IMAXBEL     Ring bell on input queue full.
	ISIG        = 40          // 50    ISIG        Enable signals INTR, QUIT, [D]SUSP.
	ICANON                    // 51    ICANON      Canonicalize input lines.
	XCASE                     // 52    XCASE       Enable input and output of uppercase characters by preceding their lowercase equivalents with "\".
	ECHO                      // 53    ECHO        Enable echoing.
	ECHOE                     // 54    ECHOE       Visually erase chars.
	ECHOK                     // 55    ECHOK       Kill character discards current line.
	ECHONL                    // 56    ECHONL      Echo NL even if ECHO is off.
	NOFLSH                    // 57    NOFLSH      Don't flush after interrupt.
	TOSTOP                    // 58    TOSTOP      Stop background jobs from output.
	IEXTEN                    // 59    IEXTEN      Enable extensions.
	ECHOCTL                   // 60    ECHOCTL     Echo control characters as ^(Char).
	ECHOKE                    // 61    ECHOKE      Visual erase for line kill.
	PENDIN                    // 62    PENDIN      Retype pending input.
	OPOST       = 70          // 70    OPOST       Enable output processing.
	OLCUC                     // 71    OLCUC       Convert lowercase to uppercase.
	ONLCR                     // 72    ONLCR       Map NL to CR-NL.
	OCRNL                     // 73    OCRNL       Translate carriage return to newline (output).
	ONOCR                     // 74    ONOCR       Translate newline to carriage return-newline (output).
	ONLRET                    // 75    ONLRET      Newline performs a carriage return  (output).
	CS7         = 90          // 90    CS7         7 bit mode.
	CS8                       // 91    CS8         8 bit mode.
	PARENB                    // 92    PARENB      Parity enable.
	PARODD                    // 93    PARODD      Odd parity, else even.
	TTYOPISPEED = 128         // 128 TTY_OP_ISPEED  Specifies the input baud rate in bits per second.
	TTYOPOSPEED               // 129 TTY_OP_OSPEED  Specifies the output baud rate in bits per second.
)

func modes(b []byte) (map[Opcode]uint32, []byte) {
	if len(b) < 4 {
		return nil, b
	}
	l := binary.BigEndian.Uint32(b)
	if len(b) < int(l)+4 {
		return nil, b
	}
	s := b[4 : l+4]
	b = b[l+4:]

	m := map[Opcode]uint32{}
	for len(s) > 5 && s[0] > 0x00 && s[0] < 160 {
		m[Opcode(s[0])], s = intVal(s[1:])
	}
	return m, b
}
<commit_msg>Fix const numbering with iota<commit_after>package sshexec

import "encoding/binary"

// Opcode represents a PTY mode opcode
type Opcode byte

// List of PTY mode opcodes
const (
	TTYOPEND Opcode = iota // 0     TTY_OP_END  Indicates end of options.
	VINTR                  // 1     VINTR       Interrupt character; 255 if none.
	VQUIT                  // 2     VQUIT       The quit character (sends SIGQUIT signal on POSIX systems).
	VERASE                 // 3     VERASE      Erase the character to left of the cursor.
	VKILL                  // 4     VKILL       Kill the current input line.
	VEOF                   // 5     VEOF        End-of-file character (sends EOF from the terminal).
	VEOL                   // 6     VEOL        End-of-line character in addition to carriage return and/or linefeed.
	VEOL2                  // 7     VEOL2       Additional end-of-line character.
	VSTART                 // 8     VSTART      Continues paused output (normally control-Q).
	VSTOP                  // 9     VSTOP       Pauses output (normally control-S).
	VSUSP                  // 10    VSUSP       Suspends the current program.
	VDSUSP                 // 11    VDSUSP      Another suspend character.
	VREPRINT               // 12    VREPRINT    Reprints the current input line.
	VWERASE                // 13    VWERASE     Erases a word left of cursor.
	VLNEXT                 // 14    VLNEXT      Enter the next character typed literally, even if it is a special character
	VFLUSH                 // 15    VFLUSH      Character to flush output.
	VSWTCH                 // 16    VSWTCH      Switch to a different shell layer.
	VSTATUS                // 17    VSTATUS     Prints system status line (load, command, pid, etc).
	VDISCARD               // 18    VDISCARD    Toggles the flushing of terminal output.
	_
	_
	_
	_
	_
	_
	_
	_
	_
	_
	_
	IGNPAR  // 30    IGNPAR      The ignore parity flag.
	PARMRK  // 31    PARMRK      Mark parity and framing errors.
	INPCK   // 32    INPCK       Enable checking of parity errors.
	ISTRIP  // 33    ISTRIP      Strip 8th bit off characters.
	INLCR   // 34    INLCR       Map NL into CR on input.
	IGNCR   // 35    IGNCR       Ignore CR on input.
	ICRNL   // 36    ICRNL       Map CR to NL on input.
	IUCLC   // 37    IUCLC       Translate uppercase characters to lowercase.
	IXON    // 38    IXON        Enable output flow control.
	IXANY   // 39    IXANY       Any char will restart after stop.
	IXOFF   // 40    IXOFF       Enable input flow control.
	IMAXBEL // 41    IMAXBEL     Ring bell on input queue full.
	_
	_
	_
	_
	_
	_
	_
	_
	ISIG    // 50    ISIG        Enable signals INTR, QUIT, [D]SUSP.
	ICANON  // 51    ICANON      Canonicalize input lines.
	XCASE   // 52    XCASE       Enable input/output of uppercase characters by preceding their lowercase equivalents with "\".
	ECHO    // 53    ECHO        Enable echoing.
	ECHOE   // 54    ECHOE       Visually erase chars.
	ECHOK   // 55    ECHOK       Kill character discards current line.
	ECHONL  // 56    ECHONL      Echo NL even if ECHO is off.
	NOFLSH  // 57    NOFLSH      Don't flush after interrupt.
	TOSTOP  // 58    TOSTOP      Stop background jobs from output.
	IEXTEN  // 59    IEXTEN      Enable extensions.
	ECHOCTL // 60    ECHOCTL     Echo control characters as ^(Char).
	ECHOKE  // 61    ECHOKE      Visual erase for line kill.
	PENDIN  // 62    PENDIN      Retype pending input.
	_
	_
	_
	_
	_
	_
	_
	OPOST  // 70    OPOST       Enable output processing.
	OLCUC  // 71    OLCUC       Convert lowercase to uppercase.
	ONLCR  // 72    ONLCR       Map NL to CR-NL.
	OCRNL  // 73    OCRNL       Translate carriage return to newline (output).
	ONOCR  // 74    ONOCR       Translate newline to carriage return-newline (output).
	ONLRET // 75    ONLRET      Newline performs a carriage return  (output).
	_
	_
	_
	_
	_
	_
	_
	_
	_
	_
	_
	_
	_
	_
	CS7               // 90    CS7         7 bit mode.
	CS8               // 91    CS8         8 bit mode.
	PARENB            // 92    PARENB      Parity enable.
	PARODD            // 93    PARODD      Odd parity, else even.
	TTYOPISPEED = 128 // 128 TTY_OP_ISPEED  Specifies the input baud rate in bits per second.
	TTYOPOSPEED = 129 // 129 TTY_OP_OSPEED  Specifies the output baud rate in bits per second.
)

func modes(b []byte) (map[Opcode]uint32, []byte) {
	if len(b) < 4 {
		return nil, b
	}
	l := binary.BigEndian.Uint32(b)
	if len(b) < int(l)+4 {
		return nil, b
	}
	s := b[4 : l+4]
	b = b[l+4:]

	m := map[Opcode]uint32{}
	for len(s) > 5 && s[0] > 0x00 && s[0] < 160 {
		m[Opcode(s[0])], s = intVal(s[1:])
	}
	return m, b
}
<|endoftext|>


<commit_before>// Package minify relates MIME type to minifiers. Several minifiers are provided in the subpackages.
package minify

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
	"sync"

	"github.com/tdewolff/parse/v2"
	"github.com/tdewolff/parse/v2/buffer"
)

// Warning is used to report usage warnings such as using a deprecated feature
var Warning = log.New(os.Stderr, "WARNING: ", 0)

// ErrNotExist is returned when no minifier exists for a given mimetype.
var ErrNotExist = errors.New("minifier does not exist for mimetype")

////////////////////////////////////////////////////////////////

// MinifierFunc is a function that implements Minifer.
type MinifierFunc func(*M, io.Writer, io.Reader, map[string]string) error

// Minify calls f(m, w, r, params)
func (f MinifierFunc) Minify(m *M, w io.Writer, r io.Reader, params map[string]string) error {
	return f(m, w, r, params)
}

// Minifier is the interface for minifiers.
// The *M parameter is used for minifying embedded resources, such as JS within HTML.
type Minifier interface {
	Minify(*M, io.Writer, io.Reader, map[string]string) error
}

////////////////////////////////////////////////////////////////

type patternMinifier struct {
	pattern *regexp.Regexp
	Minifier
}

type cmdMinifier struct {
	cmd *exec.Cmd
}

var cmdArgExtension = regexp.MustCompile(`^\.[0-9a-zA-Z]+`)

func (c *cmdMinifier) Minify(_ *M, w io.Writer, r io.Reader, _ map[string]string) error {
	cmd := &exec.Cmd{}
	*cmd = *c.cmd // concurrency safety

	var in, out *os.File
	for i, arg := range cmd.Args {
		if j := strings.Index(arg, "$in"); j != -1 {
			var err error
			ext := cmdArgExtension.FindString(arg[j+3:])
			if in, err = ioutil.TempFile("", "minify-in-*"+ext); err != nil {
				return err
			}
			cmd.Args[i] = arg[:j] + in.Name() + arg[j+3+len(ext):]
		} else if j := strings.Index(arg, "$out"); j != -1 {
			var err error
			ext := cmdArgExtension.FindString(arg[j+4:])
			if out, err = ioutil.TempFile("", "minify-out-*"+ext); err != nil {
				return err
			}
			cmd.Args[i] = arg[:j] + out.Name() + arg[j+4+len(ext):]
		}
	}

	if in == nil {
		cmd.Stdin = r
	} else if _, err := io.Copy(in, r); err != nil {
		return err
	}
	if out == nil {
		cmd.Stdout = w
	} else {
		defer io.Copy(w, out)
	}
	stderr := &bytes.Buffer{}
	cmd.Stderr = stderr

	err := cmd.Run()
	if _, ok := err.(*exec.ExitError); ok {
		if stderr.Len() != 0 {
			err = fmt.Errorf("%s", stderr.String())
		}
		err = fmt.Errorf("command %s failed: %w", cmd.Path, err)
	}
	return err
}

////////////////////////////////////////////////////////////////

// M holds a map of mimetype => function to allow recursive minifier calls of the minifier functions.
type M struct {
	mutex   sync.RWMutex
	literal map[string]Minifier
	pattern []patternMinifier

	URL *url.URL
}

// New returns a new M.
func New() *M {
	return &M{
		sync.RWMutex{},
		map[string]Minifier{},
		[]patternMinifier{},
		nil,
	}
}

// Add adds a minifier to the mimetype => function map (unsafe for concurrent use).
func (m *M) Add(mimetype string, minifier Minifier) {
	m.mutex.Lock()
	m.literal[mimetype] = minifier
	m.mutex.Unlock()
}

// AddFunc adds a minify function to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddFunc(mimetype string, minifier MinifierFunc) {
	m.mutex.Lock()
	m.literal[mimetype] = minifier
	m.mutex.Unlock()
}

// AddRegexp adds a minifier to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddRegexp(pattern *regexp.Regexp, minifier Minifier) {
	m.mutex.Lock()
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
	m.mutex.Unlock()
}

// AddFuncRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddFuncRegexp(pattern *regexp.Regexp, minifier MinifierFunc) {
	m.mutex.Lock()
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
	m.mutex.Unlock()
}

// AddCmd adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype.
func (m *M) AddCmd(mimetype string, cmd *exec.Cmd) {
	m.mutex.Lock()
	m.literal[mimetype] = &cmdMinifier{cmd}
	m.mutex.Unlock()
}

// AddCmdRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype regular expression.
func (m *M) AddCmdRegexp(pattern *regexp.Regexp, cmd *exec.Cmd) {
	m.mutex.Lock()
	m.pattern = append(m.pattern, patternMinifier{pattern, &cmdMinifier{cmd}})
	m.mutex.Unlock()
}

// Match returns the pattern and minifier that gets matched with the mediatype.
// It returns nil when no matching minifier exists.
// It has the same matching algorithm as Minify.
func (m *M) Match(mediatype string) (string, map[string]string, MinifierFunc) {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	mimetype, params := parse.Mediatype([]byte(mediatype))
	if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
		return string(mimetype), params, minifier.Minify
	}

	for _, minifier := range m.pattern {
		if minifier.pattern.Match(mimetype) {
			return minifier.pattern.String(), params, minifier.Minify
		}
	}
	return string(mimetype), params, nil
}

// Minify minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
// An error is returned when no such mimetype exists (ErrNotExist) or when an error occurred in the minifier function.
// Mediatype may take the form of 'text/plain', 'text/*', '*/*' or 'text/plain; charset=UTF-8; version=2.0'.
func (m *M) Minify(mediatype string, w io.Writer, r io.Reader) error {
	mimetype, params := parse.Mediatype([]byte(mediatype))
	return m.MinifyMimetype(mimetype, w, r, params)
}

// MinifyMimetype minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
// It is a lower level version of Minify and requires the mediatype to be split up into mimetype and parameters.
// It is mostly used internally by minifiers because it is faster (no need to convert a byte-slice to string and vice versa).
func (m *M) MinifyMimetype(mimetype []byte, w io.Writer, r io.Reader, params map[string]string) error {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
		return minifier.Minify(m, w, r, params)
	}
	for _, minifier := range m.pattern {
		if minifier.pattern.Match(mimetype) {
			return minifier.Minify(m, w, r, params)
		}
	}
	return ErrNotExist
}

// Bytes minifies an array of bytes (safe for concurrent use). When an error occurs it return the original array and the error.
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
func (m *M) Bytes(mediatype string, v []byte) ([]byte, error) {
	out := buffer.NewWriter(make([]byte, 0, len(v)))
	if err := m.Minify(mediatype, out, buffer.NewReader(v)); err != nil {
		return v, err
	}
	return out.Bytes(), nil
}

// String minifies a string (safe for concurrent use). When an error occurs it return the original string and the error.
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
func (m *M) String(mediatype string, v string) (string, error) {
	out := buffer.NewWriter(make([]byte, 0, len(v)))
	if err := m.Minify(mediatype, out, buffer.NewReader([]byte(v))); err != nil {
		return v, err
	}
	return string(out.Bytes()), nil
}

// Reader wraps a Reader interface and minifies the stream.
// Errors from the minifier are returned by the reader.
func (m *M) Reader(mediatype string, r io.Reader) io.Reader {
	pr, pw := io.Pipe()
	go func() {
		if err := m.Minify(mediatype, pw, r); err != nil {
			pw.CloseWithError(err)
		} else {
			pw.Close()
		}
	}()
	return pr
}

// minifyWriter makes sure that errors from the minifier are passed down through Close (can be blocking).
type minifyWriter struct {
	pw  *io.PipeWriter
	wg  sync.WaitGroup
	err error
}

// Write intercepts any writes to the writer.
func (w *minifyWriter) Write(b []byte) (int, error) {
	return w.pw.Write(b)
}

// Close must be called when writing has finished. It returns the error from the minifier.
func (w *minifyWriter) Close() error {
	w.pw.Close()
	w.wg.Wait()
	return w.err
}

// Writer wraps a Writer interface and minifies the stream.
// Errors from the minifier are returned by Close on the writer.
// The writer must be closed explicitly.
func (m *M) Writer(mediatype string, w io.Writer) *minifyWriter {
	pr, pw := io.Pipe()
	mw := &minifyWriter{pw, sync.WaitGroup{}, nil}
	mw.wg.Add(1)
	go func() {
		defer mw.wg.Done()

		if err := m.Minify(mediatype, w, pr); err != nil {
			io.Copy(w, pr)
			mw.err = err
		}
		pr.Close()
	}()
	return mw
}

// minifyResponseWriter wraps an http.ResponseWriter and makes sure that errors from the minifier are passed down through Close (can be blocking).
// All writes to the response writer are intercepted and minified on the fly.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
type minifyResponseWriter struct {
	http.ResponseWriter

	writer    *minifyWriter
	m         *M
	mediatype string
}

// WriteHeader intercepts any header writes and removes the Content-Length header.
func (w *minifyResponseWriter) WriteHeader(status int) {
	w.ResponseWriter.Header().Del("Content-Length")
	w.ResponseWriter.WriteHeader(status)
}

// Write intercepts any writes to the response writer.
// The first write will extract the Content-Type as the mediatype. Otherwise it falls back to the RequestURI extension.
func (w *minifyResponseWriter) Write(b []byte) (int, error) {
	if w.writer == nil {
		// first write
		if mediatype := w.ResponseWriter.Header().Get("Content-Type"); mediatype != "" {
			w.mediatype = mediatype
		}
		w.writer = w.m.Writer(w.mediatype, w.ResponseWriter)
	}
	return w.writer.Write(b)
}

// Close must be called when writing has finished. It returns the error from the minifier.
func (w *minifyResponseWriter) Close() error {
	if w.writer != nil {
		return w.writer.Close()
	}
	return nil
}

// ResponseWriter minifies any writes to the http.ResponseWriter.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
// Minification might be slower than just sending the original file! Caching is advised.
func (m *M) ResponseWriter(w http.ResponseWriter, r *http.Request) *minifyResponseWriter {
	mediatype := mime.TypeByExtension(path.Ext(r.RequestURI))
	return &minifyResponseWriter{w, nil, m, mediatype}
}

// Middleware provides a middleware function that minifies content on the fly by intercepting writes to http.ResponseWriter.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
// Minification might be slower than just sending the original file! Caching is advised.
func (m *M) Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mw := m.ResponseWriter(w, r)
		defer mw.Close()

		next.ServeHTTP(mw, r)
	})
}
<commit_msg>Error reporting for Writer, ResponseWriter and Middleware may report sooner<commit_after>// Package minify relates MIME type to minifiers. Several minifiers are provided in the subpackages.
package minify

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strings"
	"sync"

	"github.com/tdewolff/parse/v2"
	"github.com/tdewolff/parse/v2/buffer"
)

// Warning is used to report usage warnings such as using a deprecated feature
var Warning = log.New(os.Stderr, "WARNING: ", 0)

// ErrNotExist is returned when no minifier exists for a given mimetype.
var ErrNotExist = errors.New("minifier does not exist for mimetype")

////////////////////////////////////////////////////////////////

// MinifierFunc is a function that implements Minifer.
type MinifierFunc func(*M, io.Writer, io.Reader, map[string]string) error

// Minify calls f(m, w, r, params)
func (f MinifierFunc) Minify(m *M, w io.Writer, r io.Reader, params map[string]string) error {
	return f(m, w, r, params)
}

// Minifier is the interface for minifiers.
// The *M parameter is used for minifying embedded resources, such as JS within HTML.
type Minifier interface {
	Minify(*M, io.Writer, io.Reader, map[string]string) error
}

////////////////////////////////////////////////////////////////

type patternMinifier struct {
	pattern *regexp.Regexp
	Minifier
}

type cmdMinifier struct {
	cmd *exec.Cmd
}

var cmdArgExtension = regexp.MustCompile(`^\.[0-9a-zA-Z]+`)

func (c *cmdMinifier) Minify(_ *M, w io.Writer, r io.Reader, _ map[string]string) error {
	cmd := &exec.Cmd{}
	*cmd = *c.cmd // concurrency safety

	var in, out *os.File
	for i, arg := range cmd.Args {
		if j := strings.Index(arg, "$in"); j != -1 {
			var err error
			ext := cmdArgExtension.FindString(arg[j+3:])
			if in, err = ioutil.TempFile("", "minify-in-*"+ext); err != nil {
				return err
			}
			cmd.Args[i] = arg[:j] + in.Name() + arg[j+3+len(ext):]
		} else if j := strings.Index(arg, "$out"); j != -1 {
			var err error
			ext := cmdArgExtension.FindString(arg[j+4:])
			if out, err = ioutil.TempFile("", "minify-out-*"+ext); err != nil {
				return err
			}
			cmd.Args[i] = arg[:j] + out.Name() + arg[j+4+len(ext):]
		}
	}

	if in == nil {
		cmd.Stdin = r
	} else if _, err := io.Copy(in, r); err != nil {
		return err
	}
	if out == nil {
		cmd.Stdout = w
	} else {
		defer io.Copy(w, out)
	}
	stderr := &bytes.Buffer{}
	cmd.Stderr = stderr

	err := cmd.Run()
	if _, ok := err.(*exec.ExitError); ok {
		if stderr.Len() != 0 {
			err = fmt.Errorf("%s", stderr.String())
		}
		err = fmt.Errorf("command %s failed: %w", cmd.Path, err)
	}
	return err
}

////////////////////////////////////////////////////////////////

// M holds a map of mimetype => function to allow recursive minifier calls of the minifier functions.
type M struct {
	mutex   sync.RWMutex
	literal map[string]Minifier
	pattern []patternMinifier

	URL *url.URL
}

// New returns a new M.
func New() *M {
	return &M{
		sync.RWMutex{},
		map[string]Minifier{},
		[]patternMinifier{},
		nil,
	}
}

// Add adds a minifier to the mimetype => function map (unsafe for concurrent use).
func (m *M) Add(mimetype string, minifier Minifier) {
	m.mutex.Lock()
	m.literal[mimetype] = minifier
	m.mutex.Unlock()
}

// AddFunc adds a minify function to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddFunc(mimetype string, minifier MinifierFunc) {
	m.mutex.Lock()
	m.literal[mimetype] = minifier
	m.mutex.Unlock()
}

// AddRegexp adds a minifier to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddRegexp(pattern *regexp.Regexp, minifier Minifier) {
	m.mutex.Lock()
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
	m.mutex.Unlock()
}

// AddFuncRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddFuncRegexp(pattern *regexp.Regexp, minifier MinifierFunc) {
	m.mutex.Lock()
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
	m.mutex.Unlock()
}

// AddCmd adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype.
func (m *M) AddCmd(mimetype string, cmd *exec.Cmd) {
	m.mutex.Lock()
	m.literal[mimetype] = &cmdMinifier{cmd}
	m.mutex.Unlock()
}

// AddCmdRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype regular expression.
func (m *M) AddCmdRegexp(pattern *regexp.Regexp, cmd *exec.Cmd) {
	m.mutex.Lock()
	m.pattern = append(m.pattern, patternMinifier{pattern, &cmdMinifier{cmd}})
	m.mutex.Unlock()
}

// Match returns the pattern and minifier that gets matched with the mediatype.
// It returns nil when no matching minifier exists.
// It has the same matching algorithm as Minify.
func (m *M) Match(mediatype string) (string, map[string]string, MinifierFunc) {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	mimetype, params := parse.Mediatype([]byte(mediatype))
	if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
		return string(mimetype), params, minifier.Minify
	}

	for _, minifier := range m.pattern {
		if minifier.pattern.Match(mimetype) {
			return minifier.pattern.String(), params, minifier.Minify
		}
	}
	return string(mimetype), params, nil
}

// Minify minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
// An error is returned when no such mimetype exists (ErrNotExist) or when an error occurred in the minifier function.
// Mediatype may take the form of 'text/plain', 'text/*', '*/*' or 'text/plain; charset=UTF-8; version=2.0'.
func (m *M) Minify(mediatype string, w io.Writer, r io.Reader) error {
	mimetype, params := parse.Mediatype([]byte(mediatype))
	return m.MinifyMimetype(mimetype, w, r, params)
}

// MinifyMimetype minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
// It is a lower level version of Minify and requires the mediatype to be split up into mimetype and parameters.
// It is mostly used internally by minifiers because it is faster (no need to convert a byte-slice to string and vice versa).
func (m *M) MinifyMimetype(mimetype []byte, w io.Writer, r io.Reader, params map[string]string) error {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
		return minifier.Minify(m, w, r, params)
	}
	for _, minifier := range m.pattern {
		if minifier.pattern.Match(mimetype) {
			return minifier.Minify(m, w, r, params)
		}
	}
	return ErrNotExist
}

// Bytes minifies an array of bytes (safe for concurrent use). When an error occurs it return the original array and the error.
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
func (m *M) Bytes(mediatype string, v []byte) ([]byte, error) {
	out := buffer.NewWriter(make([]byte, 0, len(v)))
	if err := m.Minify(mediatype, out, buffer.NewReader(v)); err != nil {
		return v, err
	}
	return out.Bytes(), nil
}

// String minifies a string (safe for concurrent use). When an error occurs it return the original string and the error.
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
func (m *M) String(mediatype string, v string) (string, error) {
	out := buffer.NewWriter(make([]byte, 0, len(v)))
	if err := m.Minify(mediatype, out, buffer.NewReader([]byte(v))); err != nil {
		return v, err
	}
	return string(out.Bytes()), nil
}

// Reader wraps a Reader interface and minifies the stream.
// Errors from the minifier are returned by the reader.
func (m *M) Reader(mediatype string, r io.Reader) io.Reader {
	pr, pw := io.Pipe()
	go func() {
		if err := m.Minify(mediatype, pw, r); err != nil {
			pw.CloseWithError(err)
		} else {
			pw.Close()
		}
	}()
	return pr
}

// minifyWriter makes sure that errors from the minifier are passed down through Close (can be blocking).
type minifyWriter struct {
	pw  *io.PipeWriter
	wg  sync.WaitGroup
	err error
}

// Write intercepts any writes to the writer.
func (w *minifyWriter) Write(b []byte) (int, error) {
	n, _ := w.pw.Write(b)
	return n, w.err
}

// Close must be called when writing has finished. It returns the error from the minifier.
func (w *minifyWriter) Close() error {
	w.pw.Close()
	w.wg.Wait()
	return w.err
}

// Writer wraps a Writer interface and minifies the stream.
// Errors from the minifier are returned by Close on the writer.
// The writer must be closed explicitly.
func (m *M) Writer(mediatype string, w io.Writer) *minifyWriter {
	pr, pw := io.Pipe()
	mw := &minifyWriter{pw, sync.WaitGroup{}, nil}
	mw.wg.Add(1)
	go func() {
		defer mw.wg.Done()

		if err := m.Minify(mediatype, w, pr); err != nil {
			mw.err = err
			io.Copy(w, pr)
		}
		pr.Close()
	}()
	return mw
}

// minifyResponseWriter wraps an http.ResponseWriter and makes sure that errors from the minifier are passed down through Close (can be blocking).
// All writes to the response writer are intercepted and minified on the fly.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
type minifyResponseWriter struct {
	http.ResponseWriter

	writer    *minifyWriter
	m         *M
	mediatype string
}

// WriteHeader intercepts any header writes and removes the Content-Length header.
func (w *minifyResponseWriter) WriteHeader(status int) {
	w.ResponseWriter.Header().Del("Content-Length")
	w.ResponseWriter.WriteHeader(status)
}

// Write intercepts any writes to the response writer.
// The first write will extract the Content-Type as the mediatype. Otherwise it falls back to the RequestURI extension.
func (w *minifyResponseWriter) Write(b []byte) (int, error) {
	if w.writer == nil {
		// first write
		if mediatype := w.ResponseWriter.Header().Get("Content-Type"); mediatype != "" {
			w.mediatype = mediatype
		}
		w.writer = w.m.Writer(w.mediatype, w.ResponseWriter)
	}
	return w.writer.Write(b)
}

// Close must be called when writing has finished. It returns the error from the minifier.
func (w *minifyResponseWriter) Close() error {
	if w.writer != nil {
		return w.writer.Close()
	}
	return nil
}

// ResponseWriter minifies any writes to the http.ResponseWriter.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
// Minification might be slower than just sending the original file! Caching is advised.
func (m *M) ResponseWriter(w http.ResponseWriter, r *http.Request) *minifyResponseWriter {
	mediatype := mime.TypeByExtension(path.Ext(r.RequestURI))
	return &minifyResponseWriter{w, nil, m, mediatype}
}

// Middleware provides a middleware function that minifies content on the fly by intercepting writes to http.ResponseWriter.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
// Minification might be slower than just sending the original file! Caching is advised.
func (m *M) Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mw := m.ResponseWriter(w, r)
		defer mw.Close()

		next.ServeHTTP(mw, r)
	})
}
<|endoftext|>