<commit_before>package archive

import (
	"archive/tar"
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/lxd/apparmor"
	"github.com/lxc/lxd/lxd/sys"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/ioprogress"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/subprocess"
)

type nullWriteCloser struct {
	*bytes.Buffer
}

func (nwc *nullWriteCloser) Close() error {
	return nil
}

// ExtractWithFds runs extractor process under specifc AppArmor profile.
// The allowedCmds argument specify commands which are allowed to run by apparmor.
// The cmd argument is automatically added to allowedCmds slice.
func ExtractWithFds(cmd string, args []string, allowedCmds []string, stdin io.ReadCloser, sysOS *sys.OS, output *os.File) error {
	outputPath := output.Name()

	allowedCmds = append(allowedCmds, cmd)
	allowedCmdPaths := []string{}
	for _, c := range allowedCmds {
		cmdPath, err := exec.LookPath(c)
		if err != nil {
			return fmt.Errorf("Failed to start extract: Failed to find executable: %w", err)
		}
		allowedCmdPaths = append(allowedCmdPaths, cmdPath)
	}

	err := apparmor.ArchiveLoad(sysOS, outputPath, allowedCmdPaths)
	if err != nil {
		return fmt.Errorf("Failed to start extract: Failed to load profile: %w", err)
	}
	defer func() { _ = apparmor.ArchiveDelete(sysOS, outputPath) }()
	defer func() { _ = apparmor.ArchiveUnload(sysOS, outputPath) }()

	var buffer bytes.Buffer
	p, err := subprocess.NewProcessWithFds(cmd, args, stdin, output, &nullWriteCloser{&buffer})
	if err != nil {
		return fmt.Errorf("Failed to start extract: Failed to creating subprocess: %w", err)
	}

	p.SetApparmor(apparmor.ArchiveProfileName(outputPath))

	err = p.Start()
	if err != nil {
		return fmt.Errorf("Failed to start extract: Failed running: tar: %w", err)
	}

	_, err = p.Wait(context.Background())
	if err != nil {
		return shared.RunError{
			Msg:    fmt.Sprintf("Failed to run: %s %s: %s", cmd, strings.Join(args, " "), strings.TrimSpace(buffer.String())),
			Stderr: buffer.String(),
			Err:    err,
		}
	}

	return nil
}

// CompressedTarReader returns a tar reader from the supplied (optionally compressed) tarball stream.
// The unpacker arguments are those returned by DetectCompressionFile().
// The returned cancelFunc should be called when finished with reader to clean up any resources used.
// This can be done before reading to the end of the tarball if desired.
func CompressedTarReader(ctx context.Context, r io.ReadSeeker, unpacker []string, sysOS *sys.OS, outputPath string) (*tar.Reader, context.CancelFunc, error) {
	ctx, cancelFunc := context.WithCancel(ctx)

	_, err := r.Seek(0, 0)
	if err != nil {
		return nil, cancelFunc, err
	}

	var tr *tar.Reader

	if len(unpacker) > 0 {
		cmdPath, err := exec.LookPath(unpacker[0])
		if err != nil {
			return nil, cancelFunc, fmt.Errorf("Failed to start unpack: Failed to find executable: %w", err)
		}

		err = apparmor.ArchiveLoad(sysOS, outputPath, []string{cmdPath})
		if err != nil {
			return nil, cancelFunc, fmt.Errorf("Failed to start unpack: Failed to load profile: %w", err)
		}

		pipeReader, pipeWriter := io.Pipe()
		p, err := subprocess.NewProcessWithFds(unpacker[0], unpacker[1:], ioutil.NopCloser(r), pipeWriter, nil)
		if err != nil {
			return nil, cancelFunc, fmt.Errorf("Failed to start unpack: Failed to creating subprocess: %w", err)
		}

		p.SetApparmor(apparmor.ArchiveProfileName(outputPath))
		err = p.Start()
		if err != nil {
			return nil, cancelFunc, fmt.Errorf("Failed to start unpack: Failed running: %s: %w", unpacker[0], err)
		}

		ctxCancelFunc := cancelFunc

		// Now that unpacker process has started, wrap context cancel function with one that waits for
		// the unpacker process to complete.
		cancelFunc = func() {
			ctxCancelFunc()
			_ = pipeWriter.Close()
			_, _ = p.Wait(ctx)
			_ = apparmor.ArchiveUnload(sysOS, outputPath)
			_ = apparmor.ArchiveDelete(sysOS, outputPath)
		}

		tr = tar.NewReader(pipeReader)
	} else {
		tr = tar.NewReader(r)
	}

	return tr, cancelFunc, nil
}

// Unpack extracts image from archive.
func Unpack(file string, path string, blockBackend bool, sysOS *sys.OS, tracker *ioprogress.ProgressTracker) error {
	extractArgs, extension, unpacker, err := shared.DetectCompression(file)
	if err != nil {
		return err
	}

	command := ""
	args := []string{}
	var allowedCmds []string
	var reader io.Reader
	if strings.HasPrefix(extension, ".tar") {
		command = "tar"
		if sysOS.RunningInUserNS {
			// We can't create char/block devices so avoid extracting them.
			args = append(args, "--wildcards")
			args = append(args, "--exclude=dev/*")
			args = append(args, "--exclude=./dev/*")
			args = append(args, "--exclude=rootfs/dev/*")
			args = append(args, "--exclude=rootfs/./dev/*")
		}
		args = append(args, "--restrict", "--force-local")
		args = append(args, "-C", path, "--numeric-owner", "--xattrs-include=*")
		args = append(args, extractArgs...)
		args = append(args, "-")

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

		reader = f

		// Attach the ProgressTracker if supplied.
		if tracker != nil {
			fsinfo, err := f.Stat()
			if err != nil {
				return err
			}

			tracker.Length = fsinfo.Size()
			reader = &ioprogress.ProgressReader{
				ReadCloser: f,
				Tracker:    tracker,
			}
		}

		// Allow supplementary commands for the unpacker to use.
		if len(unpacker) > 0 {
			allowedCmds = append(allowedCmds, unpacker[0])
		}
	} else if strings.HasPrefix(extension, ".squashfs") {
		// unsquashfs does not support reading from stdin,
		// so ProgressTracker is not possible.
		command = "unsquashfs"
		args = append(args, "-f", "-d", path, "-n")

		// Limit unsquashfs chunk size to 10% of memory and up to 256MB (default)
		// When running on a low memory system, also disable multi-processing
		mem, err := shared.DeviceTotalMemory()
		mem = mem / 1024 / 1024 / 10
		if err == nil && mem < 256 {
			args = append(args, "-da", fmt.Sprintf("%d", mem), "-fr", fmt.Sprintf("%d", mem), "-p", "1")
		}

		args = append(args, file)
	} else {
		return fmt.Errorf("Unsupported image format: %s", extension)
	}

	outputDir, err := os.OpenFile(path, os.O_RDONLY, 0)
	if err != nil {
		return fmt.Errorf("Error opening directory: %w", err)
	}
	defer func() { _ = outputDir.Close() }()

	var readCloser io.ReadCloser
	if reader != nil {
		readCloser = ioutil.NopCloser(reader)
	}

	err = ExtractWithFds(command, args, allowedCmds, readCloser, sysOS, outputDir)
	if err != nil {
		// We can't create char/block devices in unpriv containers so ignore related errors.
		if sysOS.RunningInUserNS && command == "unsquashfs" {
			runError, ok := err.(shared.RunError)
			if !ok || runError.Stderr == "" {
				return err
			}

			// Confirm that all errors are related to character or block devices.
			found := false
			for _, line := range strings.Split(runError.Stderr, "\n") {
				line = strings.TrimSpace(line)
				if line == "" {
					continue
				}

				if !strings.Contains(line, "failed to create block device") {
					continue
				}

				if !strings.Contains(line, "failed to create character device") {
					continue
				}

				// We found an actual error.
				found = true
			}

			if !found {
				// All good, assume everything unpacked.
				return nil
			}
		}

		// Check if we ran out of space
		fs := unix.Statfs_t{}

		err1 := unix.Statfs(path, &fs)
		if err1 != nil {
			return err1
		}

		// Check if we're running out of space
		if int64(fs.Bfree) < 10 {
			if blockBackend {
				return fmt.Errorf("Unable to unpack image, run out of disk space (consider increasing your pool's volume.size)")
			}

			return fmt.Errorf("Unable to unpack image, run out of disk space")
		}

		logger.Warn("Unpack failed", logger.Ctx{"file": file, "allowedCmds": allowedCmds, "extension": extension, "path": path, "err": err})
		return fmt.Errorf("Unpack failed: %w", err)
	}

	return nil
}
<commit_msg>lxd/archive: Inserts newlines after blocks.<commit_after>package archive

import (
	"archive/tar"
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/lxd/apparmor"
	"github.com/lxc/lxd/lxd/sys"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/ioprogress"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/subprocess"
)

type nullWriteCloser struct {
	*bytes.Buffer
}

func (nwc *nullWriteCloser) Close() error {
	return nil
}

// ExtractWithFds runs extractor process under specifc AppArmor profile.
// The allowedCmds argument specify commands which are allowed to run by apparmor.
// The cmd argument is automatically added to allowedCmds slice.
func ExtractWithFds(cmd string, args []string, allowedCmds []string, stdin io.ReadCloser, sysOS *sys.OS, output *os.File) error {
	outputPath := output.Name()

	allowedCmds = append(allowedCmds, cmd)
	allowedCmdPaths := []string{}
	for _, c := range allowedCmds {
		cmdPath, err := exec.LookPath(c)
		if err != nil {
			return fmt.Errorf("Failed to start extract: Failed to find executable: %w", err)
		}

		allowedCmdPaths = append(allowedCmdPaths, cmdPath)
	}

	err := apparmor.ArchiveLoad(sysOS, outputPath, allowedCmdPaths)
	if err != nil {
		return fmt.Errorf("Failed to start extract: Failed to load profile: %w", err)
	}

	defer func() { _ = apparmor.ArchiveDelete(sysOS, outputPath) }()
	defer func() { _ = apparmor.ArchiveUnload(sysOS, outputPath) }()

	var buffer bytes.Buffer
	p, err := subprocess.NewProcessWithFds(cmd, args, stdin, output, &nullWriteCloser{&buffer})
	if err != nil {
		return fmt.Errorf("Failed to start extract: Failed to creating subprocess: %w", err)
	}

	p.SetApparmor(apparmor.ArchiveProfileName(outputPath))

	err = p.Start()
	if err != nil {
		return fmt.Errorf("Failed to start extract: Failed running: tar: %w", err)
	}

	_, err = p.Wait(context.Background())
	if err != nil {
		return shared.RunError{
			Msg:    fmt.Sprintf("Failed to run: %s %s: %s", cmd, strings.Join(args, " "), strings.TrimSpace(buffer.String())),
			Stderr: buffer.String(),
			Err:    err,
		}
	}

	return nil
}

// CompressedTarReader returns a tar reader from the supplied (optionally compressed) tarball stream.
// The unpacker arguments are those returned by DetectCompressionFile().
// The returned cancelFunc should be called when finished with reader to clean up any resources used.
// This can be done before reading to the end of the tarball if desired.
func CompressedTarReader(ctx context.Context, r io.ReadSeeker, unpacker []string, sysOS *sys.OS, outputPath string) (*tar.Reader, context.CancelFunc, error) {
	ctx, cancelFunc := context.WithCancel(ctx)

	_, err := r.Seek(0, 0)
	if err != nil {
		return nil, cancelFunc, err
	}

	var tr *tar.Reader

	if len(unpacker) > 0 {
		cmdPath, err := exec.LookPath(unpacker[0])
		if err != nil {
			return nil, cancelFunc, fmt.Errorf("Failed to start unpack: Failed to find executable: %w", err)
		}

		err = apparmor.ArchiveLoad(sysOS, outputPath, []string{cmdPath})
		if err != nil {
			return nil, cancelFunc, fmt.Errorf("Failed to start unpack: Failed to load profile: %w", err)
		}

		pipeReader, pipeWriter := io.Pipe()
		p, err := subprocess.NewProcessWithFds(unpacker[0], unpacker[1:], ioutil.NopCloser(r), pipeWriter, nil)
		if err != nil {
			return nil, cancelFunc, fmt.Errorf("Failed to start unpack: Failed to creating subprocess: %w", err)
		}

		p.SetApparmor(apparmor.ArchiveProfileName(outputPath))
		err = p.Start()
		if err != nil {
			return nil, cancelFunc, fmt.Errorf("Failed to start unpack: Failed running: %s: %w", unpacker[0], err)
		}

		ctxCancelFunc := cancelFunc

		// Now that unpacker process has started, wrap context cancel function with one that waits for
		// the unpacker process to complete.
		cancelFunc = func() {
			ctxCancelFunc()
			_ = pipeWriter.Close()
			_, _ = p.Wait(ctx)
			_ = apparmor.ArchiveUnload(sysOS, outputPath)
			_ = apparmor.ArchiveDelete(sysOS, outputPath)
		}

		tr = tar.NewReader(pipeReader)
	} else {
		tr = tar.NewReader(r)
	}

	return tr, cancelFunc, nil
}

