<commit_before>package cli

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/kopia/kopia/fs"
	"github.com/kopia/kopia/fs/repofs"
	"github.com/kopia/kopia/internal/units"
	"github.com/kopia/kopia/policy"
	"github.com/kopia/kopia/repo"
	"github.com/kopia/kopia/repo/object"
	"github.com/kopia/kopia/snapshot"
)

var (
	snapshotListCommand              = snapshotCommands.Command("list", "List snapshots of files and directories.").Alias("ls")
	snapshotListPath                 = snapshotListCommand.Arg("source", "File or directory to show history of.").String()
	snapshotListIncludeIncomplete    = snapshotListCommand.Flag("incomplete", "Include incomplete.").Short('i').Bool()
	snapshotListShowHumanReadable    = snapshotListCommand.Flag("human-readable", "Show human-readable units").Default("true").Bool()
	snapshotListShowDelta            = snapshotListCommand.Flag("delta", "Include deltas.").Short('d').Bool()
	snapshotListShowItemID           = snapshotListCommand.Flag("manifest-id", "Include manifest item ID.").Short('m').Bool()
	snapshotListShowHashCache        = snapshotListCommand.Flag("hashcache", "Include hashcache object ID.").Bool()
	snapshotListShowRetentionReasons = snapshotListCommand.Flag("retention", "Include retention reasons.").Default("true").Bool()
	snapshotListShowModTime          = snapshotListCommand.Flag("mtime", "Include file mod time").Bool()
	shapshotListShowOwner            = snapshotListCommand.Flag("owner", "Include owner").Bool()
	maxResultsPerPath                = snapshotListCommand.Flag("max-results", "Maximum number of results.").Default("1000").Int()
)

func findSnapshotsForSource(ctx context.Context, rep *repo.Repository, sourceInfo snapshot.SourceInfo) (manifestIDs []string, relPath string, err error) {
	for len(sourceInfo.Path) > 0 {
		list, err := snapshot.ListSnapshotManifests(ctx, rep, &sourceInfo)
		if err != nil {
			return nil, "", err
		}

		if len(list) > 0 {
			return list, relPath, nil
		}

		if len(relPath) > 0 {
			relPath = filepath.Base(sourceInfo.Path) + "/" + relPath
		} else {
			relPath = filepath.Base(sourceInfo.Path)
		}

		log.Debugf("No snapshots of %v@%v:%v", sourceInfo.UserName, sourceInfo.Host, sourceInfo.Path)

		parentPath := filepath.Dir(sourceInfo.Path)
		if parentPath == sourceInfo.Path {
			break
		}
		sourceInfo.Path = parentPath
	}

	return nil, "", nil
}

func findManifestIDs(ctx context.Context, rep *repo.Repository, source string) ([]string, string, error) {
	if source == "" {
		man, err := snapshot.ListSnapshotManifests(ctx, rep, nil)
		return man, "", err
	}

	si, err := snapshot.ParseSourceInfo(source, getHostName(), getUserName())
	if err != nil {
		return nil, "", fmt.Errorf("invalid directory: '%s': %s", source, err)
	}

	manifestIDs, relPath, err := findSnapshotsForSource(ctx, rep, si)
	if relPath != "" {
		relPath = "/" + relPath
	}

	return manifestIDs, relPath, err
}

func runSnapshotsCommand(ctx context.Context, rep *repo.Repository) error {
	manifestIDs, relPath, err := findManifestIDs(ctx, rep, *snapshotListPath)
	if err != nil {
		return err
	}

	manifests, err := snapshot.LoadSnapshots(ctx, rep, manifestIDs)
	if err != nil {
		return err
	}

	return outputManifestGroups(ctx, rep, manifests, strings.Split(relPath, "/"))
}

func outputManifestGroups(ctx context.Context, rep *repo.Repository, manifests []*snapshot.Manifest, relPathParts []string) error {
	separator := ""
	for _, snapshotGroup := range snapshot.GroupBySource(manifests) {
		src := snapshotGroup[0].Source
		fmt.Printf("%v%v\n", separator, src)
		separator = "\n"

		pol, _, err := policy.GetEffectivePolicy(ctx, rep, src)
		if err != nil {
			log.Warningf("unable to determine effective policy for %v", src)
		} else {
			pol.RetentionPolicy.ComputeRetentionReasons(snapshotGroup)
		}
		if err := outputManifestFromSingleSource(ctx, rep, snapshotGroup, relPathParts); err != nil {
			return err
		}
	}

	return nil
}

//nolint:gocyclo
func outputManifestFromSingleSource(ctx context.Context, rep *repo.Repository, manifests []*snapshot.Manifest, parts []string) error {
	var count int
	var lastTotalFileSize int64

	manifests = snapshot.SortByTime(manifests, false)
	if len(manifests) > *maxResultsPerPath {
		manifests = manifests[len(manifests)-*maxResultsPerPath:]
	}

	for _, m := range manifests {
		root, err := repofs.SnapshotRoot(rep, m)
		if err != nil {
			fmt.Printf("  %v <ERROR> %v\n", m.StartTime.Format("2006-01-02 15:04:05 MST"), err)
			continue
		}
		ent, err := getNestedEntry(ctx, root, parts)
		if err != nil {
			fmt.Printf("  %v <ERROR> %v\n", m.StartTime.Format("2006-01-02 15:04:05 MST"), err)
			continue
		}

		if _, ok := ent.(object.HasObjectID); !ok {
			log.Warningf("entry does not have object ID: %v", ent, err)
			continue
		}

		var bits []string
		if m.IncompleteReason != "" {
			if !*snapshotListIncludeIncomplete {
				continue
			}
			bits = append(bits, "incomplete:"+m.IncompleteReason)
		}

		bits = append(bits, maybeHumanReadableBytes(*snapshotListShowHumanReadable, ent.Metadata().FileSize))
		bits = append(bits, fmt.Sprintf("%v", ent.Metadata().FileMode()))
		if *shapshotListShowOwner {
			bits = append(bits, fmt.Sprintf("uid:%v", ent.Metadata().UserID))
			bits = append(bits, fmt.Sprintf("gid:%v", ent.Metadata().GroupID))
		}
		if *snapshotListShowModTime {
			bits = append(bits, fmt.Sprintf("modified:%v", ent.Metadata().ModTime.Format(timeFormat)))
		}

		if *snapshotListShowItemID {
			bits = append(bits, "manifest:"+m.ID)
		}
		if *snapshotListShowHashCache {
			bits = append(bits, "hashcache:"+m.HashCacheID.String())
		}

		if *snapshotListShowDelta {
			bits = append(bits, deltaBytes(ent.Metadata().FileSize-lastTotalFileSize))
		}

		if d, ok := ent.(fs.Directory); ok {
			s := d.Summary()
			if s != nil {
				bits = append(bits, fmt.Sprintf("files:%v", s.TotalFileCount))
				bits = append(bits, fmt.Sprintf("dirs:%v", s.TotalDirCount))
			}
		}

		if *snapshotListShowRetentionReasons {
			if len(m.RetentionReasons) > 0 {
				bits = append(bits, "retention:"+strings.Join(m.RetentionReasons, ","))
			}
		}

		fmt.Printf(
			"  %v %v %v\n",
			m.StartTime.Format("2006-01-02 15:04:05 MST"),
			ent.(object.HasObjectID).ObjectID(),
			strings.Join(bits, " "),
		)

		count++
		if m.IncompleteReason == "" {
			lastTotalFileSize = m.Stats.TotalFileSize
		}
	}

	return nil
}

func deltaBytes(b int64) string {
	if b > 0 {
		return "(+" + units.BytesStringBase10(b) + ")"
	}

	return ""
}

func init() {
	snapshotListCommand.Action(repositoryAction(runSnapshotsCommand))
}
<commit_msg>cli: added 'snapshot list --skip-identical option to shorten the output<commit_after>package cli

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	"github.com/kopia/kopia/fs"
	"github.com/kopia/kopia/fs/repofs"
	"github.com/kopia/kopia/internal/units"
	"github.com/kopia/kopia/policy"
	"github.com/kopia/kopia/repo"
	"github.com/kopia/kopia/repo/object"
	"github.com/kopia/kopia/snapshot"
)

var (
	snapshotListCommand              = snapshotCommands.Command("list", "List snapshots of files and directories.").Alias("ls")
	snapshotListPath                 = snapshotListCommand.Arg("source", "File or directory to show history of.").String()
	snapshotListIncludeIncomplete    = snapshotListCommand.Flag("incomplete", "Include incomplete.").Short('i').Bool()
	snapshotListShowHumanReadable    = snapshotListCommand.Flag("human-readable", "Show human-readable units").Default("true").Bool()
	snapshotListShowDelta            = snapshotListCommand.Flag("delta", "Include deltas.").Short('d').Bool()
	snapshotListShowItemID           = snapshotListCommand.Flag("manifest-id", "Include manifest item ID.").Short('m').Bool()
	snapshotListShowHashCache        = snapshotListCommand.Flag("hashcache", "Include hashcache object ID.").Bool()
	snapshotListShowRetentionReasons = snapshotListCommand.Flag("retention", "Include retention reasons.").Default("true").Bool()
	snapshotListShowModTime          = snapshotListCommand.Flag("mtime", "Include file mod time").Bool()
	shapshotListShowOwner            = snapshotListCommand.Flag("owner", "Include owner").Bool()
	snapshotListSkipIdentical        = snapshotListCommand.Flag("skip-identical", "Skip identical snapshots").Bool()
	maxResultsPerPath                = snapshotListCommand.Flag("max-results", "Maximum number of results.").Default("1000").Int()
)

func findSnapshotsForSource(ctx context.Context, rep *repo.Repository, sourceInfo snapshot.SourceInfo) (manifestIDs []string, relPath string, err error) {
	for len(sourceInfo.Path) > 0 {
		list, err := snapshot.ListSnapshotManifests(ctx, rep, &sourceInfo)
		if err != nil {
			return nil, "", err
		}

		if len(list) > 0 {
			return list, relPath, nil
		}

		if len(relPath) > 0 {
			relPath = filepath.Base(sourceInfo.Path) + "/" + relPath
		} else {
			relPath = filepath.Base(sourceInfo.Path)
		}

		log.Debugf("No snapshots of %v@%v:%v", sourceInfo.UserName, sourceInfo.Host, sourceInfo.Path)

		parentPath := filepath.Dir(sourceInfo.Path)
		if parentPath == sourceInfo.Path {
			break
		}
		sourceInfo.Path = parentPath
	}

	return nil, "", nil
}

