<commit_before>// TRANSACTION SIGNATURE (TSIG)
//
// An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
// The supported algorithms include: HmacMD5, HmacSHA1 and HmacSHA256.
//
// Basic use pattern when querying with a TSIG name "axfr." (note that these key names
// must be fully qualified - as they are domain names) and the base64 secret
// "so6ZGir4GPAqINNh9U5c3A==":
//
//	c := new(dns.Client)
//	c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
//	m := new(dns.Msg)
//	m.SetQuestion("miek.nl.", dns.TypeMX)
//	m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
//	...
//	// When sending the TSIG RR is calculated and filled in before sending
//
// When requesting an AXFR (almost all TSIG usage is when requesting zone transfers), with
// TSIG, this is the basic use pattern. In this example we request an AXFR for
// miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A=="
// and using the server 85.223.71.124
//
//	c := new(dns.Client)
//	c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
//	m := new(dns.Msg)
//	m.SetAxfr("miek.nl.")
//	m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
//	t, err := c.TransferIn(m, "85.223.71.124:53")
//	for r := range t { /* ... */ }
//
// You can now read the records from the AXFR as they come in. Each envelope is checked with TSIG.
// If something is not correct an error is returned.
//
// Basic use pattern validating and replying to a message that has TSIG set.
//
//	server := &dns.Server{Addr: ":53", Net: "udp"}
//	server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
//	go server.ListenAndServe()
//	dns.HandleFunc(".", handleRequest)
//
// 	func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
//		m := new(Msg)
//		m.SetReply(r)
//		if r.IsTsig() {
//			if w.TsigStatus() == nil {
//				// *Msg r has an TSIG record and it was validated
//				m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
//			} else {
//				// *Msg r has an TSIG records and it was not valided
//			}
//		}
//		w.Write(m)
//	}
package dns

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"hash"
	"io"
	"strconv"
	"strings"
	"time"
)

// HMAC hashing codes. These are transmitted as domain names.
const (
	HmacMD5    = "hmac-md5.sig-alg.reg.int."
	HmacSHA1   = "hmac-sha1."
	HmacSHA256 = "hmac-sha256."
)

type TSIG struct {
	Hdr        RR_Header
	Algorithm  string `dns:"domain-name"`
	TimeSigned uint64 `dns:"uint48"`
	Fudge      uint16
	MACSize    uint16
	MAC        string `dns:"size-hex"`
	OrigId     uint16
	Error      uint16
	OtherLen   uint16
	OtherData  string `dns:"size-hex"`
}

func (rr *TSIG) Header() *RR_Header {
	return &rr.Hdr
}

// TSIG has no official presentation format, but this will suffice.

func (rr *TSIG) String() string {
	s := "\n;; TSIG PSEUDOSECTION:\n"
	s += rr.Hdr.String() +
		" " + rr.Algorithm +
		" " + tsigTimeToString(rr.TimeSigned) +
		" " + strconv.Itoa(int(rr.Fudge)) +
		" " + strconv.Itoa(int(rr.MACSize)) +
		" " + strings.ToUpper(rr.MAC) +
		" " + strconv.Itoa(int(rr.OrigId)) +
		" " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR
		" " + strconv.Itoa(int(rr.OtherLen)) +
		" " + rr.OtherData
	return s
}

func (rr *TSIG) Len() int {
	return rr.Hdr.Len() + len(rr.Algorithm) + 1 + 6 +
		4 + len(rr.MAC)/2 + 1 + 6 + len(rr.OtherData)/2 + 1
}

func (rr *TSIG) Copy() RR {
	return &TSIG{*rr.Hdr.CopyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
}

// The following values must be put in wireformat, so that the MAC can be calculated.
// RFC 2845, section 3.4.2. TSIG Variables.
type tsigWireFmt struct {
	// From RR_Header
	Name  string `dns:"domain-name"`
	Class uint16
	Ttl   uint32
	// Rdata of the TSIG
	Algorithm  string `dns:"domain-name"`
	TimeSigned uint64
	Fudge      uint16
	// MACSize, MAC and OrigId excluded
	Error     uint16
	OtherLen  uint16
	OtherData string `dns:"size-hex"`
}

// If we have the MAC use this type to convert it to wiredata.
// Section 3.4.3. Request MAC
type macWireFmt struct {
	MACSize uint16
	MAC     string `dns:"size-hex"`
}

// 3.3. Time values used in TSIG calculations
type timerWireFmt struct {
	TimeSigned uint64
	Fudge      uint16
}

// TsigGenerate fills out the TSIG record attached to the message.
// The message should contain
// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
// time fudge (defaults to 300 seconds) and the current time
// The TSIG MAC is saved in that Tsig RR.
// When TsigGenerate is called for the first time requestMAC is set to the empty string and
// timersOnly is false.
// If something goes wrong an error is returned, otherwise it is nil.
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
	if m.IsTsig() == nil {
		panic("dns: TSIG not last RR in additional")
	}
	// If we barf here, the caller is to blame
	rawsecret, err := packBase64([]byte(secret))
	if err != nil {
		return nil, "", err
	}

	rr := m.Extra[len(m.Extra)-1].(*TSIG)
	m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
	mbuf, err := m.Pack()
	if err != nil {
		return nil, "", err
	}
	buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)

	t := new(TSIG)
	var h hash.Hash
	switch rr.Algorithm {
	case HmacMD5:
		h = hmac.New(md5.New, []byte(rawsecret))
	case HmacSHA1:
		h = hmac.New(sha1.New, []byte(rawsecret))
	case HmacSHA256:
		h = hmac.New(sha256.New, []byte(rawsecret))
	default:
		return nil, "", ErrKeyAlg
	}
	io.WriteString(h, string(buf))
	t.MAC = hex.EncodeToString(h.Sum(nil))
	t.MACSize = uint16(len(t.MAC) / 2) // Size is half!

	t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
	t.Fudge = rr.Fudge
	t.TimeSigned = rr.TimeSigned
	t.Algorithm = rr.Algorithm
	t.OrigId = m.Id

	tbuf := make([]byte, t.Len())
	if off, err := PackRR(t, tbuf, 0, nil, false); err == nil {
		tbuf = tbuf[:off] // reset to actual size used
	} else {
		return nil, "", err
	}
	mbuf = append(mbuf, tbuf...)
	rawSetExtraLen(mbuf, uint16(len(m.Extra)+1))
	return mbuf, t.MAC, nil
}

// TsigVerify verifies the TSIG on a message.
// If the signature does not validate err contains the
// error, otherwise it is nil.
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
	rawsecret, err := packBase64([]byte(secret))
	if err != nil {
		return err
	}
	// Srtip the TSIG from the incoming msg
	stripped, tsig, err := stripTsig(msg)
	if err != nil {
		return err
	}

	buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
	ti := uint64(time.Now().Unix()) - tsig.TimeSigned
	if uint64(tsig.Fudge) < ti {
		return ErrTime
	}

	var h hash.Hash
	switch tsig.Algorithm {
	case HmacMD5:
		h = hmac.New(md5.New, []byte(rawsecret))
	case HmacSHA1:
		h = hmac.New(sha1.New, []byte(rawsecret))
	case HmacSHA256:
		h = hmac.New(sha256.New, []byte(rawsecret))
	default:
		return ErrKeyAlg
	}
	io.WriteString(h, string(buf))
	if strings.ToUpper(hex.EncodeToString(h.Sum(nil))) != strings.ToUpper(tsig.MAC) {
		return ErrSig
	}
	return nil
}

// Create a wiredata buffer for the MAC calculation.
func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte {
	var buf []byte
	if rr.TimeSigned == 0 {
		rr.TimeSigned = uint64(time.Now().Unix())
	}
	if rr.Fudge == 0 {
		rr.Fudge = 300 // Standard (RFC) default.
	}

	if requestMAC != "" {
		m := new(macWireFmt)
		m.MACSize = uint16(len(requestMAC) / 2)
		m.MAC = requestMAC
		buf = make([]byte, len(requestMAC)) // long enough
		n, _ := PackStruct(m, buf, 0)
		buf = buf[:n]
	}

	tsigvar := make([]byte, DefaultMsgSize)
	if timersOnly {
		tsig := new(timerWireFmt)
		tsig.TimeSigned = rr.TimeSigned
		tsig.Fudge = rr.Fudge
		n, _ := PackStruct(tsig, tsigvar, 0)
		tsigvar = tsigvar[:n]
	} else {
		tsig := new(tsigWireFmt)
		tsig.Name = strings.ToLower(rr.Hdr.Name)
		tsig.Class = ClassANY
		tsig.Ttl = rr.Hdr.Ttl
		tsig.Algorithm = strings.ToLower(rr.Algorithm)
		tsig.TimeSigned = rr.TimeSigned
		tsig.Fudge = rr.Fudge
		tsig.Error = rr.Error
		tsig.OtherLen = rr.OtherLen
		tsig.OtherData = rr.OtherData
		n, _ := PackStruct(tsig, tsigvar, 0)
		tsigvar = tsigvar[:n]
	}

	if requestMAC != "" {
		x := append(buf, msgbuf...)
		buf = append(x, tsigvar...)
	} else {
		buf = append(msgbuf, tsigvar...)
	}
	return buf
}

// Strip the TSIG from the raw message
func stripTsig(msg []byte) ([]byte, *TSIG, error) {
	// Copied from msg.go's Unpack()
	// Header.
	var dh Header
	var err error
	dns := new(Msg)
	rr := new(TSIG)
	off := 0
	tsigoff := 0
	if off, err = UnpackStruct(&dh, msg, off); err != nil {
		return nil, nil, err
	}
	if dh.Arcount == 0 {
		return nil, nil, ErrNoSig
	}
	// Rcode, see msg.go Unpack()
	if int(dh.Bits&0xF) == RcodeNotAuth {
		return nil, nil, ErrAuth
	}

	// Arrays.
	dns.Question = make([]Question, dh.Qdcount)
	dns.Answer = make([]RR, dh.Ancount)
	dns.Ns = make([]RR, dh.Nscount)
	dns.Extra = make([]RR, dh.Arcount)

	for i := 0; i < len(dns.Question); i++ {
		off, err = UnpackStruct(&dns.Question[i], msg, off)
		if err != nil {
			return nil, nil, err
		}
	}
	for i := 0; i < len(dns.Answer); i++ {
		dns.Answer[i], off, err = UnpackRR(msg, off)
		if err != nil {
			return nil, nil, err
		}
	}
	for i := 0; i < len(dns.Ns); i++ {
		dns.Ns[i], off, err = UnpackRR(msg, off)
		if err != nil {
			return nil, nil, err
		}
	}
	for i := 0; i < len(dns.Extra); i++ {
		tsigoff = off
		dns.Extra[i], off, err = UnpackRR(msg, off)
		if err != nil {
			return nil, nil, err
		}
		if dns.Extra[i].Header().Rrtype == TypeTSIG {
			rr = dns.Extra[i].(*TSIG)
			// Adjust Arcount.
			arcount, _ := unpackUint16(msg, 10)
			msg[10], msg[11] = packUint16(arcount - 1)
			break
		}
	}
	if rr == nil {
		return nil, nil, ErrNoSig
	}
	return msg[:tsigoff], rr, nil
}

// Translate the TSIG time signed into a date. There is no
// need for RFC1982 calculations as this date is 48 bits.
func tsigTimeToString(t uint64) string {
	ti := time.Unix(int64(t), 0).UTC()
	return ti.Format("20060102150405")
}
<commit_msg>Fix TSIG<commit_after>// TRANSACTION SIGNATURE (TSIG)
//
// An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
// The supported algorithms include: HmacMD5, HmacSHA1 and HmacSHA256.
//
// Basic use pattern when querying with a TSIG name "axfr." (note that these key names
// must be fully qualified - as they are domain names) and the base64 secret
// "so6ZGir4GPAqINNh9U5c3A==":
//
//	c := new(dns.Client)
//	c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
//	m := new(dns.Msg)
//	m.SetQuestion("miek.nl.", dns.TypeMX)
//	m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
//	...
//	// When sending the TSIG RR is calculated and filled in before sending
//
// When requesting an AXFR (almost all TSIG usage is when requesting zone transfers), with
// TSIG, this is the basic use pattern. In this example we request an AXFR for
// miek.nl. with TSIG key named "axfr." and secret "so6ZGir4GPAqINNh9U5c3A=="
// and using the server 85.223.71.124
//
//	c := new(dns.Client)
//	c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
//	m := new(dns.Msg)
//	m.SetAxfr("miek.nl.")
//	m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
//	t, err := c.TransferIn(m, "85.223.71.124:53")
//	for r := range t { /* ... */ }
//
// You can now read the records from the AXFR as they come in. Each envelope is checked with TSIG.
// If something is not correct an error is returned.
//
// Basic use pattern validating and replying to a message that has TSIG set.
//
//	server := &dns.Server{Addr: ":53", Net: "udp"}
//	server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
//	go server.ListenAndServe()
//	dns.HandleFunc(".", handleRequest)
//
// 	func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
//		m := new(Msg)
//		m.SetReply(r)
//		if r.IsTsig() {
//			if w.TsigStatus() == nil {
//				// *Msg r has an TSIG record and it was validated
//				m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix())
//			} else {
//				// *Msg r has an TSIG records and it was not valided
//			}
//		}
//		w.Write(m)
//	}
package dns

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"hash"
	"io"
	"strconv"
	"strings"
	"time"
)

