<commit_before>package ss

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)

// JSON field prefixes.
const (
	pre2D       = "media_box2d_"
	pre3D       = "media_box3d_"
	preFlyer    = "media_flyer_"
	preDate     = "date_"
	preGenre    = "genres_"
	preSynopsis = "synopsis_"
)

const (
	baseURL      = "http://www.screenscraper.fr/"
	gameInfoPath = "api/jeuInfos.php"
	userInfoPath = "api/ssuserInfos.php"
)

// ErrNotFound is the error returned when a ROM isn't found.
var ErrNotFound = errors.New("not found")

// DevInfo is the information about the developer and used across APIs.
type DevInfo struct {
	ID       string
	Password string
	Name     string
}

// UserInfo is information about the user making the call.
type UserInfo struct {
	ID       string
	Password string
}

type UserInfoResp struct {
	ID              string `xml:"ssuser>id"`
	Level           int    `xml:"ssuser>niveau"`
	Contribution    int    `xml:"ssuser>contribution"`
	UploadedSystems int    `xml:"ssuser>uploadsysteme"`
	UploadedInfo    int    `xml:"ssuser>uploadinfos"`
	ROMsAssociated  int    `xml:"ssuser>romasso"`
	UpdatedMedia    int    `xml:"ssuser>uploadmedia"`
	FavoriteRegion  string `xml:"ssuser>favregion"`
	MaxThreads      int    `xml:"ssuser>maxthreads"`
}

// GameInfoReq is the information we use in the GameInfo command.
type GameInfoReq struct {
	Name    string
	SHA1    string
	RomType string
}

type SafeStringMap struct {
	Map map[string]string
}

func (s *SafeStringMap) UnmarshalJSON(b []byte) error {
	if s.Map == nil {
		s.Map = make(map[string]string)
	}
	x := make(map[string]json.RawMessage)
	if err := json.Unmarshal(b, &x); err != nil {
		log.Print("json: %v", err)
		return nil
	}
	for k, v := range x {
		var y string
		if err := json.Unmarshal(v, &y); err == nil {
			s.Map[k] = y
		}
	}
	return nil
}

type BoxArt struct {
	Box2D SafeStringMap `json:"media_boxs2d"`
	Box3D SafeStringMap `json:"media_boxs3d"`
}

type Media struct {
	Screenshot    string        `json:"media_screenshot"`
	ScreenMarquee string        `json:"media_screenmarquee"`
	Marquee       string        `json:"media_marquee"`
	Video         string        `json:"media_video"`
	Flyers        SafeStringMap `json:"media_flyers"`
	BoxArt        BoxArt        `json:"media_boxs"`
}

func getPrefix(m map[string]string, pre string) (string, bool) {
	for k, v := range m {
		if strings.HasPrefix(k, pre) && !strings.Contains(strings.TrimPrefix(k, pre), "_") {
			return v, true
		}
	}
	return "", false
}

func getSuffix(m map[string]string, pre string, suf []string) (string, bool) {
	if m == nil {
		return "", false
	}
	for _, x := range suf {
		if i, ok := m[pre+x]; ok {
			return i, true
		}
		if x == "xx" {
			if i, ok := getPrefix(m, pre); ok {
				return i, true
			}
		}
	}
	return "", false
}

func (m Media) Box2D(r []string) (string, bool) {
	return getSuffix(m.BoxArt.Box2D.Map, pre2D, r)
}

func (m Media) Box3D(r []string) (string, bool) {
	return getSuffix(m.BoxArt.Box3D.Map, pre3D, r)
}

func (m Media) Flyer(r []string) (string, bool) {
	return getSuffix(m.Flyers.Map, preFlyer, r)
}

type ROM struct {
	FileName   string `json:"romfilename"`
	SHA1       string `json:"romsha1"`
	RegionsRaw string `json:"romregions"`
}

func (r ROM) Regions() []string {
	var x []string
	for _, p := range strings.Split(r.RegionsRaw, ",") {
		p = strings.TrimSpace(p)
		if p == "" {
			continue
		}
		x = append(x, p)
	}
	return x
}

type Game struct {
	Synopsis  SafeStringMap              `json:"synopsis"`
	ID        string                     `json:"id"`
	Name      string                     `json:"nom"`
	Names     SafeStringMap              `json:"noms"`
	Regions   []string                   `json:"regionshortnames"`
	Publisher string                     `json:"editeur"`
	Developer string                     `json:"developpeur"`
	Players   string                     `json:"joueurs"`
	Rating    string                     `json:"note"`
	Dates     SafeStringMap              `json:"dates"`
	Genres    map[string]json.RawMessage `json:"genres:`
	Media     Media                      `json:"medias"`
	ROMs      []ROM                      `json:"roms"`
	genres    map[string]string
}

func (g Game) Date(r []string) (string, bool) {
	return getSuffix(g.Dates.Map, preDate, r)
}

func (g *Game) decodeGenre() {
	g.genres = make(map[string]string)
	for k, v := range g.Genres {
		if strings.HasSuffix(k, "_medias") || strings.HasSuffix(k, "_id") {
			continue
		}
		s := []string{}
		if err := json.Unmarshal(v, &s); err == nil {
			g.genres[k] = strings.Join(s, " / ")
		}
	}
}

func (g Game) Genre(l []string) (string, bool) {
	if g.genres == nil {
		g.decodeGenre()
	}
	return getSuffix(g.genres, preGenre, l)
}

func (g Game) Desc(l []string) (string, bool) {
	return getSuffix(g.Synopsis.Map, preSynopsis, l)
}

func (g Game) ROM(req GameInfoReq) ROM {
	for _, x := range g.ROMs {
		if strings.ToLower(x.SHA1) == strings.ToLower(req.SHA1) {
			return x
		}
	}
	for _, x := range g.ROMs {
		if x.FileName == req.Name {
			return x
		}
	}
	return ROM{}
}

type Response struct {
	Game Game `json:"jeu"`
}

type GameInfoResp struct {
	Response Response `json:"response"`
}

func SanitizeURL(s string) string {
	u, err := url.Parse(s)
	if err != nil {
		return s
	}
	v := u.Query()
	v.Set("devid", "xxx")
	v.Set("devpassword", "yyy")
	v.Set("softname", "zzz")
	v.Del("ssid")
	v.Del("sspassword")
	u.RawQuery = v.Encode()
	return u.String()
}

func User(dev DevInfo, user UserInfo) (*UserInfoResp, error) {
	u, err := url.Parse(baseURL)
	u.Path = userInfoPath
	q := url.Values{}
	q.Set("output", "xml")
	q.Set("devid", dev.ID)
	q.Set("devpassword", dev.Password)
	if dev.Name != "" {
		q.Set("softname", dev.Name)
	}
	q.Set("ssid", user.ID)
	q.Set("sspassword", user.Password)
	u.RawQuery = q.Encode()
	resp, err := http.Get(u.String())
	if err != nil {
		v := u.Query()
		v.Set("devid", "xxx")
		v.Set("devpassword", "yyy")
		v.Set("softname", "zzz")
		u.RawQuery = v.Encode()
		return nil, fmt.Errorf("getting game url:%s, error:%s", u, err)
	}
	defer resp.Body.Close()
	r := &UserInfoResp{}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if err := xml.Unmarshal(b, r); err != nil {
		return nil, fmt.Errorf("ss: cannot parse response: %q", err)
	}
	return r, nil
}

func Threads(dev DevInfo, user UserInfo) int {
	if user.ID == "" || user.Password == "" {
		return 1
	}
	i, err := User(dev, user)
	if err != nil {
		log.Print("error getting allowed threads defaulting to 1")
		return 1
	}
	if i.MaxThreads < 1 {
		return 1
	}
	return i.MaxThreads
}

// GameInfo is the call to get game info.
func GameInfo(dev DevInfo, user UserInfo, req GameInfoReq) (*GameInfoResp, error) {
	u, err := url.Parse(baseURL)
	u.Path = gameInfoPath
	q := url.Values{}
	q.Set("output", "json")
	q.Set("devid", dev.ID)
	q.Set("devpassword", dev.Password)
	if dev.Name != "" {
		q.Set("softname", dev.Name)
	}
	if user.ID != "" {
		q.Set("ssid", user.ID)
	}
	if user.Password != "" {
		q.Set("sspassword", user.Password)
	}
	if req.SHA1 != "" {
		q.Set("sha1", req.SHA1)
	}
	if req.RomType == "" {
		q.Set("romtype", "rom")
	} else {
		q.Set("romtype", req.RomType)
	}
	if req.Name != "" {
		q.Set("romnom", req.Name)
	}
	u.RawQuery = q.Encode()
	resp, err := http.Get(u.String())
	if err != nil {
		if uerr, ok := err.(*url.Error); ok {
			uerr.URL = SanitizeURL(uerr.URL)
			return nil, uerr
		}
		return nil, err
	}
	defer resp.Body.Close()
	r := &GameInfoResp{}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if bytes.HasPrefix(b, []byte("Erreur : Rom/Iso/Dossier non trouv")) {
		return nil, ErrNotFound
	}
	if err := json.Unmarshal(b, r); err != nil {
		if err.Error() == "invalid character 'm' looking for beginning of value" {
			return nil, fmt.Errorf("ss: cannot parse response(%s): %q", string(b), err)
		}
		if err.Error() == "invalid character 'A' looking for beginning of value" {
			return nil, fmt.Errorf("ss: cannot parse response(%s): %q", string(b), err)
		}
		return nil, fmt.Errorf("ss: cannot parse response: %q", err)
	}
	return r, nil
}
<commit_msg>Handle Game not found error<commit_after>package ss

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)

// JSON field prefixes.
const (
	pre2D       = "media_box2d_"
	pre3D       = "media_box3d_"
	preFlyer    = "media_flyer_"
	preDate     = "date_"
	preGenre    = "genres_"
	preSynopsis = "synopsis_"
)

const (
	baseURL      = "http://www.screenscraper.fr/"
	gameInfoPath = "api/jeuInfos.php"
	userInfoPath = "api/ssuserInfos.php"
)

// ErrNotFound is the error returned when a ROM isn't found.
var ErrNotFound = errors.New("not found")

// DevInfo is the information about the developer and used across APIs.
type DevInfo struct {
	ID       string
	Password string
	Name     string
}

// UserInfo is information about the user making the call.
type UserInfo struct {
	ID       string
	Password string
}

type UserInfoResp struct {
	ID              string `xml:"ssuser>id"`
	Level           int    `xml:"ssuser>niveau"`
	Contribution    int    `xml:"ssuser>contribution"`
	UploadedSystems int    `xml:"ssuser>uploadsysteme"`
	UploadedInfo    int    `xml:"ssuser>uploadinfos"`
	ROMsAssociated  int    `xml:"ssuser>romasso"`
	UpdatedMedia    int    `xml:"ssuser>uploadmedia"`
	FavoriteRegion  string `xml:"ssuser>favregion"`
	MaxThreads      int    `xml:"ssuser>maxthreads"`
}

// GameInfoReq is the information we use in the GameInfo command.
type GameInfoReq struct {
	Name    string
	SHA1    string
	RomType string
}

type SafeStringMap struct {
	Map map[string]string
}

func (s *SafeStringMap) UnmarshalJSON(b []byte) error {
	if s.Map == nil {
		s.Map = make(map[string]string)
	}
	x := make(map[string]json.RawMessage)
	if err := json.Unmarshal(b, &x); err != nil {
		log.Print("json: %v", err)
		return nil
	}
	for k, v := range x {
		var y string
		if err := json.Unmarshal(v, &y); err == nil {
			s.Map[k] = y
		}
	}
	return nil
}

type BoxArt struct {
	Box2D SafeStringMap `json:"media_boxs2d"`
	Box3D SafeStringMap `json:"media_boxs3d"`
}

type Media struct {
	Screenshot    string        `json:"media_screenshot"`
	ScreenMarquee string        `json:"media_screenmarquee"`
	Marquee       string        `json:"media_marquee"`
	Video         string        `json:"media_video"`
	Flyers        SafeStringMap `json:"media_flyers"`
	BoxArt        BoxArt        `json:"media_boxs"`
}

func getPrefix(m map[string]string, pre string) (string, bool) {
	for k, v := range m {
		if strings.HasPrefix(k, pre) && !strings.Contains(strings.TrimPrefix(k, pre), "_") {
			return v, true
		}
	}
	return "", false
}

func getSuffix(m map[string]string, pre string, suf []string) (string, bool) {
	if m == nil {
		return "", false
	}
	for _, x := range suf {
		if i, ok := m[pre+x]; ok {
			return i, true
		}
		if x == "xx" {
			if i, ok := getPrefix(m, pre); ok {
				return i, true
			}
		}
	}
	return "", false
}

func (m Media) Box2D(r []string) (string, bool) {
	return getSuffix(m.BoxArt.Box2D.Map, pre2D, r)
}

func (m Media) Box3D(r []string) (string, bool) {
	return getSuffix(m.BoxArt.Box3D.Map, pre3D, r)
}

func (m Media) Flyer(r []string) (string, bool) {
	return getSuffix(m.Flyers.Map, preFlyer, r)
}

type ROM struct {
	FileName   string `json:"romfilename"`
	SHA1       string `json:"romsha1"`
	RegionsRaw string `json:"romregions"`
}

func (r ROM) Regions() []string {
	var x []string
	for _, p := range strings.Split(r.RegionsRaw, ",") {
		p = strings.TrimSpace(p)
		if p == "" {
			continue
		}
		x = append(x, p)
	}
	return x
}

type Game struct {
	Synopsis  SafeStringMap              `json:"synopsis"`
	ID        string                     `json:"id"`
	Name      string                     `json:"nom"`
	Names     SafeStringMap              `json:"noms"`
	Regions   []string                   `json:"regionshortnames"`
	Publisher string                     `json:"editeur"`
	Developer string                     `json:"developpeur"`
	Players   string                     `json:"joueurs"`
	Rating    string                     `json:"note"`
	Dates     SafeStringMap              `json:"dates"`
	Genres    map[string]json.RawMessage `json:"genres:`
	Media     Media                      `json:"medias"`
	ROMs      []ROM                      `json:"roms"`
	genres    map[string]string
}

