<commit_before>// Copyright 2014 Markus Dittrich. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// nutmeg is a unit and regression test framework for MCell
package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

// global settings
// NOTE: With exception of rng these should eventually come from a settings file
var testNames []string
var mcellPath string
var testDir string
var rng *rand.Rand

// command line flags
var listTestsFlag bool
var runTestsFlag bool
var cleanTestOutput bool
var testSelection string
var numSimJobs int
var numTestJobs int

// initialize list of available unit tests
func init() {
	flag.BoolVar(&listTestsFlag, "l", false, "show available test cases")
	flag.BoolVar(&runTestsFlag, "r", false, "run specified tests")
	flag.BoolVar(&cleanTestOutput, "c", false, "clean temporary test data")
	flag.StringVar(&testSelection, "s", "all", "select test to run (default: all tests)")
	flag.IntVar(&numSimJobs, "n", 2, "number of concurrent simulation jobs (default: 2)")
	flag.IntVar(&numTestJobs, "m", 2, "number of concurrent test jobs (default: 2)")

	rng = rand.New(rand.NewSource(time.Now().UnixNano()))
	testDir = "/Users/markus/programming/go/src/github.com/haskelladdict/nutmeg/tests/"
	mcellPath = "/Users/markus/programming/c/mcell/mcell-trunk/build/mcell"

	testNames = []string{
		"remove_per_species_list_from_ht",
		"orient_flip_flip_rxn",
		"coincident_surfaces",
		"rx_flip_flip",
		"rx_dissociate_inwards",
		"misreporting_rxn_products",
		"volvol_crash",
		"find_corresponding_region",
		"incorrect_times_in_chkpt",
		"counting_crashes_on_coincident_wall",
		"quoted_tickmark_counts_parse_error",
		"no_waypoints_counting_fail",
		"find_corresponding_region_error_crash_1",
		"find_corresponding_region_error_crash_2",
		"mol_grid_grid_crash",
		"mol_surf_grid_crash",
		"uninstantiated_reference_crash_1",
		"uninstantiated_reference_crash_2",
		"enclosed_surfmol_miscount",
		"reaction_null_products_crash",
		"enclosed_meshes_with_different_properties",
		"rx_reflective_surface_bug",
		"walls_overlap_two_objects",
		"walls_overlap_partial",
		"walls_overlap_complete",
		"walls_overlap_share_edge",
		"walls_coincident",
		"unimolecular_reaction_after_chkpt",
		"unimolecular_FOREVER"}
}

// main routine
func main() {

	flag.Parse()

	switch {
	case listTestsFlag:
		fmt.Println("Available tests:")
		fmt.Println("----------------")
		for i, t := range testNames {
			fmt.Printf("[%d] %-20s\n", i, t)
		}

	case cleanTestOutput:
		tests := extractTestCases(testSelection)
		if err := cleanOutput(tests); err != nil {
			log.Fatal(err)
		}

	case runTestsFlag:
		tests := extractTestCases(testSelection)
		runTests(tests)

	default:
		flag.PrintDefaults()
	}
}

// extractTestCases parses the test selection string and assembles the list
// of requested test cases as fully qualified paths.
// NOTE: The form of the selection string is of the form
//              1,2,5:10,55
//
// Here, each number or range of numbers refers to indexed test cases as
// provided by the -s commandline flag.
// A special case is "all" which refers to all tests.
func extractTestCases(selection string) []string {

	var selectedNames []string
	if selection == "all" {
		selectedNames = testNames
	} else {
		for _, s := range strings.Split(selection, ",") {
			item := strings.TrimSpace(s)

			var items []int
			var err error
			if strings.Contains(item, ":") {
				if items, err = convertRangeToList(item); err != nil {
					log.Printf(fmt.Sprint(err))
					continue
				}
			} else {
				i, err := strconv.Atoi(item)
				if err != nil {
					log.Printf("invalid test selection %s ... skipping", item)
					continue
				}
				items = []int{i}
			}

			for _, i := range items {
				if i < 0 || i >= len(testNames) {
					log.Printf("test selection %d out of valid range (%d-%d) ... skipping",
						i, 0, len(testNames)-1)
					continue
				}
				selectedNames = append(selectedNames, testNames[i])
			}
		}
	}

	testPaths := make([]string, len(selectedNames))
	for i, name := range selectedNames {
		testPaths[i] = filepath.Join(testDir, name)
	}
	return testPaths
}

// convertRangeToList converts a single string containing a range statement
// of the form "4:9" into an explicit integer list describing the
// range [4, 5, 6, 7, 8, 9]
func convertRangeToList(rangeStatement string) ([]int, error) {

	rangeEndpoints := strings.Split(rangeStatement, ":")
	if len(rangeEndpoints) != 2 {
		return nil, errors.New(
			fmt.Sprintf("range selection %s not valid", rangeStatement))
	}

	var rangeBegin int
	var err error
	if rangeBegin, err = strconv.Atoi(rangeEndpoints[0]); err != nil {
		return nil, errors.New(
			fmt.Sprintf("invalid range start character %s", rangeEndpoints[0]))
	}

	var rangeEnd int
	if rangeEnd, err = strconv.Atoi(rangeEndpoints[1]); err != nil {
		return nil, errors.New(
			fmt.Sprintf("invalid range end character %s", rangeEndpoints[1]))
	}

	var newRange []int
	for i := rangeBegin; i <= rangeEnd; i++ {
		newRange = append(newRange, i)
	}

	return newRange, nil
}
<commit_msg>Added ability to auto assemble list of test cases.<commit_after>// Copyright 2014 Markus Dittrich. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// nutmeg is a unit and regression test framework for MCell
package main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"
)

// global settings
// NOTE: With exception of rng these should eventually come from a settings file
var testNames []string
var mcellPath string
var testDir string
var rng *rand.Rand

// command line flags
var listTestsFlag bool
var runTestsFlag bool
var cleanTestOutput bool
var testSelection string
var numSimJobs int
var numTestJobs int

// initialize list of available unit tests
func init() {
	flag.BoolVar(&listTestsFlag, "l", false, "show available test cases")
	flag.BoolVar(&runTestsFlag, "r", false, "run specified tests")
	flag.BoolVar(&cleanTestOutput, "c", false, "clean temporary test data")
	flag.StringVar(&testSelection, "s", "all", "select test to run (default: all tests)")
	flag.IntVar(&numSimJobs, "n", 2, "number of concurrent simulation jobs (default: 2)")
	flag.IntVar(&numTestJobs, "m", 2, "number of concurrent test jobs (default: 2)")

	rng = rand.New(rand.NewSource(time.Now().UnixNano()))
	testDir = "/Users/markus/programming/go/src/github.com/haskelladdict/nutmeg/tests/"
	mcellPath = "/Users/markus/programming/c/mcell/mcell-trunk/build/mcell"
}

// main routine
func main() {

	testNames, err := gatherTests(testDir)
	if err != nil {
		log.Fatal("Could not determine list of available test cases")
	}

	flag.Parse()

	switch {
	case listTestsFlag:
		fmt.Println("Available tests:")
		fmt.Println("----------------")
		for i, t := range testNames {
			fmt.Printf("[%d] %-20s\n", i, t)
		}

	case cleanTestOutput:
		tests := extractTestCases(testSelection)
		if err := cleanOutput(tests); err != nil {
			log.Fatal(err)
		}

	case runTestsFlag:
		tests := extractTestCases(testSelection)
		runTests(tests)

	default:
		flag.PrintDefaults()
	}
}

// extractTestCases parses the test selection string and assembles the list
// of requested test cases as fully qualified paths.
// NOTE: The form of the selection string is of the form
//              1,2,5:10,55
//
// Here, each number or range of numbers refers to indexed test cases as
// provided by the -s commandline flag.
// A special case is "all" which refers to all tests.
func extractTestCases(selection string) []string {

	var selectedNames []string
	if selection == "all" {
		selectedNames = testNames
	} else {
		for _, s := range strings.Split(selection, ",") {
			item := strings.TrimSpace(s)

			var items []int
			var err error
			if strings.Contains(item, ":") {
				if items, err = convertRangeToList(item); err != nil {
					log.Printf(fmt.Sprint(err))
					continue
				}
			} else {
				i, err := strconv.Atoi(item)
				if err != nil {
					log.Printf("invalid test selection %s ... skipping", item)
					continue
				}
				items = []int{i}
			}

			for _, i := range items {
				if i < 0 || i >= len(testNames) {
					log.Printf("test selection %d out of valid range (%d-%d) ... skipping",
						i, 0, len(testNames)-1)
					continue
				}
				selectedNames = append(selectedNames, testNames[i])
			}
		}
	}

	testPaths := make([]string, len(selectedNames))
	for i, name := range selectedNames {
		testPaths[i] = filepath.Join(testDir, name)
	}
	return testPaths
}

// convertRangeToList converts a single string containing a range statement
// of the form "4:9" into an explicit integer list describing the
// range [4, 5, 6, 7, 8, 9]
func convertRangeToList(rangeStatement string) ([]int, error) {

	rangeEndpoints := strings.Split(rangeStatement, ":")
	if len(rangeEndpoints) != 2 {
		return nil, errors.New(
			fmt.Sprintf("range selection %s not valid", rangeStatement))
	}

	var rangeBegin int
	var err error
	if rangeBegin, err = strconv.Atoi(rangeEndpoints[0]); err != nil {
		return nil, errors.New(
			fmt.Sprintf("invalid range start character %s", rangeEndpoints[0]))
	}

	var rangeEnd int
	if rangeEnd, err = strconv.Atoi(rangeEndpoints[1]); err != nil {
		return nil, errors.New(
			fmt.Sprintf("invalid range end character %s", rangeEndpoints[1]))
	}

	var newRange []int
	for i := rangeBegin; i <= rangeEnd; i++ {
		newRange = append(newRange, i)
	}

	return newRange, nil
}

// gatherTests determines the list of available test cases and orders them
// alphabetically
func gatherTests(testDir string) ([]string, error) {
	dirContent, err := ioutil.ReadDir(testDir)
	if err != nil {
		return nil, err
	}

	var tests []string
	for _, c := range dirContent {
		if c.IsDir() {
			tests = append(tests, c.Name())
		}
	}
	sort.Strings(tests)

	return tests, nil
}
<|endoftext|>


<commit_before>package coordinate

import (
	"testing"
	"time"
)

func TestPerformance_Line(t *testing.T) {
	const spacing = 10*time.Millisecond
	const nodes, cycles = 10, 1000
	config := DefaultConfig()
	clients, err := GenerateClients(nodes, config)
	if err != nil {
		t.Fatal(err)
	}
	truth := GenerateLine(nodes, spacing)
	Simulate(clients, truth, cycles, nil)
	stats := Evaluate(clients, truth)
	if stats.ErrorAvg > 0.004 || stats.ErrorMax > 0.015 {
		t.Fatalf("performance stats are out of spec: %v", stats)
	}
}

func TestPerformance_Grid(t *testing.T) {
	const spacing = 10*time.Millisecond
	const nodes, cycles = 25, 1000
	config := DefaultConfig()
	clients, err := GenerateClients(nodes, config)
	if err != nil {
		t.Fatal(err)
	}
	truth := GenerateGrid(nodes, spacing)
	Simulate(clients, truth, cycles, nil)
	stats := Evaluate(clients, truth)
	if stats.ErrorAvg > 0.005 || stats.ErrorMax > 0.051 {
		t.Fatalf("performance stats are out of spec: %v", stats)
	}
}

func TestPerformance_Split(t *testing.T) {
	const lan, wan = 1*time.Millisecond, 10*time.Millisecond
	const nodes, cycles = 25, 1000
	config := DefaultConfig()
	clients, err := GenerateClients(nodes, config)
	if err != nil {
		t.Fatal(err)
	}
	truth := GenerateSplit(nodes, lan, wan)
	Simulate(clients, truth, cycles, nil)
	stats := Evaluate(clients, truth)
	if stats.ErrorAvg > 0.044 || stats.ErrorMax > 0.343 {
		t.Fatalf("performance stats are out of spec: %v", stats)
	}
}