func findManifestIDs(ctx context.Context, rep *repo.Repository, source string) ([]string, string, error) {
	if source == "" {
		man, err := snapshot.ListSnapshotManifests(ctx, rep, nil)
		return man, "", err
	}

	si, err := snapshot.ParseSourceInfo(source, getHostName(), getUserName())
	if err != nil {
		return nil, "", fmt.Errorf("invalid directory: '%s': %s", source, err)
	}

	manifestIDs, relPath, err := findSnapshotsForSource(ctx, rep, si)
	if relPath != "" {
		relPath = "/" + relPath
	}

	return manifestIDs, relPath, err
}

func runSnapshotsCommand(ctx context.Context, rep *repo.Repository) error {
	manifestIDs, relPath, err := findManifestIDs(ctx, rep, *snapshotListPath)
	if err != nil {
		return err
	}

	manifests, err := snapshot.LoadSnapshots(ctx, rep, manifestIDs)
	if err != nil {
		return err
	}

	return outputManifestGroups(ctx, rep, manifests, strings.Split(relPath, "/"))
}

func outputManifestGroups(ctx context.Context, rep *repo.Repository, manifests []*snapshot.Manifest, relPathParts []string) error {
	separator := ""
	for _, snapshotGroup := range snapshot.GroupBySource(manifests) {
		src := snapshotGroup[0].Source
		fmt.Printf("%v%v\n", separator, src)
		separator = "\n"

		pol, _, err := policy.GetEffectivePolicy(ctx, rep, src)
		if err != nil {
			log.Warningf("unable to determine effective policy for %v", src)
		} else {
			pol.RetentionPolicy.ComputeRetentionReasons(snapshotGroup)
		}
		if err := outputManifestFromSingleSource(ctx, rep, snapshotGroup, relPathParts); err != nil {
			return err
		}
	}

	return nil
}

//nolint:gocyclo
func outputManifestFromSingleSource(ctx context.Context, rep *repo.Repository, manifests []*snapshot.Manifest, parts []string) error {
	var count int
	var lastTotalFileSize int64

	manifests = snapshot.SortByTime(manifests, false)
	if len(manifests) > *maxResultsPerPath {
		manifests = manifests[len(manifests)-*maxResultsPerPath:]
	}

	var previousOID object.ID
	var elidedCount int
	var maxElidedTime time.Time

	outputElided := func() {
		if elidedCount > 0 {
			fmt.Printf(
				"  + %v identical snapshots until %v\n\n",
				elidedCount,
				maxElidedTime.Format("2006-01-02 15:04:05 MST"),
			)
		}
	}

	for _, m := range manifests {
		root, err := repofs.SnapshotRoot(rep, m)
		if err != nil {
			fmt.Printf("  %v <ERROR> %v\n", m.StartTime.Format("2006-01-02 15:04:05 MST"), err)
			continue
		}
		ent, err := getNestedEntry(ctx, root, parts)
		if err != nil {
			fmt.Printf("  %v <ERROR> %v\n", m.StartTime.Format("2006-01-02 15:04:05 MST"), err)
			continue
		}

		if _, ok := ent.(object.HasObjectID); !ok {
			log.Warningf("entry does not have object ID: %v", ent, err)
			continue
		}

		var bits []string
		if m.IncompleteReason != "" {
			if !*snapshotListIncludeIncomplete {
				continue
			}
			bits = append(bits, "incomplete:"+m.IncompleteReason)
		}

		bits = append(bits, maybeHumanReadableBytes(*snapshotListShowHumanReadable, ent.Metadata().FileSize))
		bits = append(bits, fmt.Sprintf("%v", ent.Metadata().FileMode()))
		if *shapshotListShowOwner {
			bits = append(bits, fmt.Sprintf("uid:%v", ent.Metadata().UserID))
			bits = append(bits, fmt.Sprintf("gid:%v", ent.Metadata().GroupID))
		}
		if *snapshotListShowModTime {
			bits = append(bits, fmt.Sprintf("modified:%v", ent.Metadata().ModTime.Format(timeFormat)))
		}

		if *snapshotListShowItemID {
			bits = append(bits, "manifest:"+m.ID)
		}
		if *snapshotListShowHashCache {
			bits = append(bits, "hashcache:"+m.HashCacheID.String())
		}

		if *snapshotListShowDelta {
			bits = append(bits, deltaBytes(ent.Metadata().FileSize-lastTotalFileSize))
		}

		if d, ok := ent.(fs.Directory); ok {
			s := d.Summary()
			if s != nil {
				bits = append(bits, fmt.Sprintf("files:%v", s.TotalFileCount))
				bits = append(bits, fmt.Sprintf("dirs:%v", s.TotalDirCount))
			}
		}

		if *snapshotListShowRetentionReasons {
			if len(m.RetentionReasons) > 0 {
				bits = append(bits, "("+strings.Join(m.RetentionReasons, ",")+")")
			}
		}

		oid := ent.(object.HasObjectID).ObjectID()
		if *snapshotListSkipIdentical && oid == previousOID {
			elidedCount++
			maxElidedTime = m.StartTime
			continue
		}

		previousOID = oid

		outputElided()
		elidedCount = 0

		fmt.Printf(
			"  %v %v %v\n",
			m.StartTime.Format("2006-01-02 15:04:05 MST"),
			oid,
			strings.Join(bits, " "),
		)

		count++
		if m.IncompleteReason == "" {
			lastTotalFileSize = m.Stats.TotalFileSize
		}
	}
	outputElided()

	return nil
}

func deltaBytes(b int64) string {
	if b > 0 {
		return "(+" + units.BytesStringBase10(b) + ")"
	}

	return ""
}

func init() {
	snapshotListCommand.Action(repositoryAction(runSnapshotsCommand))
}
<|endoftext|>


<commit_before>package main

const VERSION = "0.6.3-alpha3"
<commit_msg>:+1: Bump up the version to 0.6.3-alpha4<commit_after>package main

const VERSION = "0.6.3-alpha4"
<|endoftext|>


<commit_before>package main

const Version string = "0.2"
<commit_msg>prepare for next development iteration.<commit_after>package main

const Version string = "0.3-SNAPSHOT"
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

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

package tchannel

// VersionInfo identifies the version of the TChannel library.
// Due to lack of proper package management, this version string will
// be maintained manually.
const VersionInfo = "1.0.2-dev"
<commit_msg>Bump version<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

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

package tchannel

// VersionInfo identifies the version of the TChannel library.
// Due to lack of proper package management, this version string will
// be maintained manually.
const VersionInfo = "1.0.2"
<|endoftext|>


<commit_before>package main

const acVersion string = "20161106"
<commit_msg>version bump<commit_after>package main

const acVersion string = "20161127"
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2015 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"strings"
)

// semanticAlphabet
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"

// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
	appMajor uint = 0
	appMinor uint = 2
	appPatch uint = 0

	// appPreRelease MUST only contain characters from semanticAlphabet
	// per the semantic versioning spec.
	appPreRelease = "beta"
)

// appBuild is defined as a variable so it can be overridden during the build
// process with '-ldflags "-X main.appBuild foo' if needed.  It MUST only
// contain characters from semanticAlphabet per the semantic versioning spec.
var appBuild string

// version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func version() string {
	// Start with the major, minor, and path versions.
	version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)

	// Append pre-release version if there is one.  The hyphen called for
	// by the semantic versioning spec is automatically appended and should
	// not be contained in the pre-release string.  The pre-release version
	// is not appended if it contains invalid characters.
	preRelease := normalizeVerString(appPreRelease)
	if preRelease != "" {
		version = fmt.Sprintf("%s-%s", version, preRelease)
	}

	// Append build metadata if there is any.  The plus called for
	// by the semantic versioning spec is automatically appended and should
	// not be contained in the build metadata string.  The build metadata
	// string is not appended if it contains invalid characters.
	build := normalizeVerString(appBuild)
	if build != "" {
		version = fmt.Sprintf("%s+%s", version, build)
	}

	return version
}

// normalizeVerString returns the passed string stripped of all characters which
// are not valid according to the semantic versioning guidelines for pre-release
// version and build metadata strings.  In particular they MUST only contain
// characters in semanticAlphabet.
func normalizeVerString(str string) string {
	result := bytes.Buffer{}
	for _, r := range str {
		if strings.ContainsRune(semanticAlphabet, r) {
			_, err := result.WriteRune(r)
			// Writing to a bytes.Buffer panics on OOM, and all
			// errors are unexpected.
			if err != nil {
				panic(err)
			}
		}
	}
	return result.String()
}
<commit_msg>Bump for v0.3.0<commit_after>// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2015 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"strings"
)

// semanticAlphabet
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"

// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
	appMajor uint = 0
	appMinor uint = 3
	appPatch uint = 0

	// appPreRelease MUST only contain characters from semanticAlphabet
	// per the semantic versioning spec.
	appPreRelease = "beta"
)

// appBuild is defined as a variable so it can be overridden during the build
// process with '-ldflags "-X main.appBuild foo' if needed.  It MUST only
// contain characters from semanticAlphabet per the semantic versioning spec.
var appBuild string

// version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func version() string {
	// Start with the major, minor, and path versions.
	version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)

	// Append pre-release version if there is one.  The hyphen called for
	// by the semantic versioning spec is automatically appended and should
	// not be contained in the pre-release string.  The pre-release version
	// is not appended if it contains invalid characters.
	preRelease := normalizeVerString(appPreRelease)
	if preRelease != "" {
		version = fmt.Sprintf("%s-%s", version, preRelease)
	}

	// Append build metadata if there is any.  The plus called for
	// by the semantic versioning spec is automatically appended and should
	// not be contained in the build metadata string.  The build metadata
	// string is not appended if it contains invalid characters.
	build := normalizeVerString(appBuild)
	if build != "" {
		version = fmt.Sprintf("%s+%s", version, build)
	}

	return version
}

