<commit_before>package golangsdk

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	zincBaseURL = "https://api.zinc.io/v1"
)

type Retailer string

const (
	Amazon     Retailer = "amazon"
	AmazonUK   Retailer = "amazon_uk"
	AmazonCA   Retailer = "amazon_ca"
	AmazonMX   Retailer = "amazon_mx"
	Walmart    Retailer = "walmart"
	Aliexpress Retailer = "aliexpress"
)

var DefaultProductOptions = ProductOptions{
	Timeout: time.Duration(time.Second * 90),
}

type Zinc struct {
	ClientToken string
	ZincBaseURL string
}

func GetRetailer(retailer string) (Retailer, error) {
	switch retailer {
	case "amazon":
		return Amazon, nil
	case "amazon_uk":
		return AmazonUK, nil
	case "amazon_ca":
		return AmazonCA, nil
	case "amazon_mx":
		return AmazonMX, nil
	case "walmart":
		return Walmart, nil
	case "aliexpress":
		return Aliexpress, nil
	default:
		return Amazon, fmt.Errorf("Invalid retailer string")
	}
}

func NewZinc(clientToken string) (*Zinc, error) {
	z := Zinc{
		ClientToken: clientToken,
		ZincBaseURL: zincBaseURL,
	}
	return &z, nil
}

type OrderRequest struct {
	Retailer            Retailer             `json:"retailer"`
	Products            []Product            `json:"products"`
	ShippingMethod      string               `json:"shipping_method,omitempty"`
	Shipping            *Shipping            `json:"shipping,omitempty"`
	ShippingAddress     *Address             `json:"shipping_address"`
	BillingAddress      *Address             `json:"billing_address,omitempty"`
	PaymentMethod       *PaymentMethod       `json:"payment_method,omitempty"`
	RetailerCredentials *RetailerCredentials `json:"retailer_credentials,omitempty"`
	GiftMessage         string               `json:"gift_message,omitempty"`
	IsGift              bool                 `json:"is_gift"`
	MaxPrice            int                  `json:"max_price"`
	Webhooks            *Webhooks            `json:"webhooks,omitempty"`
	Bundled             bool                 `json:"bundled"`
	Addax               bool                 `json:"addax"`
}

type Product struct {
	ProductId               string                   `json:"product_id"`
	Quantity                int                      `json:"quantity"`
	SellerSelectionCriteria *SellerSelectionCriteria `json:"seller_selection_criteria,omitempty"`
}

type Shipping struct {
	OrderBy  string `json:"order_by,omitempty"`
	MaxDays  int    `json:"max_days"`
	MaxPrice int    `json:"max_price"`
}

type Address struct {
	FirstName    string `json:"first_name"`
	LastName     string `json:"last_name"`
	AddressLine1 string `json:"address_line1"`
	AddressLine2 string `json:"address_line2"`
	ZipCode      string `json:"zip_code"`
	City         string `json:"city"`
	State        string `json:"state"`
	Country      string `json:"country"`
	PhoneNumber  string `json:"phone_number"`
}

type PaymentMethod struct {
	NameOnCard      string `json:"name_on_card,omitempty"`
	Number          string `json:"number,omitempty"`
	SecurityCode    string `json:"security_code,omitempty"`
	ExpirationMonth int    `json:"expiration_month,omitempty"`
	ExpirationYear  int    `json:"expiration_year,omitempty"`
	UseGift         bool   `json:"use_gift"`
}

type RetailerCredentials struct {
	Email            string `json:"email"`
	Password         string `json:"password"`
	VerificationCode string `json:"verification_code,omitempty"`
}

type Webhooks struct {
	RequestSucceeded string `json:"request_succeeded"`
	RequestFailed    string `json:"request_failed"`
	TrackingObtained string `json:"tracking_obtained"`
	StatusUpdated    string `json:"status_updated"`
}

type SellerSelectionCriteria struct {
	Prime bool `json:"prime"`
}

type OrderResponse struct {
	RequestId        string            `json:"request_id"`
	Type             string            `json:"_type"`
	Code             string            `json:"code"`
	Data             ErrorDataResponse `json:"data"`
	ErrorMessage     string            `json:"message"`
	PriceComponents  PriceComponents   `json:"price_components"`
	MerchantOrderIds []MerchantOrderId `json:"merchant_order_ids"`
	Tracking         []Tracking        `json:"tracking"`
	Request          OrderRequest      `json:"request"`
}

type PriceComponents struct {
	Shipping int `json:"shipping"`
	Subtotal int `json:"subtotal"`
	Tax      int `json:"tax"`
	Total    int `json:"total"`
}

type MerchantOrderId struct {
	MerchantOrderId string    `json:"merchant_order_id"`
	Merchant        string    `json:"merchant"`
	Account         string    `json:"account"`
	PlacedAt        time.Time `json:"placed_at"`
}

type Tracking struct {
	MerchantOrderId string    `json:"merchant_order_id"`
	ObtainedAt      time.Time `json:"obtained_at"`
	Carrier         string    `json:"carrier"`
	TrackingNumber  string    `json:"tracking_number"`
	ProductIds      []string  `json:"product_ids"`
	TrackingURL     string    `json:"tracking_url"`
}

type ProductOffersResponse struct {
	Code     string            `json:"code"`
	Data     ErrorDataResponse `json:"data"`
	Status   string            `json:"status"`
	Retailer string            `json:"retailer"`
	Offers   []ProductOffer    `json:"offers"`
}

type ProductOffer struct {
	Available            bool             `json:"available"`
	Addon                bool             `json:"addon"`
	Condition            string           `json:"condition"`
	ShippingOptions      []ShippingOption `json:"shipping_options"`
	HandlingDays         HandlingDays     `json:"handling_days"`
	PrimeOnly            bool             `json:"prime_only"`
	MarketplaceFulfilled bool             `json:"marketplace_fulfilled"`
	Currency             string           `json:"currency"`
	Seller               Seller           `json:"seller"`
	BuyBoxWinner         bool             `json:"buy_box_winner"`
	International        bool             `json:"international"`
	OfferId              string           `json:"offer_id"`
	Price                int              `json:"price"`
}

type ShippingOption struct {
	Price int `json:"price"`
}

type HandlingDays struct {
	Max int `json:"max"`
	Min int `json:"min"`
}

type Seller struct {
	NumRatings      int    `json:"num_ratings"`
	PercentPositive int    `json:"percent_positive"`
	FirstParty      bool   `json:"first_party"`
	Name            string `json:"name"`
	Id              string `json:"id"`
}

type ProductDetailsResponse struct {
	Code               string              `json:"code"`
	Data               ErrorDataResponse   `json:"data"`
	Status             string              `json:"status"`
	ProductDescription string              `json:"product_description"`
	PostDescription    string              `json:"post_description"`
	Retailer           string              `json:"retailer"`
	Epids              []ExternalProductId `json:"epids"`
	ProductDetails     []string            `json:"product_details"`
	Title              string              `json:"title"`
	VariantSpecifics   []VariantSpecific   `json:"variant_specifics"`
	ProductId          string              `json:"product_id"`
	MainImage          string              `json:"main_image"`
	Brand              string              `json:"brand"`
	MPN                string              `json:"mpn"`
	Images             []string            `json:"images"`
	FeatureBullets     []string            `json:"feature_bullets"`
}

type ExternalProductId struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

type VariantSpecific struct {
	Dimension string `json:"dimension"`
	Value     string `json:"value"`
}

type ErrorDataResponse struct {
	Message         string           `json:"message"`
	ValidatorErrors []ValidatorError `json:"validator_errors"`
	AllVariants     []Variant        `json:"all_variants"`
}

type ValidatorError struct {
	Message string `json:"message"`
	Path    string `json:"path"`
	Value   string `json:"value"`
}

type Variant struct {
	VariantSpecifics []VariantSpecific `json:"variant_specifics"`
	ProductId        string            `json:"product_id"`
}

type ProductOptions struct {
	MaxAge    int           `json:"max_age"`
	Priority  int           `json:"priority"`
	NewerThan time.Time     `json:"newer_than"`
	Timeout   time.Duration `json:"timeout"`
}

type ZincError struct {
	ErrorMessage string            `json:"error"`
	Code         string            `json:"code"`
	Data         ErrorDataResponse `json:"data"`
}

func (z ZincError) Error() string {
	return z.ErrorMessage
}

func SimpleError(errorStr string) ZincError {
	return ZincError{ErrorMessage: errorStr}
}

func (z Zinc) GetProductInfo(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, *ProductDetailsResponse, error) {
	offersChan := make(chan *ProductOffersResponse, 1)
	detailsChan := make(chan *ProductDetailsResponse, 1)
	errorsChan := make(chan error, 2)

	go func() {
		offers, err := z.GetProductOffers(productId, retailer, options)
		errorsChan <- err
		offersChan <- offers
	}()

	go func() {
		details, err := z.GetProductDetails(productId, retailer, options)
		errorsChan <- err
		detailsChan <- details
	}()

	offers := <-offersChan
	details := <-detailsChan
	for i := 0; i < 2; i++ {
		err := <-errorsChan
		if err != nil {
			return nil, nil, err
		}
	}
	return offers, details, nil
}

func (z Zinc) SendOrder(order OrderRequest) (*OrderResponse, error) {
	requestPath := fmt.Sprintf("%v/orders", z.ZincBaseURL)
	body := new(bytes.Buffer)
	if err := json.NewEncoder(body).Encode(order); err != nil {
		return nil, SimpleError(err.Error())
	}
	var resp OrderResponse
	if err := z.SendRequest("POST", requestPath, body, time.Duration(time.Second*30), &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	return &resp, nil
}

func (z Zinc) GetProductOffers(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	values.Set("version", "2")
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	requestPath := fmt.Sprintf("%v/products/%v/offers?%v", z.ZincBaseURL, productId, values.Encode())

	var resp ProductOffersResponse
	if err := z.SendRequest("GET", requestPath, nil, options.Timeout, &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	if resp.Status == "failed" {
		msg := fmt.Sprintf("Zinc API returned status 'failed' data=%+v", resp.Data)
		return &resp, ZincError{Code: resp.Code, ErrorMessage: msg, Data: resp.Data}
	}
	return &resp, nil
}

func (z Zinc) GetProductDetails(productId string, retailer Retailer, options ProductOptions) (*ProductDetailsResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	if options.Priority != 0 {
		values.Set("priority", strconv.Itoa(options.Priority))
	}
	requestPath := fmt.Sprintf("%v/products/%v?%v", z.ZincBaseURL, productId, values.Encode())

	var resp ProductDetailsResponse
	if err := z.SendRequest("GET", requestPath, nil, options.Timeout, &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	if resp.Status == "failed" {
		msg := fmt.Sprintf("Zinc API returned status 'failed' data=%+v", resp.Data)
		return &resp, ZincError{Code: resp.Code, ErrorMessage: msg, Data: resp.Data}
	}
	return &resp, nil
}

func cleanRespBody(respBody []byte) []byte {
	str := string(respBody)
	i := strings.Index(str, "HTTP/1.1 200 OK")
	if i == -1 {
		return respBody
	}
	return []byte(str[:i])
}

func (z Zinc) SendRequest(method, requestPath string, body io.Reader, timeout time.Duration, resp interface{}) error {
	httpReq, err := http.NewRequest(method, requestPath, body)
	if err != nil {
		return err
	}
	httpReq.SetBasicAuth(z.ClientToken, "")
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr, Timeout: timeout}
	httpResp, err := client.Do(httpReq)
	if err != nil {
		return err
	}
	defer httpResp.Body.Close()
	respBody, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		return err
	}
	cleanedBody := cleanRespBody(respBody)
	if err := json.Unmarshal(cleanedBody, resp); err != nil {
		log.Printf("[Golangsdk] Unable to unmarshal response request_path=%v body=%v", requestPath, string(cleanedBody))
		return SimpleError(err.Error())
	}
	return nil
}
<commit_msg>Update OrderResponse object.<commit_after>package golangsdk

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	zincBaseURL = "https://api.zinc.io/v1"
)

type Retailer string

const (
	Amazon     Retailer = "amazon"
	AmazonUK   Retailer = "amazon_uk"
	AmazonCA   Retailer = "amazon_ca"
	AmazonMX   Retailer = "amazon_mx"
	Walmart    Retailer = "walmart"
	Aliexpress Retailer = "aliexpress"
)

var DefaultProductOptions = ProductOptions{
	Timeout: time.Duration(time.Second * 90),
}

type Zinc struct {
	ClientToken string
	ZincBaseURL string
}

