<commit_before>package nnmc

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/html/charset"
)

// NNMc values:
// client http.Client with cookie
type NNMc struct {
	client http.Client
}

// Topic from forum
type Topic struct {
	Href    string
	Name    string
	Year    string
	Quality string
}

// Film all values
// ID            id
// Name          Название
// EngName       Английское название
// Href          Ссылка
// Year          Год
// Genre         Жанр
// Country       Производство
// Director      Режиссер
// Producer      Продюсер
// Actors        Актеры
// Description   Описание
// Age           Возраст
// ReleaseDate   Дата мировой премьеры
// RussianDate   Дата премьеры в России
// Duration      Продолжительность
// Quality       Качество видео
// Translation   Перевод
// SubtitlesType Вид субтитров
// Subtitles     Субтитры
// Video         Видео
// Audio         Аудио
// Kinopoisk     Рейтинг кинопоиска
// Imdb          Рейтинг IMDb
// NNM           Рейтинг nnm-club
// Sound         Звук
// Size          Размер
// DateCreate    Дата создания раздачи
// Torrent       Ссылка на torrent
// Poster        Ссылка на постер
// UpdatedAt     Дата обновления записи БД
// CreatedAt     Дата создания записи БД
type Film struct {
	ID            int64     `gorm:"column:id"             db:"id"             sql:"AUTO_INCREMENT"`
	Name          string    `gorm:"column:name"           db:"name"           sql:"type:text"`
	EngName       string    `gorm:"column:eng_name"       db:"eng_name"       sql:"type:text"`
	Href          string    `gorm:"column:href"           db:"href"           sql:"type:text"`
	Year          int64     `gorm:"column:year"           db:"year"`
	Genre         string    `gorm:"column:genre"          db:"genre"          sql:"type:text"`
	Country       string    `gorm:"column:country"        db:"country"        sql:"type:text"`
	Director      string    `gorm:"column:director"       db:"director"       sql:"type:text"`
	Producer      string    `gorm:"column:producer"       db:"producer"       sql:"type:text"`
	Actors        string    `gorm:"column:actors"         db:"actors"         sql:"type:text"`
	Description   string    `gorm:"column:description"    db:"description"    sql:"type:text"`
	Age           string    `gorm:"column:age"            db:"age"            sql:"type:text"`
	ReleaseDate   string    `gorm:"column:release_date"   db:"release_date"   sql:"type:text"`
	RussianDate   string    `gorm:"column:russian_date"   db:"russian_date"   sql:"type:text"`
	Duration      int64     `gorm:"column:duration"       db:"duration"`
	Quality       string    `gorm:"column:quality"        db:"quality"        sql:"type:text"`
	Translation   string    `gorm:"column:translation"    db:"translation"    sql:"type:text"`
	SubtitlesType string    `gorm:"column:subtitles_type" db:"subtitles_type" sql:"type:text"`
	Subtitles     string    `gorm:"column:subtitles"      db:"subtitles"      sql:"type:text"`
	Video         string    `gorm:"column:video"          db:"video"          sql:"type:text"`
	Audio         string    `gorm:"column:audio"          db:"audio"          sql:"type:text"`
	Kinopoisk     float64   `gorm:"column:kinopoisk"      db:"kinopoisk"`
	IMDb          float64   `gorm:"column:imdb"           db:"imdb"`
	NNM           float64   `gorm:"column:nnm"            db:"nnm"`
	Sound         string    `gorm:"column:sound"          db:"sound"          sql:"type:text"`
	Size          int64     `gorm:"column:size"           db:"size"`
	DateCreate    string    `gorm:"column:date_create"    db:"date_create"`
	Torrent       string    `gorm:"column:torrent"        db:"torrent"`
	Poster        string    `gorm:"column:poster"         db:"poster"`
	UpdatedAt     time.Time `gorm:"column:updated_at"     db:"updated_at"`
	CreatedAt     time.Time `gorm:"column:created_at"     db:"created_at"`
}

