<commit_before>package cml

import (
	"github.com/dgryski/go-farm"
	"github.com/lazybeaver/xorshift"
)

var rnd = xorshift.NewXorShift64Star(42)

func randFloat() float64 {
	return float64(rnd.Next()%10e5) / 10e5
}

func hash(s []byte, i, w uint) uint {
	return uint(farm.Hash64WithSeed(s, uint64(i))) % w
}
<commit_msg>Use pcgr instead of xorshift<commit_after>package cml

import (
	"github.com/dgryski/go-farm"
	"github.com/dgryski/go-pcgr"
)

var rnd = pcgr.Rand{0x0ddc0ffeebadf00d, 0xcafebabe}

func randFloat() float64 {
	return float64(rnd.Next()%10e5) / 10e5
}

func hash(s []byte, i, w uint) uint {
	return uint(farm.Hash64WithSeed(s, uint64(i))) % w
}
<|endoftext|>


<commit_before>// Package utils is a collection of helpful utilities for common actions within GoLang development
package utils

import (
	"net/http"
	"reflect"
	"strings"
	"unicode"
)

// type ApiResponse is a generic API response struct
type ApiResponse struct {
	Code    int                    `json:"code"`
	Message string                 `json:"message"`
	Result  interface{}            `json:"result"`
	Data    map[string]interface{} `json:"data"` // Generic extra data to be sent along in response
}

// GenerateSlug converts a string into a lowercase dasherized slug
//
// For example: GenerateSlug("My cool object") returns "my-cool-object"
func GenerateSlug(str string) (slug string) {
	return strings.Map(func(r rune) rune {
		switch {
		case r == ' ', r == '-':
			return '-'
		case r == '_', unicode.IsLetter(r), unicode.IsDigit(r):
			return r
		default:
			return -1
		}
		return -1
	}, strings.ToLower(strings.TrimSpace(str)))
}

// InChain returns a boolean if a string is already in a slice of strings
//
// [TODO] Extend this to work for all standard types
func InChain(needle string, haystack []string) bool {
	if haystack == nil {
		return false
	}
	for _, straw := range haystack {
		if needle == straw {
			return true
		}
	}
	return false
}

// Similar to "extend" in JS, only updates fields that are specified and not empty in newData
//
// Both newData and mainObj must be pointers to struct objects
func Update(mainObj interface{}, newData interface{}) bool {
	newDataVal, mainObjVal := reflect.ValueOf(newData).Elem(), reflect.ValueOf(mainObj).Elem()
	fieldCount := newDataVal.NumField()
	changed := false
	for i := 0; i < fieldCount; i++ {
		newField := newDataVal.Field(i)
		// They passed in a value for this field, update our DB user
		if newField.IsValid() && !IsEmpty(newField) {
			dbField := mainObjVal.Field(i)
			dbField.Set(newField)
			changed = true
		}
	}
	return changed
}

// IsEmpty checks to see if a field has a set value
//
// Goes beyond usual reflect.IsZero check to handle numbers, strings, and slices
// For structs, iterates over all accessible properties and returns true only if all nested fields
// are also empty.
func IsEmpty(val reflect.Value) bool {
	valType := val.Kind()
	switch valType {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return val.Int() == 0
	case reflect.Float32, reflect.Float64:
		return val.Float() == 0
	case reflect.String:
		return val.String() == ""
	case reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func:
		// Check for empty slices and props
		if val.IsNil() {
			return true
		} else if valType == reflect.Slice || valType == reflect.Map {
			return val.Len() == 0
		}
	case reflect.Struct:
		fieldCount := val.NumField()
		for i := 0; i < fieldCount; i++ {
			field := val.Field(i)
			if field.IsValid() && !IsEmpty(field) {
				return false
			}
		}
		return true
	default:
		return false
	}
	return false
}

// func CorsHandler provides an *extremely* broad Cors handler for development
// Not suitable for production use, as origin, method, and headers should all be
// more extensively restricted for a production environment
func CorsHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		rw.Header().Set("Content-Type", "application/json")
		rw.Header().Add("Access-Control-Allow-Origin", req.Header.Get("Origin"))
		rw.Header().Add("Access-Control-Allow-Methods", req.Header.Get("Access-Control-Request-Method"))
		rw.Header().Add("Access-Control-Allow-Headers", req.Header.Get("Access-Control-Request-Headers"))
		rw.Header().Add("Access-Control-Allow-Credentials", "true")

		// If we're getting an OPTIONS request, just send response
		if req.Method == "OPTIONS" {
			rw.WriteHeader(http.StatusOK)
			return
		}
		handler.ServeHTTP(rw, req)
	})
}
<commit_msg>Add JSONP support for CorsHandler<commit_after>// Package utils is a collection of helpful utilities for common actions within GoLang development
package utils

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"
	"unicode"
)

// type ApiResponse is a generic API response struct
type ApiResponse struct {
	Code    int                    `json:"code"`
	Message string                 `json:"message"`
	Result  interface{}            `json:"result"`
	Data    map[string]interface{} `json:"data"` // Generic extra data to be sent along in response
}

// GenerateSlug converts a string into a lowercase dasherized slug
//
// For example: GenerateSlug("My cool object") returns "my-cool-object"
func GenerateSlug(str string) (slug string) {
	return strings.Map(func(r rune) rune {
		switch {
		case r == ' ', r == '-':
			return '-'
		case r == '_', unicode.IsLetter(r), unicode.IsDigit(r):
			return r
		default:
			return -1
		}
		return -1
	}, strings.ToLower(strings.TrimSpace(str)))
}

// InChain returns a boolean if a string is already in a slice of strings
//
// [TODO] Extend this to work for all standard types
func InChain(needle string, haystack []string) bool {
	if haystack == nil {
		return false
	}
	for _, straw := range haystack {
		if needle == straw {
			return true
		}
	}
	return false
}

// Similar to "extend" in JS, only updates fields that are specified and not empty in newData
//
// Both newData and mainObj must be pointers to struct objects
func Update(mainObj interface{}, newData interface{}) bool {
	newDataVal, mainObjVal := reflect.ValueOf(newData).Elem(), reflect.ValueOf(mainObj).Elem()
	fieldCount := newDataVal.NumField()
	changed := false
	for i := 0; i < fieldCount; i++ {
		newField := newDataVal.Field(i)
		// They passed in a value for this field, update our DB user
		if newField.IsValid() && !IsEmpty(newField) {
			dbField := mainObjVal.Field(i)
			dbField.Set(newField)
			changed = true
		}
	}
	return changed
}

// IsEmpty checks to see if a field has a set value
//
// Goes beyond usual reflect.IsZero check to handle numbers, strings, and slices
// For structs, iterates over all accessible properties and returns true only if all nested fields
// are also empty.
func IsEmpty(val reflect.Value) bool {
	valType := val.Kind()
	switch valType {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return val.Int() == 0
	case reflect.Float32, reflect.Float64:
		return val.Float() == 0
	case reflect.String:
		return val.String() == ""
	case reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Map, reflect.Chan, reflect.Func:
		// Check for empty slices and props
		if val.IsNil() {
			return true
		} else if valType == reflect.Slice || valType == reflect.Map {
			return val.Len() == 0
		}
	case reflect.Struct:
		fieldCount := val.NumField()
		for i := 0; i < fieldCount; i++ {
			field := val.Field(i)
			if field.IsValid() && !IsEmpty(field) {
				return false
			}
		}
		return true
	default:
		return false
	}
	return false
}

// func CorsHandler provides an *extremely* broad Cors handler for development
// Not suitable for production use, as origin, method, and headers should all be
// more extensively restricted for a production environment
func CorsHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		jsonp := false
		if cbName := req.FormValue("callback"); cbName != "" && req.Method == "GET" {
			rw.Header().Set("Content-Type", "text/javascript")
			rw.Write([]byte(fmt.Sprintf("%v(", cbName)))
			jsonp = true
		} else {
			rw.Header().Set("Content-Type", "application/json")
			rw.Header().Add("Access-Control-Allow-Origin", req.Header.Get("Origin"))
			rw.Header().Add("Access-Control-Allow-Methods", req.Header.Get("Access-Control-Request-Method"))
			rw.Header().Add("Access-Control-Allow-Headers", req.Header.Get("Access-Control-Request-Headers"))
			rw.Header().Add("Access-Control-Allow-Credentials", "true")

			// If we're getting an OPTIONS request, just send response
			if req.Method == "OPTIONS" {
				rw.WriteHeader(http.StatusOK)
				return
			}
		}
		handler.ServeHTTP(rw, req)
		if jsonp {
			rw.Write([]byte(");"))
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/bogem/id3v2"
)

type Config struct {
	ID     string
	Secret string
}

func LoadConfig() (*Config, error) {
	file, err := os.Open("config.json")
	if err != nil {
		return nil, err
	}
	decoder := json.NewDecoder(file)
	config := new(Config)
	err = decoder.Decode(&config)
	if err != nil {
		return nil, err
	}
	return config, nil
}

func SetConfig() error {
	var id, secret string
	fmt.Print("Enter Spotify ID : ")
	fmt.Scanln(&id)
	fmt.Print("Enter Spotify Secret : ")
	fmt.Scanln(&secret)

	config := Config{id, secret}
	b, err := json.Marshal(config)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile("config.json", b, 0644)
	if err != nil {
		return err
	}

	return nil

}

func WalkDir(root string) (fileList []string) {

	filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
		// Fills fileList with all mp3 files in the `root` file tree
		if err != nil {
			return err
		}
		if !*isRecursive && filepath.Dir(path) != filepath.Dir(root) {
			return filepath.SkipDir
		}
		if filepath.Ext(path) == ".mp3" {
			fileList = append(fileList, path)
		}
		return nil
	})
	return
}

// Checks if a file already contains metadata
func CheckFrames(frames map[string][]id3v2.Framer) bool {
	if _, ok := frames["TALB"]; !ok {
		return false
	}
	if _, ok := frames["TIT2"]; !ok {
		return false
	}
	if _, ok := frames["APIC"]; !ok {
		return false
	}
	if _, ok := frames["TRCK"]; !ok {
		return false
	}
	if _, ok := frames["TPOS"]; !ok {
		return false
	}
	return true
}
<commit_msg>utils.go : Saving configs in musicrepair folder<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/bogem/id3v2"
)

type Config struct {
	ID     string
	Secret string
}

var configFolder string = path.Join(os.Getenv("HOME"), ".musicrepair")
var configPath string = path.Join(configFolder, "config.json")

func LoadConfig() (*Config, error) {
	file, err := os.Open(configPath)
	if err != nil {
		return nil, err
	}
	decoder := json.NewDecoder(file)
	config := new(Config)
	err = decoder.Decode(&config)
	if err != nil {
		return nil, err
	}
	return config, nil
}

func SetConfig() error {
	var id, secret string
	fmt.Print("Enter Spotify ID : ")
	fmt.Scanln(&id)
	fmt.Print("Enter Spotify Secret : ")
	fmt.Scanln(&secret)

	config := Config{id, secret}
	b, err := json.Marshal(config)
	if err != nil {
		return err
	}
	if err := os.Mkdir(configFolder, os.ModePerm); err != nil {
		return err
	}
	if err := ioutil.WriteFile(configPath, b, os.ModePerm); err != nil {
		return err
	}

	return nil
}

func WalkDir(root string) (fileList []string) {

	filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
		// Fills fileList with all mp3 files in the `root` file tree
		if err != nil {
			return err
		}
		if !*isRecursive && filepath.Dir(path) != filepath.Dir(root) {
			return filepath.SkipDir
		}
		if filepath.Ext(path) == ".mp3" {
			fileList = append(fileList, path)
		}
		return nil
	})
	return
}