func TestPerformance_Random(t *testing.T) {
	const mean, deviation = 100*time.Millisecond, 10*time.Millisecond
	const nodes, cycles = 25, 1000
	config := DefaultConfig()
	clients, err := GenerateClients(nodes, config)
	if err != nil {
		t.Fatal(err)
	}
	truth := GenerateRandom(nodes, mean, deviation)
	Simulate(clients, truth, cycles, nil)
	stats := Evaluate(clients, truth)
	if stats.ErrorAvg > 0.079 || stats.ErrorMax > 0.363 {
		t.Fatalf("performance stats are out of spec: %v", stats)
	}
}
<commit_msg>Tightens up thresholds in performance tests.<commit_after>package coordinate

import (
	"testing"
	"time"
)

func TestPerformance_Line(t *testing.T) {
	const spacing = 10*time.Millisecond
	const nodes, cycles = 10, 1000
	config := DefaultConfig()
	clients, err := GenerateClients(nodes, config)
	if err != nil {
		t.Fatal(err)
	}
	truth := GenerateLine(nodes, spacing)
	Simulate(clients, truth, cycles, nil)
	stats := Evaluate(clients, truth)
	if stats.ErrorAvg > 0.0012 || stats.ErrorMax > 0.0055 {
		t.Fatalf("performance stats are out of spec: %v", stats)
	}
}

func TestPerformance_Grid(t *testing.T) {
	const spacing = 10*time.Millisecond
	const nodes, cycles = 25, 1000
	config := DefaultConfig()
	clients, err := GenerateClients(nodes, config)
	if err != nil {
		t.Fatal(err)
	}
	truth := GenerateGrid(nodes, spacing)
	Simulate(clients, truth, cycles, nil)
	stats := Evaluate(clients, truth)
	if stats.ErrorAvg > 0.0014 || stats.ErrorMax > 0.0158 {
		t.Fatalf("performance stats are out of spec: %v", stats)
	}
}

func TestPerformance_Split(t *testing.T) {
	const lan, wan = 1*time.Millisecond, 10*time.Millisecond
	const nodes, cycles = 25, 1000
	config := DefaultConfig()
	clients, err := GenerateClients(nodes, config)
	if err != nil {
		t.Fatal(err)
	}
	truth := GenerateSplit(nodes, lan, wan)
	Simulate(clients, truth, cycles, nil)
	stats := Evaluate(clients, truth)
	if stats.ErrorAvg > 0.000057 || stats.ErrorMax > 0.00035 {
		t.Fatalf("performance stats are out of spec: %v", stats)
	}
}

func TestPerformance_Random(t *testing.T) {
	const mean, deviation = 100*time.Millisecond, 10*time.Millisecond
	const nodes, cycles = 25, 1000
	config := DefaultConfig()
	clients, err := GenerateClients(nodes, config)
	if err != nil {
		t.Fatal(err)
	}
	truth := GenerateRandom(nodes, mean, deviation)
	Simulate(clients, truth, cycles, nil)
	stats := Evaluate(clients, truth)
	if stats.ErrorAvg > 0.046 || stats.ErrorMax > 0.184 {
		t.Fatalf("performance stats are out of spec: %v", stats)
	}
}
<|endoftext|>


<commit_before>/*
Linthub runs golint on a GitHub repository.
*/
package main

import (
	"bytes"
	"encoding/base64"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"

	"code.google.com/p/goauth2/oauth"
	"github.com/golang/lint"
	"github.com/google/go-github/github"
)

var (
	personalAccessTokenFile = flag.String("personal_access_token_file", filepath.Join(os.Getenv("HOME"), ".linthub-token"), "a file to load a GitHub personal access token from")
	rev = flag.String("rev", "master", "revision of the repo to check")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "usage: linthub [options] owner/repo")
		flag.PrintDefaults()
	}
	flag.Parse()

	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(1)
	}
	parts := strings.Split(flag.Arg(0), "/")
	if len(parts) != 2 {
		flag.Usage()
		os.Exit(1)
	}
	owner, repo := parts[0], parts[1]

	// Either load the personal access token (and set httpClient accordingly),
	// or leave httpClient as nil to get an unauthenticated client.
	var httpClient *http.Client
	pat, err := ioutil.ReadFile(*personalAccessTokenFile)
	if err == nil {
		// security check
		fi, err := os.Stat(*personalAccessTokenFile)
		if err != nil {
			log.Fatalf("os.Stat(%q): %v", *personalAccessTokenFile, err)
		}
		if fi.Mode() & 0077 != 0 { // check that no group/world perm bits are set
			log.Fatalf("%s is too accessible; run `chmod go= %s` to fix", *personalAccessTokenFile, *personalAccessTokenFile)
		}

		tr := &oauth.Transport{
			Token: &oauth.Token{
				AccessToken: string(bytes.TrimSpace(pat)),
			},
		}
		httpClient = tr.Client()
	}

	client := github.NewClient(httpClient)
	client.UserAgent = "linthub"

	commit, _, err := client.Repositories.GetCommit(owner, repo, *rev)
	if err != nil {
		log.Fatalf("GetCommit(%q, %q, %q): %v", owner, repo, *rev, err)
	}
	sha1 := *commit.SHA
	log.Printf("%s/%s: rev %q is %s", owner, repo, *rev, sha1)

	tree, _, err := client.Git.GetTree(owner, repo, sha1, true)
	if err != nil {
		log.Fatalf("GetTree: %v", err)
	}
	log.Printf("%s/%s: found %d tree entries", owner, repo, len(tree.Entries))

	var (
		linter = new(lint.Linter)

		wg       sync.WaitGroup
		problems struct {
			sync.Mutex
			list []string
		}
	)
	addProblem := func(s string) {
		problems.Lock()
		problems.list = append(problems.list, s)
		problems.Unlock()
	}

	nGo := 0
	for _, ent := range tree.Entries {
		if ent.SHA == nil || ent.Path == nil || ent.Size == nil {
			continue
		}
		sha1, path, size := *ent.SHA, *ent.Path, *ent.Size
		if !strings.HasSuffix(path, ".go") {
			continue
		}
		if size > 100<<10 {
			log.Printf("Skipping %s because it is too big: %d > %d", path, size, 100<<10)
			continue
		}
		//log.Printf("+ %s (%d bytes)", path, size)

		wg.Add(1)
		nGo++
		go func() {
			defer wg.Done()

			src, err := getBlob(client, owner, repo, sha1)
			if err != nil {
				log.Printf("Getting blob for %s: %v", path, err)
				return
			}
			ps, err := linter.Lint(path, src)
			if err != nil {
				log.Printf("Linting %s: %v", path, err)
				return
			}
			for _, p := range ps {
				if p.Confidence < 0.8 { // TODO: flag
					continue
				}
				addProblem(fmt.Sprintf("%s:%v: %s", path, p.Position, p.Text))
			}
		}()
	}
	wg.Wait()

	sort.Strings(problems.list)
	for _, p := range problems.list {
		fmt.Println(p)
	}
	log.Printf("wow, there were %d problems in %d Go source files!", len(problems.list), nGo)
}

func getBlob(client *github.Client, owner, repo, sha1 string) ([]byte, error) {
	blob, _, err := client.Git.GetBlob(owner, repo, sha1)
	if err != nil {
		return nil, err
	}
	content := *blob.Content
	switch *blob.Encoding {
	case "base64":
		return base64.StdEncoding.DecodeString(content)
	default:
		return nil, fmt.Errorf("unknown blob encoding %q", *blob.Encoding)
	}
}
<commit_msg>gofmt<commit_after>/*
Linthub runs golint on a GitHub repository.
*/
package main

import (
	"bytes"
	"encoding/base64"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"

	"code.google.com/p/goauth2/oauth"
	"github.com/golang/lint"
	"github.com/google/go-github/github"
)

var (
	personalAccessTokenFile = flag.String("personal_access_token_file", filepath.Join(os.Getenv("HOME"), ".linthub-token"), "a file to load a GitHub personal access token from")
	rev                     = flag.String("rev", "master", "revision of the repo to check")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "usage: linthub [options] owner/repo")
		flag.PrintDefaults()
	}
	flag.Parse()

	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(1)
	}
	parts := strings.Split(flag.Arg(0), "/")
	if len(parts) != 2 {
		flag.Usage()
		os.Exit(1)
	}
	owner, repo := parts[0], parts[1]

	// Either load the personal access token (and set httpClient accordingly),
	// or leave httpClient as nil to get an unauthenticated client.
	var httpClient *http.Client
	pat, err := ioutil.ReadFile(*personalAccessTokenFile)
	if err == nil {
		// security check
		fi, err := os.Stat(*personalAccessTokenFile)
		if err != nil {
			log.Fatalf("os.Stat(%q): %v", *personalAccessTokenFile, err)
		}
		if fi.Mode()&0077 != 0 { // check that no group/world perm bits are set
			log.Fatalf("%s is too accessible; run `chmod go= %s` to fix", *personalAccessTokenFile, *personalAccessTokenFile)
		}

		tr := &oauth.Transport{
			Token: &oauth.Token{
				AccessToken: string(bytes.TrimSpace(pat)),
			},
		}
		httpClient = tr.Client()
	}

	client := github.NewClient(httpClient)
	client.UserAgent = "linthub"

	commit, _, err := client.Repositories.GetCommit(owner, repo, *rev)
	if err != nil {
		log.Fatalf("GetCommit(%q, %q, %q): %v", owner, repo, *rev, err)
	}
	sha1 := *commit.SHA
	log.Printf("%s/%s: rev %q is %s", owner, repo, *rev, sha1)

	tree, _, err := client.Git.GetTree(owner, repo, sha1, true)
	if err != nil {
		log.Fatalf("GetTree: %v", err)
	}
	log.Printf("%s/%s: found %d tree entries", owner, repo, len(tree.Entries))

	var (
		linter = new(lint.Linter)

		wg       sync.WaitGroup
		problems struct {
			sync.Mutex
			list []string
		}
	)
	addProblem := func(s string) {
		problems.Lock()
		problems.list = append(problems.list, s)
		problems.Unlock()
	}

	nGo := 0
	for _, ent := range tree.Entries {
		if ent.SHA == nil || ent.Path == nil || ent.Size == nil {
			continue
		}
		sha1, path, size := *ent.SHA, *ent.Path, *ent.Size
		if !strings.HasSuffix(path, ".go") {
			continue
		}
		if size > 100<<10 {
			log.Printf("Skipping %s because it is too big: %d > %d", path, size, 100<<10)
			continue
		}
		//log.Printf("+ %s (%d bytes)", path, size)

		wg.Add(1)
		nGo++
		go func() {
			defer wg.Done()

			src, err := getBlob(client, owner, repo, sha1)
			if err != nil {
				log.Printf("Getting blob for %s: %v", path, err)
				return
			}
			ps, err := linter.Lint(path, src)
			if err != nil {
				log.Printf("Linting %s: %v", path, err)
				return
			}
			for _, p := range ps {
				if p.Confidence < 0.8 { // TODO: flag
					continue
				}
				addProblem(fmt.Sprintf("%s:%v: %s", path, p.Position, p.Text))
			}
		}()
	}
	wg.Wait()

	sort.Strings(problems.list)
	for _, p := range problems.list {
		fmt.Println(p)
	}
	log.Printf("wow, there were %d problems in %d Go source files!", len(problems.list), nGo)
}

func getBlob(client *github.Client, owner, repo, sha1 string) ([]byte, error) {
	blob, _, err := client.Git.GetBlob(owner, repo, sha1)
	if err != nil {
		return nil, err
	}
	content := *blob.Content
	switch *blob.Encoding {
	case "base64":
		return base64.StdEncoding.DecodeString(content)
	default:
		return nil, fmt.Errorf("unknown blob encoding %q", *blob.Encoding)
	}
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
	"log"
	"os"
	"strings"
)

const (
	LVerbose = iota + 1
	LDebug
	LInfo
	LWarn
	LError
	LAssert
)

type Logger struct {
	l         *log.Logger
	callDepth int
	maxLevel  int
}

var defLogger *Logger

func NewLogger() *Logger {
	return &Logger{
		l:         log.New(os.Stderr, "", log.Ltime|log.Ldate|log.Lshortfile),
		callDepth: 3,
		maxLevel:  0,
	}
}