// Init nnmc with login password
func Init(login string, password string) (*NNMc, error) {
	var client http.Client
	cookieJar, _ := cookiejar.New(nil)
	client.Jar = cookieJar
	urlPost := "http://nnm-club.me/forum/login.php"
	form := url.Values{}
	form.Set("username", login)
	form.Add("password", password)
	form.Add("redirect", "")
	form.Add("login", "âõîä")
	req, _ := http.NewRequest("POST", urlPost, bytes.NewBufferString(form.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
	_, err := client.Do(req)
	return &NNMc{client: client}, err
}

// getHTML get body from url
func getHTML(url string, n *NNMc) ([]byte, error) {
	time.Sleep(2000 * time.Millisecond)
	resp, err := n.client.Get(url)
	if err != nil {
		log.Println("client Get error:", err)
		return nil, err
	}
	defer resp.Body.Close()
	utf8body, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		log.Println("Encoding error:", err)
		return nil, err
	}
	doc, err := ioutil.ReadAll(utf8body)
	if err != nil {
		log.Println("ioutil.ReadAll error:", err)
	}
	doc = replaceAll(doc, " ", " ")
	doc = replaceAll(doc, "&", "&")
	return doc, nil
}

// ParseForumTree get topics from forumTree
func (n *NNMc) ParseForumTree(url string) ([]Topic, error) {
	var (
		topics []Topic
		reTree = regexp.MustCompile(`<a href="(viewtopic.php\?t=\d+)"class="topictitle">(.+?)\s\((\d{4})\)\s(.+?)</a>`)
	)
	body, err := getHTML(url, n)
	if err != nil {
		return topics, err
	}
	if reTree.Match(body) == false {
		return topics, fmt.Errorf("No topic in body")
	}
	findResult := reTree.FindAllSubmatch(body, -1)
	for _, v := range findResult {
		var t Topic
		t.Href = "http://nnm-club.me/forum/" + string(v[1])
		t.Name = string(v[2])
		t.Year = string(v[3])
		t.Quality = string(v[4])
		topics = append(topics, t)
	}
	return topics, nil
}

// ParseTopic get film from topic
func (n *NNMc) ParseTopic(topic Topic) (Film, error) {
	var (
		film     Film
		reTopic  = regexp.MustCompile(`(Производство|Жанр|Режиссер|Продюсер|Актеры|Описание фильма|Описание|Возраст|Дата мировой премьеры|Дата премьеры в России|Дата Российской премьеры|Дата российской премьеры|Продолжительность|Качество видео|Качество|Перевод|Вид субтитров|Субтитры|Видео|Аудио):\s*<\/span>(.+?)<`)
		reDate   = regexp.MustCompile(`> (\d{1,2} .{3} \d{4}).{9}<`)
		reSize   = regexp.MustCompile(`Размер блока: \d.+?B"> (\d{1,2},\d{1,2}|\d{3,4}|\d{1,2})\s`)
		reRating = regexp.MustCompile(`>(\d,\d|\d)<\/span>.+?\(Голосов:`)
		reDl     = regexp.MustCompile(`<a href="download\.php\?id=(\d{5,7})" rel="nofollow">Скачать<`)
		reImg    = regexp.MustCompile(`"postImg postImgAligned img-right" title="http:\/\/assets\.nnm-club\.ws\/forum\/image\.php\?link=(.+?jpe{0,1}g)`)
	)
	name := strings.Split(topic.Name, " / ")
	switch len(name) {
	case 1:
		film.Name = strings.Trim(name[0], " ")
	case 2:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	case 3:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	}
	film.Href = topic.Href
	if year64, err := strconv.ParseInt(topic.Year, 10, 64); err == nil {
		film.Year = year64
	}
	body, err := getHTML(film.Href, n)
	if err != nil {
		return film, err
	}
	var reFn = regexp.MustCompile(`(\d{6})`)
	filename := string(reFn.Find([]byte(film.Href)))
	_ = ioutil.WriteFile(filename+".html", body, 0644)
	if reTopic.Match(body) == false {
		return film, fmt.Errorf("No topic in body")
	}
	findAttrs := reTopic.FindAllSubmatch(body, -1)
	for _, v := range findAttrs {
		one := strings.Trim(string(v[1]), " ")
		two := strings.Replace(string(v[2]), "
", "", -1)
		two = strings.Trim(two, " ")
		switch one {
		case "Производство":
			film.Country = two
		case "Жанр":
			film.Genre = strings.ToLower(two)
		case "Режиссер":
			film.Director = two
		case "Продюсер":
			film.Producer = two
		case "Актеры":
			film.Actors = two
		case "Описание фильма", "Описание":
			film.Description = two
		case "Возраст":
			film.Age = two
		case "Дата мировой премьеры":
			film.ReleaseDate = two
		case "Дата премьеры в России", "Дата российской премьеры", "Дата Российской премьеры":
			film.RussianDate = two
		case "Продолжительность":
			if i64, err := strconv.ParseInt(two, 10, 64); err == nil {
				film.Duration = i64
			}
		case "Качество видео", "Качество":
			film.Quality = two
		case "Перевод":
			if caseInsensitiveContains(two, "не требуется") == false {
				film.Translation = two
			} else {
				film.Translation = "Не требуется"
			}
		case "Вид субтитров":
			film.SubtitlesType = two
		case "Субтитры":
			film.Subtitles = two
		case "Видео":
			film.Video = two
		case "Аудио":
			film.Audio = two
		}
	}
	if reDl.Match(body) == false {
		return film, fmt.Errorf("No torrent url in body")
	}
	findDl := reDl.FindAllSubmatch(body, -1)
	film.Torrent = "http://nnm-club.me/forum/download.php?id=" + string(findDl[0][1])
	if reDate.Match(body) == true {
		film.DateCreate = replaceDate(string(reDate.FindSubmatch(body)[1]))
	}
	if reSize.Match(body) == true {
		size := string(reSize.FindSubmatch(body)[1])
		size = strings.Replace(size, ",", ".", -1)
		if s64, err := strconv.ParseFloat(size, 64); err == nil {
			if s64 < 100 {
				s64 = s64 * 1000
			}
			film.Size = int64(s64)
		}
	}
	if reRating.Match(body) == true {
		rating := string(reRating.FindSubmatch(body)[1])
		rating = strings.Replace(rating, ",", ".", -1)
		if r64, err := strconv.ParseFloat(rating, 64); err == nil {
			film.NNM = r64
		}
	}
	if reImg.Match(body) == true {
		film.Poster = string(reImg.FindSubmatch(body)[1])
	}
	return film, nil
}

func replaceAll(body []byte, from string, to string) []byte {
	var reStr = regexp.MustCompile(from)
	result := reStr.ReplaceAll(body, []byte(to))
	return result
}

func replaceDate(s string) string {
	s = strings.Replace(s, " Янв ", ".01.", -1)
	s = strings.Replace(s, " Фев ", ".02.", -1)
	s = strings.Replace(s, " Мар ", ".03.", -1)
	s = strings.Replace(s, " Апр ", ".04.", -1)
	s = strings.Replace(s, " Май ", ".05.", -1)
	s = strings.Replace(s, " Июн ", ".06.", -1)
	s = strings.Replace(s, " Июл ", ".07.", -1)
	s = strings.Replace(s, " Авг ", ".08.", -1)
	s = strings.Replace(s, " Сен ", ".09.", -1)
	s = strings.Replace(s, " Окт ", ".10.", -1)
	s = strings.Replace(s, " Ноя ", ".11.", -1)
	s = strings.Replace(s, " Дек ", ".12.", -1)
	return s
}

func caseInsensitiveContains(s, substr string) bool {
	s, substr = strings.ToUpper(s), strings.ToUpper(substr)
	return strings.Contains(s, substr)
}
<commit_msg>disable debug<commit_after>package nnmc

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/html/charset"
)

// NNMc values:
// client http.Client with cookie
type NNMc struct {
	client http.Client
}

// Topic from forum
type Topic struct {
	Href    string
	Name    string
	Year    string
	Quality string
}

// Film all values
// ID            id
// Name          Название
// EngName       Английское название
// Href          Ссылка
// Year          Год
// Genre         Жанр
// Country       Производство
// Director      Режиссер
// Producer      Продюсер
// Actors        Актеры
// Description   Описание
// Age           Возраст
// ReleaseDate   Дата мировой премьеры
// RussianDate   Дата премьеры в России
// Duration      Продолжительность
// Quality       Качество видео
// Translation   Перевод
// SubtitlesType Вид субтитров
// Subtitles     Субтитры
// Video         Видео
// Audio         Аудио
// Kinopoisk     Рейтинг кинопоиска
// Imdb          Рейтинг IMDb
// NNM           Рейтинг nnm-club
// Sound         Звук
// Size          Размер
// DateCreate    Дата создания раздачи
// Torrent       Ссылка на torrent
// Poster        Ссылка на постер
// UpdatedAt     Дата обновления записи БД
// CreatedAt     Дата создания записи БД
type Film struct {
	ID            int64     `gorm:"column:id"             db:"id"             sql:"AUTO_INCREMENT"`
	Name          string    `gorm:"column:name"           db:"name"           sql:"type:text"`
	EngName       string    `gorm:"column:eng_name"       db:"eng_name"       sql:"type:text"`
	Href          string    `gorm:"column:href"           db:"href"           sql:"type:text"`
	Year          int64     `gorm:"column:year"           db:"year"`
	Genre         string    `gorm:"column:genre"          db:"genre"          sql:"type:text"`
	Country       string    `gorm:"column:country"        db:"country"        sql:"type:text"`
	Director      string    `gorm:"column:director"       db:"director"       sql:"type:text"`
	Producer      string    `gorm:"column:producer"       db:"producer"       sql:"type:text"`
	Actors        string    `gorm:"column:actors"         db:"actors"         sql:"type:text"`
	Description   string    `gorm:"column:description"    db:"description"    sql:"type:text"`
	Age           string    `gorm:"column:age"            db:"age"            sql:"type:text"`
	ReleaseDate   string    `gorm:"column:release_date"   db:"release_date"   sql:"type:text"`
	RussianDate   string    `gorm:"column:russian_date"   db:"russian_date"   sql:"type:text"`
	Duration      int64     `gorm:"column:duration"       db:"duration"`
	Quality       string    `gorm:"column:quality"        db:"quality"        sql:"type:text"`
	Translation   string    `gorm:"column:translation"    db:"translation"    sql:"type:text"`
	SubtitlesType string    `gorm:"column:subtitles_type" db:"subtitles_type" sql:"type:text"`
	Subtitles     string    `gorm:"column:subtitles"      db:"subtitles"      sql:"type:text"`
	Video         string    `gorm:"column:video"          db:"video"          sql:"type:text"`
	Audio         string    `gorm:"column:audio"          db:"audio"          sql:"type:text"`
	Kinopoisk     float64   `gorm:"column:kinopoisk"      db:"kinopoisk"`
	IMDb          float64   `gorm:"column:imdb"           db:"imdb"`
	NNM           float64   `gorm:"column:nnm"            db:"nnm"`
	Sound         string    `gorm:"column:sound"          db:"sound"          sql:"type:text"`
	Size          int64     `gorm:"column:size"           db:"size"`
	DateCreate    string    `gorm:"column:date_create"    db:"date_create"`
	Torrent       string    `gorm:"column:torrent"        db:"torrent"`
	Poster        string    `gorm:"column:poster"         db:"poster"`
	UpdatedAt     time.Time `gorm:"column:updated_at"     db:"updated_at"`
	CreatedAt     time.Time `gorm:"column:created_at"     db:"created_at"`
}

// Init nnmc with login password
func Init(login string, password string) (*NNMc, error) {
	var client http.Client
	cookieJar, _ := cookiejar.New(nil)
	client.Jar = cookieJar
	urlPost := "http://nnm-club.me/forum/login.php"
	form := url.Values{}
	form.Set("username", login)
	form.Add("password", password)
	form.Add("redirect", "")
	form.Add("login", "âõîä")
	req, _ := http.NewRequest("POST", urlPost, bytes.NewBufferString(form.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
	_, err := client.Do(req)
	return &NNMc{client: client}, err
}

// getHTML get body from url
func getHTML(url string, n *NNMc) ([]byte, error) {
	resp, err := n.client.Get(url)
	if err != nil {
		log.Println("client Get error:", err)
		return nil, err
	}
	defer resp.Body.Close()
	utf8body, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		log.Println("Encoding error:", err)
		return nil, err
	}
	doc, err := ioutil.ReadAll(utf8body)
	if err != nil {
		log.Println("ioutil.ReadAll error:", err)
	}
	doc = replaceAll(doc, " ", " ")
	doc = replaceAll(doc, "&", "&")
	return doc, nil
}

// ParseForumTree get topics from forumTree
func (n *NNMc) ParseForumTree(url string) ([]Topic, error) {
	var (
		topics []Topic
		reTree = regexp.MustCompile(`<a href="(viewtopic.php\?t=\d+)"class="topictitle">(.+?)\s\((\d{4})\)\s(.+?)</a>`)
	)
	body, err := getHTML(url, n)
	if err != nil {
		return topics, err
	}
	if reTree.Match(body) == false {
		return topics, fmt.Errorf("No topic in body")
	}
	findResult := reTree.FindAllSubmatch(body, -1)
	for _, v := range findResult {
		var t Topic
		t.Href = "http://nnm-club.me/forum/" + string(v[1])
		t.Name = string(v[2])
		t.Year = string(v[3])
		t.Quality = string(v[4])
		topics = append(topics, t)
	}
	return topics, nil
}

// ParseTopic get film from topic
func (n *NNMc) ParseTopic(topic Topic) (Film, error) {
	var (
		film     Film
		reTopic  = regexp.MustCompile(`(Производство|Жанр|Режиссер|Продюсер|Актеры|Описание фильма|Описание|Возраст|Дата мировой премьеры|Дата премьеры в России|Дата Российской премьеры|Дата российской премьеры|Продолжительность|Качество видео|Качество|Перевод|Вид субтитров|Субтитры|Видео|Аудио):\s*<\/span>(.+?)<`)
		reDate   = regexp.MustCompile(`> (\d{1,2} .{3} \d{4}).{9}<`)
		reSize   = regexp.MustCompile(`Размер блока: \d.+?B"> (\d{1,2},\d{1,2}|\d{3,4}|\d{1,2})\s`)
		reRating = regexp.MustCompile(`>(\d,\d|\d)<\/span>.+?\(Голосов:`)
		reDl     = regexp.MustCompile(`<a href="download\.php\?id=(\d{5,7})" rel="nofollow">Скачать<`)
		reImg    = regexp.MustCompile(`"postImg postImgAligned img-right" title="http:\/\/assets\.nnm-club\.ws\/forum\/image\.php\?link=(.+?jpe{0,1}g)`)
	)
	name := strings.Split(topic.Name, " / ")
	switch len(name) {
	case 1:
		film.Name = strings.Trim(name[0], " ")
	case 2:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	case 3:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	}
	film.Href = topic.Href
	if year64, err := strconv.ParseInt(topic.Year, 10, 64); err == nil {
		film.Year = year64
	}
	body, err := getHTML(film.Href, n)
	if err != nil {
		return film, err
	}
	// var reFn = regexp.MustCompile(`(\d{6})`)
	// filename := string(reFn.Find([]byte(film.Href)))
	// _ = ioutil.WriteFile(filename+".html", body, 0644)
	if reTopic.Match(body) == false {
		return film, fmt.Errorf("No topic in body")
	}
	findAttrs := reTopic.FindAllSubmatch(body, -1)
	for _, v := range findAttrs {
		one := strings.Trim(string(v[1]), " ")
		two := strings.Replace(string(v[2]), "
", "", -1)
		two = strings.Trim(two, " ")
		switch one {
		case "Производство":
			film.Country = two
		case "Жанр":
			film.Genre = strings.ToLower(two)
		case "Режиссер":
			film.Director = two
		case "Продюсер":
			film.Producer = two
		case "Актеры":
			film.Actors = two
		case "Описание фильма", "Описание":
			film.Description = two
		case "Возраст":
			film.Age = two
		case "Дата мировой премьеры":
			film.ReleaseDate = two
		case "Дата премьеры в России", "Дата российской премьеры", "Дата Российской премьеры":
			film.RussianDate = two
		case "Продолжительность":
			if i64, err := strconv.ParseInt(two, 10, 64); err == nil {
				film.Duration = i64
			}
		case "Качество видео", "Качество":
			film.Quality = two
		case "Перевод":
			if caseInsensitiveContains(two, "не требуется") == false {
				film.Translation = two
			} else {
				film.Translation = "Не требуется"
			}
		case "Вид субтитров":
			film.SubtitlesType = two
		case "Субтитры":
			film.Subtitles = two
		case "Видео":
			film.Video = two
		case "Аудио":
			film.Audio = two
		}
	}
	// if reDl.Match(body) == false {
	// 	return film, fmt.Errorf("No torrent url in body")
	// }
	findDl := reDl.FindAllSubmatch(body, -1)
	film.Torrent = "http://nnm-club.me/forum/download.php?id=" + string(findDl[0][1])
	if reDate.Match(body) == true {
		film.DateCreate = replaceDate(string(reDate.FindSubmatch(body)[1]))
	}
	if reSize.Match(body) == true {
		size := string(reSize.FindSubmatch(body)[1])
		size = strings.Replace(size, ",", ".", -1)
		if s64, err := strconv.ParseFloat(size, 64); err == nil {
			if s64 < 100 {
				s64 = s64 * 1000
			}
			film.Size = int64(s64)
		}
	}
	if reRating.Match(body) == true {
		rating := string(reRating.FindSubmatch(body)[1])
		rating = strings.Replace(rating, ",", ".", -1)
		if r64, err := strconv.ParseFloat(rating, 64); err == nil {
			film.NNM = r64
		}
	}
	if reImg.Match(body) == true {
		film.Poster = string(reImg.FindSubmatch(body)[1])
	}
	return film, nil
}

func replaceAll(body []byte, from string, to string) []byte {
	var reStr = regexp.MustCompile(from)
	result := reStr.ReplaceAll(body, []byte(to))
	return result
}

func replaceDate(s string) string {
	s = strings.Replace(s, " Янв ", ".01.", -1)
	s = strings.Replace(s, " Фев ", ".02.", -1)
	s = strings.Replace(s, " Мар ", ".03.", -1)
	s = strings.Replace(s, " Апр ", ".04.", -1)
	s = strings.Replace(s, " Май ", ".05.", -1)
	s = strings.Replace(s, " Июн ", ".06.", -1)
	s = strings.Replace(s, " Июл ", ".07.", -1)
	s = strings.Replace(s, " Авг ", ".08.", -1)
	s = strings.Replace(s, " Сен ", ".09.", -1)
	s = strings.Replace(s, " Окт ", ".10.", -1)
	s = strings.Replace(s, " Ноя ", ".11.", -1)
	s = strings.Replace(s, " Дек ", ".12.", -1)
	return s
}

func caseInsensitiveContains(s, substr string) bool {
	s, substr = strings.ToUpper(s), strings.ToUpper(substr)
	return strings.Contains(s, substr)
}
<|endoftext|>


<commit_before>package openzwave

// #cgo LDFLAGS: -lopenzwave -Lgo/src/github.com/ninjasphere/go-openzwave/openzwave
// #cgo CPPFLAGS: -Iopenzwave/cpp/src/platform -Iopenzwave/cpp/src -Iopenzwave/cpp/src/value_classes
//
// #include "api.h"
import "C"

import (
	"fmt"

	"github.com/ninjasphere/go-openzwave/NT"
)

type state int

const (
	STATE_INIT  state = iota
	STATE_READY       = iota
)

type Node interface {
	GetHomeId() uint32
	GetId() uint8

	GetDevice() Device

	GetProductId() *ProductId
	GetProductDescription() *ProductDescription
	GetNodeName() string

	GetValue(commandClassId uint8, instanceId uint8, index uint8) Value
}

type ProductId struct {
	ManufacturerId string
	ProductId      string
}

type ProductDescription struct {
	ManufacturerName string
	ProductName      string
	ProductType      string
}

type node struct {
	cRef    *C.Node
	classes map[uint8]*valueClass
	state   state
	device  Device
}

type valueClass struct {
	commandClass uint8
	instances    map[uint8]*valueInstance
}

type valueInstance struct {
	instance uint8
	values   map[uint8]*value
}

func newGoNode(cRef *C.Node) *node {
	return &node{cRef, make(map[uint8]*valueClass), STATE_INIT, nil}
}

func (self *node) String() string {
	cRef := self.cRef

	return fmt.Sprintf(
		"Node["+
			"homeId=0x%08x, "+
			"nodeId=%03d, "+
			"basicType=%02x, "+
			"genericType=%02x, "+
			"specificType=%02x, "+
			"nodeType='%s', "+
			"manufacturerName='%s', "+
			"productName='%s', "+
			"location='%s', "+
			"manufacturerId=%s, "+
			"productType=%s, "+
			"productId=%s]",
		uint32(cRef.nodeId.homeId),
		uint8(cRef.nodeId.nodeId),
		uint8(cRef.basicType),
		uint8(cRef.genericType),
		uint8(cRef.specificType),
		C.GoString(cRef.nodeType),
		C.GoString(cRef.manufacturerName),
		C.GoString(cRef.productName),
		C.GoString(cRef.location),
		C.GoString(cRef.manufacturerId),
		C.GoString(cRef.productType),
		C.GoString(cRef.productId))
}

func (self *node) GetHomeId() uint32 {
	return uint32(self.cRef.nodeId.homeId)
}

func (self *node) GetId() uint8 {
	return uint8(self.cRef.nodeId.nodeId)
}

func (self *node) notify(api *api, nt *notification) {

	var event Event

	notificationType := nt.cRef.notificationType
	switch notificationType {
	case NT.NODE_REMOVED:
		event = &NodeUnavailable{nodeEvent{self}}
		if self.device != nil {
			self.device.NodeRemoved()
		}
		api.notifyEvent(event)
		// TODO: free the C structure.
		break

	case NT.VALUE_REMOVED:
		self.removeValue(nt)
		break

	case NT.ESSENTIAL_NODE_QUERIES_COMPLETE,
		NT.NODE_QUERIES_COMPLETE:
		// move the node into the initialized state
		// begin admission processing for the node

		switch self.state {
		case STATE_INIT:
			self.state = STATE_READY

			event = &NodeAvailable{nodeEvent{self}}
			//
			// Use a callback to construct the device for this node, then
			// pass the event to the device.
			//

			self.device = api.deviceFactory(api, self)
			self.device.NodeAdded()

			break
		default:
			event = &NodeChanged{nodeEvent{self}}
			self.device.NodeChanged()
			//
			// Pass the event to the node.
			//
		}
		api.notifyEvent(event)
		break

	case NT.VALUE_ADDED,
		NT.VALUE_CHANGED,
		NT.VALUE_REFRESHED:
		self.takeValue(nt)
		break

	case NT.NODE_NAMING,
		NT.NODE_PROTOCOL_INFO:
		// log the related information for diagnostics purposes

	}
}

// take the value structure from the notification
func (self *node) takeValue(nt *notification) *value {
	commandClassId := (uint8)(nt.value.cRef.valueId.commandClassId)
	instanceId := (uint8)(nt.value.cRef.valueId.instance)
	index := (uint8)(nt.value.cRef.valueId.index)

	instance := self.createOrGetInstance(commandClassId, instanceId)
	v, ok := instance.values[index]
	if !ok {
		v = nt.swapValueImpl(nil)
		instance.values[index] = v

	} else {
		nt.swapValueImpl(v)
	}
	return v
}

func (self *node) createOrGetInstance(commandClassId uint8, instanceId uint8) *valueInstance {
	class, ok := self.classes[commandClassId]
	if !ok {
		class = &valueClass{commandClassId, make(map[uint8]*valueInstance)}
		self.classes[commandClassId] = class
	}
	instance, ok := class.instances[instanceId]
	if !ok {
		instance = &valueInstance{instanceId, make(map[uint8]*value)}
		class.instances[instanceId] = instance
	}
	return instance
}

func (self *node) GetValue(commandClassId uint8, instanceId uint8, index uint8) Value {
	var v *value
	class, ok := self.classes[commandClassId]
	if ok {
		instance, ok := class.instances[instanceId]
		if ok {
			v, ok = instance.values[index]
		}
	}
	if ok {
		return v
	} else {
		return &missingValue{} // accessor that does nothing
	}
	return v
}

func (self *node) removeValue(nt *notification) {
	commandClassId := (uint8)(nt.value.cRef.valueId.commandClassId)
	instanceId := (uint8)(nt.value.cRef.valueId.instance)
	index := (uint8)(nt.value.cRef.valueId.index)

	class, ok := self.classes[commandClassId]
	if !ok {
		return
	}

	instance, ok := class.instances[instanceId]
	if !ok {
		return
	}

	value, ok := instance.values[index]
	_ = value

	if !ok {
		return
	} else {
		// TODO: free the C structure
		delete(instance.values, index)
		if len(instance.values) == 0 {
			delete(class.instances, instanceId)
			if len(class.instances) == 0 {
				delete(self.classes, commandClassId)
			}
		}
	}

}

func (self *node) GetDevice() Device {
	return self.device
}

func (self *node) GetProductId() *ProductId {
	return &ProductId{C.GoString(self.cRef.manufacturerId), C.GoString(self.cRef.productId)}
}

func (self *node) GetProductDescription() *ProductDescription {
	return &ProductDescription{
		C.GoString(self.cRef.manufacturerName),
		C.GoString(self.cRef.productName),
		C.GoString(self.cRef.productType)}
}

func (self *node) GetNodeName() string {
	return C.GoString(self.cRef.nodeName)
}

func (self *node) free() {
	C.freeNode(self.cRef)
}
<commit_msg>Propagate value changed/added/refreshed events to the device.<commit_after>package openzwave

// #cgo LDFLAGS: -lopenzwave -Lgo/src/github.com/ninjasphere/go-openzwave/openzwave
// #cgo CPPFLAGS: -Iopenzwave/cpp/src/platform -Iopenzwave/cpp/src -Iopenzwave/cpp/src/value_classes
//
// #include "api.h"
import "C"

import (
	"fmt"

	"github.com/ninjasphere/go-openzwave/NT"
)

type state int

const (
	STATE_INIT  state = iota
	STATE_READY       = iota
)

type Node interface {
	GetHomeId() uint32
	GetId() uint8

	GetDevice() Device

	GetProductId() *ProductId
	GetProductDescription() *ProductDescription
	GetNodeName() string

	GetValue(commandClassId uint8, instanceId uint8, index uint8) Value
}

type ProductId struct {
	ManufacturerId string
	ProductId      string
}

type ProductDescription struct {
	ManufacturerName string
	ProductName      string
	ProductType      string
}

type node struct {
	cRef    *C.Node
	classes map[uint8]*valueClass
	state   state
	device  Device
}

type valueClass struct {
	commandClass uint8
	instances    map[uint8]*valueInstance
}

type valueInstance struct {
	instance uint8
	values   map[uint8]*value
}

func newGoNode(cRef *C.Node) *node {
	return &node{cRef, make(map[uint8]*valueClass), STATE_INIT, nil}
}

func (self *node) String() string {
	cRef := self.cRef

	return fmt.Sprintf(
		"Node["+
			"homeId=0x%08x, "+
			"nodeId=%03d, "+
			"basicType=%02x, "+
			"genericType=%02x, "+
			"specificType=%02x, "+
			"nodeType='%s', "+
			"manufacturerName='%s', "+
			"productName='%s', "+
			"location='%s', "+
			"manufacturerId=%s, "+
			"productType=%s, "+
			"productId=%s]",
		uint32(cRef.nodeId.homeId),
		uint8(cRef.nodeId.nodeId),
		uint8(cRef.basicType),
		uint8(cRef.genericType),
		uint8(cRef.specificType),
		C.GoString(cRef.nodeType),
		C.GoString(cRef.manufacturerName),
		C.GoString(cRef.productName),
		C.GoString(cRef.location),
		C.GoString(cRef.manufacturerId),
		C.GoString(cRef.productType),
		C.GoString(cRef.productId))
}

func (self *node) GetHomeId() uint32 {
	return uint32(self.cRef.nodeId.homeId)
}

func (self *node) GetId() uint8 {
	return uint8(self.cRef.nodeId.nodeId)
}

func (self *node) notify(api *api, nt *notification) {

	var event Event

	notificationType := nt.cRef.notificationType
	switch notificationType {
	case NT.NODE_REMOVED:
		event = &NodeUnavailable{nodeEvent{self}}
		if self.device != nil {
			self.device.NodeRemoved()
		}
		api.notifyEvent(event)
		// TODO: free the C structure.
		break

	case NT.VALUE_REMOVED:
		self.removeValue(nt)
		break

	case NT.ESSENTIAL_NODE_QUERIES_COMPLETE,
		NT.NODE_QUERIES_COMPLETE:
		// move the node into the initialized state
		// begin admission processing for the node

		switch self.state {
		case STATE_INIT:
			self.state = STATE_READY

			event = &NodeAvailable{nodeEvent{self}}
			//
			// Use a callback to construct the device for this node, then
			// pass the event to the device.
			//

			self.device = api.deviceFactory(api, self)
			self.device.NodeAdded()

			break
		default:
			event = &NodeChanged{nodeEvent{self}}
			self.device.NodeChanged()
			//
			// Pass the event to the node.
			//
		}
		api.notifyEvent(event)
		break

	case NT.VALUE_ADDED,
		NT.VALUE_CHANGED,
		NT.VALUE_REFRESHED:
		v := self.takeValue(nt)
		if self.device != nil {
			self.device.ValueChanged(v)
		}
		break

	case NT.NODE_NAMING,
		NT.NODE_PROTOCOL_INFO:
		// log the related information for diagnostics purposes

	}
}

// take the value structure from the notification
func (self *node) takeValue(nt *notification) *value {
	commandClassId := (uint8)(nt.value.cRef.valueId.commandClassId)
	instanceId := (uint8)(nt.value.cRef.valueId.instance)
	index := (uint8)(nt.value.cRef.valueId.index)

	instance := self.createOrGetInstance(commandClassId, instanceId)
	v, ok := instance.values[index]
	if !ok {
		v = nt.swapValueImpl(nil)
		instance.values[index] = v

	} else {
		nt.swapValueImpl(v)
	}
	return v
}

func (self *node) createOrGetInstance(commandClassId uint8, instanceId uint8) *valueInstance {
	class, ok := self.classes[commandClassId]
	if !ok {
		class = &valueClass{commandClassId, make(map[uint8]*valueInstance)}
		self.classes[commandClassId] = class
	}
	instance, ok := class.instances[instanceId]
	if !ok {
		instance = &valueInstance{instanceId, make(map[uint8]*value)}
		class.instances[instanceId] = instance
	}
	return instance
}

func (self *node) GetValue(commandClassId uint8, instanceId uint8, index uint8) Value {
	var v *value
	class, ok := self.classes[commandClassId]
	if ok {
		instance, ok := class.instances[instanceId]
		if ok {
			v, ok = instance.values[index]
		}
	}
	if ok {
		return v
	} else {
		return &missingValue{} // accessor that does nothing
	}
	return v
}

func (self *node) removeValue(nt *notification) {
	commandClassId := (uint8)(nt.value.cRef.valueId.commandClassId)
	instanceId := (uint8)(nt.value.cRef.valueId.instance)
	index := (uint8)(nt.value.cRef.valueId.index)

	class, ok := self.classes[commandClassId]
	if !ok {
		return
	}

	instance, ok := class.instances[instanceId]
	if !ok {
		return
	}

	value, ok := instance.values[index]
	_ = value

	if !ok {
		return
	} else {
		// TODO: free the C structure
		delete(instance.values, index)
		if len(instance.values) == 0 {
			delete(class.instances, instanceId)
			if len(class.instances) == 0 {
				delete(self.classes, commandClassId)
			}
		}
	}

}

func (self *node) GetDevice() Device {
	return self.device
}

func (self *node) GetProductId() *ProductId {
	return &ProductId{C.GoString(self.cRef.manufacturerId), C.GoString(self.cRef.productId)}
}

func (self *node) GetProductDescription() *ProductDescription {
	return &ProductDescription{
		C.GoString(self.cRef.manufacturerName),
		C.GoString(self.cRef.productName),
		C.GoString(self.cRef.productType)}
}

func (self *node) GetNodeName() string {
	return C.GoString(self.cRef.nodeName)
}

func (self *node) free() {
	C.freeNode(self.cRef)
}
<|endoftext|>


<commit_before>package god

import (
	log "github.com/Sirupsen/logrus"
	"github.com/golang/protobuf/proto"
	"github.com/streadway/amqp"
	"golang.org/x/net/context"
)

const (
	adminService = "god.Admin"
)

type node struct {
	*amqp.Connection
	*Session

	kind uint16
	id   uint64
}

var _ AdminServer = (*node)(nil)

var self node

func Start(url string, nodeType uint16, nodeID uint64) error {
	c, err := amqp.Dial(url)
	if err != nil {
		return err
	}

	self.Connection = c
	s, err := NewSession()
	if err != nil {
		s.Close()
		return err
	}

	q, err := s.Subscribe(adminService, nodeType, nodeID)
	if err != nil {
		s.Close()
		return err
	}

	self.Session = s
	self.kind = nodeType
	self.id = nodeID

	var req AuthReq
	req.ID = nodeID
	postAdmin("Auth", &req)

	self.register(&_Admin_serviceDesc, &self)
	go self.Handle(q, nil)
	return nil
}

func Close() {
	self.Close()
}

func postAdmin(method string, msg proto.Message) error {
	return self.Post(adminService,
		self.kind, self.id,
		adminService, method, msg)
}

func (n *node) Auth(c context.Context, req *AuthReq) (*AuthAck, error) {
	log.Infof("%#v", req)
	return &AuthAck{Code: ErrorCode_OK}, nil
}
<commit_msg>update<commit_after>package god

import (
	log "github.com/Sirupsen/logrus"
	"github.com/golang/protobuf/proto"
	"github.com/nsf/termbox-go"
	"github.com/streadway/amqp"
	"golang.org/x/net/context"
)

const (
	adminService = "god.Admin"
)

type node struct {
	*amqp.Connection
	*Session

	kind uint16
	id   uint64
}

var _ AdminServer = (*node)(nil)

var self node

func Start(url string, nodeType uint16, nodeID uint64) error {
	c, err := amqp.Dial(url)
	if err != nil {
		return err
	}

	self.Connection = c
	s, err := NewSession()
	if err != nil {
		s.Close()
		return err
	}

	q, err := s.Subscribe(adminService, nodeType, nodeID)
	if err != nil {
		s.Close()
		return err
	}

	self.Session = s
	self.kind = nodeType
	self.id = nodeID

	var req AuthReq
	req.ID = nodeID
	postAdmin("Auth", &req)

	self.register(&_Admin_serviceDesc, &self)
	go self.Handle(q, nil)
	return nil
}

func RunConsole() error {
	if err := termbox.Init(); err != nil {
		return err
	}

	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			termbox.Flush()
		}
	}
}

func Close() {
	self.Close()
}

func postAdmin(method string, msg proto.Message) error {
	return self.Post(adminService,
		self.kind, self.id,
		adminService, method, msg)
}

func (n *node) Auth(c context.Context, req *AuthReq) (*AuthAck, error) {
	log.Infof("%#v", req)
	return &AuthAck{Code: ErrorCode_OK}, nil
}
<|endoftext|>


<commit_before>package jogs

import (
	"fmt"
	"reflect"
	"strings"
)

/*
	Node is a structure containing all necessary info for building an editor node.
*/
type Node struct {
	Object      interface{} // the actual data object that we want to edit
	ContainerId string      // the id of the dom node in which we want this editor placed, a.k.a. parent
	EditorId    string      // the id of the dom node of this editor
	Label       string      // the name of this editor
	Handle      string      // a key to the handler that will build this node's editor
	Idx 		int 		// the index of the node in the struct field list or slice
	Options     []string    // (optional) arguments for the handler
}

//////////////////////////////////////////////////////////////////////////////////////
// private parts
//////////////////////////////////////////////////////////////////////////////////////

/*
	ptr is a handler for nodes of type pointer
*/
type ptr struct {
	*Dispatcher
}

func (n *ptr) handle(node Node, cb Callback) {

	valueofkind := reflect.ValueOf(node.Object).Elem().Kind()

	switch valueofkind {
	case reflect.Struct:
		node.Handle = "PTR_STRUCT"
	default:
		node.Handle = "UNSUPPORTED"
		node.Object = fmt.Sprint("Unsupported ptr to <T> (must be ptr to struct)  :", valueofkind, "->", node.Object)
	}

	n.dispatch(node, cb)
}

/*
	ptr_struct is a handler for nodes of type pointer to struct
*/
type ptr_struct struct {
	*Dispatcher
}

func (n *ptr_struct) handle(node Node, cb Callback) {

	e := reflect.ValueOf(node.Object).Elem()

	for i := 0; i < e.NumField(); i++ {
		field_value := e.Field(i)
		field_name := e.Type().Field(i).Name
		//fmt.Println("struct field", i, field_name, ":", field_value.Kind(), "->", field_value.Interface())
		if !field_value.CanSet() {
			fmt.Println("field is not settable")
			continue
		}

		node_row := node
		node_row.Idx = i

		tag := e.Type().Field(i).Tag.Get("jogs")
		if tag != "" {
			//fmt.Println("tag detected: ", tag)

			fields := strings.Fields(tag)

			node_row.EditorId += "-" + field_name
			node_row.Label = field_name
			node_row.Handle = fields[0]
			node_row.Options = fields[1:]
			node_row.Object = field_value.Interface()
			n.dispatch(node_row, func(out interface{}) {
				field_value.Set(reflect.ValueOf(out))
				cb(node.Object)
			})
			continue
		}

		switch e.Field(i).Type().Kind() {
		case reflect.Struct:
			node_nested := n.nest(node_row, field_name)
			node_nested.Handle = "PTR"
			node_nested.Object = field_value.Addr().Interface()
			n.dispatch(node_nested, func(out interface{}) {
				cb(node.Object)
			})
		case reflect.Ptr:
			node_nested := n.nest(node_row, field_name)
			node_nested.Object = field_value.Interface()
			node_nested.Handle = "PTR"
			n.dispatch(node_nested, func(out interface{}) {
				cb(node.Object)
			})
		case reflect.Slice:
			node_nested := n.nest(node_row, field_name)
			node_nested.Object = field_value.Interface()
			node_nested.Handle = "SLICE"
			n.dispatch(node_nested, func(out interface{}) {
				field_value.Set(reflect.ValueOf(out))
				cb(node.Object)
			})
		default:
			node_row.EditorId += "-" + field_name
			node_row.Label = field_name
			node_row.Object = field_value.Interface()
			node_row.Handle = "LEAF"
			n.dispatch(node_row, func(out interface{}) {
				field_value.Set(reflect.ValueOf(out))
				cb(node.Object)
			})
		}
	}
}

func (n *ptr_struct) nest(node Node, field_name string) Node {
	child := node
	child.EditorId += "-" + field_name
	child.Label = field_name
	jQuery("#" + child.ContainerId).Append("

" + child.Label + "

")
	child.ContainerId = child.EditorId
	child.EditorId += "-content"
	child.ContainerId += "-content"
	return child
}
<commit_msg>Nest now uses templates<commit_after>package jogs

import (
	"fmt"
	"reflect"
	"strings"
	"html/template"
)

/*
	Node is a structure containing all necessary info for building an editor node.
*/
type Node struct {
	Object      interface{} // the actual data object that we want to edit
	ContainerId string      // the id of the dom node in which we want this editor placed, a.k.a. parent
	EditorId    string      // the id of the dom node of this editor
	Label       string      // the name of this editor
	Handle      string      // a key to the handler that will build this node's editor
	Idx 		int 		// the index of the node in the struct field list or slice
	Options     []string    // (optional) arguments for the handler
}

//////////////////////////////////////////////////////////////////////////////////////
// private parts
//////////////////////////////////////////////////////////////////////////////////////

/*
	ptr is a handler for nodes of type pointer
*/
type ptr struct {
	*Dispatcher
}

func (n *ptr) handle(node Node, cb Callback) {

	valueofkind := reflect.ValueOf(node.Object).Elem().Kind()

	switch valueofkind {
	case reflect.Struct:
		node.Handle = "PTR_STRUCT"
	default:
		node.Handle = "UNSUPPORTED"
		node.Object = fmt.Sprint("Unsupported ptr to <T> (must be ptr to struct)  :", valueofkind, "->", node.Object)
	}

	n.dispatch(node, cb)
}

/*
	ptr_struct is a handler for nodes of type pointer to struct
*/
type ptr_struct struct {
	*Dispatcher
}

func (n *ptr_struct) handle(node Node, cb Callback) {

	e := reflect.ValueOf(node.Object).Elem()

	for i := 0; i < e.NumField(); i++ {
		field_value := e.Field(i)
		field_name := e.Type().Field(i).Name
		//fmt.Println("struct field", i, field_name, ":", field_value.Kind(), "->", field_value.Interface())
		if !field_value.CanSet() {
			fmt.Println("field is not settable")
			continue
		}

		node_row := node
		node_row.Idx = i

		tag := e.Type().Field(i).Tag.Get("jogs")
		if tag != "" {
			//fmt.Println("tag detected: ", tag)

			fields := strings.Fields(tag)

			node_row.EditorId += "-" + field_name
			node_row.Label = field_name
			node_row.Handle = fields[0]
			node_row.Options = fields[1:]
			node_row.Object = field_value.Interface()
			n.dispatch(node_row, func(out interface{}) {
				field_value.Set(reflect.ValueOf(out))
				cb(node.Object)
			})
			continue
		}

		switch e.Field(i).Type().Kind() {
		case reflect.Struct:
			node_nested := n.nest(node_row, field_name)
			node_nested.Handle = "PTR"
			node_nested.Object = field_value.Addr().Interface()
			n.dispatch(node_nested, func(out interface{}) {
				cb(node.Object)
			})
		case reflect.Ptr:
			node_nested := n.nest(node_row, field_name)
			node_nested.Object = field_value.Interface()
			node_nested.Handle = "PTR"
			n.dispatch(node_nested, func(out interface{}) {
				cb(node.Object)
			})
		case reflect.Slice:
			node_nested := n.nest(node_row, field_name)
			node_nested.Object = field_value.Interface()
			node_nested.Handle = "SLICE"
			n.dispatch(node_nested, func(out interface{}) {
				field_value.Set(reflect.ValueOf(out))
				cb(node.Object)
			})
		default:
			node_row.EditorId += "-" + field_name
			node_row.Label = field_name
			node_row.Object = field_value.Interface()
			node_row.Handle = "LEAF"
			n.dispatch(node_row, func(out interface{}) {
				field_value.Set(reflect.ValueOf(out))
				cb(node.Object)
			})
		}
	}
}

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

var nest_tpl = template.Must(template.New("skin").Parse(string(`
	{{define "nest"}}

				{{.Label}}

	{{end}}
`)))

func (n *ptr_struct) nest(node Node, field_name string) Node {
	child := node
	child.EditorId += "-" + field_name
	child.Label = field_name
	jQuery("#" + child.ContainerId).Append(merge(nest_tpl, "nest", child))
	child.ContainerId = child.EditorId
	child.EditorId += "-content"
	child.ContainerId += "-content"
	return child
}
<|endoftext|>


<commit_before>package elasticsearch

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"
)

// A Node is a structure which represents a single ElasticSearch host.
type Node struct {
	sync.RWMutex
	endpoint     string
	health       Health
	searchClient *http.Client // used for Search() only
	pingClient   *http.Client // used for Ping() only
}

// NewNode constructs a Node handle. The endpoint should be of the form
// "scheme://host:port", eg. "http://es001:9200".
//
// The ping interval is dictated at a higher level (the Cluster), but individual
// ping timeouts are stored with the Nodes themselves, in a custom HTTP client,
// with a timeout as part of the Transport dialer. This custom pingClient is
// used exclusively for Ping() calls.
//
// Regular queries are made with the default searchClient http.Client, which has
// no explicit timeout set in the Transport dialer.
func NewNode(endpoint string, pingTimeout time.Duration) *Node {
	return &Node{
		endpoint:     endpoint,
		health:       Yellow,
		searchClient: &http.Client{},
		pingClient: &http.Client{
			Transport: &http.Transport{
				Dial: timeoutDialer(pingTimeout),
			},
		},
	}
}

// Ping attempts to HTTP GET a specific endpoint, parse some kind of
// status indicator, and returns true if everything was successful.
func (n *Node) Ping() bool {
	u, err := url.Parse(n.endpoint)
	if err != nil {
		log.Printf("ElasticSearch: ping: resolve: %s", err)
		return false
	}
	u.Path = "/_cluster/nodes/_local" // some arbitrary, reasonable endpoint

	resp, err := n.pingClient.Get(u.String())
	if err != nil {
		log.Printf("ElasticSearch: ping %s: GET: %s", u.Host, err)
		return false
	}
	defer resp.Body.Close()

	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("ElasticSearch: ping %s: %s", u.Host, err)
		return false
	}

	var status struct {
		OK bool `json:"ok"`
	}
	if err = json.Unmarshal(buf, &status); err != nil {
		log.Printf("ElasticSearch: ping %s: %s", u.Host, err)
		return false
	}

	if !status.OK {
		log.Printf("ElasticSearch: ping %s: ok=false", u.Host)
		return false
	}

	return true
}