// Unpack extracts image from archive.
func Unpack(file string, path string, blockBackend bool, sysOS *sys.OS, tracker *ioprogress.ProgressTracker) error {
	extractArgs, extension, unpacker, err := shared.DetectCompression(file)
	if err != nil {
		return err
	}

	command := ""
	args := []string{}
	var allowedCmds []string
	var reader io.Reader
	if strings.HasPrefix(extension, ".tar") {
		command = "tar"
		if sysOS.RunningInUserNS {
			// We can't create char/block devices so avoid extracting them.
			args = append(args, "--wildcards")
			args = append(args, "--exclude=dev/*")
			args = append(args, "--exclude=./dev/*")
			args = append(args, "--exclude=rootfs/dev/*")
			args = append(args, "--exclude=rootfs/./dev/*")
		}

		args = append(args, "--restrict", "--force-local")
		args = append(args, "-C", path, "--numeric-owner", "--xattrs-include=*")
		args = append(args, extractArgs...)
		args = append(args, "-")

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

		defer func() { _ = f.Close() }()

		reader = f

		// Attach the ProgressTracker if supplied.
		if tracker != nil {
			fsinfo, err := f.Stat()
			if err != nil {
				return err
			}

			tracker.Length = fsinfo.Size()
			reader = &ioprogress.ProgressReader{
				ReadCloser: f,
				Tracker:    tracker,
			}
		}

		// Allow supplementary commands for the unpacker to use.
		if len(unpacker) > 0 {
			allowedCmds = append(allowedCmds, unpacker[0])
		}
	} else if strings.HasPrefix(extension, ".squashfs") {
		// unsquashfs does not support reading from stdin,
		// so ProgressTracker is not possible.
		command = "unsquashfs"
		args = append(args, "-f", "-d", path, "-n")

		// Limit unsquashfs chunk size to 10% of memory and up to 256MB (default)
		// When running on a low memory system, also disable multi-processing
		mem, err := shared.DeviceTotalMemory()
		mem = mem / 1024 / 1024 / 10
		if err == nil && mem < 256 {
			args = append(args, "-da", fmt.Sprintf("%d", mem), "-fr", fmt.Sprintf("%d", mem), "-p", "1")
		}

		args = append(args, file)
	} else {
		return fmt.Errorf("Unsupported image format: %s", extension)
	}

	outputDir, err := os.OpenFile(path, os.O_RDONLY, 0)
	if err != nil {
		return fmt.Errorf("Error opening directory: %w", err)
	}

	defer func() { _ = outputDir.Close() }()

	var readCloser io.ReadCloser
	if reader != nil {
		readCloser = ioutil.NopCloser(reader)
	}

	err = ExtractWithFds(command, args, allowedCmds, readCloser, sysOS, outputDir)
	if err != nil {
		// We can't create char/block devices in unpriv containers so ignore related errors.
		if sysOS.RunningInUserNS && command == "unsquashfs" {
			runError, ok := err.(shared.RunError)
			if !ok || runError.Stderr == "" {
				return err
			}

			// Confirm that all errors are related to character or block devices.
			found := false
			for _, line := range strings.Split(runError.Stderr, "\n") {
				line = strings.TrimSpace(line)
				if line == "" {
					continue
				}

				if !strings.Contains(line, "failed to create block device") {
					continue
				}

				if !strings.Contains(line, "failed to create character device") {
					continue
				}

				// We found an actual error.
				found = true
			}

			if !found {
				// All good, assume everything unpacked.
				return nil
			}
		}

		// Check if we ran out of space
		fs := unix.Statfs_t{}

		err1 := unix.Statfs(path, &fs)
		if err1 != nil {
			return err1
		}

		// Check if we're running out of space
		if int64(fs.Bfree) < 10 {
			if blockBackend {
				return fmt.Errorf("Unable to unpack image, run out of disk space (consider increasing your pool's volume.size)")
			}

			return fmt.Errorf("Unable to unpack image, run out of disk space")
		}

		logger.Warn("Unpack failed", logger.Ctx{"file": file, "allowedCmds": allowedCmds, "extension": extension, "path": path, "err": err})
		return fmt.Errorf("Unpack failed: %w", err)
	}

	return nil
}
<|endoftext|>


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

package interference

import "github.com/google/cadvisor/info"

// InterferenceDectector detects if there's a container which
// interferences with a set of containers. The detector tracks
// a set of containers and find the victims and antagonist.
type InterferenceDetector interface {
	// Tracks the behavior of the container.
	AddContainer(ref info.ContainerReference)

	// Returns a list of possible interferences. The upper layer may take action
	// based on the interference.
	Detect() ([]*info.Interference, error)

	// The name of the detector.
	Name() string
}
<commit_msg>Remove unused file.<commit_after><|endoftext|>


<commit_before>package record_set

import (
	"fmt"
	"github.com/miekg/dns"
	"log"
	"net"
	"sort"
	"strings"
	"sync"
)

//
// Key/value store used for dns records. keys are unique, but each key has a list of values
type RecordStore interface {
	PutVal(dnsType uint16, key, val string) error        // Put a single key/value type into store.
	GetVal(dnsType uint16, key string) (string, error)   // Gets a single value. Will round robin or randomly select  if multiple values
	GetAll(dnsType uint16, key string) ([]string, error) // Get all key values
	DelKey(dnsType uint16, key string) error             // Deletes key and all values for a
	DelVal(dnsType uint16, key, value string) error      // Deletes a single value from a key. Deletes key ifthere are no more values
	Clear() error                                        // Clear all keys from set
}

type RrIndexes struct {
	sync.Mutex
	indexes map[string]int
}

func (rri *RrIndexes) NextVal(dnsType uint16, key string, vals []string) (val string) {
	vl := len(vals)
	if vl == 0 {
		return
	}
	if vl == 1 {
		val = vals[0]
		return
	}
	sort.Strings(vals)
	rri.Lock()
	defer rri.Unlock()
	kp := fmt.Sprintf("/%d/%s", dnsType, key)
	idx := rri.indexes[kp]
	if idx >= vl {
		idx = 0
	}
	val = vals[idx]
	rri.indexes[kp] = idx + 1
	return
}

type RecordSet struct {
	store      RecordStore
	rr_indexes *RrIndexes
}

func Create(store RecordStore) (rs *RecordSet) {
	rs = &RecordSet{store, &RrIndexes{indexes: make(map[string]int)}}
	return
}

func (r *RecordSet) Put(dnsType uint16, host, addr string) {
	log.Printf("Adding/updating  %X %s A %s", dnsType, host, addr)
	err := r.store.PutVal(dnsType, host+".", addr)
	if err != nil {
		log.Printf("Unable to put primary record: %s", err.Error())
		return
	}
	// For A or AAAA records, put in reverse DNS
	if dnsType == dns.TypeA || dnsType == dns.TypeAAAA {
		raddr, _ := ReverseAddr(addr)
		if raddr != "" {
			log.Printf("Adding %s PTR %s", raddr, host)
			err = r.store.PutVal(dns.TypePTR, raddr+".", host)
			if err != nil {
				log.Printf("Error %s addting PTR record %s => %s", raddr, host)
			}
		}
	}
}

func (r *RecordSet) Del(dnsType uint16, host string) {
	log.Printf("Removing %X  %s", dnsType, host)
	addrs, err := r.store.GetAll(dnsType, host+".")
	if err != nil {
		log.Printf("Unable to fetch address for host %s -- %s", host, err.Error())
	}
	for _, addr := range addrs {
		raddr, _ := ReverseAddr(addr)
		err = r.store.DelKey(dns.TypePTR, raddr+".")
		if err != nil {
			log.Printf("Unable to remove PTR record %s -- %s", raddr, err.Error())
		}
	}
	err = r.store.DelKey(dnsType, host+".")
	if err != nil {
		log.Printf("Unable to remove host key %s (%s)", host, err.Error())
	}
}

func (r *RecordSet) DelAddr(dnsType uint16, host, addr string) {
	log.Printf("Removing %X  %s %s", dnsType, host, addr)
	err := r.store.DelVal(dnsType, host+".", addr)
	if err != nil {
		log.Printf("Unable to delete  address %s for host %s -- %s", addr, host, err.Error())
		return
	}
	raddr, _ := ReverseAddr(addr)
	err = r.store.DelKey(dns.TypePTR, raddr+".")
	if err != nil {
		log.Printf("Unable to remove PTR record %s -- %s", raddr, err.Error())
	}
}

func (r *RecordSet) Get(dnsType uint16, host string) (addr string) {
	addrs, err := r.store.GetAll(dnsType, host)
	if err != nil {
		log.Printf("Unable to fetch value: %s", err.Error())
		return
	}
	if len(addrs) == 0 { // Try a wildcard
		parts := strings.SplitN(host, ".", 2)
		if len(parts) == 2 {
			wc := "*." + parts[1]
			addrs, err = r.store.GetAll(dnsType, wc)
			if err != nil {
				log.Printf("Unable to fetch value: %s", err.Error())
				return
			}
		}
	}
	if len(addrs) == 0 { // Try adouble wildcard
		parts := strings.SplitN(host, ".", 3)
		if len(parts) == 3 {
			wc := "*.*." + parts[2]
			addrs, err = r.store.GetAll(dnsType, wc)
			if err != nil {
				log.Printf("Unable to fetch value: %s", err.Error())
				return
			}
		}
	}
	return r.rr_indexes.NextVal(dnsType, host, addrs)
}

// Taken somewhat from stdlib dnsclient.go
func ReverseAddr(addr string) (arpa string, err error) {
	ip := net.ParseIP(addr)
	if ip == nil {
		return "", fmt.Errorf("Unrecognized address: %s", addr)
	}
	if ip.To4() != nil {
		arpa = fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa", ip[15], ip[14], ip[13], ip[12])
		return
	}
	// Must be IPv6
	var parts []string
	for i := len(ip) - 1; i >= 0; i-- {
		v := byte(ip[i])
		str := fmt.Sprintf("%x.%x.", v&0xf, v>>4)
		parts = append(parts, str)
	}
	// Append "ip6.arpa." and return (buf already has the final .)
	parts = append(parts, "ip6.arpa.")
	return strings.Join(parts, ""), nil
}
<commit_msg>clear index hash entries<commit_after>package record_set

import (
	"fmt"
	"github.com/miekg/dns"
	"log"
	"net"
	"sort"
	"strings"
	"sync"
)

//
// Key/value store used for dns records. keys are unique, but each key has a list of values
type RecordStore interface {
	PutVal(dnsType uint16, key, val string) error        // Put a single key/value type into store.
	GetVal(dnsType uint16, key string) (string, error)   // Gets a single value. Will round robin or randomly select  if multiple values
	GetAll(dnsType uint16, key string) ([]string, error) // Get all key values
	DelKey(dnsType uint16, key string) error             // Deletes key and all values for a
	DelVal(dnsType uint16, key, value string) error      // Deletes a single value from a key. Deletes key ifthere are no more values
	Clear() error                                        // Clear all keys from set
}

type RrIndexes struct {
	sync.Mutex
	indexes map[string]int
}

func (rri *RrIndexes) NextVal(dnsType uint16, key string, vals []string) (val string) {
	vl := len(vals)
	if vl == 0 {
		return
	}
	if vl == 1 {
		val = vals[0]
		return
	}
	sort.Strings(vals)
	rri.Lock()
	defer rri.Unlock()
	kp := fmt.Sprintf("/%d/%s", dnsType, key)
	idx := rri.indexes[kp]
	if idx >= vl {
		idx = 0
	}
	val = vals[idx]
	rri.indexes[kp] = idx + 1
	return
}

func (rri *RrIndexes) Del(dnsType uint16, key string) {
	kp := fmt.Sprintf("/%d/%s", dnsType, key)
	delete(rri.indexes, kp)
}

type RecordSet struct {
	store      RecordStore
	rr_indexes *RrIndexes
}

func Create(store RecordStore) (rs *RecordSet) {
	rs = &RecordSet{store, &RrIndexes{indexes: make(map[string]int)}}
	return
}

func (r *RecordSet) Put(dnsType uint16, host, addr string) {
	log.Printf("Adding/updating  %X %s A %s", dnsType, host, addr)
	err := r.store.PutVal(dnsType, host+".", addr)
	if err != nil {
		log.Printf("Unable to put primary record: %s", err.Error())
		return
	}
	// For A or AAAA records, put in reverse DNS
	if dnsType == dns.TypeA || dnsType == dns.TypeAAAA {
		raddr, _ := ReverseAddr(addr)
		if raddr != "" {
			log.Printf("Adding %s PTR %s", raddr, host)
			err = r.store.PutVal(dns.TypePTR, raddr+".", host)
			if err != nil {
				log.Printf("Error %s addting PTR record %s => %s", raddr, host)
			}
		}
	}
}

func (r *RecordSet) Del(dnsType uint16, host string) {
	log.Printf("Removing %X  %s", dnsType, host)
	addrs, err := r.store.GetAll(dnsType, host+".")
	if err != nil {
		log.Printf("Unable to fetch address for host %s -- %s", host, err.Error())
	}
	for _, addr := range addrs {
		raddr, _ := ReverseAddr(addr)
		err = r.store.DelKey(dns.TypePTR, raddr+".")
		if err != nil {
			log.Printf("Unable to remove PTR record %s -- %s", raddr, err.Error())
		}
	}
	err = r.store.DelKey(dnsType, host+".")
	if err != nil {
		log.Printf("Unable to remove host key %s (%s)", host, err.Error())
	}
	r.rr_indexes.Del(dnsType, host)
}

