<commit_before>// Package files is for storing files on the cozy, including binary ones like
// photos and movies. The range of possible operations with this endpoint goes
// from simple ones, like uploading a file, to more complex ones, like renaming
// a folder. It also ensure that an instance is not exceeding its quota, and
// keeps a trash to recover files recently deleted.
package files

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strings"

	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/gin-gonic/gin"
	"github.com/spf13/afero"
)

// DefaultContentType is used for files uploaded with no content-type
const DefaultContentType = "application/octet-stream"

// DocType is the type of document, eg. file or folder
type DocType string

const (
	// FileDocType is document type
	FileDocType DocType = "io.cozy.files"
	// FolderDocType is document type
	FolderDocType = "io.cozy.folders"
)

var (
	errDocAlreadyExists = errors.New("Directory already exists")
	errDocTypeInvalid   = errors.New("Invalid document type")
	errIllegalFilename  = errors.New("Invalid filename: empty or contains one of these illegal characters: / \\ : ? * \" |")
)

var regFileName = regexp.MustCompile("[\\/\\\\:\\?\\*\"|]+")

// DocMetadata encapsulates the few metadata linked to a document
// creation request.
type DocMetadata struct {
	Type       DocType
	Name       string
	FolderID   string
	Executable bool
	Tags       []string
}

func (metadata *DocMetadata) path() string {
	return metadata.FolderID + "/" + metadata.Name
}

// NewDocMetadata is the DocMetadata constructor. All inputs are
// validated and if wrong, an error is returned.
func NewDocMetadata(docTypeStr, name, folderID, tagsStr string, executable bool) (*DocMetadata, error) {
	docType, err := parseDocType(docTypeStr)
	if err != nil {
		return nil, err
	}

	if err = checkFileName(name); err != nil {
		return nil, err
	}

	// FolderID is not mandatory. If empty, the document is at the root
	// of the FS
	if folderID != "" {
		if err = checkFileName(folderID); err != nil {
			return nil, err
		}
	}

	tags := parseTags(tagsStr)

	return &DocMetadata{
		Type:       docType,
		Name:       name,
		FolderID:   folderID,
		Tags:       tags,
		Executable: executable,
	}, nil
}

// Upload is the method for uploading a file
//
// This will be used to upload a file
// @TODO
func Upload(metadata *DocMetadata, storage afero.Fs, body io.ReadCloser) error {
	if metadata.Type != FileDocType {
		return errDocTypeInvalid
	}

	path := metadata.path()

	defer body.Close()
	return afero.SafeWriteReader(storage, path, body)
}

// CreateDirectory is the method for creating a new directory
//
// @TODO
func CreateDirectory(metadata *DocMetadata, storage afero.Fs) error {
	if metadata.Type != FolderDocType {
		return errDocTypeInvalid
	}

	path := metadata.path()

	exists, err := afero.DirExists(storage, path)
	if err != nil {
		return err
	}
	if exists {
		return errDocAlreadyExists
	}

	return storage.Mkdir(path, 0777)
}

// CreationHandler handle all POST requests on /files/:folder-id
// aiming at creating a new document in the FS. Given the Type
// parameter of the request, it will either upload a new file or
// create a new directory.
//
// swagger:route POST /files/:folder-id files uploadFileOrCreateDir
func CreationHandler(c *gin.Context) {
	instance := middlewares.GetInstance(c)
	storage, err := instance.GetStorageProvider()
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	metadata, err := NewDocMetadata(
		c.Query("Type"),
		c.Query("Name"),
		c.Param("folder-id"),
		c.Query("Tags"),
		c.Query("Executable") == "true",
	)

	if err != nil {
		c.AbortWithError(http.StatusUnprocessableEntity, err)
		return
	}

	contentType := c.ContentType()
	if contentType == "" {
		contentType = DefaultContentType
	}

	exists, err := checkParentFolderID(storage, metadata.FolderID)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	if !exists {
		err = fmt.Errorf("Parent folder with given FolderID does not exist")
		c.AbortWithError(http.StatusNotFound, err)
		return
	}

	fmt.Printf("%s:\n\t- %+v\n\t- %v\n", metadata.Name, metadata, contentType)

	switch metadata.Type {
	case FileDocType:
		err = Upload(metadata, storage, c.Request.Body)
	case FolderDocType:
		err = CreateDirectory(metadata, storage)
	}

	if err != nil {
		c.AbortWithError(makeCode(err), err)
		return
	}

	data := []byte{'O', 'K'}
	c.Data(http.StatusCreated, jsonapi.ContentType, data)
}

// Routes sets the routing for the files service
func Routes(router *gin.RouterGroup) {
	router.POST("/", CreationHandler)
	router.POST("/:folder-id", CreationHandler)
}

func makeCode(err error) (code int) {
	switch err {
	case errDocAlreadyExists:
		code = http.StatusConflict
	default:
		code = http.StatusInternalServerError
	}
	return
}

func parseTags(str string) []string {
	var tags []string
	for _, tag := range strings.Split(str, ",") {
		// @TODO: more sanitization maybe ?
		tag = strings.TrimSpace(tag)
		if tag != "" {
			tags = append(tags, tag)
		}
	}
	return tags
}

func parseDocType(docType string) (DocType, error) {
	var result DocType
	var err error
	switch docType {
	case "io.cozy.files":
		result = FileDocType
	case "io.cozy.folders":
		result = FolderDocType
	default:
		err = errDocTypeInvalid
	}
	return result, err
}

func checkFileName(str string) error {
	if str == "" || regFileName.MatchString(str) {
		return errIllegalFilename
	}
	return nil
}

func checkParentFolderID(storage afero.Fs, folderID string) (bool, error) {
	if folderID == "" {
		return true, nil
	}

	exists, err := afero.DirExists(storage, folderID)
	if err != nil {
		return false, err
	}
	if !exists {
		return false, nil
	}

	return true, nil
}
<commit_msg>Remove some illegal characters and use strings.ContainsAny<commit_after>// Package files is for storing files on the cozy, including binary ones like
// photos and movies. The range of possible operations with this endpoint goes
// from simple ones, like uploading a file, to more complex ones, like renaming
// a folder. It also ensure that an instance is not exceeding its quota, and
// keeps a trash to recover files recently deleted.
package files

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"

	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/gin-gonic/gin"
	"github.com/spf13/afero"
)

// DefaultContentType is used for files uploaded with no content-type
const DefaultContentType = "application/octet-stream"

// DocType is the type of document, eg. file or folder
type DocType string

const (
	// FileDocType is document type
	FileDocType DocType = "io.cozy.files"
	// FolderDocType is document type
	FolderDocType = "io.cozy.folders"

	// ForbiddenFilenameChars is the list of forbidden characters in a filename.
	ForbiddenFilenameChars = "/"
)

var (
	errDocAlreadyExists = errors.New("Directory already exists")
	errDocTypeInvalid   = errors.New("Invalid document type")
	errIllegalFilename  = errors.New("Invalid filename: empty or contains an illegal character")
)

// DocMetadata encapsulates the few metadata linked to a document
// creation request.
type DocMetadata struct {
	Type       DocType
	Name       string
	FolderID   string
	Executable bool
	Tags       []string
}

func (metadata *DocMetadata) path() string {
	return metadata.FolderID + "/" + metadata.Name
}

// NewDocMetadata is the DocMetadata constructor. All inputs are
// validated and if wrong, an error is returned.
func NewDocMetadata(docTypeStr, name, folderID, tagsStr string, executable bool) (*DocMetadata, error) {
	docType, err := parseDocType(docTypeStr)
	if err != nil {
		return nil, err
	}

	if err = checkFileName(name); err != nil {
		return nil, err
	}

	// FolderID is not mandatory. If empty, the document is at the root
	// of the FS
	if folderID != "" {
		if err = checkFileName(folderID); err != nil {
			return nil, err
		}
	}

	tags := parseTags(tagsStr)

	return &DocMetadata{
		Type:       docType,
		Name:       name,
		FolderID:   folderID,
		Tags:       tags,
		Executable: executable,
	}, nil
}

// Upload is the method for uploading a file
//
// This will be used to upload a file
// @TODO
func Upload(metadata *DocMetadata, storage afero.Fs, body io.ReadCloser) error {
	if metadata.Type != FileDocType {
		return errDocTypeInvalid
	}

	path := metadata.path()

	defer body.Close()
	return afero.SafeWriteReader(storage, path, body)
}

// CreateDirectory is the method for creating a new directory
//
// @TODO
func CreateDirectory(metadata *DocMetadata, storage afero.Fs) error {
	if metadata.Type != FolderDocType {
		return errDocTypeInvalid
	}

	path := metadata.path()

	exists, err := afero.DirExists(storage, path)
	if err != nil {
		return err
	}
	if exists {
		return errDocAlreadyExists
	}

	return storage.Mkdir(path, 0777)
}

// CreationHandler handle all POST requests on /files/:folder-id
// aiming at creating a new document in the FS. Given the Type
// parameter of the request, it will either upload a new file or
// create a new directory.
//
// swagger:route POST /files/:folder-id files uploadFileOrCreateDir
func CreationHandler(c *gin.Context) {
	instance := middlewares.GetInstance(c)
	storage, err := instance.GetStorageProvider()
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}

	metadata, err := NewDocMetadata(
		c.Query("Type"),
		c.Query("Name"),
		c.Param("folder-id"),
		c.Query("Tags"),
		c.Query("Executable") == "true",
	)

	if err != nil {
		c.AbortWithError(http.StatusUnprocessableEntity, err)
		return
	}

	contentType := c.ContentType()
	if contentType == "" {
		contentType = DefaultContentType
	}

	exists, err := checkParentFolderID(storage, metadata.FolderID)
	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	if !exists {
		err = fmt.Errorf("Parent folder with given FolderID does not exist")
		c.AbortWithError(http.StatusNotFound, err)
		return
	}

	fmt.Printf("%s:\n\t- %+v\n\t- %v\n", metadata.Name, metadata, contentType)

	switch metadata.Type {
	case FileDocType:
		err = Upload(metadata, storage, c.Request.Body)
	case FolderDocType:
		err = CreateDirectory(metadata, storage)
	}

	if err != nil {
		c.AbortWithError(makeCode(err), err)
		return
	}

	data := []byte{'O', 'K'}
	c.Data(http.StatusCreated, jsonapi.ContentType, data)
}

// Routes sets the routing for the files service
func Routes(router *gin.RouterGroup) {
	router.POST("/", CreationHandler)
	router.POST("/:folder-id", CreationHandler)
}

func makeCode(err error) (code int) {
	switch err {
	case errDocAlreadyExists:
		code = http.StatusConflict
	default:
		code = http.StatusInternalServerError
	}
	return
}

func parseTags(str string) []string {
	var tags []string
	for _, tag := range strings.Split(str, ",") {
		tag = strings.TrimSpace(tag)
		if tag != "" {
			tags = append(tags, tag)
		}
	}
	return tags
}

func parseDocType(docType string) (DocType, error) {
	var result DocType
	var err error
	switch docType {
	case "io.cozy.files":
		result = FileDocType
	case "io.cozy.folders":
		result = FolderDocType
	default:
		err = errDocTypeInvalid
	}
	return result, err
}

func checkFileName(str string) error {
	if str == "" || strings.ContainsAny(str, ForbiddenFilenameChars) {
		return errIllegalFilename
	}
	return nil
}

func checkParentFolderID(storage afero.Fs, folderID string) (bool, error) {
	if folderID == "" {
		return true, nil
	}

	exists, err := afero.DirExists(storage, folderID)
	if err != nil {
		return false, err
	}
	if !exists {
		return false, nil
	}

	return true, nil
}
<|endoftext|>


<commit_before>package dgkala

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/buger/jsonparser"
)

const (
	incredibleOffersAPIAddress = "https://service2.digikala.com/api/IncredibleOffer/GetIncredibleOffer"
	searchAPIAddress           = "https://search.digikala.com/api/search"
	staticFilesPath            = "https://file.digikala.com/digikala/"
)

// ProductExistsStatus is a iota type for product existing status for buying
type ProductExistsStatus int

const (
	_ ProductExistsStatus = iota
	_
	// Available means the product is available to buy
	Available
	// OutOfStock means the product is not available  now and is out of stock
	OutOfStock
	// Discontinued means the product is discontinued
	Discontinued
)