// HMAC hashing codes. These are transmitted as domain names.
const (
	HmacMD5    = "hmac-md5.sig-alg.reg.int."
	HmacSHA1   = "hmac-sha1."
	HmacSHA256 = "hmac-sha256."
)

type TSIG struct {
	Hdr        RR_Header
	Algorithm  string `dns:"domain-name"`
	TimeSigned uint64 `dns:"uint48"`
	Fudge      uint16
	MACSize    uint16
	MAC        string `dns:"size-hex"`
	OrigId     uint16
	Error      uint16
	OtherLen   uint16
	OtherData  string `dns:"size-hex"`
}

func (rr *TSIG) Header() *RR_Header {
	return &rr.Hdr
}

// TSIG has no official presentation format, but this will suffice.

func (rr *TSIG) String() string {
	s := "\n;; TSIG PSEUDOSECTION:\n"
	s += rr.Hdr.String() +
		" " + rr.Algorithm +
		" " + tsigTimeToString(rr.TimeSigned) +
		" " + strconv.Itoa(int(rr.Fudge)) +
		" " + strconv.Itoa(int(rr.MACSize)) +
		" " + strings.ToUpper(rr.MAC) +
		" " + strconv.Itoa(int(rr.OrigId)) +
		" " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR
		" " + strconv.Itoa(int(rr.OtherLen)) +
		" " + rr.OtherData
	return s
}

func (rr *TSIG) Len() int {
	return rr.Hdr.Len() + len(rr.Algorithm) + 1 + 6 +
		4 + len(rr.MAC)/2 + 1 + 6 + len(rr.OtherData)/2 + 1
}

func (rr *TSIG) Copy() RR {
	return &TSIG{*rr.Hdr.CopyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
}

// The following values must be put in wireformat, so that the MAC can be calculated.
// RFC 2845, section 3.4.2. TSIG Variables.
type tsigWireFmt struct {
	// From RR_Header
	Name  string `dns:"domain-name"`
	Class uint16
	Ttl   uint32
	// Rdata of the TSIG
	Algorithm  string `dns:"domain-name"`
	TimeSigned uint64 `dns:"uint48"`
	Fudge      uint16
	// MACSize, MAC and OrigId excluded
	Error     uint16
	OtherLen  uint16
	OtherData string `dns:"size-hex"`
}

// If we have the MAC use this type to convert it to wiredata.
// Section 3.4.3. Request MAC
type macWireFmt struct {
	MACSize uint16
	MAC     string `dns:"size-hex"`
}

// 3.3. Time values used in TSIG calculations
type timerWireFmt struct {
	TimeSigned uint64 `dns:"uint48"`
	Fudge      uint16
}

// TsigGenerate fills out the TSIG record attached to the message.
// The message should contain
// a "stub" TSIG RR with the algorithm, key name (owner name of the RR),
// time fudge (defaults to 300 seconds) and the current time
// The TSIG MAC is saved in that Tsig RR.
// When TsigGenerate is called for the first time requestMAC is set to the empty string and
// timersOnly is false.
// If something goes wrong an error is returned, otherwise it is nil.
func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) {
	if m.IsTsig() == nil {
		panic("dns: TSIG not last RR in additional")
	}
	// If we barf here, the caller is to blame
	rawsecret, err := packBase64([]byte(secret))
	if err != nil {
		return nil, "", err
	}

	rr := m.Extra[len(m.Extra)-1].(*TSIG)
	m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg
	mbuf, err := m.Pack()
	if err != nil {
		return nil, "", err
	}
	buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly)

	t := new(TSIG)
	var h hash.Hash
	switch rr.Algorithm {
	case HmacMD5:
		h = hmac.New(md5.New, []byte(rawsecret))
	case HmacSHA1:
		h = hmac.New(sha1.New, []byte(rawsecret))
	case HmacSHA256:
		h = hmac.New(sha256.New, []byte(rawsecret))
	default:
		return nil, "", ErrKeyAlg
	}
	io.WriteString(h, string(buf))
	t.MAC = hex.EncodeToString(h.Sum(nil))
	t.MACSize = uint16(len(t.MAC) / 2) // Size is half!

	t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0}
	t.Fudge = rr.Fudge
	t.TimeSigned = rr.TimeSigned
	t.Algorithm = rr.Algorithm
	t.OrigId = m.Id

	tbuf := make([]byte, t.Len())
	if off, err := PackRR(t, tbuf, 0, nil, false); err == nil {
		tbuf = tbuf[:off] // reset to actual size used
	} else {
		return nil, "", err
	}
	mbuf = append(mbuf, tbuf...)
	rawSetExtraLen(mbuf, uint16(len(m.Extra)+1))
	return mbuf, t.MAC, nil
}

// TsigVerify verifies the TSIG on a message.
// If the signature does not validate err contains the
// error, otherwise it is nil.
func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error {
	rawsecret, err := packBase64([]byte(secret))
	if err != nil {
		return err
	}
	// Srtip the TSIG from the incoming msg
	stripped, tsig, err := stripTsig(msg)
	if err != nil {
		return err
	}

	buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly)
	ti := uint64(time.Now().Unix()) - tsig.TimeSigned
	if uint64(tsig.Fudge) < ti {
		return ErrTime
	}

	var h hash.Hash
	switch tsig.Algorithm {
	case HmacMD5:
		h = hmac.New(md5.New, []byte(rawsecret))
	case HmacSHA1:
		h = hmac.New(sha1.New, []byte(rawsecret))
	case HmacSHA256:
		h = hmac.New(sha256.New, []byte(rawsecret))
	default:
		return ErrKeyAlg
	}
	io.WriteString(h, string(buf))
	if strings.ToUpper(hex.EncodeToString(h.Sum(nil))) != strings.ToUpper(tsig.MAC) {
		return ErrSig
	}
	return nil
}

// Create a wiredata buffer for the MAC calculation.
func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte {
	var buf []byte
	if rr.TimeSigned == 0 {
		rr.TimeSigned = uint64(time.Now().Unix())
	}
	if rr.Fudge == 0 {
		rr.Fudge = 300 // Standard (RFC) default.
	}

	if requestMAC != "" {
		m := new(macWireFmt)
		m.MACSize = uint16(len(requestMAC) / 2)
		m.MAC = requestMAC
		buf = make([]byte, len(requestMAC)) // long enough
		n, _ := PackStruct(m, buf, 0)
		buf = buf[:n]
	}

	tsigvar := make([]byte, DefaultMsgSize)
	if timersOnly {
		tsig := new(timerWireFmt)
		tsig.TimeSigned = rr.TimeSigned
		tsig.Fudge = rr.Fudge
		n, _ := PackStruct(tsig, tsigvar, 0)
		tsigvar = tsigvar[:n]
	} else {
		tsig := new(tsigWireFmt)
		tsig.Name = strings.ToLower(rr.Hdr.Name)
		tsig.Class = ClassANY
		tsig.Ttl = rr.Hdr.Ttl
		tsig.Algorithm = strings.ToLower(rr.Algorithm)
		tsig.TimeSigned = rr.TimeSigned
		tsig.Fudge = rr.Fudge
		tsig.Error = rr.Error
		tsig.OtherLen = rr.OtherLen
		tsig.OtherData = rr.OtherData
		n, _ := PackStruct(tsig, tsigvar, 0)
		tsigvar = tsigvar[:n]
	}

	if requestMAC != "" {
		x := append(buf, msgbuf...)
		buf = append(x, tsigvar...)
	} else {
		buf = append(msgbuf, tsigvar...)
	}
	return buf
}

// Strip the TSIG from the raw message
func stripTsig(msg []byte) ([]byte, *TSIG, error) {
	// Copied from msg.go's Unpack()
	// Header.
	var dh Header
	var err error
	dns := new(Msg)
	rr := new(TSIG)
	off := 0
	tsigoff := 0
	if off, err = UnpackStruct(&dh, msg, off); err != nil {
		return nil, nil, err
	}
	if dh.Arcount == 0 {
		return nil, nil, ErrNoSig
	}
	// Rcode, see msg.go Unpack()
	if int(dh.Bits&0xF) == RcodeNotAuth {
		return nil, nil, ErrAuth
	}

	// Arrays.
	dns.Question = make([]Question, dh.Qdcount)
	dns.Answer = make([]RR, dh.Ancount)
	dns.Ns = make([]RR, dh.Nscount)
	dns.Extra = make([]RR, dh.Arcount)

	for i := 0; i < len(dns.Question); i++ {
		off, err = UnpackStruct(&dns.Question[i], msg, off)
		if err != nil {
			return nil, nil, err
		}
	}
	for i := 0; i < len(dns.Answer); i++ {
		dns.Answer[i], off, err = UnpackRR(msg, off)
		if err != nil {
			return nil, nil, err
		}
	}
	for i := 0; i < len(dns.Ns); i++ {
		dns.Ns[i], off, err = UnpackRR(msg, off)
		if err != nil {
			return nil, nil, err
		}
	}
	for i := 0; i < len(dns.Extra); i++ {
		tsigoff = off
		dns.Extra[i], off, err = UnpackRR(msg, off)
		if err != nil {
			return nil, nil, err
		}
		if dns.Extra[i].Header().Rrtype == TypeTSIG {
			rr = dns.Extra[i].(*TSIG)
			// Adjust Arcount.
			arcount, _ := unpackUint16(msg, 10)
			msg[10], msg[11] = packUint16(arcount - 1)
			break
		}
	}
	if rr == nil {
		return nil, nil, ErrNoSig
	}
	return msg[:tsigoff], rr, nil
}

// Translate the TSIG time signed into a date. There is no
// need for RFC1982 calculations as this date is 48 bits.
func tsigTimeToString(t uint64) string {
	ti := time.Unix(int64(t), 0).UTC()
	return ti.Format("20060102150405")
}
<|endoftext|>


<commit_before>// Copyright 2009 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 (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"reflect"
	"strings"
	"unicode"
	"utf8"
)

func initRewrite() {
	if *rewriteRule == "" {
		return
	}
	f := strings.Split(*rewriteRule, "->", 0)
	if len(f) != 2 {
		fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n")
		os.Exit(2)
	}
	pattern := parseExpr(f[0], "pattern")
	replace := parseExpr(f[1], "replacement")
	rewrite = func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) }
}

// parseExpr parses s as an expression.
// It might make sense to expand this to allow statement patterns,
// but there are problems with preserving formatting and also
// with what a wildcard for a statement looks like.
func parseExpr(s string, what string) ast.Expr {
	stmts, err := parser.ParseStmtList("input", s)
	if err != nil {
		fmt.Fprintf(os.Stderr, "parsing %s %s: %s\n", what, s, err)
		os.Exit(2)
	}
	if len(stmts) != 1 {
		fmt.Fprintf(os.Stderr, "%s must be single expression\n", what)
		os.Exit(2)
	}
	x, ok := stmts[0].(*ast.ExprStmt)
	if !ok {
		fmt.Fprintf(os.Stderr, "%s must be single expression\n", what)
		os.Exit(2)
	}
	return x.X
}

// rewriteFile applys the rewrite rule pattern -> replace to an entire file.
func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
	m := make(map[string]reflect.Value)
	pat := reflect.NewValue(pattern)
	repl := reflect.NewValue(replace)
	var f func(val reflect.Value) reflect.Value // f is recursive
	f = func(val reflect.Value) reflect.Value {
		for k := range m {
			m[k] = nil, false
		}
		val = apply(f, val)
		if match(m, pat, val) {
			val = subst(m, repl, reflect.NewValue(val.Interface().(ast.Node).Pos()))
		}
		return val
	}
	return apply(f, reflect.NewValue(p)).Interface().(*ast.File)
}

var positionType = reflect.Typeof(token.Position{})
var identType = reflect.Typeof((*ast.Ident)(nil))