func (r *RecordSet) DelAddr(dnsType uint16, host, addr string) {
	log.Printf("Removing %X  %s %s", dnsType, host, addr)
	err := r.store.DelVal(dnsType, host+".", addr)
	if err != nil {
		log.Printf("Unable to delete  address %s for host %s -- %s", addr, host, err.Error())
		return
	}
	raddr, _ := ReverseAddr(addr)
	err = r.store.DelKey(dns.TypePTR, raddr+".")
	if err != nil {
		log.Printf("Unable to remove PTR record %s -- %s", raddr, err.Error())
	}
	r.rr_indexes.Del(dnsType, host)
}

func (r *RecordSet) Get(dnsType uint16, host string) (addr string) {
	addrs, err := r.store.GetAll(dnsType, host)
	if err != nil {
		log.Printf("Unable to fetch value: %s", err.Error())
		return
	}
	if len(addrs) == 0 { // Try a wildcard
		parts := strings.SplitN(host, ".", 2)
		if len(parts) == 2 {
			wc := "*." + parts[1]
			addrs, err = r.store.GetAll(dnsType, wc)
			if err != nil {
				log.Printf("Unable to fetch value: %s", err.Error())
				return
			}
		}
	}
	if len(addrs) == 0 { // Try adouble wildcard
		parts := strings.SplitN(host, ".", 3)
		if len(parts) == 3 {
			wc := "*.*." + parts[2]
			addrs, err = r.store.GetAll(dnsType, wc)
			if err != nil {
				log.Printf("Unable to fetch value: %s", err.Error())
				return
			}
		}
	}
	return r.rr_indexes.NextVal(dnsType, host, addrs)
}

// Taken somewhat from stdlib dnsclient.go
func ReverseAddr(addr string) (arpa string, err error) {
	ip := net.ParseIP(addr)
	if ip == nil {
		return "", fmt.Errorf("Unrecognized address: %s", addr)
	}
	if ip.To4() != nil {
		arpa = fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa", ip[15], ip[14], ip[13], ip[12])
		return
	}
	// Must be IPv6
	var parts []string
	for i := len(ip) - 1; i >= 0; i-- {
		v := byte(ip[i])
		str := fmt.Sprintf("%x.%x.", v&0xf, v>>4)
		parts = append(parts, str)
	}
	// Append "ip6.arpa." and return (buf already has the final .)
	parts = append(parts, "ip6.arpa.")
	return strings.Join(parts, ""), nil
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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

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

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

package localconfig

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

	"github.com/hyperledger/fabric/common/viperutil"

	"github.com/op/go-logging"
	"github.com/spf13/viper"

	bccsp "github.com/hyperledger/fabric/bccsp/factory"
)

var logger = logging.MustGetLogger("configtx/tool/localconfig")

const (
	// SampleInsecureProfile references the sample profile which does not include any MSPs and uses solo for ordering.
	SampleInsecureProfile = "SampleInsecureSolo"
	// SampleSingleMSPSoloProfile references the sample profile which includes only the sample MSP and uses solo for ordering.
	SampleSingleMSPSoloProfile = "SampleSingleMSPSolo"

	// Prefix identifies the prefix for the configtxgen-related ENV vars.
	Prefix string = "CONFIGTX"
)

var (
	configName     string
	configFileName string
)

func init() {
	configName = strings.ToLower(Prefix)
	configFileName = configName + ".yaml"
}

// TopLevel consists of the structs used by the configtxgen tool.
type TopLevel struct {
	Profiles      map[string]*Profile `yaml:"Profiles"`
	Organizations []*Organization     `yaml:"Organizations"`
	Application   *Application        `yaml:"Application"`
	Orderer       *Orderer            `yaml:"Orderer"`
}

// Profile encodes orderer/application configuration combinations for the configtxgen tool.
type Profile struct {
	Application *Application `yaml:"Application"`
	Orderer     *Orderer     `yaml:"Orderer"`
}

// Application encodes the application-level configuration needed in config transactions.
type Application struct {
	Organizations []*Organization `yaml:"Organizations"`
}

// Organization encodes the organization-level configuration needed in config transactions.
type Organization struct {
	Name   string             `yaml:"Name"`
	ID     string             `yaml:"ID"`
	MSPDir string             `yaml:"MSPDir"`
	BCCSP  *bccsp.FactoryOpts `yaml:"BCCSP"`

	// Note: Viper deserialization does not seem to care for
	// embedding of types, so we use one organization struct
	// for both orderers and applications.
	AnchorPeers []*AnchorPeer `yaml:"AnchorPeers"`
}

// AnchorPeer encodes the necessary fields to identify an anchor peer.
type AnchorPeer struct {
	Host string `yaml:"Host"`
	Port int    `yaml:"Port"`
}

// ApplicationOrganization ...
// TODO This should probably be removed
type ApplicationOrganization struct {
	Organization `yaml:"Organization"`
}

// Orderer contains configuration which is used for the
// bootstrapping of an orderer by the provisional bootstrapper.
type Orderer struct {
	OrdererType   string          `yaml:"OrdererType"`
	Addresses     []string        `yaml:"Addresses"`
	BatchTimeout  time.Duration   `yaml:"BatchTimeout"`
	BatchSize     BatchSize       `yaml:"BatchSize"`
	Kafka         Kafka           `yaml:"Kafka"`
	Organizations []*Organization `yaml:"Organizations"`
	MaxChannels   uint64          `yaml:"MaxChannels"`
}

// BatchSize contains configuration affecting the size of batches.
type BatchSize struct {
	MaxMessageCount   uint32 `yaml:"MaxMessageSize"`
	AbsoluteMaxBytes  uint32 `yaml:"AbsoluteMaxBytes"`
	PreferredMaxBytes uint32 `yaml:"PreferredMaxBytes"`
}

// Kafka contains configuration for the Kafka-based orderer.
type Kafka struct {
	Brokers []string `yaml:"Brokers"`
}

var genesisDefaults = TopLevel{
	Orderer: &Orderer{
		OrdererType:  "solo",
		Addresses:    []string{"127.0.0.1:7050"},
		BatchTimeout: 2 * time.Second,
		BatchSize: BatchSize{
			MaxMessageCount:   10,
			AbsoluteMaxBytes:  100000000,
			PreferredMaxBytes: 512 * 1024,
		},
		Kafka: Kafka{
			Brokers: []string{"127.0.0.1:9092"},
		},
	},
}