// Checks if a file already contains metadata
func CheckFrames(frames map[string][]id3v2.Framer) bool {
	if _, ok := frames["TALB"]; !ok {
		return false
	}
	if _, ok := frames["TIT2"]; !ok {
		return false
	}
	if _, ok := frames["APIC"]; !ok {
		return false
	}
	if _, ok := frames["TRCK"]; !ok {
		return false
	}
	if _, ok := frames["TPOS"]; !ok {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package bip32

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"errors"
	"io"
	"math/big"

	"github.com/cmars/basen"
	"github.com/mndrix/btcutil"
	"golang.org/x/crypto/ripemd160"
)

var (
	curve                 = btcutil.Secp256k1()
	curveParams           = curve.Params()
	BitcoinBase58Encoding = basen.NewEncoding("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")

	ErrInvalidSeed      = errors.New("Invalid seed")
	ErrInvalidPublicKey = errors.New("Invalid public key")
)

//
// Hashes
//

func hashSha256(data []byte) []byte {
	hasher := sha256.New()
	hasher.Write(data)
	return hasher.Sum(nil)
}

func hashDoubleSha256(data []byte) []byte {
	return hashSha256(hashSha256(data))
}

func hashRipeMD160(data []byte) []byte {
	hasher := ripemd160.New()
	io.WriteString(hasher, string(data))
	return hasher.Sum(nil)
}

func hash160(data []byte) []byte {
	return hashRipeMD160(hashSha256(data))
}

//
// Encoding
//

func checksum(data []byte) []byte {
	return hashDoubleSha256(data)[:4]
}

func addChecksumToBytes(data []byte) []byte {
	checksum := checksum(data)
	return append(data, checksum...)
}

func base58Encode(data []byte) string {
	return BitcoinBase58Encoding.EncodeToString(data)
}

func base58Decode(data string) ([]byte, error) {
	return BitcoinBase58Encoding.DecodeString(data)
}

// Keys
func publicKeyForPrivateKey(key []byte) []byte {
	return compressPublicKey(curve.ScalarBaseMult([]byte(key)))
}

func addPublicKeys(key1 []byte, key2 []byte) []byte {
	x1, y1 := expandPublicKey(key1)
	x2, y2 := expandPublicKey(key2)
	return compressPublicKey(curve.Add(x1, y1, x2, y2))
}

func addPrivateKeys(key1 []byte, key2 []byte) []byte {
	var key1Int big.Int
	var key2Int big.Int
	key1Int.SetBytes(key1)
	key2Int.SetBytes(key2)

	key1Int.Add(&key1Int, &key2Int)
	key1Int.Mod(&key1Int, curve.Params().N)

	return key1Int.Bytes()
}

func compressPublicKey(x *big.Int, y *big.Int) []byte {
	var key bytes.Buffer

	// Write header; 0x2 for even y value; 0x3 for odd
	key.WriteByte(byte(0x2) + byte(y.Bit(0)))

	// Write X coord; Pad the key so x is aligned with the LSB. Pad size is key length - header size (1) - xBytes size
	xBytes := x.Bytes()
	for i := 0; i < (PublicKeyCompressedLength - 1 - len(xBytes)); i++ {
		key.WriteByte(0x0)
	}
	key.Write(xBytes)

	return key.Bytes()
}

// As described at https://bitcointa.lk/threads/compressed-keys-y-from-x.95735/
func expandPublicKey(key []byte) (*big.Int, *big.Int) {
	Y := big.NewInt(0)
	X := big.NewInt(0)
	qPlus1Div4 := big.NewInt(0)
	X.SetBytes(key[1:])

	// y^2 = x^3 + ax^2 + b
	// a = 0
	// => y^2 = x^3 + b
	ySquared := X.Exp(X, big.NewInt(3), nil)
	ySquared.Add(ySquared, curveParams.B)

	qPlus1Div4.Add(curveParams.P, big.NewInt(1))
	qPlus1Div4.Div(qPlus1Div4, big.NewInt(4))

	// sqrt(n) = n^((q+1)/4) if q = 3 mod 4
	Y.Exp(ySquared, qPlus1Div4, curveParams.P)

	if uint32(key[0])%2 == 0 {
		Y.Sub(curveParams.P, Y)
	}

	return X, Y
}

func validatePrivateKey(key []byte) error {
	keyInt, _ := binary.ReadVarint(bytes.NewBuffer(key))
	if keyInt == 0 || bytes.Compare(key, curveParams.N.Bytes()) >= 0 {
		return ErrInvalidSeed
	}

	return nil
}

func validateChildPublicKey(key []byte) error {
	x, y := expandPublicKey(key)

	if x.Sign() == 0 || y.Sign() == 0 {
		return ErrInvalidPublicKey
	}

	return nil
}

//
// Numerical
//
func uint32Bytes(i uint32) []byte {
	bytes := make([]byte, 4)
	binary.BigEndian.PutUint32(bytes, i)
	return bytes
}
<commit_msg>Fixed a key-length error<commit_after>package bip32

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"math/big"

	"github.com/FactomProject/basen"
	"github.com/FactomProject/btcutilecc"
	"golang.org/x/crypto/ripemd160"
)

var (
	curve                 = btcutil.Secp256k1()
	curveParams           = curve.Params()
	BitcoinBase58Encoding = basen.NewEncoding("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")

	ErrInvalidSeed      = errors.New("Invalid seed")
	ErrInvalidPublicKey = errors.New("Invalid public key")
)

//
// Hashes
//

func hashSha256(data []byte) []byte {
	hasher := sha256.New()
	hasher.Write(data)
	return hasher.Sum(nil)
}

func hashDoubleSha256(data []byte) []byte {
	return hashSha256(hashSha256(data))
}

func hashRipeMD160(data []byte) []byte {
	hasher := ripemd160.New()
	io.WriteString(hasher, string(data))
	return hasher.Sum(nil)
}

func hash160(data []byte) []byte {
	return hashRipeMD160(hashSha256(data))
}

//
// Encoding
//

func checksum(data []byte) []byte {
	return hashDoubleSha256(data)[:4]
}

func addChecksumToBytes(data []byte) []byte {
	checksum := checksum(data)
	return append(data, checksum...)
}

func base58Encode(data []byte) string {
	return BitcoinBase58Encoding.EncodeToString(data)
}

func base58Decode(data string) ([]byte, error) {
	return BitcoinBase58Encoding.DecodeString(data)
}

// Keys
func publicKeyForPrivateKey(key []byte) []byte {
	return compressPublicKey(curve.ScalarBaseMult([]byte(key)))
}

func addPublicKeys(key1 []byte, key2 []byte) []byte {
	x1, y1 := expandPublicKey(key1)
	x2, y2 := expandPublicKey(key2)
	return compressPublicKey(curve.Add(x1, y1, x2, y2))
}

func addPrivateKeys(key1 []byte, key2 []byte) []byte {
	var key1Int big.Int
	var key2Int big.Int
	key1Int.SetBytes(key1)
	key2Int.SetBytes(key2)

	key1Int.Add(&key1Int, &key2Int)
	key1Int.Mod(&key1Int, curve.Params().N)

	b := key1Int.Bytes()
	if len(b) < 32 {
		extra := make([]byte, 32-len(b))
		b = append(extra, b...)
	}
	return b
}

func compressPublicKey(x *big.Int, y *big.Int) []byte {
	var key bytes.Buffer

	// Write header; 0x2 for even y value; 0x3 for odd
	key.WriteByte(byte(0x2) + byte(y.Bit(0)))

	// Write X coord; Pad the key so x is aligned with the LSB. Pad size is key length - header size (1) - xBytes size
	xBytes := x.Bytes()
	for i := 0; i < (PublicKeyCompressedLength - 1 - len(xBytes)); i++ {
		key.WriteByte(0x0)
	}
	key.Write(xBytes)

	return key.Bytes()
}

// As described at https://bitcointa.lk/threads/compressed-keys-y-from-x.95735/
func expandPublicKey(key []byte) (*big.Int, *big.Int) {
	Y := big.NewInt(0)
	X := big.NewInt(0)
	qPlus1Div4 := big.NewInt(0)
	X.SetBytes(key[1:])

	// y^2 = x^3 + ax^2 + b
	// a = 0
	// => y^2 = x^3 + b
	ySquared := X.Exp(X, big.NewInt(3), nil)
	ySquared.Add(ySquared, curveParams.B)

	qPlus1Div4.Add(curveParams.P, big.NewInt(1))
	qPlus1Div4.Div(qPlus1Div4, big.NewInt(4))

	// sqrt(n) = n^((q+1)/4) if q = 3 mod 4
	Y.Exp(ySquared, qPlus1Div4, curveParams.P)

	if uint32(key[0])%2 == 0 {
		Y.Sub(curveParams.P, Y)
	}

	return X, Y
}

func validatePrivateKey(key []byte) error {
	if fmt.Sprintf("%x", key) == "0000000000000000000000000000000000000000000000000000000000000000" || //if the key is zero
		bytes.Compare(key, curveParams.N.Bytes()) >= 0 || //or is outside of the curve
		len(key) != 32 { //or is too short
		return ErrInvalidSeed
	}

	return nil
}

func validateChildPublicKey(key []byte) error {
	x, y := expandPublicKey(key)

	if x.Sign() == 0 || y.Sign() == 0 {
		return ErrInvalidPublicKey
	}

	return nil
}

//
// Numerical
//
func uint32Bytes(i uint32) []byte {
	bytes := make([]byte, 4)
	binary.BigEndian.PutUint32(bytes, i)
	return bytes
}
<|endoftext|>


<commit_before>package api

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/julienschmidt/httprouter"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

/*
//API Caller
builder := api.Builder("http://localhost/api")
caller := builder.PUT("/storage/:id/datas/:did")
caller.Param.Set("id", 1)
caller.Param.Set("did", 3)
caller.QParam.Set("name", "testname")
code, ret := caller.Call()
caller:= caller.GET("/storage/:id/datas/:did")
caller.Param.Set("id", 1)
caller.Param.Set("did", 3)
caller.QParam.Set("offset", "1")
code, ret := caller.Call()
caller := caller.POST("/storage/:id/datas/:did")
caller.Param.Set("id", 1)
caller.Param.Set("did", 3)
caller.Data = "testbody"
code, ret := caller.Call()
caller := caller.DELETE("/storage/:id/datas/:did")
caller.Param.Set("id", 1)
caller.Param.Set("did", 3)
code, ret := caller.Call()
*/

type BuildEngine struct {
	host    string
	isLocal bool
	addr    string
	cookie  string
}

func Builder(host string) *BuildEngine {
	url, err := url.ParseRequestURI(host)
	if err != nil {
		panic(err)
	}
	addr := ""
	isLocal := strings.Index(url.Host, "localhost") >= 0 || strings.Index(url.Host, "127.0.0.1") >= 0
	if isLocal {
		idx := strings.Index(url.Host, ":")
		if idx >= 0 {
			addr = url.Host[idx:]
		} else {
			switch url.Scheme {
			case "http":
				addr = ":80"
			case "https":
				addr = ":443"
			default:
				panic("Unknown scheme : " + url.Scheme)
			}
		}
	}
	return &BuildEngine{
		host:    host,
		addr:    addr,
		isLocal: isLocal,
	}
}

func (this *BuildEngine) createCaller(url string, method int) *Caller {
	var handler localAPIHandler
	if this.isLocal {
		handler = getLocalSupportHandler(this.addr, url, method)
	}
	var client *http.Client
	if handler == nil {
		client = new(http.Client)
	}
	return &Caller{
		engine: this,
		url:    url,
		method: method,
		Params: &CallParams{
			Params: make([]httprouter.Param, 0),
		},
		QParams: make(map[string][]string),
		handler: handler,
		client:  client,
	}
}

func (this *BuildEngine) PUT(url string) *Caller {
	return this.createCaller(url, METHOD_PUT)
}
func (this *BuildEngine) GET(url string) *Caller {
	return this.createCaller(url, METHOD_GET)
}
func (this *BuildEngine) POST(url string) *Caller {
	return this.createCaller(url, METHOD_POST)
}
func (this *BuildEngine) DELETE(url string) *Caller {
	return this.createCaller(url, METHOD_DELETE)
}