// normalizeVerString returns the passed string stripped of all characters which
// are not valid according to the semantic versioning guidelines for pre-release
// version and build metadata strings.  In particular they MUST only contain
// characters in semanticAlphabet.
func normalizeVerString(str string) string {
	result := bytes.Buffer{}
	for _, r := range str {
		if strings.ContainsRune(semanticAlphabet, r) {
			_, err := result.WriteRune(r)
			// Writing to a bytes.Buffer panics on OOM, and all
			// errors are unexpected.
			if err != nil {
				panic(err)
			}
		}
	}
	return result.String()
}
<|endoftext|>


<commit_before>package main

const VERSION = "0.5.4"
<commit_msg>:+1: Bump up the version<commit_after>package main

const VERSION = "0.5.5"
<|endoftext|>


<commit_before>// Licensed to The Moov Authors under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. The Moov Authors licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package ach

// Version Number
const Version = "v1.4.3"
<commit_msg>release v1.4.4<commit_after>// Licensed to The Moov Authors under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. The Moov Authors licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package ach

// Version Number
const Version = "v1.4.4"
<|endoftext|>


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

package ebiten

import (
	"time"

	"github.com/hajimehoshi/ebiten/v2/internal/gamepad"
	"github.com/hajimehoshi/ebiten/v2/internal/vibrate"
)

// VibrateOptions represents the options for device vibration.
type VibrateOptions struct {
	// Duration is the time duration of the effect.
	Duration time.Duration

	// Magnitude is the strength of the device vibration.
	// The value is in between 0 and 1.
	Magnitude float64
}

// Vibrate vibrates the device with the specified options.
//
// Vibrate works on mobiles and browsers.
//
// On browsers, Magnitude in the options is ignored.
//
// On Android, this line is required in the manifest setting to use Vibrate:
//
//	<uses-permission android:name="android.permission.VIBRATE"/>
//
// On Android, Magnitude in the options is recognized only when the API Level is 26 or newer.
// Otherwise, Magnitude is ignored.
//
// On iOS, CoreHaptics.framework is required to use Vibrate.
//
// On iOS, Vibrate works only when iOS version is 13.0 or newer.
// Otherwise, Vibrate does nothing.
//
// Vibrate is concurrent-safe.
func Vibrate(options *VibrateOptions) {
	vibrate.Vibrate(options.Duration, options.Magnitude)
}

// VibrateGamepadOptions represents the options for gamepad vibration.
type VibrateGamepadOptions struct {
	// Duration is the time duration of the effect.
	Duration time.Duration

	// StrongMagnitude is the rumble intensity of a low-frequency rumble motor.
	// The value is in between 0 and 1.
	StrongMagnitude float64

	// StrongMagnitude is the rumble intensity of a high-frequency rumble motor.
	// The value is in between 0 and 1.
	WeakMagnitude float64
}

// VibrateGamepad vibrates the specified gamepad with the specified options.
//
// VibrateGamepad works only on browsers and Nintendo Switch so far.
//
// VibrateGamepad is concurrent-safe.
func VibrateGamepad(gamepadID GamepadID, options *VibrateGamepadOptions) {
	g := gamepad.Get(gamepadID)
	if g == nil {
		return
	}
	g.Vibrate(options.Duration, options.StrongMagnitude, options.WeakMagnitude)
}
<commit_msg>ebiten: typo<commit_after>// Copyright 2021 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"time"

	"github.com/hajimehoshi/ebiten/v2/internal/gamepad"
	"github.com/hajimehoshi/ebiten/v2/internal/vibrate"
)

// VibrateOptions represents the options for device vibration.
type VibrateOptions struct {
	// Duration is the time duration of the effect.
	Duration time.Duration

	// Magnitude is the strength of the device vibration.
	// The value is in between 0 and 1.
	Magnitude float64
}

// Vibrate vibrates the device with the specified options.
//
// Vibrate works on mobiles and browsers.
//
// On browsers, Magnitude in the options is ignored.
//
// On Android, this line is required in the manifest setting to use Vibrate:
//
//	<uses-permission android:name="android.permission.VIBRATE"/>
//
// On Android, Magnitude in the options is recognized only when the API Level is 26 or newer.
// Otherwise, Magnitude is ignored.
//
// On iOS, CoreHaptics.framework is required to use Vibrate.
//
// On iOS, Vibrate works only when iOS version is 13.0 or newer.
// Otherwise, Vibrate does nothing.
//
// Vibrate is concurrent-safe.
func Vibrate(options *VibrateOptions) {
	vibrate.Vibrate(options.Duration, options.Magnitude)
}

// VibrateGamepadOptions represents the options for gamepad vibration.
type VibrateGamepadOptions struct {
	// Duration is the time duration of the effect.
	Duration time.Duration

	// StrongMagnitude is the rumble intensity of a low-frequency rumble motor.
	// The value is in between 0 and 1.
	StrongMagnitude float64

	// WeakMagnitude is the rumble intensity of a high-frequency rumble motor.
	// The value is in between 0 and 1.
	WeakMagnitude float64
}

// VibrateGamepad vibrates the specified gamepad with the specified options.
//
// VibrateGamepad works only on browsers and Nintendo Switch so far.
//
// VibrateGamepad is concurrent-safe.
func VibrateGamepad(gamepadID GamepadID, options *VibrateGamepadOptions) {
	g := gamepad.Get(gamepadID)
	if g == nil {
		return
	}
	g.Vibrate(options.Duration, options.StrongMagnitude, options.WeakMagnitude)
}
<|endoftext|>


<commit_before>// Copyright 2016 Olivier Mengué. All rights reserved.
// Use of this source code is governed by the Apache 2.0 license that
// can be found in the LICENSE file.

// Package jsonptr implements JSON Pointer (RFC 6901) lookup
package jsonptr

import (
	"bytes"
	"errors"
	"fmt"
	"strconv"
)

var (
	ErrSyntax   = errors.New("invalid JSON pointer")
	ErrIndex    = errors.New("invalid array index")
	ErrProperty = errors.New("property not found")
)

// PtrError is the structured error for JSON Pointer parsing or navigation
// errors
type PtrError struct {
	// Ptr is the substring of the original pointer where the error occured
	Ptr string
	// Err is one of ErrSyntax, ErrIndex, ErrProperty
	Err error
}

// Error implement the 'error' interface
func (e *PtrError) Error() string {
	return strconv.Quote(e.Ptr) + ": " + e.Err.Error()
}

func syntaxError(ptr string) *PtrError {
	return &PtrError{ptr, ErrSyntax}
}

func indexError(ptr string) *PtrError {
	return &PtrError{ptr, ErrIndex}
}

func propertyError(ptr string) *PtrError {
	return &PtrError{ptr, ErrProperty}
}

func docError(ptr string, doc interface{}) *PtrError {
	return &PtrError{ptr, fmt.Errorf("not an object or array but %T", doc)}
}

func arrayIndex(b []byte) (int, error) {
	if len(b) == 0 {
		return -1, ErrSyntax
	}
	if len(b) == 1 {
		if b[0] == '0' {
			return 0, nil
		}
		if b[0] == '-' {
			return -1, nil
		}
	}
	if b[0] < '1' {
		return -1, ErrSyntax
	}
	var n int
	const maxInt = (1 << (strconv.IntSize - 1)) - 1
	const cutoff = maxInt/10 + 1
	for i := 0; i < len(b); i++ {
		c := b[i]
		if c < '0' || c > '9' {
			return -1, ErrSyntax
		}
		if n >= cutoff {
			// Overflow
			return -1, ErrSyntax
		}
		n *= 10
		n1 := n + int(c-'0')
		if n1 < n || n1 > maxInt {
			// Overflow
			return -1, ErrSyntax
		}
		n = n1
	}
	return n, nil
}

func propertyName(b []byte) (string, error) {
	// FIXME reject '~' followed by something else than '0', '1'
	return string(
		bytes.Replace(
			bytes.Replace(b,
				[]byte(`~1`), []byte(`/`), -1),
			[]byte(`~0`), []byte(`~`), -1),
	), nil
}

// Get extracts a value from a JSON-like data tree
//
// In case of error a PtrError is returned
func Get(doc interface{}, ptr string) (interface{}, error) {
	if len(ptr) == 0 {
		return doc, nil
	}
	if ptr[0] != '/' {
		return nil, syntaxError(ptr)
	}
	bptr := []byte(ptr)
	cur := bptr[1:]
	p := int(1)
	for {
		q := bytes.IndexByte(cur, `/`[0])
		if q == -1 {
			q = len(cur)
		}
		p += q

		switch here := (doc).(type) {
		case map[string]interface{}:
			key, err := propertyName(cur[:q])
			if err != nil {
				return nil, &PtrError{string(bptr[:p]), err}
			}
			var ok bool
			if doc, ok = here[key]; !ok {
				return nil, propertyError(string(bptr[:p]))
			}
		case []interface{}:
			n, err := arrayIndex(cur[:q])
			if err != nil {
				return nil, &PtrError{string(bptr[:p]), err}
			}
			if n < 0 || n >= len(here) {
				return nil, indexError(string(bptr[:p]))
			}
			doc = here[n]
		default:
			return nil, docError(string(bptr[:p]), doc)
		}
		if p >= len(bptr) {
			break
		}
		p++
		cur = bptr[p:]
	}

	return doc, nil
}

// Set modifies a JSON-like data tree
//
// In case of error a PtrError is returned
func Set(doc *interface{}, ptr string, value interface{}) error {
	if len(ptr) == 0 {
		*doc = value
		return nil
	}
	bptr := []byte(ptr)
	p := bytes.LastIndexByte(bptr, '/')
	if p < 0 {
		return syntaxError(ptr)
	}
	prop := bptr[p+1:]
	parentPtr := string(bptr[:p])

	parent, err := Get(*doc, parentPtr)
	if err != nil {
		return err
	}

	switch parent := (parent).(type) {
	case map[string]interface{}:
		key, err := propertyName(prop)
		if err != nil {
			return &PtrError{ptr, err}
		}
		parent[key] = value
	case []interface{}:
		n, err := arrayIndex(prop)
		if err != nil {
			return &PtrError{ptr, err}

		}
		if n == -1 {
			n = len(parent)
		} else if n < len(parent) {
			parent[n] = value
			return nil
		}

		// if n > len(parent) {
		//	return &PtrError{ptr, ErrIndex}
		//}

		// TODO make+copy
		for i := n - len(parent) - 1; i > 0; i-- {
			parent = append(parent, nil)
		}
		parent = append(parent, value)
		// We appended beyond original len, so the slice changed so we have to
		// store the new one at the old place
		// No error can happen as we already parsed the pointer
		_ = Set(doc, parentPtr, parent)
	default:
		return docError(parentPtr, parent)
	}

	return nil
}
<commit_msg>Fix typo in documentation<commit_after>// Copyright 2016 Olivier Mengué. All rights reserved.
// Use of this source code is governed by the Apache 2.0 license that
// can be found in the LICENSE file.