// IncredibleOffer is a struct containing
// DGKala incredible offer properties
type IncredibleOffer struct {
	ID         uint
	ProductID  uint
	Title      string
	ImagePaths struct {
		Original, Size70, Size110, Size180, Size220 string
	}
	BannerPath         string
	BannerPathMobile   string
	BannerPathTablet   string
	Row                uint
	ProductTitleFa     string
	ProductTitleEn     string
	Discount           uint
	Price              uint
	OnlyForApplication bool
	OnlyForMembers     bool
}

type incredibleOffersResponse struct {
	Data   []IncredibleOffer
	Status string
}

// ProductColor is a struct with properties of products colors
type ProductColor struct {
	Title string
	Hex   string
	Code  string
}

// ProductSearchResult is a struct containing a product details for a search result
type ProductSearchResult struct {
	ID                  int64
	EnglishTitle        string
	PersianTitle        string
	Image               string
	ExistsStatus        ProductExistsStatus
	IsActive            bool
	URL                 string
	Rate                int64
	MinimumPrice        int64
	MaximumPrice        int64
	Likes               int64
	LastPeriodLikes     int64
	Views               int64
	LastPeriodViews     int64
	IsSpecialOffer      bool
	RegisteredDateTime  time.Time
	HasVideo            bool
	Colors              []ProductColor
	UserRatingCount     int64
	Favorites           int64
	LastPeriodFavorites int64
	LastPeriodSales     int64
	HasGift             bool
	HTMLDetails         string
}

// SearchResult returns a struct containing results of the search for a keyword
type SearchResult struct {
	ResponseTime int64
	Count        int64
	Results      []ProductSearchResult
}

func sendRequest(address string, headers map[string]string) (*http.Response, error) {
	request, err := http.NewRequest(http.MethodGet, address, nil)
	if err != nil {
		return nil, err
	}

	for key, value := range headers {
		request.Header.Add(key, value)
	}

	client := http.Client{}
	response, err := client.Do(request)
	return response, err
}

func getStaticResourceAddress(resourcePath string) string {
	return fmt.Sprintf("%s%s", staticFilesPath, resourcePath)
}

func getSearchAPIAddress(keyword string) string {
	query := url.QueryEscape(keyword)
	return fmt.Sprintf("%s?keyword=%s", searchAPIAddress, query)
}

// IncredibleOffers get a slice of DGKala IncredibleOffer items
func IncredibleOffers() ([]IncredibleOffer, error) {
	headers := map[string]string{"ApplicationVersion": "1.3.2"}
	response, err := sendRequest(incredibleOffersAPIAddress, headers)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}
	offersResponse := &incredibleOffersResponse{}
	err = json.Unmarshal(body, offersResponse)
	if err != nil {
		return nil, err
	}
	IncredibleOffers := offersResponse.Data
	return IncredibleOffers, nil
}

// Search for a product in DGKala and return a slice of DGKala SearchResult items
func Search(keyword string) (SearchResult, error) {
	searchAddress := getSearchAPIAddress(keyword)
	httpResponse, err := http.Get(searchAddress)
	if err != nil {
		return SearchResult{}, err
	}

	responseBody, err := ioutil.ReadAll(httpResponse.Body)
	if err != nil {
		return SearchResult{}, err
	}

	responseTime, err := jsonparser.GetInt(responseBody, "took")
	if err != nil {
		return SearchResult{}, err
	}

	count, err := jsonparser.GetInt(responseBody, "hits", "total")
	if err != nil {
		return SearchResult{}, err
	}

	productSearchResults := []ProductSearchResult{}
	realResultsJSONPath := []string{"hits", "hits"}
	parentJSONResultKey := "_source"
	jsonparser.ArrayEach(responseBody, func(value []byte, _ jsonparser.ValueType, _ int, _ error) {
		ID, _ := jsonparser.GetInt(value, parentJSONResultKey, "Id")
		englishTitle, _ := jsonparser.GetString(value, parentJSONResultKey, "EnTitle")
		persianTitle, _ := jsonparser.GetString(value, parentJSONResultKey, "FaTitle")
		imagePath, _ := jsonparser.GetString(value, parentJSONResultKey, "ImagePath")
		image := getStaticResourceAddress(imagePath)
		existsStatusInt, _ := jsonparser.GetInt(value, parentJSONResultKey, "ExistStatus")
		existsStatus := ProductExistsStatus(existsStatusInt)
		isActive, _ := jsonparser.GetBoolean(value, parentJSONResultKey, "IsActive")
		URL, _ := jsonparser.GetString(value, parentJSONResultKey, "UrlCode")
		rate, _ := jsonparser.GetInt(value, parentJSONResultKey, "Rate")
		minimumPrice, _ := jsonparser.GetInt(value, parentJSONResultKey, "MinPrice")
		maximumPrice, _ := jsonparser.GetInt(value, parentJSONResultKey, "MaxPrice")
		likes, _ := jsonparser.GetInt(value, parentJSONResultKey, "LikeCounter")
		lastPeriodLikes, _ := jsonparser.GetInt(value, parentJSONResultKey, "LastPeriodLikeCounter")
		views, _ := jsonparser.GetInt(value, parentJSONResultKey, "ViewCounter")
		lastPeriodViews, _ := jsonparser.GetInt(value, parentJSONResultKey, "LastPeriodViewCounter")
		isSpecialOffer, _ := jsonparser.GetBoolean(value, parentJSONResultKey, "IsSpecialOffer")
		regDateTimeString, _ := jsonparser.GetString(value, parentJSONResultKey, "RegDateTime")
		registeredDateTime, _ := time.Parse("2006-01-02T15:04:05", regDateTimeString)
		hasVideo, _ := jsonparser.GetBoolean(value, parentJSONResultKey, "HasVideo")
		colors := []ProductColor{}
		jsonparser.ArrayEach(value, func(colorsValue []byte, _ jsonparser.ValueType, _ int, _ error) {
			colorTitle, _ := jsonparser.GetString(colorsValue, "ColorTitle")
			colorHex, _ := jsonparser.GetString(colorsValue, "ColorHex")
			colorCode, _ := jsonparser.GetString(colorsValue, "ColorCode")
			currentColor := ProductColor{
				colorTitle,
				colorHex,
				colorCode,
			}
			colors = append(colors, currentColor)
		}, parentJSONResultKey, "ProductColorList")
		userRatingCount, _ := jsonparser.GetInt(value, parentJSONResultKey, "UserRating")
		favorites, _ := jsonparser.GetInt(value, parentJSONResultKey, "FavoriteCounter")
		lastPeriodFavorites, _ := jsonparser.GetInt(value, parentJSONResultKey, "LastPeriodFavoriteCounter")
		lastPeriodSales, _ := jsonparser.GetInt(value, parentJSONResultKey, "LastPeriodSaleCounter")
		hasGift, _ := jsonparser.GetBoolean(value, parentJSONResultKey, "HasGift")
		hTMLDetails, _ := jsonparser.GetString(value, parentJSONResultKey, "DetailSource")

		currentProductSearchResult := ProductSearchResult{
			ID,
			englishTitle,
			persianTitle,
			image,
			existsStatus,
			isActive,
			URL,
			rate,
			minimumPrice,
			maximumPrice,
			likes,
			lastPeriodLikes,
			views,
			lastPeriodViews,
			isSpecialOffer,
			registeredDateTime,
			hasVideo,
			colors,
			userRatingCount,
			favorites,
			lastPeriodFavorites,
			lastPeriodSales,
			hasGift,
			hTMLDetails,
		}
		productSearchResults = append(productSearchResults, currentProductSearchResult)
	}, realResultsJSONPath...)

	result := SearchResult{
		responseTime,
		count,
		productSearchResults,
	}

	return result, nil
}
<commit_msg>refactor(dgkala): use formatting directives in string API addresses<commit_after>package dgkala

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	"github.com/buger/jsonparser"
)

const (
	incredibleOffersAPIAddress = "https://service2.digikala.com/api/IncredibleOffer/GetIncredibleOffer"
	searchAPIAddress           = "https://search.digikala.com/api/search?keyword=%s"
	staticFilesPath            = "https://file.digikala.com/digikala/%s"
)

// ProductExistsStatus is a iota type for product existing status for buying
type ProductExistsStatus int

const (
	_ ProductExistsStatus = iota
	_
	// Available means the product is available to buy
	Available
	// OutOfStock means the product is not available  now and is out of stock
	OutOfStock
	// Discontinued means the product is discontinued
	Discontinued
)

// IncredibleOffer is a struct containing
// DGKala incredible offer properties
type IncredibleOffer struct {
	ID         uint
	ProductID  uint
	Title      string
	ImagePaths struct {
		Original, Size70, Size110, Size180, Size220 string
	}
	BannerPath         string
	BannerPathMobile   string
	BannerPathTablet   string
	Row                uint
	ProductTitleFa     string
	ProductTitleEn     string
	Discount           uint
	Price              uint
	OnlyForApplication bool
	OnlyForMembers     bool
}

type incredibleOffersResponse struct {
	Data   []IncredibleOffer
	Status string
}

// ProductColor is a struct with properties of products colors
type ProductColor struct {
	Title string
	Hex   string
	Code  string
}

// ProductSearchResult is a struct containing a product details for a search result
type ProductSearchResult struct {
	ID                  int64
	EnglishTitle        string
	PersianTitle        string
	Image               string
	ExistsStatus        ProductExistsStatus
	IsActive            bool
	URL                 string
	Rate                int64
	MinimumPrice        int64
	MaximumPrice        int64
	Likes               int64
	LastPeriodLikes     int64
	Views               int64
	LastPeriodViews     int64
	IsSpecialOffer      bool
	RegisteredDateTime  time.Time
	HasVideo            bool
	Colors              []ProductColor
	UserRatingCount     int64
	Favorites           int64
	LastPeriodFavorites int64
	LastPeriodSales     int64
	HasGift             bool
	HTMLDetails         string
}

// SearchResult returns a struct containing results of the search for a keyword
type SearchResult struct {
	ResponseTime int64
	Count        int64
	Results      []ProductSearchResult
}

func sendRequest(address string, headers map[string]string) (*http.Response, error) {
	request, err := http.NewRequest(http.MethodGet, address, nil)
	if err != nil {
		return nil, err
	}

	for key, value := range headers {
		request.Header.Add(key, value)
	}

	client := http.Client{}
	response, err := client.Do(request)
	return response, err
}

func getStaticResourceAddress(resourcePath string) string {
	return fmt.Sprintf(staticFilesPath, resourcePath)
}

func getSearchAPIAddress(keyword string) string {
	query := url.QueryEscape(keyword)
	return fmt.Sprintf(searchAPIAddress, query)
}

// IncredibleOffers get a slice of DGKala IncredibleOffer items
func IncredibleOffers() ([]IncredibleOffer, error) {
	headers := map[string]string{"ApplicationVersion": "1.3.2"}
	response, err := sendRequest(incredibleOffersAPIAddress, headers)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}
	var offersResponse incredibleOffersResponse
	err = json.Unmarshal(body, offersResponse)
	if err != nil {
		return nil, err
	}
	incredibleOffers := offersResponse.Data
	return incredibleOffers, nil
}

