<commit_before>package parsers

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
)

// ViewContents is the result from the view contents parser
type ViewContents struct {
	Items []ViewContentsItem
	lines []int
}

// Name returns the parser name
func (r *ViewContents) Name() string {
	return "view_contents"
}

// Lines returns the lines that this result is made from
func (r *ViewContents) Lines() []int {
	return r.lines
}

// ViewContentsItem is a single item from a view contents result
type ViewContentsItem struct {
	Name     string
	Group    string
	Location string
	Quantity int64
}

var reViewContents = regexp.MustCompile(strings.Join([]string{
	`^([\S ]*)\t`, // name
	`([\S ]*)\t`,  // group
	`(Cargo Hold|Ore Hold|(?:Drone|Fuel) Bay|(?:Low|Medium|High|Rig) Slot|Subsystem|Fighter Bay|Fighter Launch Tube|Planetary Commodities Hold|)\t`, // location
	`([\d,'\.]+)$`, // quantity
}, ""))

var reViewContents2 = regexp.MustCompile(strings.Join([]string{
	`^([\S ]*)\t`,  // name
	`([\S ]*)\t`,   // group
	`([\d,'\.]+)$`, // quantity
}, ""))

// ParseViewContents parses view contents text
func ParseViewContents(input Input) (ParserResult, Input) {
	viewContents := &ViewContents{}
	matches, rest := regexParseLines(reViewContents, input)
	matches2, rest := regexParseLines(reViewContents2, rest)
	viewContents.lines = append(viewContents.lines, regexMatchedLines(matches)...)
	viewContents.lines = append(viewContents.lines, regexMatchedLines(matches2)...)

	matchgroup := make(map[ViewContentsItem]int64)
	for _, match := range matches {
		item := ViewContentsItem{
			Name:     CleanTypeName(match[1]),
			Group:    match[2],
			Location: match[3],
		}
		matchgroup[item] += ToInt(match[4])
	}

	for _, match := range matches2 {
		item := ViewContentsItem{
			Name:  CleanTypeName(match[1]),
			Group: match[2],
		}
		matchgroup[item] += ToInt(match[3])
	}

	// add items w/totals
	for item, quantity := range matchgroup {
		item.Quantity = quantity
		viewContents.Items = append(viewContents.Items, item)
	}

	sort.Slice(viewContents.Items, func(i, j int) bool {
		return fmt.Sprintf("%v", viewContents.Items[i]) < fmt.Sprintf("%v", viewContents.Items[j])
	})

	return viewContents, rest
}
<commit_msg>Add support for Planetary Commodities Hold<commit_after>package parsers

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
)

// ViewContents is the result from the view contents parser
type ViewContents struct {
	Items []ViewContentsItem
	lines []int
}

// Name returns the parser name
func (r *ViewContents) Name() string {
	return "view_contents"
}

// Lines returns the lines that this result is made from
func (r *ViewContents) Lines() []int {
	return r.lines
}

// ViewContentsItem is a single item from a view contents result
type ViewContentsItem struct {
	Name     string
	Group    string
	Location string
	Quantity int64
}

var reViewContents = regexp.MustCompile(strings.Join([]string{
	`^([\S ]*)\t`, // name
	`([\S ]*)\t`,  // group
	`((?:Cargo|Ore|Planetary Commodities) Hold|(?:Drone|Fuel|Fighter) Bay|(?:Low|Medium|High|Rig) Slot|Subsystem|Fighter Launch Tube|)\t`, // location
	`([\d,'\.]+)$`, // quantity
}, ""))

var reViewContents2 = regexp.MustCompile(strings.Join([]string{
	`^([\S ]*)\t`,  // name
	`([\S ]*)\t`,   // group
	`([\d,'\.]+)$`, // quantity
}, ""))

// ParseViewContents parses view contents text
func ParseViewContents(input Input) (ParserResult, Input) {
	viewContents := &ViewContents{}
	matches, rest := regexParseLines(reViewContents, input)
	matches2, rest := regexParseLines(reViewContents2, rest)
	viewContents.lines = append(viewContents.lines, regexMatchedLines(matches)...)
	viewContents.lines = append(viewContents.lines, regexMatchedLines(matches2)...)

	matchgroup := make(map[ViewContentsItem]int64)
	for _, match := range matches {
		item := ViewContentsItem{
			Name:     CleanTypeName(match[1]),
			Group:    match[2],
			Location: match[3],
		}
		matchgroup[item] += ToInt(match[4])
	}

	for _, match := range matches2 {
		item := ViewContentsItem{
			Name:  CleanTypeName(match[1]),
			Group: match[2],
		}
		matchgroup[item] += ToInt(match[3])
	}

	// add items w/totals
	for item, quantity := range matchgroup {
		item.Quantity = quantity
		viewContents.Items = append(viewContents.Items, item)
	}

	sort.Slice(viewContents.Items, func(i, j int) bool {
		return fmt.Sprintf("%v", viewContents.Items[i]) < fmt.Sprintf("%v", viewContents.Items[j])
	})

	return viewContents, rest
}
<|endoftext|>


<commit_before>package repo

import (
	"database/sql/driver"
	"fmt"
)

// StatType is the name of a repository statistic (see below for a listing).
type StatType string

// Stats holds statistics for a repository.
type Stats map[StatType]int

const (
	// StatXRefs is the number of external references to any symbol defined in a
	// repository (i.e., references from other repositories). It is only
	// computed per-repository (and not per-repository-commit) because it is
	// not easy to determine which specific commit a ref references.
	StatXRefs = "xrefs"

	// StatAuthors is the number of resolved people who contributed code to any
	// symbol defined in a repository (i.e., references from other
	// repositories). It is only computed per-repository-commit.
	StatAuthors = "authors"

	// StatClients is the number of resolved people who have committed refs that
	// reference a symbol defined in the repository. It is only computed
	// per-repository (and not per-repository-commit) because it is not easy to
	// determine which specific commit a ref references.
	StatClients = "clients"

	// StatDependencies is the number of repositories that the repository
	// depends on. It is only computed per-repository-commit.
	StatDependencies = "dependencies"

	// StatDependents is the number of repositories containing refs to a symbol
	// defined in the repository. It is only computed per-repository (and not
	// per-repository-commit) because it is not easy to determine which specific
	// commit a ref references.
	StatDependents = "dependents"

	// StatSymbols is the number of symbols defined in a repository commit. It
	// is only computed per-repository-commit (or else it would count 1 symbol
	// for each revision of the repository that we have processed).
	StatSymbols = "symbols"

	// StatExportedSymbols is the number of exported symbols defined in a
	// repository commit. It is only computed per-repository-commit (or else it
	// would count 1 symbol for each revision of the repository that we have
	// processed).
	StatExportedSymbols = "exported-symbols"
)

// Value implements database/sql/driver.Valuer.
func (x StatType) Value() (driver.Value, error) {
	return string(x), nil
}

// Scan implements database/sql.Scanner.
func (x *StatType) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = StatType(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}
<commit_msg>factor out stat sort in Symbols.List<commit_after>package repo

import (
	"database/sql/driver"
	"fmt"
)

// StatType is the name of a repository statistic (see below for a listing).
type StatType string

// Stats holds statistics for a repository.
type Stats map[StatType]int

const (
	// StatXRefs is the number of external references to any symbol defined in a
	// repository (i.e., references from other repositories). It is only
	// computed per-repository (and not per-repository-commit) because it is
	// not easy to determine which specific commit a ref references.
	StatXRefs = "xrefs"

	// StatAuthors is the number of resolved people who contributed code to any
	// symbol defined in a repository (i.e., references from other
	// repositories). It is only computed per-repository-commit.
	StatAuthors = "authors"

	// StatClients is the number of resolved people who have committed refs that
	// reference a symbol defined in the repository. It is only computed
	// per-repository (and not per-repository-commit) because it is not easy to
	// determine which specific commit a ref references.
	StatClients = "clients"

	// StatDependencies is the number of repositories that the repository
	// depends on. It is only computed per-repository-commit.
	StatDependencies = "dependencies"

	// StatDependents is the number of repositories containing refs to a symbol
	// defined in the repository. It is only computed per-repository (and not
	// per-repository-commit) because it is not easy to determine which specific
	// commit a ref references.
	StatDependents = "dependents"

	// StatSymbols is the number of symbols defined in a repository commit. It
	// is only computed per-repository-commit (or else it would count 1 symbol
	// for each revision of the repository that we have processed).
	StatSymbols = "symbols"

	// StatExportedSymbols is the number of exported symbols defined in a
	// repository commit. It is only computed per-repository-commit (or else it
	// would count 1 symbol for each revision of the repository that we have
	// processed).
	StatExportedSymbols = "exported-symbols"
)

var StatTypes = map[StatType]struct{}{StatXRefs: struct{}{}, StatAuthors: struct{}{}, StatClients: struct{}{}, StatDependencies: struct{}{}, StatDependents: struct{}{}, StatSymbols: struct{}{}, StatExportedSymbols: struct{}{}}

// Value implements database/sql/driver.Valuer.
func (x StatType) Value() (driver.Value, error) {
	return string(x), nil
}

// Scan implements database/sql.Scanner.
func (x *StatType) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = StatType(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}
<|endoftext|>


<commit_before>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
/*
 *  Filename:    repository.go
 *  Package:     main
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Sun Jul  3 19:10:10 PDT 2011
 *  Description:
 */
import (
    "os"
    "exec"
)

type RepoType int
const(
    NilRepoType RepoType = iota
    GitType
    MercurialType
    // ...
)

type Repository interface {
    Initialize(commit bool) os.Error // Initialize the working directory as a new repository.
    Add(paths...string)     os.Error // Add a set of paths to the repository.
    Commit(message string)  os.Error // Commit changes with a given message.
    //IsClean()               bool     // Returns true if there is nothing to commit on the working branch.
}

type GitRepository struct {
}
func (git GitRepository) Initialize(add, commit bool) os.Error {
    var (
        initcmd = exec.Command("git", "init")
    )
    errInit := initcmd.Run()
    if errInit != nil {
        return errInit
    }
    if add {
        errAdd := git.Add(".")
        if errAdd != nil {
            return errAdd
        }
    }
    if commit {
        errCommit := git.Commit("Empty project created by gonew")
        if errCommit != nil {
            return errCommit
        }
    }
    return nil
}

func (git GitRepository) Add(paths...string) os.Error {
    var cmdslice = make([]string,len(paths)+1)
    cmdslice[0] = "add"
    copy(cmdslice[1:], paths)
    var (
        addcmd = exec.Command("git", cmdslice...)
        errAdd = addcmd.Run()
    )
    return errAdd
}

func (git GitRepository) Commit(message string) os.Error {
    var (
        commitcmd = exec.Command("git", "commit",
            "-a", "-m", "Empty project generated by gonew.")
        errCommit = commitcmd.Run()
    )
    return errCommit
}

type MercurialRepository struct {
}
func (hg MercurialRepository) Initialize(add, commit bool) os.Error {
    var (
        initcmd = exec.Command("hg", "init")
    )
    errInit := initcmd.Run()
    if errInit != nil {
        return errInit
    }
    if add {
        errAdd := hg.Add(".")
        if errAdd != nil {
            return errAdd
        }
    }
    if commit {
        errCommit := hg.Commit("Empty project created by gonew")
        if errCommit != nil {
            return errCommit
        }
    }
    return nil
}
func (hg MercurialRepository) Add(paths...string) os.Error {
    var cmdslice = make([]string,len(paths)+1)
    cmdslice[0] = "add"
    copy(cmdslice[1:], paths)
    var (
        addcmd = exec.Command("hg", cmdslice...)
        errAdd = addcmd.Run()
    )
    return errAdd
}