func (p *Logger) Verbose(tag string, v ...interface{}) {
	p.OutPutStd(LVerbose, tag, v...)
}
func (p *Logger) Debug(tag string, v ...interface{}) {
	p.OutPutStd(LDebug, tag, v...)
}
func (p *Logger) Info(tag string, v ...interface{}) {
	p.OutPutStd(LInfo, tag, v...)
}
func (p *Logger) Warn(tag string, v ...interface{}) {
	p.OutPutStd(LWarn, tag, v...)
}
func (p *Logger) Error(tag string, v ...interface{}) {
	p.OutPutStd(LError, tag, v...)
}
func (p *Logger) Assert(tag string, v ...interface{}) {
	p.OutPutStd(LAssert, tag, v...)
}

func (p *Logger) OutPutStd(level int, tag string, v ...interface{}) {
	if p.maxLevel < level {
		l := ""
		switch level {
		case LVerbose:
			l = "[V]"
		case LInfo:
			l = "[I]"
		case LDebug:
			l = "[D]"
		case LWarn:
			l = "[W]"
		case LError:
			l = "[E]"
		case LAssert:
			l = "[A]"
		}
		tag = "[" + tag + "] "
		p.l.SetPrefix(l + " ")

		var arg []interface{}
		var format string

		// 支持format
		if len(v) >= 2 {
			if _format, ok := v[0].(string); ok {
				if strings.Contains(_format, "%") {
					format = _format
					arg = v[1:]
				}
			}
		}

		if format == "" {
			arg = append([]interface{}{tag}, v...)
			format = strings.Repeat("%+v ", len(arg))
		}

		p.l.Output(p.callDepth, fmt.Sprintf(format, arg...))
	}
}

func (p *Logger) SetLogLevel(level int) {
	p.maxLevel = level
}

func (p *Logger) SetCallDepth(d int) {
	p.callDepth = d
}

func Verbose(tag string, v ...interface{}) {
	defLogger.Verbose(tag, v...)
}
func Debug(tag string, v ...interface{}) {
	defLogger.OutPutStd(LDebug, tag, v...)
}
func Info(tag string, v ...interface{}) {
	defLogger.OutPutStd(LInfo, tag, v...)
}
func Warn(tag string, v ...interface{}) {
	defLogger.OutPutStd(LWarn, tag, v...)
}
func Error(tag string, v ...interface{}) {
	defLogger.OutPutStd(LError, tag, v...)
}
func Assert(tag string, v ...interface{}) {
	defLogger.OutPutStd(LAssert, tag, v...)
}

func SetLogLevel(level int) {
	defLogger.SetLogLevel(level)
}

func SetCallDepth(d int) {
	defLogger.SetLogLevel(d)
}

func init() {
	defLogger = NewLogger()
}
<commit_msg>fix log<commit_after>package log

import (
	"fmt"
	"log"
	"os"
	"strings"
)

const (
	LVerbose = iota + 1
	LDebug
	LInfo
	LWarn
	LError
	LAssert
)

type Logger struct {
	l         *log.Logger
	callDepth int
	maxLevel  int
}

var defLogger *Logger

func NewLogger() *Logger {
	return &Logger{
		l:         log.New(os.Stderr, "", log.Ltime|log.Ldate|log.Lshortfile),
		callDepth: 3,
		maxLevel:  0,
	}
}

func (p *Logger) Verbose(tag string, v ...interface{}) {
	p.OutPutStd(LVerbose, tag, v...)
}
func (p *Logger) Debug(tag string, v ...interface{}) {
	p.OutPutStd(LDebug, tag, v...)
}
func (p *Logger) Info(tag string, v ...interface{}) {
	p.OutPutStd(LInfo, tag, v...)
}
func (p *Logger) Warn(tag string, v ...interface{}) {
	p.OutPutStd(LWarn, tag, v...)
}
func (p *Logger) Error(tag string, v ...interface{}) {
	p.OutPutStd(LError, tag, v...)
}
func (p *Logger) Assert(tag string, v ...interface{}) {
	p.OutPutStd(LAssert, tag, v...)
}

func (p *Logger) OutPutStd(level int, tag string, v ...interface{}) {
	if p.maxLevel < level {
		l := ""
		switch level {
		case LVerbose:
			l = "[V]"
		case LInfo:
			l = "[I]"
		case LDebug:
			l = "[D]"
		case LWarn:
			l = "[W]"
		case LError:
			l = "[E]"
		case LAssert:
			l = "[A]"
		}
		tag = "[" + tag + "] "
		p.l.SetPrefix(l + " ")

		var arg []interface{}
		var format string

		// 支持format
		if len(v) >= 2 {
			if _format, ok := v[0].(string); ok {
				if strings.Contains(_format, "%") {
					format = _format
					arg = append([]interface{}{tag}, v[1:]...)
				}
			}
		}

		if format == "" {
			arg = append([]interface{}{tag}, v...)
			format = strings.Repeat("%+v ", len(arg))
		}

		p.l.Output(p.callDepth, fmt.Sprintf(format, arg...))
	}
}

func (p *Logger) SetLogLevel(level int) {
	p.maxLevel = level
}

func (p *Logger) SetCallDepth(d int) {
	p.callDepth = d
}

func Verbose(tag string, v ...interface{}) {
	defLogger.Verbose(tag, v...)
}
func Debug(tag string, v ...interface{}) {
	defLogger.OutPutStd(LDebug, tag, v...)
}
func Info(tag string, v ...interface{}) {
	defLogger.OutPutStd(LInfo, tag, v...)
}
func Warn(tag string, v ...interface{}) {
	defLogger.OutPutStd(LWarn, tag, v...)
}
func Error(tag string, v ...interface{}) {
	defLogger.OutPutStd(LError, tag, v...)
}
func Assert(tag string, v ...interface{}) {
	defLogger.OutPutStd(LAssert, tag, v...)
}

func SetLogLevel(level int) {
	defLogger.SetLogLevel(level)
}

func SetCallDepth(d int) {
	defLogger.SetLogLevel(d)
}

func init() {
	defLogger = NewLogger()
}
<|endoftext|>