// Package jsonptr implements JSON Pointer (RFC 6901) lookup
package jsonptr

import (
	"bytes"
	"errors"
	"fmt"
	"strconv"
)

var (
	ErrSyntax   = errors.New("invalid JSON pointer")
	ErrIndex    = errors.New("invalid array index")
	ErrProperty = errors.New("property not found")
)

// PtrError is the structured error for JSON Pointer parsing or navigation
// errors
type PtrError struct {
	// Ptr is the substring of the original pointer where the error occurred
	Ptr string
	// Err is one of ErrSyntax, ErrIndex, ErrProperty
	Err error
}

// Error implement the 'error' interface
func (e *PtrError) Error() string {
	return strconv.Quote(e.Ptr) + ": " + e.Err.Error()
}

func syntaxError(ptr string) *PtrError {
	return &PtrError{ptr, ErrSyntax}
}

func indexError(ptr string) *PtrError {
	return &PtrError{ptr, ErrIndex}
}

func propertyError(ptr string) *PtrError {
	return &PtrError{ptr, ErrProperty}
}

func docError(ptr string, doc interface{}) *PtrError {
	return &PtrError{ptr, fmt.Errorf("not an object or array but %T", doc)}
}

func arrayIndex(b []byte) (int, error) {
	if len(b) == 0 {
		return -1, ErrSyntax
	}
	if len(b) == 1 {
		if b[0] == '0' {
			return 0, nil
		}
		if b[0] == '-' {
			return -1, nil
		}
	}
	if b[0] < '1' {
		return -1, ErrSyntax
	}
	var n int
	const maxInt = (1 << (strconv.IntSize - 1)) - 1
	const cutoff = maxInt/10 + 1
	for i := 0; i < len(b); i++ {
		c := b[i]
		if c < '0' || c > '9' {
			return -1, ErrSyntax
		}
		if n >= cutoff {
			// Overflow
			return -1, ErrSyntax
		}
		n *= 10
		n1 := n + int(c-'0')
		if n1 < n || n1 > maxInt {
			// Overflow
			return -1, ErrSyntax
		}
		n = n1
	}
	return n, nil
}

func propertyName(b []byte) (string, error) {
	// FIXME reject '~' followed by something else than '0', '1'
	return string(
		bytes.Replace(
			bytes.Replace(b,
				[]byte(`~1`), []byte(`/`), -1),
			[]byte(`~0`), []byte(`~`), -1),
	), nil
}

// Get extracts a value from a JSON-like data tree
//
// In case of error a PtrError is returned
func Get(doc interface{}, ptr string) (interface{}, error) {
	if len(ptr) == 0 {
		return doc, nil
	}
	if ptr[0] != '/' {
		return nil, syntaxError(ptr)
	}
	bptr := []byte(ptr)
	cur := bptr[1:]
	p := int(1)
	for {
		q := bytes.IndexByte(cur, `/`[0])
		if q == -1 {
			q = len(cur)
		}
		p += q

		switch here := (doc).(type) {
		case map[string]interface{}:
			key, err := propertyName(cur[:q])
			if err != nil {
				return nil, &PtrError{string(bptr[:p]), err}
			}
			var ok bool
			if doc, ok = here[key]; !ok {
				return nil, propertyError(string(bptr[:p]))
			}
		case []interface{}:
			n, err := arrayIndex(cur[:q])
			if err != nil {
				return nil, &PtrError{string(bptr[:p]), err}
			}
			if n < 0 || n >= len(here) {
				return nil, indexError(string(bptr[:p]))
			}
			doc = here[n]
		default:
			return nil, docError(string(bptr[:p]), doc)
		}
		if p >= len(bptr) {
			break
		}
		p++
		cur = bptr[p:]
	}

	return doc, nil
}

// Set modifies a JSON-like data tree
//
// In case of error a PtrError is returned
func Set(doc *interface{}, ptr string, value interface{}) error {
	if len(ptr) == 0 {
		*doc = value
		return nil
	}
	bptr := []byte(ptr)
	p := bytes.LastIndexByte(bptr, '/')
	if p < 0 {
		return syntaxError(ptr)
	}
	prop := bptr[p+1:]
	parentPtr := string(bptr[:p])

	parent, err := Get(*doc, parentPtr)
	if err != nil {
		return err
	}

	switch parent := (parent).(type) {
	case map[string]interface{}:
		key, err := propertyName(prop)
		if err != nil {
			return &PtrError{ptr, err}
		}
		parent[key] = value
	case []interface{}:
		n, err := arrayIndex(prop)
		if err != nil {
			return &PtrError{ptr, err}

		}
		if n == -1 {
			n = len(parent)
		} else if n < len(parent) {
			parent[n] = value
			return nil
		}

		// if n > len(parent) {
		//	return &PtrError{ptr, ErrIndex}
		//}

		// TODO make+copy
		for i := n - len(parent) - 1; i > 0; i-- {
			parent = append(parent, nil)
		}
		parent = append(parent, value)
		// We appended beyond original len, so the slice changed so we have to
		// store the new one at the old place
		// No error can happen as we already parsed the pointer
		_ = Set(doc, parentPtr, parent)
	default:
		return docError(parentPtr, parent)
	}

	return nil
}
<|endoftext|>


<commit_before>package jwtauth

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
)

var (
	TokenCtxKey = &contextKey{"Token"}
	ErrorCtxKey = &contextKey{"Error"}
)

var (
	ErrUnauthorized = errors.New("jwtauth: token is unauthorized")
	ErrExpired      = errors.New("jwtauth: token is expired")
)

type JwtAuth struct {
	signKey   interface{}
	verifyKey interface{}
	signer    jwt.SigningMethod
	parser    *jwt.Parser
}

// New creates a JwtAuth authenticator instance that provides middleware handlers
// and encoding/decoding functions for JWT signing.
func New(alg string, signKey interface{}, verifyKey interface{}) *JwtAuth {
	return NewWithParser(alg, &jwt.Parser{}, signKey, verifyKey)
}

// NewWithParser is the same as New, except it supports custom parser settings
// introduced in jwt-go/v2.4.0.
//
// We explicitly toggle `SkipClaimsValidation` in the `jwt-go` parser so that
// we can control when the claims are validated - in our case, by the Verifier
// http middleware handler.
func NewWithParser(alg string, parser *jwt.Parser, signKey interface{}, verifyKey interface{}) *JwtAuth {
	parser.SkipClaimsValidation = true
	return &JwtAuth{
		signKey:   signKey,
		verifyKey: verifyKey,
		signer:    jwt.GetSigningMethod(alg),
		parser:    parser,
	}
}

// Verifier http middleware handler will verify a JWT string from a http request.
//
// Verifier will search for a JWT token in a http request, in the order:
//   1. 'jwt' URI query parameter
//   2. 'Authorization: BEARER T' request header
//   3. Cookie 'jwt' value
//
// The first JWT string that is found as a query parameter, authorization header
// or cookie header is then decoded by the `jwt-go` library and a *jwt.Token
// object is set on the request context. In the case of a signature decoding error
// the Verifier will also set the error on the request context.
//
// The Verifier always calls the next http handler in sequence, which can either
// be the generic `jwtauth.Authenticator` middleware or your own custom handler
// which checks the request context jwt token and error to prepare a custom
// http response.
func Verifier(ja *JwtAuth) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return Verify(ja, "")(next)
	}
}

func Verify(ja *JwtAuth, paramAliases ...string) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		hfn := func(w http.ResponseWriter, r *http.Request) {
			ctx := r.Context()
			token, err := VerifyRequest(ja, r, paramAliases...)
			ctx = NewContext(ctx, token, err)
			next.ServeHTTP(w, r.WithContext(ctx))
		}
		return http.HandlerFunc(hfn)
	}
}

func VerifyRequest(ja *JwtAuth, r *http.Request, paramAliases ...string) (*jwt.Token, error) {
	var tokenStr string
	var err error

	// Get token from query params
	tokenStr = r.URL.Query().Get("jwt")

	// Get token from other param aliases
	if tokenStr == "" && paramAliases != nil && len(paramAliases) > 0 {
		for _, p := range paramAliases {
			tokenStr = r.URL.Query().Get(p)
			if tokenStr != "" {
				break
			}
		}
	}

	// Get token from authorization header
	if tokenStr == "" {
		bearer := r.Header.Get("Authorization")
		if len(bearer) > 7 && strings.ToUpper(bearer[0:6]) == "BEARER" {
			tokenStr = bearer[7:]
		}
	}

	// Get token from cookie
	if tokenStr == "" {
		// TODO: paramAliases should apply to cookies too..
		cookie, err := r.Cookie("jwt")
		if err == nil {
			tokenStr = cookie.Value
		}
	}

	// TODO: what other kinds of validations should we do / error messages?

	// Verify the token
	token, err := ja.Decode(tokenStr)
	if err != nil {
		switch err.Error() {
		case "token is expired":
			err = ErrExpired
		}
		return token, err
	}

	if token == nil || !token.Valid || token.Method != ja.signer {
		err = ErrUnauthorized
		return token, err
	}

	// Check expiry via "exp" claim
	if IsExpired(token) {
		err = ErrExpired
		return token, err
	}

	// Valid!
	return token, nil
}

func (ja *JwtAuth) Encode(claims Claims) (t *jwt.Token, tokenString string, err error) {
	t = jwt.New(ja.signer)
	t.Claims = claims
	tokenString, err = t.SignedString(ja.signKey)
	t.Raw = tokenString
	return
}

func (ja *JwtAuth) Decode(tokenString string) (t *jwt.Token, err error) {
	// Decode the tokenString, but avoid using custom Claims via jwt-go's
	// ParseWithClaims as the jwt-go types will cause some glitches, so easier
	// to decode as MapClaims then wrap the underlying map[string]interface{}
	// to our Claims type
	t, err = ja.parser.Parse(tokenString, ja.keyFunc)
	if err != nil {
		return nil, err
	}
	return
}