func (p *Profile) completeInitialization() {
	for {
		switch {
		case p.Orderer.OrdererType == "":
			logger.Infof("Orderer.OrdererType unset, setting to %s", genesisDefaults.Orderer.OrdererType)
			p.Orderer.OrdererType = genesisDefaults.Orderer.OrdererType
		case p.Orderer.Addresses == nil:
			logger.Infof("Orderer.Addresses unset, setting to %s", genesisDefaults.Orderer.Addresses)
			p.Orderer.Addresses = genesisDefaults.Orderer.Addresses
		case p.Orderer.BatchTimeout == 0:
			logger.Infof("Orderer.BatchTimeout unset, setting to %s", genesisDefaults.Orderer.BatchTimeout)
			p.Orderer.BatchTimeout = genesisDefaults.Orderer.BatchTimeout
		case p.Orderer.BatchSize.MaxMessageCount == 0:
			logger.Infof("Orderer.BatchSize.MaxMessageCount unset, setting to %s", genesisDefaults.Orderer.BatchSize.MaxMessageCount)
			p.Orderer.BatchSize.MaxMessageCount = genesisDefaults.Orderer.BatchSize.MaxMessageCount
		case p.Orderer.BatchSize.AbsoluteMaxBytes == 0:
			logger.Infof("Orderer.BatchSize.AbsoluteMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes)
			p.Orderer.BatchSize.AbsoluteMaxBytes = genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes
		case p.Orderer.BatchSize.PreferredMaxBytes == 0:
			logger.Infof("Orderer.BatchSize.PreferredMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.PreferredMaxBytes)
			p.Orderer.BatchSize.PreferredMaxBytes = genesisDefaults.Orderer.BatchSize.PreferredMaxBytes
		case p.Orderer.Kafka.Brokers == nil:
			logger.Infof("Orderer.Kafka.Brokers unset, setting to %v", genesisDefaults.Orderer.Kafka.Brokers)
			p.Orderer.Kafka.Brokers = genesisDefaults.Orderer.Kafka.Brokers
		default:
			return
		}
	}
}

// Load returns the orderer/application config combination that corresponds to a given profile.
func Load(profile string) *Profile {
	config := viper.New()

	config.SetConfigName("configtx")
	var cfgPath string

	// Candidate paths to look for the config file in, based on GOPATH
	searchPath := []string{
		os.Getenv("ORDERER_CFG_PATH"),
		os.Getenv("PEER_CFG_PATH"),
	}

	for _, p := range filepath.SplitList(os.Getenv("GOPATH")) {
		searchPath = append(searchPath, filepath.Join(p, "src/github.com/hyperledger/fabric/common/configtx/tool/"))
	}

	for _, path := range searchPath {
		if len(path) == 0 {
			// No point printing a "checking for" message below for an empty path
			continue
		}
		logger.Infof("Looking for %s in: %s", configFileName, path)
		if _, err := os.Stat(filepath.Join(path, configFileName)); err != nil {
			// The YAML file does not exist in this component of the path
			continue
		}
		cfgPath = path
		logger.Infof("Found %s there", configFileName)
	}

	if cfgPath == "" {
		logger.Fatalf("Could not find %s in paths of %s."+
			"Try setting ORDERER_CFG_PATH, PEER_CFG_PATH, or GOPATH correctly.",
			configFileName, searchPath)
	}

	// Path to look for the config file in
	config.AddConfigPath(cfgPath)

	// For environment variables
	config.SetEnvPrefix(Prefix)
	config.AutomaticEnv()
	// This replacer allows substitution within the particular profile without having to fully qualify the name
	replacer := strings.NewReplacer(strings.ToUpper(fmt.Sprintf("profiles.%s.", profile)), "", ".", "_")
	config.SetEnvKeyReplacer(replacer)

	err := config.ReadInConfig()
	if err != nil {
		logger.Panicf("Error reading configuration from %s in %s: %s", configFileName, cfgPath, err)
	}

	var uconf TopLevel
	err = viperutil.EnhancedExactUnmarshal(config, &uconf)
	if err != nil {
		logger.Panicf("Error unmarshaling config into struct: %s", err)
	}

	result, ok := uconf.Profiles[profile]
	if !ok {
		logger.Panicf("Could not find profile %s", profile)
	}

	result.completeInitialization()

	return result
}
<commit_msg>[FAB-2824]  Incorrect configtx.yaml selected<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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

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

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

package localconfig

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

	"github.com/hyperledger/fabric/common/viperutil"

	"github.com/op/go-logging"
	"github.com/spf13/viper"

	bccsp "github.com/hyperledger/fabric/bccsp/factory"
)

var logger = logging.MustGetLogger("configtx/tool/localconfig")

const (
	// SampleInsecureProfile references the sample profile which does not include any MSPs and uses solo for ordering.
	SampleInsecureProfile = "SampleInsecureSolo"
	// SampleSingleMSPSoloProfile references the sample profile which includes only the sample MSP and uses solo for ordering.
	SampleSingleMSPSoloProfile = "SampleSingleMSPSolo"

	// Prefix identifies the prefix for the configtxgen-related ENV vars.
	Prefix string = "CONFIGTX"
)

var (
	configName     string
	configFileName string
)

func init() {
	configName = strings.ToLower(Prefix)
	configFileName = configName + ".yaml"
}

// TopLevel consists of the structs used by the configtxgen tool.
type TopLevel struct {
	Profiles      map[string]*Profile `yaml:"Profiles"`
	Organizations []*Organization     `yaml:"Organizations"`
	Application   *Application        `yaml:"Application"`
	Orderer       *Orderer            `yaml:"Orderer"`
}

// Profile encodes orderer/application configuration combinations for the configtxgen tool.
type Profile struct {
	Application *Application `yaml:"Application"`
	Orderer     *Orderer     `yaml:"Orderer"`
}

// Application encodes the application-level configuration needed in config transactions.
type Application struct {
	Organizations []*Organization `yaml:"Organizations"`
}

// Organization encodes the organization-level configuration needed in config transactions.
type Organization struct {
	Name   string             `yaml:"Name"`
	ID     string             `yaml:"ID"`
	MSPDir string             `yaml:"MSPDir"`
	BCCSP  *bccsp.FactoryOpts `yaml:"BCCSP"`

	// Note: Viper deserialization does not seem to care for
	// embedding of types, so we use one organization struct
	// for both orderers and applications.
	AnchorPeers []*AnchorPeer `yaml:"AnchorPeers"`
}

// AnchorPeer encodes the necessary fields to identify an anchor peer.
type AnchorPeer struct {
	Host string `yaml:"Host"`
	Port int    `yaml:"Port"`
}

// ApplicationOrganization ...
// TODO This should probably be removed
type ApplicationOrganization struct {
	Organization `yaml:"Organization"`
}

// Orderer contains configuration which is used for the
// bootstrapping of an orderer by the provisional bootstrapper.
type Orderer struct {
	OrdererType   string          `yaml:"OrdererType"`
	Addresses     []string        `yaml:"Addresses"`
	BatchTimeout  time.Duration   `yaml:"BatchTimeout"`
	BatchSize     BatchSize       `yaml:"BatchSize"`
	Kafka         Kafka           `yaml:"Kafka"`
	Organizations []*Organization `yaml:"Organizations"`
	MaxChannels   uint64          `yaml:"MaxChannels"`
}

// BatchSize contains configuration affecting the size of batches.
type BatchSize struct {
	MaxMessageCount   uint32 `yaml:"MaxMessageSize"`
	AbsoluteMaxBytes  uint32 `yaml:"AbsoluteMaxBytes"`
	PreferredMaxBytes uint32 `yaml:"PreferredMaxBytes"`
}

// Kafka contains configuration for the Kafka-based orderer.
type Kafka struct {
	Brokers []string `yaml:"Brokers"`
}

var genesisDefaults = TopLevel{
	Orderer: &Orderer{
		OrdererType:  "solo",
		Addresses:    []string{"127.0.0.1:7050"},
		BatchTimeout: 2 * time.Second,
		BatchSize: BatchSize{
			MaxMessageCount:   10,
			AbsoluteMaxBytes:  100000000,
			PreferredMaxBytes: 512 * 1024,
		},
		Kafka: Kafka{
			Brokers: []string{"127.0.0.1:9092"},
		},
	},
}

func (p *Profile) completeInitialization() {
	for {
		switch {
		case p.Orderer.OrdererType == "":
			logger.Infof("Orderer.OrdererType unset, setting to %s", genesisDefaults.Orderer.OrdererType)
			p.Orderer.OrdererType = genesisDefaults.Orderer.OrdererType
		case p.Orderer.Addresses == nil:
			logger.Infof("Orderer.Addresses unset, setting to %s", genesisDefaults.Orderer.Addresses)
			p.Orderer.Addresses = genesisDefaults.Orderer.Addresses
		case p.Orderer.BatchTimeout == 0:
			logger.Infof("Orderer.BatchTimeout unset, setting to %s", genesisDefaults.Orderer.BatchTimeout)
			p.Orderer.BatchTimeout = genesisDefaults.Orderer.BatchTimeout
		case p.Orderer.BatchSize.MaxMessageCount == 0:
			logger.Infof("Orderer.BatchSize.MaxMessageCount unset, setting to %s", genesisDefaults.Orderer.BatchSize.MaxMessageCount)
			p.Orderer.BatchSize.MaxMessageCount = genesisDefaults.Orderer.BatchSize.MaxMessageCount
		case p.Orderer.BatchSize.AbsoluteMaxBytes == 0:
			logger.Infof("Orderer.BatchSize.AbsoluteMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes)
			p.Orderer.BatchSize.AbsoluteMaxBytes = genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes
		case p.Orderer.BatchSize.PreferredMaxBytes == 0:
			logger.Infof("Orderer.BatchSize.PreferredMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.PreferredMaxBytes)
			p.Orderer.BatchSize.PreferredMaxBytes = genesisDefaults.Orderer.BatchSize.PreferredMaxBytes
		case p.Orderer.Kafka.Brokers == nil:
			logger.Infof("Orderer.Kafka.Brokers unset, setting to %v", genesisDefaults.Orderer.Kafka.Brokers)
			p.Orderer.Kafka.Brokers = genesisDefaults.Orderer.Kafka.Brokers
		default:
			return
		}
	}
}

// Load returns the orderer/application config combination that corresponds to a given profile.
func Load(profile string) *Profile {
	config := viper.New()

	config.SetConfigName("configtx")
	var cfgPath string

	// Candidate paths to look for the config file in, based on GOPATH
	searchPath := []string{
		os.Getenv("ORDERER_CFG_PATH"),
		os.Getenv("PEER_CFG_PATH"),
	}

	for _, p := range filepath.SplitList(os.Getenv("GOPATH")) {
		searchPath = append(searchPath, filepath.Join(p, "src/github.com/hyperledger/fabric/common/configtx/tool/"))
	}

	for _, path := range searchPath {
		if len(path) == 0 {
			// No point printing a "checking for" message below for an empty path
			continue
		}
		logger.Infof("Looking for %s in: %s", configFileName, path)
		if _, err := os.Stat(filepath.Join(path, configFileName)); err != nil {
			// The YAML file does not exist in this component of the path
			continue
		}
		cfgPath = path
		logger.Infof("Found %s there", configFileName)
		break
	}

	if cfgPath == "" {
		logger.Fatalf("Could not find %s in paths of %s."+
			"Try setting ORDERER_CFG_PATH, PEER_CFG_PATH, or GOPATH correctly.",
			configFileName, searchPath)
	}

	// Path to look for the config file in
	config.AddConfigPath(cfgPath)

	// For environment variables
	config.SetEnvPrefix(Prefix)
	config.AutomaticEnv()
	// This replacer allows substitution within the particular profile without having to fully qualify the name
	replacer := strings.NewReplacer(strings.ToUpper(fmt.Sprintf("profiles.%s.", profile)), "", ".", "_")
	config.SetEnvKeyReplacer(replacer)

	err := config.ReadInConfig()
	if err != nil {
		logger.Panicf("Error reading configuration from %s in %s: %s", configFileName, cfgPath, err)
	}

	var uconf TopLevel
	err = viperutil.EnhancedExactUnmarshal(config, &uconf)
	if err != nil {
		logger.Panicf("Error unmarshaling config into struct: %s", err)
	}

	result, ok := uconf.Profiles[profile]
	if !ok {
		logger.Panicf("Could not find profile %s", profile)
	}

	result.completeInitialization()

	return result
}
<|endoftext|>


<commit_before>package search

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/algolia/algoliasearch-client-go/algolia/call"
	"github.com/algolia/algoliasearch-client-go/algolia/errs"
	iopt "github.com/algolia/algoliasearch-client-go/algolia/internal/opt"
	"github.com/algolia/algoliasearch-client-go/algolia/iterator"
	"github.com/algolia/algoliasearch-client-go/algolia/opt"
)

func (i *Index) GetObject(objectID string, object interface{}, opts ...interface{}) error {
	if attrs := iopt.ExtractAttributesToRetrieve(opts...); attrs != nil {
		attributesToRetrieve := attrs.Get()
		if len(attributesToRetrieve) > 0 {
			opts = append(opts, opt.ExtraURLParams(map[string]string{
				"attributesToRetrieve": strings.Join(attributesToRetrieve, ","),
			}))
		}
	}

	path := i.path("/" + url.QueryEscape(objectID))
	return i.transport.Request(&object, http.MethodGet, path, nil, call.Read, opts...)
}

type getObjectsReq struct {
	IndexName            string                          `json:"indexName"`
	ObjectID             string                          `json:"objectID"`
	AttributesToRetrieve *opt.AttributesToRetrieveOption `json:"attributesToRetrieve,omitempty"`
}

type getObjectsRes struct {
	Results interface{} `json:"results"`
}

func (i *Index) GetObjects(objectIDs []string, objects interface{}, opts ...interface{}) error {
	var (
		attributesToRetrieve = iopt.ExtractAttributesToRetrieve(opts...)
		requests             = make([]getObjectsReq, len(objectIDs))
		body                 = map[string]interface{}{"requests": requests}
		res                  = getObjectsRes{objects}
	)

	for j, objectID := range objectIDs {
		requests[j] = getObjectsReq{
			IndexName:            i.name,
			ObjectID:             url.QueryEscape(objectID),
			AttributesToRetrieve: attributesToRetrieve,
		}
	}

	path := "/1/indexes/*/objects"
	return i.transport.Request(&res, http.MethodPost, path, body, call.Read, opts...)
}

func (i *Index) SaveObject(object interface{}, opts ...interface{}) (res SaveObjectRes, err error) {
	path := i.path("")
	err = i.transport.Request(&res, http.MethodPost, path, object, call.Write, opts...)
	res.wait = i.waitTask
	return
}

func (i *Index) SaveObjects(objects interface{}, opts ...interface{}) (res MultipleBatchRes, err error) {
	return i.Batch(objects, AddObject, opts...)
}

func (i *Index) PartialUpdateObject(object interface{}, opts ...interface{}) (res UpdateTaskRes, err error) {
	objectID, ok := getObjectID(object)
	if !ok {
		err = errs.ErrMissingObjectID
		res.wait = noWait
		return
	}

	createIfNotExists := iopt.ExtractCreateIfNotExists(opts...).Get()

	path := i.path("/" + url.QueryEscape(objectID) + "/partial")
	if !createIfNotExists {
		path += "?createIfNotExists=false"
	}

	err = i.transport.Request(&res, "POST", path, object, call.Write, opts...)
	res.wait = i.waitTask
	return
}

func (i *Index) PartialUpdateObjects(objects interface{}, opts ...interface{}) (res MultipleBatchRes, err error) {
	var (
		action            BatchAction
		createIfNotExists = iopt.ExtractCreateIfNotExists(opts...).Get()
	)

	if createIfNotExists {
		action = PartialUpdateObject
	} else {
		action = PartialUpdateObjectNoCreate
	}

	return i.Batch(objects, action, opts...)
}

func (i *Index) Batch(objects interface{}, action BatchAction, opts ...interface{}) (MultipleBatchRes, error) {
	var (
		batch      []interface{}
		operations []BatchOperation
		response   BatchRes
		res        MultipleBatchRes
	)

	it := iterator.New(objects)
	autoGenerateObjectIDIfNotExist := iopt.ExtractAutoGenerateObjectIDIfNotExist(opts...).Get()

	for {
		object, err := it.Next()
		if err != nil {
			return res, fmt.Errorf("iteration failed unexpectedly: %v", err)
		}

		if !autoGenerateObjectIDIfNotExist && object != nil && !hasObjectID(object) {
			return res, fmt.Errorf("missing objectID in object %#v", object)
		}

		if len(batch) >= i.maxBatchSize || object == nil {
			operations, err = newOperationBatch(batch, action)
			if err != nil {
				return res, fmt.Errorf("could not generate intermediate batch: %v", err)
			}
			response, err = i.batch(operations, opts...)
			if err != nil {
				return res, fmt.Errorf("could not send intermediate batch: %v", err)
			}
			res.responses = append(res.responses, response)
		} else {
			batch = append(batch, object)
		}

		if object == nil {
			break
		}
	}

	return res, nil
}

func (i *Index) batch(operations []BatchOperation, opts ...interface{}) (res BatchRes, err error) {
	path := i.path("/batch")
	body := map[string][]BatchOperation{
		"requests": operations,
	}
	err = i.transport.Request(&res, http.MethodPost, path, body, call.Write, opts...)
	res.wait = i.waitTask
	return
}

func (i *Index) DeleteObject(objectID string, opts ...interface{}) (res DeleteTaskRes, err error) {
	if objectID == "" {
		err = errs.ErrMissingObjectID
		res.wait = noWait
		return
	}

	path := i.path("/" + url.QueryEscape(objectID))
	err = i.transport.Request(&res, http.MethodDelete, path, nil, call.Write, opts...)
	res.wait = i.waitTask
	return
}

func (i *Index) DeleteObjects(objectIDs []string, opts ...interface{}) (res BatchRes, err error) {
	objects := make([]interface{}, len(objectIDs))

	for j, id := range objectIDs {
		objects[j] = map[string]string{"objectID": id}
	}

	var operations []BatchOperation
	if operations, err = newOperationBatch(objects, DeleteObject); err == nil {
		res, err = i.batch(operations, opts...)
	} else {
		res.wait = noWait
	}
	return
}

type deleteByReq struct {
	AroundLatLng      *opt.AroundLatLngOption      `json:"aroundLatLng,omitempty"`
	AroundRadius      *opt.AroundRadiusOption      `json:"aroundRadius,omitempty"`
	FacetFilters      *opt.FacetFiltersOption      `json:"facetFilters,omitempty"`
	Filters           *opt.FiltersOption           `json:"filters,omitempty"`
	InsideBoundingBox *opt.InsideBoundingBoxOption `json:"insideBoundingBox,omitempty"`
	InsidePolygon     *opt.InsidePolygonOption     `json:"insidePolygon,omitempty"`
	NumericFilters    *opt.NumericFiltersOption    `json:"numericFilters,omitempty"`
}

func (i *Index) DeleteBy(opts ...interface{}) (res UpdateTaskRes, err error) {
	body := deleteByReq{
		AroundLatLng:      iopt.ExtractAroundLatLng(opts...),
		AroundRadius:      iopt.ExtractAroundRadius(opts...),
		FacetFilters:      iopt.ExtractFacetFilters(opts...),
		Filters:           iopt.ExtractFilters(opts...),
		InsideBoundingBox: iopt.ExtractInsideBoundingBox(opts...),
		InsidePolygon:     iopt.ExtractInsidePolygon(opts...),
		NumericFilters:    iopt.ExtractNumericFilters(opts...),
	}

	path := i.path("/deleteByQuery")
	err = i.transport.Request(&res, http.MethodPost, path, body, call.Write, opts...)
	res.wait = i.waitTask
	return
}
<commit_msg>changed: Expose the correct version of Index.Batch (one without automatic batching)<commit_after>package search

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/algolia/algoliasearch-client-go/algolia/call"
	"github.com/algolia/algoliasearch-client-go/algolia/errs"
	iopt "github.com/algolia/algoliasearch-client-go/algolia/internal/opt"
	"github.com/algolia/algoliasearch-client-go/algolia/iterator"
	"github.com/algolia/algoliasearch-client-go/algolia/opt"
)

func (i *Index) GetObject(objectID string, object interface{}, opts ...interface{}) error {
	if attrs := iopt.ExtractAttributesToRetrieve(opts...); attrs != nil {
		attributesToRetrieve := attrs.Get()
		if len(attributesToRetrieve) > 0 {
			opts = append(opts, opt.ExtraURLParams(map[string]string{
				"attributesToRetrieve": strings.Join(attributesToRetrieve, ","),
			}))
		}
	}

	path := i.path("/" + url.QueryEscape(objectID))
	return i.transport.Request(&object, http.MethodGet, path, nil, call.Read, opts...)
}

func (i *Index) SaveObject(object interface{}, opts ...interface{}) (res SaveObjectRes, err error) {
	path := i.path("")
	err = i.transport.Request(&res, http.MethodPost, path, object, call.Write, opts...)
	res.wait = i.waitTask
	return
}

func (i *Index) PartialUpdateObject(object interface{}, opts ...interface{}) (res UpdateTaskRes, err error) {
	objectID, ok := getObjectID(object)
	if !ok {
		err = errs.ErrMissingObjectID
		res.wait = noWait
		return
	}

	createIfNotExists := iopt.ExtractCreateIfNotExists(opts...).Get()

	path := i.path("/" + url.QueryEscape(objectID) + "/partial")
	if !createIfNotExists {
		path += "?createIfNotExists=false"
	}

	err = i.transport.Request(&res, "POST", path, object, call.Write, opts...)
	res.wait = i.waitTask
	return
}

func (i *Index) DeleteObject(objectID string, opts ...interface{}) (res DeleteTaskRes, err error) {
	if objectID == "" {
		err = errs.ErrMissingObjectID
		res.wait = noWait
		return
	}

	path := i.path("/" + url.QueryEscape(objectID))
	err = i.transport.Request(&res, http.MethodDelete, path, nil, call.Write, opts...)
	res.wait = i.waitTask
	return
}

type getObjectsReq struct {
	IndexName            string                          `json:"indexName"`
	ObjectID             string                          `json:"objectID"`
	AttributesToRetrieve *opt.AttributesToRetrieveOption `json:"attributesToRetrieve,omitempty"`
}

type getObjectsRes struct {
	Results interface{} `json:"results"`
}

func (i *Index) GetObjects(objectIDs []string, objects interface{}, opts ...interface{}) error {
	var (
		attributesToRetrieve = iopt.ExtractAttributesToRetrieve(opts...)
		requests             = make([]getObjectsReq, len(objectIDs))
		body                 = map[string]interface{}{"requests": requests}
		res                  = getObjectsRes{objects}
	)

	for j, objectID := range objectIDs {
		requests[j] = getObjectsReq{
			IndexName:            i.name,
			ObjectID:             url.QueryEscape(objectID),
			AttributesToRetrieve: attributesToRetrieve,
		}
	}

	path := "/1/indexes/*/objects"
	return i.transport.Request(&res, http.MethodPost, path, body, call.Read, opts...)
}

func (i *Index) SaveObjects(objects interface{}, opts ...interface{}) (res MultipleBatchRes, err error) {
	return i.batch(objects, AddObject, opts...)
}

func (i *Index) PartialUpdateObjects(objects interface{}, opts ...interface{}) (res MultipleBatchRes, err error) {
	var (
		action            BatchAction
		createIfNotExists = iopt.ExtractCreateIfNotExists(opts...).Get()
	)

	if createIfNotExists {
		action = PartialUpdateObject
	} else {
		action = PartialUpdateObjectNoCreate
	}

	return i.batch(objects, action, opts...)
}

func (i *Index) DeleteObjects(objectIDs []string, opts ...interface{}) (res BatchRes, err error) {
	objects := make([]interface{}, len(objectIDs))

	for j, id := range objectIDs {
		objects[j] = map[string]string{"objectID": id}
	}

	var operations []BatchOperation
	if operations, err = newOperationBatch(objects, DeleteObject); err == nil {
		res, err = i.Batch(operations, opts...)
	} else {
		res.wait = noWait
	}
	return
}

func (i *Index) batch(objects interface{}, action BatchAction, opts ...interface{}) (MultipleBatchRes, error) {
	var (
		batch      []interface{}
		operations []BatchOperation
		response   BatchRes
		res        MultipleBatchRes
	)

	it := iterator.New(objects)
	autoGenerateObjectIDIfNotExist := iopt.ExtractAutoGenerateObjectIDIfNotExist(opts...).Get()

	for {
		object, err := it.Next()
		if err != nil {
			return res, fmt.Errorf("iteration failed unexpectedly: %v", err)
		}

		if !autoGenerateObjectIDIfNotExist && object != nil && !hasObjectID(object) {
			return res, fmt.Errorf("missing objectID in object %#v", object)
		}

		if len(batch) >= i.maxBatchSize || object == nil {
			operations, err = newOperationBatch(batch, action)
			if err != nil {
				return res, fmt.Errorf("could not generate intermediate batch: %v", err)
			}
			response, err = i.Batch(operations, opts...)
			if err != nil {
				return res, fmt.Errorf("could not send intermediate batch: %v", err)
			}
			res.responses = append(res.responses, response)
		} else {
			batch = append(batch, object)
		}

		if object == nil {
			break
		}
	}

	return res, nil
}

type batchReq struct {
	Requests []BatchOperation `json:"requests,omitempty"`
}

func (i *Index) Batch(operations []BatchOperation, opts ...interface{}) (res BatchRes, err error) {
	body := batchReq{
		Requests: operations,
	}

	path := i.path("/batch")
	err = i.transport.Request(&res, http.MethodPost, path, body, call.Write, opts...)
	res.wait = i.waitTask
	return
}

type deleteByReq struct {
	AroundLatLng      *opt.AroundLatLngOption      `json:"aroundLatLng,omitempty"`
	AroundRadius      *opt.AroundRadiusOption      `json:"aroundRadius,omitempty"`
	FacetFilters      *opt.FacetFiltersOption      `json:"facetFilters,omitempty"`
	Filters           *opt.FiltersOption           `json:"filters,omitempty"`
	InsideBoundingBox *opt.InsideBoundingBoxOption `json:"insideBoundingBox,omitempty"`
	InsidePolygon     *opt.InsidePolygonOption     `json:"insidePolygon,omitempty"`
	NumericFilters    *opt.NumericFiltersOption    `json:"numericFilters,omitempty"`
}

func (i *Index) DeleteBy(opts ...interface{}) (res UpdateTaskRes, err error) {
	body := deleteByReq{
		AroundLatLng:      iopt.ExtractAroundLatLng(opts...),
		AroundRadius:      iopt.ExtractAroundRadius(opts...),
		FacetFilters:      iopt.ExtractFacetFilters(opts...),
		Filters:           iopt.ExtractFilters(opts...),
		InsideBoundingBox: iopt.ExtractInsideBoundingBox(opts...),
		InsidePolygon:     iopt.ExtractInsidePolygon(opts...),
		NumericFilters:    iopt.ExtractNumericFilters(opts...),
	}

	path := i.path("/deleteByQuery")
	err = i.transport.Request(&res, http.MethodPost, path, body, call.Write, opts...)
	res.wait = i.waitTask
	return
}
<|endoftext|>


<commit_before>package models

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"strings"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"code.google.com/p/google-api-go-client/drive/v2"

	"github.com/coopernurse/gorp"
)