func (g Game) Date(r []string) (string, bool) {
	return getSuffix(g.Dates.Map, preDate, r)
}

func (g *Game) decodeGenre() {
	g.genres = make(map[string]string)
	for k, v := range g.Genres {
		if strings.HasSuffix(k, "_medias") || strings.HasSuffix(k, "_id") {
			continue
		}
		s := []string{}
		if err := json.Unmarshal(v, &s); err == nil {
			g.genres[k] = strings.Join(s, " / ")
		}
	}
}

func (g Game) Genre(l []string) (string, bool) {
	if g.genres == nil {
		g.decodeGenre()
	}
	return getSuffix(g.genres, preGenre, l)
}

func (g Game) Desc(l []string) (string, bool) {
	return getSuffix(g.Synopsis.Map, preSynopsis, l)
}

func (g Game) ROM(req GameInfoReq) ROM {
	for _, x := range g.ROMs {
		if strings.ToLower(x.SHA1) == strings.ToLower(req.SHA1) {
			return x
		}
	}
	for _, x := range g.ROMs {
		if x.FileName == req.Name {
			return x
		}
	}
	return ROM{}
}

type Response struct {
	Game Game `json:"jeu"`
}

type GameInfoResp struct {
	Response Response `json:"response"`
}

func SanitizeURL(s string) string {
	u, err := url.Parse(s)
	if err != nil {
		return s
	}
	v := u.Query()
	v.Set("devid", "xxx")
	v.Set("devpassword", "yyy")
	v.Set("softname", "zzz")
	v.Del("ssid")
	v.Del("sspassword")
	u.RawQuery = v.Encode()
	return u.String()
}

func User(dev DevInfo, user UserInfo) (*UserInfoResp, error) {
	u, err := url.Parse(baseURL)
	u.Path = userInfoPath
	q := url.Values{}
	q.Set("output", "xml")
	q.Set("devid", dev.ID)
	q.Set("devpassword", dev.Password)
	if dev.Name != "" {
		q.Set("softname", dev.Name)
	}
	q.Set("ssid", user.ID)
	q.Set("sspassword", user.Password)
	u.RawQuery = q.Encode()
	resp, err := http.Get(u.String())
	if err != nil {
		v := u.Query()
		v.Set("devid", "xxx")
		v.Set("devpassword", "yyy")
		v.Set("softname", "zzz")
		u.RawQuery = v.Encode()
		return nil, fmt.Errorf("getting game url:%s, error:%s", u, err)
	}
	defer resp.Body.Close()
	r := &UserInfoResp{}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if err := xml.Unmarshal(b, r); err != nil {
		return nil, fmt.Errorf("ss: cannot parse response: %q", err)
	}
	return r, nil
}

func Threads(dev DevInfo, user UserInfo) int {
	if user.ID == "" || user.Password == "" {
		return 1
	}
	i, err := User(dev, user)
	if err != nil {
		log.Print("error getting allowed threads defaulting to 1")
		return 1
	}
	if i.MaxThreads < 1 {
		return 1
	}
	return i.MaxThreads
}

// GameInfo is the call to get game info.
func GameInfo(dev DevInfo, user UserInfo, req GameInfoReq) (*GameInfoResp, error) {
	u, err := url.Parse(baseURL)
	u.Path = gameInfoPath
	q := url.Values{}
	q.Set("output", "json")
	q.Set("devid", dev.ID)
	q.Set("devpassword", dev.Password)
	if dev.Name != "" {
		q.Set("softname", dev.Name)
	}
	if user.ID != "" {
		q.Set("ssid", user.ID)
	}
	if user.Password != "" {
		q.Set("sspassword", user.Password)
	}
	if req.SHA1 != "" {
		q.Set("sha1", req.SHA1)
	}
	if req.RomType == "" {
		q.Set("romtype", "rom")
	} else {
		q.Set("romtype", req.RomType)
	}
	if req.Name != "" {
		q.Set("romnom", req.Name)
	}
	u.RawQuery = q.Encode()
	resp, err := http.Get(u.String())
	if err != nil {
		if uerr, ok := err.(*url.Error); ok {
			uerr.URL = SanitizeURL(uerr.URL)
			return nil, uerr
		}
		return nil, err
	}
	defer resp.Body.Close()
	r := &GameInfoResp{}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if bytes.HasPrefix(b, []byte("Erreur : Rom/Iso/Dossier non trouv")) {
		return nil, ErrNotFound
	}
	if bytes.HasPrefix(b, []byte("Erreur : Jeu non trouv!")) {
		return nil, ErrNotFound
	}
	if err := json.Unmarshal(b, r); err != nil {
		if err.Error() == "invalid character 'm' looking for beginning of value" {
			return nil, fmt.Errorf("ss: cannot parse response(%s): %q", string(b), err)
		}
		if err.Error() == "invalid character 'A' looking for beginning of value" {
			return nil, fmt.Errorf("ss: cannot parse response(%s): %q", string(b), err)
		}
		return nil, fmt.Errorf("ss: cannot parse response: %q", err)
	}
	return r, nil
}
<|endoftext|>


<commit_before>package stein

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"time"

	"gopkg.in/v1/yaml"
)

var (
	docSep      = regexp.MustCompile(`(?m)^---$|^\.\.\.$`)
	tapTestLine = regexp.MustCompile(`(?m)^(ok|not ok)(?: (\d+))?( [^#]+)?(?:\s*#(.*))?$`)
	tapPlan     = regexp.MustCompile(`(?m)^\d..(\d+|N).*$`)
	tapVersion  = regexp.MustCompile(`(?m)^TAP version \d+$`)
	tapBailOut  = regexp.MustCompile(`(?m)^Bail out!\s*(.*)$`)
)

func Parse(r io.Reader) (*Suite, error) {
	parser := Parser{&DefaultHandler{}}
	return parser.Parse(r)
}

type Parser struct {
	Handler Handler
}

func (p *Parser) Parse(r io.Reader) (*Suite, error) {
	rd := bufio.NewReader(r)
	b, err := rd.Peek(1)
	if err != nil {
		panic(err)
	}

	if p.Handler == nil {
		p.Handler = &DefaultHandler{}
	}

	s := new(Suite)
	switch b[0] {
	case '{':
		return s, s.parse(newDecoder("json", rd), p.Handler)
	case '-':
		return s, s.parse(newDecoder("yaml", rd), p.Handler)
	default:
		return ParseTap(rd, p.Handler)
	}
}

type Time struct {
	time.Time
}

func (t *Time) parse(val string) (err error) {
	var ti time.Time
	if ti, err = time.Parse("2006-01-02 15:04:05", val); err != nil {
		if ti, err = time.Parse(time.RFC3339, val); err != nil {
			return
		}
	}
	(*t).Time = ti
	return
}

func (t *Time) UnmarshalJSON(data []byte) (err error) {
	// parse time, stripping the quotes
	return t.parse(string(data[1 : len(data)-1]))
}
func (t *Time) GetYAML() (tag string, value interface{}) {
	return "", t.String()
}
func (t *Time) SetYAML(tag string, value interface{}) bool {
	if v, ok := value.(string); ok {
		return t.parse(strings.Trim(v, `'"`)) == nil
	}
	return false
}

type Suite struct {
	Type  string
	Start *Time
	Count int
	Seed  int
	Rev   int
	Extra interface{} `yaml:",omitempty" json:",omitempty"`

	Tests []*Test
	Cases []*Case
	Notes []Note
	Final Tally
}

func (s *Suite) parse(dec decoder, handler Handler) (err error) {
	handler.HandleBeforeStream()
	var lastCase *Case
	for dec.Scan() {
		doc := dec.Scanner.Text()
		handler.HandleDoc(doc, dec.format)

		if strings.TrimSpace(doc) == "" {
			continue
		}

		if s.Start == nil {
			if err = dec.Unmarshal([]byte(doc), &s); err != nil {
				return
			}
			handler.HandleSuite(s)
			continue
		}

		var typ string
		if typ, err = dec.Type([]byte(doc)); err != nil {
			return
		}

		switch typ {
		case "case":
			var c Case
			if err = dec.Unmarshal([]byte(doc), &c); err != nil {
				return
			}

			if c.Level == 0 {
				s.Cases = append(s.Cases, &c)
				lastCase = &c
			} else if lastCase == nil {
				// TODO: possibly just add this case to the suite
				return fmt.Errorf("No parent found for test case: %v", c)
			} else {
				// find the parent
				for c.Level <= lastCase.Level {
					lastCase = lastCase.parent
				}
				// TODO: strict check to ensure c.Level == lastCase.Level + 1
				c.parent = lastCase
				lastCase.Subcases = append(lastCase.Subcases, &c)
			}
			handler.HandleCase(&c)
		case "test":
			var t Test
			if err = dec.Unmarshal([]byte(doc), &t); err != nil {
				return
			}
			if lastCase != nil {
				lastCase.Tests = append(lastCase.Tests, &t)
			} else {
				s.Tests = append(s.Tests, &t)
			}
			handler.HandleTest(&t)
		case "note":
			var n Note
			if err = dec.Unmarshal([]byte(doc), &n); err != nil {
				return
			}
			s.Notes = append(s.Notes, n)
			handler.HandleNote(&n)
		case "tally":
			var t Tally
			if err = dec.Unmarshal([]byte(doc), &t); err != nil {
				return
			}
			handler.HandleTally(&t)
		case "final":
			var t Tally
			if err = dec.Unmarshal([]byte(doc), &t); err != nil {
				return
			}
			s.Final = t
			handler.HandleFinal(&t)
		default:
			err = fmt.Errorf("Invalid type: %s", typ)
			return
		}
	}
	handler.HandleAfterStream()
	return
}

func (s Suite) Docs() (ret []interface{}) {
	ret = append(ret, s)
	for _, t := range s.Tests {
		ret = append(ret, t)
	}

	for _, c := range s.Cases {
		ret = append(ret, c.Docs()...)
	}

	for _, n := range s.Notes {
		ret = append(ret, n)
	}

	ret = append(ret, s.Final)
	return
}

func (s Suite) ToTapY() ([]byte, error) {
	var ret []byte
	for _, doc := range s.Docs() {
		if b, err := yaml.Marshal(doc); err != nil {
			return nil, err
		} else {
			ret = append(ret, []byte("---\n")...)
			ret = append(ret, b...)
			ret = append(ret, '\n')
		}
	}
	return append(ret, []byte("...")...), nil
}

type Case struct {
	Type  string
	Label string
	Level int

	Subtype string      `yaml:",omitempty" json:",omitempty"`
	Extra   interface{} `yaml:",omitempty" json:",omitempty"`

	// Not part of any TAP protocol
	Tests []*Test `yaml:",omitempty" json:",omitempty"`
	// Not part of any TAP protocol
	Subcases []*Case `yaml:",omitempty" json:",omitempty"`

	parent *Case
}

func (c Case) Docs() (ret []interface{}) {
	ret = append(ret, c)
	for _, t := range c.Tests {
		ret = append(ret, t)
	}
	for _, c := range c.Subcases {
		ret = append(ret, c.Docs()...)
	}
	return
}

// TODO: allow strings via UnmarshalJSON/YAML
type Snippet []map[string]string

type TestCoverage struct {
	File string `yaml:",omitempty" json:",omitempty"`
	// TODO: limit this to a number
	// according to tap-y/j, can be:
	// - line: 11
	// - range: 11..13
	// - list of lines: [11,12,13]
	Line interface{} `yaml:",omitempty" json:",omitempty"`
	Code string      `yaml:",omitempty" json:",omitempty"`
}

type TestException struct {
	Message   string
	File      string      `yaml:",omitempty" json:",omitempty"`
	Line      int         `yaml:",omitempty" json:",omitempty"`
	Source    string      `yaml:",omitempty" json:",omitempty"`
	Snippet   Snippet     `yaml:",omitempty" json:",omitempty"`
	Backtrace interface{} `yaml:",omitempty" json:",omitempty"`
}

type Test struct {
	Type   string
	Status string
	Label  string
	Time   float64

	Subtype   string         `yaml:",omitempty" json:",omitempty"`
	Setup     string         `yaml:",omitempty" json:",omitempty"`
	Expected  interface{}    `yaml:",omitempty" json:",omitempty"`
	Returned  interface{}    `yaml:",omitempty" json:",omitempty"`
	File      string         `yaml:",omitempty" json:",omitempty"`
	Line      int            `yaml:",omitempty" json:",omitempty"`
	Source    string         `yaml:",omitempty" json:",omitempty"`
	Snippet   Snippet        `yaml:",omitempty" json:",omitempty"`
	Coverage  *TestCoverage  `yaml:",omitempty" json:",omitempty"`
	Exception *TestException `yaml:",omitempty" json:",omitempty"`
	Stdout    string         `yaml:",omitempty" json:",omitempty"`
	Stderr    string         `yaml:",omitempty" json:",omitempty"`
	Extra     interface{}    `yaml:",omitempty" json:",omitempty"`
}

type Note struct {
	Type  string
	Text  string
	Extra interface{} `yaml:",omitempty" json:",omitempty"`
}

type Tally struct {
	Type   string
	Time   float64 `yaml:",omitempty" json:",omitempty"`
	Counts struct {
		Total int
		Pass  int
		Fail  int
		Error int
		Omit  int
		Todo  int
	}
	Extra interface{} `yaml:",omitempty" json:",omitempty"`
}

type decoder struct {
	*bufio.Scanner
	format    string
	Unmarshal func([]byte, interface{}) error
}

func scanYAMLDoc(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if loc := docSep.FindIndex(data); loc != nil {
		if loc[0] == 0 {
			// just a start document marker
			return loc[1], nil, nil
		}
		// We found a complete document
		return loc[1], data[0:loc[0]], nil
	}
	if atEOF {
		// if we're at EOF without having matched docSep, there's nothing else to get
		return len(data), nil, nil
	}
	// Request more data.
	return 0, nil, nil
}