func (ja *JwtAuth) keyFunc(t *jwt.Token) (interface{}, error) {
	if ja.verifyKey != nil {
		return ja.verifyKey, nil
	} else {
		return ja.signKey, nil
	}
}

// Authenticator is a default authentication middleware to enforce access from the
// Verifier middleware request context values. The Authenticator sends a 401 Unauthorized
// response for any unverified tokens and passes the good ones through. It's just fine
// until you decide to write something similar and customize your client response.
func Authenticator(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token, _, err := FromContext(r.Context())

		if err != nil {
			http.Error(w, http.StatusText(401), 401)
			return
		}

		if token == nil || !token.Valid {
			http.Error(w, http.StatusText(401), 401)
			return
		}

		// Token is authenticated, pass it through
		next.ServeHTTP(w, r)
	})
}

func NewContext(ctx context.Context, t *jwt.Token, err error) context.Context {
	ctx = context.WithValue(ctx, TokenCtxKey, t)
	ctx = context.WithValue(ctx, ErrorCtxKey, err)
	return ctx
}

func FromContext(ctx context.Context) (*jwt.Token, Claims, error) {
	token, _ := ctx.Value(TokenCtxKey).(*jwt.Token)

	var claims Claims
	if token != nil {
		tokenClaims, ok := token.Claims.(jwt.MapClaims)
		if !ok {
			panic("jwtauth: expecting jwt.MapClaims")
		}
		claims = Claims(tokenClaims)
	} else {
		claims = Claims{}
	}

	err, _ := ctx.Value(ErrorCtxKey).(error)

	return token, claims, err
}

func IsExpired(t *jwt.Token) bool {
	claims, ok := t.Claims.(jwt.MapClaims)
	if !ok {
		panic("jwtauth: expecting jwt.MapClaims")
	}

	if expv, ok := claims["exp"]; ok {
		var exp int64
		switch v := expv.(type) {
		case float64:
			exp = int64(v)
		case int64:
			exp = v
		case json.Number:
			exp, _ = v.Int64()
		default:
		}

		if exp < EpochNow() {
			return true
		}
	}

	return false
}

// Claims is a convenience type to manage a JWT claims hash.
type Claims map[string]interface{}

// NOTE: as of v3.0 of jwt-go, Valid() interface method is called to verify
// the claims. However, the current design we test these claims in the
// Verifier middleware, so we skip this step.
func (c Claims) Valid() error {
	return nil
}

func (c Claims) Set(k string, v interface{}) Claims {
	c[k] = v
	return c
}

func (c Claims) Get(k string) (interface{}, bool) {
	v, ok := c[k]
	return v, ok
}

// Set issued at ("iat") to specified time in the claims
func (c Claims) SetIssuedAt(tm time.Time) Claims {
	c["iat"] = tm.UTC().Unix()
	return c
}

// Set issued at ("iat") to present time in the claims
func (c Claims) SetIssuedNow() Claims {
	c["iat"] = EpochNow()
	return c
}

// Set expiry ("exp") in the claims and return itself so it can be chained
func (c Claims) SetExpiry(tm time.Time) Claims {
	c["exp"] = tm.UTC().Unix()
	return c
}

// Set expiry ("exp") in the claims to some duration from the present time
// and return itself so it can be chained
func (c Claims) SetExpiryIn(tm time.Duration) Claims {
	c["exp"] = ExpireIn(tm)
	return c
}

// Helper function that returns the NumericDate time value used by the spec
func EpochNow() int64 {
	return time.Now().UTC().Unix()
}

// Helper function to return calculated time in the future for "exp" claim.
func ExpireIn(tm time.Duration) int64 {
	return EpochNow() + int64(tm.Seconds())
}

// contextKey is a value for use with context.WithValue. It's used as
// a pointer so it fits in an interface{} without allocation. This technique
// for defining context keys was copied from Go 1.7's new use of context in net/http.
type contextKey struct {
	name string
}

func (k *contextKey) String() string {
	return "jwtauth context value " + k.name
}
<commit_msg>Implement custom search functions (#20)<commit_after>package jwtauth

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
)

var (
	TokenCtxKey = &contextKey{"Token"}
	ErrorCtxKey = &contextKey{"Error"}
)

var (
	ErrUnauthorized = errors.New("jwtauth: token is unauthorized")
	ErrExpired      = errors.New("jwtauth: token is expired")
)

var (
	// TokenFromCookie tries to retreive the token string from a cookie named
	// "jwt".
	TokenFromCookie = func(r *http.Request) string {
		cookie, err := r.Cookie("jwt")
		if err != nil {
			return ""
		}
		return cookie.Value
	}
	// TokenFromHeader tries to retreive the token string from the
	// "Authorization" reqeust header: "Authorization: BEARER T".
	TokenFromHeader = func(r *http.Request) string {
		// Get token from authorization header.
		bearer := r.Header.Get("Authorization")
		if len(bearer) > 7 && strings.ToUpper(bearer[0:6]) == "BEARER" {
			return bearer[7:]
		}
		return ""
	}
	// TokenFromQuery tries to retreive the token string from the "jwt" URI
	// query parameter.
	TokenFromQuery = func(r *http.Request) string {
		// Get token from query param named "jwt".
		return r.URL.Query().Get("jwt")
	}
)

type JwtAuth struct {
	signKey   interface{}
	verifyKey interface{}
	signer    jwt.SigningMethod
	parser    *jwt.Parser
}

// New creates a JwtAuth authenticator instance that provides middleware handlers
// and encoding/decoding functions for JWT signing.
func New(alg string, signKey interface{}, verifyKey interface{}) *JwtAuth {
	return NewWithParser(alg, &jwt.Parser{}, signKey, verifyKey)
}

// NewWithParser is the same as New, except it supports custom parser settings
// introduced in jwt-go/v2.4.0.
//
// We explicitly toggle `SkipClaimsValidation` in the `jwt-go` parser so that
// we can control when the claims are validated - in our case, by the Verifier
// http middleware handler.
func NewWithParser(alg string, parser *jwt.Parser, signKey interface{}, verifyKey interface{}) *JwtAuth {
	parser.SkipClaimsValidation = true
	return &JwtAuth{
		signKey:   signKey,
		verifyKey: verifyKey,
		signer:    jwt.GetSigningMethod(alg),
		parser:    parser,
	}
}

// Verifier http middleware handler will verify a JWT string from a http request.
//
// Verifier will search for a JWT token in a http request, in the order:
//   1. 'jwt' URI query parameter
//   2. 'Authorization: BEARER T' request header
//   3. Cookie 'jwt' value
//
// The first JWT string that is found as a query parameter, authorization header
// or cookie header is then decoded by the `jwt-go` library and a *jwt.Token
// object is set on the request context. In the case of a signature decoding error
// the Verifier will also set the error on the request context.
//
// The Verifier always calls the next http handler in sequence, which can either
// be the generic `jwtauth.Authenticator` middleware or your own custom handler
// which checks the request context jwt token and error to prepare a custom
// http response.
func Verifier(ja *JwtAuth) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return Verify(ja, TokenFromQuery, TokenFromHeader, TokenFromCookie)(next)
	}
}

func Verify(ja *JwtAuth, findTokenFns ...func(r *http.Request) string) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		hfn := func(w http.ResponseWriter, r *http.Request) {
			ctx := r.Context()
			token, err := VerifyRequest(ja, r, findTokenFns...)
			ctx = NewContext(ctx, token, err)
			next.ServeHTTP(w, r.WithContext(ctx))
		}
		return http.HandlerFunc(hfn)
	}
}

func VerifyRequest(ja *JwtAuth, r *http.Request, findTokenFns ...func(r *http.Request) string) (*jwt.Token, error) {
	var tokenStr string
	var err error

	// Extract token string from the request by calling token find functions in
	// the order they where provided. Further extraction stops if a function
	// returns a non-empty string.
	for _, fn := range findTokenFns {
		tokenStr = fn(r)
		if tokenStr != "" {
			break
		}
	}

	// TODO: what other kinds of validations should we do / error messages?

	// Verify the token
	token, err := ja.Decode(tokenStr)
	if err != nil {
		switch err.Error() {
		case "token is expired":
			err = ErrExpired
		}
		return token, err
	}

	if token == nil || !token.Valid || token.Method != ja.signer {
		err = ErrUnauthorized
		return token, err
	}

	// Check expiry via "exp" claim
	if IsExpired(token) {
		err = ErrExpired
		return token, err
	}

	// Valid!
	return token, nil
}

func (ja *JwtAuth) Encode(claims Claims) (t *jwt.Token, tokenString string, err error) {
	t = jwt.New(ja.signer)
	t.Claims = claims
	tokenString, err = t.SignedString(ja.signKey)
	t.Raw = tokenString
	return
}

func (ja *JwtAuth) Decode(tokenString string) (t *jwt.Token, err error) {
	// Decode the tokenString, but avoid using custom Claims via jwt-go's
	// ParseWithClaims as the jwt-go types will cause some glitches, so easier
	// to decode as MapClaims then wrap the underlying map[string]interface{}
	// to our Claims type
	t, err = ja.parser.Parse(tokenString, ja.keyFunc)
	if err != nil {
		return nil, err
	}
	return
}

func (ja *JwtAuth) keyFunc(t *jwt.Token) (interface{}, error) {
	if ja.verifyKey != nil {
		return ja.verifyKey, nil
	} else {
		return ja.signKey, nil
	}
}

// Authenticator is a default authentication middleware to enforce access from the
// Verifier middleware request context values. The Authenticator sends a 401 Unauthorized
// response for any unverified tokens and passes the good ones through. It's just fine
// until you decide to write something similar and customize your client response.
func Authenticator(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token, _, err := FromContext(r.Context())

		if err != nil {
			http.Error(w, http.StatusText(401), 401)
			return
		}

		if token == nil || !token.Valid {
			http.Error(w, http.StatusText(401), 401)
			return
		}

		// Token is authenticated, pass it through
		next.ServeHTTP(w, r)
	})
}

func NewContext(ctx context.Context, t *jwt.Token, err error) context.Context {
	ctx = context.WithValue(ctx, TokenCtxKey, t)
	ctx = context.WithValue(ctx, ErrorCtxKey, err)
	return ctx
}