type Caller struct {
	engine  *BuildEngine
	url     string
	method  int
	Params  *CallParams
	QParams url.Values
	Data    interface{}
	handler localAPIHandler
	client  *http.Client
}

func (this *Caller) Call() (code int, ret interface{}) {
	method := ""
	switch this.method {
	case METHOD_PUT:
		method = "PUT"
	case METHOD_GET:
		method = "GET"
	case METHOD_POST:
		method = "POST"
	case METHOD_DELETE:
		method = "DELETE"
	}
	if true {

	}
	var buffer bytes.Buffer
	buffer.WriteString(this.engine.host)
	urlPath := this.url
	if len(this.Params.Params) > 0 {
		urlList := strings.Split(urlPath, "/")
		urlHash := make(map[string]int)
		for i, v := range urlList {
			urlHash[strings.ToLower(v)] = i
		}
		for _, v := range this.Params.Params {
			if idx, has := urlHash[strings.ToLower(fmt.Sprintf(":%s", v.Key))]; has {
				urlList[idx] = v.Value
			}
		}
		urlPath = strings.Join(urlList, "/")
	}
	buffer.WriteString(urlPath)
	if len(this.QParams) > 0 {
		buffer.WriteString("?")
		isFirst := true
		for k, v := range this.QParams {
			if isFirst {
				isFirst = false
			} else {
				buffer.WriteString("&")
			}
			buffer.WriteString(k)
			buffer.WriteString("=")
			buffer.WriteString(strings.Join(v, ","))
		}
	}

	url := buffer.String()
	isRoutable := this.engine.isLocal && this.handler != nil

	var reader io.Reader
	if !isRoutable && this.Data != nil {
		b, err := json.Marshal(this.Data)
		if err != nil {
			panic(err)
		}
		reader = bytes.NewReader(b)
	}
	req, err := http.NewRequest(method, url, reader)
	if err != nil {
		panic(err)
	}
	req.Header.Add("Cookie", this.engine.cookie)
	if isRoutable {
		context := &Context{
			code:    -1,
			ret:     nil,
			Data:    this.Data,
			Params:  this.Params.Params,
			QParams: this.QParams,
			Request: req,
			Writer:  newResponse(),
		}
		this.handler(context)
		this.engine.cookie = context.Writer.Header().Get("Set-Cookie")
		code = context.code
		ret = context.ret
	} else {
		req, err := http.NewRequest(method, url, reader)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Add("Cookie", this.engine.cookie)
		if err != nil {
			panic(err)
		}
		res, err := this.client.Do(req)
		if err != nil {
			panic(err)
		}
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			//panic(err)
		}

		var reqRet interface{}
		if body != nil && len(body) > 0 {
			err := json.Unmarshal(body, &reqRet)
			if err != nil {
				reqRet = string(body)
			}
		}

		code = res.StatusCode
		ret = reqRet
	}
	return
}

type CallParams struct {
	httprouter.Params
}

func (this *CallParams) Set(key string, val string) {
	var param httprouter.Param
	param.Key = key
	param.Value = val
	has := false
	for _, v := range this.Params {
		fmt.Print(v.Key, v.Value, "\t")
	}
	fmt.Println("")
	for _, v := range this.Params {
		if strings.ToLower(key) == strings.ToLower(v.Key) {
			v.Value = val
			has = true
		}
	}
	if !has {
		this.Params = append(this.Params, param)
	}
	for _, v := range this.Params {
		fmt.Print(v.Key, v.Value, "\t")
	}
	fmt.Println("")
}

// response implements http.ResponseWriter.
type response struct {
	header      http.Header
	w           bytes.Buffer
	wroteHeader bool
}

func newResponse() *response {
	return &response{
		header: http.Header{},
	}
}

func (this *response) Header() http.Header {
	return this.header
}

func (this *response) Write(data []byte) (int, error) {
	return this.w.Write(data)
}

func (this *response) WriteHeader(code int) {
	if this.wroteHeader {
		return
	}
	this.wroteHeader = true
	if code == http.StatusNotModified {
		// Must not have body.
		this.header.Del("Content-Type")
		this.header.Del("Content-Length")
		this.header.Del("Transfer-Encoding")
	} else if this.header.Get("Content-Type") == "" {
		this.header.Set("Content-Type", "text/html; charset=utf-8")
	}

	if this.header.Get("Date") == "" {
		this.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
	}

	w := bufio.NewWriter(&this.w)
	fmt.Fprintf(w, "Status: %d %s\r\n", code, http.StatusText(code))
	this.header.Write(w)
	this.w.WriteString("\r\n")
}
<commit_msg>Test 3<commit_after>package api

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/julienschmidt/httprouter"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

/*
//API Caller
builder := api.Builder("http://localhost/api")
caller := builder.PUT("/storage/:id/datas/:did")
caller.Param.Set("id", 1)
caller.Param.Set("did", 3)
caller.QParam.Set("name", "testname")
code, ret := caller.Call()
caller:= caller.GET("/storage/:id/datas/:did")
caller.Param.Set("id", 1)
caller.Param.Set("did", 3)
caller.QParam.Set("offset", "1")
code, ret := caller.Call()
caller := caller.POST("/storage/:id/datas/:did")
caller.Param.Set("id", 1)
caller.Param.Set("did", 3)
caller.Data = "testbody"
code, ret := caller.Call()
caller := caller.DELETE("/storage/:id/datas/:did")
caller.Param.Set("id", 1)
caller.Param.Set("did", 3)
code, ret := caller.Call()
*/

type BuildEngine struct {
	host    string
	isLocal bool
	addr    string
	cookie  string
}

func Builder(host string) *BuildEngine {
	url, err := url.ParseRequestURI(host)
	if err != nil {
		panic(err)
	}
	addr := ""
	isLocal := strings.Index(url.Host, "localhost") >= 0 || strings.Index(url.Host, "127.0.0.1") >= 0
	if isLocal {
		idx := strings.Index(url.Host, ":")
		if idx >= 0 {
			addr = url.Host[idx:]
		} else {
			switch url.Scheme {
			case "http":
				addr = ":80"
			case "https":
				addr = ":443"
			default:
				panic("Unknown scheme : " + url.Scheme)
			}
		}
	}
	return &BuildEngine{
		host:    host,
		addr:    addr,
		isLocal: isLocal,
	}
}

func (this *BuildEngine) createCaller(url string, method int) *Caller {
	var handler localAPIHandler
	if this.isLocal {
		handler = getLocalSupportHandler(this.addr, url, method)
	}
	var client *http.Client
	if handler == nil {
		client = new(http.Client)
	}
	return &Caller{
		engine: this,
		url:    url,
		method: method,
		Params: &CallParams{
			Params: make([]httprouter.Param, 0),
		},
		QParams: make(map[string][]string),
		handler: handler,
		client:  client,
	}
}

func (this *BuildEngine) PUT(url string) *Caller {
	return this.createCaller(url, METHOD_PUT)
}
func (this *BuildEngine) GET(url string) *Caller {
	return this.createCaller(url, METHOD_GET)
}
func (this *BuildEngine) POST(url string) *Caller {
	return this.createCaller(url, METHOD_POST)
}
func (this *BuildEngine) DELETE(url string) *Caller {
	return this.createCaller(url, METHOD_DELETE)
}

type Caller struct {
	engine  *BuildEngine
	url     string
	method  int
	Params  *CallParams
	QParams url.Values
	Data    interface{}
	handler localAPIHandler
	client  *http.Client
}

func (this *Caller) Call() (code int, ret interface{}) {
	method := ""
	switch this.method {
	case METHOD_PUT:
		method = "PUT"
	case METHOD_GET:
		method = "GET"
	case METHOD_POST:
		method = "POST"
	case METHOD_DELETE:
		method = "DELETE"
	}
	if true {

	}
	var buffer bytes.Buffer
	buffer.WriteString(this.engine.host)
	urlPath := this.url
	if len(this.Params.Params) > 0 {
		urlList := strings.Split(urlPath, "/")
		urlHash := make(map[string]int)
		for i, v := range urlList {
			urlHash[strings.ToLower(v)] = i
		}
		for _, v := range this.Params.Params {
			if idx, has := urlHash[strings.ToLower(fmt.Sprintf(":%s", v.Key))]; has {
				urlList[idx] = v.Value
			}
		}
		urlPath = strings.Join(urlList, "/")
	}
	buffer.WriteString(urlPath)
	if len(this.QParams) > 0 {
		buffer.WriteString("?")
		isFirst := true
		for k, v := range this.QParams {
			if isFirst {
				isFirst = false
			} else {
				buffer.WriteString("&")
			}
			buffer.WriteString(k)
			buffer.WriteString("=")
			buffer.WriteString(strings.Join(v, ","))
		}
	}

	url := buffer.String()
	isRoutable := this.engine.isLocal && this.handler != nil

	var reader io.Reader
	if !isRoutable && this.Data != nil {
		b, err := json.Marshal(this.Data)
		if err != nil {
			panic(err)
		}
		reader = bytes.NewReader(b)
	}
	req, err := http.NewRequest(method, url, reader)
	if err != nil {
		panic(err)
	}
	req.Header.Add("Cookie", this.engine.cookie)
	if isRoutable {
		context := &Context{
			code:    -1,
			ret:     nil,
			Data:    this.Data,
			Params:  this.Params.Params,
			QParams: this.QParams,
			Request: req,
			Writer:  newResponse(),
		}
		this.handler(context)
		this.engine.cookie = context.Writer.Header().Get("Set-Cookie")
		code = context.code
		ret = context.ret
	} else {
		req, err := http.NewRequest(method, url, reader)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Add("Cookie", this.engine.cookie)
		if err != nil {
			panic(err)
		}
		res, err := this.client.Do(req)
		if err != nil {
			panic(err)
		}
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			//panic(err)
		}

		var reqRet interface{}
		if body != nil && len(body) > 0 {
			err := json.Unmarshal(body, &reqRet)
			if err != nil {
				reqRet = string(body)
			}
		}

		code = res.StatusCode
		ret = reqRet
	}
	return
}

type CallParams struct {
	httprouter.Params
}

func (this *CallParams) Set(key string, val string) {
	var param httprouter.Param
	param.Key = key
	param.Value = val
	has := false
	fmt.Println(key, "\t", val)
	for _, v := range this.Params {
		fmt.Print(v.Key, "\t", v.Value, "\t")
	}
	fmt.Println("")
	for _, v := range this.Params {
		if strings.ToLower(key) == strings.ToLower(v.Key) {
			v.Value = val
			has = true
		}
	}
	if !has {
		this.Params = append(this.Params, param)
	}
	for _, v := range this.Params {
		fmt.Print(v.Key, "\t", v.Value, "\t")
	}
	fmt.Println("")
}

// response implements http.ResponseWriter.
type response struct {
	header      http.Header
	w           bytes.Buffer
	wroteHeader bool
}

func newResponse() *response {
	return &response{
		header: http.Header{},
	}
}

func (this *response) Header() http.Header {
	return this.header
}

func (this *response) Write(data []byte) (int, error) {
	return this.w.Write(data)
}

func (this *response) WriteHeader(code int) {
	if this.wroteHeader {
		return
	}
	this.wroteHeader = true
	if code == http.StatusNotModified {
		// Must not have body.
		this.header.Del("Content-Type")
		this.header.Del("Content-Length")
		this.header.Del("Transfer-Encoding")
	} else if this.header.Get("Content-Type") == "" {
		this.header.Set("Content-Type", "text/html; charset=utf-8")
	}

	if this.header.Get("Date") == "" {
		this.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
	}

	w := bufio.NewWriter(&this.w)
	fmt.Fprintf(w, "Status: %d %s\r\n", code, http.StatusText(code))
	this.header.Write(w)
	this.w.WriteString("\r\n")
}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/sacloud/libsacloud"
	"github.com/sacloud/libsacloud/sacloud"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