// https://github.com/coopernurse/gorp#mapping-structs-to-tables
type App struct {
	Id          int       `db:"id"`
	Title       string    `db:"title"`
	FileId      string    `db:"file_id"`
	ApiToken    string    `db:"api_token"`
	Description string    `db:"description"`
	CreatedAt   time.Time `db:"created_at"`
	UpdatedAt   time.Time `db:"updated_at"`
}

func GetAppByApiToken(txn gorp.SqlExecutor, apiToken string) (*App, error) {
	var app App
	err := txn.SelectOne(&app, "SELECT * FROM app where api_token = ?", apiToken)

	if err != nil {
		return nil, err
	}

	return &app, nil
}

func (app *App) Bundles(txn gorp.SqlExecutor) ([]*Bundle, error) {
	var bundles []*Bundle
	_, err := txn.Select(&bundles, "SELECT * FROM bundle WHERE app_id = ? ORDER BY id DESC", app.Id)
	if err != nil {
		return nil, err
	}
	return bundles, nil
}

func (app *App) BundlesByPlatformType(txn gorp.SqlExecutor, platformType BundlePlatformType) ([]*Bundle, error) {
	var bundles []*Bundle
	_, err := txn.Select(&bundles, "SELECT * FROM bundle WHERE app_id = ? AND platform_type = ? ORDER BY id DESC", app.Id, platformType)
	if err != nil {
		return nil, err
	}
	return bundles, nil
}

func (app *App) Authorities(txn gorp.SqlExecutor) ([]*Authority, error) {
	var authorities []*Authority
	_, err := txn.Select(&authorities, "SELECT * FROM authority WHERE app_id = ? ORDER BY id ASC", app.Id)
	if err != nil {
		return nil, err
	}
	return authorities, nil
}

func (app *App) GetMaxRevisionByBundleVersion(txn gorp.SqlExecutor, bundleVersion string) (int, error) {
	revision, err := txn.SelectInt(
		"SELECT IFNULL(MAX(revision), 0) FROM bundle WHERE app_id = ? AND bundle_version = ?",
		app.Id,
		bundleVersion,
	)
	return int(revision), err
}

func NewToken() string {
	uuid := uuid.NewRandom()
	mac := hmac.New(sha256.New, nil)
	mac.Write([]byte(uuid.String()))
	tokenBytes := mac.Sum(nil)
	return hex.EncodeToString(tokenBytes)
}

// https://github.com/coopernurse/gorp#hooks
func (app *App) PreInsert(s gorp.SqlExecutor) error {
	app.CreatedAt = time.Now()
	app.UpdatedAt = app.CreatedAt
	app.ApiToken = NewToken()
	return nil
}

func (app *App) PreUpdate(s gorp.SqlExecutor) error {
	app.UpdatedAt = time.Now()
	return nil
}

func (app *App) Save(txn gorp.SqlExecutor) error {
	return txn.Insert(app)
}

func (app *App) RefreshToken(txn gorp.SqlExecutor) error {
	current, err := GetApp(txn, app.Id)
	if err != nil {
		return err
	}

	current.ApiToken = NewToken()

	_, err = txn.Update(current)
	return err
}

func (app *App) Update(txn gorp.SqlExecutor) error {
	current, err := GetApp(txn, app.Id)
	if err != nil {
		return err
	}

	current.Title = app.Title
	current.Description = app.Description

	_, err = txn.Update(current)
	return err
}

func (app *App) DeleteFromDB(txn gorp.SqlExecutor) error {
	_, err := txn.Delete(app)
	return err
}

func (app *App) DeleteFromGoogleDrive(s *GoogleService) error {
	return s.DeleteFile(app.FileId)
}

func (app *App) Delete(txn gorp.SqlExecutor, s *GoogleService) error {
	if err := app.DeleteBundles(txn); err != nil {
		return err
	}
	if err := app.DeleteAuthorities(txn); err != nil {
		return err
	}
	if err := app.DeleteFromDB(txn); err != nil {
		return err
	}
	return app.DeleteFromGoogleDrive(s)
}

func (app *App) DeleteBundles(txn gorp.SqlExecutor) error {
	bundles, err := app.Bundles(txn)
	if err != nil {
		return err
	}

	args := make([]interface{}, len(bundles))
	for i, bundle := range bundles {
		args[i] = bundle
	}

	_, err = txn.Delete(args...)
	return err
}

func (app *App) DeleteAuthority(txn gorp.SqlExecutor, s *GoogleService, authority *Authority) error {
	if err := authority.DeleteFromDB(txn); err != nil {
		return err
	}

	return s.DeletePermission(app.FileId, authority.PermissionId)
}

func (app *App) DeleteAuthorities(txn gorp.SqlExecutor) error {
	authorities, err := app.Authorities(txn)
	if err != nil {
		return err
	}

	args := make([]interface{}, len(authorities))
	for i, authority := range authorities {
		args[i] = authority
	}

	_, err = txn.Delete(args...)
	return err
}

func (app *App) HasAuthorityForEmail(txn gorp.SqlExecutor, email string) (bool, error) {
	count, err := txn.SelectInt("SELECT COUNT(id) FROM authority WHERE app_id = ? AND email = ?", app.Id, email)
	if err != nil {
		return false, err
	}
	if count > 0 {
		return true, nil
	}
	return false, nil
}

func (app *App) ParentReference() *drive.ParentReference {
	return &drive.ParentReference{
		Id: app.FileId,
	}
}

func (app *App) CreateBundle(dbm *gorp.DbMap, s *GoogleService, bundle *Bundle) error {
	bundle.AppId = app.Id

	bundleInfo, err := NewBundleInfo(bundle.File, bundle.PlatformType)
	if err != nil {
		return err
	}
	if len(bundleInfo.Version) == 0 {
		return &BundleParseError{}
	}
	bundle.BundleInfo = bundleInfo

	// increment revision number & save application information
	err = Transact(dbm, func(txn gorp.SqlExecutor) error {
		maxRevision, err := app.GetMaxRevisionByBundleVersion(txn, bundle.Version)
		if err != nil {
			return err
		}
		bundle.Revision = maxRevision + 1
		bundle.FileName = bundle.BuildFileName()
		return bundle.Save(txn)
	})
	if err != nil {
		panic(err)
	}
	bundle.Revision = maxRevision + 1
	bundle.FileName = bundle.BuildFileName()

	// upload file
	parent := app.ParentReference()
	driveFile, err := s.InsertFile(bundle.File, bundle.FileName, parent)
	if err != nil {
		return err
	}

	// update FileId
	bundle.FileId = driveFile.Id
	return Transact(dbm, func(txn gorp.SqlExecutor) error {
		return bundle.Update(txn)
	})
}

