<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"golang.org/x/crypto/ssh/terminal"
	"os"

	log "github.com/Sirupsen/logrus"
)

var cmdLogin = &Command{
	Exec:        runLogin,
	UsageLine:   "login [options]",
	Description: "Login generates a configuration file containing credentials",
	Help: `Login generates a configuration file in '/home/$USER/.scwrc'
containing credentials used to interact with the Scaleway API. This
configuration file is automatically used by the 'scw' command.`,
}

func promptUser(prompt string, output *string, echo bool) {
	fmt.Fprintf(os.Stdout, prompt)
	os.Stdout.Sync()

	if !echo {
		b, err := terminal.ReadPassword(int(os.Stdin.Fd()))
		if err != nil {
			log.Fatalf("Unable to prompt for password: %s", err)
		}
		*output = string(b)
		fmt.Fprintf(os.Stdout, "\n")
	} else {
		reader := bufio.NewReader(os.Stdin)
		*output, _ = reader.ReadString('\n')
	}
}

func init() {
	cmdLogin.Flag.StringVar(&organization, []string{"o", "-organization"}, "", "Organization")
	cmdLogin.Flag.StringVar(&token, []string{"t", "-token"}, "", "Token")
}

// FLags
var organization string // -o flag
var token string        // -t flag

func runLogin(cmd *Command, args []string) {
	if len(organization) == 0 && len(token) == 0 {
		promptUser("Organization: ", &organization, true)
		promptUser("Token: ", &token, false)
	}

	cfg := &Config{
		APIEndPoint:  "https://account.scaleway.com/",
		Organization: organization,
		Token:        token,
	}

	api, err := NewScalewayAPI(cfg.APIEndPoint, cfg.Organization, cfg.Token)
	if err != nil {
		log.Fatalf("Unable to create ScalewayAPI: %s", err)
	}
	err = api.CheckCredentials()
	if err != nil {
		log.Fatalf("Unable to contact ScalewayAPI: %s", err)
	}

	scwrc_path, err := GetConfigFilePath()
	if err != nil {
		log.Fatalf("Unable to get scwrc config file path: %s", err)
	}
	scwrc, err := os.OpenFile(scwrc_path, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
	if err != nil {
		log.Fatalf("Unable to create scwrc config file: %s", err)
	}
	defer scwrc.Close()
	encoder := json.NewEncoder(scwrc)
	err = encoder.Encode(cfg)
	if err != nil {
		log.Fatalf("Unable to encode scw config file: %s", err)
	}
}
<commit_msg>Fixed 'scw login'<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"golang.org/x/crypto/ssh/terminal"

	log "github.com/Sirupsen/logrus"
)

var cmdLogin = &Command{
	Exec:        runLogin,
	UsageLine:   "login [options]",
	Description: "Login generates a configuration file containing credentials",
	Help: `Login generates a configuration file in '/home/$USER/.scwrc'
containing credentials used to interact with the Scaleway API. This
configuration file is automatically used by the 'scw' command.`,
}

func promptUser(prompt string, output *string, echo bool) {
	fmt.Fprintf(os.Stdout, prompt)
	os.Stdout.Sync()

	if !echo {
		b, err := terminal.ReadPassword(int(os.Stdin.Fd()))
		if err != nil {
			log.Fatalf("Unable to prompt for password: %s", err)
		}
		*output = string(b)
		fmt.Fprintf(os.Stdout, "\n")
	} else {
		reader := bufio.NewReader(os.Stdin)
		*output, _ = reader.ReadString('\n')
	}
}

func init() {
	cmdLogin.Flag.StringVar(&organization, []string{"o", "-organization"}, "", "Organization")
	cmdLogin.Flag.StringVar(&token, []string{"t", "-token"}, "", "Token")
}

// FLags
var organization string // -o flag
var token string        // -t flag

func runLogin(cmd *Command, args []string) {
	if len(organization) == 0 && len(token) == 0 {
		promptUser("Organization: ", &organization, true)
		promptUser("Token: ", &token, false)
	}

	cfg := &Config{
		APIEndPoint:  "https://api.scaleway.com/",
		Organization: strings.Trim(organization, "\n"),
		Token:        strings.Trim(token, "\n"),
	}

	api, err := NewScalewayAPI(cfg.APIEndPoint, cfg.Organization, cfg.Token)
	if err != nil {
		log.Fatalf("Unable to create ScalewayAPI: %s", err)
	}
	err = api.CheckCredentials()
	if err != nil {
		log.Fatalf("Unable to contact ScalewayAPI: %s", err)
	}

	scwrc_path, err := GetConfigFilePath()
	if err != nil {
		log.Fatalf("Unable to get scwrc config file path: %s", err)
	}
	scwrc, err := os.OpenFile(scwrc_path, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
	if err != nil {
		log.Fatalf("Unable to create scwrc config file: %s", err)
	}
	defer scwrc.Close()
	encoder := json.NewEncoder(scwrc)
	err = encoder.Encode(cfg)
	if err != nil {
		log.Fatalf("Unable to encode scw config file: %s", err)
	}
}
<|endoftext|>


<commit_before>// mango - generate man pages from the source of your Go commands
//
// Description:
//
// TODO...
//
package main

import (
	"./markup"
	"./source"
	"flag"
	"fmt"
	"os"
	"path"
)

var (
	optPath = ""
	optName = ""
)

func init() {
	// Save manual pages to specified directory.
	flag.StringVar(&optPath, "dir", "", "output directory")

	// Define command name via flag instead of using filename.
	flag.StringVar(&optName, "name", "", "command name")
}

type Builder struct {
	File      *source.File
	Tokenizer *markup.Tokenizer
	Parser    *markup.Parser
	Renderer  markup.Renderer
	Writer    markup.Writer
}

func NewBuilder() *Builder {
	result := new(Builder)
	result.Tokenizer = markup.NewTokenizer()
	result.Parser = markup.NewParser()
	result.Writer = markup.NewTroffWriter()
	result.Renderer = markup.NewTroffRenderer(result.Writer)
	return result
}

func (b *Builder) Load(path string) error {
	file, err := source.NewFile(path)
	if err != nil {
		return err
	}
	b.File = file

	if len(optName) > 0 {
		b.File.Name = optName
	}
	b.Writer.WriteTitle(b.File.Name)
	b.Writer.WriteDate(b.File.Time)
	b.feedDocumentation()
	b.feedSynopsis()
	b.feedOptions()
	return nil
}

func (b *Builder) feedDocumentation() {
	tokens, err := b.Tokenizer.TokenizeString(b.File.Doc)
	if err != nil {
		return
	}

	b.Renderer.Section("Name")
	markup.Render(b.Renderer, b.Parser.Parse(tokens))
}

func (b *Builder) feedSynopsis() {
	b.Renderer.Section("Synopsis")
	b.Renderer.Text(b.File.Name)
	if len(b.File.Options) > 0 {
		b.Renderer.TextUnderline("[option]")
		b.Renderer.Text("... ")
	}
	b.Renderer.TextUnderline("[args]")
	b.Renderer.Text("... ")
	b.Renderer.Break()
}

func (b *Builder) feedOptions() {
	if len(b.File.Options) == 0 {
		return
	}

	b.Renderer.Section("Options")
	for _, opt := range b.File.Options {
		textHead := ""
		textBody := ""

		if len(opt.Short) > 0 {
			textHead = fmt.Sprintf("-%s, -%s", opt.Short, opt.Name)
		} else {
			textHead = fmt.Sprintf("-%s", opt.Name)
		}

		if len(opt.Doc) > 0 {
			textBody = opt.Doc
		} else {
			textBody = opt.Usage
		}

		// Tokenize body text. We haven't written anything yet, so if Tokenize
		// function fails, the document stays unchanged and we try to parse the
		// next option.
		tokens, err := b.Tokenizer.TokenizeString(textBody)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Warning:")
			continue
		}

		b.Renderer.TextBold(textHead)
		if len(tokens) > 0 {
			markup.Render(b.Renderer, b.Parser.ParsePart(tokens))
		}
		b.Renderer.Break()
	}
}

func (b *Builder) Save(path string) error {
	file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer file.Close()

	return markup.Save(b.Writer, file)
}

func usage() {
	fmt.Println("Usage:")
	fmt.Println("  mango [option]... [go-file]...\n")
	fmt.Println("Options:")

	flag.CommandLine.VisitAll(func(fl *flag.Flag) {
		name := fl.Name
		help := fl.Usage
		if _, ok := fl.Value.(interface {
			IsBoolFlag()
		}); !ok {
			name = fmt.Sprintf("%s=<value>", name)
		}
		fmt.Printf("  -%-18s\n\t%s\n", name, help)
	})
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
		return
	}

	for _, srcPath := range flag.Args() {
		builder := NewBuilder()

		if err := builder.Load(srcPath); err != nil {
			fmt.Fprintf(os.Stderr, "Warning: Could not open file '%s': %s\n", srcPath, err)
			continue
		}

		dstPath := fmt.Sprintf("%s.1", builder.File.Name)
		if len(optPath) > 0 {
			dstPath = path.Join(optPath, dstPath)
		}

		if err := builder.Save(dstPath); err != nil {
			fmt.Fprintf(os.Stderr, "Warning: Could not save '%s': %s\n", dstPath, err)
			continue
		}
		fmt.Printf("%s -> %s\n", srcPath, dstPath)
	}
}
<commit_msg>add flag type to options in manual pages<commit_after>// mango - generate man pages from the source of your Go commands
//
// Description:
//
// TODO...
//
package main

import (
	"./markup"
	"./source"
	"flag"
	"fmt"
	"os"
	"path"
	"strings"
)

var (
	optPath = ""
	optName = ""
)

func init() {
	// Save manual pages to specified directory.
	flag.StringVar(&optPath, "dir", "", "output directory")

	// Define command name via flag instead of using filename.
	flag.StringVar(&optName, "name", "", "command name")
}

type Builder struct {
	File      *source.File
	Tokenizer *markup.Tokenizer
	Parser    *markup.Parser
	Renderer  markup.Renderer
	Writer    markup.Writer
}

func NewBuilder() *Builder {
	result := new(Builder)
	result.Tokenizer = markup.NewTokenizer()
	result.Parser = markup.NewParser()
	result.Writer = markup.NewTroffWriter()
	result.Renderer = markup.NewTroffRenderer(result.Writer)
	return result
}

func (b *Builder) Load(path string) error {
	file, err := source.NewFile(path)
	if err != nil {
		return err
	}
	b.File = file

	if len(optName) > 0 {
		b.File.Name = optName
	}
	b.Writer.WriteTitle(b.File.Name)
	b.Writer.WriteDate(b.File.Time)
	b.feedDocumentation()
	b.feedSynopsis()
	b.feedOptions()
	return nil
}

func (b *Builder) feedDocumentation() {
	tokens, err := b.Tokenizer.TokenizeString(b.File.Doc)
	if err != nil {
		return
	}

	b.Renderer.Section("Name")
	markup.Render(b.Renderer, b.Parser.Parse(tokens))
}

func (b *Builder) feedSynopsis() {
	b.Renderer.Section("Synopsis")
	b.Renderer.Text(b.File.Name)
	if len(b.File.Options) > 0 {
		b.Renderer.TextUnderline("[option]")
		b.Renderer.Text("... ")
	}
	b.Renderer.TextUnderline("[args]")
	b.Renderer.Text("... ")
	b.Renderer.Break()
}