const (
	sakuraCloudAPIRoot = "https://secure.sakura.ad.jp/cloud/zone"
)

// Client APIクライアント
type Client struct {
	// AccessToken アクセストークン
	AccessToken string
	// AccessTokenSecret アクセストークンシークレット
	AccessTokenSecret string
	// Zone 対象ゾーン
	Zone string
	*API
	// TraceMode トレースモード
	TraceMode bool
	// DefaultTimeoutDuration デフォルトタイムアウト間隔
	DefaultTimeoutDuration time.Duration
	// ユーザーエージェント
	UserAgent string
}

// NewClient APIクライアント作成
func NewClient(token, tokenSecret, zone string) *Client {
	c := &Client{
		AccessToken:            token,
		AccessTokenSecret:      tokenSecret,
		Zone:                   zone,
		TraceMode:              false,
		DefaultTimeoutDuration: 20 * time.Minute,
		UserAgent:              fmt.Sprintf("libsacloud/%s", libsacloud.Version),
	}
	c.API = newAPI(c)
	return c
}

// Clone APIクライアント クローン作成
func (c *Client) Clone() *Client {
	n := &Client{AccessToken: c.AccessToken, AccessTokenSecret: c.AccessTokenSecret, Zone: c.Zone, TraceMode: c.TraceMode}
	n.API = newAPI(n)
	return n
}

// API libsacloudでサポートしているAPI群
type API struct {
	AuthStatus    *AuthStatusAPI    // 認証状態API
	AutoBackup    *AutoBackupAPI    // 自動バックアップAPI
	Archive       *ArchiveAPI       // アーカイブAPI
	Bill          *BillAPI          // 請求情報API
	Bridge        *BridgeAPI        // ブリッジAPi
	CDROM         *CDROMAPI         // ISOイメージAPI
	Database      *DatabaseAPI      // データベースAPI
	Disk          *DiskAPI          // ディスクAPI
	DNS           *DNSAPI           // DNS API
	Facility      *FacilityAPI      // ファシリティAPI
	GSLB          *GSLBAPI          // GSLB API
	Icon          *IconAPI          // アイコンAPI
	Interface     *InterfaceAPI     // インターフェースAPI
	Internet      *InternetAPI      // ルーターAPI
	IPAddress     *IPAddressAPI     // IPアドレスAPI
	IPv6Addr      *IPv6AddrAPI      // IPv6アドレスAPI
	IPv6Net       *IPv6NetAPI       // IPv6ネットワークAPI
	License       *LicenseAPI       // ライセンスAPI
	LoadBalancer  *LoadBalancerAPI  // ロードバランサーAPI
	Note          *NoteAPI          // スタートアップスクリプトAPI
	PacketFilter  *PacketFilterAPI  // パケットフィルタAPI
	Product       *ProductAPI       // 製品情報API
	Server        *ServerAPI        // サーバーAPI
	SimpleMonitor *SimpleMonitorAPI // シンプル監視API
	SSHKey        *SSHKeyAPI        // 公開鍵API
	Subnet        *SubnetAPI        // IPv4ネットワークAPI
	Switch        *SwitchAPI        // スイッチAPI
	VPCRouter     *VPCRouterAPI     // VPCルーターAPI
	WebAccel      *WebAccelAPI      // ウェブアクセラレータAPI

}

// ProductAPI 製品情報関連API群
type ProductAPI struct {
	Server   *ProductServerAPI   // サーバープランAPI
	License  *ProductLicenseAPI  // ライセンスプランAPI
	Disk     *ProductDiskAPI     // ディスクプランAPI
	Internet *ProductInternetAPI // ルータープランAPI
	Price    *PublicPriceAPI     // 価格情報API

}

// FacilityAPI ファシリティ関連API群
type FacilityAPI struct {
	Region *RegionAPI // リージョンAPI
	Zone   *ZoneAPI   // ゾーンAPI
}

func newAPI(client *Client) *API {
	return &API{
		AuthStatus: NewAuthStatusAPI(client),
		AutoBackup: NewAutoBackupAPI(client),
		Archive:    NewArchiveAPI(client),
		Bill:       NewBillAPI(client),
		Bridge:     NewBridgeAPI(client),
		CDROM:      NewCDROMAPI(client),
		Database:   NewDatabaseAPI(client),
		Disk:       NewDiskAPI(client),
		DNS:        NewDNSAPI(client),
		Facility: &FacilityAPI{
			Region: NewRegionAPI(client),
			Zone:   NewZoneAPI(client),
		},
		GSLB:         NewGSLBAPI(client),
		Icon:         NewIconAPI(client),
		Interface:    NewInterfaceAPI(client),
		Internet:     NewInternetAPI(client),
		IPAddress:    NewIPAddressAPI(client),
		IPv6Addr:     NewIPv6AddrAPI(client),
		IPv6Net:      NewIPv6NetAPI(client),
		License:      NewLicenseAPI(client),
		LoadBalancer: NewLoadBalancerAPI(client),
		Note:         NewNoteAPI(client),
		PacketFilter: NewPacketFilterAPI(client),
		Product: &ProductAPI{
			Server:   NewProductServerAPI(client),
			License:  NewProductLicenseAPI(client),
			Disk:     NewProductDiskAPI(client),
			Internet: NewProductInternetAPI(client),
			Price:    NewPublicPriceAPI(client),
		},
		Server:        NewServerAPI(client),
		SimpleMonitor: NewSimpleMonitorAPI(client),
		SSHKey:        NewSSHKeyAPI(client),
		Subnet:        NewSubnetAPI(client),
		Switch:        NewSwitchAPI(client),
		VPCRouter:     NewVPCRouterAPI(client),
		WebAccel:      NewWebAccelAPI(client),
	}
}

func (c *Client) getEndpoint() string {
	return fmt.Sprintf("%s/%s", sakuraCloudAPIRoot, c.Zone)
}

func (c *Client) isOkStatus(code int) bool {
	codes := map[int]bool{
		200: true,
		201: true,
		202: true,
		204: true,
		305: false,
		400: false,
		401: false,
		403: false,
		404: false,
		405: false,
		406: false,
		408: false,
		409: false,
		411: false,
		413: false,
		415: false,
		500: false,
		503: false,
	}
	return codes[code]
}

func (c *Client) newRequest(method, uri string, body interface{}) ([]byte, error) {
	var (
		client = &http.Client{}
		url    = fmt.Sprintf("%s/%s", c.getEndpoint(), uri)
		err    error
		req    *http.Request
	)

	if body != nil {
		var bodyJSON []byte
		bodyJSON, err = json.Marshal(body)
		if err != nil {
			return nil, err
		}
		if method == "GET" {
			url = fmt.Sprintf("%s/%s?%s", c.getEndpoint(), uri, bytes.NewBuffer(bodyJSON))
			req, err = http.NewRequest(method, url, nil)
		} else {
			req, err = http.NewRequest(method, url, bytes.NewBuffer(bodyJSON))
		}
		if c.TraceMode {
			b, _ := json.MarshalIndent(body, "", "\t")
			log.Printf("[libsacloud:Client#request] method : %#v , url : %s , \nbody : %s", method, url, b)
		}

	} else {
		req, err = http.NewRequest(method, url, nil)
		if c.TraceMode {
			log.Printf("[libsacloud:Client#request] method : %#v , url : %s ", method, url)
		}
	}

	if err != nil {
		return nil, fmt.Errorf("Error with request: %v - %q", url, err)
	}

	req.SetBasicAuth(c.AccessToken, c.AccessTokenSecret)
	req.Header.Add("X-Sakura-Bigint-As-Int", "1") //Use BigInt on resource ids.
	//if c.TraceMode {
	//	req.Header.Add("X-Sakura-API-Beautify", "1") // format response-JSON
	//}
	req.Header.Add("User-Agent", c.UserAgent)
	req.Method = method

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if c.TraceMode {
		v := &map[string]interface{}{}
		json.Unmarshal(data, v)
		b, _ := json.MarshalIndent(v, "", "\t")
		log.Printf("[libsacloud:Client#response] : %s", b)
	}
	if !c.isOkStatus(resp.StatusCode) {

		errResponse := &sacloud.ResultErrorValue{}
		err := json.Unmarshal(data, errResponse)

		if err != nil {
			return nil, fmt.Errorf("Error in response: %s", string(data))
		}
		return nil, fmt.Errorf("Error in response: %#v", errResponse)

	}
	if err != nil {
		return nil, err
	}

	return data, nil
}
<commit_msg>Fix client Clone() method<commit_after>package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/sacloud/libsacloud"
	"github.com/sacloud/libsacloud/sacloud"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

const (
	sakuraCloudAPIRoot = "https://secure.sakura.ad.jp/cloud/zone"
)

// Client APIクライアント
type Client struct {
	// AccessToken アクセストークン
	AccessToken string
	// AccessTokenSecret アクセストークンシークレット
	AccessTokenSecret string
	// Zone 対象ゾーン
	Zone string
	*API
	// TraceMode トレースモード
	TraceMode bool
	// DefaultTimeoutDuration デフォルトタイムアウト間隔
	DefaultTimeoutDuration time.Duration
	// ユーザーエージェント
	UserAgent string
}

// NewClient APIクライアント作成
func NewClient(token, tokenSecret, zone string) *Client {
	c := &Client{
		AccessToken:            token,
		AccessTokenSecret:      tokenSecret,
		Zone:                   zone,
		TraceMode:              false,
		DefaultTimeoutDuration: 20 * time.Minute,
		UserAgent:              fmt.Sprintf("libsacloud/%s", libsacloud.Version),
	}
	c.API = newAPI(c)
	return c
}

// Clone APIクライアント クローン作成
func (c *Client) Clone() *Client {
	n := &Client{
		AccessToken:            c.AccessToken,
		AccessTokenSecret:      c.AccessTokenSecret,
		Zone:                   c.Zone,
		TraceMode:              c.TraceMode,
		DefaultTimeoutDuration: c.DefaultTimeoutDuration,
		UserAgent:              c.UserAgent,
	}
	n.API = newAPI(n)
	return n
}

// API libsacloudでサポートしているAPI群
type API struct {
	AuthStatus    *AuthStatusAPI    // 認証状態API
	AutoBackup    *AutoBackupAPI    // 自動バックアップAPI
	Archive       *ArchiveAPI       // アーカイブAPI
	Bill          *BillAPI          // 請求情報API
	Bridge        *BridgeAPI        // ブリッジAPi
	CDROM         *CDROMAPI         // ISOイメージAPI
	Database      *DatabaseAPI      // データベースAPI
	Disk          *DiskAPI          // ディスクAPI
	DNS           *DNSAPI           // DNS API
	Facility      *FacilityAPI      // ファシリティAPI
	GSLB          *GSLBAPI          // GSLB API
	Icon          *IconAPI          // アイコンAPI
	Interface     *InterfaceAPI     // インターフェースAPI
	Internet      *InternetAPI      // ルーターAPI
	IPAddress     *IPAddressAPI     // IPアドレスAPI
	IPv6Addr      *IPv6AddrAPI      // IPv6アドレスAPI
	IPv6Net       *IPv6NetAPI       // IPv6ネットワークAPI
	License       *LicenseAPI       // ライセンスAPI
	LoadBalancer  *LoadBalancerAPI  // ロードバランサーAPI
	Note          *NoteAPI          // スタートアップスクリプトAPI
	PacketFilter  *PacketFilterAPI  // パケットフィルタAPI
	Product       *ProductAPI       // 製品情報API
	Server        *ServerAPI        // サーバーAPI
	SimpleMonitor *SimpleMonitorAPI // シンプル監視API
	SSHKey        *SSHKeyAPI        // 公開鍵API
	Subnet        *SubnetAPI        // IPv4ネットワークAPI
	Switch        *SwitchAPI        // スイッチAPI
	VPCRouter     *VPCRouterAPI     // VPCルーターAPI
	WebAccel      *WebAccelAPI      // ウェブアクセラレータAPI

}