func newDecoder(typ string, r io.Reader) decoder {
	dec := decoder{Scanner: bufio.NewScanner(r)}
	// TODO: handle plain TAP with a decoder as well
	switch typ {
	case "json":
		dec.format = "json"
		dec.Unmarshal = json.Unmarshal
	case "yaml":
		dec.format = "yaml"
		dec.Unmarshal = yaml.Unmarshal
		dec.Scanner.Split(scanYAMLDoc)
	default:
		panic("Unsupported decoder")
	}
	return dec
}

func (dec decoder) Type(b []byte) (string, error) {
	m := make(map[string]interface{})
	if err := dec.Unmarshal(b, &m); err != nil {
		return "", fmt.Errorf("Invalid document: %s", err)
	}
	if typ, ok := m["type"].(string); !ok {
		return "", fmt.Errorf("Missing 'type' key in document")
	} else {
		return typ, nil
	}
}

func ParseTap(r io.Reader, handler Handler) (*Suite, error) {
	if handler == nil {
		handler = &DefaultHandler{}
	}
	handler.HandleBeforeStream()
	rd := bufio.NewReader(r)
	var s Suite
	s.Start = &Time{time.Now()}
	first := true
	var totalTests int
	for {
		line, err := rd.ReadString('\n')
		if err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
		handler.HandleDoc(line, "tap")

		if line[0] == '#' {
			// TODO: do something with diagnostic lines
			continue
		}

		if matches := tapBailOut.FindStringSubmatch(line); matches != nil {
			// TODO: do something with the bail reason
			break
		}

		if first {
			if matches := tapVersion.FindStringSubmatch(line); matches != nil {
				// TAP 13+
				return nil, fmt.Errorf("TAP13 not supported yet")
			}
			first = false
			if matches := tapPlan.FindStringSubmatch(line); matches != nil {
				totalTests, _ = strconv.Atoi(matches[1])
				s.Count = totalTests
				// assuming plan line at beginning of output
				handler.HandleSuite(&s)
				continue
			}
		}

		matches := tapTestLine.FindStringSubmatch(line)
		if matches == nil {
			// TODO: handle extra data
			continue
		}

		t := Test{Type: "test"}
		switch matches[1] {
		case "ok":
			t.Status = "pass"
		case "not ok":
			// TODO: error?
			t.Status = "fail"
		}
		// ignore number
		t.Label = strings.TrimSpace(matches[3])
		directive := strings.TrimSpace(matches[4])
		switch {
		case strings.HasPrefix(strings.ToUpper(directive), "TODO"):
			s.Final.Counts.Todo++
			t.Label = directive
			t.Status = "todo"
		case strings.HasPrefix(strings.ToUpper(directive), "SKIP"):
			s.Final.Counts.Omit++
			t.Label = directive
			t.Status = "omit"
		case t.Status == "pass":
			s.Final.Counts.Pass++
		default:
			s.Final.Counts.Fail++
		}
		handler.HandleTest(&t)
		s.Final.Counts.Total++
		s.Tests = append(s.Tests, &t)
	}

	// fixup missing tests
	if totalTests > 0 && totalTests > s.Final.Counts.Total {
		s.Final.Counts.Fail += (totalTests - s.Final.Counts.Total)
		s.Final.Counts.Total = totalTests
	}

	handler.HandleFinal(&s.Final)
	handler.HandleAfterStream()
	return &s, nil
}
<commit_msg>Print errors instead of quitting<commit_after>package stein

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"time"

	"gopkg.in/v1/yaml"
)

var (
	docSep      = regexp.MustCompile(`(?m)^---$|^\.\.\.$`)
	tapTestLine = regexp.MustCompile(`(?m)^(ok|not ok)(?: (\d+))?( [^#]+)?(?:\s*#(.*))?$`)
	tapPlan     = regexp.MustCompile(`(?m)^\d..(\d+|N).*$`)
	tapVersion  = regexp.MustCompile(`(?m)^TAP version \d+$`)
	tapBailOut  = regexp.MustCompile(`(?m)^Bail out!\s*(.*)$`)
)

func Parse(r io.Reader) (*Suite, error) {
	parser := Parser{&DefaultHandler{}}
	return parser.Parse(r)
}

type Parser struct {
	Handler Handler
}

func (p *Parser) Parse(r io.Reader) (*Suite, error) {
	rd := bufio.NewReader(r)
	b, err := rd.Peek(1)
	if err != nil {
		panic(err)
	}

	if p.Handler == nil {
		p.Handler = &DefaultHandler{}
	}

	s := new(Suite)
	switch b[0] {
	case '{':
		return s, s.parse(newDecoder("json", rd), p.Handler)
	case '-':
		return s, s.parse(newDecoder("yaml", rd), p.Handler)
	default:
		return ParseTap(rd, p.Handler)
	}
}

type Time struct {
	time.Time
}

func (t *Time) parse(val string) (err error) {
	var ti time.Time
	if ti, err = time.Parse("2006-01-02 15:04:05", val); err != nil {
		if ti, err = time.Parse(time.RFC3339, val); err != nil {
			return
		}
	}
	(*t).Time = ti
	return
}

func (t *Time) UnmarshalJSON(data []byte) (err error) {
	// parse time, stripping the quotes
	return t.parse(string(data[1 : len(data)-1]))
}
func (t *Time) GetYAML() (tag string, value interface{}) {
	return "", t.String()
}
func (t *Time) SetYAML(tag string, value interface{}) bool {
	if v, ok := value.(string); ok {
		return t.parse(strings.Trim(v, `'"`)) == nil
	}
	return false
}

type Suite struct {
	Type  string
	Start *Time
	Count int
	Seed  int
	Rev   int
	Extra interface{} `yaml:",omitempty" json:",omitempty"`

	Tests []*Test
	Cases []*Case
	Notes []Note
	Final Tally
}

func (s *Suite) parse(dec decoder, handler Handler) (err error) {
	handler.HandleBeforeStream()
	var lastCase *Case
	for dec.Scan() {
		doc := dec.Scanner.Text()
		handler.HandleDoc(doc, dec.format)

		if strings.TrimSpace(doc) == "" {
			continue
		}

		if s.Start == nil {
			if err = dec.Unmarshal([]byte(doc), &s); err != nil {
				fmt.Println(err)
				continue
			}
			handler.HandleSuite(s)
			continue
		}

		var typ string
		if typ, err = dec.Type([]byte(doc)); err != nil {
			return
		}

		switch typ {
		case "case":
			var c Case
			if err = dec.Unmarshal([]byte(doc), &c); err != nil {
				fmt.Println(err)
				continue
			}

			if c.Level == 0 {
				s.Cases = append(s.Cases, &c)
				lastCase = &c
			} else if lastCase == nil {
				// TODO: possibly just add this case to the suite
				return fmt.Errorf("No parent found for test case: %v", c)
			} else {
				// find the parent
				for c.Level <= lastCase.Level {
					lastCase = lastCase.parent
				}
				// TODO: strict check to ensure c.Level == lastCase.Level + 1
				c.parent = lastCase
				lastCase.Subcases = append(lastCase.Subcases, &c)
			}
			handler.HandleCase(&c)
		case "test":
			var t Test
			if err = dec.Unmarshal([]byte(doc), &t); err != nil {
				fmt.Println(err)
				continue
			}
			if lastCase != nil {
				lastCase.Tests = append(lastCase.Tests, &t)
			} else {
				s.Tests = append(s.Tests, &t)
			}
			handler.HandleTest(&t)
		case "note":
			var n Note
			if err = dec.Unmarshal([]byte(doc), &n); err != nil {
				fmt.Println(err)
				continue
			}
			s.Notes = append(s.Notes, n)
			handler.HandleNote(&n)
		case "tally":
			var t Tally
			if err = dec.Unmarshal([]byte(doc), &t); err != nil {
				fmt.Println(err)
				continue
			}
			handler.HandleTally(&t)
		case "final":
			var t Tally
			if err = dec.Unmarshal([]byte(doc), &t); err != nil {
				fmt.Println(err)
				continue
			}
			s.Final = t
			handler.HandleFinal(&t)
		default:
			err = fmt.Errorf("Invalid type: %s", typ)
			return
		}
	}
	handler.HandleAfterStream()
	return
}

func (s Suite) Docs() (ret []interface{}) {
	ret = append(ret, s)
	for _, t := range s.Tests {
		ret = append(ret, t)
	}

	for _, c := range s.Cases {
		ret = append(ret, c.Docs()...)
	}

	for _, n := range s.Notes {
		ret = append(ret, n)
	}

	ret = append(ret, s.Final)
	return
}

func (s Suite) ToTapY() ([]byte, error) {
	var ret []byte
	for _, doc := range s.Docs() {
		if b, err := yaml.Marshal(doc); err != nil {
			return nil, err
		} else {
			ret = append(ret, []byte("---\n")...)
			ret = append(ret, b...)
			ret = append(ret, '\n')
		}
	}
	return append(ret, []byte("...")...), nil
}

type Case struct {
	Type  string
	Label string
	Level int

	Subtype string      `yaml:",omitempty" json:",omitempty"`
	Extra   interface{} `yaml:",omitempty" json:",omitempty"`

	// Not part of any TAP protocol
	Tests []*Test `yaml:",omitempty" json:",omitempty"`
	// Not part of any TAP protocol
	Subcases []*Case `yaml:",omitempty" json:",omitempty"`

	parent *Case
}

func (c Case) Docs() (ret []interface{}) {
	ret = append(ret, c)
	for _, t := range c.Tests {
		ret = append(ret, t)
	}
	for _, c := range c.Subcases {
		ret = append(ret, c.Docs()...)
	}
	return
}

// TODO: allow strings via UnmarshalJSON/YAML
type Snippet []map[string]string

type TestCoverage struct {
	File string `yaml:",omitempty" json:",omitempty"`
	// TODO: limit this to a number
	// according to tap-y/j, can be:
	// - line: 11
	// - range: 11..13
	// - list of lines: [11,12,13]
	Line interface{} `yaml:",omitempty" json:",omitempty"`
	Code string      `yaml:",omitempty" json:",omitempty"`
}

type TestException struct {
	Message   string
	File      string      `yaml:",omitempty" json:",omitempty"`
	Line      int         `yaml:",omitempty" json:",omitempty"`
	Source    string      `yaml:",omitempty" json:",omitempty"`
	Snippet   Snippet     `yaml:",omitempty" json:",omitempty"`
	Backtrace interface{} `yaml:",omitempty" json:",omitempty"`
}

type Test struct {
	Type   string
	Status string
	Label  string
	Time   float64

	Subtype   string         `yaml:",omitempty" json:",omitempty"`
	Setup     string         `yaml:",omitempty" json:",omitempty"`
	Expected  interface{}    `yaml:",omitempty" json:",omitempty"`
	Returned  interface{}    `yaml:",omitempty" json:",omitempty"`
	File      string         `yaml:",omitempty" json:",omitempty"`
	Line      int            `yaml:",omitempty" json:",omitempty"`
	Source    string         `yaml:",omitempty" json:",omitempty"`
	Snippet   Snippet        `yaml:",omitempty" json:",omitempty"`
	Coverage  *TestCoverage  `yaml:",omitempty" json:",omitempty"`
	Exception *TestException `yaml:",omitempty" json:",omitempty"`
	Stdout    string         `yaml:",omitempty" json:",omitempty"`
	Stderr    string         `yaml:",omitempty" json:",omitempty"`
	Extra     interface{}    `yaml:",omitempty" json:",omitempty"`
}

type Note struct {
	Type  string
	Text  string
	Extra interface{} `yaml:",omitempty" json:",omitempty"`
}

type Tally struct {
	Type   string
	Time   float64 `yaml:",omitempty" json:",omitempty"`
	Counts struct {
		Total int
		Pass  int
		Fail  int
		Error int
		Omit  int
		Todo  int
	}
	Extra interface{} `yaml:",omitempty" json:",omitempty"`
}

type decoder struct {
	*bufio.Scanner
	format    string
	Unmarshal func([]byte, interface{}) error
}

func scanYAMLDoc(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if loc := docSep.FindIndex(data); loc != nil {
		if loc[0] == 0 {
			// just a start document marker
			return loc[1], nil, nil
		}
		// We found a complete document
		return loc[1], data[0:loc[0]], nil
	}
	if atEOF {
		// if we're at EOF without having matched docSep, there's nothing else to get
		return len(data), nil, nil
	}
	// Request more data.
	return 0, nil, nil
}

func newDecoder(typ string, r io.Reader) decoder {
	dec := decoder{Scanner: bufio.NewScanner(r)}
	// TODO: handle plain TAP with a decoder as well
	switch typ {
	case "json":
		dec.format = "json"
		dec.Unmarshal = json.Unmarshal
	case "yaml":
		dec.format = "yaml"
		dec.Unmarshal = yaml.Unmarshal
		dec.Scanner.Split(scanYAMLDoc)
	default:
		panic("Unsupported decoder")
	}
	return dec
}

func (dec decoder) Type(b []byte) (string, error) {
	m := make(map[string]interface{})
	if err := dec.Unmarshal(b, &m); err != nil {
		return "", fmt.Errorf("Invalid document: %s", err)
	}
	if typ, ok := m["type"].(string); !ok {
		return "", fmt.Errorf("Missing 'type' key in document")
	} else {
		return typ, nil
	}
}