// Search for a product in DGKala and return a slice of DGKala SearchResult items
func Search(keyword string) (SearchResult, error) {
	searchAddress := getSearchAPIAddress(keyword)
	httpResponse, err := http.Get(searchAddress)
	if err != nil {
		return SearchResult{}, err
	}

	responseBody, err := ioutil.ReadAll(httpResponse.Body)
	if err != nil {
		return SearchResult{}, err
	}

	responseTime, err := jsonparser.GetInt(responseBody, "took")
	if err != nil {
		return SearchResult{}, err
	}

	count, err := jsonparser.GetInt(responseBody, "hits", "total")
	if err != nil {
		return SearchResult{}, err
	}

	productSearchResults := []ProductSearchResult{}
	realResultsJSONPath := []string{"hits", "hits"}
	parentJSONResultKey := "_source"
	jsonparser.ArrayEach(responseBody, func(value []byte, _ jsonparser.ValueType, _ int, _ error) {
		ID, _ := jsonparser.GetInt(value, parentJSONResultKey, "Id")
		englishTitle, _ := jsonparser.GetString(value, parentJSONResultKey, "EnTitle")
		persianTitle, _ := jsonparser.GetString(value, parentJSONResultKey, "FaTitle")
		imagePath, _ := jsonparser.GetString(value, parentJSONResultKey, "ImagePath")
		image := getStaticResourceAddress(imagePath)
		existsStatusInt, _ := jsonparser.GetInt(value, parentJSONResultKey, "ExistStatus")
		existsStatus := ProductExistsStatus(existsStatusInt)
		isActive, _ := jsonparser.GetBoolean(value, parentJSONResultKey, "IsActive")
		URL, _ := jsonparser.GetString(value, parentJSONResultKey, "UrlCode")
		rate, _ := jsonparser.GetInt(value, parentJSONResultKey, "Rate")
		minimumPrice, _ := jsonparser.GetInt(value, parentJSONResultKey, "MinPrice")
		maximumPrice, _ := jsonparser.GetInt(value, parentJSONResultKey, "MaxPrice")
		likes, _ := jsonparser.GetInt(value, parentJSONResultKey, "LikeCounter")
		lastPeriodLikes, _ := jsonparser.GetInt(value, parentJSONResultKey, "LastPeriodLikeCounter")
		views, _ := jsonparser.GetInt(value, parentJSONResultKey, "ViewCounter")
		lastPeriodViews, _ := jsonparser.GetInt(value, parentJSONResultKey, "LastPeriodViewCounter")
		isSpecialOffer, _ := jsonparser.GetBoolean(value, parentJSONResultKey, "IsSpecialOffer")
		regDateTimeString, _ := jsonparser.GetString(value, parentJSONResultKey, "RegDateTime")
		registeredDateTime, _ := time.Parse("2006-01-02T15:04:05", regDateTimeString)
		hasVideo, _ := jsonparser.GetBoolean(value, parentJSONResultKey, "HasVideo")
		colors := []ProductColor{}
		jsonparser.ArrayEach(value, func(colorsValue []byte, _ jsonparser.ValueType, _ int, _ error) {
			colorTitle, _ := jsonparser.GetString(colorsValue, "ColorTitle")
			colorHex, _ := jsonparser.GetString(colorsValue, "ColorHex")
			colorCode, _ := jsonparser.GetString(colorsValue, "ColorCode")
			currentColor := ProductColor{
				colorTitle,
				colorHex,
				colorCode,
			}
			colors = append(colors, currentColor)
		}, parentJSONResultKey, "ProductColorList")
		userRatingCount, _ := jsonparser.GetInt(value, parentJSONResultKey, "UserRating")
		favorites, _ := jsonparser.GetInt(value, parentJSONResultKey, "FavoriteCounter")
		lastPeriodFavorites, _ := jsonparser.GetInt(value, parentJSONResultKey, "LastPeriodFavoriteCounter")
		lastPeriodSales, _ := jsonparser.GetInt(value, parentJSONResultKey, "LastPeriodSaleCounter")
		hasGift, _ := jsonparser.GetBoolean(value, parentJSONResultKey, "HasGift")
		hTMLDetails, _ := jsonparser.GetString(value, parentJSONResultKey, "DetailSource")

		currentProductSearchResult := ProductSearchResult{
			ID,
			englishTitle,
			persianTitle,
			image,
			existsStatus,
			isActive,
			URL,
			rate,
			minimumPrice,
			maximumPrice,
			likes,
			lastPeriodLikes,
			views,
			lastPeriodViews,
			isSpecialOffer,
			registeredDateTime,
			hasVideo,
			colors,
			userRatingCount,
			favorites,
			lastPeriodFavorites,
			lastPeriodSales,
			hasGift,
			hTMLDetails,
		}
		productSearchResults = append(productSearchResults, currentProductSearchResult)
	}, realResultsJSONPath...)

	result := SearchResult{
		responseTime,
		count,
		productSearchResults,
	}

	return result, nil
}
<|endoftext|>


<commit_before>package collectors

import (
	"fmt"
	"strings"
	"time"

	"github.com/StackExchange/scollector/metadata"
	"github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/scollector/util"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_sntp_windows})
}

func c_sntp_windows() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	const metric = "sntp."
	var (
		stratum string
		delay   float64
		when    float64
		source  string
		poll    float64
	)
	if err := util.ReadCommand(func(line string) error {
		f := strings.SplitN(line, ":", 2)
		if len(f) != 2 {
			return nil
		}
		f[1] = strings.TrimSpace(f[1])
		switch f[0] {
		case "Stratum":
			sf := strings.Fields(f[1])
			if len(sf) < 1 {
				return fmt.Errorf("Unexpected value for stratum")
			}
			stratum = sf[0]
		case "Root Delay":
			d, err := time.ParseDuration(f[1])
			if err != nil {
				return err
			}
			delay = d.Seconds()
		case "Last Successful Sync Time":
			if f[1] == "unspecified" {
				break
			}
			t, err := time.Parse("1/2/2006 3:04:05 PM", f[1])
			if err != nil {
				return err
			}
			when = time.Since(t).Seconds()
		case "Source":
			source = strings.TrimSpace(f[1])
		case "Poll Interval":
			sf := strings.Fields(f[1])
			if len(sf) != 2 {
				return fmt.Errorf("Unexpected value for Poll Interval")
			}
			s := strings.Trim(sf[1], "()")
			d, err := time.ParseDuration(strings.TrimSpace(s))
			if err != nil {
				return err
			}
			poll = d.Seconds()
		}
		return nil
	}, "w32tm", "/query", "/status"); err != nil {
		return md, err
	}
	tags := opentsdb.TagSet{"remote": source}
	Add(&md, metric+"stratum", stratum, tags, metadata.Gauge, "Stratum", "")
	Add(&md, metric+"delay", delay, tags, metadata.Gauge, metadata.Second, "")
	Add(&md, metric+"when", when, tags, metadata.Gauge, metadata.Second, "")
	Add(&md, metric+"poll", poll, tags, metadata.Gauge, metadata.Second, "")
	err := util.ReadCommand(func(line string) error {
		f := strings.SplitN(line, ",", 2)
		if len(f) != 2 {
			return nil
		}
		d, err := time.ParseDuration(strings.TrimSpace(f[1]))
		if err != nil {
			return err
		}
		Add(&md, metric+"offset", d.Seconds(), tags, metadata.Gauge, metadata.Second, "")
		return nil
	}, "w32tm", "/stripchart", fmt.Sprintf("/computer:%v", source), "/samples:1", "/dataonly")
	return md, err
}
<commit_msg>cmd/scollector: No error if time service isn't running<commit_after>package collectors

import (
	"fmt"
	"strings"
	"time"

	"github.com/StackExchange/scollector/metadata"
	"github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/scollector/util"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_sntp_windows})
}

func c_sntp_windows() (opentsdb.MultiDataPoint, error) {
	var md opentsdb.MultiDataPoint
	const metric = "sntp."
	var (
		stratum string
		delay   float64
		when    float64
		source  string
		poll    float64
	)
	if err := util.ReadCommand(func(line string) error {
		f := strings.SplitN(line, ":", 2)
		if len(f) != 2 {
			return nil
		}
		f[1] = strings.TrimSpace(f[1])
		switch f[0] {
		case "Stratum":
			sf := strings.Fields(f[1])
			if len(sf) < 1 {
				return fmt.Errorf("Unexpected value for stratum")
			}
			stratum = sf[0]
		case "Root Delay":
			d, err := time.ParseDuration(f[1])
			if err != nil {
				return err
			}
			delay = d.Seconds()
		case "Last Successful Sync Time":
			if f[1] == "unspecified" {
				break
			}
			t, err := time.Parse("1/2/2006 3:04:05 PM", f[1])
			if err != nil {
				return err
			}
			when = time.Since(t).Seconds()
		case "Source":
			source = strings.TrimSpace(f[1])
		case "Poll Interval":
			sf := strings.Fields(f[1])
			if len(sf) != 2 {
				return fmt.Errorf("Unexpected value for Poll Interval")
			}
			s := strings.Trim(sf[1], "()")
			d, err := time.ParseDuration(strings.TrimSpace(s))
			if err != nil {
				return err
			}
			poll = d.Seconds()
		}
		return nil
	}, "w32tm", "/query", "/status"); err != nil {
		return nil, nil
	}
	tags := opentsdb.TagSet{"remote": source}
	Add(&md, metric+"stratum", stratum, tags, metadata.Gauge, "Stratum", "")
	Add(&md, metric+"delay", delay, tags, metadata.Gauge, metadata.Second, "")
	Add(&md, metric+"when", when, tags, metadata.Gauge, metadata.Second, "")
	Add(&md, metric+"poll", poll, tags, metadata.Gauge, metadata.Second, "")
	err := util.ReadCommand(func(line string) error {
		f := strings.SplitN(line, ",", 2)
		if len(f) != 2 {
			return nil
		}
		d, err := time.ParseDuration(strings.TrimSpace(f[1]))
		if err != nil {
			return err
		}
		Add(&md, metric+"offset", d.Seconds(), tags, metadata.Gauge, metadata.Second, "")
		return nil
	}, "w32tm", "/stripchart", fmt.Sprintf("/computer:%v", source), "/samples:1", "/dataonly")
	return md, err
}
<|endoftext|>


<commit_before>package telnet

import (
	"fmt"
)

// RFC 854: http://tools.ietf.org/html/rfc854, http://support.microsoft.com/kb/231866

var codeMap map[byte]int
var commandMap map[int]byte

const (
	NUL  = iota // NULL, no operation
	ECHO = iota // Echo
	SGA  = iota // Suppress go ahead
	ST   = iota // Status
	TM   = iota // Timing mark
	BEL  = iota // Bell
	BS   = iota // Backspace
	HT   = iota // Horizontal tab
	LF   = iota // Line feed
	FF   = iota // Form feed
	CR   = iota // Carriage return
	TT   = iota // Terminal type
	WS   = iota // Window size
	TS   = iota // Terminal speed
	RFC  = iota // Remote flow control
	LM   = iota // Line mode
	EV   = iota // Environment variables
	SE   = iota // End of subnegotiation parameters.
	NOP  = iota // No operation.
	DM   = iota // Data Mark. The data stream portion of a Synch. This should always be accompanied by a TCP Urgent notification.
	BRK  = iota // Break. NVT character BRK.
	IP   = iota // Interrupt Process
	AO   = iota // Abort output
	AYT  = iota // Are you there
	EC   = iota // Erase character
	EL   = iota // Erase line
	GA   = iota // Go ahead signal
	SB   = iota // Indicates that what follows is subnegotiation of the indicated option.
	WILL = iota // Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option.
	WONT = iota // Indicates the refusal to perform, or continue performing, the indicated option.
	DO   = iota // Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option.
	DONT = iota // Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option.
	IAC  = iota // Interpret as command

	// Non-standard codes:
	CMP1 = iota // MCCP Compress
	CMP2 = iota // MCCP Compress2
	AARD = iota // Aardwolf MUD out of band communication, http://www.aardwolf.com/blog/2008/07/10/telnet-negotiation-control-mud-client-interaction/
	ATCP = iota // Achaea Telnet Client Protocol, http://www.ironrealms.com/rapture/manual/files/FeatATCP-txt.html
	GMCP = iota // Generic Mud Communication Protocol
)

func initLookups() {
	if codeMap != nil {
		return
	}

	codeMap = map[byte]int{}
	commandMap = map[int]byte{}

	commandMap[NUL] = '\x00'
	commandMap[ECHO] = '\x01'
	commandMap[SGA] = '\x03'
	commandMap[ST] = '\x05'
	commandMap[TM] = '\x06'
	commandMap[BEL] = '\x07'
	commandMap[BS] = '\x08'
	commandMap[HT] = '\x09'
	commandMap[LF] = '\x0a'
	commandMap[FF] = '\x0c'
	commandMap[CR] = '\x0d'
	commandMap[TT] = '\x18'
	commandMap[WS] = '\x1F'
	commandMap[TS] = '\x20'
	commandMap[RFC] = '\x21'
	commandMap[LM] = '\x22'
	commandMap[EV] = '\x24'
	commandMap[SE] = '\xf0'
	commandMap[NOP] = '\xf1'
	commandMap[DM] = '\xf2'
	commandMap[BRK] = '\xf3'
	commandMap[IP] = '\xf4'
	commandMap[AO] = '\xf5'
	commandMap[AYT] = '\xf6'
	commandMap[EC] = '\xf7'
	commandMap[EL] = '\xf8'
	commandMap[GA] = '\xf9'
	commandMap[SB] = '\xfa'
	commandMap[WILL] = '\xfb'
	commandMap[WONT] = '\xfc'
	commandMap[DO] = '\xfd'
	commandMap[DONT] = '\xfe'
	commandMap[IAC] = '\xff'

	commandMap[CMP1] = '\x55'
	commandMap[CMP2] = '\x56'
	// commandMap[AARD] = '\x66'
	commandMap[ATCP] = '\xc8'
	commandMap[GMCP] = '\xc9'

	for enum, code := range commandMap {
		codeMap[code] = enum
	}
}