// ProductAPI 製品情報関連API群
type ProductAPI struct {
	Server   *ProductServerAPI   // サーバープランAPI
	License  *ProductLicenseAPI  // ライセンスプランAPI
	Disk     *ProductDiskAPI     // ディスクプランAPI
	Internet *ProductInternetAPI // ルータープランAPI
	Price    *PublicPriceAPI     // 価格情報API

}

// FacilityAPI ファシリティ関連API群
type FacilityAPI struct {
	Region *RegionAPI // リージョンAPI
	Zone   *ZoneAPI   // ゾーンAPI
}

func newAPI(client *Client) *API {
	return &API{
		AuthStatus: NewAuthStatusAPI(client),
		AutoBackup: NewAutoBackupAPI(client),
		Archive:    NewArchiveAPI(client),
		Bill:       NewBillAPI(client),
		Bridge:     NewBridgeAPI(client),
		CDROM:      NewCDROMAPI(client),
		Database:   NewDatabaseAPI(client),
		Disk:       NewDiskAPI(client),
		DNS:        NewDNSAPI(client),
		Facility: &FacilityAPI{
			Region: NewRegionAPI(client),
			Zone:   NewZoneAPI(client),
		},
		GSLB:         NewGSLBAPI(client),
		Icon:         NewIconAPI(client),
		Interface:    NewInterfaceAPI(client),
		Internet:     NewInternetAPI(client),
		IPAddress:    NewIPAddressAPI(client),
		IPv6Addr:     NewIPv6AddrAPI(client),
		IPv6Net:      NewIPv6NetAPI(client),
		License:      NewLicenseAPI(client),
		LoadBalancer: NewLoadBalancerAPI(client),
		Note:         NewNoteAPI(client),
		PacketFilter: NewPacketFilterAPI(client),
		Product: &ProductAPI{
			Server:   NewProductServerAPI(client),
			License:  NewProductLicenseAPI(client),
			Disk:     NewProductDiskAPI(client),
			Internet: NewProductInternetAPI(client),
			Price:    NewPublicPriceAPI(client),
		},
		Server:        NewServerAPI(client),
		SimpleMonitor: NewSimpleMonitorAPI(client),
		SSHKey:        NewSSHKeyAPI(client),
		Subnet:        NewSubnetAPI(client),
		Switch:        NewSwitchAPI(client),
		VPCRouter:     NewVPCRouterAPI(client),
		WebAccel:      NewWebAccelAPI(client),
	}
}

func (c *Client) getEndpoint() string {
	return fmt.Sprintf("%s/%s", sakuraCloudAPIRoot, c.Zone)
}

func (c *Client) isOkStatus(code int) bool {
	codes := map[int]bool{
		200: true,
		201: true,
		202: true,
		204: true,
		305: false,
		400: false,
		401: false,
		403: false,
		404: false,
		405: false,
		406: false,
		408: false,
		409: false,
		411: false,
		413: false,
		415: false,
		500: false,
		503: false,
	}
	return codes[code]
}

func (c *Client) newRequest(method, uri string, body interface{}) ([]byte, error) {
	var (
		client = &http.Client{}
		url    = fmt.Sprintf("%s/%s", c.getEndpoint(), uri)
		err    error
		req    *http.Request
	)

	if body != nil {
		var bodyJSON []byte
		bodyJSON, err = json.Marshal(body)
		if err != nil {
			return nil, err
		}
		if method == "GET" {
			url = fmt.Sprintf("%s/%s?%s", c.getEndpoint(), uri, bytes.NewBuffer(bodyJSON))
			req, err = http.NewRequest(method, url, nil)
		} else {
			req, err = http.NewRequest(method, url, bytes.NewBuffer(bodyJSON))
		}
		if c.TraceMode {
			b, _ := json.MarshalIndent(body, "", "\t")
			log.Printf("[libsacloud:Client#request] method : %#v , url : %s , \nbody : %s", method, url, b)
		}

	} else {
		req, err = http.NewRequest(method, url, nil)
		if c.TraceMode {
			log.Printf("[libsacloud:Client#request] method : %#v , url : %s ", method, url)
		}
	}

	if err != nil {
		return nil, fmt.Errorf("Error with request: %v - %q", url, err)
	}

	req.SetBasicAuth(c.AccessToken, c.AccessTokenSecret)
	req.Header.Add("X-Sakura-Bigint-As-Int", "1") //Use BigInt on resource ids.
	//if c.TraceMode {
	//	req.Header.Add("X-Sakura-API-Beautify", "1") // format response-JSON
	//}
	req.Header.Add("User-Agent", c.UserAgent)
	req.Method = method

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if c.TraceMode {
		v := &map[string]interface{}{}
		json.Unmarshal(data, v)
		b, _ := json.MarshalIndent(v, "", "\t")
		log.Printf("[libsacloud:Client#response] : %s", b)
	}
	if !c.isOkStatus(resp.StatusCode) {

		errResponse := &sacloud.ResultErrorValue{}
		err := json.Unmarshal(data, errResponse)

		if err != nil {
			return nil, fmt.Errorf("Error in response: %s", string(data))
		}
		return nil, fmt.Errorf("Error in response: %#v", errResponse)

	}
	if err != nil {
		return nil, err
	}

	return data, nil
}
<|endoftext|>


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

// Primitive HTTP client. See RFC 2616.

package http

import (
	"bufio"
	"crypto/tls"
	"encoding/base64"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
)

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Used in Send to implement io.ReadCloser by bundling together the
// bufio.Reader through which we read the response, and the underlying
// network connection.
type readClose struct {
	io.Reader
	io.Closer
}

// Send issues an HTTP request.  Caller should close resp.Body when done reading it.
//
// TODO: support persistent connections (multiple requests on a single connection).
// send() method is nonpublic because, when we refactor the code for persistent
// connections, it may no longer make sense to have a method with this signature.
func send(req *Request) (resp *Response, err os.Error) {
	if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
		return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
	}

	addr := req.URL.Host
	if !hasPort(addr) {
		addr += ":" + req.URL.Scheme
	}
	info := req.URL.Userinfo
	if len(info) > 0 {
		enc := base64.URLEncoding
		encoded := make([]byte, enc.EncodedLen(len(info)))
		enc.Encode(encoded, []byte(info))
		if req.Header == nil {
			req.Header = make(map[string]string)
		}
		req.Header["Authorization"] = "Basic " + string(encoded)
	}

	var conn io.ReadWriteCloser
	if req.URL.Scheme == "http" {
		conn, err = net.Dial("tcp", "", addr)
	} else { // https
		conn, err = tls.Dial("tcp", "", addr)
	}
	if err != nil {
		return nil, err
	}

	err = req.Write(conn)
	if err != nil {
		conn.Close()
		return nil, err
	}

	reader := bufio.NewReader(conn)
	resp, err = ReadResponse(reader, req.Method)
	if err != nil {
		conn.Close()
		return nil, err
	}

	resp.Body = readClose{resp.Body, conn}

	return
}

// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
func shouldRedirect(statusCode int) bool {
	switch statusCode {
	case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
		return true
	}
	return false
}

// Get issues a GET to the specified URL.  If the response is one of the following
// redirect codes, it follows the redirect, up to a maximum of 10 redirects:
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// finalURL is the URL from which the response was fetched -- identical to the
// input URL unless redirects were followed.
//
// Caller should close r.Body when done reading it.
func Get(url string) (r *Response, finalURL string, err os.Error) {
	// TODO: if/when we add cookie support, the redirected request shouldn't
	// necessarily supply the same cookies as the original.
	// TODO: set referrer header on redirects.
	for redirect := 0; ; redirect++ {
		if redirect >= 10 {
			err = os.ErrorString("stopped after 10 redirects")
			break
		}

		var req Request
		if req.URL, err = ParseURL(url); err != nil {
			break
		}
		url = req.URL.String()
		if r, err = send(&req); err != nil {
			break
		}
		if shouldRedirect(r.StatusCode) {
			r.Body.Close()
			if url = r.GetHeader("Location"); url == "" {
				err = os.ErrorString(fmt.Sprintf("%d response missing Location header", r.StatusCode))
				break
			}
			continue
		}
		finalURL = url
		return
	}

	err = &URLError{"Get", url, err}
	return
}

// Post issues a POST to the specified URL.
//
// Caller should close r.Body when done reading it.
func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
	var req Request
	req.Method = "POST"
	req.ProtoMajor = 1
	req.ProtoMinor = 1
	req.Close = true
	req.Body = nopCloser{body}
	req.Header = map[string]string{
		"Content-Type": bodyType,
	}
	req.TransferEncoding = []string{"chunked"}

	req.URL, err = ParseURL(url)
	if err != nil {
		return nil, err
	}

	return send(&req)
}

// Head issues a HEAD to the specified URL.
func Head(url string) (r *Response, err os.Error) {
	var req Request
	req.Method = "HEAD"
	if req.URL, err = ParseURL(url); err != nil {
		return
	}
	url = req.URL.String()
	if r, err = send(&req); err != nil {
		return
	}
	return
}

type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() os.Error { return nil }
<commit_msg>http: add PostForm function to post url-encoded key/value data.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Primitive HTTP client. See RFC 2616.

package http

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"encoding/base64"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
)

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Used in Send to implement io.ReadCloser by bundling together the
// bufio.Reader through which we read the response, and the underlying
// network connection.
type readClose struct {
	io.Reader
	io.Closer
}

// Send issues an HTTP request.  Caller should close resp.Body when done reading it.
//
// TODO: support persistent connections (multiple requests on a single connection).
// send() method is nonpublic because, when we refactor the code for persistent
// connections, it may no longer make sense to have a method with this signature.
func send(req *Request) (resp *Response, err os.Error) {
	if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
		return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
	}

	addr := req.URL.Host
	if !hasPort(addr) {
		addr += ":" + req.URL.Scheme
	}
	info := req.URL.Userinfo
	if len(info) > 0 {
		enc := base64.URLEncoding
		encoded := make([]byte, enc.EncodedLen(len(info)))
		enc.Encode(encoded, []byte(info))
		if req.Header == nil {
			req.Header = make(map[string]string)
		}
		req.Header["Authorization"] = "Basic " + string(encoded)
	}

	var conn io.ReadWriteCloser
	if req.URL.Scheme == "http" {
		conn, err = net.Dial("tcp", "", addr)
	} else { // https
		conn, err = tls.Dial("tcp", "", addr)
	}
	if err != nil {
		return nil, err
	}

	err = req.Write(conn)
	if err != nil {
		conn.Close()
		return nil, err
	}

	reader := bufio.NewReader(conn)
	resp, err = ReadResponse(reader, req.Method)
	if err != nil {
		conn.Close()
		return nil, err
	}

	resp.Body = readClose{resp.Body, conn}

	return
}

// True if the specified HTTP status code is one for which the Get utility should
// automatically redirect.
func shouldRedirect(statusCode int) bool {
	switch statusCode {
	case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect:
		return true
	}
	return false
}