// PingAndSet performs a Ping, and updates the Node's health accordingly.
func (n *Node) pingAndSet() {
	success := n.Ping()
	func() {
		n.Lock()
		defer n.Unlock()
		if success {
			n.health = n.health.Improve()
		} else {
			n.health = n.health.Degrade()
		}
	}()
}

// GetHealth returns the health of the node, for use in the Cluster's GetBest.
func (n *Node) GetHealth() Health {
	n.RLock()
	defer n.RUnlock()
	return n.health
}

// searchCommon performs a HTTP GET against the node+path, using the passed
// body. It returns the raw bytes of the response, and leaves it to the caller
// to marshal those bytes into the relevant response structure.
func (n *Node) searchCommon(f Fireable) ([]byte, error) {
	u, err := url.Parse(n.endpoint)
	if err != nil {
		return []byte{}, err
	}
	u.Path = f.Path()
	u.RawQuery = f.Values().Encode()

	body, err := f.Body()
	if err != nil {
		return []byte{}, err
	}

	req, err := http.NewRequest("GET", u.String(), bytes.NewBuffer(body))
	if err != nil {
		return []byte{}, err
	}

	// We don't implement an explicit timeout here. The idea is we're completely
	// transparent: if ES has a timeout, we will report it back up the stack; if
	// ES fails, ie. blocks us, we rely on the (assumed) timeouts in the stack
	// above us.
	resp, err := n.searchClient.Do(req)
	if err != nil {
		return []byte{}, err
	}
	defer resp.Body.Close()

	responseBuf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, err
	}

	return responseBuf, nil
}