// Process strips telnet control codes from the given input, returning the resulting input string
func Process(bytes []byte) string {
	initLookups()

	str := ""
	var bytesProcessed []byte

	for _, b := range bytes {
		_, found := codeMap[b]
		if !found {
			str = str + string(b)
		} else {
			bytesProcessed = append(bytesProcessed, b)
		}
	}

	if len(bytesProcessed) > 0 {
		fmt.Printf("Processed: %s\n", ToString(bytesProcessed))
	}

	return str
}

func Code(enum int) byte {
	initLookups()
	return commandMap[enum]
}

func ToString(bytes []byte) string {
	initLookups()

	str := ""
	for _, b := range bytes {
		enum, found := codeMap[b]
		result := ""

		if found {
			switch enum {
			case NUL:
				result = "NUL"
			case ECHO:
				result = "ECHO"
			case SGA:
				result = "SGA"
			case ST:
				result = "ST"
			case TM:
				result = "TM"
			case BEL:
				result = "BEL"
			case BS:
				result = "BS"
			case HT:
				result = "HT"
			case LF:
				result = "LF"
			case FF:
				result = "FF"
			case CR:
				result = "CR"
			case TT:
				result = "TT"
			case WS:
				result = "WS"
			case TS:
				result = "TS"
			case RFC:
				result = "RFC"
			case LM:
				result = "LM"
			case EV:
				result = "EV"
			case SE:
				result = "SE"
			case NOP:
				result = "NOP"
			case DM:
				result = "DM"
			case BRK:
				result = "BRK"
			case IP:
				result = "IP"
			case AO:
				result = "AO"
			case AYT:
				result = "AYT"
			case EC:
				result = "EC"
			case EL:
				result = "EL"
			case GA:
				result = "GA"
			case SB:
				result = "SB"
			case WILL:
				result = "WILL"
			case WONT:
				result = "WONT"
			case DO:
				result = "DO"
			case DONT:
				result = "DONT"
			case IAC:
				result = "IAC"
			case CMP1:
				result = "CMP1"
			case CMP2:
				result = "CMP2"
			case AARD:
				result = "AARD"
			case ATCP:
				result = "ATCP"
			case GMCP:
				result = "GMCP"
			}
		} else {
			result = "???"
		}

		if str != "" {
			str = str + " "
		}
		str = str + result
	}

	return str
}

func buildCommand(length int) []byte {
	command := make([]byte, length)
	command[0] = commandMap[IAC]
	return command
}

func WillEcho() []byte {
	command := buildCommand(3)
	command[1] = commandMap[WILL]
	command[2] = commandMap[ECHO]
	return command
}

func WontEcho() []byte {
	command := buildCommand(3)
	command[1] = commandMap[WONT]
	command[2] = commandMap[ECHO]
	return command
}

// vim: nocindent
<commit_msg>Improved extraction of telnet codes from the input stream<commit_after>package telnet

import (
	"fmt"
)

// RFC 854: http://tools.ietf.org/html/rfc854, http://support.microsoft.com/kb/231866

var codeMap map[byte]int
var commandMap map[int]byte

const (
	NUL  = iota // NULL, no operation
	ECHO = iota // Echo
	SGA  = iota // Suppress go ahead
	ST   = iota // Status
	TM   = iota // Timing mark
	BEL  = iota // Bell
	BS   = iota // Backspace
	HT   = iota // Horizontal tab
	LF   = iota // Line feed
	FF   = iota // Form feed
	CR   = iota // Carriage return
	TT   = iota // Terminal type
	WS   = iota // Window size
	TS   = iota // Terminal speed
	RFC  = iota // Remote flow control
	LM   = iota // Line mode
	EV   = iota // Environment variables
	SE   = iota // End of subnegotiation parameters.
	NOP  = iota // No operation.
	DM   = iota // Data Mark. The data stream portion of a Synch. This should always be accompanied by a TCP Urgent notification.
	BRK  = iota // Break. NVT character BRK.
	IP   = iota // Interrupt Process
	AO   = iota // Abort output
	AYT  = iota // Are you there
	EC   = iota // Erase character
	EL   = iota // Erase line
	GA   = iota // Go ahead signal
	SB   = iota // Indicates that what follows is subnegotiation of the indicated option.
	WILL = iota // Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option.
	WONT = iota // Indicates the refusal to perform, or continue performing, the indicated option.
	DO   = iota // Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option.
	DONT = iota // Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option.
	IAC  = iota // Interpret as command

	// Non-standard codes:
	CMP1 = iota // MCCP Compress
	CMP2 = iota // MCCP Compress2
	AARD = iota // Aardwolf MUD out of band communication, http://www.aardwolf.com/blog/2008/07/10/telnet-negotiation-control-mud-client-interaction/
	ATCP = iota // Achaea Telnet Client Protocol, http://www.ironrealms.com/rapture/manual/files/FeatATCP-txt.html
	GMCP = iota // Generic Mud Communication Protocol
)

func initLookups() {
	if codeMap != nil {
		return
	}

	codeMap = map[byte]int{}
	commandMap = map[int]byte{}

	commandMap[NUL] = '\x00'
	commandMap[ECHO] = '\x01'
	commandMap[SGA] = '\x03'
	commandMap[ST] = '\x05'
	commandMap[TM] = '\x06'
	commandMap[BEL] = '\x07'
	commandMap[BS] = '\x08'
	commandMap[HT] = '\x09'
	commandMap[LF] = '\x0a'
	commandMap[FF] = '\x0c'
	commandMap[CR] = '\x0d'
	commandMap[TT] = '\x18'
	commandMap[WS] = '\x1F'
	commandMap[TS] = '\x20'
	commandMap[RFC] = '\x21'
	commandMap[LM] = '\x22'
	commandMap[EV] = '\x24'
	commandMap[SE] = '\xf0'
	commandMap[NOP] = '\xf1'
	commandMap[DM] = '\xf2'
	commandMap[BRK] = '\xf3'
	commandMap[IP] = '\xf4'
	commandMap[AO] = '\xf5'
	commandMap[AYT] = '\xf6'
	commandMap[EC] = '\xf7'
	commandMap[EL] = '\xf8'
	commandMap[GA] = '\xf9'
	commandMap[SB] = '\xfa'
	commandMap[WILL] = '\xfb'
	commandMap[WONT] = '\xfc'
	commandMap[DO] = '\xfd'
	commandMap[DONT] = '\xfe'
	commandMap[IAC] = '\xff'

	commandMap[CMP1] = '\x55'
	commandMap[CMP2] = '\x56'
	commandMap[AARD] = '\x66'
	commandMap[ATCP] = '\xc8'
	commandMap[GMCP] = '\xc9'

	for enum, code := range commandMap {
		codeMap[code] = enum
	}
}

// Process strips telnet control codes from the given input, returning the resulting input string
func Process(bytes []byte) string {
	initLookups()

	str := ""
	var bytesProcessed []byte

	inIAC := false

	processByte := func(b byte) {
		bytesProcessed = append(bytesProcessed, b)
	}

	for _, b := range bytes {
		if b == commandMap[IAC] {
			inIAC = true
			processByte(b)
			continue
		}

		if inIAC {
			if b != commandMap[WILL] && b != commandMap[WONT] && b != commandMap[DO] && b != commandMap[DONT] {
				inIAC = false
			}
			processByte(b)
		}

		str = str + string(b)
	}

	if len(bytesProcessed) > 0 {
		fmt.Printf("Processed: %s\n", ToString(bytesProcessed))
	}

	return str
}

func Code(enum int) byte {
	initLookups()
	return commandMap[enum]
}

func ToString(bytes []byte) string {
	initLookups()

	str := ""
	for _, b := range bytes {
		enum, found := codeMap[b]
		result := ""

		if found {
			switch enum {
			case NUL:
				result = "NUL"
			case ECHO:
				result = "ECHO"
			case SGA:
				result = "SGA"
			case ST:
				result = "ST"
			case TM:
				result = "TM"
			case BEL:
				result = "BEL"
			case BS:
				result = "BS"
			case HT:
				result = "HT"
			case LF:
				result = "LF"
			case FF:
				result = "FF"
			case CR:
				result = "CR"
			case TT:
				result = "TT"
			case WS:
				result = "WS"
			case TS:
				result = "TS"
			case RFC:
				result = "RFC"
			case LM:
				result = "LM"
			case EV:
				result = "EV"
			case SE:
				result = "SE"
			case NOP:
				result = "NOP"
			case DM:
				result = "DM"
			case BRK:
				result = "BRK"
			case IP:
				result = "IP"
			case AO:
				result = "AO"
			case AYT:
				result = "AYT"
			case EC:
				result = "EC"
			case EL:
				result = "EL"
			case GA:
				result = "GA"
			case SB:
				result = "SB"
			case WILL:
				result = "WILL"
			case WONT:
				result = "WONT"
			case DO:
				result = "DO"
			case DONT:
				result = "DONT"
			case IAC:
				result = "IAC"
			case CMP1:
				result = "CMP1"
			case CMP2:
				result = "CMP2"
			case AARD:
				result = "AARD"
			case ATCP:
				result = "ATCP"
			case GMCP:
				result = "GMCP"
			}
		} else {
			result = "???"
		}

		if str != "" {
			str = str + " "
		}
		str = str + result
	}

	return str
}

func buildCommand(length int) []byte {
	command := make([]byte, length)
	command[0] = commandMap[IAC]
	return command
}

func WillEcho() []byte {
	command := buildCommand(3)
	command[1] = commandMap[WILL]
	command[2] = commandMap[ECHO]
	return command
}

func WontEcho() []byte {
	command := buildCommand(3)
	command[1] = commandMap[WONT]
	command[2] = commandMap[ECHO]
	return command
}

// vim: nocindent
<|endoftext|>


<commit_before>package leviq

import (
	"sync"
	"time"

	"github.com/johnsto/leviq/internal"
)

type Batch interface {
	Put(k, v []byte)
	Delete(k []byte)
	Clear()
	Write() error
	Close()
}

// Txn represents a transaction on a queue
type Txn struct {
	queue *Queue
	batch Batch
	puts  *internal.IDHeap // IDs to put
	takes *internal.IDHeap // IDs being taken
	mutex *sync.Mutex
}

// Put inserts the data into the queue.
func (txn *Txn) Put(v []byte) error {
	if v == nil {
		return nil
	}

	// get entry ID
	id := internal.NewID()

	// ID => key
	k := id.Key()

	txn.mutex.Lock()
	defer txn.mutex.Unlock()

	// insert into batch
	if txn.batch == nil {
		txn.batch = txn.queue.Batch()
	}
	dbk := joinKey(txn.queue.ns, k)
	txn.batch.Put(dbk, v)

	// mark as put
	txn.puts.Push(id)

	return nil
}

// Take gets an item from the queue, returning nil if no items are available.
func (txn *Txn) Take() ([]byte, error) {
	b, err := txn.TakeN(1, 0)
	if b == nil {
		return nil, err
	}
	return b[0], nil
}

// TakeN gets `n` items from the queue, waiting at most `t` for them to all
// become available. If no items are available, nil is returned.
func (txn *Txn) TakeN(n int, t time.Duration) ([][]byte, error) {
	ids, keys, values, err := txn.queue.take(n, t)
	if err != nil {
		return nil, err
	}

	if len(ids) == 0 {
		return nil, nil
	}
	n = len(ids)

	txn.mutex.Lock()
	defer txn.mutex.Unlock()

	// Start a new batch
	if txn.batch == nil {
		txn.batch = txn.queue.Batch()
	}

	for i := 0; i < n; i++ {
		txn.takes.Push(ids[i])
		txn.batch.Delete(keys[i])
	}

	return values, err
}