<commit_before>/*
 * This file is part of remco.
 * © 2016 The Remco Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package log

import (
	"fmt"
	"os"
	"sync"

	log "github.com/Sirupsen/logrus"
	prefixed "github.com/x-cray/logrus-prefixed-formatter"
)

var logger *log.Entry
var lock sync.RWMutex

func init() {
	host, _ := os.Hostname()
	SetFormatter("text")
	log.SetLevel(log.InfoLevel)
	logger = log.WithFields(log.Fields{
		"prefix":   fmt.Sprintf("%s[%d]", os.Args[0], os.Getpid()),
		"hostname": host,
	})
}

// SetFormatter sets the formatter. Valid formatters are json and text.
func SetFormatter(format string) {
	lock.Lock()
	defer lock.Unlock()
	switch format {
	case "json":
		log.SetFormatter(&log.JSONFormatter{})
	case "text":
		log.SetFormatter(&prefixed.TextFormatter{DisableSorting: true})
	}
}

// SetLevel sets the log level. Valid levels are panic, fatal, error, warn, info and debug.
func SetLevel(level string) error {
	lock.Lock()
	defer lock.Unlock()
	lvl, err := log.ParseLevel(level)
	if err != nil {
		return err
	}
	log.SetLevel(lvl)
	return nil
}

// Debug logs a message with severity DEBUG.
func Debug(v ...interface{}) {
	lock.Lock()
	logger.Debug(v)
	lock.Unlock()
}

// Error logs a message with severity ERROR.
func Error(v ...interface{}) {
	lock.Lock()
	logger.Error(v)
	lock.Unlock()
}

// Fatal logs a message with severity ERROR followed by a call to os.Exit().
func Fatal(v ...interface{}) {
	lock.Lock()
	logger.Fatal(v)
	lock.Unlock()
}

// Info logs a message with severity INFO.
func Info(v ...interface{}) {
	lock.Lock()
	logger.Info(v)
	lock.Unlock()
}

// Warning logs a message with severity WARNING.
func Warning(v ...interface{}) {
	lock.Lock()
	logger.Warning(v)
	lock.Unlock()
}

// WithFields is a Wrapper for logrus.WithFields
func WithFields(fields log.Fields) *log.Entry {
	return logger.WithFields(fields)
}
<commit_msg>added a source field to the log output<commit_after>/*
 * This file is part of remco.
 * © 2016 The Remco Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package log

import (
	"fmt"
	"os"
	"runtime"
	"strings"

	log "github.com/Sirupsen/logrus"
	prefixed "github.com/x-cray/logrus-prefixed-formatter"
)

var logger *log.Entry

func init() {
	host, _ := os.Hostname()
	SetFormatter("text")
	log.SetLevel(log.InfoLevel)
	logger = log.WithFields(log.Fields{
		"prefix":   fmt.Sprintf("%s[%d]", os.Args[0], os.Getpid()),
		"hostname": host,
	})
}

// SetFormatter sets the formatter. Valid formatters are json and text.
func SetFormatter(format string) {
	switch format {
	case "json":
		log.SetFormatter(&log.JSONFormatter{})
	case "text":
		log.SetFormatter(&prefixed.TextFormatter{DisableSorting: true})
	}
}

func withSource(l *log.Entry) *log.Entry {
	_, file, line, ok := runtime.Caller(2)
	if !ok {
		file = "<???>"
		line = 1
	} else {
		slash := strings.LastIndex(file, "/")
		file = file[slash+1:]
	}
	logger := l.WithField("source", fmt.Sprintf("%s:%d", file, line))
	return logger
}

// SetLevel sets the log level. Valid levels are panic, fatal, error, warn, info and debug.
func SetLevel(level string) error {
	lvl, err := log.ParseLevel(level)
	if err != nil {
		return err
	}
	log.SetLevel(lvl)
	return nil
}

// Debug logs a message with severity DEBUG.
func Debug(v ...interface{}) {
	withSource(logger).Debug(v)
}

// Error logs a message with severity ERROR.
func Error(v ...interface{}) {
	withSource(logger).Error(v)
}

// Fatal logs a message with severity ERROR followed by a call to os.Exit().
func Fatal(v ...interface{}) {
	withSource(logger).Fatal(v)
}

// Info logs a message with severity INFO.
func Info(v ...interface{}) {
	withSource(logger).Info(v)
}

// Warning logs a message with severity WARNING.
func Warning(v ...interface{}) {
	withSource(logger).Warning(v)
}

// WithFields is a Wrapper for logrus.WithFields
func WithFields(fields log.Fields) *log.Entry {
	return withSource(logger).WithFields(fields)
}
<|endoftext|>


<commit_before>package logging

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// L is the global Logger instance
var L *Logger

// New creates a new private Logger. If fileName is an empty string, the Logger will write to stdout. New will return nil if it can't create/open fileName.
func New(fileName string) *Logger {
	hostname, _ := os.Hostname()
	l := Logger{
		appName:    path.Base(os.Args[0]),
		hostName:   hostname,
		pid:        strconv.Itoa(os.Getpid()),
		doDebug:    false,
		doInfo:     true,
		doWarning:  true,
		doError:    true,
		doCritical: true,
		doFatal:    true,
	}
	err := l.SetLogFile(fileName)
	if err != nil {
		return nil
	}
	return &l
}

type Logger struct {
	appName    string
	hostName   string
	pid        string
	writer     io.WriteCloser
	doDebug    bool
	doInfo     bool
	doWarning  bool
	doError    bool
	doCritical bool
	doFatal    bool
}

func init() {
	L = New("")
}

// SetLogFile sets fileName as the log file target. An empty string sets logging to stdout (the default).
func (l *Logger) SetLogFile(fileName string) error {
	var w *os.File
	var err error
	if len(fileName) == 0 {
		w = os.Stdout
	} else {
		w, err = os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	}
	if err == nil {
		if l.writer != nil && l.writer != os.Stdout {
			l.writer.Close()
		}
		l.writer = w
	}
	return err
}

// SetOutput controls which levels of logging are enabled/disabled
func (l *Logger) SetOutput(debug, info, warning, err, critical, fatal bool) {
	l.doDebug = debug
	l.doInfo = info
	l.doWarning = warning
	l.doError = err
	l.doCritical = critical
	l.doFatal = fatal
}

// Error is like Debug for ERROR log entries.
func (l *Logger) Error(values map[string]string, format string, args ...interface{}) error {
	if !l.doError {
		return nil
	}
	return l.writeEntry("ERROR", values, format, args...)
}

// Warning is like Debug for WARNING log entries.
func (l *Logger) Warning(values map[string]string, format string, args ...interface{}) error {
	if !l.doWarning {
		return nil
	}
	return l.writeEntry("WARNING", values, format, args...)
}

// Info is like Debug for INFO log entries.
func (l *Logger) Info(values map[string]string, format string, args ...interface{}) error {
	if !l.doInfo {
		return nil
	}
	return l.writeEntry("INFO", values, format, args...)
}

// Debug writes a DEBUG log entry. The optional values map contains
// user-supplied key-value pairs. format and args are passed to fmt.Printf
// to generate the message entry.
func (l *Logger) Debug(values map[string]string, format string, args ...interface{}) error {
	if !l.doDebug {
		return nil
	}
	return l.writeEntry("DEBUG", values, format, args...)
}

// Critical is like Debug for CRITICAL log entries.
func (l *Logger) Critical(values map[string]string, format string, args ...interface{}) error {
	if !l.doCritical {
		return nil
	}
	return l.writeEntry("CRITICAL", values, format, args...)
}

// Fatal is like Debug for FATAL log entries, but it also calls os.Exit(1).
func (l *Logger) Fatal(values map[string]string, format string, args ...interface{}) error {
	if !l.doFatal {
		return nil
	}
	err := l.writeEntry("FATAL", values, format, args...)
	os.Exit(1)
	return err // won't actually get here
}

func (l *Logger) writeEntry(severity string, values map[string]string, format string, args ...interface{}) error {
	kv := l.getHeaderValues(severity)
	headerStr := makeHeaderString(kv)
	messageStr := fmt.Sprintf(format, args...)
	if strings.ContainsAny(messageStr, "{}\t") {
		messageStr = strings.Replace(messageStr, "\t", " ", -1)
		messageStr = strings.Replace(messageStr, "{", "[", -1)
		messageStr = strings.Replace(messageStr, "}", "]", -1)
	}
	jsonStr, err := makeJsonString(kv, values, messageStr)
	if err != nil {
		return err
	}
	_, err = fmt.Fprintf(l.writer, "%s\t%s\t%s\n", headerStr, messageStr, jsonStr)
	return err
}

func (l *Logger) getHeaderValues(severity string) map[string]string {
	pc, file, line, _ := runtime.Caller(3)
	f := runtime.FuncForPC(pc)
	caller := f.Name()
	m := map[string]string{
		"timestamp": time.Now().UTC().Format(time.RFC3339Nano),
		"severity":  severity,
		"pid":       l.pid,
		"app":       l.appName,
		"host":      l.hostName,
		"line":      strconv.Itoa(line),
		"file":      path.Base(file),
		"function":  path.Base(caller),
	}
	return m
}

func makeHeaderString(m map[string]string) string {
	return strings.Join([]string{m["timestamp"], m["severity"]}, "\t")
}

func makeJsonString(header map[string]string, kv map[string]string, message string) (string, error) {
	merged := make(map[string]string)
	for k, v := range kv {
		merged[k] = v
	}
	for k, v := range header {
		merged[k] = v
	}
	merged["message"] = message
	b, err := json.Marshal(merged)
	if err != nil {
		return "", err
	}
	return string(b), nil
}
<commit_msg>Fix Lint warnings<commit_after>package logging

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"strconv"
	"strings"
	"time"
)

// L is the global Logger instance.
var L *Logger

// New creates a new private Logger. If fileName is an empty string, the Logger will write to stdout. New will return nil if it can't create/open fileName.
func New(fileName string) *Logger {
	hostname, _ := os.Hostname()
	l := Logger{
		appName:    path.Base(os.Args[0]),
		hostName:   hostname,
		pid:        strconv.Itoa(os.Getpid()),
		doDebug:    false,
		doInfo:     true,
		doWarning:  true,
		doError:    true,
		doCritical: true,
		doFatal:    true,
	}
	err := l.SetLogFile(fileName)
	if err != nil {
		return nil
	}
	return &l
}

// Logger is a type passed to the logging functions. It stores the log settings.
type Logger struct {
	appName    string
	hostName   string
	pid        string
	writer     io.WriteCloser
	doDebug    bool
	doInfo     bool
	doWarning  bool
	doError    bool
	doCritical bool
	doFatal    bool
}

func init() {
	L = New("")
}

// SetLogFile sets fileName as the log file target. An empty string sets logging to stdout (the default).
func (l *Logger) SetLogFile(fileName string) error {
	var w *os.File
	var err error
	if len(fileName) == 0 {
		w = os.Stdout
	} else {
		w, err = os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	}
	if err == nil {
		if l.writer != nil && l.writer != os.Stdout {
			l.writer.Close()
		}
		l.writer = w
	}
	return err
}

// SetOutput controls which levels of logging are enabled/disabled
func (l *Logger) SetOutput(debug, info, warning, err, critical, fatal bool) {
	l.doDebug = debug
	l.doInfo = info
	l.doWarning = warning
	l.doError = err
	l.doCritical = critical
	l.doFatal = fatal
}

// Error is like Debug for ERROR log entries.
func (l *Logger) Error(values map[string]string, format string, args ...interface{}) error {
	if !l.doError {
		return nil
	}
	return l.writeEntry("ERROR", values, format, args...)
}

// Warning is like Debug for WARNING log entries.
func (l *Logger) Warning(values map[string]string, format string, args ...interface{}) error {
	if !l.doWarning {
		return nil
	}
	return l.writeEntry("WARNING", values, format, args...)
}

// Info is like Debug for INFO log entries.
func (l *Logger) Info(values map[string]string, format string, args ...interface{}) error {
	if !l.doInfo {
		return nil
	}
	return l.writeEntry("INFO", values, format, args...)
}

// Debug writes a DEBUG log entry. The optional values map contains
// user-supplied key-value pairs. format and args are passed to fmt.Printf
// to generate the message entry.
func (l *Logger) Debug(values map[string]string, format string, args ...interface{}) error {
	if !l.doDebug {
		return nil
	}
	return l.writeEntry("DEBUG", values, format, args...)
}

// Critical is like Debug for CRITICAL log entries.
func (l *Logger) Critical(values map[string]string, format string, args ...interface{}) error {
	if !l.doCritical {
		return nil
	}
	return l.writeEntry("CRITICAL", values, format, args...)
}

// Fatal is like Debug for FATAL log entries, but it also calls os.Exit(1).
func (l *Logger) Fatal(values map[string]string, format string, args ...interface{}) error {
	if !l.doFatal {
		return nil
	}
	err := l.writeEntry("FATAL", values, format, args...)
	os.Exit(1)
	return err // won't actually get here
}

func (l *Logger) writeEntry(severity string, values map[string]string, format string, args ...interface{}) error {
	kv := l.getHeaderValues(severity)
	headerStr := makeHeaderString(kv)
	messageStr := fmt.Sprintf(format, args...)
	if strings.ContainsAny(messageStr, "{}\t") {
		messageStr = strings.Replace(messageStr, "\t", " ", -1)
		messageStr = strings.Replace(messageStr, "{", "[", -1)
		messageStr = strings.Replace(messageStr, "}", "]", -1)
	}
	jsonStr, err := makeJSONString(kv, values, messageStr)
	if err != nil {
		return err
	}
	_, err = fmt.Fprintf(l.writer, "%s\t%s\t%s\n", headerStr, messageStr, jsonStr)
	return err
}

func (l *Logger) getHeaderValues(severity string) map[string]string {
	pc, file, line, _ := runtime.Caller(3)
	f := runtime.FuncForPC(pc)
	caller := f.Name()
	m := map[string]string{
		"timestamp": time.Now().UTC().Format(time.RFC3339Nano),
		"severity":  severity,
		"pid":       l.pid,
		"app":       l.appName,
		"host":      l.hostName,
		"line":      strconv.Itoa(line),
		"file":      path.Base(file),
		"function":  path.Base(caller),
	}
	return m
}

func makeHeaderString(m map[string]string) string {
	return strings.Join([]string{m["timestamp"], m["severity"]}, "\t")
}

func makeJSONString(header map[string]string, kv map[string]string, message string) (string, error) {
	merged := make(map[string]string)
	for k, v := range kv {
		merged[k] = v
	}
	for k, v := range header {
		merged[k] = v
	}
	merged["message"] = message
	b, err := json.Marshal(merged)
	if err != nil {
		return "", err
	}
	return string(b), nil
}
<|endoftext|>


<commit_before>package client

import (
	"flag"
	"log"
	"strings"
)

// These override the JSON config file ~/.camlistore's "server" and
// "password" keys
var flagServer *string = flag.String("blobserver", "", "camlistore blob server")
var flagPassword *string = flag.String("password", "", "password for blob server")

func cleanServer(server string) string {
	// Remove trailing slash if provided.
	if strings.HasSuffix(server, "/") {
		server = server[0 : len(server)-1]
	}
	// Add "http://" prefix if not present:
	if !strings.HasPrefix(server, "http") {
		server = "http://" + server
	}
	return server
}

func blobServerOrDie() string {
	if *flagServer != "" {
		return cleanServer(*flagServer)
	}
	log.Exitf("No --blobserver parameter specified.")
	return ""
}

func passwordOrDie() string {
	if *flagPassword != "" {
		return *flagPassword
	}
	log.Exitf("No --password parameter specified.")
	return ""
}

<commit_msg>Parse JSON configuration file ~/.camli/config<commit_after>package client

import (
	"camli/blobref"
	"flag"
	"json"
	"log"
	"os"
	"path"
	"strings"
	"sync"
	"syscall"
)

// These override the JSON config file ~/.camli/config "server" and
// "password" keys
var flagServer *string = flag.String("blobserver", "", "camlistore blob server")
var flagPassword *string = flag.String("password", "", "password for blob server")

func configFilePath() string {
	return path.Join(os.Getenv("HOME"), ".camli", "config")
}

var configOnce sync.Once
var config = make(map[string]interface{})
func parseConfig() {
	f, err := os.Open(configFilePath(), os.O_RDONLY, 0)
	switch {
	case err != nil && err.(*os.PathError).Error.(os.Errno) == syscall.ENOENT:
		// TODO: write empty file?
		return
	case err != nil:
		log.Printf("Error opening config file %q: %v", configFilePath(), err)
		return
	default:
		defer f.Close()
		dj := json.NewDecoder(f)
		if err := dj.Decode(&config); err != nil {
			log.Printf("Error parsing JSON in config file %q: %v", configFilePath(), err)
		}
        }
}

func cleanServer(server string) string {
	// Remove trailing slash if provided.
	if strings.HasSuffix(server, "/") {
		server = server[0 : len(server)-1]
	}
	// Add "http://" prefix if not present:
	if !strings.HasPrefix(server, "http") {
		server = "http://" + server
	}
	return server
}

func blobServerOrDie() string {
	if *flagServer != "" {
		return cleanServer(*flagServer)
	}
	configOnce.Do(parseConfig)

	log.Exitf("No --blobserver parameter specified.")
	return ""
}

func passwordOrDie() string {
	if *flagPassword != "" {
		return *flagPassword
	}
	configOnce.Do(parseConfig)

	log.Exitf("No --password parameter specified.")
	return ""
}

// Returns blobref of signer's public key, or nil if unconfigured.
func (c *Client) SignerPublicKeyBlobref() *blobref.BlobRef {
	configOnce.Do(parseConfig)
	key := "publicKeyBlobref"
	v, ok := config[key]
	if !ok {
		log.Printf("No key %q in JSON configuration file %q", key, configFilePath())
		return nil
	}
	s, ok := v.(string)
	if !ok {
		log.Printf("Expected a string value for key %q in JSON file %q",
			key, configFilePath())
	}
	ref := blobref.Parse(s)
	if ref == nil {
		log.Printf("Bogus value %#v for key %q in file %q; not a valid blobref",
			s, key, configFilePath())
	}
	return ref
}

func (c *Client) GetBlobFetcher() blobref.Fetcher {
	// TODO: make a NewSeriesAttemptFetcher(...all configured fetch paths...)
	return blobref.NewSimpleDirectoryFetcher(path.Join(os.Getenv("HOME"), ".camli", "blobs"))
}
<|endoftext|>


<commit_before>package los

import (
	"github.com/phil-mansfield/gotetra/render/io"
	rGeom "github.com/phil-mansfield/gotetra/render/geom"
	"github.com/phil-mansfield/gotetra/los/geom"
)

// CountAll computes profiles for all the given halos which count the number
// of tetrahedra overlapping points at a given radius.
func CountAll(
	hs []HaloProfiles, ts []geom.Tetra, ss []geom.Sphere,
) {
	for hi := range hs {
		h := &hs[hi]
		for ti := range ts {
			t := &ts[ti]
			s := &ss[ti]

			if h.Sphere.SphereIntersect(s) &&
				!h.minSphere.TetraContain(t) {
				h.Count(t)
			}
		}
	}
}

// DensityAll computes profiles for all the given halos which give the density
// of points at a given radius.
func DensityAll(
	hs []HaloProfiles, ts []geom.Tetra, ss []geom.Sphere, rhos []float64,
) {
	for hi := range hs {
		h := &hs[hi]
		for ti := range ts {
			t := &ts[ti]
			s := &ss[ti]
			if h.Sphere.SphereIntersect(s) &&
				!h.minSphere.TetraContain(t) {
				h.Density(t, rhos[ti])
			}
		}
	}
}

func coords(idx, cells int64) (x, y, z int64) {
    x = idx % cells
    y = (idx % (cells * cells)) / cells
    z = idx / (cells * cells)
    return x, y, z
}

func index(x, y, z, cells int64) int64 {
    return x + y * cells + z * cells * cells
}

func unpackTetra(idxs *rGeom.TetraIdxs, xs []rGeom.Vec, t *geom.Tetra) {
    for i := 0; i < 4; i++ {
		t[i] = geom.Vec(xs[idxs[i]])
    }
}

// UnpackTetrahedra converts the raw position data in a sheet segment into
// tetrahedra.
func UnpackTetrahedra(
	xs []rGeom.Vec, hd *io.SheetHeader, tsBuf []geom.Tetra,
) {
	n := hd.SegmentWidth*hd.SegmentWidth*hd.SegmentWidth
	idxBuf := new(rGeom.TetraIdxs)
	for writeIdx := int64(0); writeIdx < n; writeIdx++ {
		x, y, z := coords(writeIdx, hd.SegmentWidth)
		readIdx := index(x, y, z, hd.SegmentWidth)

		for dir := int64(0); dir < 6; dir++ {
			tIdx := 6 * writeIdx + dir
			idxBuf.Init(readIdx, hd.GridWidth + 1, 1, int(dir))
			unpackTetra(idxBuf, xs, &tsBuf[tIdx])
			tsBuf[tIdx].Orient(+1)
		}
	}

}

// TetraDensity writes the density of a slice of tetrahedra to a slice of
// floats.
func TetraDensity(hd *io.SheetHeader, ts []geom.Tetra, rhos []float64) {
	tw := hd.TotalWidth
	tCount := float64(hd.Count * 6)
	for i := range ts {
		rhos[i] = (tw * tw * tw) / (ts[i].Volume() * tCount)
	}
}

// WrapHalo updates the coordinates of a slice of HaloProfiles so that they
// as close to the given sheet as periodic boundary conditions will allow.
func WrapHalo(hps []HaloProfiles, hd *io.SheetHeader) {
	tw := float32(hd.TotalWidth)
	newC := &geom.Vec{}
	for i := range hps {
		h := &hps[i]
		for j := 0; j < 3; j++ {
			if h.C[j] + h.R < hd.Origin[j] {
				newC[j] = h.C[j] + tw
			} else {
				newC[j] = h.C[j]
			}
		}
		h.ChangeCenter(newC)
	}
}

// WrapXs updates a slice of vectors so that they will be as close to thee given
// sheet as periodic boundary conditions will allow.
func WrapXs(xs []rGeom.Vec, hd *io.SheetHeader) {
	tw := float32(hd.TotalWidth)
	for i := range xs {
		for j := 0; j < 3; j++ {
			if xs[i][j] < hd.Origin[j] { xs[i][j] += tw }
		}
	}
}
<commit_msg>Fixed implementation of UnpackTetrahedra.<commit_after>package los

import (
	"github.com/phil-mansfield/gotetra/render/io"
	rGeom "github.com/phil-mansfield/gotetra/render/geom"
	"github.com/phil-mansfield/gotetra/los/geom"
)

// CountAll computes profiles for all the given halos which count the number
// of tetrahedra overlapping points at a given radius.
func CountAll(
	hs []HaloProfiles, ts []geom.Tetra, ss []geom.Sphere,
) {
	for hi := range hs {
		h := &hs[hi]
		for ti := range ts {
			t := &ts[ti]
			s := &ss[ti]

			if h.Sphere.SphereIntersect(s) &&
				!h.minSphere.TetraContain(t) {
				h.Count(t)
			}
		}
	}
}

// DensityAll computes profiles for all the given halos which give the density
// of points at a given radius.
func DensityAll(
	hs []HaloProfiles, ts []geom.Tetra, ss []geom.Sphere, rhos []float64,
) {
	for hi := range hs {
		h := &hs[hi]
		for ti := range ts {
			t := &ts[ti]
			s := &ss[ti]
			if h.Sphere.SphereIntersect(s) &&
				!h.minSphere.TetraContain(t) {
				h.Density(t, rhos[ti])
			}
		}
	}
}

func coords(idx, cells int64) (x, y, z int64) {
    x = idx % cells
    y = (idx % (cells * cells)) / cells
    z = idx / (cells * cells)
    return x, y, z
}

func index(x, y, z, cells int64) int64 {
    return x + y * cells + z * cells * cells
}

func unpackTetra(idxs *rGeom.TetraIdxs, xs []rGeom.Vec, t *geom.Tetra) {
    for i := 0; i < 4; i++ {
		t[i] = geom.Vec(xs[idxs[i]])
    }
}

// UnpackTetrahedra converts the raw position data in a sheet segment into
// tetrahedra.
func UnpackTetrahedra(
	xs []rGeom.Vec, hd *io.SheetHeader, tsBuf []geom.Tetra,
) {
	n := hd.SegmentWidth*hd.SegmentWidth*hd.SegmentWidth
	idxBuf := new(rGeom.TetraIdxs)
	for writeIdx := int64(0); writeIdx < n; writeIdx++ {
		x, y, z := coords(writeIdx, hd.SegmentWidth)
		for dir := int64(0); dir < 6; dir++ {
			tIdx := 6 * writeIdx + dir
			idxBuf.InitCartesian(x, y, z, hd.GridWidth, int(dir))
			unpackTetra(idxBuf, xs, &tsBuf[tIdx])
			tsBuf[tIdx].Orient(+1)
		}
	}

}

// TetraDensity writes the density of a slice of tetrahedra to a slice of
// floats.
func TetraDensity(hd *io.SheetHeader, ts []geom.Tetra, rhos []float64) {
	tw := hd.TotalWidth
	tCount := float64(hd.Count * 6)
	for i := range ts {
		rhos[i] = (tw * tw * tw) / (ts[i].Volume() * tCount)
	}
}

// WrapHalo updates the coordinates of a slice of HaloProfiles so that they
// as close to the given sheet as periodic boundary conditions will allow.
func WrapHalo(hps []HaloProfiles, hd *io.SheetHeader) {
	tw := float32(hd.TotalWidth)
	newC := &geom.Vec{}
	for i := range hps {
		h := &hps[i]
		for j := 0; j < 3; j++ {
			if h.C[j] + h.R < hd.Origin[j] {
				newC[j] = h.C[j] + tw
			} else {
				newC[j] = h.C[j]
			}
		}
		h.ChangeCenter(newC)
	}
}

// WrapXs updates a slice of vectors so that they will be as close to thee given
// sheet as periodic boundary conditions will allow.
func WrapXs(xs []rGeom.Vec, hd *io.SheetHeader) {
	tw := float32(hd.TotalWidth)
	for i := range xs {
		for j := 0; j < 3; j++ {
			if xs[i][j] < hd.Origin[j] { xs[i][j] += tw }
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"syscall"
	"text/tabwriter"
	"time"

	"sigint.ca/group"
	"sigint.ca/user"
)

type normalSort []os.FileInfo
type timeSort []os.FileInfo

func (a normalSort) Len() int           { return len(a) }
func (a normalSort) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a normalSort) Less(i, j int) bool { return a[i].Name() < a[j].Name() }

func (a timeSort) Len() int           { return len(a) }
func (a timeSort) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a timeSort) Less(i, j int) bool { return a[i].ModTime().After(a[j].ModTime()) }

var (
	lflag = flag.Bool("l", false, "List in long format")
	pflag = flag.Bool("p", false, "Print only the final path element of each file name")
	rflag = flag.Bool("r", false, "Reverse the order of sort")
	sflag = flag.Bool("s", false, "Give size in KB for each entry")
	tflag = flag.Bool("t", false, "Sort by time modified (latest first) instead of by name")
)

func main() {
	elog := log.New(os.Stderr, "ls: ", 0)
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: ls [options] [file ...]")
		flag.PrintDefaults()
	}
	flag.Parse()
	var status int
	args := flag.Args()
	var noargs bool
	if len(args) == 0 {
		args = []string{"."}
		noargs = true
	}
	for _, path := range args {
		stat, err := os.Stat(path)
		if err != nil {
			elog.Print(err)
			status++
			continue
		}
		if stat.IsDir() {
			f, err := os.Open(path)
			if err != nil {
				elog.Print(err)
				status++
				continue
			}
			fi, err := f.Readdir(0)
			if err != nil {
				elog.Print(err)
				status++
				continue
			}
			f.Close()

			rev := func(i sort.Interface) sort.Interface { return i }
			if *rflag {
				rev = sort.Reverse
			}
			if *tflag {
				sort.Sort(rev(timeSort(fi)))
			} else {
				sort.Sort(rev(normalSort(fi)))
			}

			w := tabwriter.NewWriter(os.Stdout, 0, 0, 0, ' ', tabwriter.AlignRight)
			for i := range fi {
				if noargs {
					path = ""
				}
				ls(w, fi[i], path)
			}
			w.Flush()

		} else {
			if path != stat.Name() {
				path = filepath.Dir(path)
			} else {
				path = ""
			}
			ls(os.Stdout, stat, path)
		}
	}
	os.Exit(status)
}

func ls(w io.Writer, info os.FileInfo, path string) {
	if *sflag {
		fmt.Fprintf(w, "%d\t ", info.Size()/1024+1) // +1 is sloppy round-up
	}
	if *lflag {
		fmt.Fprintf(w, "%s\t ", modeString(info.Mode()))

		stat := info.Sys().(*syscall.Stat_t) // TODO: non-portable
		uid := strconv.Itoa(int(stat.Uid))
		uname := uid
		u, err := user.LookupId(uid)
		if err == nil {
			uname = u.Username
		}
		fmt.Fprintf(w, "%s\t ", uname)

		gid := strconv.Itoa(int(stat.Gid))
		gname, err := group.Name(gid)
		if err != nil {
			gname = gid
		}
		fmt.Fprintf(w, "%s\t ", gname)

		// major, minor
		if info.Mode()&os.ModeDevice > 0 {
			major, minor := devNums(stat.Rdev)
			fmt.Fprintf(w, "%3d, %3d\t ", major, minor)
		} else {
			fmt.Fprintf(w, "%d\t ", info.Size())
		}

		// modified time
		var modtime string
		year := info.ModTime().Year()
		if year == time.Now().Year() {
			modtime = info.ModTime().Format("Jan 02 15:04")
		} else {
			modtime = info.ModTime().Format("Jan 02  ") + strconv.Itoa(year)
		}

		fmt.Fprintf(w, "%s ", modtime)
	}

	if path != "" && !*pflag {
		fmt.Fprintln(w, filepath.Clean(path+"/"+info.Name()))
	} else {
		fmt.Fprintln(w, info.Name())
	}
}

func modeString(mode os.FileMode) string {
	modestr := []byte("-rwxrwxrwx")

	// type
	switch mode & os.ModeType {
	case os.ModeDevice:
		if mode&os.ModeCharDevice > 0 {
			modestr[0] = 'c'
		} else {
			modestr[0] = 'b'
		}
	case os.ModeDir:
		modestr[0] = 'd'
	case os.ModeSymlink:
		modestr[0] = 'l'
	case os.ModeNamedPipe:
		modestr[0] = 'p'
	case os.ModeSocket:
		modestr[0] = 's'
	}

	// permissions
	bit := os.FileMode(1 << 8)
	for i := range modestr[1:] {
		if mode&bit == 0 {
			modestr[1+i] = '-'
		}
		bit >>= 1
	}

	// special attributes
	if mode&os.ModeSetuid > 0 {
		if modestr[3] == 'x' {
			modestr[3] = 's'
		} else {
			modestr[3] = 'S'
		}
	}
	if mode&os.ModeSetgid > 0 {
		if modestr[6] == 'x' {
			modestr[6] = 's'
		} else {
			modestr[6] = 'S'
		}
	}
	if mode&os.ModeSticky > 0 {
		if modestr[9] == 'x' {
			modestr[9] = 't'
		} else {
			modestr[9] = 'T'
		}
	}

	return string(modestr)
}
<commit_msg>ls: match option help text style to other commands<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"syscall"
	"text/tabwriter"
	"time"

	"sigint.ca/group"
	"sigint.ca/user"
)

type normalSort []os.FileInfo
type timeSort []os.FileInfo

func (a normalSort) Len() int           { return len(a) }
func (a normalSort) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a normalSort) Less(i, j int) bool { return a[i].Name() < a[j].Name() }

func (a timeSort) Len() int           { return len(a) }
func (a timeSort) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a timeSort) Less(i, j int) bool { return a[i].ModTime().After(a[j].ModTime()) }

var (
	lflag = flag.Bool("l", false, "List in long format.")
	pflag = flag.Bool("p", false, "Print only the final path element of each file name.")
	rflag = flag.Bool("r", false, "Reverse the order of sort.")
	sflag = flag.Bool("s", false, "Give size in KB for each entry.")
	tflag = flag.Bool("t", false, "Sort by time modified (latest first) instead of by name.")
)

func main() {
	elog := log.New(os.Stderr, "ls: ", 0)
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: ls [options] [file ...]")
		flag.PrintDefaults()
	}
	flag.Parse()
	var status int
	args := flag.Args()
	var noargs bool
	if len(args) == 0 {
		args = []string{"."}
		noargs = true
	}
	for _, path := range args {
		stat, err := os.Stat(path)
		if err != nil {
			elog.Print(err)
			status++
			continue
		}
		if stat.IsDir() {
			f, err := os.Open(path)
			if err != nil {
				elog.Print(err)
				status++
				continue
			}
			fi, err := f.Readdir(0)
			if err != nil {
				elog.Print(err)
				status++
				continue
			}
			f.Close()

			rev := func(i sort.Interface) sort.Interface { return i }
			if *rflag {
				rev = sort.Reverse
			}
			if *tflag {
				sort.Sort(rev(timeSort(fi)))
			} else {
				sort.Sort(rev(normalSort(fi)))
			}

			w := tabwriter.NewWriter(os.Stdout, 0, 0, 0, ' ', tabwriter.AlignRight)
			for i := range fi {
				if noargs {
					path = ""
				}
				ls(w, fi[i], path)
			}
			w.Flush()

		} else {
			if path != stat.Name() {
				path = filepath.Dir(path)
			} else {
				path = ""
			}
			ls(os.Stdout, stat, path)
		}
	}
	os.Exit(status)
}

func ls(w io.Writer, info os.FileInfo, path string) {
	if *sflag {
		fmt.Fprintf(w, "%d\t ", info.Size()/1024+1) // +1 is sloppy round-up
	}
	if *lflag {
		fmt.Fprintf(w, "%s\t ", modeString(info.Mode()))

		stat := info.Sys().(*syscall.Stat_t) // TODO: non-portable
		uid := strconv.Itoa(int(stat.Uid))
		uname := uid
		u, err := user.LookupId(uid)
		if err == nil {
			uname = u.Username
		}
		fmt.Fprintf(w, "%s\t ", uname)

		gid := strconv.Itoa(int(stat.Gid))
		gname, err := group.Name(gid)
		if err != nil {
			gname = gid
		}
		fmt.Fprintf(w, "%s\t ", gname)

		// major, minor
		if info.Mode()&os.ModeDevice > 0 {
			major, minor := devNums(stat.Rdev)
			fmt.Fprintf(w, "%3d, %3d\t ", major, minor)
		} else {
			fmt.Fprintf(w, "%d\t ", info.Size())
		}

		// modified time
		var modtime string
		year := info.ModTime().Year()
		if year == time.Now().Year() {
			modtime = info.ModTime().Format("Jan 02 15:04")
		} else {
			modtime = info.ModTime().Format("Jan 02  ") + strconv.Itoa(year)
		}

		fmt.Fprintf(w, "%s ", modtime)
	}

	if path != "" && !*pflag {
		fmt.Fprintln(w, filepath.Clean(path+"/"+info.Name()))
	} else {
		fmt.Fprintln(w, info.Name())
	}
}

func modeString(mode os.FileMode) string {
	modestr := []byte("-rwxrwxrwx")

	// type
	switch mode & os.ModeType {
	case os.ModeDevice:
		if mode&os.ModeCharDevice > 0 {
			modestr[0] = 'c'
		} else {
			modestr[0] = 'b'
		}
	case os.ModeDir:
		modestr[0] = 'd'
	case os.ModeSymlink:
		modestr[0] = 'l'
	case os.ModeNamedPipe:
		modestr[0] = 'p'
	case os.ModeSocket:
		modestr[0] = 's'
	}

	// permissions
	bit := os.FileMode(1 << 8)
	for i := range modestr[1:] {
		if mode&bit == 0 {
			modestr[1+i] = '-'
		}
		bit >>= 1
	}

	// special attributes
	if mode&os.ModeSetuid > 0 {
		if modestr[3] == 'x' {
			modestr[3] = 's'
		} else {
			modestr[3] = 'S'
		}
	}
	if mode&os.ModeSetgid > 0 {
		if modestr[6] == 'x' {
			modestr[6] = 's'
		} else {
			modestr[6] = 'S'
		}
	}
	if mode&os.ModeSticky > 0 {
		if modestr[9] == 'x' {
			modestr[9] = 't'
		} else {
			modestr[9] = 'T'
		}
	}

	return string(modestr)
}
<|endoftext|>


<commit_before>package adsi

import (
	"sync"
	"unsafe"

	"github.com/go-ole/go-ole"
	"github.com/scjalliance/comshim"
	"gopkg.in/adsi.v0/api"
)

// ADSI Objects of LDAP:  https://msdn.microsoft.com/library/aa772208
// ADSI Objects of WinNT: https://msdn.microsoft.com/library/aa772211

// Object provides access to Active Directory objects.
type Object struct {
	m     sync.RWMutex
	iface *api.IADs
}

// NewObject returns an object that manages the given COM interface.
func NewObject(iface *api.IADs) *Object {
	comshim.Add(1)
	return &Object{iface: iface}
}

/*
// See: https://msdn.microsoft.com/library/aa772184
func GetObject(path string, iid ole.GUID) (obj *Object, err error) {
	// TODO: Implement this
	return
}

// See: https://msdn.microsoft.com/library/aa772184
func RemoteObject(server, path string) (obj *Object, err error) {
	// TODO: Implement this
	return
}
*/