func isWildcard(s string) bool {
	rune, size := utf8.DecodeRuneInString(s)
	return size == len(s) && unicode.IsLower(rune)
}

// apply replaces each AST field x in val with f(x), returning val.
// To avoid extra conversions, f operates on the reflect.Value form.
func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value {
	if val == nil {
		return nil
	}
	switch v := reflect.Indirect(val).(type) {
	case *reflect.SliceValue:
		for i := 0; i < v.Len(); i++ {
			e := v.Elem(i)
			e.SetValue(f(e))
		}
	case *reflect.StructValue:
		for i := 0; i < v.NumField(); i++ {
			e := v.Field(i)
			e.SetValue(f(e))
		}
	case *reflect.InterfaceValue:
		e := v.Elem()
		v.SetValue(f(e))
	}
	return val
}

// match returns true if pattern matches val,
// recording wildcard submatches in m.
// If m == nil, match checks whether pattern == val.
func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
	// Wildcard matches any expression.  If it appears multiple
	// times in the pattern, it must match the same expression
	// each time.
	if m != nil && pattern.Type() == identType {
		name := pattern.Interface().(*ast.Ident).Value
		if isWildcard(name) {
			if old, ok := m[name]; ok {
				return match(nil, old, val)
			}
			m[name] = val
			return true
		}
	}

	// Otherwise, the expressions must match recursively.
	if pattern == nil || val == nil {
		return pattern == nil && val == nil
	}
	if pattern.Type() != val.Type() {
		return false
	}

	// Token positions need not match.
	if pattern.Type() == positionType {
		return true
	}

	p := reflect.Indirect(pattern)
	v := reflect.Indirect(val)

	switch p := p.(type) {
	case *reflect.SliceValue:
		v := v.(*reflect.SliceValue)
		for i := 0; i < p.Len(); i++ {
			if !match(m, p.Elem(i), v.Elem(i)) {
				return false
			}
		}
		return true

	case *reflect.StructValue:
		v := v.(*reflect.StructValue)
		for i := 0; i < p.NumField(); i++ {
			if !match(m, p.Field(i), v.Field(i)) {
				return false
			}
		}
		return true

	case *reflect.InterfaceValue:
		v := v.(*reflect.InterfaceValue)
		return match(m, p.Elem(), v.Elem())
	}

	// Handle token integers, etc.
	return p.Interface() == v.Interface()
}

// subst returns a copy of pattern with values from m substituted in place
// of wildcards and pos used as the position of tokens from the pattern.
// if m == nil, subst returns a copy of pattern and doesn't change the line
// number information.
func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value {
	if pattern == nil {
		return nil
	}

	// Wildcard gets replaced with map value.
	if m != nil && pattern.Type() == identType {
		name := pattern.Interface().(*ast.Ident).Value
		if isWildcard(name) {
			if old, ok := m[name]; ok {
				return subst(nil, old, nil)
			}
		}
	}

	if pos != nil && pattern.Type() == positionType {
		return pos
	}

	// Otherwise copy.
	switch p := pattern.(type) {
	case *reflect.SliceValue:
		v := reflect.MakeSlice(p.Type().(*reflect.SliceType), p.Len(), p.Len())
		for i := 0; i < p.Len(); i++ {
			v.Elem(i).SetValue(subst(m, p.Elem(i), pos))
		}
		return v

	case *reflect.StructValue:
		v := reflect.MakeZero(p.Type()).(*reflect.StructValue)
		for i := 0; i < p.NumField(); i++ {
			v.Field(i).SetValue(subst(m, p.Field(i), pos))
		}
		return v

	case *reflect.PtrValue:
		v := reflect.MakeZero(p.Type()).(*reflect.PtrValue)
		v.PointTo(subst(m, p.Elem(), pos))
		return v

	case *reflect.InterfaceValue:
		v := reflect.MakeZero(p.Type()).(*reflect.InterfaceValue)
		v.SetValue(subst(m, p.Elem(), pos))
		return v
	}

	return pattern
}
<commit_msg>- Parse expressions as opposed to statements for gofmt rewrite patterns. Allows stand-alone types (e.g. []int as patterns) and doesn't require a semicolon at the end (which are now mandatory terminators).<commit_after>// Copyright 2009 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 (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"reflect"
	"strings"
	"unicode"
	"utf8"
)

func initRewrite() {
	if *rewriteRule == "" {
		return
	}
	f := strings.Split(*rewriteRule, "->", 0)
	if len(f) != 2 {
		fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n")
		os.Exit(2)
	}
	pattern := parseExpr(f[0], "pattern")
	replace := parseExpr(f[1], "replacement")
	rewrite = func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) }
}

// parseExpr parses s as an expression.
// It might make sense to expand this to allow statement patterns,
// but there are problems with preserving formatting and also
// with what a wildcard for a statement looks like.
func parseExpr(s string, what string) ast.Expr {
	x, err := parser.ParseExpr("input", s)
	if err != nil {
		fmt.Fprintf(os.Stderr, "parsing %s %s: %s\n", what, s, err)
		os.Exit(2)
	}
	return x
}

// rewriteFile applys the rewrite rule pattern -> replace to an entire file.
func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
	m := make(map[string]reflect.Value)
	pat := reflect.NewValue(pattern)
	repl := reflect.NewValue(replace)
	var f func(val reflect.Value) reflect.Value // f is recursive
	f = func(val reflect.Value) reflect.Value {
		for k := range m {
			m[k] = nil, false
		}
		val = apply(f, val)
		if match(m, pat, val) {
			val = subst(m, repl, reflect.NewValue(val.Interface().(ast.Node).Pos()))
		}
		return val
	}
	return apply(f, reflect.NewValue(p)).Interface().(*ast.File)
}

var positionType = reflect.Typeof(token.Position{})
var identType = reflect.Typeof((*ast.Ident)(nil))

func isWildcard(s string) bool {
	rune, size := utf8.DecodeRuneInString(s)
	return size == len(s) && unicode.IsLower(rune)
}

// apply replaces each AST field x in val with f(x), returning val.
// To avoid extra conversions, f operates on the reflect.Value form.
func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value {
	if val == nil {
		return nil
	}
	switch v := reflect.Indirect(val).(type) {
	case *reflect.SliceValue:
		for i := 0; i < v.Len(); i++ {
			e := v.Elem(i)
			e.SetValue(f(e))
		}
	case *reflect.StructValue:
		for i := 0; i < v.NumField(); i++ {
			e := v.Field(i)
			e.SetValue(f(e))
		}
	case *reflect.InterfaceValue:
		e := v.Elem()
		v.SetValue(f(e))
	}
	return val
}

// match returns true if pattern matches val,
// recording wildcard submatches in m.
// If m == nil, match checks whether pattern == val.
func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
	// Wildcard matches any expression.  If it appears multiple
	// times in the pattern, it must match the same expression
	// each time.
	if m != nil && pattern.Type() == identType {
		name := pattern.Interface().(*ast.Ident).Value
		if isWildcard(name) {
			if old, ok := m[name]; ok {
				return match(nil, old, val)
			}
			m[name] = val
			return true
		}
	}

	// Otherwise, the expressions must match recursively.
	if pattern == nil || val == nil {
		return pattern == nil && val == nil
	}
	if pattern.Type() != val.Type() {
		return false
	}

	// Token positions need not match.
	if pattern.Type() == positionType {
		return true
	}

	p := reflect.Indirect(pattern)
	v := reflect.Indirect(val)

	switch p := p.(type) {
	case *reflect.SliceValue:
		v := v.(*reflect.SliceValue)
		if p.Len() != v.Len() {
			return false
		}
		for i := 0; i < p.Len(); i++ {
			if !match(m, p.Elem(i), v.Elem(i)) {
				return false
			}
		}
		return true

	case *reflect.StructValue:
		v := v.(*reflect.StructValue)
		if p.NumField() != v.NumField() {
			return false
		}
		for i := 0; i < p.NumField(); i++ {
			if !match(m, p.Field(i), v.Field(i)) {
				return false
			}
		}
		return true

	case *reflect.InterfaceValue:
		v := v.(*reflect.InterfaceValue)
		return match(m, p.Elem(), v.Elem())
	}

	// Handle token integers, etc.
	return p.Interface() == v.Interface()
}

// subst returns a copy of pattern with values from m substituted in place
// of wildcards and pos used as the position of tokens from the pattern.
// if m == nil, subst returns a copy of pattern and doesn't change the line
// number information.
func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value {
	if pattern == nil {
		return nil
	}

	// Wildcard gets replaced with map value.
	if m != nil && pattern.Type() == identType {
		name := pattern.Interface().(*ast.Ident).Value
		if isWildcard(name) {
			if old, ok := m[name]; ok {
				return subst(nil, old, nil)
			}
		}
	}

	if pos != nil && pattern.Type() == positionType {
		return pos
	}

	// Otherwise copy.
	switch p := pattern.(type) {
	case *reflect.SliceValue:
		v := reflect.MakeSlice(p.Type().(*reflect.SliceType), p.Len(), p.Len())
		for i := 0; i < p.Len(); i++ {
			v.Elem(i).SetValue(subst(m, p.Elem(i), pos))
		}
		return v

	case *reflect.StructValue:
		v := reflect.MakeZero(p.Type()).(*reflect.StructValue)
		for i := 0; i < p.NumField(); i++ {
			v.Field(i).SetValue(subst(m, p.Field(i), pos))
		}
		return v

	case *reflect.PtrValue:
		v := reflect.MakeZero(p.Type()).(*reflect.PtrValue)
		v.PointTo(subst(m, p.Elem(), pos))
		return v

	case *reflect.InterfaceValue:
		v := reflect.MakeZero(p.Type()).(*reflect.InterfaceValue)
		v.SetValue(subst(m, p.Elem(), pos))
		return v
	}

	return pattern
}
<|endoftext|>


<commit_before>package ui

import (
	"image"
	"image/color"
	"image/png"
	"os"
	"strings"

	"github.com/eaburns/gui/thread0"
	"github.com/eaburns/gui/ui/gl"
)

// A Canvas provides a high-level interface for drawing 2d graphics.
type Canvas struct {
	solid *gl.Program
	tex   *gl.Program
	rect  *gl.Buffer
}

// NewCanvas returns a new canvas.
func NewCanvas() *Canvas {
	c := new(Canvas)
	thread0.Do(func() {
		gl.Enable(gl.Texture2D)
		gl.Enable(gl.Blend)
		gl.BlendFunc(gl.SrcAlpha, gl.OneMinusSrcAlpha)
		c.solid = loadSolidShader()
		c.tex = loadTextureShader()
		c.rect = makeRectBuffer()
	})
	return c
}

func loadSolidShader() *gl.Program {
	v := strings.NewReader(rectVertShader)
	f := strings.NewReader(solidFragShader)
	p, err := gl.NewProgram(v, f)
	if err != nil {
		panic(err)
	}
	return p
}

func loadTextureShader() *gl.Program {
	v := strings.NewReader(rectVertShader)
	f := strings.NewReader(texFragShader)
	p, err := gl.NewProgram(v, f)
	if err != nil {
		panic(err)
	}
	return p
}

func makeRectBuffer() *gl.Buffer {
	buf := gl.NewArrayBuffer()
	buf.SetData(
		gl.StaticDraw,
		0, 0, 0, 0,
		1, 0, 1, 0,
		0, 1, 0, 1,
		1, 1, 1, 1,
	)
	return buf
}

// Close releases the resources for the canvas.
func (c *Canvas) Close() {
	thread0.Do(func() {
		c.solid.Delete()
		c.tex.Delete()
		c.rect.Delete()
	})
}

// Clear clears the canvas with the given color.
func (c *Canvas) Clear(col color.Color) {
	thread0.Do(func() {
		gl.ClearColor(col)
		gl.Clear(gl.ColorBufferBit)
	})
}

// FillRect fills a rectangle with the given color.
// X and y specify the upper-right corner of the rectangle.
func (c *Canvas) FillRect(x, y, w, h float32, col color.Color) {
	thread0.Do(func() {
		r, g, b, a := col.RGBA()
		c.solid.SetUniform("color",
			float32(r)/0xFFFF,
			float32(g)/0xFFFF,
			float32(b)/0xFFFF,
			float32(a)/0xFFFF,
		)

		c.rect.Bind()
		c.solid.SetVertexAttributeData("vert", 4, 0, 0)
		c.solid.SetUniform("loc", x, y)
		c.solid.SetUniform("size", w, h)
		c.solid.DrawArrays(gl.TriangleStrip, 0, 4)
		if err := gl.CheckError(); err != nil {
			panic(err)
		}
	})
}