func (hg MercurialRepository) Commit(message string) os.Error {
    var (
        commitcmd = exec.Command("hg", "commit",
            "-a", "-m", "Empty project generated by gonew.")
        errCommit = commitcmd.Run()
    )
    return errCommit
}
<commit_msg>Fix mercurial add/commit bug.<commit_after>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
/*
 *  Filename:    repository.go
 *  Package:     main
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Sun Jul  3 19:10:10 PDT 2011
 *  Description:
 */
import (
    "os"
    "exec"
)

type RepoType int
const(
    NilRepoType RepoType = iota
    GitType
    MercurialType
    // ...
)

type Repository interface {
    Initialize(commit bool) os.Error // Initialize the working directory as a new repository.
    Add(paths...string)     os.Error // Add a set of paths to the repository.
    Commit(message string)  os.Error // Commit changes with a given message.
    //IsClean()               bool     // Returns true if there is nothing to commit on the working branch.
}

type GitRepository struct {
}
func (git GitRepository) Initialize(add, commit bool) os.Error {
    var (
        initcmd = exec.Command("git", "init")
    )
    errInit := initcmd.Run()
    if errInit != nil {
        return errInit
    }
    if add {
        errAdd := git.Add(".")
        if errAdd != nil {
            return errAdd
        }
    }
    if commit {
        errCommit := git.Commit("Empty project created by gonew")
        if errCommit != nil {
            return errCommit
        }
    }
    return nil
}

func (git GitRepository) Add(paths...string) os.Error {
    var cmdslice = make([]string,len(paths)+1)
    cmdslice[0] = "add"
    copy(cmdslice[1:], paths)
    var (
        addcmd = exec.Command("git", cmdslice...)
        errAdd = addcmd.Run()
    )
    return errAdd
}

func (git GitRepository) Commit(message string) os.Error {
    var (
        commitcmd = exec.Command("git", "commit",
            "-a", "-m", "Empty project generated by gonew.")
        errCommit = commitcmd.Run()
    )
    return errCommit
}

type MercurialRepository struct {
}
func (hg MercurialRepository) Initialize(add, commit bool) os.Error {
    var (
        initcmd = exec.Command("hg", "init")
    )
    errInit := initcmd.Run()
    if errInit != nil {
        return errInit
    }
    if add {
        errAdd := hg.Add()
        if errAdd != nil {
            return errAdd
        }
    }
    if commit {
        errCommit := hg.Commit("Empty project created by gonew")
        if errCommit != nil {
            return errCommit
        }
    }
    return nil
}
func (hg MercurialRepository) Add(paths...string) os.Error {
    var cmdslice = make([]string,len(paths)+1)
    cmdslice[0] = "add"
    copy(cmdslice[1:], paths)
    var (
        addcmd = exec.Command("hg", cmdslice...)
        errAdd = addcmd.Run()
    )
    return errAdd
}

func (hg MercurialRepository) Commit(message string) os.Error {
    var (
        commitcmd = exec.Command("hg", "commit",
            "-m", "Empty project generated by gonew.")
        errCommit = commitcmd.Run()
    )
    return errCommit
}
<|endoftext|>


<commit_before>package torrent

import (
	"container/heap"
	"context"
	"encoding/gob"
	"reflect"
	"runtime/pprof"
	"time"
	"unsafe"

	"github.com/RoaringBitmap/roaring"
	"github.com/anacrolix/log"
	"github.com/anacrolix/missinggo/v2/bitmap"
	"github.com/anacrolix/multiless"

	request_strategy "github.com/anacrolix/torrent/request-strategy"
)

func (cl *Client) tickleRequester() {
	cl.updateRequests.Broadcast()
}

func (cl *Client) getRequestStrategyInput() request_strategy.Input {
	ts := make([]request_strategy.Torrent, 0, len(cl.torrents))
	for _, t := range cl.torrents {
		if !t.haveInfo() {
			// This would be removed if metadata is handled here. We have to guard against not
			// knowing the piece size. If we have no info, we have no pieces too, so the end result
			// is the same.
			continue
		}
		rst := request_strategy.Torrent{
			InfoHash:       t.infoHash,
			ChunksPerPiece: t.chunksPerRegularPiece(),
		}
		if t.storage != nil {
			rst.Capacity = t.storage.Capacity
		}
		rst.Pieces = make([]request_strategy.Piece, 0, len(t.pieces))
		for i := range t.pieces {
			p := &t.pieces[i]
			rst.Pieces = append(rst.Pieces, request_strategy.Piece{
				Request:           !t.ignorePieceForRequests(i),
				Priority:          p.purePriority(),
				Partial:           t.piecePartiallyDownloaded(i),
				Availability:      p.availability,
				Length:            int64(p.length()),
				NumPendingChunks:  int(t.pieceNumPendingChunks(i)),
				IterPendingChunks: p.undirtiedChunksIter(),
			})
		}
		t.iterPeers(func(p *Peer) {
			if p.closed.IsSet() {
				return
			}
			if p.piecesReceivedSinceLastRequestUpdate > p.maxPiecesReceivedBetweenRequestUpdates {
				p.maxPiecesReceivedBetweenRequestUpdates = p.piecesReceivedSinceLastRequestUpdate
			}
			p.piecesReceivedSinceLastRequestUpdate = 0
			rst.Peers = append(rst.Peers, request_strategy.Peer{
				Pieces:           *p.newPeerPieces(),
				MaxRequests:      p.nominalMaxRequests(),
				ExistingRequests: p.actualRequestState.Requests,
				Choking:          p.peerChoking,
				PieceAllowedFast: p.peerAllowedFast,
				DownloadRate:     p.downloadRate(),
				Age:              time.Since(p.completedHandshake),
				Id: peerId{
					Peer: p,
					ptr:  uintptr(unsafe.Pointer(p)),
				},
			})
		})
		ts = append(ts, rst)
	}
	return request_strategy.Input{
		Torrents:           ts,
		MaxUnverifiedBytes: cl.config.MaxUnverifiedBytes,
	}
}

func init() {
	gob.Register(peerId{})
}

type peerId struct {
	*Peer
	ptr uintptr
}

func (p peerId) Uintptr() uintptr {
	return p.ptr
}

func (p peerId) GobEncode() (b []byte, _ error) {
	*(*reflect.SliceHeader)(unsafe.Pointer(&b)) = reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(&p.ptr)),
		Len:  int(unsafe.Sizeof(p.ptr)),
		Cap:  int(unsafe.Sizeof(p.ptr)),
	}
	return
}

func (p *peerId) GobDecode(b []byte) error {
	if uintptr(len(b)) != unsafe.Sizeof(p.ptr) {
		panic(len(b))
	}
	ptr := unsafe.Pointer(&b[0])
	p.ptr = *(*uintptr)(ptr)
	log.Printf("%p", ptr)
	dst := reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(&p.Peer)),
		Len:  int(unsafe.Sizeof(p.Peer)),
		Cap:  int(unsafe.Sizeof(p.Peer)),
	}
	copy(*(*[]byte)(unsafe.Pointer(&dst)), b)
	return nil
}

type RequestIndex = request_strategy.RequestIndex
type chunkIndexType = request_strategy.ChunkIndex

type peerRequests struct {
	requestIndexes       []RequestIndex
	peer                 *Peer
	torrentStrategyInput request_strategy.Torrent
}

func (p peerRequests) Len() int {
	return len(p.requestIndexes)
}

func (p peerRequests) Less(i, j int) bool {
	leftRequest := p.requestIndexes[i]
	rightRequest := p.requestIndexes[j]
	t := p.peer.t
	leftPieceIndex := leftRequest / p.torrentStrategyInput.ChunksPerPiece
	rightPieceIndex := rightRequest / p.torrentStrategyInput.ChunksPerPiece
	leftCurrent := p.peer.actualRequestState.Requests.Contains(leftRequest)
	rightCurrent := p.peer.actualRequestState.Requests.Contains(rightRequest)
	pending := func(index RequestIndex, current bool) int {
		ret := t.pendingRequests.Get(index)
		if current {
			ret--
		}
		return ret
	}
	ml := multiless.New()
	// Push requests that can't be served right now to the end. But we don't throw them away unless
	// there's a better alternative. This is for when we're using the fast extension and get choked
	// but our requests could still be good when we get unchoked.
	if p.peer.peerChoking {
		ml = ml.Bool(
			!p.peer.peerAllowedFast.Contains(leftPieceIndex),
			!p.peer.peerAllowedFast.Contains(rightPieceIndex),
		)
	}
	ml = ml.Int(
		pending(leftRequest, leftCurrent),
		pending(rightRequest, rightCurrent))
	ml = ml.Bool(rightCurrent, leftCurrent)
	ml = ml.Int(
		int(p.torrentStrategyInput.Pieces[rightPieceIndex].Priority),
		int(p.torrentStrategyInput.Pieces[leftPieceIndex].Priority))
	ml = ml.Int(
		int(p.torrentStrategyInput.Pieces[leftPieceIndex].Availability),
		int(p.torrentStrategyInput.Pieces[rightPieceIndex].Availability))
	ml = ml.Uint32(leftPieceIndex, rightPieceIndex)
	ml = ml.Uint32(leftRequest, rightRequest)
	return ml.MustLess()
}

func (p peerRequests) Swap(i, j int) {
	p.requestIndexes[i], p.requestIndexes[j] = p.requestIndexes[j], p.requestIndexes[i]
}

func (p *peerRequests) Push(x interface{}) {
	p.requestIndexes = append(p.requestIndexes, x.(RequestIndex))
}

func (p *peerRequests) Pop() interface{} {
	last := len(p.requestIndexes) - 1
	x := p.requestIndexes[last]
	p.requestIndexes = p.requestIndexes[:last]
	return x
}

func (p *Peer) getDesiredRequestState() (desired requestState) {
	input := p.t.cl.getRequestStrategyInput()
	requestHeap := peerRequests{
		peer: p,
	}
	for _, t := range input.Torrents {
		if t.InfoHash == p.t.infoHash {
			requestHeap.torrentStrategyInput = t
			break
		}
	}
	request_strategy.GetRequestablePieces(
		input,
		func(t *request_strategy.Torrent, rsp *request_strategy.Piece, pieceIndex int) {
			if t.InfoHash != p.t.infoHash {
				return
			}
			if !p.peerHasPiece(pieceIndex) {
				return
			}
			allowedFast := p.peerAllowedFast.ContainsInt(pieceIndex)
			rsp.IterPendingChunks.Iter(func(ci request_strategy.ChunkIndex) {
				if !allowedFast {
					// We must signal interest to request this..
					desired.Interested = true
					if p.peerChoking && !p.actualRequestState.Requests.Contains(ci) {
						// We can't request this right now.
						return
					}
				}
				requestHeap.requestIndexes = append(
					requestHeap.requestIndexes,
					p.t.pieceRequestIndexOffset(pieceIndex)+ci)
			})
		},
	)
	heap.Init(&requestHeap)
	for requestHeap.Len() != 0 && desired.Requests.GetCardinality() < uint64(p.nominalMaxRequests()) {
		requestIndex := heap.Pop(&requestHeap).(RequestIndex)
		desired.Requests.Add(requestIndex)
	}
	return
}

func (p *Peer) applyNextRequestState() bool {
	if p.needRequestUpdate == "" {
		return true
	}
	var more bool
	pprof.Do(
		context.Background(),
		pprof.Labels("update request", p.needRequestUpdate),
		func(_ context.Context) {
			next := p.getDesiredRequestState()
			more = p.applyRequestState(next)
		},
	)
	return more
}