func (b *Builder) feedOptions() {
	if len(b.File.Options) == 0 {
		return
	}

	b.Renderer.Section("Options")
	for _, opt := range b.File.Options {
		textHead := ""
		textType := ""
		textBody := ""

		if len(opt.Short) > 0 {
			textHead = fmt.Sprintf("-%s, -%s", opt.Short, opt.Name)
		} else {
			textHead = fmt.Sprintf("-%s", opt.Name)
		}

		if len(opt.Doc) > 0 {
			textBody = opt.Doc
		} else {
			textBody = opt.Usage
		}

		if opt.Type != "Bool" {
			textType = fmt.Sprintf("<%s>", strings.ToLower(opt.Type))
		}

		// Tokenize body text. We haven't written anything yet, so if Tokenize
		// function fails, the document stays unchanged and we try to parse the
		// next option.
		tokens, err := b.Tokenizer.TokenizeString(textBody)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Warning:")
			continue
		}

		b.Renderer.TextBold(textHead)
		if len(textType) > 0 {
			b.Renderer.Text(textType)
		}
		if len(tokens) > 0 {
			markup.Render(b.Renderer, b.Parser.ParsePart(tokens))
		}
		b.Renderer.Break()
	}
}

func (b *Builder) Save(path string) error {
	file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer file.Close()

	return markup.Save(b.Writer, file)
}

func usage() {
	fmt.Println("Usage:")
	fmt.Println("  mango [option]... [go-file]...\n")
	fmt.Println("Options:")

	flag.CommandLine.VisitAll(func(fl *flag.Flag) {
		name := fl.Name
		help := fl.Usage
		if _, ok := fl.Value.(interface {
			IsBoolFlag()
		}); !ok {
			name = fmt.Sprintf("%s=<value>", name)
		}
		fmt.Printf("  -%-18s\n\t%s\n", name, help)
	})
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
		return
	}

	for _, srcPath := range flag.Args() {
		builder := NewBuilder()

		if err := builder.Load(srcPath); err != nil {
			fmt.Fprintf(os.Stderr, "Warning: Could not open file '%s': %s\n", srcPath, err)
			continue
		}

		dstPath := fmt.Sprintf("%s.1", builder.File.Name)
		if len(optPath) > 0 {
			dstPath = path.Join(optPath, dstPath)
		}

		if err := builder.Save(dstPath); err != nil {
			fmt.Fprintf(os.Stderr, "Warning: Could not save '%s': %s\n", dstPath, err)
			continue
		}
		fmt.Printf("%s -> %s\n", srcPath, dstPath)
	}
}
<|endoftext|>


<commit_before>package plugins

import (
	"bufio"
	"fmt"
	"net/http"
	"strings"
	"unicode"

	"github.com/belak/irc"
	"github.com/belak/seabird/bot"
)

func init() {
	bot.RegisterPlugin("metar", NewMetarPlugin)
}

func NewMetarPlugin(b *bot.Bot) (bot.Plugin, error) {
	b.CommandMux.Event("metar", metarCallback, &bot.HelpInfo{
		Usage:       "<station>",
		Description: "Gives METAR report for given airport code",
	})

	return nil, nil
}

func metarCallback(b *bot.Bot, m *irc.Message) {
	if !m.FromChannel() {
		return
	}

	b.MentionReply(m, "%s", Metar(m.Trailing()))
}

// Metar is a simple function which takes a string representing the
// airport code and returns a string representing the response or an
// error.
func Metar(code string) string {
	code = strings.ToUpper(code)

	for _, letter := range code {
		if !unicode.IsDigit(letter) && !unicode.IsLetter(letter) {
			return "Not a valid airport code"
		}
	}

	resp, err := http.Get(fmt.Sprintf("http://weather.noaa.gov/pub/data/observations/metar/stations/%s.TXT", code))
	if err != nil {
		return "NOAA appears to be down"
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "Station does not exist"
	}

	in := bufio.NewReader(resp.Body)
	for {
		line, err := in.ReadString('\n')
		if err != nil {
			break
		}

		if strings.HasPrefix(line, code+" ") {
			return strings.TrimSpace(line)
		}
	}

	return "No results"
}
<commit_msg>Clean up metar plugin<commit_after>package plugins

import (
	"bufio"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"unicode"

	"github.com/belak/irc"
	"github.com/belak/seabird/bot"
)

func init() {
	bot.RegisterPlugin("metar", NewMetarPlugin)
}

func NewMetarPlugin(b *bot.Bot) (bot.Plugin, error) {
	b.CommandMux.Event("metar", metarCallback, &bot.HelpInfo{
		Usage:       "<station>",
		Description: "Gives METAR report for given airport code",
	})

	return nil, nil
}

func metarCallback(b *bot.Bot, m *irc.Message) {
	if !m.FromChannel() {
		return
	}

	r, err := Metar(m.Trailing())
	if err != nil {
		b.MentionReply(m, "Error: %s", err)
		return
	}

	b.MentionReply(m, "%s", r)
}

// Metar is a simple function which takes a string representing the
// airport code and returns a string representing the response or an
// error.
func Metar(code string) (string, error) {
	code = strings.ToUpper(code)

	for _, letter := range code {
		if !unicode.IsDigit(letter) && !unicode.IsLetter(letter) {
			return "", errors.New("Not a valid airport code")
		}
	}

	resp, err := http.Get(fmt.Sprintf("http://weather.noaa.gov/pub/data/observations/metar/stations/%s.TXT", code))
	if err != nil {
		return "", errors.New("NOAA appears to be down")
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "", errors.New("Station does not exist")
	}

	in := bufio.NewReader(resp.Body)
	for {
		line, err := in.ReadString('\n')
		if err != nil {
			break
		}

		if strings.HasPrefix(line, code+" ") {
			return strings.TrimSpace(line), nil
		}
	}

	return "", errors.New("No results")
}
<|endoftext|>


<commit_before>//
// miner.go
// Staring template for PointCoint miner.
//
// cs4501: Cryptocurrency Cafe
// University of Virginia, Spring 2015
// Project 2
//

package main

import (
	"fmt"
	"log"
	// "math/big"
	"math/rand"

	"github.com/PointCoin/btcjson"
	"github.com/PointCoin/btcutil"
	// "github.com/PointCoin/pointcoind/blockchain"
)

const (
	// This should match your settings in pointcoind.conf
	rpcuser = "ankitgupta"
	rpcpass = "password"
	// This file should exist if pointcoind was setup correctly
	cert    = "/home/ubuntu/.pointcoind/rpc.cert"
)

func main() {
	print := fmt.Println
	// Setup the client using application constants, fail horribly if there's a problem
	client := setupRpcClient(cert, rpcuser, rpcpass)

	for { // Loop forever (you may want to do something smarter!)
		// Get a new block template from pointcoind.
		log.Printf("Requesting a block template\n")
		template, err := client.GetBlockTemplate(&btcjson.TemplateRequest{})
		if err != nil {
			log.Fatal(err)
		}

		// The template returned by GetBlockTemplate provides these fields that
		// you will need to use to create a new block:

		// The hash of the previous block
		prevHash := template.PreviousHash

		// The difficulty target
		difficulty := formatDiff(template.Bits)

		// The height of the next block (number of blocks between genesis block and next block)
		height := template.Height

		// The transactions from the network
		txs := formatTransactions(template.Transactions)

		// These are configurable parameters to the coinbase transaction
		msg := "ag7bf" // replace with your UVa Computing ID (e.g., "dee2b")

		// This is my address generated with wallet of passphrase "mywallet",
		a := "PcvNdeZ6UsC8R34NQqi9YyxKfSXQcjoWHy" // replace with the address you want mining fees to go to (or leave it like this and Nick gets them)

		coinbaseTx := CreateCoinbaseTx(height, a, msg)

		txs = prepend(coinbaseTx.MsgTx(), txs)
		merkleRoot := createMerkleRoot(txs)

		// Finish the miner!
		print("Looking for valid block....")
		var nonce uint32 = rand.Uint32();

		block := CreateBlock(prevHash, merkleRoot, difficulty, nonce, txs)

		// Loop, checking hashes against difficulty until valid hash is found
		for {
			blockSha, _ := block.Header.BlockSha()
			print("Trying with nonce: ", block.Header.Nonce)

			if lessThanDiff(blockSha, difficulty) {
				// submit block
				print("valid hash found")
				err := client.SubmitBlock(btcutil.NewBlock(block), nil)
				print(err)
				break
			} else {
				if block.Header.Nonce == 4294967294 {
					block.Header.Nonce = 0
				} else {
					block.Header.Nonce += 1
				}
			}
		}
	}
}
<commit_msg>Miner checks for new template every 20 mil tries<commit_after>//
// miner.go
// Staring template for PointCoint miner.
//
// cs4501: Cryptocurrency Cafe
// University of Virginia, Spring 2015
// Project 2
//

package main

import (
	"fmt"
	"log"
	// "math/big"
	"math/rand"
	"time"
	"github.com/PointCoin/btcjson"
	"github.com/PointCoin/btcutil"
	// "github.com/PointCoin/pointcoind/blockchain"
)

const (
	// This should match your settings in pointcoind.conf
	rpcuser = "ankitgupta"
	rpcpass = "password"
	// This file should exist if pointcoind was setup correctly
	cert    = "/home/ubuntu/.pointcoind/rpc.cert"
)

func main() {
	print := fmt.Println
	// Setup the client using application constants, fail horribly if there's a problem
	client := setupRpcClient(cert, rpcuser, rpcpass)

	for { // Loop forever (you may want to do something smarter!)
		// Get a new block template from pointcoind.
		log.Printf("Requesting a block template\n")
		template, err := client.GetBlockTemplate(&btcjson.TemplateRequest{})
		if err != nil {
			log.Fatal(err)
		}

		// The template returned by GetBlockTemplate provides these fields that
		// you will need to use to create a new block:

		// The hash of the previous block
		prevHash := template.PreviousHash

		// The difficulty target
		difficulty := formatDiff(template.Bits)

		// The height of the next block (number of blocks between genesis block and next block)
		height := template.Height

		// The transactions from the network
		txs := formatTransactions(template.Transactions)

		// These are configurable parameters to the coinbase transaction
		msg := "ag7bf" // replace with your UVa Computing ID (e.g., "dee2b")

		// This is my address generated with wallet of passphrase "mywallet",
		a := "PcvNdeZ6UsC8R34NQqi9YyxKfSXQcjoWHy" // replace with the address you want mining fees to go to (or leave it like this and Nick gets them)

		coinbaseTx := CreateCoinbaseTx(height, a, msg)

		txs = prepend(coinbaseTx.MsgTx(), txs)
		merkleRoot := createMerkleRoot(txs)

		// Finish the miner!
		print("Looking for valid block....")
		source := rand.NewSource(time.Now().UnixNano())
		myRand := rand.New(source)
		var nonce uint32 = myRand.Uint32();

		block := CreateBlock(prevHash, merkleRoot, difficulty, nonce, txs)

		// Naive approach, try 20 million times before checking for new template
		for i := 0; i < 20000000; i++ {

			// Loop, checking hashes against difficulty until valid hash is found
			for {
				blockSha, _ := block.Header.BlockSha()
				// print("Trying with nonce: ", block.Header.Nonce)

				if lessThanDiff(blockSha, difficulty) {
					// submit block
					print("valid hash found")
					err := client.SubmitBlock(btcutil.NewBlock(block), nil)
					print(err)
					break
				} else {
					if block.Header.Nonce == 4294967294 {
						block.Header.Nonce = 0
					} else {
						block.Header.Nonce += 1
					}
				}
			}
		}

	}
}
<|endoftext|>