func ParseTap(r io.Reader, handler Handler) (*Suite, error) {
	if handler == nil {
		handler = &DefaultHandler{}
	}
	handler.HandleBeforeStream()
	rd := bufio.NewReader(r)
	var s Suite
	s.Start = &Time{time.Now()}
	first := true
	var totalTests int
	for {
		line, err := rd.ReadString('\n')
		if err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
		handler.HandleDoc(line, "tap")

		if line[0] == '#' {
			// TODO: do something with diagnostic lines
			continue
		}

		if matches := tapBailOut.FindStringSubmatch(line); matches != nil {
			// TODO: do something with the bail reason
			break
		}

		if first {
			if matches := tapVersion.FindStringSubmatch(line); matches != nil {
				// TAP 13+
				return nil, fmt.Errorf("TAP13 not supported yet")
			}
			first = false
			if matches := tapPlan.FindStringSubmatch(line); matches != nil {
				totalTests, _ = strconv.Atoi(matches[1])
				s.Count = totalTests
				// assuming plan line at beginning of output
				handler.HandleSuite(&s)
				continue
			}
		}

		matches := tapTestLine.FindStringSubmatch(line)
		if matches == nil {
			// TODO: handle extra data
			continue
		}

		t := Test{Type: "test"}
		switch matches[1] {
		case "ok":
			t.Status = "pass"
		case "not ok":
			// TODO: error?
			t.Status = "fail"
		}
		// ignore number
		t.Label = strings.TrimSpace(matches[3])
		directive := strings.TrimSpace(matches[4])
		switch {
		case strings.HasPrefix(strings.ToUpper(directive), "TODO"):
			s.Final.Counts.Todo++
			t.Label = directive
			t.Status = "todo"
		case strings.HasPrefix(strings.ToUpper(directive), "SKIP"):
			s.Final.Counts.Omit++
			t.Label = directive
			t.Status = "omit"
		case t.Status == "pass":
			s.Final.Counts.Pass++
		default:
			s.Final.Counts.Fail++
		}
		handler.HandleTest(&t)
		s.Final.Counts.Total++
		s.Tests = append(s.Tests, &t)
	}

	// fixup missing tests
	if totalTests > 0 && totalTests > s.Final.Counts.Total {
		s.Final.Counts.Fail += (totalTests - s.Final.Counts.Total)
		s.Final.Counts.Total = totalTests
	}

	handler.HandleFinal(&s.Final)
	handler.HandleAfterStream()
	return &s, nil
}
<|endoftext|>


<commit_before>package lua

type lValueArraySorter struct {
	L      *LState
	Fn     *LFunction
	Values []LValue
}

func (lv lValueArraySorter) Len() int {
	return len(lv.Values)
}

func (lv lValueArraySorter) Swap(i, j int) {
	lv.Values[i], lv.Values[j] = lv.Values[j], lv.Values[i]
}

func (lv lValueArraySorter) Less(i, j int) bool {
	if lv.Fn != nil {
		lv.L.Push(lv.Fn)
		lv.L.Push(lv.Values[i])
		lv.L.Push(lv.Values[j])
		lv.L.Call(2, 1)
		return LVAsBool(lv.L.reg.Pop())
	}
	return lessThan(lv.L, lv.Values[i], lv.Values[j])
}

func newLTable(acap int, hcap int) *LTable {
	if acap < 0 {
		acap = 0
	}
	if hcap < 0 {
		hcap = 0
	}
	tb := &LTable{
		array:     make([]LValue, 0, acap),
		dict:      make(map[LValue]LValue, hcap),
		keys:      nil,
		k2i:       nil,
		Metatable: LNil,
	}
	return tb
}

func (tb *LTable) Len() int {
	var prev LValue = LNil
	for i := len(tb.array) - 1; i >= 0; i-- {
		v := tb.array[i]
		if prev == LNil && v != LNil {
			return i + 1
		}
		prev = v
	}
	return 0
}

func (tb *LTable) Append(value LValue) {
	tb.array = append(tb.array, value)
}

func (tb *LTable) Insert(i int, value LValue) {
	if i > len(tb.array) {
		tb.RawSetInt(i, value)
		return
	}
	if i <= 0 {
		tb.RawSet(LNumber(i), value)
		return
	}
	i -= 1
	tb.array = append(tb.array, LNil)
	copy(tb.array[i+1:], tb.array[i:])
	tb.array[i] = value
}

func (tb *LTable) MaxN() int {
	for i := len(tb.array) - 1; i >= 0; i-- {
		if tb.array[i] != LNil {
			return i+1
		}
	}
	return 0
}

func (tb *LTable) Remove(pos int) {
	i := pos - 1
	larray := len(tb.array)
	switch {
	case i >= larray:
		return
	case i == larray-1 || i < 0:
		tb.array = tb.array[:larray-1]
	default:
		copy(tb.array[i:], tb.array[i+1:])
		tb.array[larray-1] = nil
		tb.array = tb.array[:larray-1]
	}
}

func (tb *LTable) RawSet(key LValue, value LValue) {
	switch v := key.(type) {
	case LNumber:
		if isArrayKey(v) {
			index := int(v) - 1
			alen := len(tb.array)
			switch {
			case index == alen:
				tb.array = append(tb.array, value)
			case index > alen:
				for i := 0; i < (index - alen); i++ {
					tb.array = append(tb.array, LNil)
				}
				tb.array = append(tb.array, value)
			case index < alen:
				tb.array[index] = value
			}
			return
		}
	}
	tb.dict[key] = value
}

func (tb *LTable) RawSetInt(key int, value LValue) {
	if key < 1 || key >= MaxArrayIndex {
		tb.dict[LNumber(key)] = value
		return
	}
	index := key - 1
	alen := len(tb.array)
	switch {
	case index == alen:
		tb.array = append(tb.array, value)
	case index > alen:
		for i := 0; i < (index - alen); i++ {
			tb.array = append(tb.array, LNil)
		}
		tb.array = append(tb.array, value)
	case index < alen:
		tb.array[index] = value
	}
}

func (tb *LTable) RawSetH(key LValue, value LValue) {
	tb.dict[key] = value
}

func (tb *LTable) RawGet(key LValue) LValue {
	switch v := key.(type) {
	case LNumber:
		if isArrayKey(v) {
			index := int(v) - 1
			if index >= len(tb.array) {
				return LNil
			}
			return tb.array[index]
		}
	}
	if v, ok := tb.dict[key]; ok {
		return v
	}
	return LNil
}

func (tb *LTable) RawGetInt(key int) LValue {
	index := int(key) - 1
	if index >= len(tb.array) || index < 0 {
		return LNil
	}
	return tb.array[index]
}

func (tb *LTable) RawGetH(key LValue) LValue {
	if v, ok := tb.dict[key]; ok {
		return v
	}
	return LNil
}

func (tb *LTable) ForEach(cb func(LValue, LValue)) {
	for i, v := range tb.array {
		if v != LNil {
			cb(LNumber(i+1), v)
		}
	}
	for k, v := range tb.dict {
		if v != LNil {
			cb(k, v)
		}
	}
}

func (tb *LTable) Next(key LValue) (LValue, LValue) {
	// TODO: inefficient way
	if key == LNil {
		tb.keys = nil
		tb.k2i = nil
		key = LNumber(0)
	}

	if tb.keys == nil {
		tb.keys = make([]LValue, len(tb.dict))
		tb.k2i = make(map[LValue]int)
		i := 0
		for k, _ := range tb.dict {
			tb.keys[i] = k
			tb.k2i[k] = i
			i++
		}
	}

	if kv, ok := key.(LNumber); ok && isInteger(kv) && int(kv) >= 0 {
		index := int(kv)
		for ; index < len(tb.array); index++ {
			if v := tb.array[index]; v != LNil {
				return LNumber(index + 1), v
			}
		}
		if index == len(tb.array) {
			if len(tb.dict) == 0 {
				tb.keys = nil
				tb.k2i = nil
				return LNil, LNil
			}
			key = tb.keys[0]
			if v := tb.dict[key]; v != LNil {
				return key, v
			}
		}
	}
	for i := tb.k2i[key] + 1; i < len(tb.dict); i++ {
		key = tb.keys[i]
		if v := tb.dict[key]; v != LNil {
			return key, v
		}
	}
	tb.keys = nil
	tb.k2i = nil
	return LNil, LNil
}
<commit_msg>fix table not deleting dict values<commit_after>package lua

type lValueArraySorter struct {
	L      *LState
	Fn     *LFunction
	Values []LValue
}

func (lv lValueArraySorter) Len() int {
	return len(lv.Values)
}

func (lv lValueArraySorter) Swap(i, j int) {
	lv.Values[i], lv.Values[j] = lv.Values[j], lv.Values[i]
}

func (lv lValueArraySorter) Less(i, j int) bool {
	if lv.Fn != nil {
		lv.L.Push(lv.Fn)
		lv.L.Push(lv.Values[i])
		lv.L.Push(lv.Values[j])
		lv.L.Call(2, 1)
		return LVAsBool(lv.L.reg.Pop())
	}
	return lessThan(lv.L, lv.Values[i], lv.Values[j])
}

func newLTable(acap int, hcap int) *LTable {
	if acap < 0 {
		acap = 0
	}
	if hcap < 0 {
		hcap = 0
	}
	tb := &LTable{
		array:     make([]LValue, 0, acap),
		dict:      make(map[LValue]LValue, hcap),
		keys:      nil,
		k2i:       nil,
		Metatable: LNil,
	}
	return tb
}

func (tb *LTable) Len() int {
	var prev LValue = LNil
	for i := len(tb.array) - 1; i >= 0; i-- {
		v := tb.array[i]
		if prev == LNil && v != LNil {
			return i + 1
		}
		prev = v
	}
	return 0
}

func (tb *LTable) Append(value LValue) {
	tb.array = append(tb.array, value)
}

func (tb *LTable) Insert(i int, value LValue) {
	if i > len(tb.array) {
		tb.RawSetInt(i, value)
		return
	}
	if i <= 0 {
		tb.RawSet(LNumber(i), value)
		return
	}
	i -= 1
	tb.array = append(tb.array, LNil)
	copy(tb.array[i+1:], tb.array[i:])
	tb.array[i] = value
}

func (tb *LTable) MaxN() int {
	for i := len(tb.array) - 1; i >= 0; i-- {
		if tb.array[i] != LNil {
			return i+1
		}
	}
	return 0
}

func (tb *LTable) Remove(pos int) {
	i := pos - 1
	larray := len(tb.array)
	switch {
	case i >= larray:
		return
	case i == larray-1 || i < 0:
		tb.array = tb.array[:larray-1]
	default:
		copy(tb.array[i:], tb.array[i+1:])
		tb.array[larray-1] = nil
		tb.array = tb.array[:larray-1]
	}
}

func (tb *LTable) RawSet(key LValue, value LValue) {
	switch v := key.(type) {
	case LNumber:
		if isArrayKey(v) {
			index := int(v) - 1
			alen := len(tb.array)
			switch {
			case index == alen:
				tb.array = append(tb.array, value)
			case index > alen:
				for i := 0; i < (index - alen); i++ {
					tb.array = append(tb.array, LNil)
				}
				tb.array = append(tb.array, value)
			case index < alen:
				tb.array[index] = value
			}
			return
		}
	}
	tb.RawSetH(key, value)
}

func (tb *LTable) RawSetInt(key int, value LValue) {
	if key < 1 || key >= MaxArrayIndex {
		tb.RawSetH(LNumber(key), value)
		return
	}
	index := key - 1
	alen := len(tb.array)
	switch {
	case index == alen:
		tb.array = append(tb.array, value)
	case index > alen:
		for i := 0; i < (index - alen); i++ {
			tb.array = append(tb.array, LNil)
		}
		tb.array = append(tb.array, value)
	case index < alen:
		tb.array[index] = value
	}
}

func (tb *LTable) RawSetH(key LValue, value LValue) {
	if value == LNil {
		delete(tb.dict, key)
	} else {
		tb.dict[key] = value
	}
}

func (tb *LTable) RawGet(key LValue) LValue {
	switch v := key.(type) {
	case LNumber:
		if isArrayKey(v) {
			index := int(v) - 1
			if index >= len(tb.array) {
				return LNil
			}
			return tb.array[index]
		}
	}
	if v, ok := tb.dict[key]; ok {
		return v
	}
	return LNil
}

func (tb *LTable) RawGetInt(key int) LValue {
	index := int(key) - 1
	if index >= len(tb.array) || index < 0 {
		return LNil
	}
	return tb.array[index]
}

func (tb *LTable) RawGetH(key LValue) LValue {
	if v, ok := tb.dict[key]; ok {
		return v
	}
	return LNil
}

func (tb *LTable) ForEach(cb func(LValue, LValue)) {
	for i, v := range tb.array {
		if v != LNil {
			cb(LNumber(i+1), v)
		}
	}
	for k, v := range tb.dict {
		if v != LNil {
			cb(k, v)
		}
	}
}

func (tb *LTable) Next(key LValue) (LValue, LValue) {
	// TODO: inefficient way
	if key == LNil {
		tb.keys = nil
		tb.k2i = nil
		key = LNumber(0)
	}

	if tb.keys == nil {
		tb.keys = make([]LValue, len(tb.dict))
		tb.k2i = make(map[LValue]int)
		i := 0
		for k, _ := range tb.dict {
			tb.keys[i] = k
			tb.k2i[k] = i
			i++
		}
	}

	if kv, ok := key.(LNumber); ok && isInteger(kv) && int(kv) >= 0 {
		index := int(kv)
		for ; index < len(tb.array); index++ {
			if v := tb.array[index]; v != LNil {
				return LNumber(index + 1), v
			}
		}
		if index == len(tb.array) {
			if len(tb.dict) == 0 {
				tb.keys = nil
				tb.k2i = nil
				return LNil, LNil
			}
			key = tb.keys[0]
			if v := tb.dict[key]; v != LNil {
				return key, v
			}
		}
	}
	for i := tb.k2i[key] + 1; i < len(tb.dict); i++ {
		key = tb.keys[i]
		if v := tb.dict[key]; v != LNil {
			return key, v
		}
	}
	tb.keys = nil
	tb.k2i = nil
	return LNil, LNil
}
<|endoftext|>


<commit_before>package tokenauth

import (
	"crypto/rand"
	"crypto/subtle"
	"encoding/base64"
	"errors"
	"github.com/ant0ine/go-json-rest/rest"
	"log"
	"net/http"
	"strings"
)