func FromContext(ctx context.Context) (*jwt.Token, Claims, error) {
	token, _ := ctx.Value(TokenCtxKey).(*jwt.Token)

	var claims Claims
	if token != nil {
		tokenClaims, ok := token.Claims.(jwt.MapClaims)
		if !ok {
			panic("jwtauth: expecting jwt.MapClaims")
		}
		claims = Claims(tokenClaims)
	} else {
		claims = Claims{}
	}

	err, _ := ctx.Value(ErrorCtxKey).(error)

	return token, claims, err
}

func IsExpired(t *jwt.Token) bool {
	claims, ok := t.Claims.(jwt.MapClaims)
	if !ok {
		panic("jwtauth: expecting jwt.MapClaims")
	}

	if expv, ok := claims["exp"]; ok {
		var exp int64
		switch v := expv.(type) {
		case float64:
			exp = int64(v)
		case int64:
			exp = v
		case json.Number:
			exp, _ = v.Int64()
		default:
		}

		if exp < EpochNow() {
			return true
		}
	}

	return false
}

// Claims is a convenience type to manage a JWT claims hash.
type Claims map[string]interface{}

// NOTE: as of v3.0 of jwt-go, Valid() interface method is called to verify
// the claims. However, the current design we test these claims in the
// Verifier middleware, so we skip this step.
func (c Claims) Valid() error {
	return nil
}

func (c Claims) Set(k string, v interface{}) Claims {
	c[k] = v
	return c
}

func (c Claims) Get(k string) (interface{}, bool) {
	v, ok := c[k]
	return v, ok
}

// Set issued at ("iat") to specified time in the claims
func (c Claims) SetIssuedAt(tm time.Time) Claims {
	c["iat"] = tm.UTC().Unix()
	return c
}

// Set issued at ("iat") to present time in the claims
func (c Claims) SetIssuedNow() Claims {
	c["iat"] = EpochNow()
	return c
}

// Set expiry ("exp") in the claims and return itself so it can be chained
func (c Claims) SetExpiry(tm time.Time) Claims {
	c["exp"] = tm.UTC().Unix()
	return c
}

// Set expiry ("exp") in the claims to some duration from the present time
// and return itself so it can be chained
func (c Claims) SetExpiryIn(tm time.Duration) Claims {
	c["exp"] = ExpireIn(tm)
	return c
}

// Helper function that returns the NumericDate time value used by the spec
func EpochNow() int64 {
	return time.Now().UTC().Unix()
}

// Helper function to return calculated time in the future for "exp" claim.
func ExpireIn(tm time.Duration) int64 {
	return EpochNow() + int64(tm.Seconds())
}

// contextKey is a value for use with context.WithValue. It's used as
// a pointer so it fits in an interface{} without allocation. This technique
// for defining context keys was copied from Go 1.7's new use of context in net/http.
type contextKey struct {
	name string
}

func (k *contextKey) String() string {
	return "jwtauth context value " + k.name
}
<|endoftext|>


<commit_before>package main

import (
	"runtime"
	"time"

	"gopkg.in/vbauerster/mpb.v2"
)

func createFileWorkers(fileCount int) (chan string, chan error) {
	workerCount := runtime.NumCPU() / 2

	if workerCount < 1 {
		workerCount = 1
	} else if workerCount > fileCount {
		workerCount = fileCount
	}

	jobs := make(chan string, workerCount)
	results := make(chan error, workerCount)

	p := mpb.New().RefreshRate(100 * time.Millisecond)

	var fileCounter int

	// Start workers
	for w := 1; w <= workerCount; w++ {
		go func(jobs <-chan string, results chan<- error) {
			for j := range jobs {
				fileCounter++

				results <- processFile(p, fileCounter, fileCount, j)
			}
			// not going to get any more jobs, remove worker and close result channel if it was the last worker
			workerCount--
			if workerCount < 1 {
				close(results)
				p.Stop()
			}
		}(jobs, results)
	}

	return jobs, results
}