// Get issues a GET to the specified URL.  If the response is one of the following
// redirect codes, it follows the redirect, up to a maximum of 10 redirects:
//
//    301 (Moved Permanently)
//    302 (Found)
//    303 (See Other)
//    307 (Temporary Redirect)
//
// finalURL is the URL from which the response was fetched -- identical to the
// input URL unless redirects were followed.
//
// Caller should close r.Body when done reading it.
func Get(url string) (r *Response, finalURL string, err os.Error) {
	// TODO: if/when we add cookie support, the redirected request shouldn't
	// necessarily supply the same cookies as the original.
	// TODO: set referrer header on redirects.
	for redirect := 0; ; redirect++ {
		if redirect >= 10 {
			err = os.ErrorString("stopped after 10 redirects")
			break
		}

		var req Request
		if req.URL, err = ParseURL(url); err != nil {
			break
		}
		url = req.URL.String()
		if r, err = send(&req); err != nil {
			break
		}
		if shouldRedirect(r.StatusCode) {
			r.Body.Close()
			if url = r.GetHeader("Location"); url == "" {
				err = os.ErrorString(fmt.Sprintf("%d response missing Location header", r.StatusCode))
				break
			}
			continue
		}
		finalURL = url
		return
	}

	err = &URLError{"Get", url, err}
	return
}

// Post issues a POST to the specified URL.
//
// Caller should close r.Body when done reading it.
func Post(url string, bodyType string, body io.Reader) (r *Response, err os.Error) {
	var req Request
	req.Method = "POST"
	req.ProtoMajor = 1
	req.ProtoMinor = 1
	req.Close = true
	req.Body = nopCloser{body}
	req.Header = map[string]string{
		"Content-Type": bodyType,
	}
	req.TransferEncoding = []string{"chunked"}

	req.URL, err = ParseURL(url)
	if err != nil {
		return nil, err
	}

	return send(&req)
}

// PostForm issues a POST to the specified URL,
// with data's keys and values urlencoded as the request body.
//
// Caller should close r.Body when done reading it.
func PostForm(url string, data map[string]string) (r *Response, err os.Error) {
	var req Request
	req.Method = "POST"
	req.ProtoMajor = 1
	req.ProtoMinor = 1
	req.Close = true
	body := urlencode(data)
	req.Body = nopCloser{body}
	req.Header = map[string]string{
		"Content-Type":   "application/x-www-form-urlencoded",
		"Content-Length": strconv.Itoa(body.Len()),
	}
	req.ContentLength = int64(body.Len())

	req.URL, err = ParseURL(url)
	if err != nil {
		return nil, err
	}

	return send(&req)
}

func urlencode(data map[string]string) (b *bytes.Buffer) {
	b = new(bytes.Buffer)
	first := true
	for k, v := range data {
		if first {
			first = false
		} else {
			b.WriteByte('&')
		}
		b.WriteString(URLEscape(k))
		b.WriteByte('=')
		b.WriteString(URLEscape(v))
	}
	return
}

// Head issues a HEAD to the specified URL.
func Head(url string) (r *Response, err os.Error) {
	var req Request
	req.Method = "HEAD"
	if req.URL, err = ParseURL(url); err != nil {
		return
	}
	url = req.URL.String()
	if r, err = send(&req); err != nil {
		return
	}
	return
}

type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() os.Error { return nil }
<|endoftext|>


<commit_before>/*
Copyright 2014 Rohith All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package marathon

// HealthCheck is the definition for an application health check
type HealthCheck struct {
	Command                *Command `json:"command,omitempty"`
	Protocol               string   `json:"protocol,omitempty"`
	Path                   string   `json:"path,omitempty"`
	GracePeriodSeconds     int      `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds        int      `json:"intervalSeconds,omitempty"`
	PortIndex              int      `json:"portIndex,omitempty"`
	MaxConsecutiveFailures int      `json:"maxConsecutiveFailures,omitempty"`
	TimeoutSeconds         int      `json:"timeoutSeconds,omitempty"`
}

// NewDefaultHealthCheck creates a default application health check
func NewDefaultHealthCheck() *HealthCheck {
	return &HealthCheck{
		Protocol:               "HTTP",
		Path:                   "",
		GracePeriodSeconds:     30,
		IntervalSeconds:        10,
		PortIndex:              0,
		MaxConsecutiveFailures: 3,
		TimeoutSeconds:         5,
	}
}

// HealthCheckResult is the health check result
type HealthCheckResult struct {
	Alive               bool   `json:"alive"`
	ConsecutiveFailures int    `json:"consecutiveFailures"`
	FirstSuccess        string `json:"firstSuccess"`
	LastFailure         string `json:"lastFailure"`
	LastSuccess         string `json:"lastSuccess"`
	TaskID              string `json:"taskId"`
}

// Command is the command health check type
type Command struct {
	Value string `json:"value"`
}
<commit_msg>remove omitempty on MaxConsecutiveFailures as 0 ok<commit_after>/*
Copyright 2014 Rohith All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package marathon

// HealthCheck is the definition for an application health check
type HealthCheck struct {
	Command                *Command `json:"command,omitempty"`
	Protocol               string   `json:"protocol,omitempty"`
	Path                   string   `json:"path,omitempty"`
	GracePeriodSeconds     int      `json:"gracePeriodSeconds,omitempty"`
	IntervalSeconds        int      `json:"intervalSeconds,omitempty"`
	PortIndex              int      `json:"portIndex,omitempty"`
	MaxConsecutiveFailures int      `json:"maxConsecutiveFailures"`
	TimeoutSeconds         int      `json:"timeoutSeconds,omitempty"`
}

// NewDefaultHealthCheck creates a default application health check
func NewDefaultHealthCheck() *HealthCheck {
	return &HealthCheck{
		Protocol:               "HTTP",
		Path:                   "",
		GracePeriodSeconds:     30,
		IntervalSeconds:        10,
		PortIndex:              0,
		MaxConsecutiveFailures: 3,
		TimeoutSeconds:         5,
	}
}

// HealthCheckResult is the health check result
type HealthCheckResult struct {
	Alive               bool   `json:"alive"`
	ConsecutiveFailures int    `json:"consecutiveFailures"`
	FirstSuccess        string `json:"firstSuccess"`
	LastFailure         string `json:"lastFailure"`
	LastSuccess         string `json:"lastSuccess"`
	TaskID              string `json:"taskId"`
}

// Command is the command health check type
type Command struct {
	Value string `json:"value"`
}
<|endoftext|>


<commit_before>package yiigo

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"sync"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/youtube/vitess/go/pools"
	"golang.org/x/net/context"
)

type Redis struct{}

var (
	redisPool *pools.ResourcePool
	redisMux  sync.Mutex
)

type ResourceConn struct {
	redis.Conn
}

// 关闭连接资源
func (r ResourceConn) Close() {
	r.Conn.Close()
}

/**
 * 初始化 Redis 连接池
 */
func initRedis() {
	redisMux.Lock()
	defer redisMux.Unlock()

	if redisPool == nil {
		poolMinActive := GetEnvInt("redis", "poolMinActive", 10)
		poolMaxActive := GetEnvInt("redis", "poolMaxActive", 20)
		poolIdleTimeout := GetEnvInt("redis", "poolIdleTimeout", 60000)

		redisPool = pools.NewResourcePool(func() (pools.Resource, error) {
			conn, err := dialRedis()
			return ResourceConn{conn}, err
		}, poolMinActive, poolMaxActive, time.Duration(poolIdleTimeout)*time.Millisecond)
	}
}

/**
 * 连接 Redis
 * @return redis.Conn, error
 */
func dialRedis() (redis.Conn, error) {
	host := GetEnvString("redis", "host", "localhost")
	port := GetEnvInt("redis", "port", 6379)
	connectTimeout := GetEnvInt("redis", "connectTimeout", 10000)
	readTimeout := GetEnvInt("redis", "readTimeout", 10000)
	writeTimeout := GetEnvInt("redis", "writeTimeout", 10000)

	dsn := fmt.Sprintf("%s:%d", host, port)
	conn, err := redis.DialTimeout("tcp", dsn, time.Duration(connectTimeout)*time.Millisecond, time.Duration(readTimeout)*time.Millisecond, time.Duration(writeTimeout)*time.Millisecond)

	if err != nil {
		return nil, err
	}

	return conn, nil
}

/**
 * 获取 Redis 连接资源
 * @return pools.Resource, error
 */
func (r *Redis) getConn() (pools.Resource, error) {
	if redisPool == nil {
		return nil, errors.New("redis pool is empty")
	}

	if redisPool.IsClosed() {
		initRedis()
	}

	ctx := context.TODO()
	rc, err := redisPool.Get(ctx)

	if err != nil {
		return nil, err
	}

	return rc, err
}

/**
 * Do 执行一条redis命令
 * @param cmd string
 * @param args ...interface{}
 * @return interface{}, error
 */
func (r *Redis) Do(cmd string, args ...interface{}) (interface{}, error) {
	rc, err := r.getConn()

	if err != nil {
		return nil, err
	}

	defer redisPool.Put(rc)

	conn := rc.(ResourceConn).Conn

	reply, err := conn.Do(cmd, args...)

	return reply, err
}

/**
 * Pipeline redis管道 执行一组redis命令
 * @param cmds map[string][]interface{}
 * @return interface{}, error
 */
func (r *Redis) Pipeline(cmds map[string][]interface{}) (interface{}, error) {
	rc, err := r.getConn()

	if err != nil {
		return nil, err
	}

	defer redisPool.Put(rc)

	conn := rc.(ResourceConn).Conn

	for k, v := range cmds {
		conn.Send(k, v...)
	}

	reply, err := conn.Do("EXEC")

	return reply, err
}

/**
 * ScanJSONSlice 获取json切片缓存值
 * @param reply interface{}
 * @param dest interface{} (切片指针)
 * @return error
 */
func (r *Redis) ScanJSONSlice(reply interface{}, dest interface{}) error {
	bytes, err := redis.ByteSlices(reply, nil)

	if err != nil {
		return err
	}

	if len(bytes) > 0 {
		rv := reflect.Indirect(reflect.ValueOf(dest))

		if rv.Kind() == reflect.Slice {
			rt := rv.Type().Elem()
			rv.Set(reflect.MakeSlice(rv.Type(), 0, 0))

			for _, v := range bytes {
				if v != nil {
					elem := reflect.New(rt).Elem()
					err := json.Unmarshal(v, elem.Addr().Interface())

					if err != nil {
						return err
					}

					rv.Set(reflect.Append(rv, elem))
				}
			}
		}
	}

	return nil
}

/**
 * ScanJSON 获取json缓存值
 * @param reply interface{}
 * @param dest interface{} (指针)
 * @return error
 */
func (r *Redis) ScanJSON(reply interface{}, dest interface{}) error {
	bytes, err := redis.Bytes(reply, nil)

	if err != nil {
		return err
	}

	err = json.Unmarshal(bytes, dest)

	if err != nil {
		return err
	}

	return nil
}
<commit_msg>update<commit_after>package yiigo

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"sync"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/youtube/vitess/go/pools"
	"golang.org/x/net/context"
)

type Redis struct{}

var (
	redisPool *pools.ResourcePool
	redisMux  sync.Mutex
)

type ResourceConn struct {
	redis.Conn
}

// 关闭连接资源
func (r ResourceConn) Close() {
	r.Conn.Close()
}

/**
 * 初始化 Redis 连接池
 */
func initRedis() {
	redisMux.Lock()
	defer redisMux.Unlock()

	if redisPool == nil {
		poolMinActive := GetEnvInt("redis", "poolMinActive", 10)
		poolMaxActive := GetEnvInt("redis", "poolMaxActive", 20)
		poolIdleTimeout := GetEnvInt("redis", "poolIdleTimeout", 60000)

		redisPool = pools.NewResourcePool(func() (pools.Resource, error) {
			conn, err := dialRedis()
			return ResourceConn{conn}, err
		}, poolMinActive, poolMaxActive, time.Duration(poolIdleTimeout)*time.Millisecond)
	}
}

/**
 * 连接 Redis
 * @return redis.Conn, error
 */