// AuthTokenMiddleware provides a Token Auth implementation. On success, the wrapped middleware
// is called, and the userID is made available as request.Env["REMOTE_USER"].(string)
type AuthTokenMiddleware struct {
	// Realm name to display to the user. Required.
	Realm string

	// Callback function that should perform the authentication of the user based on token.
	// Must return userID as string on success, empty string on failure. Required.
	// The returned userID is normally the primary key for your user record.
	Authenticator func(token string) string

	// Callback function that should perform the authorization of the authenticated user.
	// Must return true on success, false on failure. Optional, defaults to success.
	// Called only after an authentication success.
	Authorizer func(request *rest.Request) bool

	// Nominal token entropy in bytes. Optional, defaults to recommended 32 bytes / 256-bit.
	TokenEntropy int
}

// MiddlewareFunc makes AuthTokenMiddleware implement the Middleware interface.
func (mw *AuthTokenMiddleware) MiddlewareFunc(handler rest.HandlerFunc) rest.HandlerFunc {
	if mw.Realm == "" {
		log.Fatal("Realm is required")
	}

	if mw.Authenticator == nil {
		log.Fatal("Authenticator is required")
	}

	if mw.Authorizer == nil {
		mw.Authorizer = func(request *rest.Request) bool {
			return true
		}
	}

	if mw.TokenEntropy == 0 {
		mw.TokenEntropy = 32 // 32 byte == 256 bit == recommended secure ID entropy
	}

	return func(writer rest.ResponseWriter, request *rest.Request) {
		authHeader := request.Header.Get("Authorization")
		// Authorization header was not provided
		if authHeader == "" {
			mw.unauthorized(writer)
			return
		}

		token, err := mw.decodeAuthHeader(authHeader)
		// Authorization header was *malformed* such that we couldn't extract a token
		if err != nil {
			mw.unauthorized(writer)
			return
		}

		userID := mw.Authenticator(token)
		// The token didn't map to a user, it's most likely either invalid or expired
		if userID == "" {
			mw.unauthorized(writer)
			return
		}

		// The user's token was valid, but they're not authorized for the current request
		if !mw.Authorizer(request) {
			mw.unauthorized(writer)
			return
		}

		request.Env["REMOTE_USER"] = userID
		handler(writer, request)
	}
}

// Extract the token from the current request.
func (mw *AuthTokenMiddleware) decodeAuthHeader(header string) (string, error) {
	parts := strings.SplitN(header, " ", 2)
	if !(len(parts) == 2 && parts[0] == "Token") {
		return "", errors.New("Invalid authentication")
	}
	token, err := base64.URLEncoding.DecodeString(parts[1])
	if err != nil {
		return "", errors.New("Invalid base64")
	}
	return string(token), nil
}

// New generates a new random token
func (mw *AuthTokenMiddleware) New() (string, error) {
	bytes := make([]byte, mw.TokenEntropy)
	_, err := rand.Read(bytes[:cap(bytes)])
	if err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(bytes), nil
}

// Equal does constant-time XOR comparison of two tokens
func (mw *AuthTokenMiddleware) Equal(a, b string) bool {
	return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
}

func (mw *AuthTokenMiddleware) unauthorized(writer rest.ResponseWriter) {
	writer.Header().Set("WWW-Authenticate", "Token realm="+mw.Realm)
	rest.Error(writer, "Not Authorized", http.StatusUnauthorized)
}
<commit_msg>Provide Hash(token) helper method<commit_after>package tokenauth

import (
	"crypto/md5"
	"crypto/rand"
	"crypto/subtle"
	"encoding/base64"
	"errors"
	"github.com/ant0ine/go-json-rest/rest"
	"log"
	"net/http"
	"strings"
)

// AuthTokenMiddleware provides a Token Auth implementation. On success, the wrapped middleware
// is called, and the userID is made available as request.Env["REMOTE_USER"].(string)
type AuthTokenMiddleware struct {
	// Realm name to display to the user. Required.
	Realm string

	// Callback function that should perform the authentication of the user based on token.
	// Must return userID as string on success, empty string on failure. Required.
	// The returned userID is normally the primary key for your user record.
	Authenticator func(token string) string

	// Callback function that should perform the authorization of the authenticated user.
	// Must return true on success, false on failure. Optional, defaults to success.
	// Called only after an authentication success.
	Authorizer func(request *rest.Request) bool

	// Nominal token entropy in bytes. Optional, defaults to recommended 32 bytes / 256-bit.
	TokenEntropy int
}

// MiddlewareFunc makes AuthTokenMiddleware implement the Middleware interface.
func (mw *AuthTokenMiddleware) MiddlewareFunc(handler rest.HandlerFunc) rest.HandlerFunc {
	if mw.Realm == "" {
		log.Fatal("Realm is required")
	}

	if mw.Authenticator == nil {
		log.Fatal("Authenticator is required")
	}

	if mw.Authorizer == nil {
		mw.Authorizer = func(request *rest.Request) bool {
			return true
		}
	}

	if mw.TokenEntropy == 0 {
		mw.TokenEntropy = 32 // 32 byte == 256 bit == recommended secure ID entropy
	}

	return func(writer rest.ResponseWriter, request *rest.Request) {
		authHeader := request.Header.Get("Authorization")
		// Authorization header was not provided
		if authHeader == "" {
			mw.unauthorized(writer)
			return
		}

		token, err := mw.decodeAuthHeader(authHeader)
		// Authorization header was *malformed* such that we couldn't extract a token
		if err != nil {
			mw.unauthorized(writer)
			return
		}

		userID := mw.Authenticator(token)
		// The token didn't map to a user, it's most likely either invalid or expired
		if userID == "" {
			mw.unauthorized(writer)
			return
		}

		// The user's token was valid, but they're not authorized for the current request
		if !mw.Authorizer(request) {
			mw.unauthorized(writer)
			return
		}

		request.Env["REMOTE_USER"] = userID
		handler(writer, request)
	}
}

// Extract the token from the current request.
func (mw *AuthTokenMiddleware) decodeAuthHeader(header string) (string, error) {
	parts := strings.SplitN(header, " ", 2)
	if !(len(parts) == 2 && parts[0] == "Token") {
		return "", errors.New("Invalid authentication")
	}
	token, err := base64.URLEncoding.DecodeString(parts[1])
	if err != nil {
		return "", errors.New("Invalid base64")
	}
	return string(token), nil
}

// New generates a new random token
func (mw *AuthTokenMiddleware) New() (string, error) {
	bytes := make([]byte, mw.TokenEntropy)
	_, err := rand.Read(bytes[:cap(bytes)])
	if err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(bytes), nil
}

// Equal does constant-time XOR comparison of two tokens
func (mw *AuthTokenMiddleware) Equal(a, b string) bool {
	return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
}

// Hash applies a simple MD5 hash over a token, making it safe to store
func (mw *AuthTokenMiddleware) Hash(token string) string {
	return base64.URLEncoding.EncodeToString(md5.Sum([]byte(token)))
}

func (mw *AuthTokenMiddleware) unauthorized(writer rest.ResponseWriter) {
	writer.Header().Set("WWW-Authenticate", "Token realm="+mw.Realm)
	rest.Error(writer, "Not Authorized", http.StatusUnauthorized)
}
<|endoftext|>


<commit_before>package enmime

import (
	"fmt"
	"mime"
	"net/mail"
	"net/textproto"
	"strings"
)

// MIMEBody is the outer wrapper for MIME messages.
type MIMEBody struct {
	Text        string      // The plain text portion of the message
	Html        string      // The HTML portion of the message
	Root        MIMEPart    // The top-level MIMEPart
	Attachments []MIMEPart  // All parts having a Content-Disposition of attachment
	Inlines     []MIMEPart  // All parts having a Content-Disposition of inline
	OtherParts  []MIMEPart  // All parts not in Attachments and Inlines
	header      mail.Header // Header from original message
}

var AddressHeaders = []string{"From", "To", "Delivered-To", "Cc", "Bcc", "Reply-To"}

// IsMultipartMessage returns true if the message has a recognized multipart Content-Type
// header.  You don't need to check this before calling ParseMIMEBody, it can handle
// non-multipart messages.
func IsMultipartMessage(mailMsg *mail.Message) bool {
	// Parse top-level multipart
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, _, err := mime.ParseMediaType(ctype)
	if err != nil {
		return false
	}
	switch mediatype {
	case "multipart/alternative",
		"multipart/mixed",
		"multipart/related",
		"multipart/signed":
		return true
	default:
		if strings.HasPrefix(mediatype, "multipart/") {
			// according to rfc2046#section-5.1.7 all other multipart should
			// be treated as multipart/mixed
			return true
		}
	}
	return false
}

// IsAttachment returns true, if the given header defines an attachment.
// First it checks, if the Content-Disposition header defines an attachement.
// If this test is false, the Content-Type header is checked.
//
// Valid Attachment-Headers:
//
//    Content-Disposition: attachment; filename="frog.jpg"
//    Content-Type: attachment; filename="frog.jpg"
//
func IsAttachment(header mail.Header) bool {
	mediatype, _, _ := mime.ParseMediaType(header.Get("Content-Disposition"))
	if strings.ToLower(mediatype) == "attachment" {
		return true
	}

	mediatype, _, _ = mime.ParseMediaType(header.Get("Content-Type"))
	if strings.ToLower(mediatype) == "attachment" {
		return true
	}

	return false
}

// IsPlain returns true, if the the mime headers define a valid
// 'text/plain' or 'text/html part'. Ff emptyContentTypeIsPlain is set
// to true, a missing Content-Type header will result in a positive
// plain part detection.
func IsPlain(header mail.Header, emptyContentTypeIsPlain bool) bool {
	ctype := header.Get("Content-Type")
	if ctype == "" && emptyContentTypeIsPlain {
		return true
	}

	mediatype, _, err := mime.ParseMediaType(ctype)
	if err != nil {
		return false
	}
	switch mediatype {
	case "text/plain",
		"text/html":
		return true
	}

	return false

}

// IsBinaryBody returns true, if the mail header defines a binary body.
func IsBinaryBody(mailMsg *mail.Message) bool {
	if IsAttachment(mailMsg.Header) == true {
		return true
	}

	return !IsPlain(mailMsg.Header, true)
}

// Returns a MIME message with only one Attachment, the parsed original mail body.
func binMIME(mailMsg *mail.Message) (*MIMEBody, error) {
	// Root Node of our tree
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, mparams, err := mime.ParseMediaType(ctype)
	if err != nil {
		mediatype = "attachment"
	}

	m := &MIMEBody{
		header: mailMsg.Header,
		Root:   NewMIMEPart(nil, mediatype),
	}

	p := NewMIMEPart(nil, mediatype)
	p.content, err = decodeSection(mailMsg.Header.Get("Content-Transfer-Encoding"), mailMsg.Body)
	if err != nil {
		return nil, err
	}

	// get set headers
	p.header = make(textproto.MIMEHeader, 4)
	// Figure out our disposition, filename
	disposition, dparams, err := mime.ParseMediaType(mailMsg.Header.Get("Content-Disposition"))
	if err == nil {
		// Disposition is optional
		p.disposition = disposition
		p.fileName = DecodeHeader(dparams["filename"])
	}
	if p.fileName == "" && mparams["name"] != "" {
		p.fileName = DecodeHeader(mparams["name"])
	}
	if p.fileName == "" && mparams["file"] != "" {
		p.fileName = DecodeHeader(mparams["file"])
	}
	if p.charset == "" {
		p.charset = mparams["charset"]
	}

	p.header.Set("Content-Type", mailMsg.Header.Get("Content-Type"))
	p.header.Set("Content-Disposition", mailMsg.Header.Get("Content-Disposition"))

	m.Attachments = append(m.Attachments, p)
	return m, err
}