<commit_before>package u

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/kjk/atomicfile"
	"github.com/minio/minio-go"
)

// MinioClient represents s3/spaces etc. client
type MinioClient struct {
	StorageKey    string
	StorageSecret string
	Bucket        string
	Endpoint      string // e.g. "nyc3.digitaloceanspaces.com"
	client        *minio.Client
}

// EnsureCondfigured will panic if client not configured
func (c *MinioClient) EnsureConfigured() {
	PanicIf(c.StorageKey == "")
	PanicIf(c.StorageSecret == "")
	PanicIf(c.Bucket == "")
	PanicIf(c.Endpoint == "")
}

// URLBase returns base of url under which files are accesible
// (if public it's a publicly available file)
func (c *MinioClient) URLBase() string {
	return fmt.Sprintf("https://%s.%s/", c.Bucket, c.Endpoint)
}

func (c *MinioClient) GetClient() (*minio.Client, error) {
	if c.client != nil {
		return c.client, nil
	}

	var err error
	ssl := true
	c.client, err = minio.New(c.Endpoint, c.StorageKey, c.StorageSecret, ssl)
	return c.client, err
}

func (c *MinioClient) ListRemoteFiles(prefix string) ([]*minio.ObjectInfo, error) {
	var res []*minio.ObjectInfo
	client, err := c.GetClient()
	if err != nil {
		return nil, err
	}
	doneCh := make(chan struct{})
	defer close(doneCh)

	files := client.ListObjectsV2(c.Bucket, prefix, true, doneCh)
	for oi := range files {
		oic := oi
		res = append(res, &oic)
	}
	return res, nil
}

func isMinioNotExistsError(err error) bool {
	if err == nil {
		return false
	}
	return err.Error() == "The specified key does not exist."
}

func setPublicObjectMetadata(opts *minio.PutObjectOptions) {
	if opts.UserMetadata == nil {
		opts.UserMetadata = map[string]string{}
	}
	opts.UserMetadata["x-amz-acl"] = "public-read"
}

func (c *MinioClient) UploadReaderPublic(remotePath string, r io.Reader, size int64, contentType string) error {
	return c.UploadReader(remotePath, r, size, true, contentType)
}

func (c *MinioClient) UploadReaderPrivate(remotePath string, r io.Reader, size int64, contentType string) error {
	return c.UploadReader(remotePath, r, size, false, contentType)
}

func (c *MinioClient) UploadData(remotePath string, d []byte, opts minio.PutObjectOptions) error {
	client, err := c.GetClient()
	if err != nil {
		return err
	}
	r := bytes.NewReader(d)
	size := int64(len(d))
	_, err = client.PutObject(c.Bucket, remotePath, r, size, opts)
	return err
}

func (c *MinioClient) UploadReader(remotePath string, r io.Reader, size int64, public bool, contentType string) error {
	PanicIf(remotePath[0] == '/', "name '%s' shouldn't start with '/'", remotePath)

	if contentType == "" {
		contentType = MimeTypeFromFileName(remotePath)
	}
	//timeStart := time.Now()
	//sizeStr := humanize.Bytes(uint64(size))
	//fmt.Printf("Uploading '%s' of size %s and type %s as public.", remotePath, sizeStr, contentType)
	client, err := c.GetClient()
	if err != nil {
		return err
	}
	opts := minio.PutObjectOptions{
		ContentType: contentType,
	}
	if public {
		setPublicObjectMetadata(&opts)
	}
	opts.ContentType = contentType
	_, err = client.PutObject(c.Bucket, remotePath, r, size, opts)
	if err != nil {
		return err
	}
	//fmt.Printf(" Took %s.\n", time.Since(timeStart))
	return nil
}

func (c *MinioClient) DownloadFileAsData(remotePath string) ([]byte, error) {
	client, err := c.GetClient()
	if err != nil {
		return nil, err
	}
	opts := minio.GetObjectOptions{}
	obj, err := client.GetObject(c.Bucket, remotePath, opts)
	if err != nil {
		return nil, err
	}
	defer obj.Close()
	var buf bytes.Buffer
	_, err = io.Copy(&buf, obj)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (c *MinioClient) DownloadFileAtomically(dstPath string, remotePath string) error {
	client, err := c.GetClient()
	if err != nil {
		return err
	}
	opts := minio.GetObjectOptions{}
	obj, err := client.GetObject(c.Bucket, remotePath, opts)
	if err != nil {
		return err
	}
	defer obj.Close()

	// ensure there's a dir for destination file
	dir := filepath.Dir(dstPath)
	err = os.MkdirAll(dir, 0755)
	if err != nil {
		return err
	}

	f, err := atomicfile.New(dstPath)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(f, obj)
	if err != nil {
		return err
	}
	return f.Close()
}

func (c *MinioClient) UploadFilePublic(remotePath string, filePath string) error {
	return c.UploadFile(remotePath, filePath, true)
}

func (c *MinioClient) UploadFilePrivate(remotePath string, filePath string) error {
	return c.UploadFile(remotePath, filePath, false)
}

func (c *MinioClient) UploadFile(remotePath string, filePath string, public bool) error {
	stat, err := os.Stat(filePath)
	if err != nil {
		return err
	}
	size := stat.Size()
	f, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer func() {
		f.Close()
	}()
	return c.UploadReaderPublic(remotePath, f, size, "")
}

func (c *MinioClient) UploadDataPublic(remotePath string, d []byte) error {
	r := bytes.NewBuffer(d)
	return c.UploadReaderPublic(remotePath, r, int64(len(d)), "")
}

func (c *MinioClient) UploadStringPublic(remotePath string, s string) error {
	r := bytes.NewBufferString(s)
	return c.UploadReaderPublic(remotePath, r, int64(len(s)), "")
}

func (c *MinioClient) UploadStringPrivate(remotePath string, s string) error {
	r := bytes.NewBufferString(s)
	return c.UploadReaderPrivate(remotePath, r, int64(len(s)), "")
}

func (c *MinioClient) StatObject(remotePath string) (minio.ObjectInfo, error) {
	client, err := c.GetClient()
	if err != nil {
		return minio.ObjectInfo{}, err
	}
	var opts minio.StatObjectOptions
	return client.StatObject(c.Bucket, remotePath, opts)
}
<commit_msg>expose IsMinioNotExistsError<commit_after>package u

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/kjk/atomicfile"
	"github.com/minio/minio-go"
)

// MinioClient represents s3/spaces etc. client
type MinioClient struct {
	StorageKey    string
	StorageSecret string
	Bucket        string
	Endpoint      string // e.g. "nyc3.digitaloceanspaces.com"
	client        *minio.Client
}

// EnsureCondfigured will panic if client not configured
func (c *MinioClient) EnsureConfigured() {
	PanicIf(c.StorageKey == "")
	PanicIf(c.StorageSecret == "")
	PanicIf(c.Bucket == "")
	PanicIf(c.Endpoint == "")
}

// URLBase returns base of url under which files are accesible
// (if public it's a publicly available file)
func (c *MinioClient) URLBase() string {
	return fmt.Sprintf("https://%s.%s/", c.Bucket, c.Endpoint)
}

// GetClient returns a (cached) minio client
func (c *MinioClient) GetClient() (*minio.Client, error) {
	if c.client != nil {
		return c.client, nil
	}

	var err error
	ssl := true
	c.client, err = minio.New(c.Endpoint, c.StorageKey, c.StorageSecret, ssl)
	return c.client, err
}

// ListRemoveFiles returns a list of files under a given prefix
func (c *MinioClient) ListRemoteFiles(prefix string) ([]*minio.ObjectInfo, error) {
	var res []*minio.ObjectInfo
	client, err := c.GetClient()
	if err != nil {
		return nil, err
	}
	doneCh := make(chan struct{})
	defer close(doneCh)

	files := client.ListObjectsV2(c.Bucket, prefix, true, doneCh)
	for oi := range files {
		oic := oi
		res = append(res, &oic)
	}
	return res, nil
}

// IsMinioNotExistsError returns true if an error indicates that a key
// doesn't exist in storage
func IsMinioNotExistsError(err error) bool {
	if err == nil {
		return false
	}
	return err.Error() == "The specified key does not exist."
}

// SetPublicObjectMetadata sets options that mark object as public
// for doing put operation
func SetPublicObjectMetadata(opts *minio.PutObjectOptions) {
	if opts.UserMetadata == nil {
		opts.UserMetadata = map[string]string{}
	}
	opts.UserMetadata["x-amz-acl"] = "public-read"
}

func (c *MinioClient) UploadReaderPublic(remotePath string, r io.Reader, size int64, contentType string) error {
	return c.UploadReader(remotePath, r, size, true, contentType)
}

func (c *MinioClient) UploadReaderPrivate(remotePath string, r io.Reader, size int64, contentType string) error {
	return c.UploadReader(remotePath, r, size, false, contentType)
}

func (c *MinioClient) UploadData(remotePath string, d []byte, opts minio.PutObjectOptions) error {
	client, err := c.GetClient()
	if err != nil {
		return err
	}
	r := bytes.NewReader(d)
	size := int64(len(d))
	_, err = client.PutObject(c.Bucket, remotePath, r, size, opts)
	return err
}

func (c *MinioClient) UploadReader(remotePath string, r io.Reader, size int64, public bool, contentType string) error {
	PanicIf(remotePath[0] == '/', "name '%s' shouldn't start with '/'", remotePath)

	if contentType == "" {
		contentType = MimeTypeFromFileName(remotePath)
	}
	//timeStart := time.Now()
	//sizeStr := humanize.Bytes(uint64(size))
	//fmt.Printf("Uploading '%s' of size %s and type %s as public.", remotePath, sizeStr, contentType)
	client, err := c.GetClient()
	if err != nil {
		return err
	}
	opts := minio.PutObjectOptions{
		ContentType: contentType,
	}
	if public {
		SetPublicObjectMetadata(&opts)
	}
	opts.ContentType = contentType
	_, err = client.PutObject(c.Bucket, remotePath, r, size, opts)
	if err != nil {
		return err
	}
	//fmt.Printf(" Took %s.\n", time.Since(timeStart))
	return nil
}