func (o *Object) closed() bool {
	return (o.iface == nil)
}

// Close will release resources consumed by the object. It should be
// called when the object is no longer needed.
func (o *Object) Close() {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return
	}
	defer comshim.Done()
	run(func() error {
		o.iface.Release()
		return nil
	})
	// FIXME: What happens if the run returns an error?
	o.iface = nil
}

// Name retrieves the name of the object.
func (o *Object) Name() (name string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		name, err = o.iface.Name()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// Class retrieves the class of the object.
func (o *Object) Class() (class string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		class, err = o.iface.Class()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// GUID retrieves the globally unique identifier of the object.
func (o *Object) GUID() (guid *ole.GUID, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	err = run(func() error {
		var sguid string
		sguid, err = o.iface.GUID()
		if err != nil {
			return err
		}

		guid = ole.NewGUID(sguid)
		if guid == nil {
			return ErrInvalidGUID
		}

		return nil
	})
	return
}

// Path retrieves the fully qualified path of the object.
func (o *Object) Path() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		path, err = o.iface.AdsPath()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// Parent retrieves the fully qualified path of the object's parent.
func (o *Object) Parent() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		path, err = o.iface.Parent()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// Schema retrieves the fully qualified path of the object's schema class
// object.
func (o *Object) Schema() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		path, err = o.iface.Schema()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// ToContainer attempts to acquire a container interface for the object.
func (o *Object) ToContainer() (c *Container, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	err = run(func() error {
		idispatch, err := o.iface.QueryInterface(api.IID_IADsContainer)
		if err != nil {
			return err
		}
		iface := (*api.IADsContainer)(unsafe.Pointer(idispatch))
		c = NewContainer(iface)
		return nil
	})
	return
}
<commit_msg>Added Object.ToComputer() and Object.ToGroup()<commit_after>package adsi

import (
	"sync"
	"unsafe"

	"github.com/go-ole/go-ole"
	"github.com/scjalliance/comshim"
	"gopkg.in/adsi.v0/api"
)

// ADSI Objects of LDAP:  https://msdn.microsoft.com/library/aa772208
// ADSI Objects of WinNT: https://msdn.microsoft.com/library/aa772211

// Object provides access to Active Directory objects.
type Object struct {
	m     sync.RWMutex
	iface *api.IADs
}

// NewObject returns an object that manages the given COM interface.
func NewObject(iface *api.IADs) *Object {
	comshim.Add(1)
	return &Object{iface: iface}
}

/*
// See: https://msdn.microsoft.com/library/aa772184
func GetObject(path string, iid ole.GUID) (obj *Object, err error) {
	// TODO: Implement this
	return
}

// See: https://msdn.microsoft.com/library/aa772184
func RemoteObject(server, path string) (obj *Object, err error) {
	// TODO: Implement this
	return
}
*/

func (o *Object) closed() bool {
	return (o.iface == nil)
}

// Close will release resources consumed by the object. It should be
// called when the object is no longer needed.
func (o *Object) Close() {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return
	}
	defer comshim.Done()
	run(func() error {
		o.iface.Release()
		return nil
	})
	// FIXME: What happens if the run returns an error?
	o.iface = nil
}

// Name retrieves the name of the object.
func (o *Object) Name() (name string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		name, err = o.iface.Name()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// Class retrieves the class of the object.
func (o *Object) Class() (class string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		class, err = o.iface.Class()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// GUID retrieves the globally unique identifier of the object.
func (o *Object) GUID() (guid *ole.GUID, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	err = run(func() error {
		var sguid string
		sguid, err = o.iface.GUID()
		if err != nil {
			return err
		}

		guid = ole.NewGUID(sguid)
		if guid == nil {
			return ErrInvalidGUID
		}

		return nil
	})
	return
}

// Path retrieves the fully qualified path of the object.
func (o *Object) Path() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		path, err = o.iface.AdsPath()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// Parent retrieves the fully qualified path of the object's parent.
func (o *Object) Parent() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		path, err = o.iface.Parent()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// Schema retrieves the fully qualified path of the object's schema class
// object.
func (o *Object) Schema() (path string, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return "", ErrClosed
	}
	err = run(func() error {
		path, err = o.iface.Schema()
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// ToContainer attempts to acquire a container interface for the object.
func (o *Object) ToContainer() (c *Container, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	err = run(func() error {
		idispatch, err := o.iface.QueryInterface(api.IID_IADsContainer)
		if err != nil {
			return err
		}
		iface := (*api.IADsContainer)(unsafe.Pointer(idispatch))
		c = NewContainer(iface)
		return nil
	})
	return
}

// ToComputer attempts to acquire a computer interface for the object.
func (o *Object) ToComputer() (c *Computer, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	err = run(func() error {
		idispatch, err := o.iface.QueryInterface(api.IID_IADsComputer)
		if err != nil {
			return err
		}
		iface := (*api.IADsComputer)(unsafe.Pointer(idispatch))
		c = NewComputer(iface)
		return nil
	})
	return
}

// ToGroup attempts to acquire a group interface for the object.
func (o *Object) ToGroup() (g *Group, err error) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.closed() {
		return nil, ErrClosed
	}
	err = run(func() error {
		idispatch, err := o.iface.QueryInterface(api.IID_IADsGroup)
		if err != nil {
			return err
		}
		iface := (*api.IADsGroup)(unsafe.Pointer(idispatch))
		g = NewGroup(iface)
		return nil
	})
	return
}
<|endoftext|>


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

// The oauth package provides support for making
// OAuth2-authenticated HTTP requests.
//
// Example usage:
//
//	// Specify your configuration. (typically as a global variable)
//	var config = &oauth.Config{
//		Provider:     [TWITTER, REDDIT]
//		ClientId:     YOUR_CLIENT_ID,
//		ClientSecret: YOUR_CLIENT_SECRET,
//		Scope:        "https://www.googleapis.com/auth/buzz",
//		AuthURL:      "https://accounts.google.com/o/oauth2/auth",
//		TokenURL:     "https://accounts.google.com/o/oauth2/token",
//		RedirectURL:  "http://you.example.org/handler",
//	}
//
//	// A landing page redirects to the OAuth provider to get the auth code.
//	func landing(w http.ResponseWriter, r *http.Request) {
//		http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
//	}
//
//	// The user will be redirected back to this handler, that takes the
//	// "code" query parameter and Exchanges it for an access token.
//	func handler(w http.ResponseWriter, r *http.Request) {
//		t := &oauth.Transport{Config: config}
//		t.Exchange(r.FormValue("code"))
//		// The Transport now has a valid Token. Create an *http.Client
//		// with which we can make authenticated API requests.
//		c := t.Client()
//		c.Post(...)
//		// ...
//		// btw, r.FormValue("state") == "foo"
//	}
//
package oauth

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"time"
)

//Oauth providers
const ( // iota is reset to 0
	GENERIC = iota
	TWITTER
	REDDIT
)

type OAuthError struct {
	prefix string
	msg    string
}

func (oe OAuthError) Error() string {
	return "OAuthError: " + oe.prefix + ": " + oe.msg
}

// Cache specifies the methods that implement a Token cache.
type Cache interface {
	Token() (*Token, error)
	PutToken(*Token) error
}

// CacheFile implements Cache. Its value is the name of the file in which
// the Token is stored in JSON format.
type CacheFile string

func (f CacheFile) Token() (*Token, error) {
	file, err := os.Open(string(f))
	if err != nil {
		return nil, OAuthError{"CacheFile.Token", err.Error()}
	}
	defer file.Close()
	tok := &Token{}
	if err := json.NewDecoder(file).Decode(tok); err != nil {
		return nil, OAuthError{"CacheFile.Token", err.Error()}
	}
	return tok, nil
}

func (f CacheFile) PutToken(tok *Token) error {
	file, err := os.OpenFile(string(f), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return OAuthError{"CacheFile.PutToken", err.Error()}
	}
	if err := json.NewEncoder(file).Encode(tok); err != nil {
		file.Close()
		return OAuthError{"CacheFile.PutToken", err.Error()}
	}
	if err := file.Close(); err != nil {
		return OAuthError{"CacheFile.PutToken", err.Error()}
	}
	return nil
}

// Config is the configuration of an OAuth consumer.
type Config struct {
	// Provider is the OAuth provider to use
	Provider int

	// ClientId is the OAuth client identifier used when communicating with
	// the configured OAuth provider.
	ClientId string

	// ClientSecret is the OAuth client secret used when communicating with
	// the configured OAuth provider.
	ClientSecret string

	// Scope identifies the level of access being requested. Multiple scope
	// values should be provided as a space-delimited string.
	Scope string

	// AuthURL is the URL the user will be directed to in order to grant
	// access.
	AuthURL string

	// TokenURL is the URL used to retrieve OAuth tokens.
	TokenURL string

	// RedirectURL is the URL to which the user will be returned after
	// granting (or denying) access.
	RedirectURL string

	// TokenCache allows tokens to be cached for subsequent requests.
	TokenCache Cache

	AccessType string // Optional, "online" (default) or "offline", no refresh token if "online"

	// ApprovalPrompt indicates whether the user should be
	// re-prompted for consent. If set to "auto" (default) the
	// user will be prompted only if they haven't previously
	// granted consent and the code can only be exchanged for an
	// access token.
	// If set to "force" the user will always be prompted, and the
	// code can be exchanged for a refresh token.
	ApprovalPrompt string
}

// Token contains an end-user's tokens.
// This is the data you must store to persist authentication.
type Token struct {
	AccessToken  string
	RefreshToken string
	Expiry       time.Time         // If zero the token has no (known) expiry time.
	Extra        map[string]string // May be nil.
}

func (t *Token) Expired() bool {
	if t.Expiry.IsZero() {
		return false
	}
	return t.Expiry.Before(time.Now())
}

// Transport implements http.RoundTripper. When configured with a valid
// Config and Token it can be used to make authenticated HTTP requests.
//
//	t := &oauth.Transport{config}
//      t.Exchange(code)
//      // t now contains a valid Token
//	r, _, err := t.Client().Get("http://example.org/url/requiring/auth")
//
// It will automatically refresh the Token if it can,
// updating the supplied Token in place.
type Transport struct {
	*Config
	*Token

	// Transport is the HTTP transport to use when making requests.
	// It will default to http.DefaultTransport if nil.
	// (It should never be an oauth.Transport.)
	Transport http.RoundTripper
}

// Client returns an *http.Client that makes OAuth-authenticated requests.
func (t *Transport) Client() *http.Client {
	return &http.Client{Transport: t}
}

func (t *Transport) transport() http.RoundTripper {
	if t.Transport != nil {
		return t.Transport
	}
	return http.DefaultTransport
}

// AuthCodeURL returns a URL that the end-user should be redirected to,
// so that they may obtain an authorization code.
func (c *Config) AuthCodeURL(state string) string {
	url_, err := url.Parse(c.AuthURL)
	if err != nil {
		panic("AuthURL malformed: " + err.Error())
	}
	q := url.Values{
		"response_type":   {"code"},
		"client_id":       {c.ClientId},
		"redirect_uri":    {c.RedirectURL},
		"scope":           {c.Scope},
		"state":           {state},
		"access_type":     {c.AccessType},
		"approval_prompt": {c.ApprovalPrompt},
	}.Encode()
	if url_.RawQuery == "" {
		url_.RawQuery = q
	} else {
		url_.RawQuery += "&" + q
	}
	return url_.String()
}

// Exchange takes a code and gets access Token from the remote server.
func (t *Transport) Exchange(code string) (*Token, error) {
	if t.Config == nil {
		return nil, OAuthError{"Exchange", "no Config supplied"}
	}

	// If the transport or the cache already has a token, it is
	// passed to `updateToken` to preserve existing refresh token.
	tok := t.Token
	if tok == nil && t.TokenCache != nil {
		tok, _ = t.TokenCache.Token()
	}
	if tok == nil {
		tok = new(Token)
	}
	err := t.updateToken(tok, url.Values{
		"grant_type":   {"authorization_code"},
		"redirect_uri": {t.RedirectURL},
		"scope":        {t.Scope},
		"code":         {code},
	})
	if err != nil {
		return nil, err
	}
	t.Token = tok
	if t.TokenCache != nil {
		return tok, t.TokenCache.PutToken(tok)
	}
	return tok, nil
}

// RoundTrip executes a single HTTP transaction using the Transport's
// Token as authorization headers.
//
// This method will attempt to renew the Token if it has expired and may return
// an error related to that Token renewal before attempting the client request.
// If the Token cannot be renewed a non-nil os.Error value will be returned.
// If the Token is invalid callers should expect HTTP-level errors,
// as indicated by the Response's StatusCode.
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
	if t.Token == nil {
		if t.Config == nil {
			return nil, OAuthError{"RoundTrip", "no Config supplied"}
		}
		if t.TokenCache == nil {
			return nil, OAuthError{"RoundTrip", "no Token supplied"}
		}
		var err error
		t.Token, err = t.TokenCache.Token()
		if err != nil {
			return nil, err
		}
	}

	// Refresh the Token if it has expired.
	if t.Expired() {
		if err := t.Refresh(); err != nil {
			return nil, err
		}
	}

	// To set the Authorization header, we must make a copy of the Request
	// so that we don't modify the Request we were given.
	// This is required by the specification of http.RoundTripper.
	req = cloneRequest(req)
	req.Header.Set("Authorization", "Bearer "+t.AccessToken)

	// Make the HTTP request.
	return t.transport().RoundTrip(req)
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	// deep copy of the Header
	r2.Header = make(http.Header)
	for k, s := range r.Header {
		r2.Header[k] = s
	}
	return r2
}

// Refresh renews the Transport's AccessToken using its RefreshToken.
func (t *Transport) Refresh() error {
	if t.Token == nil {
		return OAuthError{"Refresh", "no existing Token"}
	}
	if t.RefreshToken == "" {
		return OAuthError{"Refresh", "Token expired; no Refresh Token"}
	}
	if t.Config == nil {
		return OAuthError{"Refresh", "no Config supplied"}
	}

	var err error

	if t.Config.Provider == REDDIT {
		err = t.updateToken(t.Token, url.Values{
			"grant_type":    {"refresh_token"},
			"refresh_token": {t.RefreshToken},
			"scope":         {t.Scope},
			"redirect_uri":  {t.RedirectURL},
		})
	} else {
		err = t.updateToken(t.Token, url.Values{
			"grant_type":    {"refresh_token"},
			"refresh_token": {t.RefreshToken},
		})
	}
	if err != nil {
		return err
	}
	if t.TokenCache != nil {
		return t.TokenCache.PutToken(t.Token)
	}
	return nil
}

func (t *Transport) updateToken(tok *Token, v url.Values) error {
	v.Set("client_id", t.ClientId)
	v.Set("client_secret", t.ClientSecret)

	var err error
	var r *http.Response

	if t.Config.Provider == REDDIT {
		buf := new(bytes.Buffer)
		formwriter := multipart.NewWriter(buf)
		for key, values := range v {
			err := formwriter.WriteField(key, values[0])
			if err != nil {
				return err
			}
		}
		formwriter.Close()

		client := &http.Client{Transport: t.transport()}
		req, err := http.NewRequest("POST", t.TokenURL, buf)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", formwriter.FormDataContentType())
		req.SetBasicAuth(t.ClientId, t.ClientSecret)
		r, err = client.Do(req)
	} else {
		r, err = (&http.Client{Transport: t.transport()}).PostForm(t.TokenURL, v)
	}
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if r.StatusCode != 200 {
		return OAuthError{"updateToken", r.Status}
	}
	var b struct {
		Access    string        `json:"access_token"`
		Refresh   string        `json:"refresh_token"`
		ExpiresIn time.Duration `json:"expires_in"`
		Id        string        `json:"id_token"`
	}

	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return err
		}
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return err
		}

		b.Access = vals.Get("access_token")
		b.Refresh = vals.Get("refresh_token")
		b.ExpiresIn, _ = time.ParseDuration(vals.Get("expires_in") + "s")
		b.Id = vals.Get("id_token")
	default:
		if err = json.NewDecoder(r.Body).Decode(&b); err != nil {
			return err
		}
		// The JSON parser treats the unitless ExpiresIn like 'ns' instead of 's' as above,
		// so compensate here.
		b.ExpiresIn *= time.Second
	}
	tok.AccessToken = b.Access
	// Don't overwrite `RefreshToken` with an empty value
	if len(b.Refresh) > 0 {
		tok.RefreshToken = b.Refresh
	}
	if b.ExpiresIn == 0 {
		tok.Expiry = time.Time{}
	} else {
		tok.Expiry = time.Now().Add(b.ExpiresIn)
	}
	if b.Id != "" {
		if tok.Extra == nil {
			tok.Extra = make(map[string]string)
		}
		tok.Extra["id_token"] = b.Id
	}
	return nil
}
<commit_msg>Remove Twitter, add Facebook placeholder<commit_after>// Copyright 2011 The goauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The oauth package provides support for making
// OAuth2-authenticated HTTP requests.
//
// Example usage:
//
//	// Specify your configuration. (typically as a global variable)
//	var config = &oauth.Config{
//		Provider:     [TWITTER, REDDIT]
//		ClientId:     YOUR_CLIENT_ID,
//		ClientSecret: YOUR_CLIENT_SECRET,
//		Scope:        "https://www.googleapis.com/auth/buzz",
//		AuthURL:      "https://accounts.google.com/o/oauth2/auth",
//		TokenURL:     "https://accounts.google.com/o/oauth2/token",
//		RedirectURL:  "http://you.example.org/handler",
//	}
//
//	// A landing page redirects to the OAuth provider to get the auth code.
//	func landing(w http.ResponseWriter, r *http.Request) {
//		http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
//	}
//
//	// The user will be redirected back to this handler, that takes the
//	// "code" query parameter and Exchanges it for an access token.
//	func handler(w http.ResponseWriter, r *http.Request) {
//		t := &oauth.Transport{Config: config}
//		t.Exchange(r.FormValue("code"))
//		// The Transport now has a valid Token. Create an *http.Client
//		// with which we can make authenticated API requests.
//		c := t.Client()
//		c.Post(...)
//		// ...
//		// btw, r.FormValue("state") == "foo"
//	}
//
// 2014-02-20: Twitter doesn't (yet) provider per-user auth using oauth2
//