func (p *Peer) applyRequestState(next requestState) bool {
	current := p.actualRequestState
	if !p.setInterested(next.Interested) {
		return false
	}
	more := true
	cancel := roaring.AndNot(&current.Requests, &next.Requests)
	cancel.Iterate(func(req uint32) bool {
		more = p.cancel(req)
		return more
	})
	if !more {
		return false
	}
	next.Requests.Iterate(func(req uint32) bool {
		// This could happen if the peer chokes us between the next state being generated, and us
		// trying to transmit the state.
		if p.peerChoking && !p.peerAllowedFast.Contains(bitmap.BitIndex(req/p.t.chunksPerRegularPiece())) {
			return true
		}
		var err error
		more, err = p.request(req)
		if err != nil {
			panic(err)
		} /* else {
			log.Print(req)
		} */
		return more
	})
	if more {
		p.needRequestUpdate = ""
	}
	return more
}
<commit_msg>Simplify request ordering for reversed conditions<commit_after>package torrent

import (
	"container/heap"
	"context"
	"encoding/gob"
	"reflect"
	"runtime/pprof"
	"time"
	"unsafe"

	"github.com/RoaringBitmap/roaring"
	"github.com/anacrolix/log"
	"github.com/anacrolix/missinggo/v2/bitmap"
	"github.com/anacrolix/multiless"

	request_strategy "github.com/anacrolix/torrent/request-strategy"
)

func (cl *Client) tickleRequester() {
	cl.updateRequests.Broadcast()
}

func (cl *Client) getRequestStrategyInput() request_strategy.Input {
	ts := make([]request_strategy.Torrent, 0, len(cl.torrents))
	for _, t := range cl.torrents {
		if !t.haveInfo() {
			// This would be removed if metadata is handled here. We have to guard against not
			// knowing the piece size. If we have no info, we have no pieces too, so the end result
			// is the same.
			continue
		}
		rst := request_strategy.Torrent{
			InfoHash:       t.infoHash,
			ChunksPerPiece: t.chunksPerRegularPiece(),
		}
		if t.storage != nil {
			rst.Capacity = t.storage.Capacity
		}
		rst.Pieces = make([]request_strategy.Piece, 0, len(t.pieces))
		for i := range t.pieces {
			p := &t.pieces[i]
			rst.Pieces = append(rst.Pieces, request_strategy.Piece{
				Request:           !t.ignorePieceForRequests(i),
				Priority:          p.purePriority(),
				Partial:           t.piecePartiallyDownloaded(i),
				Availability:      p.availability,
				Length:            int64(p.length()),
				NumPendingChunks:  int(t.pieceNumPendingChunks(i)),
				IterPendingChunks: p.undirtiedChunksIter(),
			})
		}
		t.iterPeers(func(p *Peer) {
			if p.closed.IsSet() {
				return
			}
			if p.piecesReceivedSinceLastRequestUpdate > p.maxPiecesReceivedBetweenRequestUpdates {
				p.maxPiecesReceivedBetweenRequestUpdates = p.piecesReceivedSinceLastRequestUpdate
			}
			p.piecesReceivedSinceLastRequestUpdate = 0
			rst.Peers = append(rst.Peers, request_strategy.Peer{
				Pieces:           *p.newPeerPieces(),
				MaxRequests:      p.nominalMaxRequests(),
				ExistingRequests: p.actualRequestState.Requests,
				Choking:          p.peerChoking,
				PieceAllowedFast: p.peerAllowedFast,
				DownloadRate:     p.downloadRate(),
				Age:              time.Since(p.completedHandshake),
				Id: peerId{
					Peer: p,
					ptr:  uintptr(unsafe.Pointer(p)),
				},
			})
		})
		ts = append(ts, rst)
	}
	return request_strategy.Input{
		Torrents:           ts,
		MaxUnverifiedBytes: cl.config.MaxUnverifiedBytes,
	}
}

func init() {
	gob.Register(peerId{})
}

type peerId struct {
	*Peer
	ptr uintptr
}

func (p peerId) Uintptr() uintptr {
	return p.ptr
}

func (p peerId) GobEncode() (b []byte, _ error) {
	*(*reflect.SliceHeader)(unsafe.Pointer(&b)) = reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(&p.ptr)),
		Len:  int(unsafe.Sizeof(p.ptr)),
		Cap:  int(unsafe.Sizeof(p.ptr)),
	}
	return
}

func (p *peerId) GobDecode(b []byte) error {
	if uintptr(len(b)) != unsafe.Sizeof(p.ptr) {
		panic(len(b))
	}
	ptr := unsafe.Pointer(&b[0])
	p.ptr = *(*uintptr)(ptr)
	log.Printf("%p", ptr)
	dst := reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(&p.Peer)),
		Len:  int(unsafe.Sizeof(p.Peer)),
		Cap:  int(unsafe.Sizeof(p.Peer)),
	}
	copy(*(*[]byte)(unsafe.Pointer(&dst)), b)
	return nil
}

type RequestIndex = request_strategy.RequestIndex
type chunkIndexType = request_strategy.ChunkIndex

type peerRequests struct {
	requestIndexes       []RequestIndex
	peer                 *Peer
	torrentStrategyInput request_strategy.Torrent
}

func (p peerRequests) Len() int {
	return len(p.requestIndexes)
}

func (p peerRequests) Less(i, j int) bool {
	leftRequest := p.requestIndexes[i]
	rightRequest := p.requestIndexes[j]
	t := p.peer.t
	leftPieceIndex := leftRequest / p.torrentStrategyInput.ChunksPerPiece
	rightPieceIndex := rightRequest / p.torrentStrategyInput.ChunksPerPiece
	leftCurrent := p.peer.actualRequestState.Requests.Contains(leftRequest)
	rightCurrent := p.peer.actualRequestState.Requests.Contains(rightRequest)
	pending := func(index RequestIndex, current bool) int {
		ret := t.pendingRequests.Get(index)
		if current {
			ret--
		}
		return ret
	}
	ml := multiless.New()
	// Push requests that can't be served right now to the end. But we don't throw them away unless
	// there's a better alternative. This is for when we're using the fast extension and get choked
	// but our requests could still be good when we get unchoked.
	if p.peer.peerChoking {
		ml = ml.Bool(
			!p.peer.peerAllowedFast.Contains(leftPieceIndex),
			!p.peer.peerAllowedFast.Contains(rightPieceIndex),
		)
	}
	ml = ml.Int(
		pending(leftRequest, leftCurrent),
		pending(rightRequest, rightCurrent))
	ml = ml.Bool(!leftCurrent, !rightCurrent)
	ml = ml.Int(
		-int(p.torrentStrategyInput.Pieces[leftPieceIndex].Priority),
		-int(p.torrentStrategyInput.Pieces[rightPieceIndex].Priority),
	)
	ml = ml.Int(
		int(p.torrentStrategyInput.Pieces[leftPieceIndex].Availability),
		int(p.torrentStrategyInput.Pieces[rightPieceIndex].Availability))
	ml = ml.Uint32(leftPieceIndex, rightPieceIndex)
	ml = ml.Uint32(leftRequest, rightRequest)
	return ml.MustLess()
}

func (p *peerRequests) Swap(i, j int) {
	p.requestIndexes[i], p.requestIndexes[j] = p.requestIndexes[j], p.requestIndexes[i]
}

func (p *peerRequests) Push(x interface{}) {
	p.requestIndexes = append(p.requestIndexes, x.(RequestIndex))
}

func (p *peerRequests) Pop() interface{} {
	last := len(p.requestIndexes) - 1
	x := p.requestIndexes[last]
	p.requestIndexes = p.requestIndexes[:last]
	return x
}

func (p *Peer) getDesiredRequestState() (desired requestState) {
	input := p.t.cl.getRequestStrategyInput()
	requestHeap := peerRequests{
		peer: p,
	}
	for _, t := range input.Torrents {
		if t.InfoHash == p.t.infoHash {
			requestHeap.torrentStrategyInput = t
			break
		}
	}
	request_strategy.GetRequestablePieces(
		input,
		func(t *request_strategy.Torrent, rsp *request_strategy.Piece, pieceIndex int) {
			if t.InfoHash != p.t.infoHash {
				return
			}
			if !p.peerHasPiece(pieceIndex) {
				return
			}
			allowedFast := p.peerAllowedFast.ContainsInt(pieceIndex)
			rsp.IterPendingChunks.Iter(func(ci request_strategy.ChunkIndex) {
				if !allowedFast {
					// We must signal interest to request this..
					desired.Interested = true
					if p.peerChoking && !p.actualRequestState.Requests.Contains(ci) {
						// We can't request this right now.
						return
					}
				}
				requestHeap.requestIndexes = append(
					requestHeap.requestIndexes,
					p.t.pieceRequestIndexOffset(pieceIndex)+ci)
			})
		},
	)
	heap.Init(&requestHeap)
	for requestHeap.Len() != 0 && desired.Requests.GetCardinality() < uint64(p.nominalMaxRequests()) {
		requestIndex := heap.Pop(&requestHeap).(RequestIndex)
		desired.Requests.Add(requestIndex)
	}
	return
}

func (p *Peer) applyNextRequestState() bool {
	if p.needRequestUpdate == "" {
		return true
	}
	var more bool
	pprof.Do(
		context.Background(),
		pprof.Labels("update request", p.needRequestUpdate),
		func(_ context.Context) {
			next := p.getDesiredRequestState()
			more = p.applyRequestState(next)
		},
	)
	return more
}

func (p *Peer) applyRequestState(next requestState) bool {
	current := p.actualRequestState
	if !p.setInterested(next.Interested) {
		return false
	}
	more := true
	cancel := roaring.AndNot(&current.Requests, &next.Requests)
	cancel.Iterate(func(req uint32) bool {
		more = p.cancel(req)
		return more
	})
	if !more {
		return false
	}
	next.Requests.Iterate(func(req uint32) bool {
		// This could happen if the peer chokes us between the next state being generated, and us
		// trying to transmit the state.
		if p.peerChoking && !p.peerAllowedFast.Contains(bitmap.BitIndex(req/p.t.chunksPerRegularPiece())) {
			return true
		}
		var err error
		more, err = p.request(req)
		if err != nil {
			panic(err)
		} /* else {
			log.Print(req)
		} */
		return more
	})
	if more {
		p.needRequestUpdate = ""
	}
	return more
}
<|endoftext|>


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

package syscall

import (
	"internal/syscall/windows/sysdll"
	"sync"
	"sync/atomic"
	"unsafe"
)

// DLLError describes reasons for DLL load failures.
type DLLError struct {
	Err     error
	ObjName string
	Msg     string
}

func (e *DLLError) Error() string { return e.Msg }

// Implemented in ../runtime/syscall_windows.go.
func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno)
func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno)
func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno)
func loadlibrary(filename *uint16) (handle uintptr, err Errno)
func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)

// A DLL implements access to a single DLL.
type DLL struct {
	Name   string
	Handle Handle
}

// LoadDLL loads the named DLL file into memory.
//
// If name is not an absolute path and is not a known system DLL used by
// Go, Windows will search for the named DLL in many locations, causing
// potential DLL preloading attacks.
//
// Use LazyDLL in golang.org/x/sys/windows for a secure way to
// load system DLLs.
func LoadDLL(name string) (*DLL, error) {
	namep, err := UTF16PtrFromString(name)
	if err != nil {
		return nil, err
	}
	var h uintptr
	var e Errno
	if sysdll.IsSystemDLL[name] {
		h, e = loadsystemlibrary(namep)
	} else {
		h, e = loadlibrary(namep)
	}
	if e != 0 {
		return nil, &DLLError{
			Err:     e,
			ObjName: name,
			Msg:     "Failed to load " + name + ": " + e.Error(),
		}
	}
	d := &DLL{
		Name:   name,
		Handle: Handle(h),
	}
	return d, nil
}