// ParseMIMEBody parses the body of the message object into a  tree of MIMEPart objects,
// each of which is aware of its content type, filename and headers.  If the part was
// encoded in quoted-printable or base64, it is decoded before being stored in the
// MIMEPart object.
func ParseMIMEBody(mailMsg *mail.Message) (*MIMEBody, error) {
	mimeMsg := &MIMEBody{header: mailMsg.Header}

	if !IsMultipartMessage(mailMsg) {

		// Attachment only?
		if IsBinaryBody(mailMsg) {
			return binMIME(mailMsg)
		}

		// Parse as text only
		bodyBytes, err := decodeSection(mailMsg.Header.Get("Content-Transfer-Encoding"),
			mailMsg.Body)
		if err != nil {
			return nil, fmt.Errorf("Error decoding text-only message: %v", err)
		}
		mimeMsg.Text = string(bodyBytes)

		// Check for HTML at top-level, eat errors quietly
		ctype := mailMsg.Header.Get("Content-Type")
		if ctype != "" {
			if mediatype, mparams, err := mime.ParseMediaType(ctype); err == nil {
				/*
				 *Content-Type: text/plain;\t charset="hz-gb-2312"
				 */
				if mparams["charset"] != "" {
					newStr, err := ConvertToUTF8String(mparams["charset"], bodyBytes)
					if err != nil {
						return nil, err
					}
					mimeMsg.Text = newStr
				} else if mediatype == "text/html" { // charset is empty, look in html body for charset
					charset, err := charsetFromHTMLString(mimeMsg.Text)

					if charset != "" && err == nil {
						newStr, err := ConvertToUTF8String(charset, bodyBytes)
						if err == nil {
							mimeMsg.Text = newStr
						}
					}
				}
				if mediatype == "text/html" {
					mimeMsg.Html = mimeMsg.Text
				}
			}
		}
	} else {
		// Parse top-level multipart
		ctype := mailMsg.Header.Get("Content-Type")
		mediatype, params, err := mime.ParseMediaType(ctype)
		if err != nil {
			return nil, fmt.Errorf("Unable to parse media type: %v", err)
		}
		if !strings.HasPrefix(mediatype, "multipart/") {
			return nil, fmt.Errorf("Unknown mediatype: %v", mediatype)
		}
		boundary := params["boundary"]
		if boundary == "" {
			return nil, fmt.Errorf("Unable to locate boundary param in Content-Type header")
		}
		// Root Node of our tree
		root := NewMIMEPart(nil, mediatype)
		mimeMsg.Root = root
		err = parseParts(root, mailMsg.Body, boundary)
		if err != nil {
			return nil, err
		}

		// Locate text body
		if mediatype == "multipart/altern" {
			match := BreadthMatchFirst(root, func(p MIMEPart) bool {
				return p.ContentType() == "text/plain" && p.Disposition() != "attachment"
			})
			if match != nil {
				if match.Charset() != "" {
					newStr, err := ConvertToUTF8String(match.Charset(), match.Content())
					if err != nil {
						return nil, err
					}
					mimeMsg.Text += newStr
				} else {
					mimeMsg.Text += string(match.Content())
				}
			}
		} else {
			// multipart is of a mixed type
			match := DepthMatchAll(root, func(p MIMEPart) bool {
				return p.ContentType() == "text/plain" && p.Disposition() != "attachment"
			})
			for i, m := range match {
				if i > 0 {
					mimeMsg.Text += "\n--\n"
				}
				if m.Charset() != "" {
					newStr, err := ConvertToUTF8String(m.Charset(), m.Content())
					if err != nil {
						return nil, err
					}
					mimeMsg.Text += newStr
				} else {
					mimeMsg.Text += string(m.Content())
				}
			}
		}

		// Locate HTML body
		match := BreadthMatchFirst(root, func(p MIMEPart) bool {
			return p.ContentType() == "text/html" && p.Disposition() != "attachment"
		})
		if match != nil {
			if match.Charset() != "" {
				newStr, err := ConvertToUTF8String(match.Charset(), match.Content())
				if err != nil {
					return nil, err
				}
				mimeMsg.Html += newStr
			} else {
				mimeMsg.Html = string(match.Content())
			}

		}

		// Locate attachments
		mimeMsg.Attachments = BreadthMatchAll(root, func(p MIMEPart) bool {
			return p.Disposition() == "attachment" || p.ContentType() == "application/octet-stream"
		})

		// Locate inlines
		mimeMsg.Inlines = BreadthMatchAll(root, func(p MIMEPart) bool {
			return p.Disposition() == "inline"
		})

		// Locate others parts not handled in "Attachments" and "inlines"
		mimeMsg.OtherParts = BreadthMatchAll(root, func(p MIMEPart) bool {
			if strings.HasPrefix(p.ContentType(), "multipart/") {
				return false
			}

			if p.Disposition() != "" {
				return false
			}

			if p.ContentType() == "application/octet-stream" {
				return false
			}

			return p.ContentType() != "text/plain" && p.ContentType() != "text/html"
		})
	}

	return mimeMsg, nil
}

// Process the specified header for RFC 2047 encoded words and return the result
func (m *MIMEBody) GetHeader(name string) string {
	return DecodeHeader(m.header.Get(name))
}

// Return AddressList with RFC 2047 encoded encoded names.
func (m *MIMEBody) AddressList(key string) ([]*mail.Address, error) {
	isAddrHeader := false
	for _, hkey := range AddressHeaders {
		if strings.ToLower(hkey) == strings.ToLower(key) {
			isAddrHeader = true
			break
		}
	}
	if !isAddrHeader {
		return nil, fmt.Errorf("%s is not address header", key)
	}

	str := DecodeToUTF8Base64Header(m.header.Get(key))
	if str == "" {
		return nil, mail.ErrHeaderNotPresent
	}
	ret, err := mail.ParseAddressList(str)
	if err != nil {
		return nil, err
	}
	return ret, nil
}
<commit_msg>Typo.<commit_after>package enmime

import (
	"fmt"
	"mime"
	"net/mail"
	"net/textproto"
	"strings"
)

// MIMEBody is the outer wrapper for MIME messages.
type MIMEBody struct {
	Text        string      // The plain text portion of the message
	Html        string      // The HTML portion of the message
	Root        MIMEPart    // The top-level MIMEPart
	Attachments []MIMEPart  // All parts having a Content-Disposition of attachment
	Inlines     []MIMEPart  // All parts having a Content-Disposition of inline
	OtherParts  []MIMEPart  // All parts not in Attachments and Inlines
	header      mail.Header // Header from original message
}

var AddressHeaders = []string{"From", "To", "Delivered-To", "Cc", "Bcc", "Reply-To"}

// IsMultipartMessage returns true if the message has a recognized multipart Content-Type
// header.  You don't need to check this before calling ParseMIMEBody, it can handle
// non-multipart messages.
func IsMultipartMessage(mailMsg *mail.Message) bool {
	// Parse top-level multipart
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, _, err := mime.ParseMediaType(ctype)
	if err != nil {
		return false
	}
	switch mediatype {
	case "multipart/alternative",
		"multipart/mixed",
		"multipart/related",
		"multipart/signed":
		return true
	default:
		if strings.HasPrefix(mediatype, "multipart/") {
			// according to rfc2046#section-5.1.7 all other multipart should
			// be treated as multipart/mixed
			return true
		}
	}
	return false
}

// IsAttachment returns true, if the given header defines an attachment.
// First it checks, if the Content-Disposition header defines an attachement.
// If this test is false, the Content-Type header is checked.
//
// Valid Attachment-Headers:
//
//    Content-Disposition: attachment; filename="frog.jpg"
//    Content-Type: attachment; filename="frog.jpg"
//
func IsAttachment(header mail.Header) bool {
	mediatype, _, _ := mime.ParseMediaType(header.Get("Content-Disposition"))
	if strings.ToLower(mediatype) == "attachment" {
		return true
	}

	mediatype, _, _ = mime.ParseMediaType(header.Get("Content-Type"))
	if strings.ToLower(mediatype) == "attachment" {
		return true
	}

	return false
}

// IsPlain returns true, if the the mime headers define a valid
// 'text/plain' or 'text/html part'. If emptyContentTypeIsPlain is set
// to true, a missing Content-Type header will result in a positive
// plain part detection.
func IsPlain(header mail.Header, emptyContentTypeIsPlain bool) bool {
	ctype := header.Get("Content-Type")
	if ctype == "" && emptyContentTypeIsPlain {
		return true
	}

	mediatype, _, err := mime.ParseMediaType(ctype)
	if err != nil {
		return false
	}
	switch mediatype {
	case "text/plain",
		"text/html":
		return true
	}

	return false

}

// IsBinaryBody returns true, if the mail header defines a binary body.
func IsBinaryBody(mailMsg *mail.Message) bool {
	if IsAttachment(mailMsg.Header) == true {
		return true
	}

	return !IsPlain(mailMsg.Header, true)
}

// Returns a MIME message with only one Attachment, the parsed original mail body.
func binMIME(mailMsg *mail.Message) (*MIMEBody, error) {
	// Root Node of our tree
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, mparams, err := mime.ParseMediaType(ctype)
	if err != nil {
		mediatype = "attachment"
	}

	m := &MIMEBody{
		header: mailMsg.Header,
		Root:   NewMIMEPart(nil, mediatype),
	}

	p := NewMIMEPart(nil, mediatype)
	p.content, err = decodeSection(mailMsg.Header.Get("Content-Transfer-Encoding"), mailMsg.Body)
	if err != nil {
		return nil, err
	}

	// get set headers
	p.header = make(textproto.MIMEHeader, 4)
	// Figure out our disposition, filename
	disposition, dparams, err := mime.ParseMediaType(mailMsg.Header.Get("Content-Disposition"))
	if err == nil {
		// Disposition is optional
		p.disposition = disposition
		p.fileName = DecodeHeader(dparams["filename"])
	}
	if p.fileName == "" && mparams["name"] != "" {
		p.fileName = DecodeHeader(mparams["name"])
	}
	if p.fileName == "" && mparams["file"] != "" {
		p.fileName = DecodeHeader(mparams["file"])
	}
	if p.charset == "" {
		p.charset = mparams["charset"]
	}

	p.header.Set("Content-Type", mailMsg.Header.Get("Content-Type"))
	p.header.Set("Content-Disposition", mailMsg.Header.Get("Content-Disposition"))

	m.Attachments = append(m.Attachments, p)
	return m, err
}

// ParseMIMEBody parses the body of the message object into a  tree of MIMEPart objects,
// each of which is aware of its content type, filename and headers.  If the part was
// encoded in quoted-printable or base64, it is decoded before being stored in the
// MIMEPart object.
func ParseMIMEBody(mailMsg *mail.Message) (*MIMEBody, error) {
	mimeMsg := &MIMEBody{header: mailMsg.Header}

	if !IsMultipartMessage(mailMsg) {

		// Attachment only?
		if IsBinaryBody(mailMsg) {
			return binMIME(mailMsg)
		}

		// Parse as text only
		bodyBytes, err := decodeSection(mailMsg.Header.Get("Content-Transfer-Encoding"),
			mailMsg.Body)
		if err != nil {
			return nil, fmt.Errorf("Error decoding text-only message: %v", err)
		}
		mimeMsg.Text = string(bodyBytes)

		// Check for HTML at top-level, eat errors quietly
		ctype := mailMsg.Header.Get("Content-Type")
		if ctype != "" {
			if mediatype, mparams, err := mime.ParseMediaType(ctype); err == nil {
				/*
				 *Content-Type: text/plain;\t charset="hz-gb-2312"
				 */
				if mparams["charset"] != "" {
					newStr, err := ConvertToUTF8String(mparams["charset"], bodyBytes)
					if err != nil {
						return nil, err
					}
					mimeMsg.Text = newStr
				} else if mediatype == "text/html" { // charset is empty, look in html body for charset
					charset, err := charsetFromHTMLString(mimeMsg.Text)

					if charset != "" && err == nil {
						newStr, err := ConvertToUTF8String(charset, bodyBytes)
						if err == nil {
							mimeMsg.Text = newStr
						}
					}
				}
				if mediatype == "text/html" {
					mimeMsg.Html = mimeMsg.Text
				}
			}
		}
	} else {
		// Parse top-level multipart
		ctype := mailMsg.Header.Get("Content-Type")
		mediatype, params, err := mime.ParseMediaType(ctype)
		if err != nil {
			return nil, fmt.Errorf("Unable to parse media type: %v", err)
		}
		if !strings.HasPrefix(mediatype, "multipart/") {
			return nil, fmt.Errorf("Unknown mediatype: %v", mediatype)
		}
		boundary := params["boundary"]
		if boundary == "" {
			return nil, fmt.Errorf("Unable to locate boundary param in Content-Type header")
		}
		// Root Node of our tree
		root := NewMIMEPart(nil, mediatype)
		mimeMsg.Root = root
		err = parseParts(root, mailMsg.Body, boundary)
		if err != nil {
			return nil, err
		}

		// Locate text body
		if mediatype == "multipart/altern" {
			match := BreadthMatchFirst(root, func(p MIMEPart) bool {
				return p.ContentType() == "text/plain" && p.Disposition() != "attachment"
			})
			if match != nil {
				if match.Charset() != "" {
					newStr, err := ConvertToUTF8String(match.Charset(), match.Content())
					if err != nil {
						return nil, err
					}
					mimeMsg.Text += newStr
				} else {
					mimeMsg.Text += string(match.Content())
				}
			}
		} else {
			// multipart is of a mixed type
			match := DepthMatchAll(root, func(p MIMEPart) bool {
				return p.ContentType() == "text/plain" && p.Disposition() != "attachment"
			})
			for i, m := range match {
				if i > 0 {
					mimeMsg.Text += "\n--\n"
				}
				if m.Charset() != "" {
					newStr, err := ConvertToUTF8String(m.Charset(), m.Content())
					if err != nil {
						return nil, err
					}
					mimeMsg.Text += newStr
				} else {
					mimeMsg.Text += string(m.Content())
				}
			}
		}

		// Locate HTML body
		match := BreadthMatchFirst(root, func(p MIMEPart) bool {
			return p.ContentType() == "text/html" && p.Disposition() != "attachment"
		})
		if match != nil {
			if match.Charset() != "" {
				newStr, err := ConvertToUTF8String(match.Charset(), match.Content())
				if err != nil {
					return nil, err
				}
				mimeMsg.Html += newStr
			} else {
				mimeMsg.Html = string(match.Content())
			}

		}

		// Locate attachments
		mimeMsg.Attachments = BreadthMatchAll(root, func(p MIMEPart) bool {
			return p.Disposition() == "attachment" || p.ContentType() == "application/octet-stream"
		})

		// Locate inlines
		mimeMsg.Inlines = BreadthMatchAll(root, func(p MIMEPart) bool {
			return p.Disposition() == "inline"
		})

		// Locate others parts not handled in "Attachments" and "inlines"
		mimeMsg.OtherParts = BreadthMatchAll(root, func(p MIMEPart) bool {
			if strings.HasPrefix(p.ContentType(), "multipart/") {
				return false
			}

			if p.Disposition() != "" {
				return false
			}

			if p.ContentType() == "application/octet-stream" {
				return false
			}

			return p.ContentType() != "text/plain" && p.ContentType() != "text/html"
		})
	}

	return mimeMsg, nil
}

// Process the specified header for RFC 2047 encoded words and return the result
func (m *MIMEBody) GetHeader(name string) string {
	return DecodeHeader(m.header.Get(name))
}

// Return AddressList with RFC 2047 encoded encoded names.
func (m *MIMEBody) AddressList(key string) ([]*mail.Address, error) {
	isAddrHeader := false
	for _, hkey := range AddressHeaders {
		if strings.ToLower(hkey) == strings.ToLower(key) {
			isAddrHeader = true
			break
		}
	}
	if !isAddrHeader {
		return nil, fmt.Errorf("%s is not address header", key)
	}

	str := DecodeToUTF8Base64Header(m.header.Get(key))
	if str == "" {
		return nil, mail.ErrHeaderNotPresent
	}
	ret, err := mail.ParseAddressList(str)
	if err != nil {
		return nil, err
	}
	return ret, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	log "github.com/cihub/seelog"
	"github.com/codegangsta/negroni"
	"github.com/facebookgo/inject"
	"goweb-scaffold/config"
	"goweb-scaffold/cron"
	"goweb-scaffold/rest"
)