// Search implements the Searcher interface for a Node.
func (n *Node) Search(r SearchRequest) (SearchResponse, error) {
	responseBuf, err := n.searchCommon(r)
	if err != nil {
		return SearchResponse{}, err
	}

	var esResponse SearchResponse
	if err = json.Unmarshal(responseBuf, &esResponse); err != nil {
		return SearchResponse{}, err
	}

	return esResponse, nil
}

// MultiSearch implements the MultiSearcher interface for a Node.
func (n *Node) MultiSearch(r MultiSearchRequest) (MultiSearchResponse, error) {
	responseBuf, err := n.searchCommon(r)
	if err != nil {
		return MultiSearchResponse{}, err
	}

	var esResponse MultiSearchResponse
	if err = json.Unmarshal(responseBuf, &esResponse); err != nil {
		return MultiSearchResponse{}, err
	}

	return esResponse, nil
}

//
//
//

type Nodes []*Node

// PingAll triggers simultaneous PingAndSets across all Nodes,
// and blocks until they've all completed.
func (n Nodes) pingAll() {
	c := make(chan bool, len(n))
	for _, node := range n {
		go func(tgt *Node) { tgt.pingAndSet(); c <- true }(node)
	}
	for i := 0; i < cap(c); i++ {
		<-c
	}
}