// MustLoadDLL is like LoadDLL but panics if load operation fails.
func MustLoadDLL(name string) *DLL {
	d, e := LoadDLL(name)
	if e != nil {
		panic(e)
	}
	return d
}

// FindProc searches DLL d for procedure named name and returns *Proc
// if found. It returns an error if search fails.
func (d *DLL) FindProc(name string) (proc *Proc, err error) {
	namep, err := BytePtrFromString(name)
	if err != nil {
		return nil, err
	}
	a, e := getprocaddress(uintptr(d.Handle), namep)
	if e != 0 {
		return nil, &DLLError{
			Err:     e,
			ObjName: name,
			Msg:     "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
		}
	}
	p := &Proc{
		Dll:  d,
		Name: name,
		addr: a,
	}
	return p, nil
}

// MustFindProc is like FindProc but panics if search fails.
func (d *DLL) MustFindProc(name string) *Proc {
	p, e := d.FindProc(name)
	if e != nil {
		panic(e)
	}
	return p
}

// Release unloads DLL d from memory.
func (d *DLL) Release() (err error) {
	return FreeLibrary(d.Handle)
}

// A Proc implements access to a procedure inside a DLL.
type Proc struct {
	Dll  *DLL
	Name string
	addr uintptr
}

// Addr returns the address of the procedure represented by p.
// The return value can be passed to Syscall to run the procedure.
func (p *Proc) Addr() uintptr {
	return p.addr
}

//go:uintptrescapes

// Call executes procedure p with arguments a. It will panic, if more than 15 arguments
// are supplied.
//
// The returned error is always non-nil, constructed from the result of GetLastError.
// Callers must inspect the primary return value to decide whether an error occurred
// (according to the semantics of the specific function being called) before consulting
// the error. The error will be guaranteed to contain syscall.Errno.
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
	switch len(a) {
	case 0:
		return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
	case 1:
		return Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
	case 2:
		return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
	case 3:
		return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
	case 4:
		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
	case 5:
		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
	case 6:
		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
	case 7:
		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
	case 8:
		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
	case 9:
		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
	case 10:
		return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0)
	case 11:
		return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0)
	case 12:
		return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])
	case 13:
		return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0)
	case 14:
		return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0)
	case 15:
		return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
	case 16:
		return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], 0, 0)
	case 17:
		return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], 0)
	case 18:
		return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], a[17])
	default:
		panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
	}
}

// A LazyDLL implements access to a single DLL.
// It will delay the load of the DLL until the first
// call to its Handle method or to one of its
// LazyProc's Addr method.
//
// LazyDLL is subject to the same DLL preloading attacks as documented
// on LoadDLL.
//
// Use LazyDLL in golang.org/x/sys/windows for a secure way to
// load system DLLs.
type LazyDLL struct {
	mu   sync.Mutex
	dll  *DLL // non nil once DLL is loaded
	Name string
}

// Load loads DLL file d.Name into memory. It returns an error if fails.
// Load will not try to load DLL, if it is already loaded into memory.
func (d *LazyDLL) Load() error {
	// Non-racy version of:
	// if d.dll == nil {
	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil {
		d.mu.Lock()
		defer d.mu.Unlock()
		if d.dll == nil {
			dll, e := LoadDLL(d.Name)
			if e != nil {
				return e
			}
			// Non-racy version of:
			// d.dll = dll
			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
		}
	}
	return nil
}

// mustLoad is like Load but panics if search fails.
func (d *LazyDLL) mustLoad() {
	e := d.Load()
	if e != nil {
		panic(e)
	}
}

// Handle returns d's module handle.
func (d *LazyDLL) Handle() uintptr {
	d.mustLoad()
	return uintptr(d.dll.Handle)
}

// NewProc returns a LazyProc for accessing the named procedure in the DLL d.
func (d *LazyDLL) NewProc(name string) *LazyProc {
	return &LazyProc{l: d, Name: name}
}

// NewLazyDLL creates new LazyDLL associated with DLL file.
func NewLazyDLL(name string) *LazyDLL {
	return &LazyDLL{Name: name}
}

// A LazyProc implements access to a procedure inside a LazyDLL.
// It delays the lookup until the Addr method is called.
type LazyProc struct {
	mu   sync.Mutex
	Name string
	l    *LazyDLL
	proc *Proc
}

// Find searches DLL for procedure named p.Name. It returns
// an error if search fails. Find will not search procedure,
// if it is already found and loaded into memory.
func (p *LazyProc) Find() error {
	// Non-racy version of:
	// if p.proc == nil {
	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
		p.mu.Lock()
		defer p.mu.Unlock()
		if p.proc == nil {
			e := p.l.Load()
			if e != nil {
				return e
			}
			proc, e := p.l.dll.FindProc(p.Name)
			if e != nil {
				return e
			}
			// Non-racy version of:
			// p.proc = proc
			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
		}
	}
	return nil
}

// mustFind is like Find but panics if search fails.
func (p *LazyProc) mustFind() {
	e := p.Find()
	if e != nil {
		panic(e)
	}
}

// Addr returns the address of the procedure represented by p.
// The return value can be passed to Syscall to run the procedure.
func (p *LazyProc) Addr() uintptr {
	p.mustFind()
	return p.proc.Addr()
}

//go:uintptrescapes

// Call executes procedure p with arguments a. It will panic, if more than 15 arguments
// are supplied.
//
// The returned error is always non-nil, constructed from the result of GetLastError.
// Callers must inspect the primary return value to decide whether an error occurred
// (according to the semantics of the specific function being called) before consulting
// the error. The error will be guaranteed to contain syscall.Errno.
func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
	p.mustFind()
	return p.proc.Call(a...)
}
<commit_msg>syscall: update doc comment to match behavior for Proc.Call<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package syscall

import (
	"internal/syscall/windows/sysdll"
	"sync"
	"sync/atomic"
	"unsafe"
)

// DLLError describes reasons for DLL load failures.
type DLLError struct {
	Err     error
	ObjName string
	Msg     string
}

func (e *DLLError) Error() string { return e.Msg }

// Implemented in ../runtime/syscall_windows.go.
func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno)
func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno)
func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno)
func loadlibrary(filename *uint16) (handle uintptr, err Errno)
func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)

// A DLL implements access to a single DLL.
type DLL struct {
	Name   string
	Handle Handle
}

// LoadDLL loads the named DLL file into memory.
//
// If name is not an absolute path and is not a known system DLL used by
// Go, Windows will search for the named DLL in many locations, causing
// potential DLL preloading attacks.
//
// Use LazyDLL in golang.org/x/sys/windows for a secure way to
// load system DLLs.
func LoadDLL(name string) (*DLL, error) {
	namep, err := UTF16PtrFromString(name)
	if err != nil {
		return nil, err
	}
	var h uintptr
	var e Errno
	if sysdll.IsSystemDLL[name] {
		h, e = loadsystemlibrary(namep)
	} else {
		h, e = loadlibrary(namep)
	}
	if e != 0 {
		return nil, &DLLError{
			Err:     e,
			ObjName: name,
			Msg:     "Failed to load " + name + ": " + e.Error(),
		}
	}
	d := &DLL{
		Name:   name,
		Handle: Handle(h),
	}
	return d, nil
}

// MustLoadDLL is like LoadDLL but panics if load operation fails.
func MustLoadDLL(name string) *DLL {
	d, e := LoadDLL(name)
	if e != nil {
		panic(e)
	}
	return d
}

// FindProc searches DLL d for procedure named name and returns *Proc
// if found. It returns an error if search fails.
func (d *DLL) FindProc(name string) (proc *Proc, err error) {
	namep, err := BytePtrFromString(name)
	if err != nil {
		return nil, err
	}
	a, e := getprocaddress(uintptr(d.Handle), namep)
	if e != 0 {
		return nil, &DLLError{
			Err:     e,
			ObjName: name,
			Msg:     "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
		}
	}
	p := &Proc{
		Dll:  d,
		Name: name,
		addr: a,
	}
	return p, nil
}

// MustFindProc is like FindProc but panics if search fails.
func (d *DLL) MustFindProc(name string) *Proc {
	p, e := d.FindProc(name)
	if e != nil {
		panic(e)
	}
	return p
}

// Release unloads DLL d from memory.
func (d *DLL) Release() (err error) {
	return FreeLibrary(d.Handle)
}

// A Proc implements access to a procedure inside a DLL.
type Proc struct {
	Dll  *DLL
	Name string
	addr uintptr
}

// Addr returns the address of the procedure represented by p.
// The return value can be passed to Syscall to run the procedure.
func (p *Proc) Addr() uintptr {
	return p.addr
}

//go:uintptrescapes

// Call executes procedure p with arguments a. It will panic if more than 18 arguments
// are supplied.
//
// The returned error is always non-nil, constructed from the result of GetLastError.
// Callers must inspect the primary return value to decide whether an error occurred
// (according to the semantics of the specific function being called) before consulting
// the error. The error will be guaranteed to contain syscall.Errno.
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
	switch len(a) {
	case 0:
		return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
	case 1:
		return Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
	case 2:
		return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
	case 3:
		return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
	case 4:
		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
	case 5:
		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
	case 6:
		return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
	case 7:
		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
	case 8:
		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
	case 9:
		return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
	case 10:
		return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0)
	case 11:
		return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0)
	case 12:
		return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])
	case 13:
		return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0)
	case 14:
		return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0)
	case 15:
		return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
	case 16:
		return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], 0, 0)
	case 17:
		return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], 0)
	case 18:
		return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], a[17])
	default:
		panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
	}
}

// A LazyDLL implements access to a single DLL.
// It will delay the load of the DLL until the first
// call to its Handle method or to one of its
// LazyProc's Addr method.
//
// LazyDLL is subject to the same DLL preloading attacks as documented
// on LoadDLL.
//
// Use LazyDLL in golang.org/x/sys/windows for a secure way to
// load system DLLs.
type LazyDLL struct {
	mu   sync.Mutex
	dll  *DLL // non nil once DLL is loaded
	Name string
}

// Load loads DLL file d.Name into memory. It returns an error if fails.
// Load will not try to load DLL, if it is already loaded into memory.
func (d *LazyDLL) Load() error {
	// Non-racy version of:
	// if d.dll == nil {
	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil {
		d.mu.Lock()
		defer d.mu.Unlock()
		if d.dll == nil {
			dll, e := LoadDLL(d.Name)
			if e != nil {
				return e
			}
			// Non-racy version of:
			// d.dll = dll
			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
		}
	}
	return nil
}

// mustLoad is like Load but panics if search fails.
func (d *LazyDLL) mustLoad() {
	e := d.Load()
	if e != nil {
		panic(e)
	}
}

// Handle returns d's module handle.
func (d *LazyDLL) Handle() uintptr {
	d.mustLoad()
	return uintptr(d.dll.Handle)
}

// NewProc returns a LazyProc for accessing the named procedure in the DLL d.
func (d *LazyDLL) NewProc(name string) *LazyProc {
	return &LazyProc{l: d, Name: name}
}

// NewLazyDLL creates new LazyDLL associated with DLL file.
func NewLazyDLL(name string) *LazyDLL {
	return &LazyDLL{Name: name}
}

// A LazyProc implements access to a procedure inside a LazyDLL.
// It delays the lookup until the Addr method is called.
type LazyProc struct {
	mu   sync.Mutex
	Name string
	l    *LazyDLL
	proc *Proc
}