// An Image can be drawn to a canvas.
type Image struct {
	tex           gl.Texture
	Width, Height float32
}

// NewImage returns a new *Image from a normalized RGBA image.
func NewImage(img *image.NRGBA) *Image {
	var t gl.Texture
	thread0.Do(func() { t = gl.MakeImageTexture(img) })
	return &Image{
		tex:    t,
		Width:  float32(img.Bounds().Dx()),
		Height: float32(img.Bounds().Dy()),
	}
}

// LoadPng returns an *Image loaded from a PNG file.
func LoadPng(path string) (*Image, error) {
	r, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer r.Close()

	img, err := png.Decode(r)
	if err != nil {
		return nil, err
	}
	return NewImage(img.(*image.NRGBA)), nil
}

// DrawImage draws an image at the given location on the canvas.
// X and y specify the upper-left corner of the image.
func (c *Canvas) DrawImage(x, y float32, img *Image) {
	thread0.Do(func() {
		img.tex.Bind(0)
		c.tex.SetUniform("tex", 0)

		c.rect.Bind()
		c.tex.SetVertexAttributeData("vert", 4, 0, 0)
		c.tex.SetUniform("loc", x, y)
		c.tex.SetUniform("size", img.Width, img.Height)
		c.tex.DrawArrays(gl.TriangleStrip, 0, 4)
		if err := gl.CheckError(); err != nil {
			panic(err)
		}
	})
}

var (
	rectVertShader = `
		#version 120
		attribute vec4 vert;
		uniform vec2 loc;
		uniform vec2 size;
		varying vec2 texCoord;
		void main(){
			vec2 p = vec2(vert.x*size.x, vert.y*size.y);
			texCoord = vert.zw;
			gl_Position = gl_ModelViewProjectionMatrix * vec4(p + loc, 0, 1);
		}`

	solidFragShader = `
		#version 120
		uniform vec4 color;
		varying vec2 texCoord;
		void main()
		{
			gl_FragColor = color;
		}`

	texFragShader = `
		#version 120
		uniform sampler2D tex;
		varying vec2 texCoord;
		void main()
		{
			gl_FragColor = texture2D(tex, texCoord);
		}`
)
<commit_msg>Use better names for Canvas fields.<commit_after>package ui

import (
	"image"
	"image/color"
	"image/png"
	"os"
	"strings"

	"github.com/eaburns/gui/thread0"
	"github.com/eaburns/gui/ui/gl"
)

// A Canvas provides a high-level interface for drawing 2d graphics.
type Canvas struct {
	solidRectProg *gl.Program
	imgRectProg   *gl.Program
	rectVerts     *gl.Buffer
}

// NewCanvas returns a new canvas.
func NewCanvas() *Canvas {
	c := new(Canvas)
	thread0.Do(func() {
		gl.Enable(gl.Texture2D)
		gl.Enable(gl.Blend)
		gl.BlendFunc(gl.SrcAlpha, gl.OneMinusSrcAlpha)
		c.solidRectProg = loadSolidRectProg()
		c.imgRectProg = loadImgRectProg()
		c.rectVerts = makeRectBuffer()
	})
	return c
}

func loadSolidRectProg() *gl.Program {
	v := strings.NewReader(rectVertShader)
	f := strings.NewReader(solidFragShader)
	p, err := gl.NewProgram(v, f)
	if err != nil {
		panic(err)
	}
	return p
}

func loadImgRectProg() *gl.Program {
	v := strings.NewReader(rectVertShader)
	f := strings.NewReader(texFragShader)
	p, err := gl.NewProgram(v, f)
	if err != nil {
		panic(err)
	}
	return p
}

func makeRectBuffer() *gl.Buffer {
	buf := gl.NewArrayBuffer()
	buf.SetData(
		gl.StaticDraw,
		0, 0, 0, 0,
		1, 0, 1, 0,
		0, 1, 0, 1,
		1, 1, 1, 1,
	)
	return buf
}

// Close releases the resources for the canvas.
func (c *Canvas) Close() {
	thread0.Do(func() {
		c.solidRectProg.Delete()
		c.imgRectProg.Delete()
		c.rectVerts.Delete()
	})
}

// Clear clears the canvas with the given color.
func (c *Canvas) Clear(col color.Color) {
	thread0.Do(func() {
		gl.ClearColor(col)
		gl.Clear(gl.ColorBufferBit)
	})
}

// FillRect fills a rectangle with the given color.
// X and y specify the upper-right corner of the rectangle.
func (c *Canvas) FillRect(x, y, w, h float32, col color.Color) {
	thread0.Do(func() {
		r, g, b, a := col.RGBA()
		c.solidRectProg.SetUniform("color",
			float32(r)/0xFFFF,
			float32(g)/0xFFFF,
			float32(b)/0xFFFF,
			float32(a)/0xFFFF,
		)

		c.rectVerts.Bind()
		c.solidRectProg.SetVertexAttributeData("vert", 4, 0, 0)
		c.solidRectProg.SetUniform("loc", x, y)
		c.solidRectProg.SetUniform("size", w, h)
		c.solidRectProg.DrawArrays(gl.TriangleStrip, 0, 4)
		if err := gl.CheckError(); err != nil {
			panic(err)
		}
	})
}

// An Image can be drawn to a canvas.
type Image struct {
	tex           gl.Texture
	Width, Height float32
}

// NewImage returns a new *Image from a normalized RGBA image.
func NewImage(img *image.NRGBA) *Image {
	var t gl.Texture
	thread0.Do(func() { t = gl.MakeImageTexture(img) })
	return &Image{
		tex:    t,
		Width:  float32(img.Bounds().Dx()),
		Height: float32(img.Bounds().Dy()),
	}
}

// LoadPng returns an *Image loaded from a PNG file.
func LoadPng(path string) (*Image, error) {
	r, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer r.Close()

	img, err := png.Decode(r)
	if err != nil {
		return nil, err
	}
	return NewImage(img.(*image.NRGBA)), nil
}

// DrawImage draws an image at the given location on the canvas.
// X and y specify the upper-left corner of the image.
func (c *Canvas) DrawImage(x, y float32, img *Image) {
	thread0.Do(func() {
		img.tex.Bind(0)
		c.imgRectProg.SetUniform("tex", 0)

		c.rectVerts.Bind()
		c.imgRectProg.SetVertexAttributeData("vert", 4, 0, 0)
		c.imgRectProg.SetUniform("loc", x, y)
		c.imgRectProg.SetUniform("size", img.Width, img.Height)
		c.imgRectProg.DrawArrays(gl.TriangleStrip, 0, 4)
		if err := gl.CheckError(); err != nil {
			panic(err)
		}
	})
}

var (
	rectVertShader = `
		#version 120
		attribute vec4 vert;
		uniform vec2 loc;
		uniform vec2 size;
		varying vec2 texCoord;
		void main(){
			vec2 p = vec2(vert.x*size.x, vert.y*size.y);
			texCoord = vert.zw;
			gl_Position = gl_ModelViewProjectionMatrix * vec4(p + loc, 0, 1);
		}`

	solidFragShader = `
		#version 120
		uniform vec4 color;
		varying vec2 texCoord;
		void main()
		{
			gl_FragColor = color;
		}`

	texFragShader = `
		#version 120
		uniform sampler2D tex;
		varying vec2 texCoord;
		void main()
		{
			gl_FragColor = texture2D(tex, texCoord);
		}`
)
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/stianeikeland/go-rpio"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	"github.com/rs/cors"
	"periph.io/x/periph/conn/gpio"
	"periph.io/x/periph/conn/gpio/gpioreg"
)

var (
	mode     = 0 // 0=go-rpio,1=embd
	demoMode = false

	// LEDs
	ledRedPin    = 4
	ledYellowPin = 17
	ledGreenPin  = 27
	ledToColor   = map[int]string{}
	ledMapEmbd   = map[int]embd.DigitalPin{}
	ledMap       = map[int]rpio.Pin{}
	ledMapPeriph = map[int]gpio.PinIO{}

	// Buttons
	buttonPin = 22
	buttonMap = map[int]embd.DigitalPin{}
)

func getLEDString(color string) string {
	return "Toggle " + strings.ToUpper(color)
}

func getToggledValue(pin embd.DigitalPin) int {
	val, _ := pin.Read()
	if val == embd.High {
		return embd.Low
	}
	return embd.High
}

func toggleLEDPeriph(pin gpio.PinIO, color string) {
	fmt.Println(getLEDString(color))
	if !demoMode {
		fmt.Println("Val to write", !pin.Read())
		pin.Out(!pin.Read())
	}
}

func toggleLEDEmbd(pin embd.DigitalPin, color string) {
	fmt.Println(getLEDString(color))
	if !demoMode {
		toggledValue := getToggledValue(pin)
		fmt.Println("Val to write", toggledValue)
		embd.DigitalWrite(pin.N(), toggledValue)
	}
}

func toggleLED(pin rpio.Pin, color string) {
	fmt.Println(getLEDString(color))
	if !demoMode {
		fmt.Println("Current value:", pin.Read())
		pin.Toggle()
	}
}

func initButtons() {
	buttonMap[0], _ = embd.NewDigitalPin(buttonPin)
	buttonMap[0].SetDirection(embd.In)
	buttonMap[0].ActiveLow(false)
	quit := make(chan interface{})
	err := buttonMap[0].Watch(embd.EdgeFalling, func(btn embd.DigitalPin) {
		fmt.Println("Pressed", btn)
		quit <- btn
	})
	if err != nil {
		panic(err)
	}
	fmt.Printf("Button %v was pressed.\n", <-quit)
}

func initLEDs() {
	if mode == 1 {
		ledMapEmbd[0], _ = embd.NewDigitalPin(ledRedPin)
		ledMapEmbd[1], _ = embd.NewDigitalPin(ledYellowPin)
		ledMapEmbd[2], _ = embd.NewDigitalPin(ledGreenPin)
		for i := 0; i < len(ledMapEmbd); i++ {
			ledMapEmbd[i].SetDirection(embd.Out)
			embd.DigitalWrite(ledMapEmbd[i].N(), embd.Low)
		}
	} else if mode == 2 {
		ledMapPeriph[0] = gpioreg.ByNumber(ledRedPin)
		ledMapPeriph[0] = gpioreg.ByNumber(ledYellowPin)
		ledMapPeriph[0] = gpioreg.ByNumber(ledGreenPin)
		for i := 0; i < len(ledMapPeriph); i++ {
			ledMapPeriph[i].Out(gpio.Low)
		}
	} else {
		ledMap[0] = rpio.Pin(ledRedPin)
		ledMap[1] = rpio.Pin(ledYellowPin)
		ledMap[2] = rpio.Pin(ledGreenPin)
		for i := 0; i < len(ledMap); i++ {
			ledMap[i].Output()
			ledMap[i].Low()
		}
	}
	initLEDcolors()
}

func initLEDcolors() {
	ledToColor[0] = "red"
	ledToColor[1] = "yellow"
	ledToColor[2] = "green"
}

func initGPIO() error {
	if mode == 1 {
		return embd.InitGPIO()
	}
	return rpio.Open()
}

func doLedToggling(i int) {
	if !demoMode {
		if mode == 1 {
			toggleLEDEmbd(ledMapEmbd[i%3], ledToColor[i%3])
		} else if mode == 2{
			toggleLEDPeriph(ledMapPeriph[i%3], ledToColor[i%3])
		} else {
			toggleLED(ledMap[i%3], ledToColor[i%3])
		}
	} else {
		fmt.Println(getLEDString(ledToColor[i%3]))
	}
	time.Sleep(time.Second)
}

type Gpio struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Value int    `json:"value"`
}

func gpios(w http.ResponseWriter, r *http.Request) {
	pins := ledMapEmbd
	fmt.Printf("Fetching %d GPIOs\n", len(pins))
	gpios := make([]Gpio, len(pins))
	for i := 0; i < len(pins); i++ {
		fmt.Println("GPIO", i)
		pin := pins[i]
		if demoMode {
			pinValue := i % 2
			gpio := Gpio{i, "GPIO" + strconv.Itoa(i), pinValue}
			gpios[i] = gpio
		} else {
			pinValue, _ := pin.Read()
			gpio := Gpio{i, "GPIO" + strconv.Itoa(pin.N()), pinValue}
			gpios[i] = gpio
		}
	}
	json, err := json.Marshal(gpios)

	if err != nil {
		panic(err)
	}

	w.Write(json)
}

func initWebServer() {
	mux := http.NewServeMux()
	mux.HandleFunc("/v1/gpios", gpios)
	handler := cors.Default().Handler(mux)
	http.ListenAndServe(":8080", handler)
}