// Commit writes the transaction to disk.
func (txn *Txn) Commit() error {
	txn.mutex.Lock()
	defer txn.mutex.Unlock()

	if len(*txn.puts) == 0 && len(*txn.takes) == 0 {
		return nil
	}

	err := txn.batch.Write()

	if err != nil {
		return err
	}

	txn.queue.putKey(*txn.puts...)
	txn.batch = nil
	txn.puts = internal.NewIDHeap()
	txn.takes = internal.NewIDHeap()

	return nil
}

// Close reverts all changes from the transaction and releases any held
// resources.
func (txn *Txn) Close() error {
	if len(*txn.puts) == 0 && len(*txn.takes) == 0 {
		return nil
	}

	if txn.batch != nil {
		txn.mutex.Lock()
		defer txn.mutex.Unlock()

		// return taken ids to the queue
		txn.queue.putKey(*txn.takes...)

		txn.batch.Clear()
		txn.batch = nil
		txn.puts = internal.NewIDHeap()
		txn.takes = internal.NewIDHeap()
	}
	return nil
}
<commit_msg>Close Batch when finished<commit_after>package leviq

import (
	"sync"
	"time"

	"github.com/johnsto/leviq/internal"
)

type Batch interface {
	Put(k, v []byte)
	Delete(k []byte)
	Clear()
	Write() error
	Close()
}

// Txn represents a transaction on a queue
type Txn struct {
	queue *Queue
	batch Batch
	puts  *internal.IDHeap // IDs to put
	takes *internal.IDHeap // IDs being taken
	mutex *sync.Mutex
}

// Put inserts the data into the queue.
func (txn *Txn) Put(v []byte) error {
	if v == nil {
		return nil
	}

	// get entry ID
	id := internal.NewID()

	// ID => key
	k := id.Key()

	txn.mutex.Lock()
	defer txn.mutex.Unlock()

	// insert into batch
	if txn.batch == nil {
		txn.batch = txn.queue.Batch()
	}
	dbk := joinKey(txn.queue.ns, k)
	txn.batch.Put(dbk, v)

	// mark as put
	txn.puts.Push(id)

	return nil
}

// Take gets an item from the queue, returning nil if no items are available.
func (txn *Txn) Take() ([]byte, error) {
	b, err := txn.TakeN(1, 0)
	if b == nil {
		return nil, err
	}
	return b[0], nil
}

// TakeN gets `n` items from the queue, waiting at most `t` for them to all
// become available. If no items are available, nil is returned.
func (txn *Txn) TakeN(n int, t time.Duration) ([][]byte, error) {
	ids, keys, values, err := txn.queue.take(n, t)
	if err != nil {
		return nil, err
	}

	if len(ids) == 0 {
		return nil, nil
	}
	n = len(ids)

	txn.mutex.Lock()
	defer txn.mutex.Unlock()

	// Start a new batch
	if txn.batch == nil {
		txn.batch = txn.queue.Batch()
	}

	for i := 0; i < n; i++ {
		txn.takes.Push(ids[i])
		txn.batch.Delete(keys[i])
	}

	return values, err
}

// Commit writes the transaction to disk.
func (txn *Txn) Commit() error {
	txn.mutex.Lock()
	defer txn.mutex.Unlock()

	if len(*txn.puts) == 0 && len(*txn.takes) == 0 {
		return nil
	}

	err := txn.batch.Write()

	if err != nil {
		return err
	}

	txn.queue.putKey(*txn.puts...)
	if txn.batch != nil {
		txn.batch.Close()
		txn.batch = nil
	}
	txn.puts = internal.NewIDHeap()
	txn.takes = internal.NewIDHeap()

	return nil
}

// Close reverts all changes from the transaction and releases any held
// resources.
func (txn *Txn) Close() error {
	if len(*txn.puts) == 0 && len(*txn.takes) == 0 {
		return nil
	}

	if txn.batch != nil {
		txn.mutex.Lock()
		defer txn.mutex.Unlock()

		// return taken ids to the queue
		txn.queue.putKey(*txn.takes...)

		txn.batch.Clear()
		txn.batch.Close()
		txn.batch = nil
		txn.puts = internal.NewIDHeap()
		txn.takes = internal.NewIDHeap()
	}
	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/validation"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/athena"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsAthenaDatabase() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAthenaDatabaseCreate,
		Read:   resourceAwsAthenaDatabaseRead,
		Update: resourceAwsAthenaDatabaseUpdate,
		Delete: resourceAwsAthenaDatabaseDelete,

		Schema: map[string]*schema.Schema{
			"name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringMatch(regexp.MustCompile("^[_a-z0-9]+$"), "see https://docs.aws.amazon.com/athena/latest/ug/tables-databases-columns-names.html"),
			},
			"bucket": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"force_destroy": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"encryption_key": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"id": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"type": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
			},
		},
	}
}

func getResultConfig(d *schema.ResourceData) (*athena.ResultConfiguration, error) {
	resultConfig := athena.ResultConfiguration{
		OutputLocation: aws.String("s3://" + d.Get("bucket").(string)),
	}

	e := d.Get("encryption_key").([]interface{})
	if len(e) <= 0 {
		return &resultConfig
	}

	data := e[0].(map[string]interface{})
	keyType := data["type"].(string)
	keyID := data["id"].(string)

	if len(keyType) <= 0 {
		return fmt.Errorf("An encryption key type is required")
	}

	if strings.HasSuffix(keyType, "_KMS") && len(keyID) <= 0 {
		return nil, fmt.Errorf("Key type %s requires a valid KMS key ID", keyType)
	}

	encryptionConfig := athena.EncryptionConfiguration{
		EncryptionOption: aws.String(keyType),
	}

	if len(keyID) > 0 {
		encryptionConfig.KmsKey = aws.String(keyID)
	}

	resultConfig.EncryptionConfiguration = &encryptionConfig

	return &resultConfig, nil
}

func resourceAwsAthenaDatabaseCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).athenaconn

	resultConfig, err := getResultConfig(d)
	if err != nil {
		return err
	}

	input := &athena.StartQueryExecutionInput{
		QueryString:         aws.String(fmt.Sprintf("create database `%s`;", d.Get("name").(string))),
		ResultConfiguration: resultConfig,
	}

	resp, err := conn.StartQueryExecution(input)
	if err != nil {
		return err
	}

	if err := executeAndExpectNoRowsWhenCreate(*resp.QueryExecutionId, d, conn); err != nil {
		return err
	}
	d.SetId(d.Get("name").(string))
	return resourceAwsAthenaDatabaseRead(d, meta)
}

func resourceAwsAthenaDatabaseRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).athenaconn

	resultConfig, err := getResultConfig(d)
	if err != nil {
		return err
	}

	input := &athena.StartQueryExecutionInput{
		QueryString:         aws.String(fmt.Sprint("show databases;")),
		ResultConfiguration: resultConfig,
	}

	resp, err := conn.StartQueryExecution(input)
	if err != nil {
		return err
	}

	if err := executeAndExpectMatchingRow(*resp.QueryExecutionId, d.Get("name").(string), conn); err != nil {
		return err
	}
	return nil
}

func resourceAwsAthenaDatabaseUpdate(d *schema.ResourceData, meta interface{}) error {
	return resourceAwsAthenaDatabaseRead(d, meta)
}

func resourceAwsAthenaDatabaseDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).athenaconn

	resultConfig, err := getResultConfig(d)
	if err != nil {
		return err
	}

	name := d.Get("name").(string)

	queryString := fmt.Sprintf("drop database `%s`", name)
	if d.Get("force_destroy").(bool) {
		queryString += " cascade"
	}
	queryString += ";"

	input := &athena.StartQueryExecutionInput{
		QueryString:         aws.String(queryString),
		ResultConfiguration: resultConfig,
	}

	resp, err := conn.StartQueryExecution(input)
	if err != nil {
		return err
	}

	if err := executeAndExpectNoRowsWhenDrop(*resp.QueryExecutionId, d, conn); err != nil {
		return err
	}
	return nil
}

func executeAndExpectNoRowsWhenCreate(qeid string, d *schema.ResourceData, conn *athena.Athena) error {
	rs, err := queryExecutionResult(qeid, conn)
	if err != nil {
		return err
	}
	if len(rs.Rows) != 0 {
		return fmt.Errorf("Athena create database, unexpected query result: %s", flattenAthenaResultSet(rs))
	}
	return nil
}

func executeAndExpectMatchingRow(qeid string, dbName string, conn *athena.Athena) error {
	rs, err := queryExecutionResult(qeid, conn)
	if err != nil {
		return err
	}
	for _, row := range rs.Rows {
		for _, datum := range row.Data {
			if *datum.VarCharValue == dbName {
				return nil
			}
		}
	}
	return fmt.Errorf("Athena not found database: %s, query result: %s", dbName, flattenAthenaResultSet(rs))
}

func executeAndExpectNoRowsWhenDrop(qeid string, d *schema.ResourceData, conn *athena.Athena) error {
	rs, err := queryExecutionResult(qeid, conn)
	if err != nil {
		return err
	}
	if len(rs.Rows) != 0 {
		return fmt.Errorf("Athena drop database, unexpected query result: %s", flattenAthenaResultSet(rs))
	}
	return nil
}

func queryExecutionResult(qeid string, conn *athena.Athena) (*athena.ResultSet, error) {
	executionStateConf := &resource.StateChangeConf{
		Pending:    []string{athena.QueryExecutionStateQueued, athena.QueryExecutionStateRunning},
		Target:     []string{athena.QueryExecutionStateSucceeded},
		Refresh:    queryExecutionStateRefreshFunc(qeid, conn),
		Timeout:    10 * time.Minute,
		Delay:      3 * time.Second,
		MinTimeout: 3 * time.Second,
	}
	_, err := executionStateConf.WaitForState()
	if err != nil {
		return nil, err
	}

	qrinput := &athena.GetQueryResultsInput{
		QueryExecutionId: aws.String(qeid),
	}
	resp, err := conn.GetQueryResults(qrinput)
	if err != nil {
		return nil, err
	}
	return resp.ResultSet, nil
}

func queryExecutionStateRefreshFunc(qeid string, conn *athena.Athena) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		input := &athena.GetQueryExecutionInput{
			QueryExecutionId: aws.String(qeid),
		}
		out, err := conn.GetQueryExecution(input)
		if err != nil {
			return nil, "failed", err
		}
		status := out.QueryExecution.Status
		if *status.State == athena.QueryExecutionStateFailed &&
			status.StateChangeReason != nil {
			err = fmt.Errorf("reason: %s", *status.StateChangeReason)
		}
		return out, *out.QueryExecution.Status.State, err
	}
}

func flattenAthenaResultSet(rs *athena.ResultSet) string {
	ss := make([]string, 0)
	for _, row := range rs.Rows {
		for _, datum := range row.Data {
			ss = append(ss, *datum.VarCharValue)
		}
	}
	return strings.Join(ss, "\n")
}
<commit_msg>Fix a few returns.<commit_after>package aws

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/hashicorp/terraform/helper/validation"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/athena"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsAthenaDatabase() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAthenaDatabaseCreate,
		Read:   resourceAwsAthenaDatabaseRead,
		Update: resourceAwsAthenaDatabaseUpdate,
		Delete: resourceAwsAthenaDatabaseDelete,

		Schema: map[string]*schema.Schema{
			"name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringMatch(regexp.MustCompile("^[_a-z0-9]+$"), "see https://docs.aws.amazon.com/athena/latest/ug/tables-databases-columns-names.html"),
			},
			"bucket": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"force_destroy": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},
			"encryption_key": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"id": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"type": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
			},
		},
	}
}

func getResultConfig(d *schema.ResourceData) (*athena.ResultConfiguration, error) {
	resultConfig := athena.ResultConfiguration{
		OutputLocation: aws.String("s3://" + d.Get("bucket").(string)),
	}

	e := d.Get("encryption_key").([]interface{})
	if len(e) <= 0 {
		return &resultConfig, nil
	}

	data := e[0].(map[string]interface{})
	keyType := data["type"].(string)
	keyID := data["id"].(string)

	if len(keyType) <= 0 {
		return nil, fmt.Errorf("An encryption key type is required")
	}

	if strings.HasSuffix(keyType, "_KMS") && len(keyID) <= 0 {
		return nil, fmt.Errorf("Key type %s requires a valid KMS key ID", keyType)
	}

	encryptionConfig := athena.EncryptionConfiguration{
		EncryptionOption: aws.String(keyType),
	}

	if len(keyID) > 0 {
		encryptionConfig.KmsKey = aws.String(keyID)
	}

	resultConfig.EncryptionConfiguration = &encryptionConfig

	return &resultConfig, nil
}

func resourceAwsAthenaDatabaseCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).athenaconn

	resultConfig, err := getResultConfig(d)
	if err != nil {
		return err
	}

	input := &athena.StartQueryExecutionInput{
		QueryString:         aws.String(fmt.Sprintf("create database `%s`;", d.Get("name").(string))),
		ResultConfiguration: resultConfig,
	}

	resp, err := conn.StartQueryExecution(input)
	if err != nil {
		return err
	}

	if err := executeAndExpectNoRowsWhenCreate(*resp.QueryExecutionId, d, conn); err != nil {
		return err
	}
	d.SetId(d.Get("name").(string))
	return resourceAwsAthenaDatabaseRead(d, meta)
}

func resourceAwsAthenaDatabaseRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).athenaconn

	resultConfig, err := getResultConfig(d)
	if err != nil {
		return err
	}

	input := &athena.StartQueryExecutionInput{
		QueryString:         aws.String(fmt.Sprint("show databases;")),
		ResultConfiguration: resultConfig,
	}

	resp, err := conn.StartQueryExecution(input)
	if err != nil {
		return err
	}

	if err := executeAndExpectMatchingRow(*resp.QueryExecutionId, d.Get("name").(string), conn); err != nil {
		return err
	}
	return nil
}

func resourceAwsAthenaDatabaseUpdate(d *schema.ResourceData, meta interface{}) error {
	return resourceAwsAthenaDatabaseRead(d, meta)
}

func resourceAwsAthenaDatabaseDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).athenaconn

	resultConfig, err := getResultConfig(d)
	if err != nil {
		return err
	}

	name := d.Get("name").(string)

	queryString := fmt.Sprintf("drop database `%s`", name)
	if d.Get("force_destroy").(bool) {
		queryString += " cascade"
	}
	queryString += ";"

	input := &athena.StartQueryExecutionInput{
		QueryString:         aws.String(queryString),
		ResultConfiguration: resultConfig,
	}

	resp, err := conn.StartQueryExecution(input)
	if err != nil {
		return err
	}

	if err := executeAndExpectNoRowsWhenDrop(*resp.QueryExecutionId, d, conn); err != nil {
		return err
	}
	return nil
}

func executeAndExpectNoRowsWhenCreate(qeid string, d *schema.ResourceData, conn *athena.Athena) error {
	rs, err := queryExecutionResult(qeid, conn)
	if err != nil {
		return err
	}
	if len(rs.Rows) != 0 {
		return fmt.Errorf("Athena create database, unexpected query result: %s", flattenAthenaResultSet(rs))
	}
	return nil
}

func executeAndExpectMatchingRow(qeid string, dbName string, conn *athena.Athena) error {
	rs, err := queryExecutionResult(qeid, conn)
	if err != nil {
		return err
	}
	for _, row := range rs.Rows {
		for _, datum := range row.Data {
			if *datum.VarCharValue == dbName {
				return nil
			}
		}
	}
	return fmt.Errorf("Athena not found database: %s, query result: %s", dbName, flattenAthenaResultSet(rs))
}

func executeAndExpectNoRowsWhenDrop(qeid string, d *schema.ResourceData, conn *athena.Athena) error {
	rs, err := queryExecutionResult(qeid, conn)
	if err != nil {
		return err
	}
	if len(rs.Rows) != 0 {
		return fmt.Errorf("Athena drop database, unexpected query result: %s", flattenAthenaResultSet(rs))
	}
	return nil
}

func queryExecutionResult(qeid string, conn *athena.Athena) (*athena.ResultSet, error) {
	executionStateConf := &resource.StateChangeConf{
		Pending:    []string{athena.QueryExecutionStateQueued, athena.QueryExecutionStateRunning},
		Target:     []string{athena.QueryExecutionStateSucceeded},
		Refresh:    queryExecutionStateRefreshFunc(qeid, conn),
		Timeout:    10 * time.Minute,
		Delay:      3 * time.Second,
		MinTimeout: 3 * time.Second,
	}
	_, err := executionStateConf.WaitForState()
	if err != nil {
		return nil, err
	}

	qrinput := &athena.GetQueryResultsInput{
		QueryExecutionId: aws.String(qeid),
	}
	resp, err := conn.GetQueryResults(qrinput)
	if err != nil {
		return nil, err
	}
	return resp.ResultSet, nil
}

func queryExecutionStateRefreshFunc(qeid string, conn *athena.Athena) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		input := &athena.GetQueryExecutionInput{
			QueryExecutionId: aws.String(qeid),
		}
		out, err := conn.GetQueryExecution(input)
		if err != nil {
			return nil, "failed", err
		}
		status := out.QueryExecution.Status
		if *status.State == athena.QueryExecutionStateFailed &&
			status.StateChangeReason != nil {
			err = fmt.Errorf("reason: %s", *status.StateChangeReason)
		}
		return out, *out.QueryExecution.Status.State, err
	}
}

func flattenAthenaResultSet(rs *athena.ResultSet) string {
	ss := make([]string, 0)
	for _, row := range rs.Rows {
		for _, datum := range row.Data {
			ss = append(ss, *datum.VarCharValue)
		}
	}
	return strings.Join(ss, "\n")
}
<|endoftext|>


<commit_before>// $G $F.go && $L $F.$A &&./$A.out

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import "os"

func main() {
	var i uint64 =
		' ' +
		'a' +
		'ä' +
		'本' +
		'\a' +
		'\b' +
		'\f' +
		'\n' +
		'\r' +
		'\t' +
		'\v' +
		'\\' +
		'\'' +
		'\000' +
		'\123' +
		'\x00' +
		'\xca' +
		'\xFE' +
		'\u0123' +
		'\ubabe' +
		'\U0123ABCD' +
		'\Ucafebabe'
		;
	if '\Ucafebabe' != 0xcafebabe {
		print("cafebabe wrong\n");
		os.Exit(1)
	}
	if i != 0xcc238de1 {
		print("number is ", i, " should be ", 0xcc238de1, "\n");
		os.Exit(1)
		}
}
<commit_msg>fix build: invalid character literals<commit_after>// $G $F.go && $L $F.$A &&./$A.out

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import "os"

func main() {
	var i uint64 =
		' ' +
		'a' +
		'ä' +
		'本' +
		'\a' +
		'\b' +
		'\f' +
		'\n' +
		'\r' +
		'\t' +
		'\v' +
		'\\' +
		'\'' +
		'\000' +
		'\123' +
		'\x00' +
		'\xca' +
		'\xFE' +
		'\u0123' +
		'\ubabe' +
		'\U0010FFFF' +
		'\U000ebabe'
		;
	if '\U000ebabe' != 0x000ebabe {
		print("ebabe wrong\n");
		os.Exit(1)
	}
	if i != 0x20e213 {
		print("number is ", i, " should be ", 0x20e213, "\n");
		os.Exit(1)
		}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"log"
	"net"
	"time"

	"sync"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
	"github.com/shadowsocks/go-shadowsocks2/socks"
)

type mode int

const (
	remoteServer mode = iota
	relayClient
	socksClient
)

const udpBufSize = 64 * 1024

func unpack(dst, src []byte, ciphers []shadowaead.Cipher) ([]byte, shadowaead.Cipher, error) {
	for _, cipher := range ciphers {
		buf, err := shadowaead.Unpack(dst, src, cipher)
		if err != nil {
			continue
		}
		return buf, cipher, nil
	}
	return nil, nil, errors.New("could not find valid cipher")
}

// Listen on addr for encrypted packets and basically do UDP NAT.
func udpRemote(addr string, ciphers []shadowaead.Cipher) {
	c, err := net.ListenPacket("udp", addr)
	if err != nil {
		log.Printf("UDP remote listen error: %v", err)
		return
	}
	defer c.Close()

	nm := newNATmap(config.UDPTimeout)
	buf := make([]byte, udpBufSize)

	log.Printf("listening UDP on %s", addr)
	for {
		func() {
			n, raddr, err := c.ReadFrom(buf)
			defer log.Printf("Done with %v", raddr.String())
			if err != nil {
				log.Printf("UDP remote read error: %v", err)
				return
			}
			buf, cipher, err := unpack(buf, buf[:n], ciphers)
			if err != nil {
				log.Printf("UDP remote read error: %v", err)
				return
			}

			tgtAddr := socks.SplitAddr(buf[:n])
			if tgtAddr == nil {
				log.Printf("failed to split target address from packet: %q", buf[:n])
				return
			}

			tgtUDPAddr, err := net.ResolveUDPAddr("udp", tgtAddr.String())
			if err != nil {
				log.Printf("failed to resolve target UDP address: %v", err)
				return
			}

			payload := buf[len(tgtAddr):n]

			pc := nm.Get(raddr.String())
			if pc == nil {
				pc, err = net.ListenPacket("udp", "")
				if err != nil {
					log.Printf("UDP remote listen error: %v", err)
					return
				}

				nm.Add(raddr, shadowaead.NewPacketConn(c, cipher), pc, remoteServer)
			}

			_, err = pc.WriteTo(payload, tgtUDPAddr) // accept only UDPAddr despite the signature
			if err != nil {
				log.Printf("UDP remote write error: %v", err)
				return
			}
		}()
	}
}

// Packet NAT table
type natmap struct {
	sync.RWMutex
	m       map[string]net.PacketConn
	timeout time.Duration
}

func newNATmap(timeout time.Duration) *natmap {
	m := &natmap{}
	m.m = make(map[string]net.PacketConn)
	m.timeout = timeout
	return m
}

func (m *natmap) Get(key string) net.PacketConn {
	m.RLock()
	defer m.RUnlock()
	return m.m[key]
}

func (m *natmap) Set(key string, pc net.PacketConn) {
	m.Lock()
	defer m.Unlock()

	m.m[key] = pc
}

func (m *natmap) Del(key string) net.PacketConn {
	m.Lock()
	defer m.Unlock()

	pc, ok := m.m[key]
	if ok {
		delete(m.m, key)
		return pc
	}
	return nil
}

func (m *natmap) Add(peer net.Addr, dst, src net.PacketConn, role mode) {
	m.Set(peer.String(), src)

	go func() {
		timedCopy(dst, peer, src, m.timeout, role)
		if pc := m.Del(peer.String()); pc != nil {
			pc.Close()
		}
	}()
}

// copy from src to dst at target with read timeout
func timedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration, role mode) error {
	buf := make([]byte, udpBufSize)

	for {
		src.SetReadDeadline(time.Now().Add(timeout))
		n, raddr, err := src.ReadFrom(buf)
		if err != nil {
			return err
		}

		switch role {
		case remoteServer: // server -> client: add original packet source
			srcAddr := socks.ParseAddr(raddr.String())
			copy(buf[len(srcAddr):], buf[:n])
			copy(buf, srcAddr)
			_, err = dst.WriteTo(buf[:len(srcAddr)+n], target)
		case relayClient: // client -> user: strip original packet source
			srcAddr := socks.SplitAddr(buf[:n])
			_, err = dst.WriteTo(buf[len(srcAddr):n], target)
		case socksClient: // client -> socks5 program: just set RSV and FRAG = 0
			_, err = dst.WriteTo(append([]byte{0, 0, 0}, buf[:n]...), target)
		}

		if err != nil {
			return err
		}
	}
}
<commit_msg>Fix udp multi user<commit_after>package main

import (
	"errors"
	"log"
	"net"
	"time"

	"sync"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
	"github.com/shadowsocks/go-shadowsocks2/socks"
)

type mode int

const (
	remoteServer mode = iota
	relayClient
	socksClient
)

const udpBufSize = 64 * 1024

// upack decripts src into dst. It tries each cipher until it finds one that authenticates
// correctly. dst and src must not overlap.
func unpack(dst, src []byte, ciphers []shadowaead.Cipher) ([]byte, shadowaead.Cipher, error) {
	for i, cipher := range ciphers {
		log.Printf("Trying cipher %v", i)
		buf, err := shadowaead.Unpack(dst, src, cipher)
		if err != nil {
			log.Printf("Failed cipher %v: %v", i, err)
			continue
		}
		log.Printf("Selected cipher %v", i)
		return buf, cipher, nil
	}
	return nil, nil, errors.New("could not find valid cipher")
}