// Find searches DLL for procedure named p.Name. It returns
// an error if search fails. Find will not search procedure,
// if it is already found and loaded into memory.
func (p *LazyProc) Find() error {
	// Non-racy version of:
	// if p.proc == nil {
	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
		p.mu.Lock()
		defer p.mu.Unlock()
		if p.proc == nil {
			e := p.l.Load()
			if e != nil {
				return e
			}
			proc, e := p.l.dll.FindProc(p.Name)
			if e != nil {
				return e
			}
			// Non-racy version of:
			// p.proc = proc
			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
		}
	}
	return nil
}

// mustFind is like Find but panics if search fails.
func (p *LazyProc) mustFind() {
	e := p.Find()
	if e != nil {
		panic(e)
	}
}

// Addr returns the address of the procedure represented by p.
// The return value can be passed to Syscall to run the procedure.
func (p *LazyProc) Addr() uintptr {
	p.mustFind()
	return p.proc.Addr()
}

//go:uintptrescapes

// Call executes procedure p with arguments a. It will panic, if more than 15 arguments
// are supplied.
//
// The returned error is always non-nil, constructed from the result of GetLastError.
// Callers must inspect the primary return value to decide whether an error occurred
// (according to the semantics of the specific function being called) before consulting
// the error. The error will be guaranteed to contain syscall.Errno.
func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
	p.mustFind()
	return p.proc.Call(a...)
}
<|endoftext|>


<commit_before>// The daemon is a set of go routines that run in endless loops
// and perform necessary back-end functions.  These are spawned
// one by one in main.go.
package main

import (
	"html/template"
	"io/ioutil"
	"log"
	"path/filepath"
	"time"
)

// Parse HTML templates on a set interval.
// NOTE: Errors in templates will result in a "maintenance" notice
// on the site and the error below in the logs.
func RefreshTemplates() {
	var tmplGlob = filepath.Join(Config.Server.Templates, "*.html")
	var err error

	for {
		Templates, err = template.ParseGlob(tmplGlob)
		if err != nil {
			log.Println("Error parsing HTML templates!!! Site is down.")
			SiteDown = true
		} else {
			SiteDown = false
		}

		// Wait and refresh templates again
		time.Sleep(time.Minute * time.Duration(Config.Daemon.TmplFreq))
	}

	return
}

// Clean up expired uploads on a set interval
// NOTE: The occasional error may show up in logs if this runs and sees a
// directory that doesn't have an info.json file in it yet.
func ScrubDownloads() {
	for {
		entries, err := ioutil.ReadDir(Config.Server.Assets)
		if err != nil {
			log.Println("ERROR: Scrub cannot read assets directory:", err.Error())
		} else {
			// Loop over each file
			for _, entry := range entries {
				// Skip NoPurgeCheck directories
				var skipDir bool
				for _, npcdir := range Config.Daemon.NoPurgeCheck {
					if entry.Name() == npcdir {
						skipDir = true
						break
					}
				}

				if skipDir {
					continue
				}

				if entry.IsDir() {
					// See if the info.json file exists
					var infofile = filepath.Join(Config.Server.Assets, entry.Name(), "info.json")

					// Read in the info.json file
					tracker, trackererr := LoadTrackerFromFile(infofile)
					if trackererr != nil || tracker.When == (time.Time{}) {
						log.Println("WARNING: Scrub error parsing info file")
						continue
					}

					// Check tracker.ExpirationDate against current time
					//    Delete download dir if expired
					if tracker.ExpirationDate.Before(time.Now().Local()) {
						var dirpath = filepath.Join(Config.Server.Assets, entry.Name())
						rderr := RemoveDir(dirpath)
						if rderr != nil {
							log.Println(tracker.Dnldcode, "Could not remove expired files:", rderr.Error())
							continue
						}

						log.Println(tracker.Dnldcode, "Files expired and purged")
					}
				}
			}
		}

		// Wait before doing it all over again
		time.Sleep(time.Minute * time.Duration(Config.Daemon.PurgeFreq))
	}
}
<commit_msg>Verbose logging on template errors<commit_after>// The daemon is a set of go routines that run in endless loops
// and perform necessary back-end functions.  These are spawned
// one by one in main.go.
package main

import (
	"html/template"
	"io/ioutil"
	"log"
	"path/filepath"
	"time"
)

// Parse HTML templates on a set interval.
// NOTE: Errors in templates will result in a "maintenance" notice
// on the site and the error below in the logs.
func RefreshTemplates() {
	var tmplGlob = filepath.Join(Config.Server.Templates, "*.html")
	var err error

	for {
		Templates, err = template.ParseGlob(tmplGlob)
		if err != nil {
			log.Println("Error parsing HTML templates!!! Site is down: " + err.Error())
			SiteDown = true
		} else {
			SiteDown = false
		}

		// Wait and refresh templates again
		time.Sleep(time.Minute * time.Duration(Config.Daemon.TmplFreq))
	}

	return
}

// Clean up expired uploads on a set interval
// NOTE: The occasional error may show up in logs if this runs and sees a
// directory that doesn't have an info.json file in it yet.
func ScrubDownloads() {
	for {
		entries, err := ioutil.ReadDir(Config.Server.Assets)
		if err != nil {
			log.Println("ERROR: Scrub cannot read assets directory:", err.Error())
		} else {
			// Loop over each file
			for _, entry := range entries {
				// Skip NoPurgeCheck directories
				var skipDir bool
				for _, npcdir := range Config.Daemon.NoPurgeCheck {
					if entry.Name() == npcdir {
						skipDir = true
						break
					}
				}

				if skipDir {
					continue
				}

				if entry.IsDir() {
					// See if the info.json file exists
					var infofile = filepath.Join(Config.Server.Assets, entry.Name(), "info.json")

					// Read in the info.json file
					tracker, trackererr := LoadTrackerFromFile(infofile)
					if trackererr != nil || tracker.When == (time.Time{}) {
						log.Println("WARNING: Scrub error parsing info file")
						continue
					}

					// Check tracker.ExpirationDate against current time
					//    Delete download dir if expired
					if tracker.ExpirationDate.Before(time.Now().Local()) {
						var dirpath = filepath.Join(Config.Server.Assets, entry.Name())
						rderr := RemoveDir(dirpath)
						if rderr != nil {
							log.Println(tracker.Dnldcode, "Could not remove expired files:", rderr.Error())
							continue
						}

						log.Println(tracker.Dnldcode, "Files expired and purged")
					}
				}
			}
		}

		// Wait before doing it all over again
		time.Sleep(time.Minute * time.Duration(Config.Daemon.PurgeFreq))
	}
}
<|endoftext|>


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

// Package logging implements log library for other applications. It provides
// functions Debug, Info, Warning, Error, Critical, and formatting version
// Logf.
//
// Example:
//
//	logger := logging.SimpleLogger("main")
//	logger.SetLevel(logging.WARNING)
//	logger.Error("test for error")
//	logger.Warning("test for warning", "second parameter")
//	logger.Debug("test for debug")
//
package logging