func (c *MinioClient) DownloadFileAsData(remotePath string) ([]byte, error) {
	client, err := c.GetClient()
	if err != nil {
		return nil, err
	}
	opts := minio.GetObjectOptions{}
	obj, err := client.GetObject(c.Bucket, remotePath, opts)
	if err != nil {
		return nil, err
	}
	defer obj.Close()
	var buf bytes.Buffer
	_, err = io.Copy(&buf, obj)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (c *MinioClient) DownloadFileAtomically(dstPath string, remotePath string) error {
	client, err := c.GetClient()
	if err != nil {
		return err
	}
	opts := minio.GetObjectOptions{}
	obj, err := client.GetObject(c.Bucket, remotePath, opts)
	if err != nil {
		return err
	}
	defer obj.Close()

	// ensure there's a dir for destination file
	dir := filepath.Dir(dstPath)
	err = os.MkdirAll(dir, 0755)
	if err != nil {
		return err
	}

	f, err := atomicfile.New(dstPath)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(f, obj)
	if err != nil {
		return err
	}
	return f.Close()
}

func (c *MinioClient) UploadFilePublic(remotePath string, filePath string) error {
	return c.UploadFile(remotePath, filePath, true)
}

func (c *MinioClient) UploadFilePrivate(remotePath string, filePath string) error {
	return c.UploadFile(remotePath, filePath, false)
}

func (c *MinioClient) UploadFile(remotePath string, filePath string, public bool) error {
	stat, err := os.Stat(filePath)
	if err != nil {
		return err
	}
	size := stat.Size()
	f, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer func() {
		f.Close()
	}()
	return c.UploadReaderPublic(remotePath, f, size, "")
}

func (c *MinioClient) UploadDataPublic(remotePath string, d []byte) error {
	r := bytes.NewBuffer(d)
	return c.UploadReaderPublic(remotePath, r, int64(len(d)), "")
}

func (c *MinioClient) UploadStringPublic(remotePath string, s string) error {
	r := bytes.NewBufferString(s)
	return c.UploadReaderPublic(remotePath, r, int64(len(s)), "")
}

func (c *MinioClient) UploadStringPrivate(remotePath string, s string) error {
	r := bytes.NewBufferString(s)
	return c.UploadReaderPrivate(remotePath, r, int64(len(s)), "")
}

func (c *MinioClient) StatObject(remotePath string) (minio.ObjectInfo, error) {
	client, err := c.GetClient()
	if err != nil {
		return minio.ObjectInfo{}, err
	}
	var opts minio.StatObjectOptions
	return client.StatObject(c.Bucket, remotePath, opts)
}
<|endoftext|>


<commit_before>// Copyright 2013 Dario Castañé. All rights reserved.
// 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.

// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.

package mergo

import (
	"fmt"
	"reflect"
)

func hasMergeableFields(dst reflect.Value) (exported bool) {
	for i, n := 0, dst.NumField(); i < n; i++ {
		field := dst.Type().Field(i)
		if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
			exported = exported || hasMergeableFields(dst.Field(i))
		} else if isExportedComponent(&field) {
			exported = exported || len(field.PkgPath) == 0
		}
	}
	return
}

func isExportedComponent(field *reflect.StructField) bool {
	pkgPath := field.PkgPath
	if len(pkgPath) > 0 {
		return false
	}
	c := field.Name[0]
	if 'a' <= c && c <= 'z' || c == '_' {
		return false
	}
	return true
}

type Config struct {
	Overwrite                    bool
	AppendSlice                  bool
	TypeCheck                    bool
	Transformers                 Transformers
	overwriteWithEmptyValue      bool
	overwriteSliceWithEmptyValue bool
	sliceDeepCopy                bool
	debug                        bool
}

type Transformers interface {
	Transformer(reflect.Type) func(dst, src reflect.Value) error
}

// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
	overwrite := config.Overwrite
	typeCheck := config.TypeCheck
	overwriteWithEmptySrc := config.overwriteWithEmptyValue
	overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue
	sliceDeepCopy := config.sliceDeepCopy

	if !src.IsValid() {
		return
	}
	if dst.CanAddr() {
		addr := dst.UnsafeAddr()
		h := 17 * addr
		seen := visited[h]
		typ := dst.Type()
		for p := seen; p != nil; p = p.next {
			if p.ptr == addr && p.typ == typ {
				return nil
			}
		}
		// Remember, remember...
		visited[h] = &visit{addr, typ, seen}
	}

	if config.Transformers != nil && !isEmptyValue(dst) {
		if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
			err = fn(dst, src)
			return
		}
	}

	switch dst.Kind() {
	case reflect.Struct:
		if hasMergeableFields(dst) {
			for i, n := 0, dst.NumField(); i < n; i++ {
				if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
					return
				}
			}
		} else {
			if dst.CanSet() && (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) {
				dst.Set(src)
			}
		}
	case reflect.Map:
		if dst.IsNil() && !src.IsNil() {
			if dst.CanSet() {
				dst.Set(reflect.MakeMap(dst.Type()))
			} else {
				dst = src
				return
			}
		}

		if src.Kind() != reflect.Map {
			if overwrite {
				dst.Set(src)
			}
			return
		}

		for _, key := range src.MapKeys() {
			srcElement := src.MapIndex(key)
			if !srcElement.IsValid() {
				continue
			}
			dstElement := dst.MapIndex(key)
			switch srcElement.Kind() {
			case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
				if srcElement.IsNil() {
					if overwrite {
						dst.SetMapIndex(key, srcElement)
					}
					continue
				}
				fallthrough
			default:
				if !srcElement.CanInterface() {
					continue
				}
				switch reflect.TypeOf(srcElement.Interface()).Kind() {
				case reflect.Struct:
					fallthrough
				case reflect.Ptr:
					fallthrough
				case reflect.Map:
					srcMapElm := srcElement
					dstMapElm := dstElement
					if srcMapElm.CanInterface() {
						srcMapElm = reflect.ValueOf(srcMapElm.Interface())
						if dstMapElm.IsValid() {
							dstMapElm = reflect.ValueOf(dstMapElm.Interface())
						}
					}
					if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
						return
					}
				case reflect.Slice:
					srcSlice := reflect.ValueOf(srcElement.Interface())

					var dstSlice reflect.Value
					if !dstElement.IsValid() || dstElement.IsNil() {
						dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
					} else {
						dstSlice = reflect.ValueOf(dstElement.Interface())
					}

					if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
						if typeCheck && srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = srcSlice
					} else if config.AppendSlice {
						if srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
					} else if sliceDeepCopy {
						i := 0
						for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ {
							srcElement := srcSlice.Index(i)
							dstElement := dstSlice.Index(i)

							if srcElement.CanInterface() {
								srcElement = reflect.ValueOf(srcElement.Interface())
							}
							if dstElement.CanInterface() {
								dstElement = reflect.ValueOf(dstElement.Interface())
							}

							if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
								return
							}
						}

					}
					dst.SetMapIndex(key, dstSlice)
				}
			}
			if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
				continue
			}

			if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) {
				if dst.IsNil() {
					dst.Set(reflect.MakeMap(dst.Type()))
				}
				dst.SetMapIndex(key, srcElement)
			}
		}
	case reflect.Slice:
		if !dst.CanSet() {
			break
		}
		if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
			dst.Set(src)
		} else if config.AppendSlice {
			if src.Type() != dst.Type() {
				return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
			}
			dst.Set(reflect.AppendSlice(dst, src))
		} else if sliceDeepCopy {
			for i := 0; i < src.Len() && i < dst.Len(); i++ {
				srcElement := src.Index(i)
				dstElement := dst.Index(i)
				if srcElement.CanInterface() {
					srcElement = reflect.ValueOf(srcElement.Interface())
				}
				if dstElement.CanInterface() {
					dstElement = reflect.ValueOf(dstElement.Interface())
				}

				if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
					return
				}
			}
		}
	case reflect.Ptr:
		fallthrough
	case reflect.Interface:
		if isReflectNil(src) {
			if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) {
				dst.Set(src)
			}
			break
		}

		if src.Kind() != reflect.Interface {
			if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			} else if src.Kind() == reflect.Ptr {
				if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
					return
				}
			} else if dst.Elem().Type() == src.Type() {
				if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
					return
				}
			} else {
				return ErrDifferentArgumentsTypes
			}
			break
		}

		if dst.IsNil() || overwrite {
			if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
				dst.Set(src)
			}
			break
		}

		if dst.Elem().Kind() == src.Elem().Kind() {
			if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
				return
			}
			break
		}
	default:
		mustSet := (isEmptyValue(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc)
		if mustSet {
			if dst.CanSet() {
				dst.Set(src)
			} else {
				dst = src
			}
		}
	}

	return
}

// Merge will fill any empty for value type attributes on the dst struct using corresponding
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, opts...)
}

// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, append(opts, WithOverride)...)
}

// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
	return func(config *Config) {
		config.Transformers = transformers
	}
}

// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
	config.Overwrite = true
}

// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
func WithOverwriteWithEmptyValue(config *Config) {
	config.Overwrite = true
	config.overwriteWithEmptyValue = true
}

// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
func WithOverrideEmptySlice(config *Config) {
	config.overwriteSliceWithEmptyValue = true
}

// WithAppendSlice will make merge append slices instead of overwriting it.
func WithAppendSlice(config *Config) {
	config.AppendSlice = true
}

// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
func WithTypeCheck(config *Config) {
	config.TypeCheck = true
}

// WithSliceDeepCopy will merge slice element one by one with Overwrite flag.
func WithSliceDeepCopy(config *Config) {
	config.sliceDeepCopy = true
	config.Overwrite = true
}

func merge(dst, src interface{}, opts ...func(*Config)) error {
	if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
		return ErrNonPointerAgument
	}
	var (
		vDst, vSrc reflect.Value
		err        error
	)

	config := &Config{}

	for _, opt := range opts {
		opt(config)
	}

	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
		return err
	}
	if vDst.Type() != vSrc.Type() {
		return ErrDifferentArgumentsTypes
	}
	return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}

// IsReflectNil is the reflect value provided nil
func isReflectNil(v reflect.Value) bool {
	k := v.Kind()
	switch k {
	case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
		// Both interface and slice are nil if first word is 0.
		// Both are always bigger than a word; assume flagIndir.
		return v.IsNil()
	default:
		return false
	}
}
<commit_msg>fix: gate transformers on valid non-nil destinations<commit_after>// Copyright 2013 Dario Castañé. All rights reserved.
// 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.

// Based on src/pkg/reflect/deepequal.go from official
// golang's stdlib.

package mergo

import (
	"fmt"
	"reflect"
)

func hasMergeableFields(dst reflect.Value) (exported bool) {
	for i, n := 0, dst.NumField(); i < n; i++ {
		field := dst.Type().Field(i)
		if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
			exported = exported || hasMergeableFields(dst.Field(i))
		} else if isExportedComponent(&field) {
			exported = exported || len(field.PkgPath) == 0
		}
	}
	return
}

func isExportedComponent(field *reflect.StructField) bool {
	pkgPath := field.PkgPath
	if len(pkgPath) > 0 {
		return false
	}
	c := field.Name[0]
	if 'a' <= c && c <= 'z' || c == '_' {
		return false
	}
	return true
}

type Config struct {
	Overwrite                    bool
	AppendSlice                  bool
	TypeCheck                    bool
	Transformers                 Transformers
	overwriteWithEmptyValue      bool
	overwriteSliceWithEmptyValue bool
	sliceDeepCopy                bool
	debug                        bool
}

type Transformers interface {
	Transformer(reflect.Type) func(dst, src reflect.Value) error
}

// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
	overwrite := config.Overwrite
	typeCheck := config.TypeCheck
	overwriteWithEmptySrc := config.overwriteWithEmptyValue
	overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue
	sliceDeepCopy := config.sliceDeepCopy

	if !src.IsValid() {
		return
	}
	if dst.CanAddr() {
		addr := dst.UnsafeAddr()
		h := 17 * addr
		seen := visited[h]
		typ := dst.Type()
		for p := seen; p != nil; p = p.next {
			if p.ptr == addr && p.typ == typ {
				return nil
			}
		}
		// Remember, remember...
		visited[h] = &visit{addr, typ, seen}
	}

	if config.Transformers != nil && !isReflectNil(dst) && dst.IsValid() {
		if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
			err = fn(dst, src)
			return
		}
	}

	switch dst.Kind() {
	case reflect.Struct:
		if hasMergeableFields(dst) {
			for i, n := 0, dst.NumField(); i < n; i++ {
				if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
					return
				}
			}
		} else {
			if dst.CanSet() && (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) {
				dst.Set(src)
			}
		}
	case reflect.Map:
		if dst.IsNil() && !src.IsNil() {
			if dst.CanSet() {
				dst.Set(reflect.MakeMap(dst.Type()))
			} else {
				dst = src
				return
			}
		}

		if src.Kind() != reflect.Map {
			if overwrite {
				dst.Set(src)
			}
			return
		}

		for _, key := range src.MapKeys() {
			srcElement := src.MapIndex(key)
			if !srcElement.IsValid() {
				continue
			}
			dstElement := dst.MapIndex(key)
			switch srcElement.Kind() {
			case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
				if srcElement.IsNil() {
					if overwrite {
						dst.SetMapIndex(key, srcElement)
					}
					continue
				}
				fallthrough
			default:
				if !srcElement.CanInterface() {
					continue
				}
				switch reflect.TypeOf(srcElement.Interface()).Kind() {
				case reflect.Struct:
					fallthrough
				case reflect.Ptr:
					fallthrough
				case reflect.Map:
					srcMapElm := srcElement
					dstMapElm := dstElement
					if srcMapElm.CanInterface() {
						srcMapElm = reflect.ValueOf(srcMapElm.Interface())
						if dstMapElm.IsValid() {
							dstMapElm = reflect.ValueOf(dstMapElm.Interface())
						}
					}
					if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
						return
					}
				case reflect.Slice:
					srcSlice := reflect.ValueOf(srcElement.Interface())

					var dstSlice reflect.Value
					if !dstElement.IsValid() || dstElement.IsNil() {
						dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
					} else {
						dstSlice = reflect.ValueOf(dstElement.Interface())
					}

					if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
						if typeCheck && srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = srcSlice
					} else if config.AppendSlice {
						if srcSlice.Type() != dstSlice.Type() {
							return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
						}
						dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
					} else if sliceDeepCopy {
						i := 0
						for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ {
							srcElement := srcSlice.Index(i)
							dstElement := dstSlice.Index(i)

							if srcElement.CanInterface() {
								srcElement = reflect.ValueOf(srcElement.Interface())
							}
							if dstElement.CanInterface() {
								dstElement = reflect.ValueOf(dstElement.Interface())
							}

							if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
								return
							}
						}

					}
					dst.SetMapIndex(key, dstSlice)
				}
			}
			if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
				continue
			}

			if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) {
				if dst.IsNil() {
					dst.Set(reflect.MakeMap(dst.Type()))
				}
				dst.SetMapIndex(key, srcElement)
			}
		}
	case reflect.Slice:
		if !dst.CanSet() {
			break
		}
		if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
			dst.Set(src)
		} else if config.AppendSlice {
			if src.Type() != dst.Type() {
				return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
			}
			dst.Set(reflect.AppendSlice(dst, src))
		} else if sliceDeepCopy {
			for i := 0; i < src.Len() && i < dst.Len(); i++ {
				srcElement := src.Index(i)
				dstElement := dst.Index(i)
				if srcElement.CanInterface() {
					srcElement = reflect.ValueOf(srcElement.Interface())
				}
				if dstElement.CanInterface() {
					dstElement = reflect.ValueOf(dstElement.Interface())
				}

				if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
					return
				}
			}
		}
	case reflect.Ptr:
		fallthrough
	case reflect.Interface:
		if isReflectNil(src) {
			if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) {
				dst.Set(src)
			}
			break
		}

		if src.Kind() != reflect.Interface {
			if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
				if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
					dst.Set(src)
				}
			} else if src.Kind() == reflect.Ptr {
				if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
					return
				}
			} else if dst.Elem().Type() == src.Type() {
				if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
					return
				}
			} else {
				return ErrDifferentArgumentsTypes
			}
			break
		}

		if dst.IsNil() || overwrite {
			if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
				dst.Set(src)
			}
			break
		}

		if dst.Elem().Kind() == src.Elem().Kind() {
			if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
				return
			}
			break
		}
	default:
		mustSet := (isEmptyValue(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc)
		if mustSet {
			if dst.CanSet() {
				dst.Set(src)
			} else {
				dst = src
			}
		}
	}

	return
}

// Merge will fill any empty for value type attributes on the dst struct using corresponding
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, opts...)
}

// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
	return merge(dst, src, append(opts, WithOverride)...)
}

// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
	return func(config *Config) {
		config.Transformers = transformers
	}
}

// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
	config.Overwrite = true
}

// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
func WithOverwriteWithEmptyValue(config *Config) {
	config.Overwrite = true
	config.overwriteWithEmptyValue = true
}

// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
func WithOverrideEmptySlice(config *Config) {
	config.overwriteSliceWithEmptyValue = true
}

// WithAppendSlice will make merge append slices instead of overwriting it.
func WithAppendSlice(config *Config) {
	config.AppendSlice = true
}

// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
func WithTypeCheck(config *Config) {
	config.TypeCheck = true
}

// WithSliceDeepCopy will merge slice element one by one with Overwrite flag.
func WithSliceDeepCopy(config *Config) {
	config.sliceDeepCopy = true
	config.Overwrite = true
}

func merge(dst, src interface{}, opts ...func(*Config)) error {
	if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
		return ErrNonPointerAgument
	}
	var (
		vDst, vSrc reflect.Value
		err        error
	)

	config := &Config{}

	for _, opt := range opts {
		opt(config)
	}

	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
		return err
	}
	if vDst.Type() != vSrc.Type() {
		return ErrDifferentArgumentsTypes
	}
	return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}

// IsReflectNil is the reflect value provided nil
func isReflectNil(v reflect.Value) bool {
	k := v.Kind()
	switch k {
	case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
		// Both interface and slice are nil if first word is 0.
		// Both are always bigger than a word; assume flagIndir.
		return v.IsNil()
	default:
		return false
	}
}
<|endoftext|>


<commit_before>package storage

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/viant/toolbox"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"net/http"
	"strings"
	"path/filepath"
	"time"
	"bytes"
	"github.com/viant/toolbox/cred"
)

//httpStorageService represents basic http storage service (only limited listing and full download are supported)
type httpStorageService struct {
	Credential *cred.Config
}

func newHttpClient() (*http.Client, error) {
	return toolbox.NewHttpClient(&toolbox.HttpOptions{Key:"MaxIdleConns", Value:0})
}

func (s *httpStorageService) addCredentialToURLIfNeeded(URL string) string {
	if s.Credential == nil || s.Credential.Password == "" || s.Credential.Username == "" {
		return URL
	}
	prasedURL, err := url.Parse(URL)
	if err != nil {
		return URL
	}
	if prasedURL.User != nil {
		return URL
	}
	return strings.Replace(URL, "://", fmt.Sprintf("://%v:%v@", s.Credential.Username, s.Credential.Password), 1)
}

type hRef struct {
	URL   string
	Value string
}

func extractLinks(body string) []*hRef {
	var result = make([]*hRef, 0)
	var linkContents = strings.Split(string(body), "href=\"")
	for i := 1; i < len(linkContents); i++ {
		var linkContent = linkContents[i]
		linkEndPosition := strings.Index(linkContent, "\"")
		if linkEndPosition == -1 {
			continue
		}
		var content = ""
		contentStartPosition := strings.Index(linkContent, ">")
		if contentStartPosition != 1 {
			content = string(linkContent[contentStartPosition+1:])
			contentEndPosition := strings.Index(content, "<")
			if contentEndPosition != -1 {
				content = string(content[:contentEndPosition])
			}
		}

		link := &hRef{
			URL:   string(linkContent[:linkEndPosition]),
			Value: strings.Trim(content, " \t\r\n"),
		}
		result = append(result, link)

	}
	return result
}

//List returns a list of object for supplied url
func (s *httpStorageService) List(URL string) ([]Object, error) {
	listURL := s.addCredentialToURLIfNeeded(URL)
	client, err := newHttpClient()
	if err != nil {
		return nil, err
	}
	response, err := client.Get(listURL)

	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	now := time.Now()
	contentType := response.Header.Get("Content-Type")
	var result = make([]Object, 0)

	if response.Status != "200 OK" {
		return nil, fmt.Errorf("Invalid response code: %v", response.Status)
	}

	isGitUrl := strings.Contains(URL, "github.com")
	if strings.Contains(contentType, "text/html") {

		links := extractLinks(string(body))

		if isGitUrl {

			for _, link := range links {
				if ! ((strings.Contains(link.URL, "/blob/") || strings.Contains(link.URL, "/tree/")) && strings.HasSuffix(link.URL, link.Value)) {
					continue
				}

				linkType := StorageObjectContentType
				if strings.Contains(link.URL, "/tree/") {
					linkType = StorageObjectFolderType
				}

				baseURL := toolbox.URLBase(URL)
				objectURL := toolbox.URLPathJoin(baseURL, link.URL)

				if linkType == StorageObjectContentType {
					objectURL = strings.Replace(objectURL, "/blob/", "/", 1)
					objectURL = strings.Replace(objectURL, "github.com", "raw.githubusercontent.com", 1)
				}
				storageObject := newHttpFileObject(objectURL, linkType, nil, now, 1)
				result = append(result, storageObject)
			}

		} else {
			for _, link := range links {
				if link.URL == "" || strings.Contains(link.URL, ":") || strings.HasPrefix(link.URL, "#") || strings.HasPrefix(link.URL, "?") || strings.HasPrefix(link.URL, ".") || strings.HasPrefix(link.URL, "/") {
					continue
				}
				linkType := StorageObjectContentType
				if strings.HasSuffix(link.URL, "/") {
					linkType = StorageObjectFolderType
				}
				objectURL := toolbox.URLPathJoin(URL, link.URL)
				storageObject := newHttpFileObject(objectURL, linkType, nil, now, 1)
				result = append(result, storageObject)
			}
		}
	}

	if strings.Contains(string(body), ">..<") {
		return result, err
	}
	storageObject := newHttpFileObject(URL, StorageObjectContentType, nil, now, response.ContentLength)
	result = append(result, storageObject)
	return result, err
}

//Exists returns true if resource exists
func (s *httpStorageService) Exists(URL string) (bool, error) {
	client, err := newHttpClient()
	if err != nil {
		return false, err
	}
	response, err := client.Get(URL)
	if err != nil {
		return false, err
	}
	return response.StatusCode == 200, nil
}

//Object returns a Object for supplied url
func (s *httpStorageService) StorageObject(URL string) (Object, error) {
	objects, err := s.List(URL)
	if err != nil {
		return nil, err
	}
	if len(objects) == 0 {
		return nil, fmt.Errorf("Resource not found: %v", URL)
	}

	return objects[0], nil
}

//Download returns reader for downloaded storage object
func (s *httpStorageService) Download(object Object) (io.Reader, error) {
	client, err := newHttpClient()
	if err != nil {
		return nil, err
	}
	response, err := client.Get(s.addCredentialToURLIfNeeded(object.URL()))
	content, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	return bytes.NewReader(content), err
}

//Upload uploads provided reader content for supplied url.
func (s *httpStorageService) Upload(URL string, reader io.Reader) error {
	return errors.New("unsupported")
}

func (s *httpStorageService) Register(schema string, service Service) error {
	return errors.New("unsupported")
}

//Delete removes passed in storage object
func (s *httpStorageService) Delete(object Object) error {
	fileName, err := toolbox.FileFromURL(object.URL())
	if err != nil {
		return err
	}
	return os.Remove(fileName)
}

func (s *httpStorageService) Close() error {
	return nil
}

func NewHttpStorageService(credential *cred.Config) Service {
	return &httpStorageService{
		Credential: credential,
	}
}