func main() {
	fmt.Println("Parsing parameters")
	num := flag.Int("num", 3, "number of blinks")
	modeFromCli := flag.Int("mode", 0, "mode")
	button := flag.Bool("button", false, "button mode")
	api := flag.Bool("api", true, "API enabled")
	demo := flag.Bool("demo", false, "Demo mode enabled")
	flag.Parse()

	mode = *modeFromCli
	demoMode = *demo

	if demoMode {
		fmt.Println("Running in demo mode, no physical hw interaction")
	}
	if mode == 1 {
		fmt.Println("Running using Embd.io")
	} else if mode == 2 {
		fmt.Println("Running using periph")
	} else {
		fmt.Println("Running using go-rpio")
	}
	fmt.Println("Number of blinks:", *num)

	if *api && mode == 1 {
		go initWebServer()
	}

	if !demoMode {
		var err = initGPIO()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		initLEDs()
		if mode == 1 && *button {
			initButtons()
		}

		if mode == 1 {
			defer embd.CloseGPIO()
		} else {
			defer rpio.Close()
		}
	} else {
		initLEDcolors()
	}

	if !*button {
		if *num == 0 {
			var counter = 0
			for {
				doLedToggling(counter)
				counter++
			}
		} else {
			for i := 0; i < *num; i++ {
				doLedToggling(i)
			}
		}
	}
}
<commit_msg>Fixed nil reference in led init<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/stianeikeland/go-rpio"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	"github.com/rs/cors"
	"periph.io/x/periph/conn/gpio"
	"periph.io/x/periph/conn/gpio/gpioreg"
)

var (
	mode     = 0 // 0=go-rpio,1=embd
	demoMode = false

	// LEDs
	ledRedPin    = 4
	ledYellowPin = 17
	ledGreenPin  = 27
	ledToColor   = map[int]string{}
	ledMapEmbd   = map[int]embd.DigitalPin{}
	ledMap       = map[int]rpio.Pin{}
	ledMapPeriph = map[int]gpio.PinIO{}

	// Buttons
	buttonPin = 22
	buttonMap = map[int]embd.DigitalPin{}
)

func getLEDString(color string) string {
	return "Toggle " + strings.ToUpper(color)
}

func getToggledValue(pin embd.DigitalPin) int {
	val, _ := pin.Read()
	if val == embd.High {
		return embd.Low
	}
	return embd.High
}

func toggleLEDPeriph(pin gpio.PinIO, color string) {
	fmt.Println(getLEDString(color))
	if !demoMode {
		fmt.Println("Val to write", !pin.Read())
		pin.Out(!pin.Read())
	}
}

func toggleLEDEmbd(pin embd.DigitalPin, color string) {
	fmt.Println(getLEDString(color))
	if !demoMode {
		toggledValue := getToggledValue(pin)
		fmt.Println("Val to write", toggledValue)
		embd.DigitalWrite(pin.N(), toggledValue)
	}
}

func toggleLED(pin rpio.Pin, color string) {
	fmt.Println(getLEDString(color))
	if !demoMode {
		fmt.Println("Current value:", pin.Read())
		pin.Toggle()
	}
}

func initButtons() {
	buttonMap[0], _ = embd.NewDigitalPin(buttonPin)
	buttonMap[0].SetDirection(embd.In)
	buttonMap[0].ActiveLow(false)
	quit := make(chan interface{})
	err := buttonMap[0].Watch(embd.EdgeFalling, func(btn embd.DigitalPin) {
		fmt.Println("Pressed", btn)
		quit <- btn
	})
	if err != nil {
		panic(err)
	}
	fmt.Printf("Button %v was pressed.\n", <-quit)
}

func initLEDs() {
	if mode == 1 {
		ledMapEmbd[0], _ = embd.NewDigitalPin(ledRedPin)
		ledMapEmbd[1], _ = embd.NewDigitalPin(ledYellowPin)
		ledMapEmbd[2], _ = embd.NewDigitalPin(ledGreenPin)
		for i := 0; i < len(ledMapEmbd); i++ {
			ledMapEmbd[i].SetDirection(embd.Out)
			embd.DigitalWrite(ledMapEmbd[i].N(), embd.Low)
		}
	} else if mode == 2 {
		ledMapPeriph[0] = gpioreg.ByNumber(ledRedPin)
		ledMapPeriph[1] = gpioreg.ByNumber(ledYellowPin)
		ledMapPeriph[2] = gpioreg.ByNumber(ledGreenPin)
		for i := 0; i < len(ledMapPeriph); i++ {
			ledMapPeriph[i].Out(gpio.Low)
		}
	} else {
		ledMap[0] = rpio.Pin(ledRedPin)
		ledMap[1] = rpio.Pin(ledYellowPin)
		ledMap[2] = rpio.Pin(ledGreenPin)
		for i := 0; i < len(ledMap); i++ {
			ledMap[i].Output()
			ledMap[i].Low()
		}
	}
	initLEDcolors()
}

func initLEDcolors() {
	ledToColor[0] = "red"
	ledToColor[1] = "yellow"
	ledToColor[2] = "green"
}

func initGPIO() error {
	if mode == 1 {
		return embd.InitGPIO()
	}
	return rpio.Open()
}

func doLedToggling(i int) {
	if !demoMode {
		if mode == 1 {
			toggleLEDEmbd(ledMapEmbd[i%3], ledToColor[i%3])
		} else if mode == 2{
			toggleLEDPeriph(ledMapPeriph[i%3], ledToColor[i%3])
		} else {
			toggleLED(ledMap[i%3], ledToColor[i%3])
		}
	} else {
		fmt.Println(getLEDString(ledToColor[i%3]))
	}
	time.Sleep(time.Second)
}

type Gpio struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Value int    `json:"value"`
}

func gpios(w http.ResponseWriter, r *http.Request) {
	pins := ledMapEmbd
	fmt.Printf("Fetching %d GPIOs\n", len(pins))
	gpios := make([]Gpio, len(pins))
	for i := 0; i < len(pins); i++ {
		fmt.Println("GPIO", i)
		pin := pins[i]
		if demoMode {
			pinValue := i % 2
			gpio := Gpio{i, "GPIO" + strconv.Itoa(i), pinValue}
			gpios[i] = gpio
		} else {
			pinValue, _ := pin.Read()
			gpio := Gpio{i, "GPIO" + strconv.Itoa(pin.N()), pinValue}
			gpios[i] = gpio
		}
	}
	json, err := json.Marshal(gpios)

	if err != nil {
		panic(err)
	}

	w.Write(json)
}

func initWebServer() {
	mux := http.NewServeMux()
	mux.HandleFunc("/v1/gpios", gpios)
	handler := cors.Default().Handler(mux)
	http.ListenAndServe(":8080", handler)
}

func main() {
	fmt.Println("Parsing parameters")
	num := flag.Int("num", 3, "number of blinks")
	modeFromCli := flag.Int("mode", 0, "mode")
	button := flag.Bool("button", false, "button mode")
	api := flag.Bool("api", true, "API enabled")
	demo := flag.Bool("demo", false, "Demo mode enabled")
	flag.Parse()

	mode = *modeFromCli
	demoMode = *demo

	if demoMode {
		fmt.Println("Running in demo mode, no physical hw interaction")
	}
	if mode == 1 {
		fmt.Println("Running using Embd.io")
	} else if mode == 2 {
		fmt.Println("Running using periph")
	} else {
		fmt.Println("Running using go-rpio")
	}
	fmt.Println("Number of blinks:", *num)

	if *api && mode == 1 {
		go initWebServer()
	}

	if !demoMode {
		var err = initGPIO()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		initLEDs()
		if mode == 1 && *button {
			initButtons()
		}

		if mode == 1 {
			defer embd.CloseGPIO()
		} else {
			defer rpio.Close()
		}
	} else {
		initLEDcolors()
	}

	if !*button {
		if *num == 0 {
			var counter = 0
			for {
				doLedToggling(counter)
				counter++
			}
		} else {
			for i := 0; i < *num; i++ {
				doLedToggling(i)
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
SPDX-License-Identifier: MIT

MIT License

Copyright (c) 2017 Thanh Ha

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// lhc is a checker to find code files missing license headers.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strings"
	"unicode"
)

var VERSION = "0.1.0"

// check and exit if error.
func check(e error) {
	if e != nil {
		fmt.Println(e)
		os.Exit(1)
	}
}

// fetchLicense from file and return license text.
func fetchLicense(filename string) string {
	file, err := os.Open(filename)
	check(err)
	defer file.Close()

	comment, multilineComment := false, false
	licenseText := ""
	scanner := bufio.NewScanner(file)

	// Read the first 2 bytes to decide if it is a comment string
	b := make([]byte, 2)
	_, err = file.Read(b)
	check(err)
	if isComment(string(b)) {
		comment = true
	}
	file.Seek(0, 0) // Reset so we can read the full file next

	i := 0
	for scanner.Scan() {
		// Read only the first few lines to not read entire code file
		i++
		if i > 50 {
			break
		}

		s := scanner.Text()

		if ignoreComment(s) {
			continue
		}

		if comment == true {
			if strings.HasPrefix(s, "/*") {
				multilineComment = true
			} else if strings.Contains(s, "*/") {
				multilineComment = false
			}

			if !multilineComment && !isComment(s) {
				break
			}

			s = trimComment(s)
		}

		licenseText += s
	}

	return stripSpaces(licenseText)
}

// Check if a string is a comment line.
func isComment(str string) bool {
	if !strings.HasPrefix(str, "#") &&
		!strings.HasPrefix(str, "//") &&
		!strings.HasPrefix(str, "/*") {
		return false
	}

	return true
}

func ignoreComment(str string) bool {
	if strings.Contains(str, "Copyright") ||
		strings.Contains(str, "SPDX-License-Identifier") {
		return true
	}

	return false
}

// Strip whitespace from string.
func stripSpaces(str string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, str)
}

// Trim the comment prefix from string.
func trimComment(str string) string {
	str = strings.TrimPrefix(str, "#")
	str = strings.TrimPrefix(str, "//")
	str = strings.TrimPrefix(str, "/*")
	str = strings.Split(str, "*/")[0]
	return str
}

// Usage prints a statement to explain how to use this command.
func usage() {
	fmt.Printf("Usage: %s [OPTIONS] [PATTERN]...\n", os.Args[0])
	fmt.Printf("Scans a directory for files matching PATTERN and compares them with an expected license header.\n")
	fmt.Printf("\nPATTERN is a space separated list of regex patterns to search for files.\n")
	fmt.Printf("\nOptions:\n")
	flag.PrintDefaults()
}

func main() {
	licensePtr := flag.String("license", "license.txt", "Comma-separated list of license files to compare against.")
	versionPtr := flag.Bool("version", false, "Print version")
	// directoryPtr := flag.String("directory", ".", "Directory to search for files.")

	flag.Usage = usage
	flag.Parse()

	if *versionPtr {
		fmt.Println("License Checker version", VERSION)
		os.Exit(0)
	}

	fmt.Println("Search Patterns:", flag.Args())

	licenseText := fetchLicense(*licensePtr)
	headerText := fetchLicense("lhc.go")

	if licenseText != headerText {
		fmt.Println("✘", "lhc.go")
	} else {
		fmt.Println("✔", "lhc.go")
	}
}
<commit_msg>Compare license headers for a given list of files<commit_after>/*
SPDX-License-Identifier: MIT

MIT License

Copyright (c) 2017 Thanh Ha

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// lhc is a checker to find code files missing license headers.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"unicode"
)

var VERSION = "0.1.0"

// check and exit if error.
func check(e error) {
	if e != nil {
		fmt.Println(e)
		os.Exit(1)
	}
}

// fetchLicense from file and return license text.
func fetchLicense(filename string) string {
	file, err := os.Open(filename)
	check(err)
	defer file.Close()

	comment, multilineComment := false, false
	licenseText := ""
	scanner := bufio.NewScanner(file)

	// Read the first 2 bytes to decide if it is a comment string
	b := make([]byte, 2)
	_, err = file.Read(b)
	check(err)
	if isComment(string(b)) {
		comment = true
	}
	file.Seek(0, 0) // Reset so we can read the full file next

	i := 0
	for scanner.Scan() {
		// Read only the first few lines to not read entire code file
		i++
		if i > 50 {
			break
		}

		s := scanner.Text()

		if ignoreComment(s) {
			continue
		}

		if comment == true {
			if strings.HasPrefix(s, "/*") {
				multilineComment = true
			} else if strings.Contains(s, "*/") {
				multilineComment = false
			}

			if !multilineComment && !isComment(s) {
				break
			}

			s = trimComment(s)
		}

		licenseText += s
	}

	return stripSpaces(licenseText)
}