import (
	"github.com/ccding/go-config-reader/config"
	"io"
	"os"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

// Pre-defined formats
const (
	DefaultFileName   = "logging.log"                   // default logging filename
	DefaultConfigFile = "logging.conf"                  // default logging configuration file
	DefaultTimeFormat = "2006-01-02 15:04:05.999999999" // defaulttime format
	bufSize           = 1000                            // buffer size for writer
	queueSize         = 10000                           // chan queue size in async logging
	reqSize           = 10000                           // chan queue size in async logging
)

// Logger is the logging struct.
type Logger struct {

	// Be careful of the alignment issue of the variable seqid because it
	// uses the sync/atomic.AddUint64() operation. If the alignment is
	// wrong, it will cause a panic. To solve the alignment issue in an
	// easy way, we put seqid to the beginning of the structure.
	// seqid is only visiable internally.
	seqid uint64 // last used sequence number in record

	// These variables can be configured by users.
	name         string    // logger name
	level        Level     // record level higher than this will be printed
	recordFormat string    // format of the record
	recordArgs   []string  // arguments to be used in the recordFormat
	out          io.Writer // writer
	sync         bool      // use sync or async way to record logs
	timeFormat   string    // format for time

	// These variables are visible to users.
	startTime time.Time // start time of the logger

	// Internally used variables, which don't have get and set functions.
	wlock   sync.Mutex   // writer lock
	queue   chan string  // queue used in async logging
	request chan request // queue used in non-runtime logging
	flush   chan bool    // flush signal for the watcher to write
	quit    chan bool    // quit signal for the watcher to quit
	fd      *os.File     // file handler, used to close the file on destroy
	runtime bool         // with runtime operation or not
}

// SimpleLogger creates a new logger with simple configuration.
func SimpleLogger(name string) (*Logger, error) {
	return createLogger(name, WARNING, BasicFormat, DefaultTimeFormat, os.Stdout, false)
}

// BasicLogger creates a new logger with basic configuration.
func BasicLogger(name string) (*Logger, error) {
	return FileLogger(name, WARNING, BasicFormat, DefaultTimeFormat, DefaultFileName, false)
}

// RichLogger creates a new logger with simple configuration.
func RichLogger(name string) (*Logger, error) {
	return FileLogger(name, NOTSET, RichFormat, DefaultTimeFormat, DefaultFileName, false)
}

// FileLogger creates a new logger with file output.
func FileLogger(name string, level Level, format string, timeFormat string, file string, sync bool) (*Logger, error) {
	out, err := os.Create(file)
	if err != nil {
		return nil, err
	}
	logger, err := createLogger(name, level, format, timeFormat, out, sync)
	if err == nil {
		logger.fd = out
		return logger, nil
	} else {
		return nil, err
	}
}

// WriterLogger creates a new logger with a writer
func WriterLogger(name string, level Level, format string, timeFormat string, out io.Writer, sync bool) (*Logger, error) {
	return createLogger(name, level, format, timeFormat, out, sync)
}

// WriterLogger creates a new logger from a configuration file
func ConfigLogger(filename string) (*Logger, error) {
	conf := config.NewConfig(filename)
	err := conf.Read()
	if err != nil {
		return nil, err
	}
	name := conf.Get("", "name")
	slevel := conf.Get("", "level")
	if slevel == "" {
		slevel = "0"
	}
	l, err := strconv.Atoi(slevel)
	if err != nil {
		return nil, err
	}
	level := Level(l)
	format := conf.Get("", "format")
	if format == "" {
		format = BasicFormat
	}
	timeFormat := conf.Get("", "timeFormat")
	if timeFormat == "" {
		timeFormat = DefaultTimeFormat
	}
	ssync := conf.Get("", "sync")
	if ssync == "" {
		ssync = "0"
	}
	file := conf.Get("", "file")
	if file == "" {
		file = DefaultFileName
	}
	sync := true
	if ssync == "0" {
		sync = false
	} else if ssync == "1" {
		sync = true
	} else {
		return nil, err
	}
	return FileLogger(name, level, format, timeFormat, file, sync)
}

// createLogger create a new logger
func createLogger(name string, level Level, format string, timeFormat string, out io.Writer, sync bool) (*Logger, error) {
	logger := new(Logger)

	err := logger.parseFormat(format)
	if err != nil {
		return nil, err
	}

	// asign values to logger
	logger.name = name
	logger.level = level
	logger.out = out
	logger.seqid = 0
	logger.sync = sync
	logger.queue = make(chan string, queueSize)
	logger.request = make(chan request, reqSize)
	logger.flush = make(chan bool)
	logger.quit = make(chan bool)
	logger.startTime = time.Now()
	logger.fd = nil
	logger.timeFormat = timeFormat

	// start watcher to write logs if it is async or no runtime field
	if !logger.sync {
		go logger.watcher()
	}

	return logger, nil
}

// Destroy sends quit signal to watcher and releases all the resources.
func (logger *Logger) Destroy() {
	if !logger.sync {
		// quit watcher
		logger.quit <- true
		// wait for watcher quit
		<-logger.quit
	}
	// clean up
	if logger.fd != nil {
		logger.fd.Close()
	}
}

// Flush the writer
func (logger *Logger) Flush() {
	if !logger.sync {
		// send flush signal
		logger.flush <- true
		// wait for flush finish
		<-logger.flush
	}
}

// Getter functions

func (logger *Logger) Name() string {
	return logger.name
}

func (logger *Logger) StartTime() int64 {
	return logger.startTime.UnixNano()
}

func (logger *Logger) TimeFormat() string {
	return logger.timeFormat
}

func (logger *Logger) Level() Level {
	return Level(atomic.LoadInt32((*int32)(&logger.level)))
}

func (logger *Logger) RecordFormat() string {
	return logger.recordFormat
}

func (logger *Logger) RecordArgs() []string {
	return logger.recordArgs
}

func (logger *Logger) Writer() io.Writer {
	return logger.out
}

func (logger *Logger) Sync() bool {
	return logger.sync
}

// Setter functions

func (logger *Logger) SetLevel(level Level) {
	atomic.StoreInt32((*int32)(&logger.level), int32(level))
}

func (logger *Logger) SetWriter(out ...io.Writer) {
	logger.out = io.MultiWriter(out...)
}
<commit_msg>Change default behavior of FileLogger to appending file<commit_after>// Copyright 2013, Cong Ding. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// author: Cong Ding <dinggnu@gmail.com>

// Package logging implements log library for other applications. It provides
// functions Debug, Info, Warning, Error, Critical, and formatting version
// Logf.
//
// Example:
//
//	logger := logging.SimpleLogger("main")
//	logger.SetLevel(logging.WARNING)
//	logger.Error("test for error")
//	logger.Warning("test for warning", "second parameter")
//	logger.Debug("test for debug")
//
package logging

import (
	"github.com/ccding/go-config-reader/config"
	"io"
	"os"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

// Pre-defined formats
const (
	DefaultFileName   = "logging.log"                   // default logging filename
	DefaultConfigFile = "logging.conf"                  // default logging configuration file
	DefaultTimeFormat = "2006-01-02 15:04:05.999999999" // defaulttime format
	bufSize           = 1000                            // buffer size for writer
	queueSize         = 10000                           // chan queue size in async logging
	reqSize           = 10000                           // chan queue size in async logging
)

// Logger is the logging struct.
type Logger struct {

	// Be careful of the alignment issue of the variable seqid because it
	// uses the sync/atomic.AddUint64() operation. If the alignment is
	// wrong, it will cause a panic. To solve the alignment issue in an
	// easy way, we put seqid to the beginning of the structure.
	// seqid is only visiable internally.
	seqid uint64 // last used sequence number in record

	// These variables can be configured by users.
	name         string    // logger name
	level        Level     // record level higher than this will be printed
	recordFormat string    // format of the record
	recordArgs   []string  // arguments to be used in the recordFormat
	out          io.Writer // writer
	sync         bool      // use sync or async way to record logs
	timeFormat   string    // format for time

	// These variables are visible to users.
	startTime time.Time // start time of the logger

	// Internally used variables, which don't have get and set functions.
	wlock   sync.Mutex   // writer lock
	queue   chan string  // queue used in async logging
	request chan request // queue used in non-runtime logging
	flush   chan bool    // flush signal for the watcher to write
	quit    chan bool    // quit signal for the watcher to quit
	fd      *os.File     // file handler, used to close the file on destroy
	runtime bool         // with runtime operation or not
}

// SimpleLogger creates a new logger with simple configuration.
func SimpleLogger(name string) (*Logger, error) {
	return createLogger(name, WARNING, BasicFormat, DefaultTimeFormat, os.Stdout, false)
}

// BasicLogger creates a new logger with basic configuration.
func BasicLogger(name string) (*Logger, error) {
	return FileLogger(name, WARNING, BasicFormat, DefaultTimeFormat, DefaultFileName, false)
}

// RichLogger creates a new logger with simple configuration.
func RichLogger(name string) (*Logger, error) {
	return FileLogger(name, NOTSET, RichFormat, DefaultTimeFormat, DefaultFileName, false)
}

// FileLogger creates a new logger with file output.
func FileLogger(name string, level Level, format string, timeFormat string, file string, sync bool) (*Logger, error) {
	out, err := os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModeAppend|0666)
	if err != nil {
		return nil, err
	}
	logger, err := createLogger(name, level, format, timeFormat, out, sync)
	if err == nil {
		logger.fd = out
		return logger, nil
	} else {
		return nil, err
	}
}

// WriterLogger creates a new logger with a writer
func WriterLogger(name string, level Level, format string, timeFormat string, out io.Writer, sync bool) (*Logger, error) {
	return createLogger(name, level, format, timeFormat, out, sync)
}

// WriterLogger creates a new logger from a configuration file
func ConfigLogger(filename string) (*Logger, error) {
	conf := config.NewConfig(filename)
	err := conf.Read()
	if err != nil {
		return nil, err
	}
	name := conf.Get("", "name")
	slevel := conf.Get("", "level")
	if slevel == "" {
		slevel = "0"
	}
	l, err := strconv.Atoi(slevel)
	if err != nil {
		return nil, err
	}
	level := Level(l)
	format := conf.Get("", "format")
	if format == "" {
		format = BasicFormat
	}
	timeFormat := conf.Get("", "timeFormat")
	if timeFormat == "" {
		timeFormat = DefaultTimeFormat
	}
	ssync := conf.Get("", "sync")
	if ssync == "" {
		ssync = "0"
	}
	file := conf.Get("", "file")
	if file == "" {
		file = DefaultFileName
	}
	sync := true
	if ssync == "0" {
		sync = false
	} else if ssync == "1" {
		sync = true
	} else {
		return nil, err
	}
	return FileLogger(name, level, format, timeFormat, file, sync)
}

// createLogger create a new logger
func createLogger(name string, level Level, format string, timeFormat string, out io.Writer, sync bool) (*Logger, error) {
	logger := new(Logger)

	err := logger.parseFormat(format)
	if err != nil {
		return nil, err
	}

	// asign values to logger
	logger.name = name
	logger.level = level
	logger.out = out
	logger.seqid = 0
	logger.sync = sync
	logger.queue = make(chan string, queueSize)
	logger.request = make(chan request, reqSize)
	logger.flush = make(chan bool)
	logger.quit = make(chan bool)
	logger.startTime = time.Now()
	logger.fd = nil
	logger.timeFormat = timeFormat

	// start watcher to write logs if it is async or no runtime field
	if !logger.sync {
		go logger.watcher()
	}

	return logger, nil
}

// Destroy sends quit signal to watcher and releases all the resources.
func (logger *Logger) Destroy() {
	if !logger.sync {
		// quit watcher
		logger.quit <- true
		// wait for watcher quit
		<-logger.quit
	}
	// clean up
	if logger.fd != nil {
		logger.fd.Close()
	}
}

// Flush the writer
func (logger *Logger) Flush() {
	if !logger.sync {
		// send flush signal
		logger.flush <- true
		// wait for flush finish
		<-logger.flush
	}
}

// Getter functions

func (logger *Logger) Name() string {
	return logger.name
}

func (logger *Logger) StartTime() int64 {
	return logger.startTime.UnixNano()
}

func (logger *Logger) TimeFormat() string {
	return logger.timeFormat
}

func (logger *Logger) Level() Level {
	return Level(atomic.LoadInt32((*int32)(&logger.level)))
}

func (logger *Logger) RecordFormat() string {
	return logger.recordFormat
}

func (logger *Logger) RecordArgs() []string {
	return logger.recordArgs
}

func (logger *Logger) Writer() io.Writer {
	return logger.out
}

func (logger *Logger) Sync() bool {
	return logger.sync
}

// Setter functions

func (logger *Logger) SetLevel(level Level) {
	atomic.StoreInt32((*int32)(&logger.level), int32(level))
}

func (logger *Logger) SetWriter(out ...io.Writer) {
	logger.out = io.MultiWriter(out...)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strings"
	"time"

	"github.com/rakoo/go-ini"

	"code.google.com/p/go-imap/go1/imap"
)

var (
	mailbox = flag.String("mailbox", "[Gmail]/All Mail", "The mailbox to watch")
)

func main() {
	flag.Parse()
	email, password := getCredentials()

	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt, os.Kill)
	go func() {
		<-quit
		os.Exit(0)
	}()

	connect := func(server string) (c *imap.Client, err error) {
		c, err = imap.DialTLS(server, &tls.Config{})
		if err != nil {
			return
		}

		_, err = c.Auth(imap.PlainAuth(email, password, ""))
		if err != nil {
			return
		}

		log.Println("Successfully authed")

		cmd, err := c.Select(*mailbox, true)
		if err != nil {
			log.Println("Error selecting mailbox: ", err)
			return
		}
		_, err = cmd.Result(imap.OK)
		if err != nil {
			return
		}

		log.Println("Successfully selected ", *mailbox)

		_, err = c.Idle()
		if err != nil {
			return
		}

		log.Println("Starting idle...")
		c.Data = nil

		return
	}

loop:
	for {
		c, err := connect("imap.gmail.com")
		if err != nil {
			continue
		}

		wait := true
		for wait {
			err = c.Recv(15 * time.Minute)
			switch err {
			case nil:
				fallthrough
			case io.EOF:
				// We received content from server -- sync mails
				wait = false
			case imap.ErrTimeout:
				// after the timeout, wakeup the connection
				if _, err := c.IdleTerm(); err != nil {
					log.Println("Error finishing idle:: ", err)
					continue loop
				}
				if _, err := imap.Wait(c.Noop()); err != nil {
					log.Println("Error nooping: ", err)
					continue loop
				}
				log.Println("Keeping it alive !")
				if _, err := c.Idle(); err != nil {
					log.Println("Error re-idling: ", err)
					continue loop
				}
			default:
				log.Println("Error while receiving content: ", err)
				continue loop
			}
		}

		for _, rsp := range c.Data {
			if rsp.Label == "EXISTS" {
				log.Println("New message, running sync...")
				cmd := exec.Command("offlineimap", "-u", "Quiet")
				cmd.Stdout = os.Stderr
				cmd.Stderr = os.Stderr
				err := cmd.Run()
				if err != nil {
					log.Printf("Error running sync: %s\n", err)
				}
				log.Println("Ran sync")
			}
		}

		c.Data = nil
	}
}

func getCredentials() (email, password string) {
	cfg, err := ini.LoadFile(filepath.Join(os.Getenv("HOME"),
		".offlineimaprc"))
	if err != nil {
		log.Fatal(err)
	}

	accounts, ok := cfg.Get("general", "accounts")
	if !ok {
		log.Fatal("No general/accounts string")
	}

	// Only take first one
	account := strings.Split(accounts, ",")[0]
	remoterepo, ok := cfg.Get("Account "+account, "remoterepository")
	if !ok {
		log.Fatal(fmt.Sprintf("No section 'Account %s'/remoterepository",
			account))
	}

	typ, ok := cfg.Get("Repository "+remoterepo, "type")
	if !ok {
		log.Fatal("No type in repo")
	}
	if typ != "Gmail" {
		log.Fatal("I can only manage Gmail repos; curren type is ", typ)
	}
	user, okuser := cfg.Get("Repository "+remoterepo, "remoteuser")
	password, okpass := cfg.Get("Repository "+remoterepo, "remotepass")
	if !okuser || !okpass {
		log.Fatal("Invalid config")
	}

	return user + "@gmail.com", password
}
<commit_msg>Run command on each new loop<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strings"
	"time"

	"github.com/rakoo/go-ini"

	"code.google.com/p/go-imap/go1/imap"
)