func GetRetailer(retailer string) (Retailer, error) {
	switch retailer {
	case "amazon":
		return Amazon, nil
	case "amazon_uk":
		return AmazonUK, nil
	case "amazon_ca":
		return AmazonCA, nil
	case "amazon_mx":
		return AmazonMX, nil
	case "walmart":
		return Walmart, nil
	case "aliexpress":
		return Aliexpress, nil
	default:
		return Amazon, fmt.Errorf("Invalid retailer string")
	}
}

func NewZinc(clientToken string) (*Zinc, error) {
	z := Zinc{
		ClientToken: clientToken,
		ZincBaseURL: zincBaseURL,
	}
	return &z, nil
}

type OrderRequest struct {
	Retailer            Retailer             `json:"retailer"`
	Products            []Product            `json:"products"`
	ShippingMethod      string               `json:"shipping_method,omitempty"`
	Shipping            *Shipping            `json:"shipping,omitempty"`
	ShippingAddress     *Address             `json:"shipping_address"`
	BillingAddress      *Address             `json:"billing_address,omitempty"`
	PaymentMethod       *PaymentMethod       `json:"payment_method,omitempty"`
	RetailerCredentials *RetailerCredentials `json:"retailer_credentials,omitempty"`
	GiftMessage         string               `json:"gift_message,omitempty"`
	IsGift              bool                 `json:"is_gift"`
	MaxPrice            int                  `json:"max_price"`
	Webhooks            *Webhooks            `json:"webhooks,omitempty"`
	Bundled             bool                 `json:"bundled"`
	Addax               bool                 `json:"addax"`
}

type Product struct {
	ProductId               string                   `json:"product_id"`
	Quantity                int                      `json:"quantity"`
	SellerSelectionCriteria *SellerSelectionCriteria `json:"seller_selection_criteria,omitempty"`
}

type Shipping struct {
	OrderBy  string `json:"order_by,omitempty"`
	MaxDays  int    `json:"max_days"`
	MaxPrice int    `json:"max_price"`
}

type Address struct {
	FirstName    string `json:"first_name"`
	LastName     string `json:"last_name"`
	AddressLine1 string `json:"address_line1"`
	AddressLine2 string `json:"address_line2"`
	ZipCode      string `json:"zip_code"`
	City         string `json:"city"`
	State        string `json:"state"`
	Country      string `json:"country"`
	PhoneNumber  string `json:"phone_number"`
}

type PaymentMethod struct {
	NameOnCard      string `json:"name_on_card,omitempty"`
	Number          string `json:"number,omitempty"`
	SecurityCode    string `json:"security_code,omitempty"`
	ExpirationMonth int    `json:"expiration_month,omitempty"`
	ExpirationYear  int    `json:"expiration_year,omitempty"`
	UseGift         bool   `json:"use_gift"`
}

type RetailerCredentials struct {
	Email            string `json:"email"`
	Password         string `json:"password"`
	VerificationCode string `json:"verification_code,omitempty"`
}

type Webhooks struct {
	RequestSucceeded string `json:"request_succeeded"`
	RequestFailed    string `json:"request_failed"`
	TrackingObtained string `json:"tracking_obtained"`
	StatusUpdated    string `json:"status_updated"`
}

type SellerSelectionCriteria struct {
	Prime bool `json:"prime"`
}

type OrderResponse struct {
	RequestId        string             `json:"request_id"`
	Type             string             `json:"_type"`
	Code             string             `json:"code"`
	Data             *ErrorDataResponse `json:"data,omitempty"`
	ErrorMessage     string             `json:"message"`
	PriceComponents  *PriceComponents   `json:"price_components,omitempty"`
	MerchantOrderIds []MerchantOrderId  `json:"merchant_order_ids"`
	Tracking         []Tracking         `json:"tracking"`
	Request          *OrderRequest      `json:"request,omitempty"`
}

type PriceComponents struct {
	Shipping int `json:"shipping"`
	Subtotal int `json:"subtotal"`
	Tax      int `json:"tax"`
	Total    int `json:"total"`
}

type MerchantOrderId struct {
	MerchantOrderId string    `json:"merchant_order_id"`
	Merchant        string    `json:"merchant"`
	Account         string    `json:"account"`
	PlacedAt        time.Time `json:"placed_at"`
}

type Tracking struct {
	MerchantOrderId string    `json:"merchant_order_id"`
	ObtainedAt      time.Time `json:"obtained_at"`
	Carrier         string    `json:"carrier"`
	TrackingNumber  string    `json:"tracking_number"`
	ProductIds      []string  `json:"product_ids"`
	TrackingURL     string    `json:"tracking_url"`
}

type ProductOffersResponse struct {
	Code     string            `json:"code"`
	Data     ErrorDataResponse `json:"data"`
	Status   string            `json:"status"`
	Retailer string            `json:"retailer"`
	Offers   []ProductOffer    `json:"offers"`
}

type ProductOffer struct {
	Available            bool             `json:"available"`
	Addon                bool             `json:"addon"`
	Condition            string           `json:"condition"`
	ShippingOptions      []ShippingOption `json:"shipping_options"`
	HandlingDays         HandlingDays     `json:"handling_days"`
	PrimeOnly            bool             `json:"prime_only"`
	MarketplaceFulfilled bool             `json:"marketplace_fulfilled"`
	Currency             string           `json:"currency"`
	Seller               Seller           `json:"seller"`
	BuyBoxWinner         bool             `json:"buy_box_winner"`
	International        bool             `json:"international"`
	OfferId              string           `json:"offer_id"`
	Price                int              `json:"price"`
}

type ShippingOption struct {
	Price int `json:"price"`
}

type HandlingDays struct {
	Max int `json:"max"`
	Min int `json:"min"`
}

type Seller struct {
	NumRatings      int    `json:"num_ratings"`
	PercentPositive int    `json:"percent_positive"`
	FirstParty      bool   `json:"first_party"`
	Name            string `json:"name"`
	Id              string `json:"id"`
}

type ProductDetailsResponse struct {
	Code               string              `json:"code"`
	Data               ErrorDataResponse   `json:"data"`
	Status             string              `json:"status"`
	ProductDescription string              `json:"product_description"`
	PostDescription    string              `json:"post_description"`
	Retailer           string              `json:"retailer"`
	Epids              []ExternalProductId `json:"epids"`
	ProductDetails     []string            `json:"product_details"`
	Title              string              `json:"title"`
	VariantSpecifics   []VariantSpecific   `json:"variant_specifics"`
	ProductId          string              `json:"product_id"`
	MainImage          string              `json:"main_image"`
	Brand              string              `json:"brand"`
	MPN                string              `json:"mpn"`
	Images             []string            `json:"images"`
	FeatureBullets     []string            `json:"feature_bullets"`
}

type ExternalProductId struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

type VariantSpecific struct {
	Dimension string `json:"dimension"`
	Value     string `json:"value"`
}

type ErrorDataResponse struct {
	Message         string           `json:"message"`
	ValidatorErrors []ValidatorError `json:"validator_errors"`
	AllVariants     []Variant        `json:"all_variants"`
}

type ValidatorError struct {
	Message string `json:"message"`
	Path    string `json:"path"`
	Value   string `json:"value"`
}

type Variant struct {
	VariantSpecifics []VariantSpecific `json:"variant_specifics"`
	ProductId        string            `json:"product_id"`
}

type ProductOptions struct {
	MaxAge    int           `json:"max_age"`
	Priority  int           `json:"priority"`
	NewerThan time.Time     `json:"newer_than"`
	Timeout   time.Duration `json:"timeout"`
}

type ZincError struct {
	ErrorMessage string            `json:"error"`
	Code         string            `json:"code"`
	Data         ErrorDataResponse `json:"data"`
}

func (z ZincError) Error() string {
	return z.ErrorMessage
}

func SimpleError(errorStr string) ZincError {
	return ZincError{ErrorMessage: errorStr}
}

func (z Zinc) GetProductInfo(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, *ProductDetailsResponse, error) {
	offersChan := make(chan *ProductOffersResponse, 1)
	detailsChan := make(chan *ProductDetailsResponse, 1)
	errorsChan := make(chan error, 2)

	go func() {
		offers, err := z.GetProductOffers(productId, retailer, options)
		errorsChan <- err
		offersChan <- offers
	}()

	go func() {
		details, err := z.GetProductDetails(productId, retailer, options)
		errorsChan <- err
		detailsChan <- details
	}()

	offers := <-offersChan
	details := <-detailsChan
	for i := 0; i < 2; i++ {
		err := <-errorsChan
		if err != nil {
			return nil, nil, err
		}
	}
	return offers, details, nil
}