// Listen on addr for encrypted packets and basically do UDP NAT.
func udpRemote(addr string, ciphers []shadowaead.Cipher) {
	c, err := net.ListenPacket("udp", addr)
	if err != nil {
		log.Printf("UDP remote listen error: %v", err)
		return
	}
	defer c.Close()

	nm := newNATmap(config.UDPTimeout)
	cipherBuf := make([]byte, udpBufSize)
	buf := make([]byte, udpBufSize)

	log.Printf("listening UDP on %s", addr)
	for {
		func() {
			n, raddr, err := c.ReadFrom(cipherBuf)
			defer log.Printf("Done with %v", raddr.String())
			if err != nil {
				log.Printf("UDP remote read error: %v", err)
				return
			}
			log.Printf("Request from %v", raddr)
			buf, cipher, err := unpack(buf, cipherBuf[:n], ciphers)
			if err != nil {
				log.Printf("UDP remote read error: %v", err)
				return
			}

			tgtAddr := socks.SplitAddr(buf[:n])
			if tgtAddr == nil {
				log.Printf("failed to split target address from packet: %q", buf[:n])
				return
			}

			tgtUDPAddr, err := net.ResolveUDPAddr("udp", tgtAddr.String())
			if err != nil {
				log.Printf("failed to resolve target UDP address: %v", err)
				return
			}

			payload := buf[len(tgtAddr):n]

			pc := nm.Get(raddr.String())
			if pc == nil {
				pc, err = net.ListenPacket("udp", "")
				if err != nil {
					log.Printf("UDP remote listen error: %v", err)
					return
				}

				nm.Add(raddr, shadowaead.NewPacketConn(c, cipher), pc, remoteServer)
			}

			_, err = pc.WriteTo(payload, tgtUDPAddr) // accept only UDPAddr despite the signature
			if err != nil {
				log.Printf("UDP remote write error: %v", err)
				return
			}
		}()
	}
}

// Packet NAT table
type natmap struct {
	sync.RWMutex
	m       map[string]net.PacketConn
	timeout time.Duration
}

func newNATmap(timeout time.Duration) *natmap {
	m := &natmap{}
	m.m = make(map[string]net.PacketConn)
	m.timeout = timeout
	return m
}

func (m *natmap) Get(key string) net.PacketConn {
	m.RLock()
	defer m.RUnlock()
	return m.m[key]
}

func (m *natmap) Set(key string, pc net.PacketConn) {
	m.Lock()
	defer m.Unlock()

	m.m[key] = pc
}

func (m *natmap) Del(key string) net.PacketConn {
	m.Lock()
	defer m.Unlock()

	pc, ok := m.m[key]
	if ok {
		delete(m.m, key)
		return pc
	}
	return nil
}

func (m *natmap) Add(peer net.Addr, dst, src net.PacketConn, role mode) {
	m.Set(peer.String(), src)

	go func() {
		timedCopy(dst, peer, src, m.timeout, role)
		if pc := m.Del(peer.String()); pc != nil {
			pc.Close()
		}
	}()
}

// copy from src to dst at target with read timeout
func timedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration, role mode) error {
	buf := make([]byte, udpBufSize)

	for {
		src.SetReadDeadline(time.Now().Add(timeout))
		n, raddr, err := src.ReadFrom(buf)
		if err != nil {
			return err
		}

		switch role {
		case remoteServer: // server -> client: add original packet source
			srcAddr := socks.ParseAddr(raddr.String())
			copy(buf[len(srcAddr):], buf[:n])
			copy(buf, srcAddr)
			_, err = dst.WriteTo(buf[:len(srcAddr)+n], target)
		case relayClient: // client -> user: strip original packet source
			srcAddr := socks.SplitAddr(buf[:n])
			_, err = dst.WriteTo(buf[len(srcAddr):n], target)
		case socksClient: // client -> socks5 program: just set RSV and FRAG = 0
			_, err = dst.WriteTo(append([]byte{0, 0, 0}, buf[:n]...), target)
		}

		if err != nil {
			return err
		}
	}
}
<|endoftext|>


<commit_before>package models

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"time"

	"github.com/technoweenie/grohl"
)

type IMagick struct{}

type processPipelineStep func(workingDirectoryPath string, inputFilePath string, args *ProcessArgs) (outputFilePath string, err error)

var defaultPipeline = []processPipelineStep{
	downloadRemote,
	preProcessImage,
	processImage,
}

// Process a remote asset url using graphicsmagick with the args supplied
// and write the response to w
func (p *IMagick) Process(w http.ResponseWriter, r *http.Request, args *ProcessArgs) (err error) {
	tempDir, err := createTemporaryWorkspace()
	if err != nil {
		return
	}
	// defer os.RemoveAll(tempDir)

	var filePath string

	for _, step := range defaultPipeline {
		filePath, err = step(tempDir, filePath, args)
		if err != nil {
			return
		}
	}

	// serve response
	http.ServeFile(w, r, filePath)
	return
}

func createTemporaryWorkspace() (string, error) {
	return ioutil.TempDir("", "_firesize")
}

func downloadRemote(tempDir string, _ string, args *ProcessArgs) (string, error) {
	url := args.Url
	inFile := filepath.Join(tempDir, "in")

	grohl.Log(grohl.Data{
		"processor": "imagick",
		"download":  url,
		"local":     inFile,
	})

	out, err := os.Create(inFile)
	if err != nil {
		return inFile, err
	}
	defer out.Close()

	resp, err := http.Get(url)
	if err != nil {
		return inFile, err
	}
	defer resp.Body.Close()

	_, err = io.Copy(out, resp.Body)

	return inFile, err
}

func preProcessImage(tempDir string, inFile string, args *ProcessArgs) (string, error) {
	if isAnimatedGif(inFile) {
		args.Format = "gif" // Total hack cos format is incorrectly .png on example
		return coalesceAnimatedGif(tempDir, inFile)
	} else {
		return inFile, nil
	}
}

func processImage(tempDir string, inFile string, args *ProcessArgs) (string, error) {
	outFile := filepath.Join(tempDir, "out")
	cmdArgs, outFileWithFormat := args.CommandArgs(inFile, outFile)

	grohl.Log(grohl.Data{
		"processor": "imagick",
		"args":      cmdArgs,
	})

	executable := "convert"
	cmd := exec.Command(executable, cmdArgs...)
	var outErr bytes.Buffer
	cmd.Stdout, cmd.Stderr = &outErr, &outErr
	err := runWithTimeout(cmd, 60*time.Second)
	if err != nil {
		grohl.Log(grohl.Data{
			"processor": "imagick",
			"step":      "convert",
			"failure":   err,
			"args":      cmdArgs,
			"output":    string(outErr.Bytes()),
		})
	}

	return outFileWithFormat, err
}

func isAnimatedGif(inFile string) bool {
	// identify -format %n updates-product-click.gif # => 105
	cmd := exec.Command("identify", "-format", "%n", inFile)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := runWithTimeout(cmd, 10*time.Second)
	if err != nil {
		output := string(stderr.Bytes())
		grohl.Log(grohl.Data{
			"processor": "imagick",
			"step":      "identify",
			"failure":   err,
			"output":    output,
		})
	} else {
		output := string(stdout.Bytes())
		numFrames, err := strconv.Atoi(output)
		if err != nil {
			grohl.Log(grohl.Data{
				"processor": "imagick",
				"step":      "identify",
				"failure":   err,
				"output":    output,
				"message":   "non numeric identify output",
			})
		} else {
			grohl.Log(grohl.Data{
				"processor":  "imagick",
				"step":       "identify",
				"num-frames": numFrames,
			})
			return numFrames > 1
		}
	}
	// if anything fucks out assume not animated
	return false
}

func coalesceAnimatedGif(tempDir string, inFile string) (string, error) {
	outFile := filepath.Join(tempDir, "temp")

	// convert do.gif -coalesce temporary.gif
	cmd := exec.Command("convert", inFile, "-coalesce", outFile)
	var outErr bytes.Buffer
	cmd.Stdout, cmd.Stderr = &outErr, &outErr

	err := runWithTimeout(cmd, 60*time.Second)
	if err != nil {
		grohl.Log(grohl.Data{
			"processor": "imagick",
			"step":      "coalesce",
			"failure":   err,
			"output":    string(outErr.Bytes()),
		})
	}

	return outFile, err
}

func runWithTimeout(cmd *exec.Cmd, timeout time.Duration) error {
	// Start the process
	err := cmd.Start()
	if err != nil {
		return err
	}

	// Kill the process if it doesn't exit in time
	defer time.AfterFunc(timeout, func() {
		fmt.Println("command timed out")
		cmd.Process.Kill()
	}).Stop()

	// Wait for the process to finish
	return cmd.Wait()
}
<commit_msg>Try to TrimSpace on identify output.<commit_after>package models

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/technoweenie/grohl"
)

type IMagick struct{}

type processPipelineStep func(workingDirectoryPath string, inputFilePath string, args *ProcessArgs) (outputFilePath string, err error)

var defaultPipeline = []processPipelineStep{
	downloadRemote,
	preProcessImage,
	processImage,
}

// Process a remote asset url using graphicsmagick with the args supplied
// and write the response to w
func (p *IMagick) Process(w http.ResponseWriter, r *http.Request, args *ProcessArgs) (err error) {
	tempDir, err := createTemporaryWorkspace()
	if err != nil {
		return
	}
	// defer os.RemoveAll(tempDir)

	var filePath string

	for _, step := range defaultPipeline {
		filePath, err = step(tempDir, filePath, args)
		if err != nil {
			return
		}
	}

	// serve response
	http.ServeFile(w, r, filePath)
	return
}

func createTemporaryWorkspace() (string, error) {
	return ioutil.TempDir("", "_firesize")
}

func downloadRemote(tempDir string, _ string, args *ProcessArgs) (string, error) {
	url := args.Url
	inFile := filepath.Join(tempDir, "in")

	grohl.Log(grohl.Data{
		"processor": "imagick",
		"download":  url,
		"local":     inFile,
	})

	out, err := os.Create(inFile)
	if err != nil {
		return inFile, err
	}
	defer out.Close()

	resp, err := http.Get(url)
	if err != nil {
		return inFile, err
	}
	defer resp.Body.Close()

	_, err = io.Copy(out, resp.Body)

	return inFile, err
}

func preProcessImage(tempDir string, inFile string, args *ProcessArgs) (string, error) {
	if isAnimatedGif(inFile) {
		args.Format = "gif" // Total hack cos format is incorrectly .png on example
		return coalesceAnimatedGif(tempDir, inFile)
	} else {
		return inFile, nil
	}
}

func processImage(tempDir string, inFile string, args *ProcessArgs) (string, error) {
	outFile := filepath.Join(tempDir, "out")
	cmdArgs, outFileWithFormat := args.CommandArgs(inFile, outFile)

	grohl.Log(grohl.Data{
		"processor": "imagick",
		"args":      cmdArgs,
	})

	executable := "convert"
	cmd := exec.Command(executable, cmdArgs...)
	var outErr bytes.Buffer
	cmd.Stdout, cmd.Stderr = &outErr, &outErr
	err := runWithTimeout(cmd, 60*time.Second)
	if err != nil {
		grohl.Log(grohl.Data{
			"processor": "imagick",
			"step":      "convert",
			"failure":   err,
			"args":      cmdArgs,
			"output":    string(outErr.Bytes()),
		})
	}

	return outFileWithFormat, err
}

func isAnimatedGif(inFile string) bool {
	// identify -format %n updates-product-click.gif # => 105
	cmd := exec.Command("identify", "-format", "%n", inFile)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := runWithTimeout(cmd, 10*time.Second)
	if err != nil {
		output := string(stderr.Bytes())
		grohl.Log(grohl.Data{
			"processor": "imagick",
			"step":      "identify",
			"failure":   err,
			"output":    output,
		})
	} else {
		output := string(stdout.Bytes())
		output = strings.TrimSpace(output)
		numFrames, err := strconv.Atoi(output)
		if err != nil {
			grohl.Log(grohl.Data{
				"processor": "imagick",
				"step":      "identify",
				"failure":   err,
				"output":    output,
				"message":   "non numeric identify output",
			})
		} else {
			grohl.Log(grohl.Data{
				"processor":  "imagick",
				"step":       "identify",
				"num-frames": numFrames,
			})
			return numFrames > 1
		}
	}
	// if anything fucks out assume not animated
	return false
}