func (app *App) CreateAuthority(txn gorp.SqlExecutor, s *GoogleService, authority *Authority) error {
	authority.AppId = app.Id

	permission := s.CreateUserPermission(authority.Email, "reader")
	permissionInserted, err := s.InsertPermission(app.FileId, permission)
	if err != nil {
		return err
	}
	authority.PermissionId = permissionInserted.Id

	return authority.Save(txn)
}

func CreateApp(txn gorp.SqlExecutor, s *GoogleService, app *App) error {
	driveFolder, err := s.CreateFolder(app.Title)
	if err != nil {
		return err
	}
	app.FileId = driveFolder.Id

	return app.Save(txn)
}

func GetApp(txn gorp.SqlExecutor, id int) (*App, error) {
	app, err := txn.Get(App{}, id)
	if err != nil {
		return nil, err
	}
	return app.(*App), nil
}

func GetApps(txn gorp.SqlExecutor, fileIds []string) ([]*App, error) {
	if len(fileIds) <= 0 {
		return []*App{}, nil
	}

	args := make([]interface{}, len(fileIds))
	quarks := make([]string, len(fileIds))
	for i, fileId := range fileIds {
		args[i] = fileId
		quarks[i] = "?"
	}

	var apps []*App
	_, err := txn.Select(&apps, fmt.Sprintf("SELECT * FROM app WHERE file_id in (%s) ORDER BY id DESC", strings.Join(quarks, ",")), args...)
	if err != nil {
		return nil, err
	}

	return apps, nil
}
<commit_msg>うまく merge できていなかったところを修正<commit_after>package models

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"strings"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"code.google.com/p/google-api-go-client/drive/v2"

	"github.com/coopernurse/gorp"
)

// https://github.com/coopernurse/gorp#mapping-structs-to-tables
type App struct {
	Id          int       `db:"id"`
	Title       string    `db:"title"`
	FileId      string    `db:"file_id"`
	ApiToken    string    `db:"api_token"`
	Description string    `db:"description"`
	CreatedAt   time.Time `db:"created_at"`
	UpdatedAt   time.Time `db:"updated_at"`
}

func GetAppByApiToken(txn gorp.SqlExecutor, apiToken string) (*App, error) {
	var app App
	err := txn.SelectOne(&app, "SELECT * FROM app where api_token = ?", apiToken)

	if err != nil {
		return nil, err
	}

	return &app, nil
}

func (app *App) Bundles(txn gorp.SqlExecutor) ([]*Bundle, error) {
	var bundles []*Bundle
	_, err := txn.Select(&bundles, "SELECT * FROM bundle WHERE app_id = ? ORDER BY id DESC", app.Id)
	if err != nil {
		return nil, err
	}
	return bundles, nil
}

func (app *App) BundlesByPlatformType(txn gorp.SqlExecutor, platformType BundlePlatformType) ([]*Bundle, error) {
	var bundles []*Bundle
	_, err := txn.Select(&bundles, "SELECT * FROM bundle WHERE app_id = ? AND platform_type = ? ORDER BY id DESC", app.Id, platformType)
	if err != nil {
		return nil, err
	}
	return bundles, nil
}

func (app *App) Authorities(txn gorp.SqlExecutor) ([]*Authority, error) {
	var authorities []*Authority
	_, err := txn.Select(&authorities, "SELECT * FROM authority WHERE app_id = ? ORDER BY id ASC", app.Id)
	if err != nil {
		return nil, err
	}
	return authorities, nil
}

func (app *App) GetMaxRevisionByBundleVersion(txn gorp.SqlExecutor, bundleVersion string) (int, error) {
	revision, err := txn.SelectInt(
		"SELECT IFNULL(MAX(revision), 0) FROM bundle WHERE app_id = ? AND bundle_version = ?",
		app.Id,
		bundleVersion,
	)
	return int(revision), err
}

func NewToken() string {
	uuid := uuid.NewRandom()
	mac := hmac.New(sha256.New, nil)
	mac.Write([]byte(uuid.String()))
	tokenBytes := mac.Sum(nil)
	return hex.EncodeToString(tokenBytes)
}

// https://github.com/coopernurse/gorp#hooks
func (app *App) PreInsert(s gorp.SqlExecutor) error {
	app.CreatedAt = time.Now()
	app.UpdatedAt = app.CreatedAt
	app.ApiToken = NewToken()
	return nil
}

func (app *App) PreUpdate(s gorp.SqlExecutor) error {
	app.UpdatedAt = time.Now()
	return nil
}

func (app *App) Save(txn gorp.SqlExecutor) error {
	return txn.Insert(app)
}

func (app *App) RefreshToken(txn gorp.SqlExecutor) error {
	current, err := GetApp(txn, app.Id)
	if err != nil {
		return err
	}

	current.ApiToken = NewToken()

	_, err = txn.Update(current)
	return err
}

func (app *App) Update(txn gorp.SqlExecutor) error {
	current, err := GetApp(txn, app.Id)
	if err != nil {
		return err
	}

	current.Title = app.Title
	current.Description = app.Description

	_, err = txn.Update(current)
	return err
}

func (app *App) DeleteFromDB(txn gorp.SqlExecutor) error {
	_, err := txn.Delete(app)
	return err
}

func (app *App) DeleteFromGoogleDrive(s *GoogleService) error {
	return s.DeleteFile(app.FileId)
}

func (app *App) Delete(txn gorp.SqlExecutor, s *GoogleService) error {
	if err := app.DeleteBundles(txn); err != nil {
		return err
	}
	if err := app.DeleteAuthorities(txn); err != nil {
		return err
	}
	if err := app.DeleteFromDB(txn); err != nil {
		return err
	}
	return app.DeleteFromGoogleDrive(s)
}

func (app *App) DeleteBundles(txn gorp.SqlExecutor) error {
	bundles, err := app.Bundles(txn)
	if err != nil {
		return err
	}

	args := make([]interface{}, len(bundles))
	for i, bundle := range bundles {
		args[i] = bundle
	}

	_, err = txn.Delete(args...)
	return err
}

func (app *App) DeleteAuthority(txn gorp.SqlExecutor, s *GoogleService, authority *Authority) error {
	if err := authority.DeleteFromDB(txn); err != nil {
		return err
	}

	return s.DeletePermission(app.FileId, authority.PermissionId)
}

func (app *App) DeleteAuthorities(txn gorp.SqlExecutor) error {
	authorities, err := app.Authorities(txn)
	if err != nil {
		return err
	}

	args := make([]interface{}, len(authorities))
	for i, authority := range authorities {
		args[i] = authority
	}

	_, err = txn.Delete(args...)
	return err
}

func (app *App) HasAuthorityForEmail(txn gorp.SqlExecutor, email string) (bool, error) {
	count, err := txn.SelectInt("SELECT COUNT(id) FROM authority WHERE app_id = ? AND email = ?", app.Id, email)
	if err != nil {
		return false, err
	}
	if count > 0 {
		return true, nil
	}
	return false, nil
}

func (app *App) ParentReference() *drive.ParentReference {
	return &drive.ParentReference{
		Id: app.FileId,
	}
}

func (app *App) CreateBundle(dbm *gorp.DbMap, s *GoogleService, bundle *Bundle) error {
	bundle.AppId = app.Id

	bundleInfo, err := NewBundleInfo(bundle.File, bundle.PlatformType)
	if err != nil {
		return err
	}
	if len(bundleInfo.Version) == 0 {
		return &BundleParseError{}
	}
	bundle.BundleInfo = bundleInfo

	// increment revision number & save application information
	err = Transact(dbm, func(txn gorp.SqlExecutor) error {
		maxRevision, err := app.GetMaxRevisionByBundleVersion(txn, bundle.BundleVersion)
		if err != nil {
			return err
		}
		bundle.Revision = maxRevision + 1
		bundle.FileName = bundle.BuildFileName()
		return bundle.Save(txn)
	})
	if err != nil {
		panic(err)
	}

	// upload file
	parent := app.ParentReference()
	driveFile, err := s.InsertFile(bundle.File, bundle.FileName, parent)
	if err != nil {
		return err
	}

	// update FileId
	bundle.FileId = driveFile.Id
	return Transact(dbm, func(txn gorp.SqlExecutor) error {
		return bundle.Update(txn)
	})
}

func (app *App) CreateAuthority(txn gorp.SqlExecutor, s *GoogleService, authority *Authority) error {
	authority.AppId = app.Id

	permission := s.CreateUserPermission(authority.Email, "reader")
	permissionInserted, err := s.InsertPermission(app.FileId, permission)
	if err != nil {
		return err
	}
	authority.PermissionId = permissionInserted.Id

	return authority.Save(txn)
}

func CreateApp(txn gorp.SqlExecutor, s *GoogleService, app *App) error {
	driveFolder, err := s.CreateFolder(app.Title)
	if err != nil {
		return err
	}
	app.FileId = driveFolder.Id

	return app.Save(txn)
}

func GetApp(txn gorp.SqlExecutor, id int) (*App, error) {
	app, err := txn.Get(App{}, id)
	if err != nil {
		return nil, err
	}
	return app.(*App), nil
}

func GetApps(txn gorp.SqlExecutor, fileIds []string) ([]*App, error) {
	if len(fileIds) <= 0 {
		return []*App{}, nil
	}

	args := make([]interface{}, len(fileIds))
	quarks := make([]string, len(fileIds))
	for i, fileId := range fileIds {
		args[i] = fileId
		quarks[i] = "?"
	}

	var apps []*App
	_, err := txn.Select(&apps, fmt.Sprintf("SELECT * FROM app WHERE file_id in (%s) ORDER BY id DESC", strings.Join(quarks, ",")), args...)
	if err != nil {
		return nil, err
	}

	return apps, nil
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"

	"github.com/Zamiell/isaac-racing-server/src/log"
	"github.com/gin-gonic/gin"
)

/*
	Data types
*/

/*
type LeaderboardSeeded []models.LeaderboardRowSeeded

func (l LeaderboardSeeded) Len() int {
	return len(s)
}
func (l LeaderboardSeeded) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (l LeaderboardSeeded) Less(i, j int) bool {
	return len(s[i]) < len(s[j])
}

type LeaderboardUnseeded []models.LeaderboardRowUnseeded

*/

/*
	HTTP handlers
*/

func httpLeaderboards(c *gin.Context) {
	// Local variables
	w := c.Writer

	/*
		leaderboardSeeded, err := db.Users.GetLeaderboardSeeded()
		if err != nil {
			log.Error("Failed to get the seeded leaderboard:", err)
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
			return
		}
	*/
	leaderboardUnseeded, err := db.Users.GetLeaderboardUnseeded()
	if err != nil {
		log.Error("Failed to get the unseeded leaderboard:", err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	// Construct the "Top 10 Unseeded Times" leaderboard
	/*var leaderboardTop10Times string
	for _, row := range leaderboardUnseeded {

	}*/

	// Construct the "Most Races Played" leaderboard
	// TODO

	data := TemplateData{
		Title:               "Leaderboards",
		LeaderboardUnseeded: leaderboardUnseeded,
	}

	httpServeTemplate(w, "leaderboards", data)
}
<commit_msg>dont need this anymore<commit_after>package main

import (
	"net/http"

	"github.com/Zamiell/isaac-racing-server/src/log"
	"github.com/gin-gonic/gin"
)

/*
	Data types
*/

/*
type LeaderboardSeeded []models.LeaderboardRowSeeded

func (l LeaderboardSeeded) Len() int {
	return len(s)
}
func (l LeaderboardSeeded) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (l LeaderboardSeeded) Less(i, j int) bool {
	return len(s[i]) < len(s[j])
}

type LeaderboardUnseeded []models.LeaderboardRowUnseeded

*/

/*
	HTTP handlers
*/

func httpLeaderboards(c *gin.Context) {
	// Local variables
	w := c.Writer

	/*
		leaderboardSeeded, err := db.Users.GetLeaderboardSeeded()
		if err != nil {
			log.Error("Failed to get the seeded leaderboard:", err)
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
			return
		}
	*/
	leaderboardUnseeded, err := db.Users.GetLeaderboardUnseeded()
	if err != nil {
		log.Error("Failed to get the unseeded leaderboard:", err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	// Construct the "Most Races Played" leaderboard
	// TODO

	data := TemplateData{
		Title:               "Leaderboards",
		LeaderboardUnseeded: leaderboardUnseeded,
	}

	httpServeTemplate(w, "leaderboards", data)
}
<|endoftext|>


<commit_before>package librariesio

import (
	"context"
	"fmt"
	"net/http"
	"reflect"
	"strings"
	"testing"

	"github.com/hackebrot/go-repr/repr"
)

func TestUser(t *testing.T) {
	server, mux, url := startNewServer()
	client := NewClient(APIKey)
	client.BaseURL = url
	defer server.Close()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if method := "GET"; method != r.Method {
			t.Errorf("expected HTTP %v request, got %v", method, r.Method)
		}

		if url := r.URL.String(); !strings.Contains(url, "/github/hackebrot") {
			t.Errorf("unexpected URL, got %v", url)
		}

		fmt.Fprintf(w, `{
			"login":"hackebrot",
			"name":"Raphael Pierzina",
			"blog":"https://raphael.codes"
		}`)
	})

	user, _, err := client.User(context.Background(), "hackebrot")
	if err != nil {
		t.Fatalf("User returned unexpected error: %v", err)
	}

	want := &User{
		Name:  String("Raphael Pierzina"),
		Login: String("hackebrot"),
		Blog:  String("https://raphael.codes"),
	}

	if !reflect.DeepEqual(user, want) {
		t.Errorf("\nExpected %v\nGot %v", repr.Repr(want), repr.Repr(user))
	}
}
<commit_msg>Implement a unit test for UserProjects<commit_after>package librariesio

import (
	"context"
	"fmt"
	"net/http"
	"reflect"
	"strings"
	"testing"

	"github.com/hackebrot/go-repr/repr"
)

func TestUser(t *testing.T) {
	server, mux, url := startNewServer()
	client := NewClient(APIKey)
	client.BaseURL = url
	defer server.Close()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if method := "GET"; method != r.Method {
			t.Errorf("expected HTTP %v request, got %v", method, r.Method)
		}

		if url := r.URL.String(); !strings.Contains(url, "/github/hackebrot") {
			t.Errorf("unexpected URL, got %v", url)
		}

		fmt.Fprintf(w, `{
			"login":"hackebrot",
			"name":"Raphael Pierzina",
			"blog":"https://raphael.codes"
		}`)
	})

	user, _, err := client.User(context.Background(), "hackebrot")
	if err != nil {
		t.Fatalf("User returned unexpected error: %v", err)
	}

	want := &User{
		Name:  String("Raphael Pierzina"),
		Login: String("hackebrot"),
		Blog:  String("https://raphael.codes"),
	}

	if !reflect.DeepEqual(user, want) {
		t.Errorf("\nExpected %v\nGot %v", repr.Repr(want), repr.Repr(user))
	}
}