type httpStorageObject struct {
	*AbstractObject
}

func (o *httpStorageObject) Unwrap(target interface{}) error {
	return fmt.Errorf("unsuported target %T", target)
}

func newHttpFileObject(url string, objectType int, source interface{}, lastModified time.Time, size int64) Object {
	var isDir = objectType == StorageObjectFolderType
	var _, name = toolbox.URLSplit(url)
	var fileMode , _ = NewFileMode("-r--r--r--")
	if isDir {
		fileMode , _ = NewFileMode("dr--r--r--")
	}
	fileInfo := NewFileInfo(name, size, fileMode, lastModified, isDir)
	abstract := NewAbstractStorageObject(url, source, fileInfo)
	result := &httpStorageObject{
		AbstractObject: abstract,
	}
	result.AbstractObject.Object = result
	return result
}

const HttpProviderScheme = "http"
const HttpsProviderScheme = "https"

func init() {
	NewStorageProvider().Registry[HttpsProviderScheme] = serviceProvider
	NewStorageProvider().Registry[HttpProviderScheme] = serviceProvider

}

func serviceProvider(credentialFile string) (Service, error) {

	if credentialFile == "" {
		return NewHttpStorageService(nil), nil
	}

	if !strings.HasPrefix(credentialFile, "/") {
		dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
		credentialFile = path.Join(dir, credentialFile)
	}
	config, err :=cred.NewConfig(credentialFile)
	if err != nil {
		return nil, err
	}
	return NewHttpStorageService(config), nil
}
<commit_msg>lowercase error message<commit_after>package storage

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/viant/toolbox"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"net/http"
	"strings"
	"path/filepath"
	"time"
	"bytes"
	"github.com/viant/toolbox/cred"
)

//httpStorageService represents basic http storage service (only limited listing and full download are supported)
type httpStorageService struct {
	Credential *cred.Config
}

func newHttpClient() (*http.Client, error) {
	return toolbox.NewHttpClient(&toolbox.HttpOptions{Key:"MaxIdleConns", Value:0})
}

func (s *httpStorageService) addCredentialToURLIfNeeded(URL string) string {
	if s.Credential == nil || s.Credential.Password == "" || s.Credential.Username == "" {
		return URL
	}
	prasedURL, err := url.Parse(URL)
	if err != nil {
		return URL
	}
	if prasedURL.User != nil {
		return URL
	}
	return strings.Replace(URL, "://", fmt.Sprintf("://%v:%v@", s.Credential.Username, s.Credential.Password), 1)
}

type hRef struct {
	URL   string
	Value string
}

func extractLinks(body string) []*hRef {
	var result = make([]*hRef, 0)
	var linkContents = strings.Split(string(body), "href=\"")
	for i := 1; i < len(linkContents); i++ {
		var linkContent = linkContents[i]
		linkEndPosition := strings.Index(linkContent, "\"")
		if linkEndPosition == -1 {
			continue
		}
		var content = ""
		contentStartPosition := strings.Index(linkContent, ">")
		if contentStartPosition != 1 {
			content = string(linkContent[contentStartPosition+1:])
			contentEndPosition := strings.Index(content, "<")
			if contentEndPosition != -1 {
				content = string(content[:contentEndPosition])
			}
		}

		link := &hRef{
			URL:   string(linkContent[:linkEndPosition]),
			Value: strings.Trim(content, " \t\r\n"),
		}
		result = append(result, link)

	}
	return result
}

//List returns a list of object for supplied url
func (s *httpStorageService) List(URL string) ([]Object, error) {
	listURL := s.addCredentialToURLIfNeeded(URL)
	client, err := newHttpClient()
	if err != nil {
		return nil, err
	}
	response, err := client.Get(listURL)

	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	now := time.Now()
	contentType := response.Header.Get("Content-Type")
	var result = make([]Object, 0)

	if response.Status != "200 OK" {
		return nil, fmt.Errorf("Invalid response code: %v", response.Status)
	}

	isGitUrl := strings.Contains(URL, "github.com")
	if strings.Contains(contentType, "text/html") {

		links := extractLinks(string(body))

		if isGitUrl {

			for _, link := range links {
				if ! ((strings.Contains(link.URL, "/blob/") || strings.Contains(link.URL, "/tree/")) && strings.HasSuffix(link.URL, link.Value)) {
					continue
				}

				linkType := StorageObjectContentType
				if strings.Contains(link.URL, "/tree/") {
					linkType = StorageObjectFolderType
				}

				baseURL := toolbox.URLBase(URL)
				objectURL := toolbox.URLPathJoin(baseURL, link.URL)

				if linkType == StorageObjectContentType {
					objectURL = strings.Replace(objectURL, "/blob/", "/", 1)
					objectURL = strings.Replace(objectURL, "github.com", "raw.githubusercontent.com", 1)
				}
				storageObject := newHttpFileObject(objectURL, linkType, nil, now, 1)
				result = append(result, storageObject)
			}

		} else {
			for _, link := range links {
				if link.URL == "" || strings.Contains(link.URL, ":") || strings.HasPrefix(link.URL, "#") || strings.HasPrefix(link.URL, "?") || strings.HasPrefix(link.URL, ".") || strings.HasPrefix(link.URL, "/") {
					continue
				}
				linkType := StorageObjectContentType
				if strings.HasSuffix(link.URL, "/") {
					linkType = StorageObjectFolderType
				}
				objectURL := toolbox.URLPathJoin(URL, link.URL)
				storageObject := newHttpFileObject(objectURL, linkType, nil, now, 1)
				result = append(result, storageObject)
			}
		}
	}

	if strings.Contains(string(body), ">..<") {
		return result, err
	}
	storageObject := newHttpFileObject(URL, StorageObjectContentType, nil, now, response.ContentLength)
	result = append(result, storageObject)
	return result, err
}

//Exists returns true if resource exists
func (s *httpStorageService) Exists(URL string) (bool, error) {
	client, err := newHttpClient()
	if err != nil {
		return false, err
	}
	response, err := client.Get(URL)
	if err != nil {
		return false, err
	}
	return response.StatusCode == 200, nil
}

//Object returns a Object for supplied url
func (s *httpStorageService) StorageObject(URL string) (Object, error) {
	objects, err := s.List(URL)
	if err != nil {
		return nil, err
	}
	if len(objects) == 0 {
		return nil, fmt.Errorf("resource  not found: %v", URL)
	}

	return objects[0], nil
}

//Download returns reader for downloaded storage object
func (s *httpStorageService) Download(object Object) (io.Reader, error) {
	client, err := newHttpClient()
	if err != nil {
		return nil, err
	}
	response, err := client.Get(s.addCredentialToURLIfNeeded(object.URL()))
	content, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	return bytes.NewReader(content), err
}

//Upload uploads provided reader content for supplied url.
func (s *httpStorageService) Upload(URL string, reader io.Reader) error {
	return errors.New("unsupported")
}

func (s *httpStorageService) Register(schema string, service Service) error {
	return errors.New("unsupported")
}

//Delete removes passed in storage object
func (s *httpStorageService) Delete(object Object) error {
	fileName, err := toolbox.FileFromURL(object.URL())
	if err != nil {
		return err
	}
	return os.Remove(fileName)
}

func (s *httpStorageService) Close() error {
	return nil
}

func NewHttpStorageService(credential *cred.Config) Service {
	return &httpStorageService{
		Credential: credential,
	}
}

type httpStorageObject struct {
	*AbstractObject
}

func (o *httpStorageObject) Unwrap(target interface{}) error {
	return fmt.Errorf("unsuported target %T", target)
}

func newHttpFileObject(url string, objectType int, source interface{}, lastModified time.Time, size int64) Object {
	var isDir = objectType == StorageObjectFolderType
	var _, name = toolbox.URLSplit(url)
	var fileMode , _ = NewFileMode("-r--r--r--")
	if isDir {
		fileMode , _ = NewFileMode("dr--r--r--")
	}
	fileInfo := NewFileInfo(name, size, fileMode, lastModified, isDir)
	abstract := NewAbstractStorageObject(url, source, fileInfo)
	result := &httpStorageObject{
		AbstractObject: abstract,
	}
	result.AbstractObject.Object = result
	return result
}

const HttpProviderScheme = "http"
const HttpsProviderScheme = "https"

func init() {
	NewStorageProvider().Registry[HttpsProviderScheme] = serviceProvider
	NewStorageProvider().Registry[HttpProviderScheme] = serviceProvider

}

func serviceProvider(credentialFile string) (Service, error) {

	if credentialFile == "" {
		return NewHttpStorageService(nil), nil
	}

	if !strings.HasPrefix(credentialFile, "/") {
		dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
		credentialFile = path.Join(dir, credentialFile)
	}
	config, err :=cred.NewConfig(credentialFile)
	if err != nil {
		return nil, err
	}
	return NewHttpStorageService(config), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	goflags "github.com/jessevdk/go-flags" // rename import to `goflags` (file scope) so we can use `var flags` (package scope)
	"go/build"
	"os"
)

// flags
var flags struct {
	Verbose    bool   `long:"verbose" short:"v" description:"Show verbose debug information"`
	ImportPath string `long:"import-path" short:"i" description:"Import path to use. Using PWD when left empty."`

	Append struct {
		Executable string `long:"exec" description:"Executable to append" required:"true"`
	} `command:"append"`

	EmbedGo   struct{} `command:"embed"`
	EmbedSyso struct{} `command:"embed-syso"`
	Clean     struct{} `command:"clean"`
}

// flags parser
var flagsParser *goflags.Parser

// initFlags parses the given flags.
// when the user asks for help (-h or --help): the application exists with status 0
// when unexpected flags is given: the application exits with status 1
func parseArguments() {
	// create flags parser in global var, for flagsParser.Active.Name (operation)
	flagsParser = goflags.NewParser(&flags, goflags.Default)

	// parse flags
	args, err := flagsParser.Parse()
	if err != nil {
		// assert the err to be a flags.Error
		flagError := err.(*goflags.Error)
		if flagError.Type == goflags.ErrHelp {
			// user asked for help on flags.
			// program can exit successfully
			os.Exit(0)
		}
		if flagError.Type == goflags.ErrUnknownFlag {
			fmt.Println("Use --help to view available options.")
			os.Exit(1)
		}
		if flagError.Type == goflags.ErrRequired {
			os.Exit(1)
		}
		fmt.Printf("Error parsing flags: %s\n", err)
		os.Exit(1)
	}

	// error on left-over arguments
	if len(args) > 0 {
		fmt.Printf("Unexpected arguments: %s\nUse --help to view available options.", args)
		os.Exit(1)
	}

	// default ImportPath to pwd when not set
	if len(flags.ImportPath) == 0 {
		pwd, err := os.Getwd()
		if err != nil {
			fmt.Printf("error getting pwd: %s\n", err)
			os.Exit(1)
		}
		verbosef("using pwd as import path\n")
		// find non-absolute path for this pwd
		pkg, err := build.ImportDir(pwd, build.FindOnly)
		if err != nil {
			fmt.Printf("error using current directory as import path: %s\n", err)
			os.Exit(1)
		}
		flags.ImportPath = pkg.ImportPath
		verbosef("using import path: %s\n", flags.ImportPath)
		return
	}
}
<commit_msg>Change flag `embed` to `embed-go`<commit_after>package main

import (
	"fmt"
	goflags "github.com/jessevdk/go-flags" // rename import to `goflags` (file scope) so we can use `var flags` (package scope)
	"go/build"
	"os"
)