func processFile(p *mpb.Progress, fileNum, fileCount int, file string) error {
	patch, err := parsePatchFile(file)
	if err != nil {
		return err
	}

	patch, err = translatePatch(p, fileNum, fileCount, patch)
	if err != nil {
		return err
	}

	err = writePatchFile(patch)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Add bar for all file progress<commit_after>package main

import (
	"runtime"
	"time"

	"gopkg.in/vbauerster/mpb.v2"
)

func createFileWorkers(fileCount int) (chan string, chan error) {
	workerCount := runtime.NumCPU() / 2

	if workerCount < 1 {
		workerCount = 1
	} else if workerCount > fileCount {
		workerCount = fileCount
	}

	jobs := make(chan string, workerCount)
	results := make(chan error, workerCount)

	p := mpb.New().RefreshRate(100 * time.Millisecond)

	var fileCounter int

	bar := p.AddBar(int64(fileCount)).
		PrependName("All file progress", 25, mpb.DwidthSync|mpb.DextraSpace).
		PrependCounters("%4s/%4s", 0, 10, mpb.DwidthSync|mpb.DextraSpace)

	// Start workers
	for w := 1; w <= workerCount; w++ {
		go func(jobs <-chan string, results chan<- error) {
			for j := range jobs {
				fileCounter++

				results <- processFile(p, fileCounter, fileCount, j)

				bar.Incr(1)
			}
			// not going to get any more jobs, remove worker and close result channel if it was the last worker
			workerCount--
			if workerCount < 1 {
				close(results)
				p.Stop()
			}
		}(jobs, results)
	}

	return jobs, results
}

func processFile(p *mpb.Progress, fileNum, fileCount int, file string) error {
	patch, err := parsePatchFile(file)
	if err != nil {
		return err
	}

	patch, err = translatePatch(p, fileNum, fileCount, patch)
	if err != nil {
		return err
	}

	err = writePatchFile(patch)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

// TODO: could use the wowhead item xml api.
// e.g. http://www.wowhead.com/item=113939&xml
// does not work for spells or itemsets...

import (
	"bytes"
	"errors"
	"github.com/PuerkitoBio/goquery"
	"github.com/gosexy/to"
	"github.com/robertkrimen/otto"
	"io/ioutil"
	"net/http"
	nurl "net/url"
	"regexp"

	"github.com/spf13/nitro"
)

var (
	itemRe          = regexp.MustCompile(`(?s)\<script type="text/javascript"\>//\<!\[CDATA\[.*?(g_items\.add.*?)//\]\]\>\</script\>`)
	wowheadNPCUrlRe = regexp.MustCompile(`wowhead.com/\??npc=(\d+)`)
	displayIdRe     = regexp.MustCompile(`displayId: (\d+)`)
	spellIdRe       = regexp.MustCompile(`<a href="(.*?)" class="q2">`)

	Timer *nitro.B
)

func init() {
	Timer = nitro.Initalize()
	nitro.AnalysisOn = false
}

const (
	// a helper that replaces some variables that wowhead would
	// have with my own objects. The main part is g_items where
	// I set the add function of that object to processItem, which
	// we will inject into the javascript using otto.Set().
	jscriptHelper = `
g_items = {add: processItem};
Summary = function(){};
`
)

func fixWowheadURL(u string) (string, error) {
	tmpurl, err := nurl.Parse(u)
	if err != nil {
		return "", err
	}

	if tmpurl.Host == "" {
		tmpurl.Host = "wowhead.com"
	}

	if tmpurl.Scheme == "" {
		tmpurl.Scheme = "http"
	}

	return tmpurl.String(), nil
}

// Parses the wowhead html and finds where it's adding items
// to the comparison list via g_items.add. I use otto and a
// little javascript helper and a javascript function to parse
// and interpret the javascript and find all of the item data I need.
// Example of the javascript we will parse:
//   g_items.add(22423, {name_enus:'Dreadnaught Bracers', quality:4,icon:'INV_Bracer_15',jsonequip:{...}});
func wowhead(options map[string]interface{}) (TMorphItems, error) {
	url := to.String(options["url"])

	Timer.Step("started")

	// if they just put a wowhead item url in, just output that item
	if matches := wowheadUrlRe.FindStringSubmatch(url); len(matches) > 0 {
		items, err := wowapi([]string{matches[1]})
		if err != nil {
			return nil, err
		}

		if len(items) > 0 {
			return items, nil
		}
	}

	Timer.Step("wowhead url match")

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

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	Timer.Step("grab doc")

	if match := itemRe.FindSubmatch(data); len(match) > 0 {
		o := otto.New()

		// We fill these in to make it work for wowhead transmog sets.
		dollarObj, _ := o.Object(`$ = {}`)
		dollarObj.Set("extend", func(call otto.FunctionCall) otto.Value {
			return otto.UndefinedValue()
		})
		o.Set("$", dollarObj)
		g_spellsObj, _ := o.Object(`g_spells = {}`)
		o.Set("g_spells", g_spellsObj)

		var tmorphItems TMorphItems
		seenMainHand := false
		// Our processItem function that gets called via the g_items.add() call
		// and the jscriptHelper script.
		o.Set("processItem", func(call otto.FunctionCall) otto.Value {
			// data we want is in the second argument
			v, _ := call.Argument(1).Export()
			// we're only interested in the jsonequip map
			datam := Map(v.(map[string]interface{})["jsonequip"])

			slot := int(to.Int64(datam["slot"]))
			id := int(to.Int64(datam["id"]))
			if v, ok := slotMap[slot]; ok {
				slot = v
			}

			if canDisplaySlot(slot) {
				// We're going to assume if someone has a list
				// that contains two main hands, they mean they want
				// it in their main hand and off hand.
				if slot == 16 {
					if seenMainHand {
						slot = 17
					}
					seenMainHand = true
				}

				tmorphItems = append(tmorphItems, &TMorphItem{
					Type: "item",
					Args: []int{slot, id},
				})
			}

			return otto.UndefinedValue()
		})

		// run the
		_, err = o.Run(jscriptHelper + string(match[1]))
		if err != nil {
			return nil, err
		}

		if len(tmorphItems) > 0 {
			return tmorphItems, nil
		}
	}

	Timer.Step("find items in wowhead page")

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(data))
	if err != nil {
		return nil, errors.New("Could not parse wowhead NPC html")
	}

	Timer.Step("make goquery doc")

	// we got an npc. find the display id
	if matches := wowheadNPCUrlRe.FindStringSubmatch(url); len(matches) > 0 {
		node := doc.Find(`a:contains("View in 3D")`)
		if node.Length() == 0 {
			return nil, errors.New(`Unable to find "View in 3D" on page.`)
		}

		onclick, ok := node.Attr("onclick")
		if !ok {
			return nil, errors.New(`Unable to find "onclick" handler for "View in 3D" link`)
		}

		matches := displayIdRe.FindStringSubmatch(onclick)
		if len(matches) <= 1 {
			return nil, errors.New(`Unable to find display ID`)
		}

		return TMorphItems{
			&TMorphItem{
				Type: "morph",
				Args: []int{int(to.Int64(matches[1]))},
			},
		}, nil
	}

	Timer.Step("find npc display id")

	// try to find a url in the effect that we can parse for a displayid.
	node := doc.Find(`th:contains("Effect")`)
	if node.Length() > 0 {
		node = node.Next().Find("a")
		if node.Length() > 0 {
			if urltext, ok := node.Attr("href"); ok {
				fixedurl, err := fixWowheadURL(urltext)
				if err == nil {
					// just rerun the function with the found url
					return wowhead(map[string]interface{}{
						"url": fixedurl,
					})
				}
			}
		}
	}

	Timer.Step("find effect")

	// look for a spell id in a tooltip that may have a spell effect we
	// need to parse
	if matches := spellIdRe.FindStringSubmatch(string(data)); len(matches) > 1 {
		fixedurl, err := fixWowheadURL(matches[1])
		if err == nil {
			// rerun the function with the url we found
			return wowhead(map[string]interface{}{
				"url": fixedurl,
			})
		}
	}

	Timer.Step("find spell id")

	return nil, errors.New(`Could not find anything to morph on that wowhead page.`)
}
<commit_msg>updated wowhead code to work with a change they made<commit_after>package main

// TODO: could use the wowhead item xml api.
// e.g. http://www.wowhead.com/item=113939&xml
// does not work for spells or itemsets...

import (
	"bytes"
	"errors"
	"github.com/PuerkitoBio/goquery"
	"github.com/gosexy/to"
	"github.com/robertkrimen/otto"
	"io/ioutil"
	"net/http"
	nurl "net/url"
	"regexp"

	"github.com/spf13/nitro"
)

var (
	itemRe          = regexp.MustCompile(`(?s)\<script type="text/javascript"\>//\<!\[CDATA\[.*?(g_items\.add.*?)//\]\]\>\</script\>`)
	wowheadNPCUrlRe = regexp.MustCompile(`wowhead.com/\??npc=(\d+)`)
	displayIdRe     = regexp.MustCompile(`displayId: (\d+)`)
	spellIdRe       = regexp.MustCompile(`<a href="(.*?)" class="q2">`)

	Timer *nitro.B
)

func init() {
	Timer = nitro.Initalize()
	nitro.AnalysisOn = false
}

const (
	// a helper that replaces some variables that wowhead would
	// have with my own objects. The main part is g_items where
	// I set the add function of that object to processItem, which
	// we will inject into the javascript using otto.Set().
	jscriptHelper = `
g_items = {add: processItem};
Summary = function(){};
`
)

func fixWowheadURL(u string) (string, error) {
	tmpurl, err := nurl.Parse(u)
	if err != nil {
		return "", err
	}

	if tmpurl.Host == "" {
		tmpurl.Host = "wowhead.com"
	}

	if tmpurl.Scheme == "" {
		tmpurl.Scheme = "http"
	}

	return tmpurl.String(), nil
}

// Parses the wowhead html and finds where it's adding items
// to the comparison list via g_items.add. I use otto and a
// little javascript helper and a javascript function to parse
// and interpret the javascript and find all of the item data I need.
// Example of the javascript we will parse:
//   g_items.add(22423, {name_enus:'Dreadnaught Bracers', quality:4,icon:'INV_Bracer_15',jsonequip:{...}});
func wowhead(options map[string]interface{}) (TMorphItems, error) {
	url := to.String(options["url"])

	Timer.Step("started")

	// if they just put a wowhead item url in, just output that item
	if matches := wowheadUrlRe.FindStringSubmatch(url); len(matches) > 0 {
		items, err := wowapi([]string{matches[1]})
		if err != nil {
			return nil, err
		}

		if len(items) > 0 {
			return items, nil
		}
	}

	Timer.Step("wowhead url match")

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

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	Timer.Step("grab doc")

	if match := itemRe.FindSubmatch(data); len(match) > 0 {
		o := otto.New()

		// We fill these in to make it work for wowhead transmog sets.
		dollarObj, _ := o.Object(`$ = {}`)
		dollarObj.Set("extend", func(call otto.FunctionCall) otto.Value {
			return otto.UndefinedValue()
		})
		o.Set("$", dollarObj)
		g_spellsObj, _ := o.Object(`g_spells = {}`)
		o.Set("g_spells", g_spellsObj)
		o.Set("ts_PopulateScreenshotDiv", func(call otto.FunctionCall) otto.Value {
		  return otto.UndefinedValue()
		})

		var tmorphItems TMorphItems
		seenMainHand := false
		// Our processItem function that gets called via the g_items.add() call
		// and the jscriptHelper script.
		o.Set("processItem", func(call otto.FunctionCall) otto.Value {
			// data we want is in the second argument
			v, _ := call.Argument(1).Export()
			// we're only interested in the jsonequip map
			datam := Map(v.(map[string]interface{})["jsonequip"])

			slot := int(to.Int64(datam["slot"]))
			id := int(to.Int64(datam["id"]))
			if v, ok := slotMap[slot]; ok {
				slot = v
			}

			if canDisplaySlot(slot) {
				// We're going to assume if someone has a list
				// that contains two main hands, they mean they want
				// it in their main hand and off hand.
				if slot == 16 {
					if seenMainHand {
						slot = 17
					}
					seenMainHand = true
				}

				tmorphItems = append(tmorphItems, &TMorphItem{
					Type: "item",
					Args: []int{slot, id},
				})
			}

			return otto.UndefinedValue()
		})

		// run the
		_, err = o.Run(jscriptHelper + string(match[1]))
		if err != nil {
			return nil, err
		}

		if len(tmorphItems) > 0 {
			return tmorphItems, nil
		}
	}

	Timer.Step("find items in wowhead page")

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(data))
	if err != nil {
		return nil, errors.New("Could not parse wowhead NPC html")
	}

	Timer.Step("make goquery doc")

	// we got an npc. find the display id
	if matches := wowheadNPCUrlRe.FindStringSubmatch(url); len(matches) > 0 {
		node := doc.Find(`a:contains("View in 3D")`)
		if node.Length() == 0 {
			return nil, errors.New(`Unable to find "View in 3D" on page.`)
		}

		onclick, ok := node.Attr("onclick")
		if !ok {
			return nil, errors.New(`Unable to find "onclick" handler for "View in 3D" link`)
		}

		matches := displayIdRe.FindStringSubmatch(onclick)
		if len(matches) <= 1 {
			return nil, errors.New(`Unable to find display ID`)
		}

		return TMorphItems{
			&TMorphItem{
				Type: "morph",
				Args: []int{int(to.Int64(matches[1]))},
			},
		}, nil
	}

	Timer.Step("find npc display id")

	// try to find a url in the effect that we can parse for a displayid.
	node := doc.Find(`th:contains("Effect")`)
	if node.Length() > 0 {
		node = node.Next().Find("a")
		if node.Length() > 0 {
			if urltext, ok := node.Attr("href"); ok {
				fixedurl, err := fixWowheadURL(urltext)
				if err == nil {
					// just rerun the function with the found url
					return wowhead(map[string]interface{}{
						"url": fixedurl,
					})
				}
			}
		}
	}

	Timer.Step("find effect")

	// look for a spell id in a tooltip that may have a spell effect we
	// need to parse
	if matches := spellIdRe.FindStringSubmatch(string(data)); len(matches) > 1 {
		fixedurl, err := fixWowheadURL(matches[1])
		if err == nil {
			// rerun the function with the url we found
			return wowhead(map[string]interface{}{
				"url": fixedurl,
			})
		}
	}

	Timer.Step("find spell id")

	return nil, errors.New(`Could not find anything to morph on that wowhead page.`)
}
<|endoftext|>


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

// Package zip enables you to transparently read or write ZIP compressed archives and the files inside them.
package zip

import (
	"archive/zip"
	"errors"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
)

// File represents a file in archive.
type File struct {
	*zip.FileHeader
	oldName    string
	oldComment string
	absPath    string
}

// ZipArchive represents a file archive, compressed with Zip.
type ZipArchive struct {
	*zip.ReadCloser
	FileName   string
	Comment    string
	NumFiles   int
	Flag       int
	Permission os.FileMode

	files        []*File
	isHasChanged bool

	// For supporting to flush to io.Writer.
	writer      io.Writer
	isHasWriter bool
}

// Create creates the named zip file, truncating
// it if it already exists. If successful, methods on the returned
// ZipArchive can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(fileName string) (zip *ZipArchive, err error) {
	os.MkdirAll(path.Dir(fileName), os.ModePerm)
	return OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
}

// Open opens the named zip file for reading.  If successful, methods on
// the returned ZipArchive can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(fileName string) (zip *ZipArchive, err error) {
	return OpenFile(fileName, os.O_RDONLY, 0)
}

// OpenFile is the generalized open call; most users will use Open
// instead. It opens the named zip file with specified flag
// (O_RDONLY etc.) if applicable. If successful,
// methods on the returned ZipArchive can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(fileName string, flag int, perm os.FileMode) (zip *ZipArchive, err error) {
	zip = &ZipArchive{}
	err = zip.Open(fileName, flag, perm)
	return zip, err
}

// New accepts a variable that implemented interface io.Writer
// for write-only purpose operations.
func New(w io.Writer) (zip *ZipArchive) {
	return &ZipArchive{
		writer:      w,
		isHasWriter: true,
	}
}

func hasPrefix(name string, prefixes []string) bool {
	for _, prefix := range prefixes {
		if strings.HasPrefix(name, prefix) {
			return true
		}
	}
	return false
}