var taskRunner cron.TaskRunner
var restHandler rest.RestHandler
var globalConfig config.GlobalConfig

func main() {
	// flag parsing
	var port string
	flag.StringVar(&port, "port", "8000", "port")
	flag.Parse()

	// set logger
	seelogConf, _ := ioutil.ReadAll(config.LoadAsset("/config/seelog.xml"))
	logger, _ := log.LoggerFromConfigAsBytes(seelogConf)
	log.ReplaceLogger(logger)

	// config loading
	globalConfig = config.LoadGcloudConfig(
		config.LoadAsset("/config/config.json"))
	log.Debugf("config: %+v", globalConfig)

	buildDependencyGraph()

	// run cron job
	taskRunner.GlobalRun()
	defer cron.GlobalCron.Stop()

	// http server
	n := negroni.Classic()
	router := rest.BuildRouter(restHandler)
	n.Use(negroni.HandlerFunc(cors))
	n.UseHandler(router)
	n.Run(fmt.Sprintf(":%s", port))
}

// buildDependencyGraph builds dependency graph
func buildDependencyGraph() {
	var g inject.Graph
	err := g.Provide(
		&inject.Object{Value: &globalConfig},
		&inject.Object{Value: &taskRunner},
		&inject.Object{Value: &restHandler},
	)
	if err != nil {
		os.Exit(1)
	}
	if err := g.Populate(); err != nil {
		os.Exit(1)
	}
	// :~)
}

// cors middleware (cross-origin resource sharing)
func cors(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	rw.Header().Set("Access-Control-Allow-Origin", "*")
	rw.Header().Set("Access-Control-Allow-Headers", "X-Requested-With, X-CSRF-Token, X-PINGOTHER")

	if req.Method == "OPTIONS" {
		method := req.Header.Get("Access-Control-Request-Method")

		if method == "" {
			http.Error(rw, "Bad Request", http.StatusBadRequest)
			return
		}

		rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
		return
	}

	next(rw, req)
}
<commit_msg>Refactor<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"goweb-scaffold/config"
	"goweb-scaffold/cron"
	"goweb-scaffold/rest"

	log "github.com/cihub/seelog"
	"github.com/codegangsta/negroni"
	"github.com/facebookgo/inject"
)

var taskRunner cron.TaskRunner
var restHandler rest.RestHandler
var globalConfig config.GlobalConfig

func main() {
	// flag parsing
	var port string
	flag.StringVar(&port, "port", "8000", "port")
	flag.Parse()

	// set logger
	seelogConf, _ := ioutil.ReadAll(config.LoadAsset("/config/seelog.xml"))
	logger, _ := log.LoggerFromConfigAsBytes(seelogConf)
	log.ReplaceLogger(logger)

	// config loading
	globalConfig = config.LoadGcloudConfig(
		config.LoadAsset("/config/config.json"))
	log.Debugf("config: %+v", globalConfig)

	buildDependencyGraph()

	// run cron job
	taskRunner.GlobalRun()
	defer cron.GlobalCron.Stop()

	// http server
	n := negroni.Classic()
	router := rest.BuildRouter(restHandler)
	n.Use(negroni.HandlerFunc(cors))
	n.UseHandler(router)
	n.Run(fmt.Sprintf(":%s", port))
}

// buildDependencyGraph builds dependency graph
func buildDependencyGraph() {
	var g inject.Graph
	err := g.Provide(
		&inject.Object{Value: &globalConfig},
		&inject.Object{Value: &taskRunner},
		&inject.Object{Value: &restHandler},
	)
	if err != nil {
		os.Exit(1)
	}
	if err := g.Populate(); err != nil {
		os.Exit(1)
	}
	// :~)
}

// cors middleware (cross-origin resource sharing)
func cors(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	rw.Header().Set("Access-Control-Allow-Origin", "*")
	rw.Header().Set("Access-Control-Allow-Headers", "X-Requested-With, X-CSRF-Token, X-PINGOTHER")

	if req.Method == "OPTIONS" {
		method := req.Header.Get("Access-Control-Request-Method")

		if method == "" {
			http.Error(rw, "Bad Request", http.StatusBadRequest)
			return
		}

		rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
		return
	}

	next(rw, req)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"golang.org/x/sys/windows/registry"

	"github.com/andlabs/ui"
	"github.com/fsnotify/fsnotify"
)

const (
	hostFile = "C:/Windows/System32/drivers/etc/hosts"
)

// https://github.com/spf13/hugo/blob/master/watcher/batcher.go
func main() {
	initGUI()
	initBgProcessor()
}

func makeBasicControls() ui.Control {
	return ui.NewButton("aaa")
}

func makeNumbersAndLists() ui.Control {
	return ui.NewButton("bbb")
}

// go build -ldflags -H=windowsgui ./...
func initGUI() {
	if err := ui.Main(func() {
		box := ui.NewVerticalBox()
		box.SetPadded(true)
		hbox := ui.NewHorizontalBox()
		hbox.SetPadded(true)
		hbox.Append(contructBasicControls(), true)
		hbox.Append(contructNumberAndListControls(), true)
		box.Append(hbox, false)

		window := ui.NewWindow("Lily", 640, 480, true)
		window.SetMargined(true)
		window.SetChild(box)
		window.OnClosing(func(*ui.Window) bool {
			os.Exit(0)
			ui.Quit()
			return false
		})
		window.Show()
	}); err != nil {
		panic(err)
	}
}

func initBgProcessor() {
	openRegistry()
	batcher, err := New(time.Millisecond * 300)
	if err == nil {
		if err = batcher.Add(hostFile); err != nil {
			log.Fatal(err)
		}
		for events := range batcher.Events {
			// fmt.Println("events: ", events)
			for _, event := range events {
				if event.Op&fsnotify.Write == fsnotify.Write {
					log.Println("modified file:", event)
					process()
					break
				}
			}
		}
	}
}

func contructBasicControls() ui.Control {
	group := ui.NewGroup("Basic Controls")
	group.SetMargined(true)
	inner := ui.NewVerticalBox()
	inner.SetPadded(true)
	inner.Append(ui.NewButton("Button"), false)
	inner.Append(ui.NewCheckbox("Checkbox"), false)
	entry := ui.NewEntry()
	entry.SetText("Entry")
	inner.Append(entry, false)
	inner.Append(ui.NewLabel("Label"), false)
	inner.Append(ui.NewHorizontalSeparator(), false)
	inner.Append(ui.NewDatePicker(), false)
	inner.Append(ui.NewTimePicker(), false)
	inner.Append(ui.NewDateTimePicker(), false)
	group.SetChild(inner)
	return group
}

func contructNumberAndListControls() ui.Control {
	outer := ui.NewVerticalBox()
	outer.SetPadded(true)
	numbersGroup := ui.NewGroup("Numbers")
	numbersGroup.SetMargined(true)
	numbersBox := ui.NewVerticalBox()
	numbersBox.SetPadded(true)
	numbersBox.Append(ui.NewSpinbox(0, 100), false)
	numbersBox.Append(ui.NewSlider(0, 100), false)
	// numbersBox.Append(ui.NewProgressBar(), false)
	numbersGroup.SetChild(numbersBox)
	outer.Append(numbersGroup, false)

	listsGroup := ui.NewGroup("Lists")
	listsGroup.SetMargined(true)
	listsBox := ui.NewVerticalBox()
	listsBox.SetPadded(true)
	cbox := ui.NewCombobox()
	cbox.Append("Combobox Item 1")
	cbox.Append("Combobox Item 2")
	cbox.Append("Combobox Item 3")
	listsBox.Append(cbox, false)
	radio := ui.NewRadioButtons()
	radio.Append("Radio button 1")
	radio.Append("Radio button 2")
	radio.Append("Radio button 3")
	listsBox.Append(radio, false)
	tab := ui.NewTab()
	tab.Append("Tab 1", ui.NewHorizontalBox())
	tab.Append("Tab 2", ui.NewHorizontalBox())
	tab.Append("Tab 3", ui.NewHorizontalBox())
	listsBox.Append(tab, true)

	listsGroup.SetChild(listsBox)
	outer.Append(listsGroup, false)
	return outer
}

func openRegistry() {
	k, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.ALL_ACCESS)
	if err != nil {
		log.Fatal(err)
	}
	defer k.Close()
	fmt.Println(k.SetDWordValue("DnsCacheEnabled", 0x1))
	fmt.Println(k.SetDWordValue("DnsCacheTimeout", 0x1))
	fmt.Println(k.SetDWordValue("ServerInfoTimeOut", 0x1))
	if s, _, err := k.GetIntegerValue("DnsCacheEnabled"); err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("DnsCacheEnabled is %q\n", s)
	}

	if s, _, err := k.GetIntegerValue("DnsCacheTimeout"); err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("DnsCacheTimeout is %q\n", s)
	}

	if s, _, err := k.GetIntegerValue("ServerInfoTimeOut"); err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("ServerInfoTimeOut is %q\n", s)
	}
}

func process() {
	// hostConfigMap := readFile()
	browserProcessMap := getBrowserProcessMap()
	table := getTCPTable()
	for i := uint32(0); i < uint32(table.dwNumEntries); i++ {
		row := table.table[i]
		if row.dwOwningPid <= 0 {
			continue
		}
		// remoteAddr := row.displayIP(row.dwRemoteAddr)
		// if _, ok := hostConfigMap[remoteAddr]; ok {
		// fmt.Println("====== remoteAddr= ", remoteAddr, "\tbrowserProcessMap = ", browserProcessMap, "\tpid = ", row.dwOwningPid)
		if processName, ok := browserProcessMap[uint32(row.dwOwningPid)]; ok {
			fmt.Println("Closing pid = ", row.dwOwningPid, "\tprocess= ", processName)
			CloseTCPEntry(row)
		}
		// }
		// fmt.Printf("\t%-6d\t%s:%-16d\t%s:%-16d\t%d\t%d\n", row.dwState, row.displayIP(row.dwLocalAddr), row.displayPort(row.dwLocalPort), row.displayIP(row.dwRemoteAddr), row.displayPort(row.dwRemotePort), row.dwOwningPid, row.dwOffloadState)
	}
}

type Batcher struct {
	*fsnotify.Watcher
	interval time.Duration
	done     chan struct{}

	Events chan []fsnotify.Event // Events are returned on this channel
}

func New(interval time.Duration) (*Batcher, error) {
	watcher, err := fsnotify.NewWatcher()

	batcher := &Batcher{}
	batcher.Watcher = watcher
	batcher.interval = interval
	batcher.done = make(chan struct{}, 1)
	batcher.Events = make(chan []fsnotify.Event, 1)

	if err == nil {
		go batcher.run()
	}

	return batcher, err
}

func (b *Batcher) run() {
	tick := time.Tick(b.interval)
	evs := make([]fsnotify.Event, 0)
OuterLoop:
	for {
		select {
		case ev := <-b.Watcher.Events:
			evs = append(evs, ev)
		case <-tick:
			if len(evs) == 0 {
				continue
			}
			b.Events <- evs
			evs = make([]fsnotify.Event, 0)
		case <-b.done:
			break OuterLoop
		}
	}
	close(b.done)
}

func (b *Batcher) Close() {
	b.done <- struct{}{}
	b.Watcher.Close()
}

func readFile() map[string]string {
	hostConfigMap := make(map[string]string)
	file, err := os.Open(hostFile)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	// fmt.Println("============================== Reading file begin =====================================")
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line != "" && !strings.HasPrefix(line, "#") {
			config := strings.Split(scanner.Text(), " ")
			if len(config) != 2 {
				config = strings.Split(scanner.Text(), "\t")
			}
			if len(config) == 2 {
				// fmt.Println(config[1], "\t", config[0])
				hostConfigMap[config[0]] = config[1]
			}
		}
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	// fmt.Println("============================== Reading file end =====================================")
	return hostConfigMap
}
<commit_msg>Code refactor<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"golang.org/x/sys/windows/registry"

	"github.com/andlabs/ui"
	"github.com/fsnotify/fsnotify"
)

const (
	hostFile = "C:/Windows/System32/drivers/etc/hosts"
)

// https://github.com/spf13/hugo/blob/master/watcher/batcher.go
func main() {
	go initGUI()
	initBgProcessor()
}

// go build -ldflags -H=windowsgui ./...
func initGUI() {
	if err := ui.Main(func() {
		hbox := ui.NewHorizontalBox()
		hbox.SetPadded(true)
		hbox.Append(contructBasicControls(), true)
		hbox.Append(contructNumberAndListControls(), true)

		box := ui.NewVerticalBox()
		box.SetPadded(true)
		box.Append(hbox, false)

		window := ui.NewWindow("Lily", 640, 480, true)
		window.SetMargined(true)
		window.SetChild(box)
		window.OnClosing(func(*ui.Window) bool {
			os.Exit(0)
			ui.Quit()
			return false
		})
		window.Show()
	}); err != nil {
		panic(err)
	}
}

func initBgProcessor() {
	openRegistry()
	batcher, err := New(time.Millisecond * 300)
	if err == nil {
		if err = batcher.Add(hostFile); err != nil {
			log.Fatal(err)
		}
		for events := range batcher.Events {
			// fmt.Println("events: ", events)
			for _, event := range events {
				if event.Op&fsnotify.Write == fsnotify.Write {
					log.Println("modified file:", event)
					process()
					break
				}
			}
		}
	}
}

func contructBasicControls() ui.Control {
	group := ui.NewGroup("Basic Controls")
	group.SetMargined(true)
	inner := ui.NewVerticalBox()
	inner.SetPadded(true)
	inner.Append(ui.NewButton("Button"), false)
	inner.Append(ui.NewCheckbox("Checkbox"), false)
	entry := ui.NewEntry()
	entry.SetText("Entry")
	inner.Append(entry, false)
	inner.Append(ui.NewLabel("Label"), false)
	inner.Append(ui.NewHorizontalSeparator(), false)
	inner.Append(ui.NewDatePicker(), false)
	inner.Append(ui.NewTimePicker(), false)
	inner.Append(ui.NewDateTimePicker(), false)
	group.SetChild(inner)
	return group
}