func TestUserProjects(t *testing.T) {
	server, mux, url := startNewServer()
	client := NewClient(APIKey)
	client.BaseURL = url
	defer server.Close()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if method := "GET"; method != r.Method {
			t.Errorf("expected HTTP %v request, got %v", method, r.Method)
		}

		if url := r.URL.String(); !strings.Contains(url, "/github/hackebrot/projects") {
			t.Errorf("unexpected URL, got %v", url)
		}

		fmt.Fprintf(w, `[
			{
				"name":"go-repr",
				"keywords": ["go", "golang", "helper"],
				"repository_url": "https://github.com/hackebrot/go-repr"
			},
			{
				"name":"go-librariesio",
				"keywords": ["api-client", "go", "golang"],
				"repository_url": "https://github.com/hackebrot/go-librariesio"
			}
		]`)
	})

	projects, _, err := client.UserProjects(context.Background(), "hackebrot")

	if err != nil {
		t.Fatalf("UserProjects returned unexpected error: %v", err)
	}

	want := []*Project{
		{
			Name: String("go-repr"),
			Keywords: []*string{
				String("go"),
				String("golang"),
				String("helper"),
			},
			RepositoryURL: String("https://github.com/hackebrot/go-repr"),
		},
		{
			Name: String("go-librariesio"),
			Keywords: []*string{
				String("api-client"),
				String("go"),
				String("golang"),
			},
			RepositoryURL: String("https://github.com/hackebrot/go-librariesio"),
		},
	}

	if !reflect.DeepEqual(projects, want) {
		t.Errorf("\nExpected %v\nGot %v", repr.Repr(want), repr.Repr(projects))
	}
}
<|endoftext|>


<commit_before>package dma

import (
	"unsafe"
)

type DMA dmaperiph

func (p *DMA) EnableClock(lp bool) {
	p.enableClock(lp)
}

func (p *DMA) DisableClock() {
	p.disableClock()
}

func (p *DMA) Reset() {
	p.reset()
}

// Channel returns value that represents sn-th stream (channel in F1/L1 series
// nomenclature) with cn-th request channel set (ignored in case of F1/L1
// series). Channels with the same sn points to the same DMA stream so they can
// not be used concurently.
func (p *DMA) Channel(sn, cn int) *Channel {
	return p.getChannel(sn, cn)
}

type Channel channel

type Event byte

const (
	Complete     Event = trce // Transfer Complete Event.
	HalfComplete Event = htce // Half Transfer Complete Event.

	EvAll = Complete | HalfComplete
)

type Error byte

const (
	ErrTransfer   Error = trerr // Transfer Error.
	ErrDirectMode Error = dmerr // Direct Mode Error.
	ErrFIFO       Error = fferr // FIFO Error.

	ErrAll = ErrTransfer | ErrDirectMode | ErrFIFO
)

func (e Error) Error() string {
	var (
		s string
		d Error
	)
	switch {
	case e&ErrTransfer != 0:
		d = ErrTransfer
		s = "DMA transfer+"
	case e&ErrDirectMode != 0:
		d = ErrDirectMode
		s = "DMA direct mode+"
	case e&ErrFIFO != 0:
		d = ErrFIFO
		s = "DMA FIFO+"
	default:
		return ""
	}
	if e&^d == 0 {
		s = s[:len(s)-1]
	}
	return s
}

// Status returns current event and error flags.
func (ch *Channel) Status() (Event, Error) {
	flags := ch.status()
	return Event(flags) & EvAll, Error(flags) & ErrAll
}

// ClearEvents clears specified event flags.
func (ch *Channel) Clear(ev Event, err Error) {
	ch.clear(byte(ev) | byte(err))
}

// Enable enables the channel ch. All events and errors should be cleared
// before call this method.
func (ch *Channel) Enable() {
	ch.enable()
}

// Disable disables channel.
func (ch *Channel) Disable() {
	ch.disable()
}

// Returns true if channel is enabled.
func (ch *Channel) Enabled() bool {
	return ch.enabled()
}

// IRQEnabled returns events that are enabled to generate interrupt requests.
func (ch *Channel) IRQEnabled() (Event, Error) {
	flags := ch.irqEnabled()
	return Event(flags) & EvAll, Error(flags) & ErrAll
}

// EnableIRQ enables generation of IRQs by ev, err. Documentation does not
// mention it, but IRQ can be not generated if an event was asserted before
// enable IRQ for it. So always enable IRQs before channel. Typically, the
// correct sequence is as follows:
//	ch.Clear(EvAll, ErrAll)
//	ch.EnableIRQ(ev, err)
//	ch.Enable()
func (ch *Channel) EnableIRQ(ev Event, err Error) {
	ch.enableIRQ(byte(ev) | byte(err))
}

// DisableIRQ disables IRQs generation by ev, err.
func (ch *Channel) DisableIRQ(ev Event, err Error) {
	ch.disableIRQ(byte(ev) | byte(err))
}

type Mode uint32

const (
	PTM Mode = 0   // Read from peripheral, write to memory.
	MTP Mode = mtp // Read from memory, write to peripheral.
	MTM Mode = mtm // Read from memory (AddrP), write to memory.

	Circ Mode = circ // Enable circular mode.
	IncP Mode = incP // Peripheral increment mode.
	IncM Mode = incM // Memory increment mode.

	Direct   Mode = 0        // Direct mode.
	FIFO_1_4 Mode = fifo_1_4 // FIFO mode, threshold 1/4.
	FIFO_2_4 Mode = fifo_2_4 // FIFO mode, threshold 2/4.
	FIFO_3_4 Mode = fifo_3_4 // FIFO mode, threshold 3/4.
	FIFO_4_4 Mode = fifo_4_4 // FIFO mode, threshold 4/4.
)

// Setup configures channel.
func (ch *Channel) Setup(m Mode) {
	ch.setup(m)
}

type Prio byte

const (
	Low      Prio = 0     // Stream priority level: Low.
	Medium   Prio = prioM // Stream priority level: Medium.
	High     Prio = prioH // Stream priority level: High.
	VeryHigh Prio = prioV // Stream priority level: Very high.
)

func (ch *Channel) SetPrio(prio Prio) {
	ch.setPrio(prio)
}

func (ch *Channel) Prio() Prio {
	return ch.prio()
}

// WordSize returns the current word size (in bytes) for peripheral and memory
// side of transfer.
func (ch *Channel) WordSize() (p, m uintptr) {
	return ch.wordSize()
}

// SetWordSize sets the word size (in bytes) for peripheral and memory side of
// transfer.
func (ch *Channel) SetWordSize(p, m uintptr) {
	ch.setWordSize(p, m)
}

// Len returns current number of words to transfer.
func (ch *Channel) Len() int {
	return ch.len()
}

// SetLen sets number of words to transfer (n <= 65535).
func (ch *Channel) SetLen(n int) {
	ch.setLen(n)
}

// SetAddrP sets peripheral address (or memory source address in case of MTM).
func (ch *Channel) SetAddrP(a unsafe.Pointer) {
	ch.setAddrP(a)
}

// SetAddrM sets memory address.
func (ch *Channel) SetAddrM(a unsafe.Pointer) {
	ch.setAddrM(a)
}

// Request represents request number for DMA channel.
type Request int8

// Request returns current request source.
func (ch *Channel) Request() Request {
	return ch.request()
}

// SetRequest selects request source for channel.
func (ch *Channel) SetRequest(req Request) {
	ch.setRequest(req)
}
<commit_msg>stm32/hal/dma: Fix Channel.Clear doc.<commit_after>package dma

import (
	"unsafe"
)

type DMA dmaperiph

func (p *DMA) EnableClock(lp bool) {
	p.enableClock(lp)
}

func (p *DMA) DisableClock() {
	p.disableClock()
}

func (p *DMA) Reset() {
	p.reset()
}

// Channel returns value that represents sn-th stream (channel in F1/L1 series
// nomenclature) with cn-th request channel set (ignored in case of F1/L1
// series). Channels with the same sn points to the same DMA stream so they can
// not be used concurently.
func (p *DMA) Channel(sn, cn int) *Channel {
	return p.getChannel(sn, cn)
}

type Channel channel

type Event byte

const (
	Complete     Event = trce // Transfer Complete Event.
	HalfComplete Event = htce // Half Transfer Complete Event.

	EvAll = Complete | HalfComplete
)

type Error byte

const (
	ErrTransfer   Error = trerr // Transfer Error.
	ErrDirectMode Error = dmerr // Direct Mode Error.
	ErrFIFO       Error = fferr // FIFO Error.

	ErrAll = ErrTransfer | ErrDirectMode | ErrFIFO
)

func (e Error) Error() string {
	var (
		s string
		d Error
	)
	switch {
	case e&ErrTransfer != 0:
		d = ErrTransfer
		s = "DMA transfer+"
	case e&ErrDirectMode != 0:
		d = ErrDirectMode
		s = "DMA direct mode+"
	case e&ErrFIFO != 0:
		d = ErrFIFO
		s = "DMA FIFO+"
	default:
		return ""
	}
	if e&^d == 0 {
		s = s[:len(s)-1]
	}
	return s
}

// Status returns current event and error flags.
func (ch *Channel) Status() (Event, Error) {
	flags := ch.status()
	return Event(flags) & EvAll, Error(flags) & ErrAll
}

// Clear clears specified flags.
func (ch *Channel) Clear(ev Event, err Error) {
	ch.clear(byte(ev) | byte(err))
}

// Enable enables the channel ch. All events and errors should be cleared
// before call this method.
func (ch *Channel) Enable() {
	ch.enable()
}

// Disable disables channel.
func (ch *Channel) Disable() {
	ch.disable()
}

// Returns true if channel is enabled.
func (ch *Channel) Enabled() bool {
	return ch.enabled()
}

// IRQEnabled returns events that are enabled to generate interrupt requests.
func (ch *Channel) IRQEnabled() (Event, Error) {
	flags := ch.irqEnabled()
	return Event(flags) & EvAll, Error(flags) & ErrAll
}

// EnableIRQ enables generation of IRQs by ev, err. Documentation does not
// mention it, but IRQ can be not generated if an event was asserted before
// enable IRQ for it. So always enable IRQs before channel. Typically, the
// correct sequence is as follows:
//	ch.Clear(EvAll, ErrAll)
//	ch.EnableIRQ(ev, err)
//	ch.Enable()
func (ch *Channel) EnableIRQ(ev Event, err Error) {
	ch.enableIRQ(byte(ev) | byte(err))
}

// DisableIRQ disables IRQs generation by ev, err.
func (ch *Channel) DisableIRQ(ev Event, err Error) {
	ch.disableIRQ(byte(ev) | byte(err))
}

type Mode uint32

const (
	PTM Mode = 0   // Read from peripheral, write to memory.
	MTP Mode = mtp // Read from memory, write to peripheral.
	MTM Mode = mtm // Read from memory (AddrP), write to memory.

	Circ Mode = circ // Enable circular mode.
	IncP Mode = incP // Peripheral increment mode.
	IncM Mode = incM // Memory increment mode.

	Direct   Mode = 0        // Direct mode.
	FIFO_1_4 Mode = fifo_1_4 // FIFO mode, threshold 1/4.
	FIFO_2_4 Mode = fifo_2_4 // FIFO mode, threshold 2/4.
	FIFO_3_4 Mode = fifo_3_4 // FIFO mode, threshold 3/4.
	FIFO_4_4 Mode = fifo_4_4 // FIFO mode, threshold 4/4.
)