package oauth

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"time"
)

//Oauth providers
const ( // iota is reset to 0
	GENERIC = iota
	FACEBOOK
	REDDIT
)

type OAuthError struct {
	prefix string
	msg    string
}

func (oe OAuthError) Error() string {
	return "OAuthError: " + oe.prefix + ": " + oe.msg
}

// Cache specifies the methods that implement a Token cache.
type Cache interface {
	Token() (*Token, error)
	PutToken(*Token) error
}

// CacheFile implements Cache. Its value is the name of the file in which
// the Token is stored in JSON format.
type CacheFile string

func (f CacheFile) Token() (*Token, error) {
	file, err := os.Open(string(f))
	if err != nil {
		return nil, OAuthError{"CacheFile.Token", err.Error()}
	}
	defer file.Close()
	tok := &Token{}
	if err := json.NewDecoder(file).Decode(tok); err != nil {
		return nil, OAuthError{"CacheFile.Token", err.Error()}
	}
	return tok, nil
}

func (f CacheFile) PutToken(tok *Token) error {
	file, err := os.OpenFile(string(f), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return OAuthError{"CacheFile.PutToken", err.Error()}
	}
	if err := json.NewEncoder(file).Encode(tok); err != nil {
		file.Close()
		return OAuthError{"CacheFile.PutToken", err.Error()}
	}
	if err := file.Close(); err != nil {
		return OAuthError{"CacheFile.PutToken", err.Error()}
	}
	return nil
}