func (z Zinc) SendOrder(order OrderRequest) (*OrderResponse, error) {
	requestPath := fmt.Sprintf("%v/orders", z.ZincBaseURL)
	body := new(bytes.Buffer)
	if err := json.NewEncoder(body).Encode(order); err != nil {
		return nil, SimpleError(err.Error())
	}
	var resp OrderResponse
	if err := z.SendRequest("POST", requestPath, body, time.Duration(time.Second*30), &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	return &resp, nil
}

func (z Zinc) GetProductOffers(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	values.Set("version", "2")
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	requestPath := fmt.Sprintf("%v/products/%v/offers?%v", z.ZincBaseURL, productId, values.Encode())

	var resp ProductOffersResponse
	if err := z.SendRequest("GET", requestPath, nil, options.Timeout, &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	if resp.Status == "failed" {
		msg := fmt.Sprintf("Zinc API returned status 'failed' data=%+v", resp.Data)
		return &resp, ZincError{Code: resp.Code, ErrorMessage: msg, Data: resp.Data}
	}
	return &resp, nil
}

func (z Zinc) GetProductDetails(productId string, retailer Retailer, options ProductOptions) (*ProductDetailsResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	if options.Priority != 0 {
		values.Set("priority", strconv.Itoa(options.Priority))
	}
	requestPath := fmt.Sprintf("%v/products/%v?%v", z.ZincBaseURL, productId, values.Encode())

	var resp ProductDetailsResponse
	if err := z.SendRequest("GET", requestPath, nil, options.Timeout, &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	if resp.Status == "failed" {
		msg := fmt.Sprintf("Zinc API returned status 'failed' data=%+v", resp.Data)
		return &resp, ZincError{Code: resp.Code, ErrorMessage: msg, Data: resp.Data}
	}
	return &resp, nil
}

func cleanRespBody(respBody []byte) []byte {
	str := string(respBody)
	i := strings.Index(str, "HTTP/1.1 200 OK")
	if i == -1 {
		return respBody
	}
	return []byte(str[:i])
}

func (z Zinc) SendRequest(method, requestPath string, body io.Reader, timeout time.Duration, resp interface{}) error {
	httpReq, err := http.NewRequest(method, requestPath, body)
	if err != nil {
		return err
	}
	httpReq.SetBasicAuth(z.ClientToken, "")
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr, Timeout: timeout}
	httpResp, err := client.Do(httpReq)
	if err != nil {
		return err
	}
	defer httpResp.Body.Close()
	respBody, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		return err
	}
	cleanedBody := cleanRespBody(respBody)
	if err := json.Unmarshal(cleanedBody, resp); err != nil {
		log.Printf("[Golangsdk] Unable to unmarshal response request_path=%v body=%v", requestPath, string(cleanedBody))
		return SimpleError(err.Error())
	}
	return nil
}
<|endoftext|>


<commit_before>package gosseract

import (
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"github.com/nu7hatch/gouuid"
)

type AnywayArgs struct {
	SourcePath  string
	Destination string
}

var (
	TMPDIR  = "/tmp"
	OUTEXT  = ".txt"
	COMMAND = "tesseract"
	VERSION = "0.0.1"
)

func Greeting() string {
	return "Hello,Gosseract!"
}

// func `gosseract.Anyway` can OCR from multi args
func Anyway(args AnywayArgs) string {
	// 最終的な返り値
	out := ""
	// tesseractが標準出力に対応してるハズ
	// tesseractのバージョンを見るようなメソッドを用意しないとアカンなこれ
	if args.Destination == "" {
		args.Destination = genTmpFilePath()
	}
	// tesseractコマンドを実行
	command := exec.Command(COMMAND, args.SourcePath, args.Destination)
	e := command.Run()
	if e != nil {
		panic(e)
	}
	// 出力を読む
	// tesseractの出力はコマンドラインの第二引数に.txtを付けたものに置かれる
	fn := args.Destination + OUTEXT
	f, _ := os.OpenFile(fn, 1, 1)
	buf, _ := ioutil.ReadFile(f.Name())
	out = string(buf)

	_ = os.Remove(f.Name())

	return out
}

func getTesseractVersion() string {
	command := exec.Command(COMMAND, "--version")
	var stderr bytes.Buffer
	command.Stderr = &stderr //謎に標準エラーで来るw
	e := command.Run()
	if e != nil {
		panic(e)
	}
	// なんかクズい
	tesseractInfo := strings.Split(stderr.String(), " ")[1]
	return strings.TrimRight(tesseractInfo, "\n")
}

/**
 * 利用可能な言語の一覧を取得する
 */
func getAvailableLanguages() []string {
	command := exec.Command(COMMAND, "--list-langs")
	var stderr bytes.Buffer
	command.Stderr = &stderr //謎に標準エラーで来るw
	e := command.Run()
	if e != nil {
		panic(e)
	}
	langs := strings.Split(stderr.String(), "\n")
	langs = langs[1 : len(langs)-1]
	return langs
}

/**
 * ソースファイルパスと
 * オプションアーギュメントのスライスを受け取り
 * OCRしたものを返す
 * ファイル操作などを隠蔽する
 */
func execute(source string, args []string) string {
	_args := []string{}
	_args = append(_args, source)

	dest := genTmpFilePath()

	_args = append(_args, dest)
	for _, a := range args {
		_args = append(_args, a)
	}
	_ = _exec(COMMAND, _args)

	// 出力を読む
	// tesseractの出力はコマンドラインの第二引数に.txtを付けたものに置かれる
	// TODO4: DRY
	fn := dest + OUTEXT

	f, _ := os.OpenFile(fn, 1, 1)
	buf, _ := ioutil.ReadFile(f.Name())
	out := string(buf)

	_ = os.Remove(fn)

	return out
}

func tesseractInstalled() bool {
	out := _exec("which", []string{"tesseract"})
	if out == "" {
		return false
	}
	return true
}

/**
 * 汎用: コマンドを実行する
 */
func _exec(command string, args []string) string {
	cmd := _generateCommand(command, args)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	_ = cmd.Run()
	if stdout.String() != "" {
		return stdout.String()
	}
	return stderr.String()
}

/**
 * TODO#3: こんなラッパーメソッド作らんとアカンのクソじゃね？
 */
func _generateCommand(_command string, args []string) *exec.Cmd {
	if len(args) == 0 {
		return exec.Command(_command)
	}
	if len(args) == 1 {
		return exec.Command(_command, args[0])
	}
	if len(args) == 2 {
		return exec.Command(_command, args[0], args[1])
	}
	if len(args) == 3 {
		return exec.Command(_command, args[0], args[1], args[2])
	}
	if len(args) == 4 {
		return exec.Command(_command, args[0], args[1], args[2], args[3])
	}
	if len(args) == 5 {
		return exec.Command(_command, args[0], args[1], args[2], args[3], args[4])
	}
	return exec.Command(_command)
}

func genTmpFilePath() string {
	id, _ := uuid.NewV4()
	return TMPDIR + "/" + id.String()
}
<commit_msg>do some clean ups<commit_after>package gosseract

import (
	"bytes"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"github.com/nu7hatch/gouuid"
)

type AnywayArgs struct {
	SourcePath  string
	Destination string
}

var (
	TMPDIR  = "/tmp"
	OUTEXT  = ".txt"
	COMMAND = "tesseract"
	VERSION = "0.0.1"
)

func Greeting() string {
	return "Hello,Gosseract!"
}

// func `gosseract.Anyway` can OCR from multi args
func Anyway(args AnywayArgs) string {
	// 最終的な返り値
	out := ""
	// tesseractが標準出力に対応してるハズ
	// tesseractのバージョンを見るようなメソッドを用意しないとアカンなこれ
	if args.Destination == "" {
		args.Destination = genTmpFilePath()
	}
	// tesseractコマンドを実行
	command := exec.Command(COMMAND, args.SourcePath, args.Destination)
	e := command.Run()
	if e != nil {
		panic(e)
	}
	// 出力を読む
	// tesseractの出力はコマンドラインの第二引数に.txtを付けたものに置かれる
	fn := args.Destination + OUTEXT
	f, _ := os.OpenFile(fn, 1, 1)
	buf, _ := ioutil.ReadFile(f.Name())
	out = string(buf)

	_ = os.Remove(f.Name())

	return out
}

func getTesseractVersion() string {
	command := exec.Command(COMMAND, "--version")
	var stderr bytes.Buffer
	command.Stderr = &stderr //謎に標準エラーで来るw
	e := command.Run()
	if e != nil {
		panic(e)
	}
	// なんかクズい
	tesseractInfo := strings.Split(stderr.String(), " ")[1]
	return strings.TrimRight(tesseractInfo, "\n")
}

/**
 * 利用可能な言語の一覧を取得する
 */
func getAvailableLanguages() []string {
	command := exec.Command(COMMAND, "--list-langs")
	var stderr bytes.Buffer
	command.Stderr = &stderr //謎に標準エラーで来るw
	e := command.Run()
	if e != nil {
		panic(e)
	}
	langs := strings.Split(stderr.String(), "\n")
	return langs[1 : len(langs)-1]
}

/**
 * ソースファイルパスと
 * オプションアーギュメントのスライスを受け取り
 * OCRしたものを返す
 * ファイル操作などを隠蔽する
 */
func execute(source string, args []string) string {
	_args := []string{}
	_args = append(_args, source)

	dest := genTmpFilePath()

	_args = append(_args, dest)
	for _, a := range args {
		_args = append(_args, a)
	}
	_ = _exec(COMMAND, _args)

	// 出力を読む
	// tesseractの出力はコマンドラインの第二引数に.txtを付けたものに置かれる
	// TODO4: DRY
	fn := dest + OUTEXT

	f, _ := os.OpenFile(fn, 1, 1)
	buf, _ := ioutil.ReadFile(f.Name())
	out := string(buf)

	_ = os.Remove(fn)

	return out
}

func tesseractInstalled() bool {
	found := _exec("which", []string{"tesseract"})

	return found != ""
}

/**
 * 汎用: コマンドを実行する
 */
func _exec(command string, args []string) string {
	cmd := exec.Command(command, args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	_ = cmd.Run()
	if stdout.String() != "" {
		return stdout.String()
	}
	return stderr.String()
}

func genTmpFilePath() string {
	id, _ := uuid.NewV4()
	return TMPDIR + "/" + id.String()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/abates/disgo"
	"github.com/gorilla/mux"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
)

type appHandler func(http.ResponseWriter, *http.Request) (interface{}, error)

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	i, err := fn(w, r)
	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(422)
		if err := json.NewEncoder(w).Encode(err); err != nil {
			panic(err)
		}
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err = json.NewEncoder(w).Encode(i); err != nil {
		panic(err)
	}
}

func decode(r *http.Request, limit int64, v interface{}) error {
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, limit))
	if err != nil {
		return err
	}

	if err := r.Body.Close(); err != nil {
		return err
	}

	return json.Unmarshal(body, v)
}

type searchCriteria struct {
	Hash     disgo.PHash `json:"hash"`
	Distance uint        `json:"distance"`
}

func Search(w http.ResponseWriter, r *http.Request) (interface{}, error) {
	var c searchCriteria
	if err := decode(r, 10240, &c); err != nil {
		return nil, err
	}
	return disgo.SearchByHash(c.Hash, c.Distance)
}

type imageInfo struct {
	Hash     disgo.PHash `json:"hash"`
	Location string      `json:"location"`
}

func Add(w http.ResponseWriter, r *http.Request) (interface{}, error) {
	var i imageInfo
	if err := decode(r, 10240, &i); err != nil {
		return nil, err
	}

	if err := disgo.AddLocation(i.Location, i.Hash); err != nil {
		return nil, err
	}

	return "Added " + i.Location + " to the database", nil
}

func Show(w http.ResponseWriter, r *http.Request) (interface{}, error) {
	vars := mux.Vars(r)
	hash, err := strconv.ParseUint(vars["hash"], 10, 64)
	if err != nil {
		return nil, err
	}
	return disgo.Find(disgo.PHash(hash))
}
<commit_msg>Moved data structures to the API file<commit_after>package main

import (
	"encoding/json"
	"github.com/abates/disgo"
	"github.com/gorilla/mux"
	"io"
	"io/ioutil"
	"net/http"
	"strconv"
)

type appHandler func(http.ResponseWriter, *http.Request) (interface{}, error)

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	i, err := fn(w, r)
	if err != nil {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(422)
		if err := json.NewEncoder(w).Encode(err); err != nil {
			panic(err)
		}
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err = json.NewEncoder(w).Encode(i); err != nil {
		panic(err)
	}
}

func decode(r *http.Request, limit int64, v interface{}) error {
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, limit))
	if err != nil {
		return err
	}

	if err := r.Body.Close(); err != nil {
		return err
	}

	return json.Unmarshal(body, v)
}

func Search(w http.ResponseWriter, r *http.Request) (interface{}, error) {
	var c disgo.SearchCriteria
	if err := decode(r, 10240, &c); err != nil {
		return nil, err
	}
	return disgo.SearchByHash(c.Hash, c.Distance)
}

func Add(w http.ResponseWriter, r *http.Request) (interface{}, error) {
	var i disgo.ImageInfo
	if err := decode(r, 10240, &i); err != nil {
		return nil, err
	}

	if err := disgo.AddLocation(i.Location, i.Hash); err != nil {
		return nil, err
	}

	return "Added " + i.Location + " to the database", nil
}

func Show(w http.ResponseWriter, r *http.Request) (interface{}, error) {
	vars := mux.Vars(r)
	hash, err := strconv.ParseUint(vars["hash"], 10, 64)
	if err != nil {
		return nil, err
	}
	return disgo.Find(disgo.PHash(hash))
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"fmt"
	"net/http"
)

var (
	// indexTemplate is the go template for the index page.
	indexTemplate = `<!doctype html>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
    <link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
    <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
    <link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
    <link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
    <link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
    <link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
    <link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
    <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
    <link rel="icon" type="image/png" href="/android-chrome-192x192.png" sizes="192x192">
    <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
    <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
    <link rel="manifest" href="/manifest.json">
    <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#272f32">
    <meta name="apple-mobile-web-app-title" content="s">
    <meta name="application-name" content="s">
    <meta name="msapplication-TileColor" content="#272f32">
    <meta name="msapplication-TileImage" content="/mstile-144x144.png">
    <meta name="theme-color" content="#272f32">

    <title>s</title>

    <form name="search" action="/search" method="POST">

        <select id="provider" name="provider" tabindex="2">
{{range .Providers}}           <option{{.|Label}}{{.|Selected}}>{{.}}</option>
{{end}}        </select>

        <select id="tag" name="tag" tabindex="3">
{{range .Tags}}           <option{{.|Label}}>{{.}}</option>
{{end}}        </select>

      <input class="input" type="text" name="q" tabindex="1" placeholder="{{.Placeholder}}" autofocus required />

      <input type="submit" value="[ s ]" tabindex="4" />
    </form>

`

	// indexCSS is the css for the main index page.
	indexCSS = `*{font-family:"Tahoma","Geneva",sans-serif;font-size:14pt;text-align:center}
body{margin:0;padding:2em;background-color:#272F32;color:#DAEAEF}
#rightCorner{position:absolute;top:1.5em;right:1.5em;text-align:right}
a,a:visited{color:#FFF;font-size:.8em}
a:active,a:hover{color:#9DBDC6}
select{text-align:left;margin-bottom:.5em}
option{text-align:left}
form{margin-top:10em}
input[type=text]{width:100%;max-width:450px;border-bottom:1px solid #DAEAEF}
input{color:#DAEAEF;background-color:#272F32;display:inline-block;padding:0 0 .5em;margin:0 0 .5em;border:0;outline:none;border-radius:0;-webkit-border-radius:0;-webkit-appearance:none;appearance:none;-moz-appearance:none}
input:required{box-shadow:none}
input:invalid{box-shadow:none}
input[type=submit]{background-color:#272F32;font-size:2em;transition:color .2s ease}
input[type=submit]:active,input[type=submit]:hover{color:#9DBDC6}
input::-webkit-input-placeholder{font-style:italic}
input::-moz-placeholder{font-style:italic}
input:-moz-placeholder{font-style:italic}
input:-ms-input-placeholder{font-style:italic}
input:focus::-webkit-input-placeholder{color:transparent}
input:focus::-moz-placeholder{color:transparent}
input:focus:-moz-placeholder{color:transparent}
input:focus:-ms-input-placeholder{color:transparent}`
)

// indexJS is the javascript for the main index page.
func indexJS(defaultProvider string) string {
	var b bytes.Buffer

	b.WriteString(`function sInit() {
    "use strict";
    var searchForm = document.forms.search;
    searchForm.onsubmit = function () {
        var v = searchForm.q.value;
        if (v === null || v === "") {
            return false;
        }
    };
    var tag = document.getElementById("tag");
    var provider = document.getElementById("provider");
    tag.onchange = function () {
        if (tag.value === "") {
            if (provider.value === "") {
                provider.value = "`)

	b.WriteString(defaultProvider)
	b.WriteString(`";
            }
        } else {
            provider.value = "";
        }
    };
}
sInit();`)

	return b.String()
}