// GetBest returns the "best" Node, as decided by each Node's health.
// It's possible that no Node will be healthy enough to be returned.
// In that case, GetBest returns an error, and processing cannot continue.
func (n Nodes) getBest() (*Node, error) {
	green, yellow := []*Node{}, []*Node{}
	for _, node := range n {
		switch node.GetHealth() {
		case Green:
			green = append(green, node)
		case Yellow:
			yellow = append(yellow, node)
		}
	}

	if len(green) > 0 {
		return green[rand.Intn(len(green))], nil
	}

	if len(yellow) > 0 {
		return yellow[rand.Intn(len(yellow))], nil
	}

	return nil, fmt.Errorf("no healthy nodes available")
}

//
//
//

// Health is some encoding of the perceived state of a Node.
// A Cluster should favor sending queries against healthier nodes.
type Health int

const (
	Green Health = iota // resemblance to cluster health codes is coincidental
	Yellow
	Red
)

func (h Health) String() string {
	switch h {
	case Green:
		return "Green"
	case Yellow:
		return "Yellow"
	case Red:
		return "Red"
	}
	panic("unreachable")
}

func (h Health) Improve() Health {
	switch h {
	case Red:
		return Yellow
	default:
		return Green
	}
	panic("unreachable")
}

func (h Health) Degrade() Health {
	switch h {
	case Green:
		return Yellow
	default:
		return Red
	}
	panic("unreachable")
}

//
//
//

// timeoutDialer returns a function that can be put into an HTTP Client's
// Transport, which will cause all requests made on that client to abort
// if they're not handled within the passed duration.
func timeoutDialer(d time.Duration) func(net, addr string) (net.Conn, error) {
	return func(netw, addr string) (net.Conn, error) {
		c, err := net.Dial(netw, addr)
		if err != nil {
			return nil, err
		}
		c.SetDeadline(time.Now().Add(d))
		return c, nil
	}
}
<commit_msg>Set MaxIdleConnsPerHost for Node.searchClient<commit_after>package elasticsearch

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"
)

// A Node is a structure which represents a single ElasticSearch host.
type Node struct {
	sync.RWMutex
	endpoint     string
	health       Health
	searchClient *http.Client // used for Search() only
	pingClient   *http.Client // used for Ping() only
}

// NewNode constructs a Node handle. The endpoint should be of the form
// "scheme://host:port", eg. "http://es001:9200".
//
// The ping interval is dictated at a higher level (the Cluster), but individual
// ping timeouts are stored with the Nodes themselves, in a custom HTTP client,
// with a timeout as part of the Transport dialer. This custom pingClient is
// used exclusively for Ping() calls.
//
// Regular queries are made with the default searchClient http.Client, which has
// no explicit timeout set in the Transport dialer.
func NewNode(endpoint string, pingTimeout time.Duration) *Node {
	return &Node{
		endpoint: endpoint,
		health:   Yellow,
		searchClient: &http.Client{
			Transport: &http.Transport{
				MaxIdleConnsPerHost: 250,
			},
		},
		pingClient: &http.Client{
			Transport: &http.Transport{
				Dial: timeoutDialer(pingTimeout),
			},
		},
	}
}

// Ping attempts to HTTP GET a specific endpoint, parse some kind of
// status indicator, and returns true if everything was successful.
func (n *Node) Ping() bool {
	u, err := url.Parse(n.endpoint)
	if err != nil {
		log.Printf("ElasticSearch: ping: resolve: %s", err)
		return false
	}
	u.Path = "/_cluster/nodes/_local" // some arbitrary, reasonable endpoint

	resp, err := n.pingClient.Get(u.String())
	if err != nil {
		log.Printf("ElasticSearch: ping %s: GET: %s", u.Host, err)
		return false
	}
	defer resp.Body.Close()

	buf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("ElasticSearch: ping %s: %s", u.Host, err)
		return false
	}

	var status struct {
		OK bool `json:"ok"`
	}
	if err = json.Unmarshal(buf, &status); err != nil {
		log.Printf("ElasticSearch: ping %s: %s", u.Host, err)
		return false
	}

	if !status.OK {
		log.Printf("ElasticSearch: ping %s: ok=false", u.Host)
		return false
	}

	return true
}

// PingAndSet performs a Ping, and updates the Node's health accordingly.
func (n *Node) pingAndSet() {
	success := n.Ping()
	func() {
		n.Lock()
		defer n.Unlock()
		if success {
			n.health = n.health.Improve()
		} else {
			n.health = n.health.Degrade()
		}
	}()
}

// GetHealth returns the health of the node, for use in the Cluster's GetBest.
func (n *Node) GetHealth() Health {
	n.RLock()
	defer n.RUnlock()
	return n.health
}

// searchCommon performs a HTTP GET against the node+path, using the passed
// body. It returns the raw bytes of the response, and leaves it to the caller
// to marshal those bytes into the relevant response structure.
func (n *Node) searchCommon(f Fireable) ([]byte, error) {
	u, err := url.Parse(n.endpoint)
	if err != nil {
		return []byte{}, err
	}
	u.Path = f.Path()
	u.RawQuery = f.Values().Encode()

	body, err := f.Body()
	if err != nil {
		return []byte{}, err
	}

	req, err := http.NewRequest("GET", u.String(), bytes.NewBuffer(body))
	if err != nil {
		return []byte{}, err
	}

	// We don't implement an explicit timeout here. The idea is we're completely
	// transparent: if ES has a timeout, we will report it back up the stack; if
	// ES fails, ie. blocks us, we rely on the (assumed) timeouts in the stack
	// above us.
	resp, err := n.searchClient.Do(req)
	if err != nil {
		return []byte{}, err
	}
	defer resp.Body.Close()

	responseBuf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return []byte{}, err
	}

	return responseBuf, nil
}

// Search implements the Searcher interface for a Node.
func (n *Node) Search(r SearchRequest) (SearchResponse, error) {
	responseBuf, err := n.searchCommon(r)
	if err != nil {
		return SearchResponse{}, err
	}

	var esResponse SearchResponse
	if err = json.Unmarshal(responseBuf, &esResponse); err != nil {
		return SearchResponse{}, err
	}

	return esResponse, nil
}

// MultiSearch implements the MultiSearcher interface for a Node.
func (n *Node) MultiSearch(r MultiSearchRequest) (MultiSearchResponse, error) {
	responseBuf, err := n.searchCommon(r)
	if err != nil {
		return MultiSearchResponse{}, err
	}

	var esResponse MultiSearchResponse
	if err = json.Unmarshal(responseBuf, &esResponse); err != nil {
		return MultiSearchResponse{}, err
	}

	return esResponse, nil
}

//
//
//

type Nodes []*Node

// PingAll triggers simultaneous PingAndSets across all Nodes,
// and blocks until they've all completed.
func (n Nodes) pingAll() {
	c := make(chan bool, len(n))
	for _, node := range n {
		go func(tgt *Node) { tgt.pingAndSet(); c <- true }(node)
	}
	for i := 0; i < cap(c); i++ {
		<-c
	}
}

// GetBest returns the "best" Node, as decided by each Node's health.
// It's possible that no Node will be healthy enough to be returned.
// In that case, GetBest returns an error, and processing cannot continue.
func (n Nodes) getBest() (*Node, error) {
	green, yellow := []*Node{}, []*Node{}
	for _, node := range n {
		switch node.GetHealth() {
		case Green:
			green = append(green, node)
		case Yellow:
			yellow = append(yellow, node)
		}
	}

	if len(green) > 0 {
		return green[rand.Intn(len(green))], nil
	}

	if len(yellow) > 0 {
		return yellow[rand.Intn(len(yellow))], nil
	}

	return nil, fmt.Errorf("no healthy nodes available")
}

//
//
//

// Health is some encoding of the perceived state of a Node.
// A Cluster should favor sending queries against healthier nodes.
type Health int

const (
	Green Health = iota // resemblance to cluster health codes is coincidental
	Yellow
	Red
)

func (h Health) String() string {
	switch h {
	case Green:
		return "Green"
	case Yellow:
		return "Yellow"
	case Red:
		return "Red"
	}
	panic("unreachable")
}

func (h Health) Improve() Health {
	switch h {
	case Red:
		return Yellow
	default:
		return Green
	}
	panic("unreachable")
}

func (h Health) Degrade() Health {
	switch h {
	case Green:
		return Yellow
	default:
		return Red
	}
	panic("unreachable")
}

//
//
//