func dialRedis() (redis.Conn, error) {
	host := GetEnvString("redis", "host", "localhost")
	port := GetEnvInt("redis", "port", 6379)
	connectTimeout := GetEnvInt("redis", "connectTimeout", 10000)
	readTimeout := GetEnvInt("redis", "readTimeout", 10000)
	writeTimeout := GetEnvInt("redis", "writeTimeout", 10000)

	dsn := fmt.Sprintf("%s:%d", host, port)
	conn, err := redis.DialTimeout("tcp", dsn, time.Duration(connectTimeout)*time.Millisecond, time.Duration(readTimeout)*time.Millisecond, time.Duration(writeTimeout)*time.Millisecond)

	if err != nil {
		return nil, err
	}

	return conn, nil
}

/**
 * 获取 Redis 连接资源
 * @return pools.Resource, error
 */
func (r *Redis) getConn() (pools.Resource, error) {
	if redisPool == nil {
		return nil, errors.New("redis pool is empty")
	}

	if redisPool.IsClosed() {
		initRedis()
	}

	ctx := context.TODO()
	rc, err := redisPool.Get(ctx)

	if err != nil {
		return nil, err
	}

	return rc, err
}

/**
 * Do 执行一条redis命令
 * @param cmd string
 * @param args ...interface{}
 * @return interface{}, error
 */
func (r *Redis) Do(cmd string, args ...interface{}) (interface{}, error) {
	rc, err := r.getConn()

	if err != nil {
		return nil, err
	}

	defer redisPool.Put(rc)

	conn := rc.(ResourceConn).Conn

	reply, err := conn.Do(cmd, args...)

	return reply, err
}

/**
 * Pipeline redis管道，执行一组redis命令
 * @param cmds map[string][]interface{}
 * @return interface{}, error
 */
func (r *Redis) Pipeline(cmds map[string][]interface{}) (interface{}, error) {
	rc, err := r.getConn()

	if err != nil {
		return nil, err
	}

	defer redisPool.Put(rc)

	conn := rc.(ResourceConn).Conn

	for k, v := range cmds {
		conn.Send(k, v...)
	}

	reply, err := conn.Do("EXEC")

	return reply, err
}

/**
 * ScanJSONSlice 获取json切片缓存值
 * @param reply interface{}
 * @param dest interface{} (切片指针)
 * @return error
 */
func (r *Redis) ScanJSONSlice(reply interface{}, dest interface{}) error {
	bytes, err := redis.ByteSlices(reply, nil)

	if err != nil {
		return err
	}

	if len(bytes) > 0 {
		rv := reflect.Indirect(reflect.ValueOf(dest))

		if rv.Kind() == reflect.Slice {
			rt := rv.Type().Elem()
			rv.Set(reflect.MakeSlice(rv.Type(), 0, 0))

			for _, v := range bytes {
				if v != nil {
					elem := reflect.New(rt).Elem()
					err := json.Unmarshal(v, elem.Addr().Interface())

					if err != nil {
						return err
					}

					rv.Set(reflect.Append(rv, elem))
				}
			}
		}
	}

	return nil
}

/**
 * ScanJSON 获取json缓存值
 * @param reply interface{}
 * @param dest interface{} (指针)
 * @return error
 */
func (r *Redis) ScanJSON(reply interface{}, dest interface{}) error {
	bytes, err := redis.Bytes(reply, nil)

	if err != nil {
		return err
	}

	err = json.Unmarshal(bytes, dest)

	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package streams

var ServerStaticHtml = `
<!DOCTYPE html>

    <title>Series - Streams</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">

Streams

                Load
                Refresh
                Mark as watched

`
<commit_msg>support global actions<commit_after>package streams

var ServerStaticHtml = `
<!DOCTYPE html>

    <title>Series - Streams</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">

Streams

                Load
                Refresh
                Mark as watched

`
<|endoftext|>


<commit_before>package yum

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sort"

	gocfg "github.com/gonuts/config"
	"github.com/gonuts/logger"
)

type Client struct {
	msg         *logger.Logger
	siteroot    string
	etcdir      string
	lbyumcache  string
	yumconf     string
	yumreposdir string
	configured  bool
	repos       map[string]*Repository
	repourls    map[string]string
}

// newClient returns a Client from siteroot and backends.
// manualConfig is just for internal tests
func newClient(siteroot string, backends []string, checkForUpdates, manualConfig bool) (*Client, error) {
	client := &Client{
		msg:         logger.NewLogger("yum", logger.INFO, os.Stdout),
		siteroot:    siteroot,
		etcdir:      filepath.Join(siteroot, "etc"),
		lbyumcache:  filepath.Join(siteroot, "var", "cache", "lbyum"),
		yumconf:     filepath.Join(siteroot, "etc", "yum.conf"),
		yumreposdir: filepath.Join(siteroot, "etc", "yum.repos.d"),
		configured:  false,
		repos:       make(map[string]*Repository),
		repourls:    make(map[string]string),
	}

	if manualConfig {
		return client, nil
	}

	// load the config and set the URLs accordingly
	urls, err := client.loadConfig()
	if err != nil {
		return nil, err
	}

	// At this point we have the repo names and URLs in self.repourls
	// we know connect to them to get the best method to get the appropriate files
	err = client.initRepositories(urls, checkForUpdates, backends)
	if err != nil {
		return nil, err
	}

	return client, err
}

// New returns a new YUM Client, rooted at siteroot.
func New(siteroot string) (*Client, error) {
	checkForUpdates := true
	manualConfig := false
	backends := []string{
		"RepositorySQLiteBackend",
		"RepositoryXMLBackend",
	}
	return newClient(siteroot, backends, checkForUpdates, manualConfig)
}

// Close cleans up after use
func (yum *Client) Close() error {
	var err error
	for name, repo := range yum.repos {
		e := repo.Close()
		if e != nil {
			yum.msg.Errorf("error closing repo [%s]: %v\n", name, e)
			e = err
		} else {
			yum.msg.Debugf("closed repo [%s]\n", name)
		}
	}
	return err
}

// SetLevel sets the verbosity level of Client
func (yum *Client) SetLevel(lvl logger.Level) {
	yum.msg.SetLevel(lvl)
	for _, repo := range yum.repos {
		repo.msg.SetLevel(lvl)
	}
}

// FindLatestMatchingName locates a package by name and returns the latest available version
func (yum *Client) FindLatestMatchingName(name, version, release string) (*Package, error) {
	var err error
	var pkg *Package
	found := make(Packages, 0)
	errors := make([]error, 0, len(yum.repos))

	for _, repo := range yum.repos {
		p, err := repo.FindLatestMatchingName(name, version, release)
		if err != nil {
			errors = append(errors, err)
			continue
		}
		found = append(found, p)
	}

	if len(found) > 0 {
		sort.Sort(found)
		pkg = found[len(found)-1]
		return pkg, err
	}

	if len(errors) == len(yum.repos) && len(errors) > 0 {
		return nil, errors[0]
	}

	return pkg, err
}

// FindLatestMatchingRequire locates a package providing a given functionality.
func (yum *Client) FindLatestMatchingRequire(requirement *Requires) (*Package, error) {
	var err error
	var pkg *Package
	found := make(Packages, 0)
	errors := make([]error, 0, len(yum.repos))

	for _, repo := range yum.repos {
		p, err := repo.FindLatestMatchingRequire(requirement)
		if err != nil {
			errors = append(errors, err)
			yum.msg.Debugf("no match for req=%s.%s-%s (repo=%s)\n",
				requirement.Name(), requirement.Version(), requirement.Release(),
				repo.RepoUrl,
			)
			continue
		}
		found = append(found, p)
	}

	if len(found) > 0 {
		sort.Sort(found)
		pkg = found[len(found)-1]
		return pkg, err
	}

	if len(errors) == len(yum.repos) && len(errors) > 0 {
		return nil, errors[0]
	}

	return pkg, err
}

// ListPackages lists all packages satisfying pattern (a regexp)
func (yum *Client) ListPackages(name, version, release string) ([]*Package, error) {
	var err error
	re_name := regexp.MustCompile(name)
	re_vers := regexp.MustCompile(version)
	re_rel := regexp.MustCompile(release)
	pkgs := make([]*Package, 0)
	for _, repo := range yum.repos {
		for _, pkg := range repo.GetPackages() {
			if re_name.MatchString(pkg.Name()) &&
				re_vers.MatchString(pkg.Version()) &&
				// FIXME: sprintf is ugly
				re_rel.MatchString(fmt.Sprintf("%d", pkg.Release())) {
				pkgs = append(pkgs, pkg)
			}
		}
	}
	return pkgs, err
}

// RequiredPackages returns the list of all required packages for pkg (including pkg itself)
func (yum *Client) RequiredPackages(pkg *Package) ([]*Package, error) {
	pkgs, err := yum.PackageDeps(pkg)
	if err != nil {
		return nil, err
	}
	pkgs = append(pkgs, pkg)
	return pkgs, err
}

// PackageDeps returns all dependencies for the package (excluding the package itself)
func (yum *Client) PackageDeps(pkg *Package) ([]*Package, error) {
	var err error
	processed := make(map[*Package]struct{})
	deps, err := yum.pkgDeps(pkg, processed)
	if err != nil {
		return nil, err
	}

	set := make(map[string]struct{})
	pkgs := make([]*Package, 0, len(deps))
	for p := range deps {
		if _, dup := set[p.RpmName()]; !dup {
			set[p.RpmName()] = struct{}{}
			pkgs = append(pkgs, p)
		}
	}
	return pkgs, err
}

// pkgDeps returns all dependencies for the package (excluding the package itself)
func (yum *Client) pkgDeps(pkg *Package, processed map[*Package]struct{}) (map[*Package]struct{}, error) {
	var err error
	var lasterr error
	msg := yum.msg

	processed[pkg] = struct{}{}
	required := make(map[*Package]struct{})

	nreqs := len(pkg.Requires())
	msg.Verbosef(">>> pkg %s.%s-%s (req=%d)\n", pkg.Name(), pkg.Version(), pkg.Release(), nreqs)
	for ireq, req := range pkg.Requires() {
		msg.Verbosef("[%03d/%03d] processing deps for %s.%s-%s\n", ireq, nreqs, req.Name(), req.Version(), req.Release())
		if str_in_slice(req.Name(), g_IGNORED_PACKAGES) {
			msg.Verbosef("[%03d/%03d] processing deps for %s.%s-%s [IGNORE]\n", ireq, nreqs, req.Name(), req.Version(), req.Release())
			continue
		}
		p, err := yum.FindLatestMatchingRequire(req)
		if err != nil {
			lasterr = err
			msg.Debugf("could not find match for %s.%s-%s\n", req.Name(), req.Version(), req.Release())
			continue
		}
		if _, dup := processed[p]; dup {
			msg.Warnf("cyclic dependency in repository with package: %s.%s-%s\n", p.Name(), p.Version(), p.Release())
			continue
		}
		if p == nil {
			msg.Errorf("package %s.%s-%s not found!\n", req.Name(), req.Version(), req.Release())
			lasterr = fmt.Errorf("package %s.%s-%s not found", req.Name(), req.Version(), req.Release())
			continue
			//return nil, fmt.Errorf("package %s.%s-%s not found", req.Name(), req.Version(), req.Release())
		}
		msg.Verbosef("--> adding dep %s.%s-%s\n", p.Name(), p.Version(), p.Release())
		required[p] = struct{}{}
		sdeps, err := yum.pkgDeps(p, processed)
		if err != nil {
			lasterr = err
			continue
			//return nil, err
		}
		for sdep := range sdeps {
			required[sdep] = struct{}{}
		}
	}

	if lasterr != nil {
		err = lasterr
	}
	return required, err
}