func coalesceAnimatedGif(tempDir string, inFile string) (string, error) {
	outFile := filepath.Join(tempDir, "temp")

	// convert do.gif -coalesce temporary.gif
	cmd := exec.Command("convert", inFile, "-coalesce", outFile)
	var outErr bytes.Buffer
	cmd.Stdout, cmd.Stderr = &outErr, &outErr

	err := runWithTimeout(cmd, 60*time.Second)
	if err != nil {
		grohl.Log(grohl.Data{
			"processor": "imagick",
			"step":      "coalesce",
			"failure":   err,
			"output":    string(outErr.Bytes()),
		})
	}

	return outFile, err
}

func runWithTimeout(cmd *exec.Cmd, timeout time.Duration) error {
	// Start the process
	err := cmd.Start()
	if err != nil {
		return err
	}

	// Kill the process if it doesn't exit in time
	defer time.AfterFunc(timeout, func() {
		fmt.Println("command timed out")
		cmd.Process.Kill()
	}).Stop()

	// Wait for the process to finish
	return cmd.Wait()
}
<|endoftext|>


<commit_before>// Copyright © 2016 Mark Bates <mark@markbates.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package generate

import (
	"errors"
	"fmt"
	"path/filepath"

	"github.com/markbates/gentronics"
	"github.com/markbates/inflect"
	"github.com/spf13/cobra"
)

// ResourceCmd generates a new actions/resource file and a stub test.
var ResourceCmd = &cobra.Command{
	Use:     "resource [name]",
	Aliases: []string{"r"},
	Short:   "Generates a new actions/resource file",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return errors.New("You must specifiy a resource name!")
		}
		name := args[0]
		data := gentronics.Data{
			"name":       name,
			"singular":   inflect.Singularize(name),
			"plural":     inflect.Pluralize(name),
			"camel":      inflect.Camelize(name),
			"underscore": inflect.Underscore(name),
		}
		return NewResourceGenerator(data).Run(".", data)
	},
}

// NewResourceGenerator generates a new actions/resource file and a stub test.
func NewResourceGenerator(data gentronics.Data) *gentronics.Generator {
	g := gentronics.New()
	g.Add(gentronics.NewFile(filepath.Join("actions", fmt.Sprintf("%s.go", data["underscore"])), rAction))
	g.Add(gentronics.NewFile(filepath.Join("actions", fmt.Sprintf("%s_test.go", data["underscore"])), rActionTest))
	g.Add(Fmt)
	return g
}

var rAction = `package actions

import "github.com/markbates/buffalo"

type {{.camel}}Resource struct{}

// List default implementation. Returns a 404
func (v *{{.camel}}Resource) List(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Show default implementation. Returns a 404
func (v *{{.camel}}Resource) Show(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// New default implementation. Returns a 404
func (v *{{.camel}}Resource) New(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Create default implementation. Returns a 404
func (v *{{.camel}}Resource) Create(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Edit default implementation. Returns a 404
func (v *{{.camel}}Resource) Edit(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Update default implementation. Returns a 404
func (v *{{.camel}}Resource) Update(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Destroy default implementation. Returns a 404
func (v *{{.camel}}Resource) Destroy(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}`

var rActionTest = `package actions_test

import (
	"testing"

	"github.com/stretchr/testify/require"
)

func Test_{{.camel}}Resource_List(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Show(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_New(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Create(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Edit(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Update(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Destroy(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}
`
<commit_msg>auto mount a generated resource<commit_after>// Copyright © 2016 Mark Bates <mark@markbates.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package generate

import (
	"errors"
	"fmt"
	"path/filepath"

	"github.com/markbates/gentronics"
	"github.com/markbates/inflect"
	"github.com/spf13/cobra"
)

// ResourceCmd generates a new actions/resource file and a stub test.
var ResourceCmd = &cobra.Command{
	Use:     "resource [name]",
	Aliases: []string{"r"},
	Short:   "Generates a new actions/resource file",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return errors.New("You must specifiy a resource name!")
		}
		name := args[0]
		data := gentronics.Data{
			"name":       name,
			"singular":   inflect.Singularize(name),
			"plural":     inflect.Pluralize(name),
			"camel":      inflect.Camelize(name),
			"underscore": inflect.Underscore(name),
		}
		return NewResourceGenerator(data).Run(".", data)
	},
}

// NewResourceGenerator generates a new actions/resource file and a stub test.
func NewResourceGenerator(data gentronics.Data) *gentronics.Generator {
	g := gentronics.New()
	g.Add(gentronics.NewFile(filepath.Join("actions", fmt.Sprintf("%s.go", data["underscore"])), rAction))
	g.Add(gentronics.NewFile(filepath.Join("actions", fmt.Sprintf("%s_test.go", data["underscore"])), rActionTest))
	g.Add(Fmt)
	return g
}

var rAction = `package actions

import "github.com/markbates/buffalo"

type {{.camel}}Resource struct{
	buffalo.Resource
}

func init() {
	App().Resource("/{{.underscore}}", &{{.camel}}Resource{&buffalo.BaseResource{}})
}

// List default implementation. Returns a 404
func (v *{{.camel}}Resource) List(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Show default implementation. Returns a 404
func (v *{{.camel}}Resource) Show(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// New default implementation. Returns a 404
func (v *{{.camel}}Resource) New(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Create default implementation. Returns a 404
func (v *{{.camel}}Resource) Create(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Edit default implementation. Returns a 404
func (v *{{.camel}}Resource) Edit(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Update default implementation. Returns a 404
func (v *{{.camel}}Resource) Update(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}

// Destroy default implementation. Returns a 404
func (v *{{.camel}}Resource) Destroy(c buffalo.Context) error {
	return c.Error(404, errors.New("resource not implemented"))
}`

var rActionTest = `package actions_test

import (
	"testing"

	"github.com/stretchr/testify/require"
)

func Test_{{.camel}}Resource_List(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Show(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_New(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Create(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Edit(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Update(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}

func Test_{{.camel}}Resource_Destroy(t *testing.T) {
	r := require.New(t)
	r.Fail("Not Implemented!")
}
`
<|endoftext|>


<commit_before>// Package gcs implements remote storage of state on Google Cloud Storage (GCS).
package gcs

import (
	"context"
	"fmt"
	"strings"

	"cloud.google.com/go/storage"
	"github.com/hashicorp/terraform/backend"
	"github.com/hashicorp/terraform/helper/pathorcontents"
	"github.com/hashicorp/terraform/helper/schema"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/option"
)

// gcsBackend implements "backend".Backend for GCS.
// Input(), Validate() and Configure() are implemented by embedding *schema.Backend.
// State(), DeleteState() and States() are implemented explicitly.
type gcsBackend struct {
	*schema.Backend

	storageClient  *storage.Client
	storageContext context.Context

	bucketName       string
	prefix           string
	defaultStateFile string
}

func New() backend.Backend {
	be := &gcsBackend{}
	be.Backend = &schema.Backend{
		ConfigureFunc: be.configure,
		Schema: map[string]*schema.Schema{
			"bucket": {
				Type:        schema.TypeString,
				Required:    true,
				Description: "The name of the Google Cloud Storage bucket",
			},

			"path": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: "(Legacy) Path of the default state file; use prefix instead",
			},

			"prefix": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: "The directory where state files will be saved inside the bucket",
			},

			"credentials": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: "Google Cloud JSON Account Key",
				Default:     "",
			},
		},
	}

	return be
}

func (b *gcsBackend) configure(ctx context.Context) error {
	if b.storageClient != nil {
		return nil
	}

	// ctx is a background context with the backend config added.
	// Since no context is passed to remoteClient.Get(), .Lock(), etc. but
	// one is required for calling the GCP API, we're holding on to this
	// context here and re-use it later.
	b.storageContext = ctx

	data := schema.FromContextBackendConfig(b.storageContext)

	b.bucketName = data.Get("bucket").(string)
	b.prefix = strings.TrimLeft(data.Get("prefix").(string), "/")

	b.defaultStateFile = strings.TrimLeft(data.Get("path").(string), "/")

	var tokenSource oauth2.TokenSource

	if credentials := data.Get("credentials").(string); credentials != "" {
		credentialsJson, _, err := pathorcontents.Read(data.Get("credentials").(string))
		if err != nil {
			return fmt.Errorf("Error loading credentials: %v", err)
		}

		jwtConfig, err := google.JWTConfigFromJSON([]byte(credentialsJson), storage.ScopeReadWrite)
		if err != nil {
			return fmt.Errorf("Failed to get Google OAuth2 token: %v", err)
		}

		tokenSource = jwtConfig.TokenSource(b.storageContext)
	} else {
		var err error
		tokenSource, err = google.DefaultTokenSource(b.storageContext, storage.ScopeReadWrite)
		if err != nil {
			return fmt.Errorf("Failed to get Google Application Default Credentials: %v", err)
		}
	}

	client, err := storage.NewClient(b.storageContext, option.WithTokenSource(tokenSource))
	if err != nil {
		return fmt.Errorf("Failed to create Google Storage client: %v", err)
	}

	b.storageClient = client

	return nil
}
<commit_msg>backend/remote-state/gcs: Mark the "path" option as deprecated.<commit_after>// Package gcs implements remote storage of state on Google Cloud Storage (GCS).
package gcs

import (
	"context"
	"fmt"
	"strings"

	"cloud.google.com/go/storage"
	"github.com/hashicorp/terraform/backend"
	"github.com/hashicorp/terraform/helper/pathorcontents"
	"github.com/hashicorp/terraform/helper/schema"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/option"
)

// gcsBackend implements "backend".Backend for GCS.
// Input(), Validate() and Configure() are implemented by embedding *schema.Backend.
// State(), DeleteState() and States() are implemented explicitly.
type gcsBackend struct {
	*schema.Backend

	storageClient  *storage.Client
	storageContext context.Context

	bucketName       string
	prefix           string
	defaultStateFile string
}

func New() backend.Backend {
	be := &gcsBackend{}
	be.Backend = &schema.Backend{
		ConfigureFunc: be.configure,
		Schema: map[string]*schema.Schema{
			"bucket": {
				Type:        schema.TypeString,
				Required:    true,
				Description: "The name of the Google Cloud Storage bucket",
			},

			"path": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: "Path of the default state file",
				Deprecated:  "Use the \"prefix\" option instead",
			},

			"prefix": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: "The directory where state files will be saved inside the bucket",
			},

			"credentials": {
				Type:        schema.TypeString,
				Optional:    true,
				Description: "Google Cloud JSON Account Key",
				Default:     "",
			},
		},
	}

	return be
}

func (b *gcsBackend) configure(ctx context.Context) error {
	if b.storageClient != nil {
		return nil
	}

	// ctx is a background context with the backend config added.
	// Since no context is passed to remoteClient.Get(), .Lock(), etc. but
	// one is required for calling the GCP API, we're holding on to this
	// context here and re-use it later.
	b.storageContext = ctx

	data := schema.FromContextBackendConfig(b.storageContext)

	b.bucketName = data.Get("bucket").(string)
	b.prefix = strings.TrimLeft(data.Get("prefix").(string), "/")

	b.defaultStateFile = strings.TrimLeft(data.Get("path").(string), "/")

	var tokenSource oauth2.TokenSource

	if credentials := data.Get("credentials").(string); credentials != "" {
		credentialsJson, _, err := pathorcontents.Read(data.Get("credentials").(string))
		if err != nil {
			return fmt.Errorf("Error loading credentials: %v", err)
		}

		jwtConfig, err := google.JWTConfigFromJSON([]byte(credentialsJson), storage.ScopeReadWrite)
		if err != nil {
			return fmt.Errorf("Failed to get Google OAuth2 token: %v", err)
		}

		tokenSource = jwtConfig.TokenSource(b.storageContext)
	} else {
		var err error
		tokenSource, err = google.DefaultTokenSource(b.storageContext, storage.ScopeReadWrite)
		if err != nil {
			return fmt.Errorf("Failed to get Google Application Default Credentials: %v", err)
		}
	}

	client, err := storage.NewClient(b.storageContext, option.WithTokenSource(tokenSource))
	if err != nil {
		return fmt.Errorf("Failed to create Google Storage client: %v", err)
	}

	b.storageClient = client

	return nil
}
<|endoftext|>