// flags
var flags struct {
	Verbose    bool   `long:"verbose" short:"v" description:"Show verbose debug information"`
	ImportPath string `long:"import-path" short:"i" description:"Import path to use. Using PWD when left empty."`

	Append struct {
		Executable string `long:"exec" description:"Executable to append" required:"true"`
	} `command:"append"`

	EmbedGo   struct{} `command:"embed-go"`
	EmbedSyso struct{} `command:"embed-syso"`
	Clean     struct{} `command:"clean"`
}

// flags parser
var flagsParser *goflags.Parser

// initFlags parses the given flags.
// when the user asks for help (-h or --help): the application exists with status 0
// when unexpected flags is given: the application exits with status 1
func parseArguments() {
	// create flags parser in global var, for flagsParser.Active.Name (operation)
	flagsParser = goflags.NewParser(&flags, goflags.Default)

	// parse flags
	args, err := flagsParser.Parse()
	if err != nil {
		// assert the err to be a flags.Error
		flagError := err.(*goflags.Error)
		if flagError.Type == goflags.ErrHelp {
			// user asked for help on flags.
			// program can exit successfully
			os.Exit(0)
		}
		if flagError.Type == goflags.ErrUnknownFlag {
			fmt.Println("Use --help to view available options.")
			os.Exit(1)
		}
		if flagError.Type == goflags.ErrRequired {
			os.Exit(1)
		}
		fmt.Printf("Error parsing flags: %s\n", err)
		os.Exit(1)
	}

	// error on left-over arguments
	if len(args) > 0 {
		fmt.Printf("Unexpected arguments: %s\nUse --help to view available options.", args)
		os.Exit(1)
	}

	// default ImportPath to pwd when not set
	if len(flags.ImportPath) == 0 {
		pwd, err := os.Getwd()
		if err != nil {
			fmt.Printf("error getting pwd: %s\n", err)
			os.Exit(1)
		}
		verbosef("using pwd as import path\n")
		// find non-absolute path for this pwd
		pkg, err := build.ImportDir(pwd, build.FindOnly)
		if err != nil {
			fmt.Printf("error using current directory as import path: %s\n", err)
			os.Exit(1)
		}
		flags.ImportPath = pkg.ImportPath
		verbosef("using import path: %s\n", flags.ImportPath)
		return
	}
}
<|endoftext|>


<commit_before>package chyle

import (
	"net/http"
)

// jiraIssueDecorator fetch data using jira issue api
type jiraIssueDecorator struct {
	client http.Client
}

// decorate fetch remote jira service if a jiraIssueId is defined to fetch issue datas
func (j jiraIssueDecorator) decorate(commitMap *map[string]interface{}) (*map[string]interface{}, error) {
	var ID string

	if data, ok := (*commitMap)["jiraIssueId"]; true {
		if !ok {
			return commitMap, nil
		}

		if data, ok := data.(string); ok {
			ID = data
		}
	}

	req, err := http.NewRequest("GET", chyleConfig.DECORATORS.JIRA.CREDENTIALS.URL+"/rest/api/2/issue/"+ID, nil)

	if err != nil {
		return commitMap, err
	}

	req.SetBasicAuth(chyleConfig.DECORATORS.JIRA.CREDENTIALS.USERNAME, chyleConfig.DECORATORS.JIRA.CREDENTIALS.PASSWORD)
	req.Header.Set("Content-Type", "application/json")

	return decorateMapFromJSONResponse(&j.client, req, chyleConfig.DECORATORS.JIRA.KEYS, commitMap)
}

// buildJiraIssueDecorator create a new jira ticket decorator
func buildJiraIssueDecorator() decorater {
	return jiraIssueDecorator{http.Client{}}
}
<commit_msg>ref(chyle/decorator_jira_issue) : remove useless check<commit_after>package chyle

import (
	"net/http"
)

// jiraIssueDecorator fetch data using jira issue api
type jiraIssueDecorator struct {
	client http.Client
}

// decorate fetch remote jira service if a jiraIssueId is defined to fetch issue datas
func (j jiraIssueDecorator) decorate(commitMap *map[string]interface{}) (*map[string]interface{}, error) {
	var ID string
	var ok bool

	if ID, ok = (*commitMap)["jiraIssueId"].(string); !ok {
		return commitMap, nil
	}

	req, err := http.NewRequest("GET", chyleConfig.DECORATORS.JIRA.CREDENTIALS.URL+"/rest/api/2/issue/"+ID, nil)

	if err != nil {
		return commitMap, err
	}

	req.SetBasicAuth(chyleConfig.DECORATORS.JIRA.CREDENTIALS.USERNAME, chyleConfig.DECORATORS.JIRA.CREDENTIALS.PASSWORD)
	req.Header.Set("Content-Type", "application/json")

	return decorateMapFromJSONResponse(&j.client, req, chyleConfig.DECORATORS.JIRA.KEYS, commitMap)
}

// buildJiraIssueDecorator create a new jira ticket decorator
func buildJiraIssueDecorator() decorater {
	return jiraIssueDecorator{http.Client{}}
}
<|endoftext|>


<commit_before>package buffalo

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"reflect"
	"strings"

	"github.com/gobuffalo/events"
	gcontext "github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/markbates/inflect"
)

// RouteInfo provides information about the underlying route that
// was built.
type RouteInfo struct {
	Method      string     `json:"method"`
	Path        string     `json:"path"`
	HandlerName string     `json:"handler"`
	PathName    string     `json:"pathName"`
	Aliases     []string   `json:"aliases"`
	MuxRoute    *mux.Route `json:"-"`
	Handler     Handler    `json:"-"`
	App         *App       `json:"-"`
}

// String returns a JSON representation of the RouteInfo
func (ri RouteInfo) String() string {
	b, _ := json.MarshalIndent(ri, "", "  ")
	return string(b)
}

// Alias path patterns to the this route. This is not the
// same as a redirect.
func (ri *RouteInfo) Alias(aliases ...string) *RouteInfo {
	ri.Aliases = append(ri.Aliases, aliases...)
	for _, a := range aliases {
		ri.App.router.Handle(a, ri).Methods(ri.Method)
	}
	return ri
}

// Name allows users to set custom names for the routes.
func (ri *RouteInfo) Name(name string) *RouteInfo {
	routeIndex := -1
	for index, route := range ri.App.Routes() {
		if route.Path == ri.Path && route.Method == ri.Method {
			routeIndex = index
			break
		}
	}

	name = inflect.CamelizeDownFirst(name)

	if !strings.HasSuffix(name, "Path") {
		name = name + "Path"
	}

	ri.PathName = name
	if routeIndex != -1 {
		ri.App.Routes()[routeIndex] = reflect.ValueOf(ri).Interface().(*RouteInfo)
	}

	return ri
}

//BuildPathHelper Builds a routeHelperfunc for a particular RouteInfo
func (ri *RouteInfo) BuildPathHelper() RouteHelperFunc {
	cRoute := ri
	return func(opts map[string]interface{}) (template.HTML, error) {
		pairs := []string{}
		for k, v := range opts {
			pairs = append(pairs, k)
			pairs = append(pairs, fmt.Sprintf("%v", v))
		}

		url, err := cRoute.MuxRoute.URL(pairs...)
		if err != nil {
			return "", fmt.Errorf("missing parameters for %v", cRoute.Path)
		}

		result := url.Path
		result = addExtraParamsTo(result, opts)

		return template.HTML(result), nil
	}
}

func (ri RouteInfo) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	defer gcontext.Clear(req)

	a := ri.App

	c := a.newContext(ri, res, req)
	defer c.Flash().persist(c.Session())

	payload := events.Payload{
		"route":   ri,
		"app":     a,
		"context": c,
	}

	events.EmitPayload(EvtRouteStarted, payload)

	err := a.Middleware.handler(ri)(c)

	if err != nil {
		events.EmitError(EvtRouteErr, err, payload)
		// things have really hit the fan if we're here!!
		a.Logger.Error(err)
		c.Response().WriteHeader(500)
		c.Response().Write([]byte(err.Error()))
	}
	events.EmitPayload(EvtRouteFinished, payload)
}
<commit_msg>don't swollow error on route construction (#1513)<commit_after>package buffalo

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"reflect"
	"strings"

	"github.com/gobuffalo/events"
	gcontext "github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/markbates/inflect"
	"github.com/pkg/errors"
)

// RouteInfo provides information about the underlying route that
// was built.
type RouteInfo struct {
	Method      string     `json:"method"`
	Path        string     `json:"path"`
	HandlerName string     `json:"handler"`
	PathName    string     `json:"pathName"`
	Aliases     []string   `json:"aliases"`
	MuxRoute    *mux.Route `json:"-"`
	Handler     Handler    `json:"-"`
	App         *App       `json:"-"`
}

// String returns a JSON representation of the RouteInfo
func (ri RouteInfo) String() string {
	b, _ := json.MarshalIndent(ri, "", "  ")
	return string(b)
}

// Alias path patterns to the this route. This is not the
// same as a redirect.
func (ri *RouteInfo) Alias(aliases ...string) *RouteInfo {
	ri.Aliases = append(ri.Aliases, aliases...)
	for _, a := range aliases {
		ri.App.router.Handle(a, ri).Methods(ri.Method)
	}
	return ri
}

// Name allows users to set custom names for the routes.
func (ri *RouteInfo) Name(name string) *RouteInfo {
	routeIndex := -1
	for index, route := range ri.App.Routes() {
		if route.Path == ri.Path && route.Method == ri.Method {
			routeIndex = index
			break
		}
	}

	name = inflect.CamelizeDownFirst(name)

	if !strings.HasSuffix(name, "Path") {
		name = name + "Path"
	}

	ri.PathName = name
	if routeIndex != -1 {
		ri.App.Routes()[routeIndex] = reflect.ValueOf(ri).Interface().(*RouteInfo)
	}

	return ri
}

//BuildPathHelper Builds a routeHelperfunc for a particular RouteInfo
func (ri *RouteInfo) BuildPathHelper() RouteHelperFunc {
	cRoute := ri
	return func(opts map[string]interface{}) (template.HTML, error) {
		pairs := []string{}
		for k, v := range opts {
			pairs = append(pairs, k)
			pairs = append(pairs, fmt.Sprintf("%v", v))
		}

		url, err := cRoute.MuxRoute.URL(pairs...)
		if err != nil {
			return "", errors.Wrapf(err, "missing parameters for %v", cRoute.Path)
		}

		result := url.Path
		result = addExtraParamsTo(result, opts)

		return template.HTML(result), nil
	}
}

func (ri RouteInfo) ServeHTTP(res http.ResponseWriter, req *http.Request) {
	defer gcontext.Clear(req)

	a := ri.App

	c := a.newContext(ri, res, req)
	defer c.Flash().persist(c.Session())

	payload := events.Payload{
		"route":   ri,
		"app":     a,
		"context": c,
	}

	events.EmitPayload(EvtRouteStarted, payload)

	err := a.Middleware.handler(ri)(c)

	if err != nil {
		events.EmitError(EvtRouteErr, err, payload)
		// things have really hit the fan if we're here!!
		a.Logger.Error(err)
		c.Response().WriteHeader(500)
		c.Response().Write([]byte(err.Error()))
	}
	events.EmitPayload(EvtRouteFinished, payload)
}
<|endoftext|>


<commit_before>package layers

import (
	"archive/tar"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"registry/logger"
	"sort"
	"strings"
)

const TAR_FILES_INFO_SIZE = 8

type TarError string