// searchTemplate is the html used for tag based searches.
func searchTemplate(uris []string, w http.ResponseWriter) {
	var b bytes.Buffer

	b.WriteString(`<!doctype html>

    <meta charset="UTF-8">
    <title>s</title>

`)

	w.Header().Set("Content-Type", "text/html")
	w.Write(b.Bytes())
}

// SearchJS is the javascript used for tag based searches.
func searchJS(uris []string, b *bytes.Buffer) {
	b.WriteString(`function sInit() {
    "use strict";
    var urls = [];`)
	for _, u := range uris {
		b.WriteString(fmt.Sprintf("\n    urls.push(window.open(%q));", u))
	}
	b.WriteString(`
    var goBack = function () {
        window.history.back();
    };
    try {
        urls[urls.length - 1].focus();
        goBack();
    } catch (e) {
        alert("Pop-up Blocker is enabled! Please add this site to your exception list.");
        setTimeout(goBack, 5000);
    }
}
sInit();`)
}
<commit_msg>Cleanup markup for index page and change main form action to get to work around bug with the omnibox in chrome<commit_after>package server

import (
	"bytes"
	"fmt"
	"net/http"
)

var (
	// indexTemplate is the go template for the index page.
	indexTemplate = `<!doctype html>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
    <link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
    <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
    <link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
    <link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
    <link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
    <link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
    <link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
    <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
    <link rel="icon" type="image/png" href="/android-chrome-192x192.png" sizes="192x192">
    <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
    <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
    <link rel="manifest" href="/manifest.json">
    <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#272f32">
    <meta name="apple-mobile-web-app-title" content="s">
    <meta name="application-name" content="s">
    <meta name="msapplication-TileColor" content="#272f32">
    <meta name="msapplication-TileImage" content="/mstile-144x144.png">
    <meta name="theme-color" content="#272f32">

    <title>s</title>

    <form name="search" action="/search" method="get">

        <select id="provider" name="provider" tabindex="2">
{{range .Providers}}           <option{{.|Label}}{{.|Selected}}>{{.}}</option>
{{end}}        </select>

        <select id="tag" name="tag" tabindex="3">
{{range .Tags}}           <option{{.|Label}}>{{.}}</option>
{{end}}        </select>

      <input class="input" type="text" name="q" tabindex="1" placeholder="{{.Placeholder}}" autofocus required>

      <input type="submit" value="[ s ]" tabindex="4">
    </form>

`

	// indexCSS is the css for the main index page.
	indexCSS = `*{font-family:"Tahoma","Geneva",sans-serif;font-size:14pt;text-align:center}
body{margin:0;padding:2em;background-color:#272F32;color:#DAEAEF}
#rightCorner{position:absolute;top:1.5em;right:1.5em;text-align:right}
a,a:visited{color:#FFF;font-size:.8em}
a:active,a:hover{color:#9DBDC6}
select{text-align:left;margin-bottom:.5em}
option{text-align:left}
form{margin-top:10em}
input[type=text]{width:100%;max-width:450px;border-bottom:1px solid #DAEAEF}
input{color:#DAEAEF;background-color:#272F32;display:inline-block;padding:0 0 .5em;margin:0 0 .5em;border:0;outline:none;border-radius:0;-webkit-border-radius:0;-webkit-appearance:none;appearance:none;-moz-appearance:none}
input:required{box-shadow:none}
input:invalid{box-shadow:none}
input[type=submit]{background-color:#272F32;font-size:2em;transition:color .2s ease}
input[type=submit]:active,input[type=submit]:hover{color:#9DBDC6}
input::-webkit-input-placeholder{font-style:italic}
input::-moz-placeholder{font-style:italic}
input:-moz-placeholder{font-style:italic}
input:-ms-input-placeholder{font-style:italic}
input:focus::-webkit-input-placeholder{color:transparent}
input:focus::-moz-placeholder{color:transparent}
input:focus:-moz-placeholder{color:transparent}
input:focus:-ms-input-placeholder{color:transparent}`
)

// indexJS is the javascript for the main index page.
func indexJS(defaultProvider string) string {
	var b bytes.Buffer

	b.WriteString(`function sInit() {
    "use strict";
    var searchForm = document.forms.search;
    searchForm.onsubmit = function () {
        var v = searchForm.q.value;
        if (v === null || v === "") {
            return false;
        }
    };
    var tag = document.getElementById("tag");
    var provider = document.getElementById("provider");
    tag.onchange = function () {
        if (tag.value === "") {
            if (provider.value === "") {
                provider.value = "`)

	b.WriteString(defaultProvider)
	b.WriteString(`";
            }
        } else {
            provider.value = "";
        }
    };
}
sInit();`)

	return b.String()
}

// searchTemplate is the html used for tag based searches.
func searchTemplate(uris []string, w http.ResponseWriter) {
	var b bytes.Buffer

	b.WriteString(`<!doctype html>

    <meta charset="UTF-8">
    <title>s</title>

`)

	w.Header().Set("Content-Type", "text/html")
	w.Write(b.Bytes())
}

// SearchJS is the javascript used for tag based searches.
func searchJS(uris []string, b *bytes.Buffer) {
	b.WriteString(`function sInit() {
    "use strict";
    var urls = [];`)
	for _, u := range uris {
		b.WriteString(fmt.Sprintf("\n    urls.push(window.open(%q));", u))
	}
	b.WriteString(`
    var goBack = function () {
        window.history.back();
    };
    try {
        urls[urls.length - 1].focus();
        goBack();
    } catch (e) {
        alert("Pop-up Blocker is enabled! Please add this site to your exception list.");
        setTimeout(goBack, 5000);
    }
}
sInit();`)
}
<|endoftext|>


<commit_before>package service

import (
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/ninjasphere/app-presets/model"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
	nmodel "github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/rpc"
	"strings"
	"time"
)

const defaultTimeout = 10 * time.Second

var excludedChannels []string = []string{}

type Connection interface {
	ExportService(service interface{}, topic string, ann *nmodel.ServiceAnnouncement) (*rpc.ExportedService, error)
	GetServiceClient(serviceTopic string) *ninja.ServiceClient
}

type PresetsService struct {
	Model       *model.Presets
	Save        func(*model.Presets)
	Conn        Connection
	Log         *logger.Logger
	initialized bool
}

func (ps *PresetsService) Init() error {
	if ps.Log == nil {
		return fmt.Errorf("illegal state: no logger")
	}
	if ps.Model == nil {
		return fmt.Errorf("illegal state: Model is nil")
	}
	if ps.Save == nil {
		return fmt.Errorf("illegal state: Save is nil")
	}
	if ps.Conn == nil {
		return fmt.Errorf("illegal state: Conn is nil")
	}

	var err error
	siteID := config.MustString("siteId")
	topic := fmt.Sprintf("$site/%s/service/%s", siteID, "presets")
	announcement := &nmodel.ServiceAnnouncement{
		Schema: "http://schema.ninjablocks.com/service/presets",
	}
	if _, err = ps.Conn.ExportService(ps, topic, announcement); err != nil {
		return err
	}

	ps.initialized = true
	return nil
}

func (ps *PresetsService) Destroy() error {
	ps.initialized = false
	return nil
}