var (
	mailbox = flag.String("mailbox", "[Gmail]/All Mail", "The mailbox to watch")
)

func main() {
	flag.Parse()
	email, password := getCredentials()

	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt, os.Kill)
	go func() {
		<-quit
		os.Exit(0)
	}()

	connect := func(server string) (c *imap.Client, err error) {
		c, err = imap.DialTLS(server, &tls.Config{})
		if err != nil {
			return
		}

		_, err = c.Auth(imap.PlainAuth(email, password, ""))
		if err != nil {
			return
		}

		log.Println("Successfully authed")

		cmd, err := c.Select(*mailbox, true)
		if err != nil {
			log.Println("Error selecting mailbox: ", err)
			return
		}
		_, err = cmd.Result(imap.OK)
		if err != nil {
			return
		}

		log.Println("Successfully selected ", *mailbox)

		_, err = c.Idle()
		if err != nil {
			return
		}

		log.Println("Starting idle...")
		c.Data = nil

		return
	}

loop:
	for {
		if err := cmd(); err != nil {
			log.Printf("Error running sync on new loop: %s\n", err)
			continue
		}

		c, err := connect("imap.gmail.com")
		if err != nil {
			continue
		}

		wait := true
		for wait {
			err = c.Recv(15 * time.Minute)
			switch err {
			case nil:
				fallthrough
			case io.EOF:
				// We received content from server -- sync mails
				wait = false
			case imap.ErrTimeout:
				// after the timeout, wakeup the connection
				if _, err := c.IdleTerm(); err != nil {
					log.Println("Error finishing idle:: ", err)
					continue loop
				}
				if _, err := imap.Wait(c.Noop()); err != nil {
					log.Println("Error nooping: ", err)
					continue loop
				}
				log.Println("Keeping it alive !")
				if _, err := c.Idle(); err != nil {
					log.Println("Error re-idling: ", err)
					continue loop
				}
			default:
				log.Println("Error while receiving content: ", err)
				continue loop
			}
		}

		for _, rsp := range c.Data {
			if rsp.Label == "EXISTS" {
				log.Println("New message, running sync...")
				if err := cmd(); err != nil {
					log.Printf("Error running sync: %s\n", err)
				}
				log.Println("Ran sync")
			}
		}

		c.Data = nil
	}
}

func getCredentials() (email, password string) {
	cfg, err := ini.LoadFile(filepath.Join(os.Getenv("HOME"),
		".offlineimaprc"))
	if err != nil {
		log.Fatal(err)
	}

	accounts, ok := cfg.Get("general", "accounts")
	if !ok {
		log.Fatal("No general/accounts string")
	}

	// Only take first one
	account := strings.Split(accounts, ",")[0]
	remoterepo, ok := cfg.Get("Account "+account, "remoterepository")
	if !ok {
		log.Fatal(fmt.Sprintf("No section 'Account %s'/remoterepository",
			account))
	}

	typ, ok := cfg.Get("Repository "+remoterepo, "type")
	if !ok {
		log.Fatal("No type in repo")
	}
	if typ != "Gmail" {
		log.Fatal("I can only manage Gmail repos; curren type is ", typ)
	}
	user, okuser := cfg.Get("Repository "+remoterepo, "remoteuser")
	password, okpass := cfg.Get("Repository "+remoterepo, "remotepass")
	if !okuser || !okpass {
		log.Fatal("Invalid config")
	}

	return user + "@gmail.com", password
}

func cmd() error {
	cmd := exec.Command("offlineimap", "-u", "Quiet")
	cmd.Stdout = os.Stderr
	cmd.Stderr = os.Stderr
	return cmd.Run()
}
<|endoftext|>


<commit_before>package goutils

import (
	"testing"
)

type  mt struct{}

func TestDirDirWalk(t *testing.T) {
	tests := []struct{
		path string
		expected map[string]mt
		expectedErr string
	}{
		{
			path:  "invalid",
			expected: nil,
			expectedErr: "invalid does not exist",
		},
		{
                        path:  "test/pixies",
                        expected: map[string]mt{
                                "I-cant-forget.txt": mt{},
				"Ive-been-waiting-for-you.txt": mt{},
				"born-in-chicago.txt": mt{},
                                "debaser-I.txt": mt{},
                                "debaser-II.txt": mt{},
                                "gigantic.txt": mt{},
				"something-against-you.txt": mt{},
				"where-is-my-mind.txt": mt{},
				"surfer-rosa": mt{},
                                "doolittle": mt{},
                        },
                        expectedErr: "",
                },
              	{
                        path:  "test/pink-floyd",
                        expected: map[string]mt{
                                "echos.txt": mt{},
				"one-of-these-days.txt": mt{},
                                "san-tropez.txt": mt{},
                                "seamus.txt": mt{},
                                "I-V.txt": mt{},
                                "VI-IX.txt": mt{},
                                "have-a-cigar.txt": mt{},
                                "wish-you-were-here-part1.txt": mt{},
                                "wish-you-were-here-part2.txt": mt{},
                                "wish-you-were-here-part3.txt": mt{},
                                "wish-you-were-here": mt{},
                                "meddle": mt{},
                                "shine-on-you-crazy-diamond": mt{},
                        },
                        expectedErr: "",
                },
              	{
                        path:  "test",
                       	expected: map[string]mt{
                                "I-cant-forget.txt": mt{},
                                "Ive-been-waiting-for-you.txt": mt{},
                                "born-in-chicago.txt": mt{},
                                "debaser-I.txt": mt{},
                                "debaser-II.txt": mt{},
                                "gigantic.txt": mt{},
                                "something-against-you.txt": mt{},
                                "where-is-my-mind.txt": mt{},
                                "echos.txt": mt{},
                                "one-of-these-days.txt": mt{},
                                "san-tropez.txt": mt{},
                                "seamus.txt": mt{},
                                "I-V.txt": mt{},
                                "VI-IX.txt": mt{},
                                "have-a-cigar.txt": mt{},
                                "wish-you-were-here-part1.txt": mt{},
                                "wish-you-were-here-part2.txt": mt{},
                                "wish-you-were-here-part3.txt": mt{},
				"tmbg-ana-ng.txt": mt{},
                                "tmbg-particle-man.txt": mt{},
                                "tmbg-sapphire-bullets-of-pure-love.txt": mt{},
				"pixies": mt{},
				"pink-floyd": mt{},
                                "surfer-rosa": mt{},
                                "doolittle": mt{},
                                "wish-you-were-here": mt{},
                                "meddle": mt{},
                                "shine-on-you-crazy-diamond": mt{},
                        },
                        expectedErr: "",
                },

	}

	for _, test := range tests {
		d := &Dir{Files: []file{}}
		err := d.Walk(test.path)
		if err != nil {
			if err.Error() != test.expectedErr {
				t.Errorf("Expected %s got %s", test.expectedErr, err)
			}
		} else {
			if test.expectedErr != "" {
				t.Errorf("Expected error %s", err)
			} else {
				for _, f := range d.Files {
					if _, ok := test.expected[f.Info.Name()]; !ok {
						t.Errorf("%s was indexed but not found in the expected filename list", f.Info.Name())
					}
				}
			}
		}
	}
}

func TestPathExists(t *testing.T) {
	tests := []struct{
		path	 string
		expected bool
		errS	 string
	}{
		{"", false, ""},
		{"test", true, ""},
		{"test/pixies/born-in-chicago.txt", true, ""},
		{"tst", false, ""},
		{"test/pink-floyd/animals", false, ""},
	}

	for  _, test := range tests{
		exists, err := PathExists(test.path)
		if err != nil {
			if test.errS == "" {
				t.Errorf("An unexpected error was encountered while checking the existence of %s: %s", test.path, err.Error())
			} else {
				if err.Error() != test.errS {
					t.Errorf("Expected %s, got %s", test.errS, err.Error())
				}
			}
		} else {
			if test.errS != "" {
				t.Errorf("%s was expected, but no error was encountered.", test.errS)
			} else {
				if exists != test.expected {
					t.Errorf("Expected %v got %v for %s", test.expected, exists, test.path)
				}
			}
		}
	}
}
<commit_msg>fix path tests<commit_after>package goutils

import (
	"testing"
)

type mt struct{}

func TestDirDirWalk(t *testing.T) {
	tests := []struct {
		path        string
		expected    map[string]mt
		expectedErr string
	}{
		{
			path:        "invalid",
			expected:    nil,
			expectedErr: "invalid does not exist",
		},
		{
			path: "../test/pixies",
			expected: map[string]mt{
				"I-cant-forget.txt":            mt{},
				"Ive-been-waiting-for-you.txt": mt{},
				"born-in-chicago.txt":          mt{},
				"debaser-I.txt":                mt{},
				"debaser-II.txt":               mt{},
				"gigantic.txt":                 mt{},
				"something-against-you.txt":    mt{},
				"where-is-my-mind.txt":         mt{},
				"surfer-rosa":                  mt{},
				"doolittle":                    mt{},
			},
			expectedErr: "",
		},
		{
			path: "../test/pink-floyd",
			expected: map[string]mt{
				"echos.txt":                    mt{},
				"one-of-these-days.txt":        mt{},
				"san-tropez.txt":               mt{},
				"seamus.txt":                   mt{},
				"I-V.txt":                      mt{},
				"VI-IX.txt":                    mt{},
				"have-a-cigar.txt":             mt{},
				"wish-you-were-here-part1.txt": mt{},
				"wish-you-were-here-part2.txt": mt{},
				"wish-you-were-here-part3.txt": mt{},
				"wish-you-were-here":           mt{},
				"meddle":                       mt{},
				"shine-on-you-crazy-diamond":   mt{},
			},
			expectedErr: "",
		},
		{
			path: "../test",
			expected: map[string]mt{
				"I-cant-forget.txt":                      mt{},
				"Ive-been-waiting-for-you.txt":           mt{},
				"born-in-chicago.txt":                    mt{},
				"debaser-I.txt":                          mt{},
				"debaser-II.txt":                         mt{},
				"gigantic.txt":                           mt{},
				"something-against-you.txt":              mt{},
				"where-is-my-mind.txt":                   mt{},
				"echos.txt":                              mt{},
				"one-of-these-days.txt":                  mt{},
				"san-tropez.txt":                         mt{},
				"seamus.txt":                             mt{},
				"I-V.txt":                                mt{},
				"VI-IX.txt":                              mt{},
				"have-a-cigar.txt":                       mt{},
				"wish-you-were-here-part1.txt":           mt{},
				"wish-you-were-here-part2.txt":           mt{},
				"wish-you-were-here-part3.txt":           mt{},
				"tmbg-ana-ng.txt":                        mt{},
				"tmbg-particle-man.txt":                  mt{},
				"tmbg-sapphire-bullets-of-pure-love.txt": mt{},
				"pixies":                     mt{},
				"pink-floyd":                 mt{},
				"surfer-rosa":                mt{},
				"doolittle":                  mt{},
				"wish-you-were-here":         mt{},
				"meddle":                     mt{},
				"shine-on-you-crazy-diamond": mt{},
			},
			expectedErr: "",
		},
	}

	for _, test := range tests {
		d := &Dir{Files: []file{}}
		err := d.Walk(test.path)
		if err != nil {
			if err.Error() != test.expectedErr {
				t.Errorf("Expected %q got %q", test.expectedErr, err)
			}
		} else {
			if test.expectedErr != "" {
				t.Errorf("Expected error %s", err)
			} else {
				for _, f := range d.Files {
					if _, ok := test.expected[f.Info.Name()]; !ok {
						t.Errorf("%s was indexed but not found in the expected filename list", f.Info.Name())
					}
				}
			}
		}
	}
}