// ListName returns a string slice of files' name in ZipArchive.
func (z *ZipArchive) ListName(prefixes ...string) []string {
	isHasPrefix := len(prefixes) > 0
	names := make([]string, 0, z.NumFiles)
	for _, f := range z.files {
		if isHasPrefix {
			if hasPrefix(f.Name, prefixes) {
				names = append(names, f.Name)
			}
			continue
		}
		names = append(names, f.Name)
	}
	return names
}

// AddEmptyDir adds a directory entry to ZipArchive,
// it returns false when directory already existed.
func (z *ZipArchive) AddEmptyDir(dirPath string) bool {
	if !strings.HasSuffix(dirPath, "/") {
		dirPath += "/"
	}

	for _, f := range z.files {
		if dirPath == f.Name {
			return false
		}
	}

	dirPath = strings.TrimSuffix(dirPath, "/")
	if strings.Contains(dirPath, "/") {
		// Auto add all upper level directory.
		tmpPath := path.Dir(dirPath)
		z.AddEmptyDir(tmpPath)
	}
	z.files = append(z.files, &File{
		FileHeader: &zip.FileHeader{
			Name:             dirPath + "/",
			UncompressedSize: 0,
		},
	})
	z.updateStat()
	return true
}

// AddFile adds a directory and subdirectories entries to ZipArchive,
func (z *ZipArchive) AddDir(dirPath, absPath string) error {
	dir, err := os.Open(absPath)
	if err != nil {
		return err
	}
	defer dir.Close()

	z.AddEmptyDir(dirPath)

	// Get file info slice
	fis, err := dir.Readdir(0)
	if err != nil {
		return err
	}

	for _, fi := range fis {
		curPath := strings.Replace(absPath+"/"+fi.Name(), "\\", "/", -1)
		tmpRecPath := strings.Replace(filepath.Join(dirPath, fi.Name()), "\\", "/", -1)
		if fi.IsDir() {
			err = z.AddDir(tmpRecPath, curPath)
			if err != nil {
				return err
			}
		} else {
			err = z.AddFile(tmpRecPath, curPath)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// AddFile adds a file entry to ZipArchive,
func (z *ZipArchive) AddFile(fileName, absPath string) error {
	if globalFilter(absPath) {
		return nil
	}

	f, err := os.Open(absPath)
	if err != nil {
		return err
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		return err
	}

	file := new(File)
	file.FileHeader, err = zip.FileInfoHeader(fi)
	if err != nil {
		return err
	}
	file.Name = fileName
	file.absPath = absPath

	z.AddEmptyDir(path.Dir(fileName))

	isExist := false
	for _, f := range z.files {
		if fileName == f.Name {
			f = file
			isExist = true
			break
		}
	}

	if !isExist {
		z.files = append(z.files, file)
	}
	z.updateStat()
	return nil
}

// DeleteIndex deletes an entry in the archive using its index.
func (z *ZipArchive) DeleteIndex(index int) error {
	if index >= z.NumFiles {
		return errors.New("index out of range of number of files")
	}

	z.files = append(z.files[:index], z.files[index+1:]...)
	return nil
}

// DeleteName deletes an entry in the archive using its name.
func (z *ZipArchive) DeleteName(name string) error {
	for i, f := range z.files {
		if f.Name == name {
			return z.DeleteIndex(i)
		}
	}
	return errors.New("entry with given name not found")
}

func (z *ZipArchive) updateStat() {
	z.NumFiles = len(z.files)
	z.isHasChanged = true
}

// copy copies file from source to target path.
// It returns false and error when error occurs in underlying functions.
func copy(destPath, srcPath string) error {
	sf, err := os.Open(srcPath)
	if err != nil {
		return err
	}
	defer sf.Close()

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

	// buffer reader, do chunk transfer
	buf := make([]byte, 1024)
	for {
		// read a chunk
		n, err := sf.Read(buf)
		if err != nil && err != io.EOF {
			return err
		}
		if n == 0 {
			break
		}
		// write a chunk
		if _, err := df.Write(buf[:n]); err != nil {
			return err
		}
	}
	return nil
}

func globalFilter(name string) bool {
	if strings.Contains(name, ".DS_Store") {
		return true
	}
	return false
}
<commit_msg>Add permission set in function copy<commit_after>// Copyright 2013 cae authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Package zip enables you to transparently read or write ZIP compressed archives and the files inside them.
package zip

import (
	"archive/zip"
	"errors"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
)

// File represents a file in archive.
type File struct {
	*zip.FileHeader
	oldName    string
	oldComment string
	absPath    string
}

// ZipArchive represents a file archive, compressed with Zip.
type ZipArchive struct {
	*zip.ReadCloser
	FileName   string
	Comment    string
	NumFiles   int
	Flag       int
	Permission os.FileMode

	files        []*File
	isHasChanged bool

	// For supporting to flush to io.Writer.
	writer      io.Writer
	isHasWriter bool
}

// Create creates the named zip file, truncating
// it if it already exists. If successful, methods on the returned
// ZipArchive can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(fileName string) (zip *ZipArchive, err error) {
	os.MkdirAll(path.Dir(fileName), os.ModePerm)
	return OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
}

// Open opens the named zip file for reading.  If successful, methods on
// the returned ZipArchive can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func Open(fileName string) (zip *ZipArchive, err error) {
	return OpenFile(fileName, os.O_RDONLY, 0)
}

// OpenFile is the generalized open call; most users will use Open
// instead. It opens the named zip file with specified flag
// (O_RDONLY etc.) if applicable. If successful,
// methods on the returned ZipArchive can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(fileName string, flag int, perm os.FileMode) (zip *ZipArchive, err error) {
	zip = &ZipArchive{}
	err = zip.Open(fileName, flag, perm)
	return zip, err
}

// New accepts a variable that implemented interface io.Writer
// for write-only purpose operations.
func New(w io.Writer) (zip *ZipArchive) {
	return &ZipArchive{
		writer:      w,
		isHasWriter: true,
	}
}

func hasPrefix(name string, prefixes []string) bool {
	for _, prefix := range prefixes {
		if strings.HasPrefix(name, prefix) {
			return true
		}
	}
	return false
}

// ListName returns a string slice of files' name in ZipArchive.
func (z *ZipArchive) ListName(prefixes ...string) []string {
	isHasPrefix := len(prefixes) > 0
	names := make([]string, 0, z.NumFiles)
	for _, f := range z.files {
		if isHasPrefix {
			if hasPrefix(f.Name, prefixes) {
				names = append(names, f.Name)
			}
			continue
		}
		names = append(names, f.Name)
	}
	return names
}

// AddEmptyDir adds a directory entry to ZipArchive,
// it returns false when directory already existed.
func (z *ZipArchive) AddEmptyDir(dirPath string) bool {
	if !strings.HasSuffix(dirPath, "/") {
		dirPath += "/"
	}

	for _, f := range z.files {
		if dirPath == f.Name {
			return false
		}
	}

	dirPath = strings.TrimSuffix(dirPath, "/")
	if strings.Contains(dirPath, "/") {
		// Auto add all upper level directory.
		tmpPath := path.Dir(dirPath)
		z.AddEmptyDir(tmpPath)
	}
	z.files = append(z.files, &File{
		FileHeader: &zip.FileHeader{
			Name:             dirPath + "/",
			UncompressedSize: 0,
		},
	})
	z.updateStat()
	return true
}

// AddFile adds a directory and subdirectories entries to ZipArchive,
func (z *ZipArchive) AddDir(dirPath, absPath string) error {
	dir, err := os.Open(absPath)
	if err != nil {
		return err
	}
	defer dir.Close()

	z.AddEmptyDir(dirPath)

	// Get file info slice
	fis, err := dir.Readdir(0)
	if err != nil {
		return err
	}

	for _, fi := range fis {
		curPath := strings.Replace(absPath+"/"+fi.Name(), "\\", "/", -1)
		tmpRecPath := strings.Replace(filepath.Join(dirPath, fi.Name()), "\\", "/", -1)
		if fi.IsDir() {
			err = z.AddDir(tmpRecPath, curPath)
			if err != nil {
				return err
			}
		} else {
			err = z.AddFile(tmpRecPath, curPath)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// AddFile adds a file entry to ZipArchive,
func (z *ZipArchive) AddFile(fileName, absPath string) error {
	if globalFilter(absPath) {
		return nil
	}

	f, err := os.Open(absPath)
	if err != nil {
		return err
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		return err
	}

	file := new(File)
	file.FileHeader, err = zip.FileInfoHeader(fi)
	if err != nil {
		return err
	}
	file.Name = fileName
	file.absPath = absPath

	z.AddEmptyDir(path.Dir(fileName))

	isExist := false
	for _, f := range z.files {
		if fileName == f.Name {
			f = file
			isExist = true
			break
		}
	}

	if !isExist {
		z.files = append(z.files, file)
	}
	z.updateStat()
	return nil
}

// DeleteIndex deletes an entry in the archive using its index.
func (z *ZipArchive) DeleteIndex(index int) error {
	if index >= z.NumFiles {
		return errors.New("index out of range of number of files")
	}

	z.files = append(z.files[:index], z.files[index+1:]...)
	return nil
}

// DeleteName deletes an entry in the archive using its name.
func (z *ZipArchive) DeleteName(name string) error {
	for i, f := range z.files {
		if f.Name == name {
			return z.DeleteIndex(i)
		}
	}
	return errors.New("entry with given name not found")
}

func (z *ZipArchive) updateStat() {
	z.NumFiles = len(z.files)
	z.isHasChanged = true
}

// copy copies file from source to target path.
// It returns false and error when error occurs in underlying functions.
func copy(destPath, srcPath string) error {
	sf, err := os.Open(srcPath)
	if err != nil {
		return err
	}
	defer sf.Close()

	si, err := sf.Stat()
	if err != nil {
		return err
	}

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

	// buffer reader, do chunk transfer
	buf := make([]byte, 1024)
	for {
		// read a chunk
		n, err := sf.Read(buf)
		if err != nil && err != io.EOF {
			return err
		}
		if n == 0 {
			break
		}
		// write a chunk
		if _, err := df.Write(buf[:n]); err != nil {
			return err
		}
	}

	return os.Chmod(destPath, si.Mode())
}

func globalFilter(name string) bool {
	if strings.Contains(name, ".DS_Store") {
		return true
	}
	return false
}
<|endoftext|>