func (ps *PresetsService) checkInit() {
	if ps.Log == nil {
		ps.Log = logger.GetLogger("com.ninja.app-presets")
	}
	if !ps.initialized {
		ps.Log.Fatalf("illegal state: the service is not initialized")
	}
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenes
func (ps *PresetsService) FetchScenes(scope string) (*[]*model.Scene, error) {
	ps.checkInit()
	collect := make([]*model.Scene, 0, 0)
	for _, m := range ps.Model.Scenes {
		if m.Scope == scope {
			collect = append(collect, m)
		}
	}
	return &collect, nil
}

// see: http://schema.ninjablocks.com/service/presets#fetchScene
func (ps *PresetsService) FetchScene(id string) (*model.Scene, error) {
	ps.checkInit()
	for _, m := range ps.Model.Scenes {
		if m.ID == id {
			return m, nil
		}
	}
	return nil, fmt.Errorf("No such scene: %s", id)
}

func copyState(ch *nmodel.Channel) interface{} {
	if ch.LastState != nil {
		if state, ok := ch.LastState.(map[string]interface{}); ok {
			if payload, ok := state["payload"]; ok {
				return payload
			}
		}
	}
	return nil
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenePrototype
func (ps *PresetsService) FetchScenePrototype(scope string) (*model.Scene, error) {
	ps.checkInit()

	location := ""
	roomOffset := strings.Index(scope, "room/")
	if roomOffset >= 0 {
		location = scope[roomOffset:]
	}
	thingClient := ps.Conn.GetServiceClient("$home/services/ThingModel")
	things := make([]*nmodel.Thing, 0)
	keptThings := make([]*nmodel.Thing, 0, len(things))
	if err := thingClient.Call("fetchAll", nil, &things, defaultTimeout); err != nil {
		return nil, err
	}

	for _, t := range things {
		if !t.Promoted ||
			(location != "" && (t.Location == nil || *t.Location != location)) {
			continue
		}
		keptThings = append(keptThings, t)
	}
	result := &model.Scene{
		Scope:  scope,
		Things: make([]model.ThingState, 0, len(keptThings)),
	}
	for _, t := range keptThings {
		if t.Device == nil || t.Device.Channels == nil {
			continue
		}
		thingState := model.ThingState{
			ID:       t.ID,
			Channels: make([]model.ChannelState, 0, len(*t.Device.Channels)),
		}
	Channels:
		for _, c := range *t.Device.Channels {

			for _, x := range excludedChannels {
				// don't include channels with excluded schema
				if x == c.Schema {
					continue Channels
				}
			}

			if c.SupportedMethods == nil {
				// don't include channels with no supported methods
				continue
			}

			found := false
			for _, m := range *c.SupportedMethods {
				found = (m == "set")
				if found {
					break
				}
			}
			if !found {
				// don't include channels that do not support the set method
				continue
			}
			channelState := model.ChannelState{
				ID:    c.ID,
				State: copyState(c),
			}
			thingState.Channels = append(thingState.Channels, channelState)
		}
		if len(thingState.Channels) > 0 {
			result.Things = append(result.Things, thingState)
		}
	}
	return result, nil
}

// see: http://schema.ninjablocks.com/service/presets#storeScene
func (ps *PresetsService) StoreScene(model *model.Scene) (*model.Scene, error) {
	ps.checkInit()
	var found int

	if model.Scope == "" {
		return nil, fmt.Errorf("illegal argument: model.Scope is empty")
	}

	if model.ID == "prototype" {
		return nil, fmt.Errorf("illegal argument: model.ID == \"prototype\"")
	}

	for i, m := range ps.Model.Scenes {
		if model.ID == "" {
			if m.Scope == model.Scope && m.Slot == model.Slot {
				found = i
			}
		} else {
			if m.ID == model.ID {
				found = i
			}
		}
	}

	if model.ID == "" {
		model.ID = uuid.NewUUID().String()
	}

	if found >= len(ps.Model.Scenes) {
		ps.Model.Scenes = append(ps.Model.Scenes, model)
	} else {
		ps.Model.Scenes[found] = model
	}
	ps.Save(ps.Model)
	return model, nil
}

// see: http://schema.ninjablocks.com/service/presets#applyScene
func (ps *PresetsService) ApplyScene(id string) error {
	ps.checkInit()
	return fmt.Errorf("unimplemented function: ApplyScene")
}
<commit_msg>Refresh the state of each channel.<commit_after>package service

import (
	"code.google.com/p/go-uuid/uuid"
	"fmt"
	"github.com/ninjasphere/app-presets/model"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
	nmodel "github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/rpc"
	"strings"
	"time"
)

const defaultTimeout = 10 * time.Second

var excludedChannels []string = []string{}

type Connection interface {
	ExportService(service interface{}, topic string, ann *nmodel.ServiceAnnouncement) (*rpc.ExportedService, error)
	GetServiceClient(serviceTopic string) *ninja.ServiceClient
}

type PresetsService struct {
	Model       *model.Presets
	Save        func(*model.Presets)
	Conn        Connection
	Log         *logger.Logger
	initialized bool
}

func (ps *PresetsService) Init() error {
	if ps.Log == nil {
		return fmt.Errorf("illegal state: no logger")
	}
	if ps.Model == nil {
		return fmt.Errorf("illegal state: Model is nil")
	}
	if ps.Save == nil {
		return fmt.Errorf("illegal state: Save is nil")
	}
	if ps.Conn == nil {
		return fmt.Errorf("illegal state: Conn is nil")
	}

	var err error
	siteID := config.MustString("siteId")
	topic := fmt.Sprintf("$site/%s/service/%s", siteID, "presets")
	announcement := &nmodel.ServiceAnnouncement{
		Schema: "http://schema.ninjablocks.com/service/presets",
	}
	if _, err = ps.Conn.ExportService(ps, topic, announcement); err != nil {
		return err
	}

	ps.initialized = true
	return nil
}

func (ps *PresetsService) Destroy() error {
	ps.initialized = false
	return nil
}

func (ps *PresetsService) checkInit() {
	if ps.Log == nil {
		ps.Log = logger.GetLogger("com.ninja.app-presets")
	}
	if !ps.initialized {
		ps.Log.Fatalf("illegal state: the service is not initialized")
	}
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenes
func (ps *PresetsService) FetchScenes(scope string) (*[]*model.Scene, error) {
	ps.checkInit()
	collect := make([]*model.Scene, 0, 0)
	for _, m := range ps.Model.Scenes {
		if m.Scope == scope {
			collect = append(collect, m)
		}
	}
	return &collect, nil
}

// see: http://schema.ninjablocks.com/service/presets#fetchScene
func (ps *PresetsService) FetchScene(id string) (*model.Scene, error) {
	ps.checkInit()
	for _, m := range ps.Model.Scenes {
		if m.ID == id {
			return m, nil
		}
	}
	return nil, fmt.Errorf("No such scene: %s", id)
}

func copyState(ch *nmodel.Channel) interface{} {
	if ch.LastState != nil {
		if state, ok := ch.LastState.(map[string]interface{}); ok {
			if payload, ok := state["payload"]; ok {
				return payload
			}
		}
	}
	return nil
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenePrototype
func (ps *PresetsService) FetchScenePrototype(scope string) (*model.Scene, error) {
	ps.checkInit()

	location := ""
	roomOffset := strings.Index(scope, "room/")
	if roomOffset >= 0 {
		location = scope[roomOffset:]
	}
	thingClient := ps.Conn.GetServiceClient("$home/services/ThingModel")
	things := make([]*nmodel.Thing, 0)
	keptThings := make([]*nmodel.Thing, 0, len(things))
	if err := thingClient.Call("fetchAll", nil, &things, defaultTimeout); err != nil {
		return nil, err
	}

	for _, t := range things {
		if !t.Promoted ||
			(location != "" && (t.Location == nil || *t.Location != location)) {
			continue
		}
		keptThings = append(keptThings, t)
	}
	result := &model.Scene{
		Scope:  scope,
		Things: make([]model.ThingState, 0, len(keptThings)),
	}
	for _, t := range keptThings {
		if t.Device == nil || t.Device.Channels == nil {
			continue
		}
		thingState := model.ThingState{
			ID:       t.ID,
			Channels: make([]model.ChannelState, 0, len(*t.Device.Channels)),
		}
		channelIndex := make(map[string]int)
	Channels:
		for _, c := range *t.Device.Channels {

			for _, x := range excludedChannels {
				// don't include channels with excluded schema
				if x == c.Schema {
					continue Channels
				}
			}

			if c.SupportedMethods == nil {
				// don't include channels with no supported methods
				continue
			}

			found := false
			for _, m := range *c.SupportedMethods {
				found = (m == "set")
				if found {
					break
				}
			}
			if !found {
				// don't include channels that do not support the set method
				continue
			}
			channelState := model.ChannelState{
				ID: c.ID,
			}
			channelIndex[c.ID] = len(thingState.Channels)
			thingState.Channels = append(thingState.Channels, channelState)
		}

		// refresh the Thing to get the latest channel states

		refreshedThing := &nmodel.Thing{}
		if err := thingClient.Call("fetch", thingState.ID, &refreshedThing, defaultTimeout); err != nil {
			ps.Log.Errorf("error while refreshing thing %s: %v", thingState.ID, err)
			continue
		}
		if refreshedThing.Device == nil || refreshedThing.Device.Channels == nil {
			continue
		}

		for _, c := range *refreshedThing.Device.Channels {
			if x, ok := channelIndex[c.ID]; ok {
				thingState.Channels[x].State = copyState(c)
			}
		}

		// remove channels for which we don't have state.

		j := 0
		for i, c := range thingState.Channels {
			if c.State != nil {
				if i != j {
					thingState.Channels[j] = thingState.Channels[i]
				}
				j++
			} else {
				ps.Log.Debugf("Skipping channel %v of thing %v because no current state available", c.ID, thingState.ID)
			}
		}
		if j != len(thingState.Channels) {
			thingState.Channels = thingState.Channels[0:j]
		}

		if len(thingState.Channels) > 0 {
			result.Things = append(result.Things, thingState)
		}
	}
	return result, nil
}

// see: http://schema.ninjablocks.com/service/presets#storeScene
func (ps *PresetsService) StoreScene(model *model.Scene) (*model.Scene, error) {
	ps.checkInit()
	var found int

	if model.Scope == "" {
		return nil, fmt.Errorf("illegal argument: model.Scope is empty")
	}

	if model.ID == "prototype" {
		return nil, fmt.Errorf("illegal argument: model.ID == \"prototype\"")
	}

	for i, m := range ps.Model.Scenes {
		if model.ID == "" {
			if m.Scope == model.Scope && m.Slot == model.Slot {
				found = i
			}
		} else {
			if m.ID == model.ID {
				found = i
			}
		}
	}

	if model.ID == "" {
		model.ID = uuid.NewUUID().String()
	}

	if found >= len(ps.Model.Scenes) {
		ps.Model.Scenes = append(ps.Model.Scenes, model)
	} else {
		ps.Model.Scenes[found] = model
	}
	ps.Save(ps.Model)
	return model, nil
}

// see: http://schema.ninjablocks.com/service/presets#applyScene
func (ps *PresetsService) ApplyScene(id string) error {
	ps.checkInit()
	if _, err := ps.FetchScene(id); err != nil {
		return err
	} else {
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/cloudfoundry/cli/plugin"
	"github.com/davidehringer/cf-buildpack-management-plugin/buildpacks"
	"strings"
	"fmt"
	"os"
)

type BuildpackManager struct{}

func (c *BuildpackManager) GetMetadata() plugin.PluginMetadata {
	primaryUsage := "cf configure-buildpacks PATH_TO_YAML_CONFIG_FILE [-dryRun]"
	secondaryUsage := `   The provided path for the configuration file can be an absolute or relative path to
   a file.  The file should have a map named "buildpacks" containing an array of buildpacks. The "filename"
   values for each buildpack can be an absolute or relative path to a file.

   Valid YAML file example:
   ---
   buildpacks:
   - name: java
     position: 1
     enabled: true
     locked: false
     filename: java-buildpack-offline-v3.0.zip
   - name: ruby_buildpack
     position: 2
     enabled: true
     locked: false
     filename: ruby_buildpack-cached-v1.3.0.zip
    `
    flags := make(map[string]string)
    flags["dryRun"] = "stop before making any changes"

	return plugin.PluginMetadata{
		Name: "buildpack-management",
		Version: plugin.VersionType{
			Major: 0,
			Minor: 9,
			Build: 0,
		},
		Commands: []plugin.Command{
			{
				Name:     "configure-buildpacks",
				HelpText: "Configures system buildpacks using a declarative configuration file.",
				UsageDetails: plugin.Usage{
					Usage: strings.Join([]string{primaryUsage, secondaryUsage}, "\n\n"),
					Options: flags,
				},
			},
		},
	}
}

func main() {
	plugin.Start(new(BuildpackManager))
}

func (c *BuildpackManager) Run(cliConnection plugin.CliConnection, args []string) {
	if args[0] == "configure-buildpacks" {
		if len(args) < 2 {
			// TODO this is useless validation. Make useful
			fmt.Println("Incorrect Usage. \n\nPATH_TO_YAML_CONFIG_FILE is a required argument")
			os.Exit(1)
		}

		manifestRepo := buildpacks.NewFilesystemBuildpackManifestRepo()
		buildpackRepo := buildpacks.NewCliBuildpackRepository(cliConnection)

		config, err := manifestRepo.ReadManifest(args[1])
		if(err != nil){
			fmt.Println("error: ", err)
			os.Exit(1)
		}

		service := buildpacks.NewCliBuildpackService(cliConnection, buildpackRepo)
		service.ConfigureBuildpacks(config, dryRun(args))
	}
}

func dryRun(args []string) (dryRun bool) {
	for _, arg := range args {
		if(arg == "--dryRun") {
			dryRun = true
		}
	}
	return
}

type Result struct {
	Resources []Resource
}

type Resource struct {
	Entity *Buildpack
}

type Buildpack struct {
	Name     string
	Position int
	Enabled  bool
	Filename string
}
<commit_msg>Removed dead code<commit_after>package main

import (
	"github.com/cloudfoundry/cli/plugin"
	"github.com/davidehringer/cf-buildpack-management-plugin/buildpacks"
	"strings"
	"fmt"
	"os"
)

type BuildpackManager struct{}

func (c *BuildpackManager) GetMetadata() plugin.PluginMetadata {
	primaryUsage := "cf configure-buildpacks PATH_TO_YAML_CONFIG_FILE [-dryRun]"
	secondaryUsage := `   The provided path for the configuration file can be an absolute or relative path to
   a file.  The file should have a map named "buildpacks" containing an array of buildpacks. The "filename"
   values for each buildpack can be an absolute or relative path to a file.

   Valid YAML file example:
   ---
   buildpacks:
   - name: java
     position: 1
     enabled: true
     locked: false
     filename: java-buildpack-offline-v3.0.zip
   - name: ruby_buildpack
     position: 2
     enabled: true
     locked: false
     filename: ruby_buildpack-cached-v1.3.0.zip
    `
    flags := make(map[string]string)
    flags["dryRun"] = "stop before making any changes"

	return plugin.PluginMetadata{
		Name: "buildpack-management",
		Version: plugin.VersionType{
			Major: 0,
			Minor: 9,
			Build: 0,
		},
		Commands: []plugin.Command{
			{
				Name:     "configure-buildpacks",
				HelpText: "Configures system buildpacks using a declarative configuration file.",
				UsageDetails: plugin.Usage{
					Usage: strings.Join([]string{primaryUsage, secondaryUsage}, "\n\n"),
					Options: flags,
				},
			},
		},
	}
}

func main() {
	plugin.Start(new(BuildpackManager))
}

func (c *BuildpackManager) Run(cliConnection plugin.CliConnection, args []string) {
	if args[0] == "configure-buildpacks" {
		if len(args) < 2 {
			// TODO this is useless validation. Make useful
			fmt.Println("Incorrect Usage. \n\nPATH_TO_YAML_CONFIG_FILE is a required argument")
			os.Exit(1)
		}

		manifestRepo := buildpacks.NewFilesystemBuildpackManifestRepo()
		buildpackRepo := buildpacks.NewCliBuildpackRepository(cliConnection)

		config, err := manifestRepo.ReadManifest(args[1])
		if(err != nil){
			fmt.Println("error: ", err)
			os.Exit(1)
		}

		service := buildpacks.NewCliBuildpackService(cliConnection, buildpackRepo)
		service.ConfigureBuildpacks(config, dryRun(args))
	}
}

func dryRun(args []string) (dryRun bool) {
	for _, arg := range args {
		if(arg == "--dryRun") {
			dryRun = true
		}
	}
	return
}
<|endoftext|>


<commit_before>package shadowsocks

import (
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"strconv"
	"time"
	"syscall"
	"sync"
)

const (
	idType  = 0 // address type index
	idIP0   = 1 // ip addres start index
	idDmLen = 1 // domain address length index
	idDm0   = 2 // domain address start index

	typeIPv4 = 1 // type is ipv4 address
	typeDm   = 3 // type is domain address
	typeIPv6 = 4 // type is ipv6 address

	lenIPv4   = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port
	lenIPv6   = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port
	lenDmBase = 1 + 1 + 2           // 1addrType + 1addrLen + 2port, plus addrLen
)

type UDP interface {
	ReadFromUDP(b []byte) (n int, src *net.UDPAddr, err error)
	Read(b []byte) (n int, err error)
	WriteToUDP(b []byte, src *net.UDPAddr) (n int, err error)
	Write(b []byte) (n int, err error)
	Close() error
	SetWriteDeadline(t time.Time) error
	SetReadDeadline(t time.Time) error
	LocalAddr() net.Addr
	RemoteAddr() net.Addr
	ReadFrom(b []byte) (int, net.Addr, error)
}

type UDPConn struct {
	UDP
	*Cipher
}

func NewUDPConn(cn UDP, cipher *Cipher) *UDPConn {
	return &UDPConn{cn, cipher}
}

type CachedUDPConn struct {
	timer *time.Timer
	UDP
	i string
}

func NewCachedUDPConn(cn UDP) *CachedUDPConn {
	return &CachedUDPConn{nil, cn, ""}
}

func (c *CachedUDPConn) Check() {
	go nl.Delete(c.i)
}

func (c *CachedUDPConn) Close() error{
	c.timer.Stop()
	return c.UDP.Close()
}

func (c *CachedUDPConn) SetTimer(index string) {
	c.i = index
	c.timer = time.AfterFunc(120*time.Second, c.Check)
}

func (c *CachedUDPConn) Refresh() bool {
	return c.timer.Reset(120*time.Second)
}

type NATlist struct {
	sync.Mutex
	Conns map[string]*CachedUDPConn
	AliveConns int
}

func (nl *NATlist) Delete(srcaddr string) {
	nl.Lock()
	c , ok := nl.Conns[srcaddr]
	if ok {
		c.Close()
		delete(nl.Conns, srcaddr)
		nl.AliveConns -= 1
	}
	for k, _  := range ReqList {
		delete(ReqList, k)
	}
	defer nl.Unlock()
}

func (nl *NATlist) Get(srcaddr *net.UDPAddr, ss *UDPConn) (c *CachedUDPConn, ok bool, err error){
	nl.Lock()
	defer nl.Unlock()
	index := srcaddr.String()
	_ , ok = nl.Conns[index]
	if !ok {
		//NAT not exists or expired
		nl.AliveConns += 1
		delete(nl.Conns, index)
		ok = false
		//full cone
		conn, err := net.ListenUDP("udp", &net.UDPAddr{
			IP:   net.IPv6zero,
			Port: 0,
		})
		if err != nil {
			return nil, false, err
		}
		nl.Conns[index] = NewCachedUDPConn(conn)
		c , _ = nl.Conns[index]
		c.SetTimer(index)
		go Pipeloop(ss, srcaddr, c)
	} else {
		//NAT exists
		c , _ = nl.Conns[index]
		c.Refresh()
	}
	err = nil
	return
}

func ParseHeader(addr net.Addr) ([]byte, int) {
//what if the request address type is domain???
	ip, port, err := net.SplitHostPort(addr.String())
	if err != nil {
		return nil, 0
	}
	buf := make([]byte, 20)
	IP := net.ParseIP(ip)
	b1 := IP.To4()
	iplen := 0
	if b1 == nil { //ipv6
		b1 = IP.To16()
		buf[0] = typeIPv6
		iplen = net.IPv6len
	} else { //ipv4
		buf[0] = typeIPv4
		iplen = net.IPv4len
	}
	copy(buf[1:], b1)
	port_i, _ := strconv.Atoi(port)
	binary.BigEndian.PutUint16(buf[1+iplen:], uint16(port_i))
	return buf[:1+iplen+2], 1+iplen+2
}

func Pipeloop(ss *UDPConn, srcaddr *net.UDPAddr, remote UDP) {
	buf := udpBuf.Get()
	defer udpBuf.Put(buf)
	defer nl.Delete(srcaddr.String())
	for{
		remote.SetReadDeadline(time.Now().Add(readTimeout))
		n, raddr, err := remote.ReadFrom(buf)
		if err != nil {
			if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) {
				// log too many open file error
				// EMFILE is process reaches open file limits, ENFILE is system limit
				fmt.Println("[udp]read error:", err)
			} else if ne.Err.Error() == "use of closed network connection" {
				fmt.Println("[udp]Connection Closing:", remote.LocalAddr())
			} else {
				fmt.Println("[udp]error reading from:", remote.LocalAddr(), err)
			}
			return
		}
		// need improvement here
		if N, ok := ReqList[raddr.String()]; ok {
			go ss.WriteToUDP(append(N.Req[:N.ReqLen], buf[:n]...), srcaddr)
		}	else {
			header, hlen := ParseHeader(raddr)
			go ss.WriteToUDP(append(header[:hlen], buf[:n]...), srcaddr)
		}
	}
}

type ReqNode struct {
	Req []byte
	ReqLen int
}

var ReqList = map[string]*ReqNode{}

var nl = NATlist{Conns: map[string]*CachedUDPConn{}}

var udpBuf = NewLeakyBuf(nBuf, bufSize)

func (c *UDPConn) handleUDPConnection(n int, src *net.UDPAddr, receive []byte) {
	var dstIP net.IP
	var reqLen int
	defer udpBuf.Put(receive)

	switch receive[idType] {
	case typeIPv4:
		reqLen = lenIPv4
		dstIP = net.IP(receive[idIP0 : idIP0+net.IPv4len])
	case typeIPv6:
		reqLen = lenIPv6
		dstIP = net.IP(receive[idIP0 : idIP0+net.IPv6len])
	case typeDm:
		reqLen = int(receive[idDmLen]) + lenDmBase
		dIP, err := net.ResolveIPAddr("ip" ,string(receive[idDm0 : idDm0+receive[idDmLen]]))
		if err != nil{
			fmt.Sprintf("[udp]failed to resolve domain name: %s\n", string(receive[idDm0 : idDm0+receive[idDmLen]]))
			return
		}
		dstIP = dIP.IP
	default:
		fmt.Sprintf("[udp]addr type %d not supported", receive[idType])
		return
	}
	dst := &net.UDPAddr{
		IP:   dstIP,
		Port: int(binary.BigEndian.Uint16(receive[reqLen-2 : reqLen])),
	}
	if _, ok := ReqList[dst.String()]; !ok {
		req := make([]byte, reqLen)
		for i:=0;i<reqLen;i++ {
			req[i] = receive[i]
		}
		ReqList[dst.String()] = &ReqNode{req, reqLen}
	}

	remote, _, err := nl.Get(src, c)
	if err != nil {
		return
	}
	remote.SetWriteDeadline(time.Now().Add(readTimeout))
	_, err = remote.WriteToUDP(receive[reqLen:n], dst)
	if err != nil {
		if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) {
			// log too many open file error
			// EMFILE is process reaches open file limits, ENFILE is system limit
			fmt.Println("[udp]write error:", err)
		} else {
			fmt.Println("[udp]error connecting to:", dst, err)
		}
		return
	}
	// Pipeloop
	return
}

func (c *UDPConn) ReadAndHandleUDPReq()  {
	buf := udpBuf.Get()
	n, src, err := c.ReadFromUDP(buf[0:])
	if err != nil {
		return
	}
	go c.handleUDPConnection(n, src, buf)
}

//n is the size of the payload
func (c *UDPConn) ReadFromUDP(b []byte) (n int, src *net.UDPAddr, err error) {
	buf := udpBuf.Get()
	n, src, err = c.UDP.ReadFromUDP(buf[0:])
	if err != nil {
		return
	}
	defer udpBuf.Put(buf)

	iv := buf[:c.info.ivLen]
	if err = c.initDecrypt(iv); err != nil {
		return
	}
	c.decrypt(b[0:n - c.info.ivLen], buf[c.info.ivLen : n])
	n = n - c.info.ivLen
	return
}

func (c *UDPConn) Read(b []byte) (n int, err error) {
	buf := udpBuf.Get()
	n, err = c.UDP.Read(buf[0:])
	if err != nil {
		return
	}
	defer udpBuf.Put(buf)

	iv := buf[:c.info.ivLen]
	if err = c.initDecrypt(iv); err != nil {
		return
	}
	c.decrypt(b[0:n - c.info.ivLen], buf[c.info.ivLen : n])
	n = n - c.info.ivLen
	return
}

//n = iv + payload
func (c *UDPConn) WriteToUDP(b []byte, src *net.UDPAddr) (n int, err error) {
	var cipherData []byte
	dataStart := 0

	var iv []byte
	iv, err = c.initEncrypt()
	if err != nil {
		return
	}
	// Put initialization vector in buffer, do a single write to send both
	// iv and data.
	cipherData = make([]byte, len(b)+len(iv))
	copy(cipherData, iv)
	dataStart = len(iv)

	c.encrypt(cipherData[dataStart:], b)
	n, err = c.UDP.WriteToUDP(cipherData, src)
	return
}

func (c *UDPConn) Write(b []byte) (n int, err error) {
	var cipherData []byte
	dataStart := 0

	var iv []byte
	iv, err = c.initEncrypt()
	if err != nil {
		return
	}
	// Put initialization vector in buffer, do a single write to send both
	// iv and data.
	cipherData = make([]byte, len(b)+len(iv))
	copy(cipherData, iv)
	dataStart = len(iv)

	c.encrypt(cipherData[dataStart:], b)
	n, err = c.UDP.Write(cipherData)
	return
}
<commit_msg>safer ReqList.<commit_after>package shadowsocks

import (
	"encoding/binary"
	"fmt"
	"net"
	"strconv"
	"time"
	"syscall"
	"sync"
)

const (
	idType    = 0 // address type index
	idIP0     = 1 // ip addres start index
	idDmLen   = 1 // domain address length index
	idDm0     = 2 // domain address start index

	typeIPv4  = 1 // type is ipv4 address
	typeDm    = 3 // type is domain address
	typeIPv6  = 4 // type is ipv6 address

	lenIPv4   = 1 + net.IPv4len + 2 // 1addrType + ipv4 + 2port
	lenIPv6   = 1 + net.IPv6len + 2 // 1addrType + ipv6 + 2port
	lenDmBase = 1 + 1 + 2           // 1addrType + 1addrLen + 2port, plus addrLen
)

var (
	reqList   = ReqList{List:map[string]*ReqNode{}}
	nl        = NATlist{Conns: map[string]*CachedUDPConn{}}
	udpBuf    = NewLeakyBuf(nBuf, bufSize)
)

type UDP interface {
	ReadFromUDP(b []byte) (n int, src *net.UDPAddr, err error)
	Read(b []byte) (n int, err error)
	WriteToUDP(b []byte, src *net.UDPAddr) (n int, err error)
	Write(b []byte) (n int, err error)
	Close() error
	SetWriteDeadline(t time.Time) error
	SetReadDeadline(t time.Time) error
	LocalAddr() net.Addr
	RemoteAddr() net.Addr
	ReadFrom(b []byte) (int, net.Addr, error)
}

type UDPConn struct {
	UDP
	*Cipher
}

func NewUDPConn(cn UDP, cipher *Cipher) *UDPConn {
	return &UDPConn{cn, cipher}
}

type CachedUDPConn struct {
	timer *time.Timer
	UDP
	i string // scraddr
}

func NewCachedUDPConn(cn UDP) *CachedUDPConn {
	return &CachedUDPConn{nil, cn, ""}
}

func (c *CachedUDPConn) Check() {
	go nl.Delete(c.i)
}

func (c *CachedUDPConn) Close() error{
	c.timer.Stop()
	return c.UDP.Close()
}

func (c *CachedUDPConn) SetTimer(index string) {
	c.i = index
	c.timer = time.AfterFunc(120*time.Second, c.Check)
}

func (c *CachedUDPConn) Refresh() bool {
	return c.timer.Reset(120*time.Second)
}

type NATlist struct {
	sync.Mutex
	Conns map[string]*CachedUDPConn
	AliveConns int
}

func (nl *NATlist) Delete(srcaddr string) {
	nl.Lock()
	c , ok := nl.Conns[srcaddr]
	if ok {
		c.Close()
		delete(nl.Conns, srcaddr)
		nl.AliveConns -= 1
	}
	reqList.Refresh()
	defer nl.Unlock()
}

func (nl *NATlist) Get(srcaddr *net.UDPAddr, ss *UDPConn) (c *CachedUDPConn, ok bool, err error){
	nl.Lock()
	defer nl.Unlock()
	index := srcaddr.String()
	_ , ok = nl.Conns[index]
	if !ok {
		//NAT not exists or expired
		nl.AliveConns += 1
		delete(nl.Conns, index)
		ok = false
		//full cone
		conn, err := net.ListenUDP("udp", &net.UDPAddr{
			IP:   net.IPv6zero,
			Port: 0,
		})
		if err != nil {
			return nil, false, err
		}
		nl.Conns[index] = NewCachedUDPConn(conn)
		c , _ = nl.Conns[index]
		c.SetTimer(index)
		go Pipeloop(ss, srcaddr, c)
	} else {
		//NAT exists
		c , _ = nl.Conns[index]
		c.Refresh()
	}
	err = nil
	return
}

type ReqNode struct {
	Req []byte
	ReqLen int
}

type ReqList struct {
	List map[string]*ReqNode
	sync.Mutex
}

func (r *ReqList) Refresh() {
	r.Lock()
	defer r.Unlock()
	for k, _  := range r.List {
		delete(r.List, k)
	}
}

func (r *ReqList) Get(dstaddr string) (req *ReqNode, ok bool) {
	r.Lock()
	defer r.Unlock()
	req, ok = r.List[dstaddr]
	return
}

func (r *ReqList) Put(dstaddr string, req *ReqNode) {
	r.Lock()
	defer r.Unlock()
	r.List[dstaddr] = req
	return
}

func ParseHeader(addr net.Addr) ([]byte, int) {
//what if the request address type is domain???
	ip, port, err := net.SplitHostPort(addr.String())
	if err != nil {
		return nil, 0
	}
	buf := make([]byte, 20)
	IP := net.ParseIP(ip)
	b1 := IP.To4()
	iplen := 0
	if b1 == nil { //ipv6
		b1 = IP.To16()
		buf[0] = typeIPv6
		iplen = net.IPv6len
	} else { //ipv4
		buf[0] = typeIPv4
		iplen = net.IPv4len
	}
	copy(buf[1:], b1)
	port_i, _ := strconv.Atoi(port)
	binary.BigEndian.PutUint16(buf[1+iplen:], uint16(port_i))
	return buf[:1+iplen+2], 1+iplen+2
}

func Pipeloop(ss *UDPConn, srcaddr *net.UDPAddr, remote UDP) {
	buf := udpBuf.Get()
	defer udpBuf.Put(buf)
	defer nl.Delete(srcaddr.String())
	for{
		remote.SetReadDeadline(time.Now().Add(readTimeout))
		n, raddr, err := remote.ReadFrom(buf)
		if err != nil {
			if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) {
				// log too many open file error
				// EMFILE is process reaches open file limits, ENFILE is system limit
				fmt.Println("[udp]read error:", err)
			} else if ne.Err.Error() == "use of closed network connection" {
				fmt.Println("[udp]Connection Closing:", remote.LocalAddr())
			} else {
				fmt.Println("[udp]error reading from:", remote.LocalAddr(), err)
			}
			return
		}
		// need improvement here
		if N, ok := reqList.Get(raddr.String()); ok {
			go ss.WriteToUDP(append(N.Req[:N.ReqLen], buf[:n]...), srcaddr)
		}	else {
			header, hlen := ParseHeader(raddr)
			go ss.WriteToUDP(append(header[:hlen], buf[:n]...), srcaddr)
		}
	}
}

func (c *UDPConn) handleUDPConnection(n int, src *net.UDPAddr, receive []byte) {
	var dstIP net.IP
	var reqLen int
	defer udpBuf.Put(receive)

	switch receive[idType] {
	case typeIPv4:
		reqLen = lenIPv4
		dstIP = net.IP(receive[idIP0 : idIP0+net.IPv4len])
	case typeIPv6:
		reqLen = lenIPv6
		dstIP = net.IP(receive[idIP0 : idIP0+net.IPv6len])
	case typeDm:
		reqLen = int(receive[idDmLen]) + lenDmBase
		dIP, err := net.ResolveIPAddr("ip" ,string(receive[idDm0 : idDm0+receive[idDmLen]]))
		if err != nil{
			fmt.Sprintf("[udp]failed to resolve domain name: %s\n", string(receive[idDm0 : idDm0+receive[idDmLen]]))
			return
		}
		dstIP = dIP.IP
	default:
		fmt.Sprintf("[udp]addr type %d not supported", receive[idType])
		return
	}
	dst := &net.UDPAddr{
		IP:   dstIP,
		Port: int(binary.BigEndian.Uint16(receive[reqLen-2 : reqLen])),
	}
	if _, ok := reqList.Get(dst.String()); !ok {
		req := make([]byte, reqLen)
		for i:=0;i<reqLen;i++ {
			req[i] = receive[i]
		}
		reqList.Put(dst.String(), &ReqNode{req, reqLen})
	}

	remote, _, err := nl.Get(src, c)
	if err != nil {
		return
	}
	remote.SetWriteDeadline(time.Now().Add(readTimeout))
	_, err = remote.WriteToUDP(receive[reqLen:n], dst)
	if err != nil {
		if ne, ok := err.(*net.OpError); ok && (ne.Err == syscall.EMFILE || ne.Err == syscall.ENFILE) {
			// log too many open file error
			// EMFILE is process reaches open file limits, ENFILE is system limit
			fmt.Println("[udp]write error:", err)
		} else {
			fmt.Println("[udp]error connecting to:", dst, err)
		}
		return
	}
	// Pipeloop
	return
}

func (c *UDPConn) ReadAndHandleUDPReq()  {
	buf := udpBuf.Get()
	n, src, err := c.ReadFromUDP(buf[0:])
	if err != nil {
		return
	}
	go c.handleUDPConnection(n, src, buf)
}

//n is the size of the payload
func (c *UDPConn) ReadFromUDP(b []byte) (n int, src *net.UDPAddr, err error) {
	buf := udpBuf.Get()
	n, src, err = c.UDP.ReadFromUDP(buf[0:])
	if err != nil {
		return
	}
	defer udpBuf.Put(buf)

	iv := buf[:c.info.ivLen]
	if err = c.initDecrypt(iv); err != nil {
		return
	}
	c.decrypt(b[0:n - c.info.ivLen], buf[c.info.ivLen : n])
	n = n - c.info.ivLen
	return
}

func (c *UDPConn) Read(b []byte) (n int, err error) {
	buf := udpBuf.Get()
	n, err = c.UDP.Read(buf[0:])
	if err != nil {
		return
	}
	defer udpBuf.Put(buf)

	iv := buf[:c.info.ivLen]
	if err = c.initDecrypt(iv); err != nil {
		return
	}
	c.decrypt(b[0:n - c.info.ivLen], buf[c.info.ivLen : n])
	n = n - c.info.ivLen
	return
}

//n = iv + payload
func (c *UDPConn) WriteToUDP(b []byte, src *net.UDPAddr) (n int, err error) {
	var cipherData []byte
	dataStart := 0

	var iv []byte
	iv, err = c.initEncrypt()
	if err != nil {
		return
	}
	// Put initialization vector in buffer, do a single write to send both
	// iv and data.
	cipherData = make([]byte, len(b)+len(iv))
	copy(cipherData, iv)
	dataStart = len(iv)

	c.encrypt(cipherData[dataStart:], b)
	n, err = c.UDP.WriteToUDP(cipherData, src)
	return
}

func (c *UDPConn) Write(b []byte) (n int, err error) {
	var cipherData []byte
	dataStart := 0

	var iv []byte
	iv, err = c.initEncrypt()
	if err != nil {
		return
	}
	// Put initialization vector in buffer, do a single write to send both
	// iv and data.
	cipherData = make([]byte, len(b)+len(iv))
	copy(cipherData, iv)
	dataStart = len(iv)

	c.encrypt(cipherData[dataStart:], b)
	n, err = c.UDP.Write(cipherData)
	return
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"regexp"

	"github.com/9seconds/ah/app/environments"
	"github.com/9seconds/ah/app/historyentries"
	"github.com/9seconds/ah/app/utils"
)

// Tee implements t (trace, tee) command.
func Tee(input []string, pseudoTTY bool, env *environments.Environment) {
	hashChan := getPreciseHashChan(env)

	output, err := ioutil.TempFile(os.TempDir(), "ah")
	if err != nil {
		utils.Logger.Panic("Cannot create temporary file")
	}

	bufferedOutput := gzip.NewWriter(output)
	combinedStdout := io.MultiWriter(os.Stdout, bufferedOutput)
	combinedStderr := io.MultiWriter(os.Stderr, bufferedOutput)

	commandError := utils.Exec(pseudoTTY,
		os.Stdin, combinedStdout, combinedStderr,
		input[0], input[1:]...)

	bufferedOutput.Close()
	output.Close()

	result := <-hashChan
	if hash, ok := result.(string); ok {
		err := os.Rename(output.Name(), env.GetTraceFileName(hash))
		if err != nil {
			utils.Logger.Errorf("Cannot save trace: %v", err)
		}
	} else {
		utils.Logger.Errorf("Error occured on fetching command number: %v", result)
	}

	if commandError != nil {
		os.Exit(utils.GetStatusCode(commandError))
	}
}

func getPreciseHashChan(env *environments.Environment) (hashChan chan interface{}) {
	hashChan = make(chan interface{}, 1)

	go func() {
		commands, err := historyentries.GetCommands(historyentries.GetCommandsAll, getPreciseFilter(), env)
		if err != nil {
			hashChan <- fmt.Errorf("Cannot fetch commands list: %v", err)
			return
		}
		commandList := commands.Result().([]historyentries.HistoryEntry)

		if len(commandList) == 0 {
			hashChan <- errors.New("Command list is empty")
			return
		}

		found := len(commandList) - 1
		for idx := len(commandList) - 2; idx >= 0; idx-- {
			if commandList[idx].GetTimestamp() < environments.CreatedAt {
				break
			}
			found = idx
		}
		hashChan <- commandList[found].GetTraceName()
	}()

	return hashChan
}

func getPreciseFilter() *utils.Regexp {
	buffer := new(bytes.Buffer)
	for idx := 0; idx < len(os.Args)-1; idx++ {
		buffer.WriteString(regexp.QuoteMeta(os.Args[idx]))
		buffer.WriteString(`\s+`)
	}
	buffer.WriteString(regexp.QuoteMeta(os.Args[len(os.Args)-1]))

	return utils.CreateRegexp(buffer.String())
}
<commit_msg>Going back to the blocking version of the tee command<commit_after>package commands

import (
	"bytes"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"regexp"

	"github.com/9seconds/ah/app/environments"
	"github.com/9seconds/ah/app/historyentries"
	"github.com/9seconds/ah/app/utils"
)

// Tee implements t (trace, tee) command.
func Tee(input []string, pseudoTTY bool, env *environments.Environment) {
	output, err := ioutil.TempFile(os.TempDir(), "ah")
	if err != nil {
		utils.Logger.Panic("Cannot create temporary file")
	}

	bufferedOutput := gzip.NewWriter(output)
	combinedStdout := io.MultiWriter(os.Stdout, bufferedOutput)
	combinedStderr := io.MultiWriter(os.Stderr, bufferedOutput)

	commandError := utils.Exec(pseudoTTY,
		os.Stdin, combinedStdout, combinedStderr,
		input[0], input[1:]...)

	bufferedOutput.Close()
	output.Close()

	if hash, err := getPreciseHash(env); err == nil {
		err = os.Rename(output.Name(), env.GetTraceFileName(hash))
		if err != nil {
			utils.Logger.Errorf("Cannot save trace: %v", err)
		}
	} else {
		utils.Logger.Errorf("Error occured on fetching command number: %v", err)
	}

	if commandError != nil {
		os.Exit(utils.GetStatusCode(commandError))
	}
}

func getPreciseHash(env *environments.Environment) (hash string, err error) {
	commands, err := historyentries.GetCommands(historyentries.GetCommandsAll, getPreciseFilter(), env)
	if err != nil {
		err = fmt.Errorf("Cannot fetch commands list: %v", err)
		return
	}
	commandList := commands.Result().([]historyentries.HistoryEntry)

	if len(commandList) == 0 {
		err = errors.New("Command list is empty")
		return
	}

	found := len(commandList) - 1
	for idx := len(commandList) - 2; idx >= 0; idx-- {
		if commandList[idx].GetTimestamp() < environments.CreatedAt {
			break
		}
		found = idx
	}
	hash = commandList[found].GetTraceName()

	return
}

func getPreciseFilter() *utils.Regexp {
	buffer := new(bytes.Buffer)
	for idx := 0; idx < len(os.Args)-1; idx++ {
		buffer.WriteString(regexp.QuoteMeta(os.Args[idx]))
		buffer.WriteString(`\s+`)
	}
	buffer.WriteString(regexp.QuoteMeta(os.Args[len(os.Args)-1]))

	return utils.CreateRegexp(buffer.String())
}
<|endoftext|>


<commit_before>package walnut

import (
	"testing"
)

// configuration input which should parse successfully, returning
// a specific set of definitions
var valid = []struct {
	in string
	d  []def
}{
	{"", []def{}},
	{"\n\t\t\n\n ", []def{}},
	{"#abc", []def{}},
	{"a=1", []def{
		{"a", "1", 1},
	}},
	{"b=2\nc=3", []def{
		{"b", "2", 1},
		{"c", "3", 2},
	}},
	{"d\n e=4", []def{
		{"d.e", "4", 2},
	}},
	{"foo\n\tbar=5\n\tbaz=6", []def{
		{"foo.bar", "5", 2},
		{"foo.baz", "6", 3},
	}},
	{"#\nabc\n def=7\n #\n ghi=8", []def{
		{"abc.def", "7", 3},
		{"abc.ghi", "8", 5},
	}},
}

func TestValidConfigurations(test *testing.T) {
	h := "parseConfig(%#v) ->"

	for _, t := range valid {
		d, line := parseConfig([]byte(t.in))

		if line != 0 {
			test.Errorf(h+" line = %d, want 0", t.in, line)
			continue
		}

		if len(d) != len(t.d) {
			test.Errorf(h+" %v, want %v", t.in, d, t.d)
			continue
		}

		for i := 0; i < len(t.d); i++ {
			if d[i] != t.d[i] {
				test.Errorf(h+" %v, want %v", t.in, d, t.d)
			}
		}
	}
}

// configuration input which should trigger an error on a specific line
var invalid = []struct {
	in string
	l  int
}{
	{" foo=3", 1},
	{"a=1\n b=2", 2},
	{"c\n d=3\n  e=4", 3},
	{"f\n  g=5\n h=6", 3},
	{"i\n\t j=7\n  k=8", 3},
}

func TestInvalidConfigurations(test *testing.T) {
	h := "parseConfig(%#v) ->"

	for _, t := range invalid {
		_, line := parseConfig([]byte(t.in))
		if line != t.l {
			test.Errorf(h+" %d != %d", t.in, line, t.l)
		}
	}
}
<commit_msg>Update parse_test.go to work the the new interface<commit_after>package walnut

import (
	"strings"
	"testing"
	"testing/iotest"
)

// configuration input which should parse successfully, returning
// a specific set of definitions
var valid = []struct {
	in string
	d  []def
}{
	{"", []def{}},
	{"\n\t\t\n\n ", []def{}},
	{"#abc", []def{}},
	{"a=1", []def{
		{"a", "1", 1},
	}},
	{"b=2\nc=3", []def{
		{"b", "2", 1},
		{"c", "3", 2},
	}},
	{"d\n e=4", []def{
		{"d.e", "4", 2},
	}},
	{"foo\n\tbar=5\n\tbaz=6", []def{
		{"foo.bar", "5", 2},
		{"foo.baz", "6", 3},
	}},
	{"#\nabc\n def=7\n #\n ghi=8", []def{
		{"abc.def", "7", 3},
		{"abc.ghi", "8", 5},
	}},
}

func TestParseValid(test *testing.T) {
	h := "parse(%#v) ->"

	for _, t := range valid {
		r := iotest.OneByteReader(strings.NewReader(t.in))
		d, err := parse(r, make([]byte, 1024))

		if err != nil {
			test.Errorf(h+" err = %v, want nil", t.in, err)
			continue
		}

		if len(d) != len(t.d) {
			test.Errorf(h+" %v, want %v", t.in, d, t.d)
			continue
		}

		for i := 0; i < len(t.d); i++ {
			if d[i] != t.d[i] {
				test.Errorf(h+" %v, want %v", t.in, d, t.d)
			}
		}
	}
}

// configuration input which should trigger an indentation error
var indentErrors = []struct {
	in  string
	err string
}{
	{" foo=3", "invalid indentation on line 1"},
	{"a=1\n b=2", "invalid indentation on line 2"},
	{"c\n d=3\n  e=4", "invalid indentation on line 3"},
	{"f\n  g=5\n h=6", "invalid indentation on line 3"},
	{"i\n\t j=7\n  k=8", "invalid indentation on line 3"},
}

func TestParseIndentErrors(test *testing.T) {
	h := "parse(%#v) ->"

	for _, t := range indentErrors {
		r := iotest.OneByteReader(strings.NewReader(t.in))
		_, err := parse(r, make([]byte, 1024))

		if err == nil || err.Error() != t.err {
			test.Errorf(h+" %q != %q", t.in, err, t.err)
		}
	}
}
<|endoftext|>


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

package main

import (
	"math"
	"regexp"
	"testing"
)

func TestParse(t *testing.T) {
	for i, tt := range []struct {
		inre           string
		xtrans, ytrans string
		vars           map[string]float64
		wantX          []float64
		wantY          float64
	}{
		{
			inre:   `(?P<N>\d+)-\d+$`,
			xtrans: "math.Log(N), 1.0",
			ytrans: "Y",
			vars:   map[string]float64{"N": 10.0, "Y": 2.0},
			wantX:  []float64{math.Log(10.0), 1.0},
			wantY:  2.0,
		},
	} {
		inre := regexp.MustCompile(tt.inre)
		names := readNames(inre)
		xExpr, err := parseX(names, tt.xtrans)
		if err != nil {
			panic(err)
		}
		for xi, expr := range xExpr {
			if x := expr.value(tt.vars); x != tt.wantX[xi] {
				t.Errorf("%d: expected x[%d] = %f, got %f", i, xi, tt.wantX[xi], x)
			}
		}
		names["Y"] = struct{}{}
		yExpr, err := parseY(names, tt.ytrans)
		if err != nil {
			panic(err)
		}
		if y := yExpr.value(tt.vars); y != tt.wantY {
			t.Errorf("%d: expected y = %f, got %f", i, tt.wantY, y)
		}
	}
}
<commit_msg>test more of the parser<commit_after>// Copyright ©2016 Jonathan J Lawlor. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"math"
	"regexp"
	"testing"
)

func TestParse(t *testing.T) {
	for i, tt := range []struct {
		inre           string
		xtrans, ytrans string
		xrpn           [][]string
		xstrings       []string
		vars           map[string]float64
		wantX          []float64
		wantY          float64
	}{
		{
			inre:     `(?P<N>\d+)-\d+$`,
			xtrans:   "math.Log(N), 1.0",
			xrpn:     [][]string{{"N", "math.Log"}, {"1.0"}},
			xstrings: []string{"math.Log(N)", "1.0"},
			ytrans:   "Y",
			vars:     map[string]float64{"N": 10.0, "Y": 2.0},
			wantX:    []float64{math.Log(10.0), 1.0},
			wantY:    2.0,
		}, {
			inre:     `(?P<N>\d+)-\d+$`,
			xtrans:   "N*N, N, 1.0",
			xrpn:     [][]string{{"N", "N", "*"}, {"N"}, {"1.0"}},
			xstrings: []string{"N*N", "N", "1.0"},
			ytrans:   "Y+1",
			vars:     map[string]float64{"N": 10.0, "Y": 2.0},
			wantX:    []float64{100.0, 10.0, 1.0},
			wantY:    3.0,
		}, {
			inre:     `(?P<M>\d+)(?P<N>\d+)-\d+$`,
			xtrans:   "-math.Hypot(M+N, M-N), +M/N",
			xrpn:     [][]string{{"M", "N", "+", "M", "N", "-", "math.Hypot", "u-"}, {"M", "u+", "N", "/"}},
			xstrings: []string{"-math.Hypot(M+N, M-N)", "+M/N"},
			ytrans:   "Y+1",
			vars:     map[string]float64{"M": 3.5, "N": 0.5, "Y": -1.0},
			wantX:    []float64{-5.0, 7.0},
			wantY:    0.0,
		},
	} {
		inre := regexp.MustCompile(tt.inre)
		names := readNames(inre)
		xExpr, err := parseX(names, tt.xtrans)
		if err != nil {
			panic(err)
		}
		for xi, expr := range xExpr {
			if x := expr.value(tt.vars); x != tt.wantX[xi] {
				t.Errorf("%d: expected x[%d] = %f, got %f", i, xi, tt.wantX[xi], x)
			}
			if x := expr.String(); x != tt.xstrings[xi] {
				t.Errorf("%d: expected x[%d].String() = %s, got %s", i, xi, tt.xstrings[xi], x)
			}
			for xouti, xout := range expr.output {
				if xout.String() != tt.xrpn[xi][xouti] {
					t.Errorf("%d: expected x[%d].output[%d].String() = %s, got %s", i, xi, xouti, tt.xrpn[xi][xouti], xout.String())
				}
			}
		}
		names["Y"] = struct{}{}
		yExpr, err := parseY(names, tt.ytrans)
		if err != nil {
			panic(err)
		}
		if y := yExpr.value(tt.vars); y != tt.wantY {
			t.Errorf("%d: expected y = %f, got %f", i, tt.wantY, y)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*
Package trace contains types for representing trace information, and
functions for global configuration of tracing.

The following assumes a basic familiarity with OpenCensus concepts.
See http://opencensus.io.

Enabling Tracing for a Program

To use OpenCensus tracing, register at least one Exporter. You can use
one of the provided exporters or write your own.

    trace.RegisterExporter(anExporter)

By default, traces will be sampled relatively rarely. To change the sampling
frequency for your entire program, call SetDefaultSampler. Use a ProbabilitySampler
to sample a subset of traces, or use AlwaysSample to collect a trace on every run:

    trace.SetDefaultSampler(trace.AlwaysSample())

Adding Spans to a Trace

A trace consists of a tree of spans. In Go, the current span is carried in a
context.Context.

It is common to want to capture all the activity of a function call in a span. For
this to work, the function must take a context.Context as a parameter. Add these two
lines to the top of the function:

    ctx, span := trace.StartSpan(ctx, "your choice of name")
    defer span.End()

StartSpan will create a new top-level span if the context
doesn't contain another span, otherwise it will create a child span.

As a suggestion, use the fully-qualified function name as the span name, e.g.
"github.com/me/mypackage.Run".
*/
package trace // import "go.opencensus.io/trace"
<commit_msg>Remove ending dot in documentation URL (#630)<commit_after>// Copyright 2017, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*
Package trace contains types for representing trace information, and
functions for global configuration of tracing.

The following assumes a basic familiarity with OpenCensus concepts.
See http://opencensus.io

Enabling Tracing for a Program

To use OpenCensus tracing, register at least one Exporter. You can use
one of the provided exporters or write your own.

    trace.RegisterExporter(anExporter)

By default, traces will be sampled relatively rarely. To change the sampling
frequency for your entire program, call SetDefaultSampler. Use a ProbabilitySampler
to sample a subset of traces, or use AlwaysSample to collect a trace on every run:

    trace.SetDefaultSampler(trace.AlwaysSample())

Adding Spans to a Trace

A trace consists of a tree of spans. In Go, the current span is carried in a
context.Context.

It is common to want to capture all the activity of a function call in a span. For
this to work, the function must take a context.Context as a parameter. Add these two
lines to the top of the function:

    ctx, span := trace.StartSpan(ctx, "your choice of name")
    defer span.End()

StartSpan will create a new top-level span if the context
doesn't contain another span, otherwise it will create a child span.

As a suggestion, use the fully-qualified function name as the span name, e.g.
"github.com/me/mypackage.Run".
*/
package trace // import "go.opencensus.io/trace"
<|endoftext|>


<commit_before>package gopcap

//-----------------------------------------------------------------------------
// Unknown Transport
//-----------------------------------------------------------------------------

// UnknownTransport represents the data for a Transport-Layer packet that gopcap doesn't
// understand. It simply provides uninterpreted data representing the entire transport-layer
// packet.
type UnknownTransport struct {
	data []byte
}

func (u *UnknownTransport) TransportData() []byte {
	return u.data
}

func (u *UnknownTransport) FromBytes(data []byte) error {
	u.data = data
	return nil
}
<commit_msg>Define the TCP segment.<commit_after>package gopcap

//-----------------------------------------------------------------------------
// Unknown Transport
//-----------------------------------------------------------------------------

// UnknownTransport represents the data for a Transport-Layer packet that gopcap doesn't
// understand. It simply provides uninterpreted data representing the entire transport-layer
// packet.
type UnknownTransport struct {
	data []byte
}

func (u *UnknownTransport) TransportData() []byte {
	return u.data
}

func (u *UnknownTransport) FromBytes(data []byte) error {
	u.data = data
	return nil
}

//-----------------------------------------------------------------------------
// TCPSegment
//-----------------------------------------------------------------------------

// TCPSegment represents the data for a single Transmission Control Protocol segment. This method of
// storing a TCPSegment is less efficient than storing the binary representation on the wire.
type TCPSegment struct {
	SourcePort      uint16
	DestinationPort uint16
	SequenceNumber  uint32
	AckNumber       uint32
	HeaderSize      uint8
	NS              bool // This should be viewed as a temporary solution for flags: it's hugely space-inefficient,
	CWR             bool // and so I'll probably update the TCPSegment to handle flags differently.
	ECE             bool
	URG             bool
	ACK             bool
	PSH             bool
	RST             bool
	SYN             bool
	FIN             bool
	WindowSize      uint16
	Checksum        uint16
	UrgentOffset    uint16
	OptionData      []byte // This is temporary. We should handle TCP options properly.
}
<|endoftext|>