func contructNumberAndListControls() ui.Control {
	outer := ui.NewVerticalBox()
	outer.SetPadded(true)
	numbersGroup := ui.NewGroup("Numbers")
	numbersGroup.SetMargined(true)
	numbersBox := ui.NewVerticalBox()
	numbersBox.SetPadded(true)
	numbersBox.Append(ui.NewSpinbox(0, 100), false)
	numbersBox.Append(ui.NewSlider(0, 100), false)
	// numbersBox.Append(ui.NewProgressBar(), false)
	numbersGroup.SetChild(numbersBox)
	outer.Append(numbersGroup, false)

	listsGroup := ui.NewGroup("Lists")
	listsGroup.SetMargined(true)
	listsBox := ui.NewVerticalBox()
	listsBox.SetPadded(true)
	cbox := ui.NewCombobox()
	cbox.Append("Combobox Item 1")
	cbox.Append("Combobox Item 2")
	cbox.Append("Combobox Item 3")
	listsBox.Append(cbox, false)
	radio := ui.NewRadioButtons()
	radio.Append("Radio button 1")
	radio.Append("Radio button 2")
	radio.Append("Radio button 3")
	listsBox.Append(radio, false)
	tab := ui.NewTab()
	tab.Append("Tab 1", ui.NewHorizontalBox())
	tab.Append("Tab 2", ui.NewHorizontalBox())
	tab.Append("Tab 3", ui.NewHorizontalBox())
	listsBox.Append(tab, true)

	listsGroup.SetChild(listsBox)
	outer.Append(listsGroup, false)
	return outer
}

func openRegistry() {
	k, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.ALL_ACCESS)
	if err != nil {
		log.Fatal(err)
	}
	defer k.Close()
	fmt.Println(k.SetDWordValue("DnsCacheEnabled", 0x1))
	fmt.Println(k.SetDWordValue("DnsCacheTimeout", 0x1))
	fmt.Println(k.SetDWordValue("ServerInfoTimeOut", 0x1))
	if s, _, err := k.GetIntegerValue("DnsCacheEnabled"); err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("DnsCacheEnabled is %q\n", s)
	}

	if s, _, err := k.GetIntegerValue("DnsCacheTimeout"); err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("DnsCacheTimeout is %q\n", s)
	}

	if s, _, err := k.GetIntegerValue("ServerInfoTimeOut"); err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("ServerInfoTimeOut is %q\n", s)
	}
}

func process() {
	// hostConfigMap := readFile()
	browserProcessMap := getBrowserProcessMap()
	table := getTCPTable()
	for i := uint32(0); i < uint32(table.dwNumEntries); i++ {
		row := table.table[i]
		if row.dwOwningPid <= 0 {
			continue
		}
		// remoteAddr := row.displayIP(row.dwRemoteAddr)
		// if _, ok := hostConfigMap[remoteAddr]; ok {
		// fmt.Println("====== remoteAddr= ", remoteAddr, "\tbrowserProcessMap = ", browserProcessMap, "\tpid = ", row.dwOwningPid)
		if processName, ok := browserProcessMap[uint32(row.dwOwningPid)]; ok {
			fmt.Println("Closing pid = ", row.dwOwningPid, "\tprocess= ", processName)
			CloseTCPEntry(row)
		}
		// }
		// fmt.Printf("\t%-6d\t%s:%-16d\t%s:%-16d\t%d\t%d\n", row.dwState, row.displayIP(row.dwLocalAddr), row.displayPort(row.dwLocalPort), row.displayIP(row.dwRemoteAddr), row.displayPort(row.dwRemotePort), row.dwOwningPid, row.dwOffloadState)
	}
}

type Batcher struct {
	*fsnotify.Watcher
	interval time.Duration
	done     chan struct{}

	Events chan []fsnotify.Event // Events are returned on this channel
}

func New(interval time.Duration) (*Batcher, error) {
	watcher, err := fsnotify.NewWatcher()

	batcher := &Batcher{}
	batcher.Watcher = watcher
	batcher.interval = interval
	batcher.done = make(chan struct{}, 1)
	batcher.Events = make(chan []fsnotify.Event, 1)

	if err == nil {
		go batcher.run()
	}

	return batcher, err
}

func (b *Batcher) run() {
	tick := time.Tick(b.interval)
	evs := make([]fsnotify.Event, 0)
OuterLoop:
	for {
		select {
		case ev := <-b.Watcher.Events:
			evs = append(evs, ev)
		case <-tick:
			if len(evs) == 0 {
				continue
			}
			b.Events <- evs
			evs = make([]fsnotify.Event, 0)
		case <-b.done:
			break OuterLoop
		}
	}
	close(b.done)
}

func (b *Batcher) Close() {
	b.done <- struct{}{}
	b.Watcher.Close()
}

func readFile() map[string]string {
	hostConfigMap := make(map[string]string)
	file, err := os.Open(hostFile)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	// fmt.Println("============================== Reading file begin =====================================")
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line != "" && !strings.HasPrefix(line, "#") {
			config := strings.Split(scanner.Text(), " ")
			if len(config) != 2 {
				config = strings.Split(scanner.Text(), "\t")
			}
			if len(config) == 2 {
				// fmt.Println(config[1], "\t", config[0])
				hostConfigMap[config[0]] = config[1]
			}
		}
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	// fmt.Println("============================== Reading file end =====================================")
	return hostConfigMap
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"

	"./archive/tar"
)

func main() {
	flag.Parse()
	log.SetOutput(os.Stderr)
	for _, arg := range flag.Args() {
		func() {
			// Open the tar archive
			fh, err := os.Open(arg)
			if err != nil {
				log.Fatal(err)
			}
			defer fh.Close()

			output, err := os.Create(fmt.Sprintf("%s.out", arg))
			if err != nil {
				log.Fatal(err)
			}
			defer output.Close()
			log.Printf("writing %q to %q", fh.Name(), output.Name())

			fi, err := fh.Stat()
			if err != nil {
				log.Fatal(err)
			}
			size := fi.Size()
			var sum int64
			tr := tar.NewReader(fh)
			tr.RawAccounting = true
			for {
				hdr, err := tr.Next()
				if err != nil {
					if err != io.EOF {
						log.Println(err)
					}
					break
				}

				pre := tr.RawBytes()
				output.Write(pre)
				sum += int64(len(pre))

				var i int64
				if i, err = io.Copy(output, tr); err != nil {
					log.Println(err)
					break
				}
				sum += i

				post := tr.RawBytes()
				output.Write(post)
				sum += int64(len(post))

				fmt.Println(hdr.Name, "pre:", len(pre), "read:", i, "post:", len(post))
			}

			if size != sum {
				fmt.Printf("Size: %d; Sum: %d; Diff: %d\n", size, sum, size-sum)
				fmt.Printf("Compare like `cmp -bl %s %s | less`\n", fh.Name(), output.Name())
			} else {
				fmt.Printf("Size: %d; Sum: %d\n", size, sum)
			}
		}()
	}
}
<commit_msg>main.go: found the padding after EOF ...<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"

	"./archive/tar"
)

func main() {
	flag.Parse()
	log.SetOutput(os.Stderr)
	for _, arg := range flag.Args() {
		func() {
			// Open the tar archive
			fh, err := os.Open(arg)
			if err != nil {
				log.Fatal(err)
			}
			defer fh.Close()

			output, err := os.Create(fmt.Sprintf("%s.out", arg))
			if err != nil {
				log.Fatal(err)
			}
			defer output.Close()
			log.Printf("writing %q to %q", fh.Name(), output.Name())

			fi, err := fh.Stat()
			if err != nil {
				log.Fatal(err)
			}
			size := fi.Size()
			var sum int64
			tr := tar.NewReader(fh)
			tr.RawAccounting = true
			for {
				hdr, err := tr.Next()
				if err != nil {
					if err != io.EOF {
						log.Println(err)
					}
					// even when an EOF is reached, there is often 1024 null bytes on
					// the end of an archive. Collect them too.
					post := tr.RawBytes()
					output.Write(post)
					sum += int64(len(post))
					break
				}

				pre := tr.RawBytes()
				output.Write(pre)
				sum += int64(len(pre))

				var i int64
				if i, err = io.Copy(output, tr); err != nil {
					log.Println(err)
					break
				}
				sum += i

				post := tr.RawBytes()
				output.Write(post)
				sum += int64(len(post))

				fmt.Println(hdr.Name, "pre:", len(pre), "read:", i, "post:", len(post))
			}

			if size != sum {
				fmt.Printf("Size: %d; Sum: %d; Diff: %d\n", size, sum, size-sum)
				fmt.Printf("Compare like `cmp -bl %s %s | less`\n", fh.Name(), output.Name())
			} else {
				fmt.Printf("Size: %d; Sum: %d\n", size, sum)
			}
		}()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"

	"github.com/zetamatta/nyagos/defined"
	"github.com/zetamatta/nyagos/frame"
	"github.com/zetamatta/nyagos/mains"
)

var version string

func main() {
	var dummy [1]byte
	frame.Version = version

	if err := frame.Start(mains.Main); err != nil && err != io.EOF {
		fmt.Fprintln(os.Stderr, err)
		if defined.DBG {
			os.Stdin.Read(dummy[:])
		}
		os.Exit(1)
	}
	if defined.DBG {
		os.Stdin.Read(dummy[:])
	}
}
<commit_msg>make compact main.main()<commit_after>package main

import (
	"fmt"
	"io"
	"os"

	"github.com/zetamatta/nyagos/defined"
	"github.com/zetamatta/nyagos/frame"
	"github.com/zetamatta/nyagos/mains"
)

var version string

func main() {
	frame.Version = version
	rc := 0
	if err := frame.Start(mains.Main); err != nil && err != io.EOF {
		fmt.Fprintln(os.Stderr, err)
		rc = 1
	}
	if defined.DBG {
		os.Stdin.Read(make([]byte, 1))
	}
	os.Exit(rc)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"path/filepath"

	"gopkg.in/yaml.v2"
)

type Config struct {
	Vault  VaultConfig  `yaml:"vault"`
	Server ServerConfig `yaml:"server"`
}

type VaultConfig struct {
	Url string `yaml:"url"`
}

type ServerConfig struct {
	Port        string `yaml:"port"`
	Secure      bool   `yaml:"secure"`
	Certificate string `yaml:"certificate"`
	PrivateKey  string `yaml:"private_key"`
}

var config Config

func main() {

	var configFileName = flag.String("c", "./config.yaml", "When supplied defines the path to the config yaml file")
	flag.Parse()

	ReadConfiguration(*configFileName)

	http.Handle("/v1/", NewProxy())
	http.Handle("/", http.FileServer(http.Dir("./static")))

	if config.Server.Secure {
		http.ListenAndServeTLS(":"+config.Server.Port, config.Server.Certificate, config.Server.PrivateKey, nil)
	} else {
		http.ListenAndServe(":"+config.Server.Port, nil)
	}
}

func NewProxy() http.Handler {
	director := func(req *http.Request) {
		out, _ := url.Parse(config.Vault.Url)

		req.URL.Scheme = out.Scheme
		req.URL.Host = out.Host
	}
	return &httputil.ReverseProxy{Director: director}
}

func ReadConfiguration(configFileName string) {
	filename, _ := filepath.Abs(configFileName)
	yamlFile, err := ioutil.ReadFile(filename)

	if err != nil {
		panic("You must supply a config.yaml file")
	}

	err = yaml.Unmarshal(yamlFile, &config)
	if err != nil {
		panic("Invalid yaml file")
	}
}
<commit_msg>Read config from environment variables when config file isn't supplied<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"

	"gopkg.in/yaml.v2"
)

type Config struct {
	Vault  VaultConfig  `yaml:"vault"`
	Server ServerConfig `yaml:"server"`
}

type VaultConfig struct {
	Url string `yaml:"url"`
}

type ServerConfig struct {
	Port        string `yaml:"port"`
	Secure      bool   `yaml:"secure"`
	Certificate string `yaml:"certificate"`
	PrivateKey  string `yaml:"private_key"`
}

var config Config

func main() {

	var configFileName = flag.String("c", "./config.yaml", "When supplied defines the path to the config yaml file")
	flag.Parse()

	ReadConfiguration(*configFileName)

	http.Handle("/v1/", NewProxy())
	http.Handle("/", http.FileServer(http.Dir("./static")))

	if config.Server.Secure {
		http.ListenAndServeTLS(":"+config.Server.Port, config.Server.Certificate, config.Server.PrivateKey, nil)
	} else {
		http.ListenAndServe(":"+config.Server.Port, nil)
	}
}

func NewProxy() http.Handler {
	director := func(req *http.Request) {
		out, _ := url.Parse(config.Vault.Url)

		req.URL.Scheme = out.Scheme
		req.URL.Host = out.Host
	}
	return &httputil.ReverseProxy{Director: director}
}

func ReadConfiguration(configFileName string) {
	filename, _ := filepath.Abs(configFileName)
	yamlFile, err := ioutil.ReadFile(filename)

	if err != nil {
		fmt.Println("Could not find configuration. Using environment variables / defaults.")
		// panic("You must supply a config.yaml file")
		secure := false
		if os.Getenv("SERVER_SECURE") == "true" {
			secure = true
		}

		config = Config{
			VaultConfig{
				getOrDefault("VAULT_URL", "http://localhost:8200"),
			},
			ServerConfig{
				getOrDefault("SERVER_PORT", "8080"),
				secure,
				getOrDefault("SERVER_CERTIFACTE_PATH", ""),
				getOrDefault("SERVER_PRIVATE_KEY_PATH", ""),
			},
		}
		fmt.Println(config)
		return
	}

	err = yaml.Unmarshal(yamlFile, &config)
	if err != nil {
		panic("Invalid yaml file")
	}
}

func getOrDefault(envKey string, defaultValue string) string {
	value, present := os.LookupEnv(envKey)
	if present {
		return value
	}
	return defaultValue
}
<|endoftext|>