// timeoutDialer returns a function that can be put into an HTTP Client's
// Transport, which will cause all requests made on that client to abort
// if they're not handled within the passed duration.
func timeoutDialer(d time.Duration) func(net, addr string) (net.Conn, error) {
	return func(netw, addr string) (net.Conn, error) {
		c, err := net.Dial(netw, addr)
		if err != nil {
			return nil, err
		}
		c.SetDeadline(time.Now().Add(d))
		return c, nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"container/list"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/btcsuite/btcd/wire"
	"github.com/lightningnetwork/lnd/lnwallet"
	"github.com/lightningnetwork/lnd/lnwire"
)

// channelState...
type channelState uint8

const (
	// TODO(roasbeef): others??
	channelPending channelState = iota
	channelOpen
	channelClosed
	channelDispute
	channelPendingPayment
)

const (
	numAllowedRetransmits = 5
	pingInterval          = 1 * time.Minute
)

// outgoinMsg...
type outgoinMsg struct {
	msg      lnwire.Message
	sentChan chan struct{}
}

// peer...
// TODO(roasbeef): make this a package now??
// inspired by btcd/peer.go
type peer struct {
	started    int32
	connected  int32
	disconnect int32 // only to be used atomically
	// *ETcpConn or w/e it is in strux
	conn net.Conn

	// TODO(rosabeef): one for now, may need more granularity
	sync.RWMutex

	addr            string
	lnID            [32]byte // TODO(roasbeef): copy from strux
	inbound         bool
	protocolVersion uint32

	// For purposes of detecting retransmits, etc.
	// lastNMessages map[lnwire.Message]struct{}

	timeConnected    time.Time
	lastSend         time.Time
	lastRecv         time.Time
	bytesReceived    uint64
	bytesSent        uint64
	satoshisSent     uint64
	satoshisReceived uint64
	// TODO(roasbeef): pings??

	sendQueueSync chan struct{}
	outgoingQueue chan outgoinMsg
	sendQueue     chan outgoinMsg

	// TODO(roasbeef): akward import, just rename to Wallet?
	wallet *lnwallet.LightningWallet // (tadge: what is this for?)

	// Only will be set if the channel is in the 'pending' state.
	reservation *lnwallet.ChannelReservation

	channel *lnwallet.LightningChannel // TODO(roasbeef): rename to PaymentChannel??

	queueQuit chan struct{}
	quit      chan struct{}
	wg        sync.WaitGroup
}

// readNextMessage...
func (p *peer) readNextMessage() (lnwire.Message, []byte, error) {
	// TODO(roasbeef): use our own net magic?
	_, nextMsg, rawPayload, err := lnwire.ReadMessage(p.conn, 0, wire.TestNet)
	if err != nil {
		return nil, nil, err
	}

	return nextMsg, rawPayload, nil
}

// inHandler..
func (p *peer) inHandler() {
	// TODO(roasbeef): set timeout for initial channel request or version
	// exchange.

out:
	for atomic.LoadInt32(&p.disconnect) == 0 {
		nextMsg, _, err := p.readNextMessage()
		if err != nil {
			// TODO(roasbeef): log error
			break out
		}

		// TODO(roasbeef): state-machine to track version exchange
		switch msg := nextMsg.(type) {
		// TODO(roasbeef): cases
		}
	}

	p.wg.Done()
}

// writeMessage...
func (p *peer) writeMessage(msg lnwire.Message) error {
	// Simply exit if we're shutting down.
	if atomic.LoadInt32(&p.disconnect) != 0 {
		return nil
	}

	_, err := lnwire.WriteMessage(p.conn, msg, 0,
		wire.TestNet)

	return err
}

// outHandler..
func (p *peer) outHandler() {
	// pingTicker is used to periodically send pings to the remote peer.
	pingTicker := time.NewTicker(pingInterval)
	defer pingTicker.Stop()

out:
	for {
		select {
		case outMsg := <-p.sendQueue:
			switch m := outMsg.msg.(type) {
			// TODO(roasbeef): handle special write cases
			}

			if err := p.writeMessage(outMsg.msg); err != nil {
				// TODO(roasbeef): disconnect
			}

			// Synchronize with the outHandler.
			p.sendQueueSync <- struct{}{}
		case <-pingTicker.C:
			// TODO(roasbeef): ping em
		case <-p.quit:
			break out

		}
	}

	// Wait for the queueHandler to finish so we can empty out all pending
	// messages avoiding a possible deadlock somewhere.
	<-p.queueQuit

	// Drain any lingering messages that we're meant to be sent. But since
	// we're shutting down, just ignore them.
fin:
	for {
		select {
		case msg := <-p.sendQueue:
			if msg.sentChan != nil {
				msg.sentChan <- struct{}{}
			}
		default:
			break fin
		}
	}
	p.wg.Done()
}

// queueHandler..
func (p *peer) queueHandler() {
	waitOnSync := false
	pendingMsgs := list.New()
out:
	for {
		select {
		case msg := <-p.outgoingQueue:
			if !waitOnSync {
				p.sendQueue <- msg
			} else {
				pendingMsgs.PushBack(msg)
			}
			waitOnSync = true
		case <-p.sendQueueSync:
			// If there aren't any more remaining messages in the
			// queue, then we're no longer waiting to synchronize
			// with the outHandler.
			next := pendingMsgs.Front()
			if next == nil {
				waitOnSync = false
				continue
			}

			// Notify the outHandler about the next item to
			// asynchronously send.
			val := pendingMsgs.Remove(next)
			p.sendQueue <- val.(outgoinMsg)
			// TODO(roasbeef): other sync stuffs
		case <-p.quit:
			break out
		}
	}

	close(p.queueQuit)
	p.wg.Done()
}
<commit_msg>add lnAddr implementation to peer.go, finish peer draft<commit_after>package main

import (
	"container/list"
	"encoding/hex"
	"fmt"
	"net"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
	"li.lan/labs/plasma/lnwallet"
	"li.lan/labs/plasma/lnwire"
)

var (
	numNodes int32
)

// channelState...
type channelState uint8

const (
	// TODO(roasbeef): others??
	channelPending channelState = iota
	channelOpen
	channelClosed
	channelDispute
	channelPendingPayment
)

const (
	numAllowedRetransmits = 5
	pingInterval          = 1 * time.Minute

	outgoingQueueLen = 50
)

// lnAddr...
type lnAddr struct {
	lnId   [16]byte // redundant because adr contains it
	pubKey *btcec.PublicKey

	bitcoinAddr btcutil.Address
	netAddr     *net.TCPAddr

	name        string
	endorsement []byte
}

// String...
func (l *lnAddr) String() string {
	var encodedId []byte
	if l.pubKey == nil {
		encodedId = l.bitcoinAddr.ScriptAddress()
	} else {
		encodedId = l.pubKey.SerializeCompressed()
	}

	return fmt.Sprintf("%v@%v", encodedId, l.netAddr)
}

// newLnAddr...
func newLnAddr(encodedAddr string) (*lnAddr, error) {
	// The format of an lnaddr is "<pubkey or pkh>@host"
	idHost := strings.Split(encodedAddr, "@")
	if len(idHost) != 2 {
		return nil, fmt.Errorf("invalid format for lnaddr string: %v", encodedAddr)
	}

	// Attempt to resolve the IP address, this handles parsing IPv6 zones,
	// and such.
	fmt.Println("host: ", idHost[1])
	ipAddr, err := net.ResolveTCPAddr("tcp", idHost[1])
	if err != nil {
		return nil, err
	}

	addr := &lnAddr{netAddr: ipAddr}

	idLen := len(idHost[0])
	switch {
	// Is the ID a hex-encoded compressed public key?
	case idLen > 65 && idLen < 69:
		pubkeyBytes, err := hex.DecodeString(idHost[0])
		if err != nil {
			return nil, err
		}

		addr.pubKey, err = btcec.ParsePubKey(pubkeyBytes, btcec.S256())
		if err != nil {
			return nil, err
		}

		// got pubey, populate address from pubkey
		pkh := btcutil.Hash160(addr.pubKey.SerializeCompressed())
		addr.bitcoinAddr, err = btcutil.NewAddressPubKeyHash(pkh,
			&chaincfg.TestNet3Params)
		if err != nil {
			return nil, err
		}
	// Is the ID a string encoded bitcoin address?
	case idLen > 33 && idLen < 37:
		addr.bitcoinAddr, err = btcutil.DecodeAddress(idHost[0],
			&chaincfg.TestNet3Params)
		if err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("invalid address %s", idHost[0])
	}

	// Finally, populate the lnid from the address.
	copy(addr.lnId[:], addr.bitcoinAddr.ScriptAddress())

	return addr, nil
}

// outgoinMsg...
type outgoinMsg struct {
	msg      lnwire.Message
	sentChan chan struct{}
}

// peer...
// inspired by btcd/peer.go
type peer struct {
	// only to be used atomically
	started    int32
	connected  int32
	disconnect int32

	conn net.Conn

	lightningAddr   lnAddr
	inbound         bool
	protocolVersion uint32
	peerId          int32

	// For purposes of detecting retransmits, etc.
	lastNMessages map[lnwire.Message]struct{}

	sync.RWMutex
	timeConnected    time.Time
	lastSend         time.Time
	lastRecv         time.Time
	bytesReceived    uint64
	bytesSent        uint64
	satoshisSent     uint64
	satoshisReceived uint64
	// TODO(roasbeef): pings??

	sendQueueSync chan struct{}
	outgoingQueue chan outgoinMsg
	sendQueue     chan outgoinMsg

	// Only will be set if the channel is in the 'pending' state.
	reservation *lnwallet.ChannelReservation

	lnChannel *lnwallet.LightningChannel

	queueQuit chan struct{}
	quit      chan struct{}
	wg        sync.WaitGroup
}

// newPeer...
func newPeer(conn net.Conn, server *server) *peer {
	return &peer{
		conn:   conn,
		peerId: atomic.AddInt32(&numNodes, 1),

		lastNMessages: make(map[lnwire.Message]struct{}),

		sendQueueSync: make(chan struct{}, 1),
		sendQueue:     make(chan outgoinMsg, 1),
		outgoingQueue: make(chan outgoinMsg, outgoingQueueLen),

		queueQuit: make(chan struct{}),
		quit:      make(chan struct{}),
	}
}

func (p *peer) Start() error {
	if atomic.AddInt32(&p.started, 1) != 1 {
		return nil
	}

	// TODO(roasbeef): version handshake

	p.wg.Add(3)
	go p.inHandler()
	go p.queueHandler()
	go p.outHandler()

	return nil
}

func (p *peer) Stop() error {
	// If we're already disconnecting, just exit.
	if atomic.AddInt32(&p.disconnect, 1) != 1 {
		return nil
	}

	// Otherwise, close the connection if we're currently connected.
	if atomic.LoadInt32(&p.connected) != 0 {
		p.conn.Close()
	}

	// Signal all worker goroutines to gracefully exit.
	close(p.quit)

	return nil
}

// readNextMessage...
func (p *peer) readNextMessage() (lnwire.Message, []byte, error) {
	// TODO(roasbeef): use our own net magic?
	_, nextMsg, rawPayload, err := lnwire.ReadMessage(p.conn, 0, wire.TestNet)
	if err != nil {
		return nil, nil, err
	}

	return nextMsg, rawPayload, nil
}

// inHandler..
func (p *peer) inHandler() {
	// TODO(roasbeef): set timeout for initial channel request or version
	// exchange.

out:
	for atomic.LoadInt32(&p.disconnect) == 0 {
		nextMsg, _, err := p.readNextMessage()
		if err != nil {
			// TODO(roasbeef): log error
			break out
		}

		// TODO(roasbeef): state-machine to track version exchange
		switch msg := nextMsg.(type) {
		// TODO(roasbeef): cases
		}
	}

	p.wg.Done()
}

// writeMessage...
func (p *peer) writeMessage(msg lnwire.Message) error {
	// Simply exit if we're shutting down.
	if atomic.LoadInt32(&p.disconnect) != 0 {
		return nil
	}

	_, err := lnwire.WriteMessage(p.conn, msg, 0,
		wire.TestNet)

	return err
}

// outHandler..
func (p *peer) outHandler() {
	// pingTicker is used to periodically send pings to the remote peer.
	pingTicker := time.NewTicker(pingInterval)
	defer pingTicker.Stop()

out:
	for {
		select {
		case outMsg := <-p.sendQueue:
			switch m := outMsg.msg.(type) {
			// TODO(roasbeef): handle special write cases
			}

			if err := p.writeMessage(outMsg.msg); err != nil {
				// TODO(roasbeef): disconnect
			}

			// Synchronize with the outHandler.
			p.sendQueueSync <- struct{}{}
		case <-pingTicker.C:
			// TODO(roasbeef): ping em
		case <-p.quit:
			break out

		}
	}

	// Wait for the queueHandler to finish so we can empty out all pending
	// messages avoiding a possible deadlock somewhere.
	<-p.queueQuit

	// Drain any lingering messages that we're meant to be sent. But since
	// we're shutting down, just ignore them.
fin:
	for {
		select {
		case msg := <-p.sendQueue:
			if msg.sentChan != nil {
				msg.sentChan <- struct{}{}
			}
		default:
			break fin
		}
	}
	p.wg.Done()
}

// queueHandler..
func (p *peer) queueHandler() {
	waitOnSync := false
	pendingMsgs := list.New()
out:
	for {
		select {
		case msg := <-p.outgoingQueue:
			if !waitOnSync {
				p.sendQueue <- msg
			} else {
				pendingMsgs.PushBack(msg)
			}
			waitOnSync = true
		case <-p.sendQueueSync:
			// If there aren't any more remaining messages in the
			// queue, then we're no longer waiting to synchronize
			// with the outHandler.
			next := pendingMsgs.Front()
			if next == nil {
				waitOnSync = false
				continue
			}

			// Notify the outHandler about the next item to
			// asynchronously send.
			val := pendingMsgs.Remove(next)
			p.sendQueue <- val.(outgoinMsg)
			// TODO(roasbeef): other sync stuffs
		case <-p.quit:
			break out
		}
	}

	close(p.queueQuit)
	p.wg.Done()
}
<|endoftext|>


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

// Package implements support for SPKAC/PKAC data as produced by the html
// <keygen> element (Signed Public Key And Challenge).
//
// References:
// - https://web.archive.org/web/20070401073244/http://wp.netscape.com/eng/security/comm4-keygen.html
// - https://www.openssl.org/docs/apps/spkac.html
// - http://lists.whatwg.org/pipermail/whatwg-whatwg.org/attachments/20080714/07ea5534/attachment.txt
package pkac

import (
	"crypto"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"errors"
)

type pkacInfo struct {
	Raw       asn1.RawContent
	PublicKey publicKeyInfo
	Challenge string
}

type spkacInfo struct {
	Raw       asn1.RawContent
	Pkac      pkacInfo
	Algorithm pkix.AlgorithmIdentifier
	Signature asn1.BitString
}

func parseSpkac(derBytes []byte, validate bool) (pub crypto.PublicKey, err error) {
	var info spkacInfo
	if _, err = asn1.Unmarshal(derBytes, &info); err != nil {
		return
	}

	algo := getPublicKeyAlgorithmFromOID(info.Pkac.PublicKey.Algorithm.Algorithm)
	if algo == x509.UnknownPublicKeyAlgorithm {
		return nil, errors.New("x509: unknown public key algorithm")
	}

	pub, err = parsePublicKey(algo, &info.Pkac.PublicKey)
	if err != nil {
		return
	}

	sigAlgo := getSignatureDetailsFromOID(info.Algorithm.Algorithm)
	if sigAlgo == x509.UnknownSignatureAlgorithm {
		return nil, errors.New("x509: unknown signature algorithm")
	}

	if !validate {
		return
	}
	err = validateSignature(sigAlgo, pub, info.Pkac.Raw, info.Signature.Bytes)
	if err != nil {
		return
	}
	return
}

// Parse a BER-encoded SPKAC and return the public key from it without
// validating a signature.
//
// This function is provided for compatibility with PKAC blobs using
// message digests that are known to be broken (e.g. RSA with MD2).
func ParseSPKAC(derBytes []byte) (crypto.PublicKey, error) {
	return parseSpkac(derBytes, false)
}

// Parse a BER-encoded SPKAC and return the public key from it,
// validating a signature to ensure integrity.
func ValidateSPKAC(derBytes []byte) (pub crypto.PublicKey, err error) {
	return parseSpkac(derBytes, true)
}
<commit_msg>Update links and docs<commit_after>// Copyright 2014 Simon Eisenmann. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package pkac implements support for SPKAC/PKAC data as produced by the html
// <keygen> element (Signed Public Key And Challenge).
//
// References:
// - https://web.archive.org/web/20070401073244/http://wp.netscape.com/eng/security/comm4-keygen.html
// - https://wiki.openssl.org/index.php/Manual:Spkac(1)
// - http://lists.whatwg.org/pipermail/whatwg-whatwg.org/attachments/20080714/07ea5534/attachment.txt
package pkac

import (
	"crypto"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"errors"
)

type pkacInfo struct {
	Raw       asn1.RawContent
	PublicKey publicKeyInfo
	Challenge string
}

type spkacInfo struct {
	Raw       asn1.RawContent
	Pkac      pkacInfo
	Algorithm pkix.AlgorithmIdentifier
	Signature asn1.BitString
}

func parseSpkac(derBytes []byte, validate bool) (pub crypto.PublicKey, err error) {
	var info spkacInfo
	if _, err = asn1.Unmarshal(derBytes, &info); err != nil {
		return
	}

	algo := getPublicKeyAlgorithmFromOID(info.Pkac.PublicKey.Algorithm.Algorithm)
	if algo == x509.UnknownPublicKeyAlgorithm {
		return nil, errors.New("x509: unknown public key algorithm")
	}

	pub, err = parsePublicKey(algo, &info.Pkac.PublicKey)
	if err != nil {
		return
	}

	sigAlgo := getSignatureDetailsFromOID(info.Algorithm.Algorithm)
	if sigAlgo == x509.UnknownSignatureAlgorithm {
		return nil, errors.New("x509: unknown signature algorithm")
	}

	if !validate {
		return
	}
	err = validateSignature(sigAlgo, pub, info.Pkac.Raw, info.Signature.Bytes)
	if err != nil {
		return
	}
	return
}

// ParseSPKAC parses a BER-encoded SPKAC and return the public key from it without
// validating a signature.
//
// This function is provided for compatibility with PKAC blobs using
// message digests that are known to be broken (e.g. RSA with MD2).
func ParseSPKAC(derBytes []byte) (crypto.PublicKey, error) {
	return parseSpkac(derBytes, false)
}

// ValidateSPKAC parses a BER-encoded SPKAC and return the public key from it,
// validating a signature to ensure integrity.
func ValidateSPKAC(derBytes []byte) (pub crypto.PublicKey, err error) {
	return parseSpkac(derBytes, true)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	config "github.com/spf13/viper"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

//Runtime
func init() {
	config.SetConfigName("config")
	config.AddConfigPath(".\\")
	config.ReadInConfig()

	config.SetDefault("Binding", ":8080")
	config.SetDefault("ScriptFolder", ".\\scripts\\")
}

func main() {
	mx := mux.NewRouter()

	mx.HandleFunc("/", IndexHandler)
	mx.HandleFunc("/exit", ExitHandler)
	mx.HandleFunc("/command/{name:\\S+}", RunShell)
	mx.HandleFunc("/script/{name:\\S+}", RunScript)

	log.Info("Listening at " + config.GetString("Binding"))
	http.ListenAndServe(config.GetString("Binding"), mx)
}

//Functions
func ParseArgs(r *http.Request) string {
	var argsBuffer bytes.Buffer

	pUrl, err := url.Parse(r.RequestURI)
	if err != nil {
		log.Error("Error Parsing URL")
	}

	for key, value := range pUrl.Query() {
		argsBuffer.WriteString(" " + key)
		argsBuffer.WriteString(" " + value[0])
	}

	return argsBuffer.String()
}

func exec_script(sc string) string {
	sc = string(strings.TrimSpace(sc))

	cmd := exec.Command("powershell.exe", "-NoLogo", "-NonInteractive", "-Command", "&{", sc, "}")
	out, err := cmd.Output()

	if err != nil {
		log.WithFields(log.Fields{
			"command": string(sc),
		}).Error(err.Error())
	}

	return string(out)
}

//Handlers
func IndexHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Service Running"))
}

func ExitHandler(w http.ResponseWriter, r *http.Request) {
	defer os.Exit(0)

	log.Info("Shutting Down")
	w.Write([]byte(fmt.Sprintf("Shutting Down")))
	time.Sleep(3000 * time.Millisecond)
}

func RunShell(w http.ResponseWriter, r *http.Request) {
	var commbuffer bytes.Buffer
	commbuffer.WriteString(mux.Vars(r)["name"])
	commbuffer.WriteString(ParseArgs(r))
	commbuffer.WriteString(" | ConvertTo-Json")

	w.Write([]byte(fmt.Sprintf(exec_script(commbuffer.String()))))
}

func RunScript(w http.ResponseWriter, r *http.Request) {
	var commbuffer bytes.Buffer

	commbuffer.WriteString("&\"")
	commbuffer.WriteString(filepath.Join(config.GetString("ScriptFolder"), mux.Vars(r)["name"]))
	commbuffer.WriteString("\"")
	commbuffer.WriteString(ParseArgs(r))

	w.Write([]byte(fmt.Sprintf(exec_script(commbuffer.String()))))
}
<commit_msg>Fixed issue with /command/ context<commit_after>package main

import (
	"bytes"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	config "github.com/spf13/viper"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

//Runtime
func init() {
	config.SetConfigName("config")
	config.AddConfigPath(".\\")
	config.ReadInConfig()

	config.SetDefault("Binding", ":8080")
	config.SetDefault("ScriptFolder", ".\\scripts\\")
}

func main() {
	mx := mux.NewRouter()

	mx.HandleFunc("/", IndexHandler)
	mx.HandleFunc("/exit", ExitHandler)
	mx.HandleFunc("/command/{name:\\S+}", RunCommand)
	mx.HandleFunc("/script/{name:\\S+}", RunScript)

	log.Info("Listening at " + config.GetString("Binding"))
	http.ListenAndServe(config.GetString("Binding"), mx)
}

//Functions
func ParseArgs(r *http.Request) string {
	var argsBuffer bytes.Buffer

	pUrl, err := url.Parse(r.RequestURI)
	if err != nil {
		log.Error("Error Parsing URL")
	}

	for key, value := range pUrl.Query() {
		argsBuffer.WriteString(" " + key)
		argsBuffer.WriteString(" " + value[0])
	}

	return argsBuffer.String()
}

func exec_script(sc string) string {
	sc = string(strings.TrimSpace(sc))

	cmd := exec.Command("powershell.exe", "-NoLogo", "-NonInteractive", "-Command", "&{", sc, "}")
	out, err := cmd.Output()

	if err != nil {
		log.WithFields(log.Fields{
			"command": string(sc),
		}).Error(err.Error())
	}

	return string(out)
}

//Handlers
func IndexHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Service Running"))
}

func ExitHandler(w http.ResponseWriter, r *http.Request) {
	defer os.Exit(0)

	log.Info("Shutting Down")
	w.Write([]byte(fmt.Sprintf("Shutting Down")))
	time.Sleep(3000 * time.Millisecond)
}

func RunCommand(w http.ResponseWriter, r *http.Request) {
	var commbuffer bytes.Buffer
	commbuffer.WriteString(mux.Vars(r)["name"])
	commbuffer.WriteString(ParseArgs(r))
	commbuffer.WriteString(" | ConvertTo-Json")

	w.Write([]byte(fmt.Sprintf(exec_script(commbuffer.String()))))
}

func RunScript(w http.ResponseWriter, r *http.Request) {
	var commbuffer bytes.Buffer

	commbuffer.WriteString("&\"")
	commbuffer.WriteString(filepath.Join(config.GetString("ScriptFolder"), mux.Vars(r)["name"]))
	commbuffer.WriteString("\"")
	commbuffer.WriteString(ParseArgs(r))

	w.Write([]byte(fmt.Sprintf(exec_script(commbuffer.String()))))
}
<|endoftext|>


<commit_before>package vm

import (
	"bytes"
	"math/big"
	"strconv"
	"testing"

	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/state"
	"github.com/ethereum/go-ethereum/tests/helper"
)

type Account struct {
	Balance string
	Code    string
	Nonce   string
	Storage map[string]string
}

type Log struct {
	AddressF string   `json:"address"`
	DataF    string   `json:"data"`
	TopicsF  []string `json:"topics"`
	BloomF   string   `json:"bloom"`
}

func (self Log) Address() []byte      { return ethutil.Hex2Bytes(self.AddressF) }
func (self Log) Data() []byte         { return ethutil.Hex2Bytes(self.DataF) }
func (self Log) RlpData() interface{} { return nil }
func (self Log) Topics() [][]byte {
	t := make([][]byte, len(self.TopicsF))
	for i, topic := range self.TopicsF {
		t[i] = ethutil.Hex2Bytes(topic)
	}
	return t
}

func StateObjectFromAccount(db ethutil.Database, addr string, account Account) *state.StateObject {
	obj := state.NewStateObject(ethutil.Hex2Bytes(addr), db)
	obj.SetBalance(ethutil.Big(account.Balance))

	if ethutil.IsHex(account.Code) {
		account.Code = account.Code[2:]
	}
	obj.SetCode(ethutil.Hex2Bytes(account.Code))
	obj.SetNonce(ethutil.Big(account.Nonce).Uint64())

	return obj
}

type Env struct {
	CurrentCoinbase   string
	CurrentDifficulty string
	CurrentGasLimit   string
	CurrentNumber     string
	CurrentTimestamp  interface{}
	PreviousHash      string
}

type VmTest struct {
	Callcreates interface{}
	//Env         map[string]string
	Env         Env
	Exec        map[string]string
	Transaction map[string]string
	Logs        []Log
	Gas         string
	Out         string
	Post        map[string]Account
	Pre         map[string]Account
}

func RunVmTest(p string, t *testing.T) {
	tests := make(map[string]VmTest)
	helper.CreateFileTests(t, p, &tests)

	for name, test := range tests {
		db, _ := ethdb.NewMemDatabase()
		statedb := state.New(nil, db)
		for addr, account := range test.Pre {
			obj := StateObjectFromAccount(db, addr, account)
			statedb.SetStateObject(obj)
			for a, v := range account.Storage {
				obj.SetState(helper.FromHex(a), ethutil.NewValue(helper.FromHex(v)))
			}
		}

		// XXX Yeah, yeah...
		env := make(map[string]string)
		env["currentCoinbase"] = test.Env.CurrentCoinbase
		env["currentDifficulty"] = test.Env.CurrentDifficulty
		env["currentGasLimit"] = test.Env.CurrentGasLimit
		env["currentNumber"] = test.Env.CurrentNumber
		env["previousHash"] = test.Env.PreviousHash
		if n, ok := test.Env.CurrentTimestamp.(float64); ok {
			env["currentTimestamp"] = strconv.Itoa(int(n))
		} else {
			env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
		}

		var (
			ret  []byte
			gas  *big.Int
			err  error
			logs state.Logs
		)

		isVmTest := len(test.Exec) > 0
		if isVmTest {
			ret, logs, gas, err = helper.RunVm(statedb, env, test.Exec)
		} else {
			ret, logs, gas, err = helper.RunState(statedb, env, test.Transaction)
		}

		rexp := helper.FromHex(test.Out)
		if bytes.Compare(rexp, ret) != 0 {
			t.Errorf("%s's return failed. Expected %x, got %x\n", name, rexp, ret)
		}

		if isVmTest {
			if len(test.Gas) == 0 && err == nil {
				t.Errorf("%s's gas unspecified, indicating an error. VM returned (incorrectly) successfull", name)
			} else {
				gexp := ethutil.Big(test.Gas)
				if gexp.Cmp(gas) != 0 {
					t.Errorf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
				}
			}
		}

		for addr, account := range test.Post {
			obj := statedb.GetStateObject(helper.FromHex(addr))
			if obj == nil {
				continue
			}

			if len(test.Exec) == 0 {
				if obj.Balance().Cmp(ethutil.Big(account.Balance)) != 0 {
					t.Errorf("%s's : (%x) balance failed. Expected %v, got %v => %v\n", name, obj.Address()[:4], account.Balance, obj.Balance(), new(big.Int).Sub(ethutil.Big(account.Balance), obj.Balance()))
				}
			}

			for addr, value := range account.Storage {
				v := obj.GetState(helper.FromHex(addr)).Bytes()
				vexp := helper.FromHex(value)

				if bytes.Compare(v, vexp) != 0 {
					t.Errorf("%s's : (%x: %s) storage failed. Expected %x, got %x (%v %v)\n", name, obj.Address()[0:4], addr, vexp, v, ethutil.BigD(vexp), ethutil.BigD(v))
				}
			}
		}

		if len(test.Logs) > 0 {
			if len(test.Logs) != len(logs) {
				t.Errorf("log length mismatch. Expected %d, got %d", len(test.Logs), len(logs))
			} else {
				for i, log := range test.Logs {
					genBloom := ethutil.LeftPadBytes(types.LogsBloom(state.Logs{logs[i]}).Bytes(), 64)
					if !bytes.Equal(genBloom, ethutil.Hex2Bytes(log.BloomF)) {
						t.Errorf("bloom mismatch")
					}
				}
			}
		}
	}
	logger.Flush()
}

// I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail.
func TestVMArithmetic(t *testing.T) {
	const fn = "../files/VMTests/vmArithmeticTest.json"
	RunVmTest(fn, t)
}

func TestBitwiseLogicOperation(t *testing.T) {
	const fn = "../files/VMTests/vmBitwiseLogicOperationTest.json"
	RunVmTest(fn, t)
}

func TestBlockInfo(t *testing.T) {
	const fn = "../files/VMTests/vmBlockInfoTest.json"
	RunVmTest(fn, t)
}

func TestEnvironmentalInfo(t *testing.T) {
	const fn = "../files/VMTests/vmEnvironmentalInfoTest.json"
	RunVmTest(fn, t)
}

func TestFlowOperation(t *testing.T) {
	const fn = "../files/VMTests/vmIOandFlowOperationsTest.json"
	RunVmTest(fn, t)
}

func TestLogTest(t *testing.T) {
	const fn = "../files/VMTests/vmLogTest.json"
	RunVmTest(fn, t)
}

func TestPerformance(t *testing.T) {
	t.Skip()
	const fn = "../files/VMTests/vmPerformance.json"
	RunVmTest(fn, t)
}

func TestPushDupSwap(t *testing.T) {
	const fn = "../files/VMTests/vmPushDupSwapTest.json"
	RunVmTest(fn, t)
}

func TestVMSha3(t *testing.T) {
	const fn = "../files/VMTests/vmSha3Test.json"
	RunVmTest(fn, t)
}

func TestVm(t *testing.T) {
	const fn = "../files/VMTests/vmtests.json"
	RunVmTest(fn, t)
}

func TestVmLog(t *testing.T) {
	const fn = "../files/VMTests/vmLogTest.json"
	RunVmTest(fn, t)
}

/*
func TestStateSystemOperations(t *testing.T) {
	const fn = "../files/StateTests/stSystemOperationsTest.json"
	RunVmTest(fn, t)
}

func TestStatePreCompiledContracts(t *testing.T) {
	const fn = "../files/StateTests/stPreCompiledContracts.json"
	RunVmTest(fn, t)
}

func TestStateRecursiveCreate(t *testing.T) {
	const fn = "../files/StateTests/stRecursiveCreate.json"
	RunVmTest(fn, t)
}

func TestStateSpecial(t *testing.T) {
	const fn = "../files/StateTests/stSpecialTest.json"
	RunVmTest(fn, t)
}

func TestStateRefund(t *testing.T) {
	const fn = "../files/StateTests/stRefundTest.json"
	RunVmTest(fn, t)
}

func TestStateBlockHash(t *testing.T) {
	const fn = "../files/StateTests/stBlockHashTest.json"
	RunVmTest(fn, t)
}

func TestStateInitCode(t *testing.T) {
	const fn = "../files/StateTests/stInitCodeTest.json"
	RunVmTest(fn, t)
}

func TestStateLog(t *testing.T) {
	const fn = "../files/StateTests/stLogTests.json"
	RunVmTest(fn, t)
}

func TestStateTransaction(t *testing.T) {
	t.Skip()
	const fn = "../files/StateTests/stTransactionTest.json"
	RunVmTest(fn, t)
}
*/
<commit_msg>Delet => Delete<commit_after>package vm

import (
	"bytes"
	"math/big"
	"strconv"
	"testing"

	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/ethutil"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/state"
	"github.com/ethereum/go-ethereum/tests/helper"
)

type Account struct {
	Balance string
	Code    string
	Nonce   string
	Storage map[string]string
}

type Log struct {
	AddressF string   `json:"address"`
	DataF    string   `json:"data"`
	TopicsF  []string `json:"topics"`
	BloomF   string   `json:"bloom"`
}

func (self Log) Address() []byte      { return ethutil.Hex2Bytes(self.AddressF) }
func (self Log) Data() []byte         { return ethutil.Hex2Bytes(self.DataF) }
func (self Log) RlpData() interface{} { return nil }
func (self Log) Topics() [][]byte {
	t := make([][]byte, len(self.TopicsF))
	for i, topic := range self.TopicsF {
		t[i] = ethutil.Hex2Bytes(topic)
	}
	return t
}

func StateObjectFromAccount(db ethutil.Database, addr string, account Account) *state.StateObject {
	obj := state.NewStateObject(ethutil.Hex2Bytes(addr), db)
	obj.SetBalance(ethutil.Big(account.Balance))

	if ethutil.IsHex(account.Code) {
		account.Code = account.Code[2:]
	}
	obj.SetCode(ethutil.Hex2Bytes(account.Code))
	obj.SetNonce(ethutil.Big(account.Nonce).Uint64())

	return obj
}

type Env struct {
	CurrentCoinbase   string
	CurrentDifficulty string
	CurrentGasLimit   string
	CurrentNumber     string
	CurrentTimestamp  interface{}
	PreviousHash      string
}

type VmTest struct {
	Callcreates interface{}
	//Env         map[string]string
	Env           Env
	Exec          map[string]string
	Transaction   map[string]string
	Logs          []Log
	Gas           string
	Out           string
	Post          map[string]Account
	Pre           map[string]Account
	PostStateRoot string
}

func RunVmTest(p string, t *testing.T) {
	tests := make(map[string]VmTest)
	helper.CreateFileTests(t, p, &tests)

	for name, test := range tests {
		db, _ := ethdb.NewMemDatabase()
		statedb := state.New(nil, db)
		for addr, account := range test.Pre {
			obj := StateObjectFromAccount(db, addr, account)
			statedb.SetStateObject(obj)
			for a, v := range account.Storage {
				obj.SetState(helper.FromHex(a), ethutil.NewValue(helper.FromHex(v)))
			}
		}

		// XXX Yeah, yeah...
		env := make(map[string]string)
		env["currentCoinbase"] = test.Env.CurrentCoinbase
		env["currentDifficulty"] = test.Env.CurrentDifficulty
		env["currentGasLimit"] = test.Env.CurrentGasLimit
		env["currentNumber"] = test.Env.CurrentNumber
		env["previousHash"] = test.Env.PreviousHash
		if n, ok := test.Env.CurrentTimestamp.(float64); ok {
			env["currentTimestamp"] = strconv.Itoa(int(n))
		} else {
			env["currentTimestamp"] = test.Env.CurrentTimestamp.(string)
		}

		var (
			ret  []byte
			gas  *big.Int
			err  error
			logs state.Logs
		)

		isVmTest := len(test.Exec) > 0
		if isVmTest {
			ret, logs, gas, err = helper.RunVm(statedb, env, test.Exec)
		} else {
			ret, logs, gas, err = helper.RunState(statedb, env, test.Transaction)
		}

		rexp := helper.FromHex(test.Out)
		if bytes.Compare(rexp, ret) != 0 {
			t.Errorf("%s's return failed. Expected %x, got %x\n", name, rexp, ret)
		}

		if isVmTest {
			if len(test.Gas) == 0 && err == nil {
				t.Errorf("%s's gas unspecified, indicating an error. VM returned (incorrectly) successfull", name)
			} else {
				gexp := ethutil.Big(test.Gas)
				if gexp.Cmp(gas) != 0 {
					t.Errorf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
				}
			}
		}

		for addr, account := range test.Post {
			obj := statedb.GetStateObject(helper.FromHex(addr))
			if obj == nil {
				continue
			}

			if len(test.Exec) == 0 {
				if obj.Balance().Cmp(ethutil.Big(account.Balance)) != 0 {
					t.Errorf("%s's : (%x) balance failed. Expected %v, got %v => %v\n", name, obj.Address()[:4], account.Balance, obj.Balance(), new(big.Int).Sub(ethutil.Big(account.Balance), obj.Balance()))
				}
			}

			for addr, value := range account.Storage {
				v := obj.GetState(helper.FromHex(addr)).Bytes()
				vexp := helper.FromHex(value)

				if bytes.Compare(v, vexp) != 0 {
					t.Errorf("%s's : (%x: %s) storage failed. Expected %x, got %x (%v %v)\n", name, obj.Address()[0:4], addr, vexp, v, ethutil.BigD(vexp), ethutil.BigD(v))
				}
			}
		}

		if !isVmTest {
			if !bytes.Equal(ethutil.Hex2Bytes(test.PostStateRoot), statedb.Root()) {
				t.Errorf("Post state root error. Expected %s, got %x", test.PostStateRoot, statedb.Root())
			}
		}

		if len(test.Logs) > 0 {
			if len(test.Logs) != len(logs) {
				t.Errorf("log length mismatch. Expected %d, got %d", len(test.Logs), len(logs))
			} else {
				for i, log := range test.Logs {
					genBloom := ethutil.LeftPadBytes(types.LogsBloom(state.Logs{logs[i]}).Bytes(), 64)
					if !bytes.Equal(genBloom, ethutil.Hex2Bytes(log.BloomF)) {
						t.Errorf("bloom mismatch")
					}
				}
			}
		}
	}
	logger.Flush()
}

// I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail.
func TestVMArithmetic(t *testing.T) {
	const fn = "../files/VMTests/vmArithmeticTest.json"
	RunVmTest(fn, t)
}

func TestBitwiseLogicOperation(t *testing.T) {
	const fn = "../files/VMTests/vmBitwiseLogicOperationTest.json"
	RunVmTest(fn, t)
}

func TestBlockInfo(t *testing.T) {
	const fn = "../files/VMTests/vmBlockInfoTest.json"
	RunVmTest(fn, t)
}

func TestEnvironmentalInfo(t *testing.T) {
	const fn = "../files/VMTests/vmEnvironmentalInfoTest.json"
	RunVmTest(fn, t)
}

func TestFlowOperation(t *testing.T) {
	const fn = "../files/VMTests/vmIOandFlowOperationsTest.json"
	RunVmTest(fn, t)
}

func TestLogTest(t *testing.T) {
	const fn = "../files/VMTests/vmLogTest.json"
	RunVmTest(fn, t)
}

func TestPerformance(t *testing.T) {
	t.Skip()
	const fn = "../files/VMTests/vmPerformance.json"
	RunVmTest(fn, t)
}

func TestPushDupSwap(t *testing.T) {
	const fn = "../files/VMTests/vmPushDupSwapTest.json"
	RunVmTest(fn, t)
}

func TestVMSha3(t *testing.T) {
	const fn = "../files/VMTests/vmSha3Test.json"
	RunVmTest(fn, t)
}

func TestVm(t *testing.T) {
	const fn = "../files/VMTests/vmtests.json"
	RunVmTest(fn, t)
}

func TestVmLog(t *testing.T) {
	const fn = "../files/VMTests/vmLogTest.json"
	RunVmTest(fn, t)
}

/*
func TestStateSystemOperations(t *testing.T) {
	const fn = "../files/StateTests/stSystemOperationsTest.json"
	RunVmTest(fn, t)
}

func TestStatePreCompiledContracts(t *testing.T) {
	const fn = "../files/StateTests/stPreCompiledContracts.json"
	RunVmTest(fn, t)
}

func TestStateRecursiveCreate(t *testing.T) {
	const fn = "../files/StateTests/stRecursiveCreate.json"
	RunVmTest(fn, t)
}

func TestStateSpecial(t *testing.T) {
	const fn = "../files/StateTests/stSpecialTest.json"
	RunVmTest(fn, t)
}

func TestStateRefund(t *testing.T) {
	const fn = "../files/StateTests/stRefundTest.json"
	RunVmTest(fn, t)
}

func TestStateBlockHash(t *testing.T) {
	const fn = "../files/StateTests/stBlockHashTest.json"
	RunVmTest(fn, t)
}

func TestStateInitCode(t *testing.T) {
	const fn = "../files/StateTests/stInitCodeTest.json"
	RunVmTest(fn, t)
}

func TestStateLog(t *testing.T) {
	const fn = "../files/StateTests/stLogTests.json"
	RunVmTest(fn, t)
}

func TestStateTransaction(t *testing.T) {
	t.Skip()
	const fn = "../files/StateTests/stTransactionTest.json"
	RunVmTest(fn, t)
}
*/
<|endoftext|>