// Check if a string is a comment line.
func isComment(str string) bool {
	if !strings.HasPrefix(str, "#") &&
		!strings.HasPrefix(str, "//") &&
		!strings.HasPrefix(str, "/*") {
		return false
	}

	return true
}

func ignoreComment(str string) bool {
	s := strings.ToUpper(str)
	if strings.HasPrefix(s, "#!") ||
		strings.Contains(s, "COPYRIGHT") ||
		strings.Contains(s, "SPDX-LICENSE-IDENTIFIER") {
		return true
	}

	return false
}

// Strip whitespace from string.
func stripSpaces(str string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, str)
}

// Trim the comment prefix from string.
func trimComment(str string) string {
	str = strings.TrimPrefix(str, "#")
	str = strings.TrimPrefix(str, "//")
	str = strings.TrimPrefix(str, "/*")
	str = strings.Split(str, "*/")[0]
	return str
}

// Usage prints a statement to explain how to use this command.
func usage() {
	fmt.Printf("Usage: %s [OPTIONS] [FILE]...\n", os.Args[0])
	fmt.Printf("Compare FILE with an expected license header.\n")
	fmt.Printf("\nOptions:\n")
	flag.PrintDefaults()
}

func visit(pattern []string) filepath.WalkFunc {
	return func(path string, info os.FileInfo, err error) error {
		fmt.Printf("Visited: %s\n", path)
		return nil
	}
}

func main() {
	licensePtr := flag.String("license", "license.txt", "Comma-separated list of license files to compare against.")
	versionPtr := flag.Bool("version", false, "Print version")
	// extensions := flag.String("extensions", false, "Instead of a list of files list of extensions to search")
	// directoryPtr := flag.String("directory", ".", "Directory to search for files.")

	flag.Usage = usage
	flag.Parse()

	if *versionPtr {
		fmt.Println("License Checker version", VERSION)
		os.Exit(0)
	}

	fmt.Println("Search Patterns:", flag.Args())

	licenseText := fetchLicense(*licensePtr)
	fmt.Println(licenseText)

	for _, f := range flag.Args() {
		headerText := fetchLicense(f)
		fmt.Println(headerText)
		if licenseText != headerText {
			fmt.Println("✘", f)
		} else {
			fmt.Println("✔", f)
		}
	}

	// filepath.Walk(".", visit(flag.Args()))
}
<|endoftext|>


<commit_before>/*
SPDX-License-Identifier: MIT

MIT License

Copyright (c) 2017 Thanh Ha

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strings"
	"unicode"
)

var VERSION = "0.1.0"

func check(e error) {
	if e != nil {
		fmt.Println(e)
		os.Exit(1)
	}
}

func fetchLicense(filename string) string {
    file, err := os.Open(filename)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer file.Close()

    comment, commentSection := false, false
	licenseText := ""
	scanner := bufio.NewScanner(file)

    b1 := make([]byte, 2)
    n1, err := file.Read(b1)
    check(err)
    if isComment(string(b1)) {
        fmt.Printf("Comment string detected. %d bytes: %s\n", n1, string(b1))
        comment = true
    }
    file.Seek(0, 0)  // Reset so we can read the full file

    i := 0
	for scanner.Scan() {
		s := scanner.Text()

        if strings.Contains(s, "Copyright") {
            continue
        } else if strings.Contains(s, "SPDX-License-Identifier") {
            continue
        }

        if comment == true {
    		if strings.HasPrefix(s, "/*") {
    			commentSection = true
    		} else if commentSection && strings.Contains(s, "*/") {
    			commentSection = false
    		}

    		if !commentSection &&
    			!isComment(s) {
    			break
    		}

    		s = strings.TrimPrefix(s, "#")
    		s = strings.TrimPrefix(s, "//")
    		s = strings.TrimPrefix(s, "/*")
    		s = strings.Split(s, "*/")[0]
        }

		licenseText += s

        // Limit to reading only the first few lines to not read entire code file
        i++
        if i > 100 {
            break
        }
	}

    return stripSpaces(licenseText)
}

func isComment(str string) bool {
    if !strings.HasPrefix(str, "#") &&
        !strings.HasPrefix(str, "//") &&
        !strings.HasPrefix(str, "/*") {
        return false
    }

    return true
}

func stripSpaces(str string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, str)
}

func usage() {
	fmt.Printf("Usage: %s [OPTIONS] [PATTERN]...\n", os.Args[0])
	fmt.Printf("Scans a directory for files matching PATTERN and compares them with an expected license header.\n")
	fmt.Printf("\nPATTERN is a space separated list of regex patterns to search for files.\n")
	fmt.Printf("\nOptions:\n")
	flag.PrintDefaults()
}

func main() {
	licensePtr := flag.String("license", "license.txt", "Comma-separated list of license files to compare against.")
	versionPtr := flag.Bool("version", false, "Print version")
	// directoryPtr := flag.String("directory", ".", "Directory to search for files.")

	flag.Usage = usage
	flag.Parse()

	if *versionPtr {
		fmt.Println("License Checker version", VERSION)
		os.Exit(0)
	}

	fmt.Println("Search Patterns:", flag.Args())

	licenseText := fetchLicense(*licensePtr)
	fmt.Println("License Text")
	fmt.Println(licenseText)

	headerText := fetchLicense("lhc.go")
	fmt.Println("Header Text")
	fmt.Println(headerText)

	if licenseText != headerText {
		fmt.Println("WARNING: License header does not match.", "lhc.go")
	}
}
<commit_msg>Optimize code<commit_after>/*
SPDX-License-Identifier: MIT

MIT License

Copyright (c) 2017 Thanh Ha

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strings"
	"unicode"
)

var VERSION = "0.1.0"

func check(e error) {
	if e != nil {
		fmt.Println(e)
		os.Exit(1)
	}
}

func fetchLicense(filename string) string {
	file, err := os.Open(filename)
	check(err)
	defer file.Close()

	comment, multilineComment := false, false
	licenseText := ""
	scanner := bufio.NewScanner(file)

	b := make([]byte, 2)
	_, err = file.Read(b)
	check(err)
	if isComment(string(b)) {
		comment = true
	}
	file.Seek(0, 0) // Reset so we can read the full file next

	i := 0
	for scanner.Scan() {
		// Read only the first few lines to not read entire code file
		i++
		if i > 50 {
			break
		}

		s := scanner.Text()

		if strings.Contains(s, "Copyright") {
			continue
		} else if strings.Contains(s, "SPDX-License-Identifier") {
			continue
		}

		if comment == true {
			if strings.HasPrefix(s, "/*") {
				multilineComment = true
			} else if strings.Contains(s, "*/") {
				multilineComment = false
			}

			if !multilineComment && !isComment(s) {
				break
			}

			s = trimComment(s)
		}

		licenseText += s
	}

	return stripSpaces(licenseText)
}

func isComment(str string) bool {
	if !strings.HasPrefix(str, "#") &&
		!strings.HasPrefix(str, "//") &&
		!strings.HasPrefix(str, "/*") {
		return false
	}

	return true
}

func stripSpaces(str string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, str)
}

func trimComment(str string) string {
	str = strings.TrimPrefix(str, "#")
	str = strings.TrimPrefix(str, "//")
	str = strings.TrimPrefix(str, "/*")
	str = strings.Split(str, "*/")[0]
	return str
}

func usage() {
	fmt.Printf("Usage: %s [OPTIONS] [PATTERN]...\n", os.Args[0])
	fmt.Printf("Scans a directory for files matching PATTERN and compares them with an expected license header.\n")
	fmt.Printf("\nPATTERN is a space separated list of regex patterns to search for files.\n")
	fmt.Printf("\nOptions:\n")
	flag.PrintDefaults()
}

func main() {
	licensePtr := flag.String("license", "license.txt", "Comma-separated list of license files to compare against.")
	versionPtr := flag.Bool("version", false, "Print version")
	// directoryPtr := flag.String("directory", ".", "Directory to search for files.")

	flag.Usage = usage
	flag.Parse()

	if *versionPtr {
		fmt.Println("License Checker version", VERSION)
		os.Exit(0)
	}

	fmt.Println("Search Patterns:", flag.Args())

	licenseText := fetchLicense(*licensePtr)
	headerText := fetchLicense("lhc.go")

	if licenseText != headerText {
		fmt.Println("✘", "lhc.go")
	} else {
		fmt.Println("✔", "lhc.go")
	}
}
<|endoftext|>


<commit_before>// a proxy package is a proxy driver for dabase/sql.

package proxy

import "database/sql/driver"

type Proxy struct {
	Driver driver.Driver
	Hooks  *Hooks
}

type Hooks struct {
	// PreOpen is a callback that gets called before any
	// attempt to open the sql connection is made, and is ALWAYS
	// called.
	//
	// The first return value is passed to both `Hooks.Open` and
	// `Hooks.PostOpen` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Driver.Open` method and `Hooks.Open`
	// methods are not called.
	PreOpen func(name string) (interface{}, error)

	// Open is called after the underlying driver's `Driver.Open` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreOpen` method, and may be nil.
	//
	// If this callback returns an error, then the `conn` object is
	// closed by calling the `Close` method, and the error from this
	// callback is returned by the `db.Open` method.
	Open func(ctx interface{}, conn driver.Conn) error

	// PostOpen is a callback that gets called at the end of
	// the call to `db.Open(). It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreOpen` method, and may be nil.
	PostOpen func(ctx interface{}, conn driver.Conn) error

	// PreExec is a callback that gets called prior to calling
	// `Stmt.Exec`, and is ALWAYS called. If this callback returns an
	// error, the underlying driver's `Stmt.Exec` and `Hooks.Exec` methods
	// are not called.
	//
	// The first return value is passed to both `Hooks.Exec` and
	// `Hooks.PostExec` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Driver.Exec` method and `Hooks.Exec`
	// methods are not called.
	PreExec func(stmt *Stmt, args []driver.Value) (interface{}, error)

	// Exec is called after the underlying driver's `Driver.Exec` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreExec` method, and may be nil.
	//
	// If this callback returns an error, then the error from this
	// callback is returned by the `Stmt.Exec` method.
	Exec func(ctx interface{}, stmt *Stmt, args []driver.Value, result driver.Result) error

	// PostExec is a callback that gets called at the end of
	// the call to `Stmt.Exec`. It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreExec` method, and may be nil.
	PostExec func(ctx interface{}, stmt *Stmt, args []driver.Value, result driver.Result) error

	// PreQuery is a callback that gets called prior to calling
	// `Stmt.Query`, and is ALWAYS called. If this callback returns an
	// error, the underlying driver's `Stmt.Query` and `Hooks.Query` methods
	// are not called.
	//
	// The first return value is passed to both `Hooks.Query` and
	// `Hooks.PostQuery` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Stmt.Query` method and `Hooks.Query`
	// methods are not called.
	PreQuery func(stmt *Stmt, args []driver.Value) (interface{}, error)

	// Query is called after the underlying driver's `Stmt.Query` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreQuery` method, and may be nil.
	//
	// If this callback returns an error, then the error from this
	// callback is returned by the `Stmt.Query` method.
	Query func(ctx interface{}, stmt *Stmt, args []driver.Value, rows driver.Rows) error

	// PostQuery is a callback that gets called at the end of
	// the call to `Stmt.Query`. It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreQuery` method, and may be nil.
	PostQuery func(ctx interface{}, stmt *Stmt, args []driver.Value, rows driver.Rows) error

	// PreBegin is a callback that gets called prior to calling
	// `Stmt.Begin`, and is ALWAYS called. If this callback returns an
	// error, the underlying driver's `Conn.Begin` and `Hooks.Begin` methods
	// are not called.
	//
	// The first return value is passed to both `Hooks.Begin` and
	// `Hooks.PostBegin` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Conn.Begin` method and `Hooks.Begin`
	// methods are not called.
	PreBegin  func(conn *Conn) (interface{}, error)

	// Query is called after the underlying driver's `Conn.Begin` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreBegin` method, and may be nil.
	//
	// If this callback returns an error, then the error from this
	// callback is returned by the `Stmt.Begin` method.
	Begin     func(ctx interface{}, conn *Conn) error

	// PostBegin is a callback that gets called at the end of
	// the call to `Stmt.Begin`. It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreBegin` method, and may be nil.
	PostBegin func(ctx interface{}, conn *Conn) error

	PreCommit  func(tx *Tx) (interface{}, error)
	Commit     func(ctx interface{}, tx *Tx) error
	PostCommit func(ctx interface{}, tx *Tx) error

	PreRollback  func(tx *Tx) (interface{}, error)
	Rollback     func(ctx interface{}, tx *Tx) error
	PostRollback func(ctx interface{}, tx *Tx) error
}