// Config is the configuration of an OAuth consumer.
type Config struct {
	// Provider is the OAuth provider to use
	Provider int

	// ClientId is the OAuth client identifier used when communicating with
	// the configured OAuth provider.
	ClientId string

	// ClientSecret is the OAuth client secret used when communicating with
	// the configured OAuth provider.
	ClientSecret string

	// Scope identifies the level of access being requested. Multiple scope
	// values should be provided as a space-delimited string.
	Scope string

	// AuthURL is the URL the user will be directed to in order to grant
	// access.
	AuthURL string

	// TokenURL is the URL used to retrieve OAuth tokens.
	TokenURL string

	// RedirectURL is the URL to which the user will be returned after
	// granting (or denying) access.
	RedirectURL string

	// TokenCache allows tokens to be cached for subsequent requests.
	TokenCache Cache

	AccessType string // Optional, "online" (default) or "offline", no refresh token if "online"

	// ApprovalPrompt indicates whether the user should be
	// re-prompted for consent. If set to "auto" (default) the
	// user will be prompted only if they haven't previously
	// granted consent and the code can only be exchanged for an
	// access token.
	// If set to "force" the user will always be prompted, and the
	// code can be exchanged for a refresh token.
	ApprovalPrompt string
}

// Token contains an end-user's tokens.
// This is the data you must store to persist authentication.
type Token struct {
	AccessToken  string
	RefreshToken string
	Expiry       time.Time         // If zero the token has no (known) expiry time.
	Extra        map[string]string // May be nil.
}