func (e TarError) Error() string {
	return string(e)
}

type TarInfo struct {
	TarSum       *TarSum
	TarFilesInfo *TarFilesInfo
	Error        error
}

func NewTarInfo(sumSeed []byte) *TarInfo {
	return &TarInfo{
		TarSum:       NewTarSum(sumSeed),
		TarFilesInfo: NewTarFilesInfo(),
		Error:        nil,
	}
}

func (t *TarInfo) Load(file io.ReadSeeker) {
	var reader *tar.Reader
	file.Seek(0, 0)
	gzipReader, err := gzip.NewReader(file)
	if err != nil {
		// likely not a gzip compressed file
		file.Seek(0, 0)
		reader = tar.NewReader(file)
	} else {
		reader = tar.NewReader(gzipReader)
	}
	for {
		header, err := reader.Next()
		if err == io.EOF {
			// end of tar file
			break
		} else if err != nil {
			// error occured
			logger.Debug("[TarInfoLoad] Error when reading tar stream tarsum. Disabling TarSum, TarFilesInfo. Error: %s", err.Error())
			t.Error = TarError(err.Error())
			return
		}
		t.TarSum.Append(header, reader)
		t.TarFilesInfo.Append(header)
	}
}

type TarSum struct {
	seed   []byte
	hashes []string
}

func NewTarSum(seed []byte) *TarSum {
	return (&TarSum{}).init(seed)
}

func (t *TarSum) init(seed []byte) *TarSum {
	t.seed = seed
	t.hashes = []string{}
	return t
}

func (t *TarSum) Append(header *tar.Header, reader io.Reader) {
	headerStr := "name" + header.Name
	headerStr += fmt.Sprintf("mode%d", header.Mode)
	headerStr += fmt.Sprintf("uid%d", header.Uid)
	headerStr += fmt.Sprintf("gid%d", header.Gid)
	headerStr += fmt.Sprintf("size%d", header.Size)
	headerStr += fmt.Sprintf("mtime%d", header.ModTime.UTC().Unix())
	headerStr += fmt.Sprintf("typeflag%c", header.Typeflag)
	headerStr += "linkname" + header.Linkname
	headerStr += "uname" + header.Uname
	headerStr += "gname" + header.Gname
	headerStr += fmt.Sprintf("devmajor%d", header.Devmajor)
	headerStr += fmt.Sprintf("devminor%d", header.Devminor)
	sha := sha256.New()
	if header.Size > int64(0) {
		sha.Write([]byte(headerStr))
		_, err := io.Copy(sha, reader)
		if err != nil {
			sha.Reset()
			sha.Write([]byte(headerStr))
		}
	} else {
		sha.Write([]byte(headerStr))
	}
	t.hashes = append(t.hashes, hex.EncodeToString(sha.Sum(nil)))
}

func (t *TarSum) Compute() string {
	sort.Strings(t.hashes)
	sha := sha256.New()
	sha.Write(t.seed)
	for _, hash := range t.hashes {
		sha.Write([]byte(hash))
	}
	tarsum := "tarsum+sha256:" + hex.EncodeToString(sha.Sum(nil))
	logger.Debug("[TarSumCompute] return %s", tarsum)
	return tarsum
}

type TarFilesInfo struct {
	headers []*tar.Header
}

func NewTarFilesInfo() *TarFilesInfo {
	return &TarFilesInfo{headers: []*tar.Header{}}
}

func (t *TarFilesInfo) Load(file io.Reader) error {
	reader := tar.NewReader(file)
	for {
		header, err := reader.Next()
		if err == io.EOF {
			// end of tar file
			break
		} else if err != nil {
			// error occured
			return TarError(err.Error())
		}
		t.Append(header)
	}
	return nil
}

func (t *TarFilesInfo) Append(h *tar.Header) {
	t.headers = append(t.headers, h)
}

func (t *TarFilesInfo) Json() ([]byte, error) {
	// convert to the weird tuple docker-registry 0.6.5 uses (why wasn't this just a map!?)
	tupleSlice := [][]interface{}{}
	for _, header := range t.headers {
		filename := header.Name
		isDeleted := false
		if filename == "." {
			filename = "/"
		}
		if strings.HasPrefix(filename, "./") {
			filename = "/" + strings.TrimPrefix(filename, "./")
		}
		if strings.HasPrefix(filename, "/.wh.") {
			filename = "/" + strings.TrimPrefix(filename, "/.wh.")
			isDeleted = true
		}
		if strings.HasPrefix(filename, "/.wh.") {
			// wtf is this!? isn't this redundant!? just copying from docker-registry 0.6.5
			continue
		}

		filetype := "u"
		switch header.Typeflag {
		case tar.TypeReg:
			fallthrough
		case tar.TypeRegA:
			filetype = "f"
		case tar.TypeLink:
			filetype = "l"
		case tar.TypeSymlink:
			filetype = "s"
		case tar.TypeChar:
			filetype = "c"
		case tar.TypeBlock:
			filetype = "b"
		case tar.TypeDir:
			filetype = "d"
		case tar.TypeFifo:
			filetype = "i"
		case tar.TypeCont:
			filetype = "t"
		case tar.TypeGNULongName:
			fallthrough
		case tar.TypeGNULongLink:
			fallthrough
		case 'S': // GNU Sparse (for some reason archive/tar doesn't have a constant for it)
			filetype = string([]byte{header.Typeflag})
		}

		tupleSlice = append(tupleSlice, []interface{}{
			filename,
			filetype,
			isDeleted,
			header.Size,
			header.ModTime.Unix(),
			header.Mode,
			header.Uid,
			header.Gid,
		})
	}
	return json.Marshal(&tupleSlice)
}
<commit_msg>yay! whack-a-mole with commits!<commit_after>package layers

import (
	"archive/tar"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"registry/logger"
	"sort"
	"strings"
)

const TAR_FILES_INFO_SIZE = 8

type TarError string

func (e TarError) Error() string {
	return string(e)
}

type TarInfo struct {
	TarSum       *TarSum
	TarFilesInfo *TarFilesInfo
	Error        error
}

func NewTarInfo(sumSeed []byte) *TarInfo {
	return &TarInfo{
		TarSum:       NewTarSum(sumSeed),
		TarFilesInfo: NewTarFilesInfo(),
		Error:        nil,
	}
}

func (t *TarInfo) Load(file io.ReadSeeker) {
	var reader *tar.Reader
	file.Seek(0, 0)
	gzipReader, err := gzip.NewReader(file)
	if err != nil {
		// likely not a gzip compressed file
		file.Seek(0, 0)
		reader = tar.NewReader(file)
	} else {
		reader = tar.NewReader(gzipReader)
	}
	for {
		header, err := reader.Next()
		if err == io.EOF {
			// end of tar file
			break
		} else if err != nil {
			// error occured
			logger.Debug("[TarInfoLoad] Error when reading tar stream tarsum. Disabling TarSum, TarFilesInfo. Error: %s", err.Error())
			t.Error = TarError(err.Error())
			return
		}
		t.TarSum.Append(header, reader)
		t.TarFilesInfo.Append(header)
	}
}

type TarSum struct {
	seed   []byte
	hashes []string
}

func NewTarSum(seed []byte) *TarSum {
	return (&TarSum{}).init(seed)
}

func (t *TarSum) init(seed []byte) *TarSum {
	t.seed = seed
	t.hashes = []string{}
	return t
}

func (t *TarSum) Append(header *tar.Header, reader io.Reader) {
	headerStr := "name" + header.Name
	if header.Typeflag == tar.TypeDir && !strings.HasSuffix(headerStr, "/") {
		headerStr += "/"
	}
	headerStr += fmt.Sprintf("mode%d", header.Mode)
	headerStr += fmt.Sprintf("uid%d", header.Uid)
	headerStr += fmt.Sprintf("gid%d", header.Gid)
	headerStr += fmt.Sprintf("size%d", header.Size)
	headerStr += fmt.Sprintf("mtime%d", header.ModTime.UTC().Unix())
	headerStr += fmt.Sprintf("typeflag%c", header.Typeflag)
	headerStr += "linkname" + header.Linkname
	headerStr += "uname" + header.Uname
	headerStr += "gname" + header.Gname
	headerStr += fmt.Sprintf("devmajor%d", header.Devmajor)
	headerStr += fmt.Sprintf("devminor%d", header.Devminor)
	sha := sha256.New()
	if header.Size > int64(0) {
		_, err := io.Copy(sha, reader)
		if err != nil {
			sha.Reset()
			sha.Write([]byte(headerStr))
		}
	} else {
		sha.Write([]byte(headerStr))
	}
	t.hashes = append(t.hashes, hex.EncodeToString(sha.Sum(nil)))
}

func (t *TarSum) Compute() string {
	sort.Strings(t.hashes)
	sha := sha256.New()
	sha.Write(t.seed)
	for _, hash := range t.hashes {
		sha.Write([]byte(hash))
	}
	tarsum := hex.EncodeToString(sha.Sum(nil))
	logger.Debug("[TarSumCompute] return %s", tarsum)
	return tarsum
}

type TarFilesInfo struct {
	headers []*tar.Header
}

func NewTarFilesInfo() *TarFilesInfo {
	return &TarFilesInfo{headers: []*tar.Header{}}
}

func (t *TarFilesInfo) Load(file io.Reader) error {
	reader := tar.NewReader(file)
	for {
		header, err := reader.Next()
		if err == io.EOF {
			// end of tar file
			break
		} else if err != nil {
			// error occured
			return TarError(err.Error())
		}
		t.Append(header)
	}
	return nil
}

func (t *TarFilesInfo) Append(h *tar.Header) {
	t.headers = append(t.headers, h)
}

func (t *TarFilesInfo) Json() ([]byte, error) {
	// convert to the weird tuple docker-registry 0.6.5 uses (why wasn't this just a map!?)
	tupleSlice := [][]interface{}{}
	for _, header := range t.headers {
		filename := header.Name
		isDeleted := false
		if filename == "." {
			filename = "/"
		}
		if strings.HasPrefix(filename, "./") {
			filename = "/" + strings.TrimPrefix(filename, "./")
		}
		if strings.HasPrefix(filename, "/.wh.") {
			filename = "/" + strings.TrimPrefix(filename, "/.wh.")
			isDeleted = true
		}
		if strings.HasPrefix(filename, "/.wh.") {
			// wtf is this!? isn't this redundant!? just copying from docker-registry 0.6.5
			continue
		}

		filetype := "u"
		switch header.Typeflag {
		case tar.TypeReg:
			fallthrough
		case tar.TypeRegA:
			filetype = "f"
		case tar.TypeLink:
			filetype = "l"
		case tar.TypeSymlink:
			filetype = "s"
		case tar.TypeChar:
			filetype = "c"
		case tar.TypeBlock:
			filetype = "b"
		case tar.TypeDir:
			filetype = "d"
		case tar.TypeFifo:
			filetype = "i"
		case tar.TypeCont:
			filetype = "t"
		case tar.TypeGNULongName:
			fallthrough
		case tar.TypeGNULongLink:
			fallthrough
		case 'S': // GNU Sparse (for some reason archive/tar doesn't have a constant for it)
			filetype = string([]byte{header.Typeflag})
		}

		tupleSlice = append(tupleSlice, []interface{}{
			filename,
			filetype,
			isDeleted,
			header.Size,
			header.ModTime.Unix(),
			header.Mode,
			header.Uid,
			header.Gid,
		})
	}
	return json.Marshal(&tupleSlice)
}
<|endoftext|>