func TestPathExists(t *testing.T) {
	tests := []struct {
		path     string
		expected bool
		errS     string
	}{
		{"", false, ""},
		{"../test", true, ""},
		{"../test/pixies/born-in-chicago.txt", true, ""},
		{"../tst", false, ""},
		{"../test/pink-floyd/animals", false, ""},
	}

	for _, test := range tests {
		exists, err := PathExists(test.path)
		if err != nil {
			if test.errS == "" {
				t.Errorf("An unexpected error was encountered while checking the existence of %s: %s", test.path, err.Error())
			} else {
				if err.Error() != test.errS {
					t.Errorf("Expected %q, got %q", test.errS, err.Error())
				}
			}
		} else {
			if test.errS != "" {
				t.Errorf("%s was expected, but no error was encountered.", test.errS)
			} else {
				if exists != test.expected {
					t.Errorf("Expected %v got %v for %s", test.expected, exists, test.path)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package bd

import (
	"database/sql"
	"database/sql/driver"
	"time"

	_ "github.com/lib/pq"
	"github.com/rafaeljusto/atiradorfrequente/núcleo/erros"
	"github.com/registrobr/gostk/db"
)

// Conexão armazena a conexão global do sistema com a base de dados. Todas as
// transações com o banco de dados relacional devem ser abertas a partir desta
// variável. Internamente possui um sistema de pool automático, o que garante a
// escalabilidade.
var Conexão BD

// BD representa uma conexão do banco de dados. Util para manipulação em
// cenários de testes de integração.
type BD interface {
	Begin() (Tx, error)
	Close() error
	Driver() driver.Driver
	Exec(query string, args ...interface{}) (sql.Result, error)
	Ping() error
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) *sql.Row
	SetMaxIdleConns(n int)
	SetMaxOpenConns(n int)
}

// Tx representa uma transação do banco de dados. Util para reutilizar
// transações em cenários de testes de integração.
type Tx interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) *sql.Row
	Prepare(query string) (*sql.Stmt, error)
	Rollback() error
	Commit() error
}

type bd struct {
	*db.DB
}

// Begin sobrescreve o comportamento padrão da biblioteca adicionando a
// capacidade de timeout ao se criar uma nova transação.
func (b *bd) Begin() (Tx, error) {
	return b.DB.Begin()
}

// IniciarConexão conecta-se ao banco de dados com os parâmetros informados.
var IniciarConexão = func(parâmetrosConexão db.ConnParams, txTempoEsgotado time.Duration) error {
	// TODO(rafaeljusto): Adicionar semáforos para evitar concorrência no acesso a
	// variável global Conexão

	if Conexão != nil && Conexão.Ping() == nil {
		return nil
	}

	conexão, err := db.ConnectPostgres(parâmetrosConexão)
	if err != nil {
		return erros.Novo(err)
	}

	Conexão = &bd{
		DB: db.NewDB(conexão, txTempoEsgotado),
	}

	if err := Conexão.Ping(); err != nil {
		return erros.Novo(err)
	}

	return nil
}
<commit_msg>Documentando importação oculta<commit_after>package bd

import (
	"database/sql"
	"database/sql/driver"
	"time"

	_ "github.com/lib/pq" // habilitando suporte a biblioteca libpq
	"github.com/rafaeljusto/atiradorfrequente/núcleo/erros"
	"github.com/registrobr/gostk/db"
)

// Conexão armazena a conexão global do sistema com a base de dados. Todas as
// transações com o banco de dados relacional devem ser abertas a partir desta
// variável. Internamente possui um sistema de pool automático, o que garante a
// escalabilidade.
var Conexão BD

// BD representa uma conexão do banco de dados. Util para manipulação em
// cenários de testes de integração.
type BD interface {
	Begin() (Tx, error)
	Close() error
	Driver() driver.Driver
	Exec(query string, args ...interface{}) (sql.Result, error)
	Ping() error
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) *sql.Row
	SetMaxIdleConns(n int)
	SetMaxOpenConns(n int)
}

// Tx representa uma transação do banco de dados. Util para reutilizar
// transações em cenários de testes de integração.
type Tx interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) *sql.Row
	Prepare(query string) (*sql.Stmt, error)
	Rollback() error
	Commit() error
}

type bd struct {
	*db.DB
}

// Begin sobrescreve o comportamento padrão da biblioteca adicionando a
// capacidade de timeout ao se criar uma nova transação.
func (b *bd) Begin() (Tx, error) {
	return b.DB.Begin()
}

// IniciarConexão conecta-se ao banco de dados com os parâmetros informados.
var IniciarConexão = func(parâmetrosConexão db.ConnParams, txTempoEsgotado time.Duration) error {
	// TODO(rafaeljusto): Adicionar semáforos para evitar concorrência no acesso a
	// variável global Conexão

	if Conexão != nil && Conexão.Ping() == nil {
		return nil
	}

	conexão, err := db.ConnectPostgres(parâmetrosConexão)
	if err != nil {
		return erros.Novo(err)
	}

	Conexão = &bd{
		DB: db.NewDB(conexão, txTempoEsgotado),
	}

	if err := Conexão.Ping(); err != nil {
		return erros.Novo(err)
	}

	return nil
}
<|endoftext|>


<commit_before>package auth

import (
	"errors"
	"net/http"
	"net/url"
)

// GitHubUser represents a GitHub user object returned by the OAuth2 service.
type GitHubUser struct {
	UserEmail   string `json:"email"`
	UserAvatar  string `json:"avatar_url"`
	UserName    string `json:"name"`
	UserLogin   string `json:"login"`
	UserLink    string `json:"html_url"`
}

func (u *GitHubUser) Id() string {
	return u.UserLogin
}

func (u *GitHubUser) Provider() string {
	return "github.com"
}

func (u *GitHubUser) Name() string {
	return u.UserName
}

func (u *GitHubUser) Email() string {
	return u.UserEmail
}

func (u *GitHubUser) Picture() string {
	return u.UserAvatar
}

func (u *GitHubUser) Link() string {
	return u.UserLink
}

// GithubProvider is an implementation of Github's Oauth2 protocol.
// See http://developer.github.com/v3/oauth/
type GithubProvider struct {
	OAuth2Mixin

	AuthorizeUrl    string
	AccessTokenUrl  string
	UserResourceUrl string
}

// NewGithubProvider allocates and returns a new GithubProvider.
func NewGithubProvider(clientId, clientSecret string) *GithubProvider {
	github := GithubProvider{}
	github.AuthorizeUrl = "https://github.com/login/oauth/authorize"
	github.AccessTokenUrl = "https://github.com/login/oauth/access_token"
	github.UserResourceUrl = "https://api.github.com/user"
	github.ClientId = clientId
	github.ClientSecret = clientSecret
	return &github
}

// Redirect will do an http.Redirect, sending the user to the Github login
// screen.
func (self *GithubProvider) Redirect(w http.ResponseWriter, r *http.Request) {
	params := make(url.Values)
	params.Add("scope", "users")
	self.OAuth2Mixin.AuthorizeRedirect(w, r, self.AuthorizeUrl, params)
}

// GetAuthenticatedUser will retrieve the Authentication User from the
// http.Request object.
func (self *GithubProvider) GetAuthenticatedUser(r *http.Request) (User, error) {
	// Get the OAuth2 Access Token
	token, err := self.GetAccessToken(r)
	if err != nil {
		return nil, err
	}

	// Use the Access Token to retrieve the user's information
	user := GitHubUser{}
	err = self.OAuth2Mixin.GetAuthenticatedUser(self.UserResourceUrl, token, nil, &user)
	return &user, err
}

// GetAccessToken will retrieve the Access Token from the http.Request URL.
func (self *GithubProvider) GetAccessToken(r *http.Request) (string, error) {

	code := r.URL.Query().Get("code")
	if code == "" {
		return "", errors.New("No Access Code in the Request URL")
	}

	params := make(url.Values)
	params.Add("scope", "users")
	params.Add("code", code)

	return self.OAuth2Mixin.GetAccessToken(self.AccessTokenUrl, params, nil)
}
<commit_msg>Change nullable GithubUser strings to interface{} to avoid json exception<commit_after>package auth

import (
	"errors"
	"net/http"
	"net/url"
)

type GitHubUser struct {
	UserEmail   interface{} `json:"email"`
	UserName    interface{} `json:"name"`
	UserAvatar  interface{} `json:"avatar_url"`
	UserLogin   string `json:"login"`
	UserLink    string `json:"html_url"`
}

func (u *GitHubUser) Id() string {
	return u.UserLogin
}

func (u *GitHubUser) Provider() string {
	return "github.com"
}

func (u *GitHubUser) Link() string {
	return u.UserLink
}

func (u *GitHubUser) Name() string {
	if u.UserName == nil { return "" }
	return u.UserName.(string)
}

func (u *GitHubUser) Email() string {
	if u.UserEmail == nil { return "" }
	return u.UserEmail.(string)
}

func (u *GitHubUser) Picture() string {
	if u.UserAvatar == nil { return "" }
	return u.UserAvatar.(string)
}

// GithubProvider is an implementation of Github's Oauth2 protocol.
// See http://developer.github.com/v3/oauth/
type GithubProvider struct {
	OAuth2Mixin

	AuthorizeUrl    string
	AccessTokenUrl  string
	UserResourceUrl string
}

// NewGithubProvider allocates and returns a new GithubProvider.
func NewGithubProvider(clientId, clientSecret string) *GithubProvider {
	github := GithubProvider{}
	github.AuthorizeUrl = "https://github.com/login/oauth/authorize"
	github.AccessTokenUrl = "https://github.com/login/oauth/access_token"
	github.UserResourceUrl = "https://api.github.com/user"
	github.ClientId = clientId
	github.ClientSecret = clientSecret
	return &github
}

// Redirect will do an http.Redirect, sending the user to the Github login
// screen.
func (self *GithubProvider) Redirect(w http.ResponseWriter, r *http.Request) {
	params := make(url.Values)
	params.Add("scope", "users")
	self.OAuth2Mixin.AuthorizeRedirect(w, r, self.AuthorizeUrl, params)
}

// GetAuthenticatedUser will retrieve the Authentication User from the
// http.Request object.
func (self *GithubProvider) GetAuthenticatedUser(r *http.Request) (User, error) {
	// Get the OAuth2 Access Token
	token, err := self.GetAccessToken(r)
	if err != nil {
		return nil, err
	}

	// Use the Access Token to retrieve the user's information
	user := GitHubUser{}
	err = self.OAuth2Mixin.GetAuthenticatedUser(self.UserResourceUrl, token, nil, &user)
	return &user, err
}

// GetAccessToken will retrieve the Access Token from the http.Request URL.
func (self *GithubProvider) GetAccessToken(r *http.Request) (string, error) {

	code := r.URL.Query().Get("code")
	if code == "" {
		return "", errors.New("No Access Code in the Request URL")
	}

	params := make(url.Values)
	params.Add("scope", "users")
	params.Add("code", code)

	return self.OAuth2Mixin.GetAccessToken(self.AccessTokenUrl, params, nil)
}
<|endoftext|>