func (t *Token) Expired() bool {
	if t.Expiry.IsZero() {
		return false
	}
	return t.Expiry.Before(time.Now())
}

// Transport implements http.RoundTripper. When configured with a valid
// Config and Token it can be used to make authenticated HTTP requests.
//
//	t := &oauth.Transport{config}
//      t.Exchange(code)
//      // t now contains a valid Token
//	r, _, err := t.Client().Get("http://example.org/url/requiring/auth")
//
// It will automatically refresh the Token if it can,
// updating the supplied Token in place.
type Transport struct {
	*Config
	*Token

	// Transport is the HTTP transport to use when making requests.
	// It will default to http.DefaultTransport if nil.
	// (It should never be an oauth.Transport.)
	Transport http.RoundTripper
}

// Client returns an *http.Client that makes OAuth-authenticated requests.
func (t *Transport) Client() *http.Client {
	return &http.Client{Transport: t}
}

func (t *Transport) transport() http.RoundTripper {
	if t.Transport != nil {
		return t.Transport
	}
	return http.DefaultTransport
}

// AuthCodeURL returns a URL that the end-user should be redirected to,
// so that they may obtain an authorization code.
func (c *Config) AuthCodeURL(state string) string {
	url_, err := url.Parse(c.AuthURL)
	if err != nil {
		panic("AuthURL malformed: " + err.Error())
	}
	q := url.Values{
		"response_type":   {"code"},
		"client_id":       {c.ClientId},
		"redirect_uri":    {c.RedirectURL},
		"scope":           {c.Scope},
		"state":           {state},
		"access_type":     {c.AccessType},
		"approval_prompt": {c.ApprovalPrompt},
	}.Encode()
	if url_.RawQuery == "" {
		url_.RawQuery = q
	} else {
		url_.RawQuery += "&" + q
	}
	return url_.String()
}

// Exchange takes a code and gets access Token from the remote server.
func (t *Transport) Exchange(code string) (*Token, error) {
	if t.Config == nil {
		return nil, OAuthError{"Exchange", "no Config supplied"}
	}

	// If the transport or the cache already has a token, it is
	// passed to `updateToken` to preserve existing refresh token.
	tok := t.Token
	if tok == nil && t.TokenCache != nil {
		tok, _ = t.TokenCache.Token()
	}
	if tok == nil {
		tok = new(Token)
	}
	err := t.updateToken(tok, url.Values{
		"grant_type":   {"authorization_code"},
		"redirect_uri": {t.RedirectURL},
		"scope":        {t.Scope},
		"code":         {code},
	})
	if err != nil {
		return nil, err
	}
	t.Token = tok
	if t.TokenCache != nil {
		return tok, t.TokenCache.PutToken(tok)
	}
	return tok, nil
}

// RoundTrip executes a single HTTP transaction using the Transport's
// Token as authorization headers.
//
// This method will attempt to renew the Token if it has expired and may return
// an error related to that Token renewal before attempting the client request.
// If the Token cannot be renewed a non-nil os.Error value will be returned.
// If the Token is invalid callers should expect HTTP-level errors,
// as indicated by the Response's StatusCode.
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
	if t.Token == nil {
		if t.Config == nil {
			return nil, OAuthError{"RoundTrip", "no Config supplied"}
		}
		if t.TokenCache == nil {
			return nil, OAuthError{"RoundTrip", "no Token supplied"}
		}
		var err error
		t.Token, err = t.TokenCache.Token()
		if err != nil {
			return nil, err
		}
	}

	// Refresh the Token if it has expired.
	if t.Expired() {
		if err := t.Refresh(); err != nil {
			return nil, err
		}
	}

	// To set the Authorization header, we must make a copy of the Request
	// so that we don't modify the Request we were given.
	// This is required by the specification of http.RoundTripper.
	req = cloneRequest(req)
	req.Header.Set("Authorization", "Bearer "+t.AccessToken)

	// Make the HTTP request.
	return t.transport().RoundTrip(req)
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	// deep copy of the Header
	r2.Header = make(http.Header)
	for k, s := range r.Header {
		r2.Header[k] = s
	}
	return r2
}

// Refresh renews the Transport's AccessToken using its RefreshToken.
func (t *Transport) Refresh() error {
	if t.Token == nil {
		return OAuthError{"Refresh", "no existing Token"}
	}
	if t.RefreshToken == "" {
		return OAuthError{"Refresh", "Token expired; no Refresh Token"}
	}
	if t.Config == nil {
		return OAuthError{"Refresh", "no Config supplied"}
	}

	var err error

	if t.Config.Provider == REDDIT {
		err = t.updateToken(t.Token, url.Values{
			"grant_type":    {"refresh_token"},
			"refresh_token": {t.RefreshToken},
			"scope":         {t.Scope},
			"redirect_uri":  {t.RedirectURL},
		})
	} else {
		err = t.updateToken(t.Token, url.Values{
			"grant_type":    {"refresh_token"},
			"refresh_token": {t.RefreshToken},
		})
	}
	if err != nil {
		return err
	}
	if t.TokenCache != nil {
		return t.TokenCache.PutToken(t.Token)
	}
	return nil
}

func (t *Transport) updateToken(tok *Token, v url.Values) error {
	v.Set("client_id", t.ClientId)
	v.Set("client_secret", t.ClientSecret)

	var err error
	var r *http.Response

	if t.Config.Provider == REDDIT {
		buf := new(bytes.Buffer)
		formwriter := multipart.NewWriter(buf)
		for key, values := range v {
			err := formwriter.WriteField(key, values[0])
			if err != nil {
				return err
			}
		}
		formwriter.Close()

		client := &http.Client{Transport: t.transport()}
		req, err := http.NewRequest("POST", t.TokenURL, buf)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", formwriter.FormDataContentType())
		req.SetBasicAuth(t.ClientId, t.ClientSecret)
		r, err = client.Do(req)
	} else {
		r, err = (&http.Client{Transport: t.transport()}).PostForm(t.TokenURL, v)
	}
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if r.StatusCode != 200 {
		return OAuthError{"updateToken", r.Status}
	}
	var b struct {
		Access    string        `json:"access_token"`
		Refresh   string        `json:"refresh_token"`
		ExpiresIn time.Duration `json:"expires_in"`
		Id        string        `json:"id_token"`
	}

	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return err
		}
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return err
		}

		b.Access = vals.Get("access_token")
		b.Refresh = vals.Get("refresh_token")
		b.ExpiresIn, _ = time.ParseDuration(vals.Get("expires_in") + "s")
		b.Id = vals.Get("id_token")
	default:
		if err = json.NewDecoder(r.Body).Decode(&b); err != nil {
			return err
		}
		// The JSON parser treats the unitless ExpiresIn like 'ns' instead of 's' as above,
		// so compensate here.
		b.ExpiresIn *= time.Second
	}
	tok.AccessToken = b.Access
	// Don't overwrite `RefreshToken` with an empty value
	if len(b.Refresh) > 0 {
		tok.RefreshToken = b.Refresh
	}
	if b.ExpiresIn == 0 {
		tok.Expiry = time.Time{}
	} else {
		tok.Expiry = time.Now().Add(b.ExpiresIn)
	}
	if b.Id != "" {
		if tok.Extra == nil {
			tok.Extra = make(map[string]string)
		}
		tok.Extra["id_token"] = b.Id
	}
	return nil
}
<|endoftext|>