func NewProxy(driver driver.Driver, hooks *Hooks) *Proxy {
	if hooks == nil {
		hooks = &Hooks{}
	}
	return &Proxy{
		Driver: driver,
		Hooks:  hooks,
	}
}

func (p *Proxy) Open(name string) (driver.Conn, error) {
	var err error
	var ctx interface{}

	var conn driver.Conn
	if h := p.Hooks.PostOpen; h != nil {
		// Setup PostOpen. This needs to be a closure like this
		// or otherwise changes to the `ctx` and `conn` parameters
		// within this Open() method does not get applied at the
		// time defer is fired
		defer func() { h(ctx, conn) }()
	}

	if h := p.Hooks.PreOpen; h != nil {
		if ctx, err = h(name); err != nil {
			return nil, err
		}
	}
	conn, err = p.Driver.Open(name)
	if err != nil {
		return nil, err
	}

	conn = &Conn{
		Conn:  conn,
		Proxy: p,
	}

	if hook := p.Hooks.Open; hook != nil {
		if err = hook(ctx, conn); err != nil {
			conn.Close()
			return nil, err
		}
	}
	return conn, nil
}
<commit_msg>Add docs for hooks<commit_after>// a proxy package is a proxy driver for dabase/sql.

package proxy

import "database/sql/driver"

type Proxy struct {
	Driver driver.Driver
	Hooks  *Hooks
}

type Hooks struct {
	// PreOpen is a callback that gets called before any
	// attempt to open the sql connection is made, and is ALWAYS
	// called.
	//
	// The first return value is passed to both `Hooks.Open` and
	// `Hooks.PostOpen` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Driver.Open` method and `Hooks.Open`
	// methods are not called.
	PreOpen func(name string) (interface{}, error)

	// Open is called after the underlying driver's `Driver.Open` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreOpen` method, and may be nil.
	//
	// If this callback returns an error, then the `conn` object is
	// closed by calling the `Close` method, and the error from this
	// callback is returned by the `db.Open` method.
	Open func(ctx interface{}, conn driver.Conn) error

	// PostOpen is a callback that gets called at the end of
	// the call to `db.Open(). It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreOpen` method, and may be nil.
	PostOpen func(ctx interface{}, conn driver.Conn) error

	// PreExec is a callback that gets called prior to calling
	// `Stmt.Exec`, and is ALWAYS called. If this callback returns an
	// error, the underlying driver's `Stmt.Exec` and `Hooks.Exec` methods
	// are not called.
	//
	// The first return value is passed to both `Hooks.Exec` and
	// `Hooks.PostExec` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Driver.Exec` method and `Hooks.Exec`
	// methods are not called.
	PreExec func(stmt *Stmt, args []driver.Value) (interface{}, error)

	// Exec is called after the underlying driver's `Driver.Exec` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreExec` method, and may be nil.
	//
	// If this callback returns an error, then the error from this
	// callback is returned by the `Stmt.Exec` method.
	Exec func(ctx interface{}, stmt *Stmt, args []driver.Value, result driver.Result) error

	// PostExec is a callback that gets called at the end of
	// the call to `Stmt.Exec`. It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreExec` method, and may be nil.
	PostExec func(ctx interface{}, stmt *Stmt, args []driver.Value, result driver.Result) error

	// PreQuery is a callback that gets called prior to calling
	// `Stmt.Query`, and is ALWAYS called. If this callback returns an
	// error, the underlying driver's `Stmt.Query` and `Hooks.Query` methods
	// are not called.
	//
	// The first return value is passed to both `Hooks.Query` and
	// `Hooks.PostQuery` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Stmt.Query` method and `Hooks.Query`
	// methods are not called.
	PreQuery func(stmt *Stmt, args []driver.Value) (interface{}, error)

	// Query is called after the underlying driver's `Stmt.Query` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreQuery` method, and may be nil.
	//
	// If this callback returns an error, then the error from this
	// callback is returned by the `Stmt.Query` method.
	Query func(ctx interface{}, stmt *Stmt, args []driver.Value, rows driver.Rows) error

	// PostQuery is a callback that gets called at the end of
	// the call to `Stmt.Query`. It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreQuery` method, and may be nil.
	PostQuery func(ctx interface{}, stmt *Stmt, args []driver.Value, rows driver.Rows) error

	// PreBegin is a callback that gets called prior to calling
	// `Stmt.Begin`, and is ALWAYS called. If this callback returns an
	// error, the underlying driver's `Conn.Begin` and `Hooks.Begin` methods
	// are not called.
	//
	// The first return value is passed to both `Hooks.Begin` and
	// `Hooks.PostBegin` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Conn.Begin` method and `Hooks.Begin`
	// methods are not called.
	PreBegin  func(conn *Conn) (interface{}, error)

	// Begin is called after the underlying driver's `Conn.Begin` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreBegin` method, and may be nil.
	//
	// If this callback returns an error, then the error from this
	// callback is returned by the `Conn.Begin` method.
	Begin     func(ctx interface{}, conn *Conn) error

	// PostBegin is a callback that gets called at the end of
	// the call to `Conn.Begin`. It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreBegin` method, and may be nil.
	PostBegin func(ctx interface{}, conn *Conn) error

	// PreCommit is a callback that gets called prior to calling
	// `Tx.Commit`, and is ALWAYS called. If this callback returns an
	// error, the underlying driver's `Tx.Commit` and `Hooks.Commit` methods
	// are not called.
	//
	// The first return value is passed to both `Hooks.Commit` and
	// `Hooks.PostCommit` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Tx.Commit` method and `Hooks.Commit`
	// methods are not called.
	PreCommit  func(tx *Tx) (interface{}, error)

	// Commit is called after the underlying driver's `Tx.Commit` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreCommit` method, and may be nil.
	//
	// If this callback returns an error, then the error from this
	// callback is returned by the `Tx.Commit` method.
	Commit     func(ctx interface{}, tx *Tx) error

	// PostCommit is a callback that gets called at the end of
	// the call to `Tx.Commit`. It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreCommit` method, and may be nil.
	PostCommit func(ctx interface{}, tx *Tx) error

	// PreRollback is a callback that gets called prior to calling
	// `Tx.Rollback`, and is ALWAYS called. If this callback returns an
	// error, the underlying driver's `Tx.Rollback` and `Hooks.Rollback` methods
	// are not called.
	//
	// The first return value is passed to both `Hooks.Rollback` and
	// `Hooks.PostRollback` callbacks. You may specify anything you want.
	// Return nil if you do not need to use it.
	//
	// The second return value is indicates the error found while
	// executing this hook. If this callback returns an error,
	// the underlying driver's `Tx.Rollback` method and `Hooks.Rollback`
	PreRollback  func(tx *Tx) (interface{}, error)

	// Rollback is called after the underlying driver's `Tx.Rollback` method
	// returns without any errors.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreRollback` method, and may be nil.
	//
	// If this callback returns an error, then the error from this
	// callback is returned by the `Tx.Rollback` method.
	Rollback     func(ctx interface{}, tx *Tx) error

	// PostRollback is a callback that gets called at the end of
	// the call to `Tx.Rollback`. It is ALWAYS called.
	//
	// The `ctx` parameter is the return value supplied from the
	// `Hooks.PreRollback` method, and may be nil.
	PostRollback func(ctx interface{}, tx *Tx) error
}

func NewProxy(driver driver.Driver, hooks *Hooks) *Proxy {
	if hooks == nil {
		hooks = &Hooks{}
	}
	return &Proxy{
		Driver: driver,
		Hooks:  hooks,
	}
}

func (p *Proxy) Open(name string) (driver.Conn, error) {
	var err error
	var ctx interface{}

	var conn driver.Conn
	if h := p.Hooks.PostOpen; h != nil {
		// Setup PostOpen. This needs to be a closure like this
		// or otherwise changes to the `ctx` and `conn` parameters
		// within this Open() method does not get applied at the
		// time defer is fired
		defer func() { h(ctx, conn) }()
	}

	if h := p.Hooks.PreOpen; h != nil {
		if ctx, err = h(name); err != nil {
			return nil, err
		}
	}
	conn, err = p.Driver.Open(name)
	if err != nil {
		return nil, err
	}

	conn = &Conn{
		Conn:  conn,
		Proxy: p,
	}

	if hook := p.Hooks.Open; hook != nil {
		if err = hook(ctx, conn); err != nil {
			conn.Close()
			return nil, err
		}
	}
	return conn, nil
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"net/http"
	"net/url"
	"strconv"

	"gopkg.in/logex.v1"
)

func bindHandler(mux *http.ServeMux) {
	mux.HandleFunc("/proxy", proxyHandler)
}

type ProxyConfig struct {
	Url   string
	Start int64
	End   int64
}

func proxyUrl(host, source string, start, end int64) string {
	u := url.Values{
		"url": {source},
	}
	if start >= 0 {
		u.Add("start", strconv.FormatInt(start, 10))
	}
	if end > 0 {
		u.Add("end", strconv.FormatInt(end, 10))
	}
	return "http://" + host + "/proxy?" + u.Encode()
}

func proxyDo(method string, p *ProxyConfig, h http.Header) (io.ReadCloser, int, error) {
	req, err := http.NewRequest(method, p.Url, nil)
	if err != nil {
		return nil, 400, logex.Trace(err)
	}
	if p.Start >= 0 {
		setRange(req.Header, p.Start, p.End)
	}
	resp, err := DefaultClient.Do(req)
	if err != nil {
		return nil, 400, logex.Trace(err)
	}
	for k, v := range resp.Header {
		for _, vv := range v {
			h.Add(k, vv)
		}
	}
	resp.Header.Set(H_SOURCE, resp.Request.URL.String())

	switch resp.StatusCode {
	case 206:
		return resp.Body, resp.StatusCode, nil
	case 200:
		// panic
		return resp.Body, resp.StatusCode, nil
	default:
		return nil, resp.StatusCode, logex.NewError("remote error:", resp.Status)
	}
}

func proxyHandler(w http.ResponseWriter, req *http.Request) {
	cfg := new(ProxyConfig)
	cfg.Url = req.FormValue("url")
	cfg.Start, _ = strconv.ParseInt(req.FormValue("start"), 10, 64)
	cfg.End, _ = strconv.ParseInt(req.FormValue("end"), 10, 64)

	rc, code, err := proxyDo(req.Method, cfg, w.Header())
	if err != nil {
		http.Error(w, err.Error(), code)
		return
	}
	defer rc.Close()
	w.WriteHeader(code)
	_, err = io.Copy(w, rc)
	if err != nil {
		logex.Error(err)
		return
	}
}
<commit_msg>fix source<commit_after>package main

import (
	"io"
	"net/http"
	"net/url"
	"strconv"

	"gopkg.in/logex.v1"
)

func bindHandler(mux *http.ServeMux) {
	mux.HandleFunc("/proxy", proxyHandler)
}

type ProxyConfig struct {
	Url   string
	Start int64
	End   int64
}

func proxyUrl(host, source string, start, end int64) string {
	u := url.Values{
		"url": {source},
	}
	if start >= 0 {
		u.Add("start", strconv.FormatInt(start, 10))
	}
	if end > 0 {
		u.Add("end", strconv.FormatInt(end, 10))
	}
	return "http://" + host + "/proxy?" + u.Encode()
}

func proxyDo(method string, p *ProxyConfig, h http.Header) (io.ReadCloser, int, error) {
	req, err := http.NewRequest(method, p.Url, nil)
	if err != nil {
		return nil, 400, logex.Trace(err)
	}
	if p.Start >= 0 {
		setRange(req.Header, p.Start, p.End)
	}
	resp, err := DefaultClient.Do(req)
	if err != nil {
		return nil, 400, logex.Trace(err)
	}

	resp.Header.Set(H_SOURCE, resp.Request.URL.String())
	for k, v := range resp.Header {
		for _, vv := range v {
			h.Add(k, vv)
		}
	}

	switch resp.StatusCode {
	case 206:
		return resp.Body, resp.StatusCode, nil
	case 200:
		// panic
		return resp.Body, resp.StatusCode, nil
	default:
		return nil, resp.StatusCode, logex.NewError("remote error:", resp.Status)
	}
}