// loadConfig looks up the location of the yum repository
func (yum *Client) loadConfig() (map[string]string, error) {
	fis, err := ioutil.ReadDir(yum.yumreposdir)
	if err != nil {
		return nil, err
	}
	pattern := regexp.MustCompile(`(.*)\.repo$`)
	for _, fi := range fis {
		if fi.IsDir() {
			continue
		}
		if !pattern.MatchString(fi.Name()) {
			continue
		}
		fname := filepath.Join(yum.yumreposdir, fi.Name())
		repos, err := yum.parseRepoConfigFile(fname)
		if err != nil {
			return nil, err
		}
		for k, v := range repos {
			yum.repourls[k] = v
		}
	}

	yum.configured = true
	if len(yum.repourls) <= 0 {
		return nil, fmt.Errorf("could not find repository config file in [%s]", yum.yumreposdir)
	}
	return yum.repourls, err
}

// parseRepoConfigFile parses the xyz.repo file and returns a map of reponame/repourl
func (yum *Client) parseRepoConfigFile(fname string) (map[string]string, error) {
	var err error
	repos := make(map[string]string)

	cfg, err := gocfg.ReadDefault(fname)
	if err != nil {
		return nil, err
	}

	for _, section := range cfg.Sections() {
		if !cfg.HasOption(section, "baseurl") {
			continue
		}
		repourl, err := cfg.String(section, "baseurl")
		if err != nil {
			return nil, err
		}
		yum.msg.Debugf("adding repo=%q url=%q from file [%s]\n", section, repourl, fname)
		repos[section] = repourl
	}
	return repos, err
}

func (yum *Client) initRepositories(urls map[string]string, checkForUpdates bool, backends []string) error {
	var err error

	const setupBackend = true

	// setup the repositories
	for repo, repourl := range urls {
		cachedir := filepath.Join(yum.lbyumcache, repo)
		err = os.MkdirAll(cachedir, 0755)
		if err != nil {
			return err
		}
		r, err := NewRepository(
			repo, repourl, cachedir,
			backends, setupBackend, checkForUpdates,
		)
		if err != nil {
			return err
		}
		r.msg = yum.msg
		yum.repos[repo] = r
	}

	yum.repourls = urls
	return err
}

// EOF
<commit_msg>yum: use RpmName for set-uniqueness instead of *Package<commit_after>package yum

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sort"

	gocfg "github.com/gonuts/config"
	"github.com/gonuts/logger"
)

type Client struct {
	msg         *logger.Logger
	siteroot    string
	etcdir      string
	lbyumcache  string
	yumconf     string
	yumreposdir string
	configured  bool
	repos       map[string]*Repository
	repourls    map[string]string
}

// newClient returns a Client from siteroot and backends.
// manualConfig is just for internal tests
func newClient(siteroot string, backends []string, checkForUpdates, manualConfig bool) (*Client, error) {
	client := &Client{
		msg:         logger.NewLogger("yum", logger.INFO, os.Stdout),
		siteroot:    siteroot,
		etcdir:      filepath.Join(siteroot, "etc"),
		lbyumcache:  filepath.Join(siteroot, "var", "cache", "lbyum"),
		yumconf:     filepath.Join(siteroot, "etc", "yum.conf"),
		yumreposdir: filepath.Join(siteroot, "etc", "yum.repos.d"),
		configured:  false,
		repos:       make(map[string]*Repository),
		repourls:    make(map[string]string),
	}

	if manualConfig {
		return client, nil
	}

	// load the config and set the URLs accordingly
	urls, err := client.loadConfig()
	if err != nil {
		return nil, err
	}

	// At this point we have the repo names and URLs in self.repourls
	// we know connect to them to get the best method to get the appropriate files
	err = client.initRepositories(urls, checkForUpdates, backends)
	if err != nil {
		return nil, err
	}

	return client, err
}

// New returns a new YUM Client, rooted at siteroot.
func New(siteroot string) (*Client, error) {
	checkForUpdates := true
	manualConfig := false
	backends := []string{
		"RepositorySQLiteBackend",
		"RepositoryXMLBackend",
	}
	return newClient(siteroot, backends, checkForUpdates, manualConfig)
}

// Close cleans up after use
func (yum *Client) Close() error {
	var err error
	for name, repo := range yum.repos {
		e := repo.Close()
		if e != nil {
			yum.msg.Errorf("error closing repo [%s]: %v\n", name, e)
			e = err
		} else {
			yum.msg.Debugf("closed repo [%s]\n", name)
		}
	}
	return err
}

// SetLevel sets the verbosity level of Client
func (yum *Client) SetLevel(lvl logger.Level) {
	yum.msg.SetLevel(lvl)
	for _, repo := range yum.repos {
		repo.msg.SetLevel(lvl)
	}
}

// FindLatestMatchingName locates a package by name and returns the latest available version
func (yum *Client) FindLatestMatchingName(name, version, release string) (*Package, error) {
	var err error
	var pkg *Package
	found := make(Packages, 0)
	errors := make([]error, 0, len(yum.repos))

	for _, repo := range yum.repos {
		p, err := repo.FindLatestMatchingName(name, version, release)
		if err != nil {
			errors = append(errors, err)
			continue
		}
		found = append(found, p)
	}

	if len(found) > 0 {
		sort.Sort(found)
		pkg = found[len(found)-1]
		return pkg, err
	}

	if len(errors) == len(yum.repos) && len(errors) > 0 {
		return nil, errors[0]
	}

	return pkg, err
}

// FindLatestMatchingRequire locates a package providing a given functionality.
func (yum *Client) FindLatestMatchingRequire(requirement *Requires) (*Package, error) {
	var err error
	var pkg *Package
	found := make(Packages, 0)
	errors := make([]error, 0, len(yum.repos))

	for _, repo := range yum.repos {
		p, err := repo.FindLatestMatchingRequire(requirement)
		if err != nil {
			errors = append(errors, err)
			yum.msg.Debugf("no match for req=%s.%s-%s (repo=%s)\n",
				requirement.Name(), requirement.Version(), requirement.Release(),
				repo.RepoUrl,
			)
			continue
		}
		found = append(found, p)
	}

	if len(found) > 0 {
		sort.Sort(found)
		pkg = found[len(found)-1]
		return pkg, err
	}

	if len(errors) == len(yum.repos) && len(errors) > 0 {
		return nil, errors[0]
	}

	return pkg, err
}

// ListPackages lists all packages satisfying pattern (a regexp)
func (yum *Client) ListPackages(name, version, release string) ([]*Package, error) {
	var err error
	re_name := regexp.MustCompile(name)
	re_vers := regexp.MustCompile(version)
	re_rel := regexp.MustCompile(release)
	pkgs := make([]*Package, 0)
	for _, repo := range yum.repos {
		for _, pkg := range repo.GetPackages() {
			if re_name.MatchString(pkg.Name()) &&
				re_vers.MatchString(pkg.Version()) &&
				// FIXME: sprintf is ugly
				re_rel.MatchString(fmt.Sprintf("%d", pkg.Release())) {
				pkgs = append(pkgs, pkg)
			}
		}
	}
	return pkgs, err
}

// RequiredPackages returns the list of all required packages for pkg (including pkg itself)
func (yum *Client) RequiredPackages(pkg *Package) ([]*Package, error) {
	pkgs, err := yum.PackageDeps(pkg)
	if err != nil {
		return nil, err
	}
	pkgs = append(pkgs, pkg)
	return pkgs, err
}

// PackageDeps returns all dependencies for the package (excluding the package itself)
func (yum *Client) PackageDeps(pkg *Package) ([]*Package, error) {
	var err error
	processed := make(map[string]*Package)
	deps, err := yum.pkgDeps(pkg, processed)
	if err != nil {
		return nil, err
	}

	pkgs := make([]*Package, 0, len(deps))
	for _, p := range deps {
		pkgs = append(pkgs, p)
	}
	return pkgs, err
}

// pkgDeps returns all dependencies for the package (excluding the package itself)
func (yum *Client) pkgDeps(pkg *Package, processed map[string]*Package) (map[string]*Package, error) {
	var err error
	var lasterr error
	msg := yum.msg

	processed[pkg.RpmName()] = pkg
	required := make(map[string]*Package)

	nreqs := len(pkg.Requires())
	msg.Verbosef(">>> pkg %s.%s-%s (req=%d)\n", pkg.Name(), pkg.Version(), pkg.Release(), nreqs)
	for ireq, req := range pkg.Requires() {
		msg.Verbosef("[%03d/%03d] processing deps for %s.%s-%s\n", ireq, nreqs, req.Name(), req.Version(), req.Release())
		if str_in_slice(req.Name(), g_IGNORED_PACKAGES) {
			msg.Verbosef("[%03d/%03d] processing deps for %s.%s-%s [IGNORE]\n", ireq, nreqs, req.Name(), req.Version(), req.Release())
			continue
		}
		p, err := yum.FindLatestMatchingRequire(req)
		if err != nil {
			lasterr = err
			msg.Debugf("could not find match for %s.%s-%s\n", req.Name(), req.Version(), req.Release())
			continue
		}
		if _, dup := processed[p.RpmName()]; dup {
			msg.Warnf("cyclic dependency in repository with package: %s.%s-%s\n", p.Name(), p.Version(), p.Release())
			continue
		}
		if p == nil {
			msg.Errorf("package %s.%s-%s not found!\n", req.Name(), req.Version(), req.Release())
			lasterr = fmt.Errorf("package %s.%s-%s not found", req.Name(), req.Version(), req.Release())
			continue
			//return nil, fmt.Errorf("package %s.%s-%s not found", req.Name(), req.Version(), req.Release())
		}
		msg.Verbosef("--> adding dep %s.%s-%s\n", p.Name(), p.Version(), p.Release())
		required[p.RpmName()] = p
		sdeps, err := yum.pkgDeps(p, processed)
		if err != nil {
			lasterr = err
			continue
			//return nil, err
		}
		for _, sdep := range sdeps {
			required[sdep.RpmName()] = sdep
		}
	}

	if lasterr != nil {
		err = lasterr
	}
	return required, err
}

// loadConfig looks up the location of the yum repository
func (yum *Client) loadConfig() (map[string]string, error) {
	fis, err := ioutil.ReadDir(yum.yumreposdir)
	if err != nil {
		return nil, err
	}
	pattern := regexp.MustCompile(`(.*)\.repo$`)
	for _, fi := range fis {
		if fi.IsDir() {
			continue
		}
		if !pattern.MatchString(fi.Name()) {
			continue
		}
		fname := filepath.Join(yum.yumreposdir, fi.Name())
		repos, err := yum.parseRepoConfigFile(fname)
		if err != nil {
			return nil, err
		}
		for k, v := range repos {
			yum.repourls[k] = v
		}
	}

	yum.configured = true
	if len(yum.repourls) <= 0 {
		return nil, fmt.Errorf("could not find repository config file in [%s]", yum.yumreposdir)
	}
	return yum.repourls, err
}

// parseRepoConfigFile parses the xyz.repo file and returns a map of reponame/repourl
func (yum *Client) parseRepoConfigFile(fname string) (map[string]string, error) {
	var err error
	repos := make(map[string]string)

	cfg, err := gocfg.ReadDefault(fname)
	if err != nil {
		return nil, err
	}

	for _, section := range cfg.Sections() {
		if !cfg.HasOption(section, "baseurl") {
			continue
		}
		repourl, err := cfg.String(section, "baseurl")
		if err != nil {
			return nil, err
		}
		yum.msg.Debugf("adding repo=%q url=%q from file [%s]\n", section, repourl, fname)
		repos[section] = repourl
	}
	return repos, err
}

func (yum *Client) initRepositories(urls map[string]string, checkForUpdates bool, backends []string) error {
	var err error

	const setupBackend = true

	// setup the repositories
	for repo, repourl := range urls {
		cachedir := filepath.Join(yum.lbyumcache, repo)
		err = os.MkdirAll(cachedir, 0755)
		if err != nil {
			return err
		}
		r, err := NewRepository(
			repo, repourl, cachedir,
			backends, setupBackend, checkForUpdates,
		)
		if err != nil {
			return err
		}
		r.msg = yum.msg
		yum.repos[repo] = r
	}

	yum.repourls = urls
	return err
}

// EOF
<|endoftext|>