// Setup configures channel.
func (ch *Channel) Setup(m Mode) {
	ch.setup(m)
}

type Prio byte

const (
	Low      Prio = 0     // Stream priority level: Low.
	Medium   Prio = prioM // Stream priority level: Medium.
	High     Prio = prioH // Stream priority level: High.
	VeryHigh Prio = prioV // Stream priority level: Very high.
)

func (ch *Channel) SetPrio(prio Prio) {
	ch.setPrio(prio)
}

func (ch *Channel) Prio() Prio {
	return ch.prio()
}

// WordSize returns the current word size (in bytes) for peripheral and memory
// side of transfer.
func (ch *Channel) WordSize() (p, m uintptr) {
	return ch.wordSize()
}

// SetWordSize sets the word size (in bytes) for peripheral and memory side of
// transfer.
func (ch *Channel) SetWordSize(p, m uintptr) {
	ch.setWordSize(p, m)
}

// Len returns current number of words to transfer.
func (ch *Channel) Len() int {
	return ch.len()
}

// SetLen sets number of words to transfer (n <= 65535).
func (ch *Channel) SetLen(n int) {
	ch.setLen(n)
}

// SetAddrP sets peripheral address (or memory source address in case of MTM).
func (ch *Channel) SetAddrP(a unsafe.Pointer) {
	ch.setAddrP(a)
}

// SetAddrM sets memory address.
func (ch *Channel) SetAddrM(a unsafe.Pointer) {
	ch.setAddrM(a)
}

// Request represents request number for DMA channel.
type Request int8

// Request returns current request source.
func (ch *Channel) Request() Request {
	return ch.request()
}

// SetRequest selects request source for channel.
func (ch *Channel) SetRequest(req Request) {
	ch.setRequest(req)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"crypto/rand"
	"database/sql"
	"encoding/hex"
	"fmt"
	"github.com/ChimeraCoder/anaconda"
	"github.com/bradfitz/gomemcache/memcache"
	"github.com/bradleypeabody/gorilla-sessions-memcache"
	"github.com/garyburd/go-oauth/oauth"
	"github.com/gorilla/sessions"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/standard"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"
)
import _ "github.com/go-sql-driver/mysql"

var store = func() *gsm.MemcacheStore {
	var memcacheClient = memcache.New("localhost:11211")
	var store = gsm.NewMemcacheStore(memcacheClient, "session_prefix_", []byte("secret-key-goes-here"))
	store.Options = &sessions.Options{
		MaxAge:   0,
		Path:     "/signin/",
		Secure:   true,
		HttpOnly: true,
	}
	store.StoreMethod = gsm.StoreMethodGob
	return store
}()

const sessionName = "justaway_session"
const dbSource = "justaway@tcp(192.168.0.10:3306)/justaway"

func signin(c echo.Context) error {
	url, tempCred, err := anaconda.AuthorizationURL("https://justaway.info/signin/callback")

	if err != nil {
		return c.String(200, err.Error())
	}

	session, _ := store.Get(c.Request().(*standard.Request).Request, sessionName)
	session.Values["request_token"] = tempCred.Token
	session.Values["request_secret"] = tempCred.Secret
	session.Save(c.Request().(*standard.Request).Request, c.Response().(*standard.Response).ResponseWriter)

	return c.Redirect(http.StatusTemporaryRedirect, url)
}

func callback(c echo.Context) error {
	session, _ := store.Get(c.Request().(*standard.Request).Request, sessionName)
	token := session.Values["request_token"]
	secret := session.Values["request_secret"]
	tempCred := oauth.Credentials{
		Token:  token.(string),
		Secret: secret.(string),
	}
	cred, _, err := anaconda.GetCredentials(&tempCred, c.QueryParam("oauth_verifier"))
	if err != nil {
		return c.String(200, err.Error())
	}

	session.Values["access_token"] = cred.Token
	session.Values["access_secret"] = cred.Secret
	session.Save(c.Request().(*standard.Request).Request, c.Response().(*standard.Response).ResponseWriter)

	api := anaconda.NewTwitterApi(cred.Token, cred.Secret)

	v := url.Values{}
	v.Set("include_entities", "false")
	v.Set("skip_status", "true")
	user, err := api.GetSelf(v)

	now := time.Now()
	fmt.Printf("callback user_id:%s screen_name:%s name:%s\n", user.Id, user.ScreenName, user.Name)

	db, err := sql.Open("mysql", dbSource)
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	stmtIns, err := db.Prepare(`
		INSERT INTO account(
			crawler_id,
			user_id,
			name,
			screen_name,
			api_token,
			access_token,
			access_token_secret,
			status,
			created_on,
			updated_on
		) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
			name = VALUES(name),
			screen_name = VALUES(screen_name),
			api_token = VALUES(api_token),
			access_token = VALUES(access_token),
			access_token_secret = VALUES(access_token_secret),
			status = VALUES(status),
			updated_on = ?
	`)
	if err != nil {
		panic(err.Error())
	}
	defer stmtIns.Close()

	apiToken := makeToken()

	_, err = stmtIns.Exec(1, user.Id, user.Name, user.ScreenName, apiToken, cred.Token, cred.Secret, "ACTIVE", now.Unix(), 0, now.Unix())
	if err != nil {
		panic(err.Error())
	}

	req, _ := http.NewRequest("GET", "http://127.0.0.1:8001/"+user.IdStr+"/start", nil)
	client := new(http.Client)
	resp, _ := client.Do(req)
	defer resp.Body.Close()
	byteArray, _ := ioutil.ReadAll(resp.Body)
	fmt.Printf("request user_id:%s screen_name:%s res:%s\n", user.IdStr, user.ScreenName, string(byteArray))

	return c.Render(http.StatusOK, "index", user.IdStr+"-"+apiToken)
}

func makeToken() string {
	b := make([]byte, 32)
	rand.Read(b)
	hasher := md5.New()
	hasher.Write(b)
	return hex.EncodeToString(hasher.Sum(nil))
}

func activity(c echo.Context) error {
	apiToken := c.Request().Header().Get("X-Justaway-API-Token")
	if apiToken == "" {
		return c.String(401, "Missing X-Justaway-API-Token header")
	}

	db, err := sql.Open("mysql", dbSource)
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	stmtOut, err := db.Prepare("SELECT user_id FROM account WHERE api_token = ?")
	if err != nil {
		panic(err.Error())
	}
	defer stmtOut.Close()

	var userIdStr string

	err = stmtOut.QueryRow(apiToken).Scan(&userIdStr)
	if err != nil {
		return c.String(401, "Invalid X-Justaway-API-Token header")
	}

	stmtData, err := db.Prepare("SELECT id, data FROM activity WHERE user_id = ? ORDER BY id DESC LIMIT 200")
	if err != nil {
		panic(err.Error())
	}
	defer stmtData.Close()

	rows, err := stmtData.Query(userIdStr)
	if err != nil {
		panic(err.Error())
	}
	defer rows.Close()

	maxIdStr := "null"
	minIdStr := "null"
	events := ""
	count := 0
	for rows.Next() {
		count++
		var id string
		var data string
		err = rows.Scan(&id, &data)
		if err != nil {
			panic(err.Error())
		}
		if events == "" {
			events = data
		} else {
			events = events + "," + data
		}
		if maxIdStr == "null" {
			maxIdStr = "\""+id+"\""
		}
		minIdStr = "\""+id+"\""
	}

	return c.String(200, "{\"events\":["+events+"],\"max_id_str\":"+maxIdStr+",\"min_id_str\":"+minIdStr+"}")
}
<commit_msg>imported and not used: "strconv"<commit_after>package main

import (
	"crypto/md5"
	"crypto/rand"
	"database/sql"
	"encoding/hex"
	"fmt"
	"github.com/ChimeraCoder/anaconda"
	"github.com/bradfitz/gomemcache/memcache"
	"github.com/bradleypeabody/gorilla-sessions-memcache"
	"github.com/garyburd/go-oauth/oauth"
	"github.com/gorilla/sessions"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/standard"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)
import _ "github.com/go-sql-driver/mysql"

var store = func() *gsm.MemcacheStore {
	var memcacheClient = memcache.New("localhost:11211")
	var store = gsm.NewMemcacheStore(memcacheClient, "session_prefix_", []byte("secret-key-goes-here"))
	store.Options = &sessions.Options{
		MaxAge:   0,
		Path:     "/signin/",
		Secure:   true,
		HttpOnly: true,
	}
	store.StoreMethod = gsm.StoreMethodGob
	return store
}()

const sessionName = "justaway_session"
const dbSource = "justaway@tcp(192.168.0.10:3306)/justaway"

func signin(c echo.Context) error {
	url, tempCred, err := anaconda.AuthorizationURL("https://justaway.info/signin/callback")

	if err != nil {
		return c.String(200, err.Error())
	}

	session, _ := store.Get(c.Request().(*standard.Request).Request, sessionName)
	session.Values["request_token"] = tempCred.Token
	session.Values["request_secret"] = tempCred.Secret
	session.Save(c.Request().(*standard.Request).Request, c.Response().(*standard.Response).ResponseWriter)

	return c.Redirect(http.StatusTemporaryRedirect, url)
}

func callback(c echo.Context) error {
	session, _ := store.Get(c.Request().(*standard.Request).Request, sessionName)
	token := session.Values["request_token"]
	secret := session.Values["request_secret"]
	tempCred := oauth.Credentials{
		Token:  token.(string),
		Secret: secret.(string),
	}
	cred, _, err := anaconda.GetCredentials(&tempCred, c.QueryParam("oauth_verifier"))
	if err != nil {
		return c.String(200, err.Error())
	}

	session.Values["access_token"] = cred.Token
	session.Values["access_secret"] = cred.Secret
	session.Save(c.Request().(*standard.Request).Request, c.Response().(*standard.Response).ResponseWriter)

	api := anaconda.NewTwitterApi(cred.Token, cred.Secret)

	v := url.Values{}
	v.Set("include_entities", "false")
	v.Set("skip_status", "true")
	user, err := api.GetSelf(v)

	now := time.Now()
	fmt.Printf("callback user_id:%s screen_name:%s name:%s\n", user.Id, user.ScreenName, user.Name)

	db, err := sql.Open("mysql", dbSource)
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	stmtIns, err := db.Prepare(`
		INSERT INTO account(
			crawler_id,
			user_id,
			name,
			screen_name,
			api_token,
			access_token,
			access_token_secret,
			status,
			created_on,
			updated_on
		) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE
			name = VALUES(name),
			screen_name = VALUES(screen_name),
			api_token = VALUES(api_token),
			access_token = VALUES(access_token),
			access_token_secret = VALUES(access_token_secret),
			status = VALUES(status),
			updated_on = ?
	`)
	if err != nil {
		panic(err.Error())
	}
	defer stmtIns.Close()

	apiToken := makeToken()

	_, err = stmtIns.Exec(1, user.Id, user.Name, user.ScreenName, apiToken, cred.Token, cred.Secret, "ACTIVE", now.Unix(), 0, now.Unix())
	if err != nil {
		panic(err.Error())
	}

	req, _ := http.NewRequest("GET", "http://127.0.0.1:8001/"+user.IdStr+"/start", nil)
	client := new(http.Client)
	resp, _ := client.Do(req)
	defer resp.Body.Close()
	byteArray, _ := ioutil.ReadAll(resp.Body)
	fmt.Printf("request user_id:%s screen_name:%s res:%s\n", user.IdStr, user.ScreenName, string(byteArray))

	return c.Render(http.StatusOK, "index", user.IdStr+"-"+apiToken)
}

func makeToken() string {
	b := make([]byte, 32)
	rand.Read(b)
	hasher := md5.New()
	hasher.Write(b)
	return hex.EncodeToString(hasher.Sum(nil))
}

func activity(c echo.Context) error {
	apiToken := c.Request().Header().Get("X-Justaway-API-Token")
	if apiToken == "" {
		return c.String(401, "Missing X-Justaway-API-Token header")
	}

	db, err := sql.Open("mysql", dbSource)
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	stmtOut, err := db.Prepare("SELECT user_id FROM account WHERE api_token = ?")
	if err != nil {
		panic(err.Error())
	}
	defer stmtOut.Close()

	var userIdStr string

	err = stmtOut.QueryRow(apiToken).Scan(&userIdStr)
	if err != nil {
		return c.String(401, "Invalid X-Justaway-API-Token header")
	}

	stmtData, err := db.Prepare("SELECT id, data FROM activity WHERE user_id = ? ORDER BY id DESC LIMIT 200")
	if err != nil {
		panic(err.Error())
	}
	defer stmtData.Close()

	rows, err := stmtData.Query(userIdStr)
	if err != nil {
		panic(err.Error())
	}
	defer rows.Close()

	maxIdStr := "null"
	minIdStr := "null"
	events := ""
	count := 0
	for rows.Next() {
		count++
		var id string
		var data string
		err = rows.Scan(&id, &data)
		if err != nil {
			panic(err.Error())
		}
		if events == "" {
			events = data
		} else {
			events = events + "," + data
		}
		if maxIdStr == "null" {
			maxIdStr = "\""+id+"\""
		}
		minIdStr = "\""+id+"\""
	}

	return c.String(200, "{\"events\":["+events+"],\"max_id_str\":"+maxIdStr+",\"min_id_str\":"+minIdStr+"}")
}
<|endoftext|>