func proxyHandler(w http.ResponseWriter, req *http.Request) {
	cfg := new(ProxyConfig)
	cfg.Url = req.FormValue("url")
	cfg.Start, _ = strconv.ParseInt(req.FormValue("start"), 10, 64)
	cfg.End, _ = strconv.ParseInt(req.FormValue("end"), 10, 64)

	rc, code, err := proxyDo(req.Method, cfg, w.Header())
	if err != nil {
		http.Error(w, err.Error(), code)
		return
	}
	defer rc.Close()
	w.WriteHeader(code)
	_, err = io.Copy(w, rc)
	if err != nil {
		logex.Error(err)
		return
	}
}
<|endoftext|>


<commit_before>package arn

import (
	"errors"
	"fmt"
	"os"
	"path"
	"reflect"
	"strings"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
	"github.com/akyoto/color"
)

// Force interface implementations
var (
	_ fmt.Stringer           = (*Anime)(nil)
	_ Likeable               = (*Anime)(nil)
	_ PostParent             = (*Anime)(nil)
	_ api.Deletable          = (*Anime)(nil)
	_ api.Editable           = (*Anime)(nil)
	_ api.CustomEditable     = (*Anime)(nil)
	_ api.ArrayEventListener = (*Anime)(nil)
)

// Actions
func init() {
	API.RegisterActions("Anime", []*api.Action{
		// Like anime
		LikeAction(),

		// Unlike anime
		UnlikeAction(),
	})
}

// Edit creates an edit log entry.
func (anime *Anime) Edit(ctx aero.Context, key string, value reflect.Value, newValue reflect.Value) (consumed bool, err error) {
	user := GetUserFromContext(ctx)

	if key == "Status" {
		oldStatus := value.String()
		newStatus := newValue.String()

		// Notify people who want to know about finished series
		if oldStatus == "current" && newStatus == "finished" {
			go func() {
				for _, user := range anime.UsersWatchingOrPlanned() {
					if !user.Settings().Notification.AnimeFinished {
						continue
					}

					user.SendNotification(&PushNotification{
						Title:   anime.Title.ByUser(user),
						Message: anime.Title.ByUser(user) + " has finished airing!",
						Icon:    anime.ImageLink("medium"),
						Link:    "https://notify.moe" + anime.Link(),
						Type:    NotificationTypeAnimeFinished,
					})
				}
			}()
		}
	}

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "edit", "Anime", anime.ID, key, fmt.Sprint(value.Interface()), fmt.Sprint(newValue.Interface()))
	logEntry.Save()

	return false, nil
}

// OnAppend saves a log entry.
func (anime *Anime) OnAppend(ctx aero.Context, key string, index int, obj interface{}) {
	onAppend(anime, ctx, key, index, obj)
}

// OnRemove saves a log entry.
func (anime *Anime) OnRemove(ctx aero.Context, key string, index int, obj interface{}) {
	onRemove(anime, ctx, key, index, obj)
}

// Authorize returns an error if the given API POST request is not authorized.
func (anime *Anime) Authorize(ctx aero.Context, action string) error {
	user := GetUserFromContext(ctx)

	if user == nil || (user.Role != "editor" && user.Role != "admin") {
		return errors.New("Not logged in or not authorized to edit this anime")
	}

	return nil
}

// DeleteInContext deletes the anime in the given context.
func (anime *Anime) DeleteInContext(ctx aero.Context) error {
	user := GetUserFromContext(ctx)

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "delete", "Anime", anime.ID, "", fmt.Sprint(anime), "")
	logEntry.Save()

	return anime.Delete()
}

// Delete deletes the anime from the database.
func (anime *Anime) Delete() error {
	// Delete anime characters
	DB.Delete("AnimeCharacters", anime.ID)

	// Delete anime relations
	DB.Delete("AnimeRelations", anime.ID)

	// Delete anime episodes
	DB.Delete("AnimeEpisodes", anime.ID)

	// Delete anime list items
	for animeList := range StreamAnimeLists() {
		removed := animeList.Remove(anime.ID)

		if removed {
			animeList.Save()
		}
	}

	// Delete anime ID from existing relations
	for relations := range StreamAnimeRelations() {
		removed := relations.Remove(anime.ID)

		if removed {
			relations.Save()
		}
	}

	// Delete anime ID from quotes
	for quote := range StreamQuotes() {
		if quote.AnimeID == anime.ID {
			quote.AnimeID = ""
			quote.Save()
		}
	}

	// Remove posts
	for _, post := range anime.Posts() {
		err := post.Delete()

		if err != nil {
			return err
		}
	}

	// Delete soundtrack tags
	for track := range StreamSoundTracks() {
		newTags := []string{}

		for _, tag := range track.Tags {
			if strings.HasPrefix(tag, "anime:") {
				parts := strings.Split(tag, ":")
				id := parts[1]

				if id == anime.ID {
					continue
				}
			}

			newTags = append(newTags, tag)
		}

		if len(track.Tags) != len(newTags) {
			track.Tags = newTags
			track.Save()
		}
	}

	// Delete images on file system
	if anime.HasImage() {
		err := os.Remove(path.Join(Root, "images/anime/original/", anime.ID+anime.Image.Extension))

		if err != nil {
			// Don't return the error.
			// It's too late to stop the process at this point.
			// Instead, log the error.
			color.Red(err.Error())
		}

		os.Remove(path.Join(Root, "images/anime/large/", anime.ID+".jpg"))
		os.Remove(path.Join(Root, "images/anime/large/", anime.ID+"@2.jpg"))
		os.Remove(path.Join(Root, "images/anime/large/", anime.ID+".webp"))
		os.Remove(path.Join(Root, "images/anime/large/", anime.ID+"@2.webp"))

		os.Remove(path.Join(Root, "images/anime/medium/", anime.ID+".jpg"))
		os.Remove(path.Join(Root, "images/anime/medium/", anime.ID+"@2.jpg"))
		os.Remove(path.Join(Root, "images/anime/medium/", anime.ID+".webp"))
		os.Remove(path.Join(Root, "images/anime/medium/", anime.ID+"@2.webp"))

		os.Remove(path.Join(Root, "images/anime/small/", anime.ID+".jpg"))
		os.Remove(path.Join(Root, "images/anime/small/", anime.ID+"@2.jpg"))
		os.Remove(path.Join(Root, "images/anime/small/", anime.ID+".webp"))
		os.Remove(path.Join(Root, "images/anime/small/", anime.ID+"@2.webp"))
	}

	// Delete the actual anime
	DB.Delete("Anime", anime.ID)

	return nil
}

// Save saves the anime in the database.
func (anime *Anime) Save() {
	DB.Set("Anime", anime.ID, anime)
}
<commit_msg>Fixed anime deletion (closes #240)<commit_after>package arn

import (
	"errors"
	"fmt"
	"os"
	"path"
	"reflect"
	"strings"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
	"github.com/akyoto/color"
)

// Force interface implementations
var (
	_ fmt.Stringer           = (*Anime)(nil)
	_ Likeable               = (*Anime)(nil)
	_ PostParent             = (*Anime)(nil)
	_ api.Deletable          = (*Anime)(nil)
	_ api.Editable           = (*Anime)(nil)
	_ api.CustomEditable     = (*Anime)(nil)
	_ api.ArrayEventListener = (*Anime)(nil)
)

// Actions
func init() {
	API.RegisterActions("Anime", []*api.Action{
		// Like anime
		LikeAction(),

		// Unlike anime
		UnlikeAction(),
	})
}

// Edit creates an edit log entry.
func (anime *Anime) Edit(ctx aero.Context, key string, value reflect.Value, newValue reflect.Value) (consumed bool, err error) {
	user := GetUserFromContext(ctx)

	if key == "Status" {
		oldStatus := value.String()
		newStatus := newValue.String()

		// Notify people who want to know about finished series
		if oldStatus == "current" && newStatus == "finished" {
			go func() {
				for _, user := range anime.UsersWatchingOrPlanned() {
					if !user.Settings().Notification.AnimeFinished {
						continue
					}

					user.SendNotification(&PushNotification{
						Title:   anime.Title.ByUser(user),
						Message: anime.Title.ByUser(user) + " has finished airing!",
						Icon:    anime.ImageLink("medium"),
						Link:    "https://notify.moe" + anime.Link(),
						Type:    NotificationTypeAnimeFinished,
					})
				}
			}()
		}
	}

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "edit", "Anime", anime.ID, key, fmt.Sprint(value.Interface()), fmt.Sprint(newValue.Interface()))
	logEntry.Save()

	return false, nil
}

// OnAppend saves a log entry.
func (anime *Anime) OnAppend(ctx aero.Context, key string, index int, obj interface{}) {
	onAppend(anime, ctx, key, index, obj)
}

// OnRemove saves a log entry.
func (anime *Anime) OnRemove(ctx aero.Context, key string, index int, obj interface{}) {
	onRemove(anime, ctx, key, index, obj)
}

// Authorize returns an error if the given API POST request is not authorized.
func (anime *Anime) Authorize(ctx aero.Context, action string) error {
	user := GetUserFromContext(ctx)

	if user == nil || (user.Role != "editor" && user.Role != "admin") {
		return errors.New("Not logged in or not authorized to edit this anime")
	}

	return nil
}

// DeleteInContext deletes the anime in the given context.
func (anime *Anime) DeleteInContext(ctx aero.Context) error {
	user := GetUserFromContext(ctx)

	// Write log entry
	logEntry := NewEditLogEntry(user.ID, "delete", "Anime", anime.ID, "", fmt.Sprint(anime), "")
	logEntry.Save()

	return anime.Delete()
}

// Delete deletes the anime from the database.
func (anime *Anime) Delete() error {
	// Delete anime characters
	DB.Delete("AnimeCharacters", anime.ID)

	// Delete anime relations
	DB.Delete("AnimeRelations", anime.ID)

	// Delete anime episodes
	for _, episode := range anime.Episodes() {
		err := episode.Delete()

		if err != nil {
			return err
		}
	}

	// Delete anime list items
	for animeList := range StreamAnimeLists() {
		removed := animeList.Remove(anime.ID)

		if removed {
			animeList.Save()
		}
	}

	// Delete anime ID from existing relations
	for relations := range StreamAnimeRelations() {
		removed := relations.Remove(anime.ID)

		if removed {
			relations.Save()
		}
	}

	// Delete anime ID from quotes
	for quote := range StreamQuotes() {
		if quote.AnimeID == anime.ID {
			quote.AnimeID = ""
			quote.Save()
		}
	}

	// Remove posts
	for _, post := range anime.Posts() {
		err := post.Delete()

		if err != nil {
			return err
		}
	}

	// Delete soundtrack tags
	for track := range StreamSoundTracks() {
		newTags := []string{}

		for _, tag := range track.Tags {
			if strings.HasPrefix(tag, "anime:") {
				parts := strings.Split(tag, ":")
				id := parts[1]

				if id == anime.ID {
					continue
				}
			}

			newTags = append(newTags, tag)
		}

		if len(track.Tags) != len(newTags) {
			track.Tags = newTags
			track.Save()
		}
	}

	// Delete images on file system
	if anime.HasImage() {
		err := os.Remove(path.Join(Root, "images/anime/original/", anime.ID+anime.Image.Extension))

		if err != nil {
			// Don't return the error.
			// It's too late to stop the process at this point.
			// Instead, log the error.
			color.Red(err.Error())
		}

		os.Remove(path.Join(Root, "images/anime/large/", anime.ID+".jpg"))
		os.Remove(path.Join(Root, "images/anime/large/", anime.ID+"@2.jpg"))
		os.Remove(path.Join(Root, "images/anime/large/", anime.ID+".webp"))
		os.Remove(path.Join(Root, "images/anime/large/", anime.ID+"@2.webp"))

		os.Remove(path.Join(Root, "images/anime/medium/", anime.ID+".jpg"))
		os.Remove(path.Join(Root, "images/anime/medium/", anime.ID+"@2.jpg"))
		os.Remove(path.Join(Root, "images/anime/medium/", anime.ID+".webp"))
		os.Remove(path.Join(Root, "images/anime/medium/", anime.ID+"@2.webp"))

		os.Remove(path.Join(Root, "images/anime/small/", anime.ID+".jpg"))
		os.Remove(path.Join(Root, "images/anime/small/", anime.ID+"@2.jpg"))
		os.Remove(path.Join(Root, "images/anime/small/", anime.ID+".webp"))
		os.Remove(path.Join(Root, "images/anime/small/", anime.ID+"@2.webp"))
	}

	// Delete the actual anime
	DB.Delete("Anime", anime.ID)

	return nil
}

// Save saves the anime in the database.
func (anime *Anime) Save() {
	DB.Set("Anime", anime.ID, anime)
}
<|endoftext|>