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

package main

import (
	"errors"
	"flag"
	"fmt"
	"image"
	"image/color"
	"image/png"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/zagrodzki/goscope/dummy"
	"github.com/zagrodzki/goscope/scope"
)

type aggrPoint struct {
	sumY  int
	sizeY int
}

func (p aggrPoint) add(y int) aggrPoint {
	p.sumY += y
	p.sizeY++
	return p
}

func (p aggrPoint) toPoint(x int) image.Point {
	return image.Point{x, p.sumY / p.sizeY}
}

type floatRange struct {
	min float64
	max float64
}

func (r floatRange) width() float64 {
	return r.max - r.min
}

func samplesToPoints(samples []scope.Sample, sampleRangeY floatRange, start, end image.Point) []image.Point {
	if len(samples) == 0 {
		return nil
	}

	sampleWidthX := float64(len(samples) - 1)
	sampleWidthY := sampleRangeY.width()

	pixelStartX := float64(start.X)
	pixelEndY := float64(end.Y)
	pixelWidthX := float64(end.X - start.X)
	pixelWidthY := float64(end.Y - start.Y)

	aggrPoints := make(map[int]aggrPoint)
	for i, y := range samples {
		mapX := int(pixelStartX + float64(i)/sampleWidthX*pixelWidthX)
		mapY := int(pixelEndY - float64(y-scope.Sample(sampleRangeY.min))/sampleWidthY*pixelWidthY)
		aggrPoints[mapX] = aggrPoints[mapX].add(mapY)
	}
	var points []image.Point
	for x, p := range aggrPoints {
		points = append(points, p.toPoint(x))
	}

	return points
}

// Plot represents the entire plotting area.
type Plot struct {
	*image.RGBA
}

// Fill fills the plot with a color.
func (plot Plot) Fill(col color.RGBA) {
	bounds := plot.Bounds()
	for i := bounds.Min.X; i < bounds.Max.X; i++ {
		for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
			plot.Set(i, j, col)
		}
	}
}

func isInside(x, y int, start, end image.Point) bool {
	return x >= start.X && x <= end.X && y >= start.Y && y <= end.Y
}

// DrawLine draws a straight line from pixel p1 to p2.
func (plot Plot) DrawLine(p1, p2 image.Point, start, end image.Point, col color.RGBA) {
	if p1.X == p2.X { // vertical line
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			plot.Set(p1.X, i, col)
		}
		return
	}

	// Calculating the parameters of the equation
	// of the straight line (in the form y=a*x+b)
	// passing through p1 and p2.

	// slope of the line
	a := float64(p1.Y-p2.Y) / float64(p1.X-p2.X)
	// intercept of the line
	b := float64(p1.Y) - float64(p1.X)*a

	// To avoid visual "gaps" between the pixels we switch on,
	// we draw the line in one of two ways.
	if abs(p1.X-p2.X) >= abs(p1.Y-p2.Y) {
		// If the line is more horizontal than vertical,
		// for every pixel column between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.X, p2.X); i <= max(p1.X, p2.X); i++ {
			y := int(a*float64(i) + b)
			if isInside(i, y, start, end) {
				plot.Set(i, y, col)
			}
		}
	} else {
		// If the line is more vertical than horizontal,
		// for every pixel row between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			x := int((float64(i) - b) / a)
			if isInside(x, i, start, end) {
				plot.Set(x, i, col)
			}
		}
	}
}

// DrawSamples draws samples in the image rectangle defined by
// starting (upper left) and ending (lower right) pixel.
func (plot Plot) DrawSamples(samples []scope.Sample, sampleRangeY floatRange, start, end image.Point, col color.RGBA) {
	points := samplesToPoints(samples, sampleRangeY, start, end)
	sort.Sort(pointsByX(points))
	for i := 1; i < len(points); i++ {
		plot.DrawLine(points[i-1], points[i], start, end, col)
	}
}

// DrawAll draws samples from all the channels into one image.
func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, ranges map[scope.ChanID]floatRange, cols map[scope.ChanID]color.RGBA) {
	b := plot.Bounds()
	x1 := b.Min.X + 10
	x2 := b.Max.X - 10
	y1 := b.Min.Y + 10
	y2 := b.Min.Y + 10 + int((b.Max.Y-b.Min.Y-10*(len(samples)+1))/len(samples))
	step := y2 - b.Min.Y
	for id, v := range samples {
		plot.DrawSamples(v, ranges[id], image.Point{x1, y1}, image.Point{x2, y2}, cols[id])
		y1 = y1 + step
		y2 = y2 + step
	}
}

// CreatePlot plots samples from the device.
func CreatePlot(dev scope.Device, ranges map[scope.ChanID]floatRange) (Plot, error) {
	plot := Plot{image.NewRGBA(image.Rect(0, 0, 800, 600))}

	data, stop, err := dev.StartSampling()
	defer stop()
	if err != nil {
		return plot, err
	}
	samples := (<-data).Samples

	colWhite := color.RGBA{255, 255, 255, 255}
	colRed := color.RGBA{255, 0, 0, 255}
	colGreen := color.RGBA{0, 255, 0, 255}
	colBlue := color.RGBA{0, 0, 255, 255}
	colBlack := color.RGBA{0, 0, 0, 255}
	chanCols := [4]color.RGBA{colRed, colGreen, colBlue, colBlack}

	plot.Fill(colWhite)

	cols := make(map[scope.ChanID]color.RGBA)
	next := 0
	for _, id := range dev.Channels() {
		if _, exists := ranges[id]; !exists {
			ranges[id] = floatRange{-1, 1}
		}
		cols[id] = chanCols[next]
		next = (next + 1) % 4
	}
	plot.DrawAll(samples, ranges, cols)
	return plot, nil
}

func plotToPng(dev scope.Device, ranges map[scope.ChanID]floatRange, outputFile string) error {
	plot, err := CreatePlot(dev, ranges)
	if err != nil {
		return err
	}

	f, err := os.Create(outputFile)
	if err != nil {
		return err
	}
	defer f.Close()
	png.Encode(f, plot)
	return nil
}

type pointsByX []image.Point

func (a pointsByX) Len() int {
	return len(a)
}

func (a pointsByX) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a pointsByX) Less(i, j int) bool {
	return a[i].X < a[j].X
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

type chanRanges map[scope.ChanID]floatRange

func (i *chanRanges) String() string {
	return fmt.Sprintf("%v", *i)
}

func (i *chanRanges) Set(value string) error {
	parts := strings.Split(value, ":")
	if len(parts) != 2 {
		return errors.New("use format: \"chanID:lowerY,upperY\"")
	}
	numbers := strings.Split(parts[1], ",")
	if len(numbers) != 2 {
		return errors.New("use format: \"chanID:lowerY,upperY\"")
	}
	min, err := strconv.ParseFloat(numbers[0], 64)
	if err != nil {
		return err
	}
	max, err := strconv.ParseFloat(numbers[1], 64)
	if err != nil {
		return err
	}
	(*i)[scope.ChanID(parts[0])] = floatRange{min, max}
	return nil
}

func main() {
	dev, err := dummy.Open("")
	if err != nil {
		log.Fatalf("Cannot open the device: %v", err)
	}

	fileName := flag.String("file", "draw.png", "output file name")
	ranges := chanRanges{}
	flag.Var(&ranges, "range", "channel Y range, format: \"chanID:lowerY,upperY\"")
	flag.Parse()

	if err := plotToPng(dev, ranges, *fileName); err != nil {
		log.Fatalf("Cannot plot samples: %v", err)
	}
}
<commit_msg>range and plotToPng made public<commit_after>//  Copyright 2016 The goscope Authors
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

package main

import (
	"errors"
	"flag"
	"fmt"
	"image"
	"image/color"
	"image/png"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/zagrodzki/goscope/dummy"
	"github.com/zagrodzki/goscope/scope"
)

type aggrPoint struct {
	sumY  int
	sizeY int
}

func (p aggrPoint) add(y int) aggrPoint {
	p.sumY += y
	p.sizeY++
	return p
}

func (p aggrPoint) toPoint(x int) image.Point {
	return image.Point{x, p.sumY / p.sizeY}
}

// ChannelYRange represents a y-range of samples to be plotted
type ChannelYRange struct {
	Min float64
	Max float64
}

// Width returns the width of the range.
func (r ChannelYRange) Width() float64 {
	return r.Max - r.Min
}

func samplesToPoints(samples []scope.Sample, sampleRangeY ChannelYRange, start, end image.Point) []image.Point {
	if len(samples) == 0 {
		return nil
	}

	sampleWidthX := float64(len(samples) - 1)
	sampleWidthY := sampleRangeY.Width()

	pixelStartX := float64(start.X)
	pixelEndY := float64(end.Y)
	pixelWidthX := float64(end.X - start.X)
	pixelWidthY := float64(end.Y - start.Y)

	aggrPoints := make(map[int]aggrPoint)
	for i, y := range samples {
		mapX := int(pixelStartX + float64(i)/sampleWidthX*pixelWidthX)
		mapY := int(pixelEndY - float64(y-scope.Sample(sampleRangeY.Min))/sampleWidthY*pixelWidthY)
		aggrPoints[mapX] = aggrPoints[mapX].add(mapY)
	}
	var points []image.Point
	for x, p := range aggrPoints {
		points = append(points, p.toPoint(x))
	}

	return points
}

// Plot represents the entire plotting area.
type Plot struct {
	*image.RGBA
}

// Fill fills the plot with a color.
func (plot Plot) Fill(col color.RGBA) {
	bounds := plot.Bounds()
	for i := bounds.Min.X; i < bounds.Max.X; i++ {
		for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
			plot.Set(i, j, col)
		}
	}
}

func isInside(x, y int, start, end image.Point) bool {
	return x >= start.X && x <= end.X && y >= start.Y && y <= end.Y
}

// DrawLine draws a straight line from pixel p1 to p2.
// Only the line fragment inside the image rectangle defined by
// starting (upper left) and ending (lower right) pixel is drawn.
func (plot Plot) DrawLine(p1, p2 image.Point, start, end image.Point, col color.RGBA) {
	if p1.X == p2.X { // vertical line
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			plot.Set(p1.X, i, col)
		}
		return
	}

	// Calculating the parameters of the equation
	// of the straight line (in the form y=a*x+b)
	// passing through p1 and p2.

	// slope of the line
	a := float64(p1.Y-p2.Y) / float64(p1.X-p2.X)
	// intercept of the line
	b := float64(p1.Y) - float64(p1.X)*a

	// To avoid visual "gaps" between the pixels we switch on,
	// we draw the line in one of two ways.
	if abs(p1.X-p2.X) >= abs(p1.Y-p2.Y) {
		// If the line is more horizontal than vertical,
		// for every pixel column between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.X, p2.X); i <= max(p1.X, p2.X); i++ {
			y := int(a*float64(i) + b)
			if isInside(i, y, start, end) {
				plot.Set(i, y, col)
			}
		}
	} else {
		// If the line is more vertical than horizontal,
		// for every pixel row between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			x := int((float64(i) - b) / a)
			if isInside(x, i, start, end) {
				plot.Set(x, i, col)
			}
		}
	}
}

// DrawSamples draws samples in the image rectangle defined by
// starting (upper left) and ending (lower right) pixel.
func (plot Plot) DrawSamples(samples []scope.Sample, sampleRangeY ChannelYRange, start, end image.Point, col color.RGBA) {
	points := samplesToPoints(samples, sampleRangeY, start, end)
	sort.Sort(pointsByX(points))
	for i := 1; i < len(points); i++ {
		plot.DrawLine(points[i-1], points[i], start, end, col)
	}
}

// DrawAll draws samples from all the channels into one image.
func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, ranges map[scope.ChanID]ChannelYRange, cols map[scope.ChanID]color.RGBA) {
	b := plot.Bounds()
	x1 := b.Min.X + 10
	x2 := b.Max.X - 10
	y1 := b.Min.Y + 10
	y2 := b.Min.Y + 10 + int((b.Max.Y-b.Min.Y-10*(len(samples)+1))/len(samples))
	step := y2 - b.Min.Y
	for id, v := range samples {
		plot.DrawSamples(v, ranges[id], image.Point{x1, y1}, image.Point{x2, y2}, cols[id])
		y1 = y1 + step
		y2 = y2 + step
	}
}

// CreatePlot plots samples from the device.
func CreatePlot(dev scope.Device, ranges map[scope.ChanID]ChannelYRange) (Plot, error) {
	plot := Plot{image.NewRGBA(image.Rect(0, 0, 800, 600))}

	data, stop, err := dev.StartSampling()
	defer stop()
	if err != nil {
		return plot, err
	}
	samples := (<-data).Samples

	colWhite := color.RGBA{255, 255, 255, 255}
	colRed := color.RGBA{255, 0, 0, 255}
	colGreen := color.RGBA{0, 255, 0, 255}
	colBlue := color.RGBA{0, 0, 255, 255}
	colBlack := color.RGBA{0, 0, 0, 255}
	chanCols := [4]color.RGBA{colRed, colGreen, colBlue, colBlack}

	plot.Fill(colWhite)

	cols := make(map[scope.ChanID]color.RGBA)
	next := 0
	for _, id := range dev.Channels() {
		if _, exists := ranges[id]; !exists {
			ranges[id] = ChannelYRange{-1, 1}
		}
		cols[id] = chanCols[next]
		next = (next + 1) % 4
	}
	plot.DrawAll(samples, ranges, cols)
	return plot, nil
}

// PlotToPng creates a plot of the samples from the device
// and saves it as PNG.
func PlotToPng(dev scope.Device, ranges map[scope.ChanID]ChannelYRange, outputFile string) error {
	plot, err := CreatePlot(dev, ranges)
	if err != nil {
		return err
	}

	f, err := os.Create(outputFile)
	if err != nil {
		return err
	}
	defer f.Close()
	png.Encode(f, plot)
	return nil
}

type pointsByX []image.Point

func (a pointsByX) Len() int {
	return len(a)
}

func (a pointsByX) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a pointsByX) Less(i, j int) bool {
	return a[i].X < a[j].X
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

type channelYRanges map[scope.ChanID]ChannelYRange

func (i *channelYRanges) String() string {
	return fmt.Sprintf("%v", *i)
}

func (i *channelYRanges) Set(value string) error {
	parts := strings.Split(value, ":")
	if len(parts) != 2 {
		return errors.New("use format: \"chanID:lowerY,upperY\"")
	}
	numbers := strings.Split(parts[1], ",")
	if len(numbers) != 2 {
		return errors.New("use format: \"chanID:lowerY,upperY\"")
	}
	min, err := strconv.ParseFloat(numbers[0], 64)
	if err != nil {
		return err
	}
	max, err := strconv.ParseFloat(numbers[1], 64)
	if err != nil {
		return err
	}
	(*i)[scope.ChanID(parts[0])] = ChannelYRange{min, max}
	return nil
}

func main() {
	dev, err := dummy.Open("")
	if err != nil {
		log.Fatalf("Cannot open the device: %v", err)
	}

	fileName := flag.String("file", "draw.png", "output file name")
	ranges := channelYRanges{}
	flag.Var(&ranges, "range", "channel Y range, format: \"chanID:lowerY,upperY\"")
	flag.Parse()

	if err := PlotToPng(dev, ranges, *fileName); err != nil {
		log.Fatalf("Cannot plot samples: %v", err)
	}
}
<|endoftext|>


<commit_before>// Encode, decode and correct (single bit) a binary code using Hamming code.
// Usage: hamcode [command] [binary code]
//
// Available Commands:
//  correct            Print the corrected binary code
//  encode             Print the encoded data binary using Hamming code
//  decode             Print the data binary code inside the input Hamming code
//  help, -h, --help   Print the help
//
// Author: Hamidreza Mahdavipanah
// Repository: github.com/mahdavipanah/hamcode
package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	// Checks if program has any argument
	if len(os.Args) < 2 {
		printHelp()
		return
	}

	// Checks if help output is asked
	if os.Args[1] == "--help" || os.Args[1] == "-h" || os.Args[1] == "help" {
		printHelp()
		return
	}

	// Checks if no valid command entered
	if os.Args[1] != "correct" && os.Args[1] != "decode" && os.Args[1] != "encode" {
		fmt.Fprintf(os.Stderr, "%v: Unknown command\nSee 'hamcode help'.\n", os.Args[1])
		os.Exit(1)
	}

	// Checks if program has at least 3 arguments
	if len(os.Args) < 3 {
		fmt.Fprintln(os.Stderr, "%v: Command needs a binary code.", os.Args[1])
		os.Exit(1)
	}

	bin := os.Args[2]

	if !isValidBinary(bin) {
		fmt.Fprintln(os.Stderr, "Invalid binary code.")
		os.Exit(1)
	}

	// Determines if the code should interpreted right to left
	rtl := false

	// Checks if there is any option for the command
	if len(os.Args) > 3 {
		if os.Args[3] == "--rtl" {
			rtl = true
		} else {
			fmt.Fprintf(os.Stderr, "Unknown option '%v'.\nSee 'hamcode help'.\n", os.Args[3])
			os.Exit(1)
		}
	}

	switch os.Args[1] {
	case "correct":
		corrected, _ := correct(bin)
		fmt.Println(corrected)
	case "encode":
		if rtl {
			bin = Reverse(bin)
		}
		fmt.Println(encode(bin))
	case "decode":
		decoded := decode(bin)
		if rtl {
			decoded = Reverse(decoded)
		}
		fmt.Println(decoded)
	}
}

// Reverse returns its argument string reversed rune-wise left to right.
func Reverse(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

// Corrects the bin and returns the corrected binary and error bit position.
// Returns bin and 0 if there are no errors.
func correct(bin string) (string, int) {
	// Parities
	p := make([]int, 0)

	// Finds parities and calculates their values
	for pos, _ := range bin {
		pos++

		if isPerfectSquare(pos) {
			c := 0
			for i, _ := range bin {
				i++
				// Checks if the bit should be calculated
				if i != pos && (i&pos != 0) {
					c ^= cToB(bin[i-1])
				}
			}

			// Appends the calculated parity bit
			p = append(p, c^cToB(bin[pos-1]))
		}
	}

	errPos := errorPosition(p) - 1

	// Checks if there was any error in the binary code
	if errPos != -1 {
		c := "0"

		if bin[errPos] == '0' {
			c = "1"
		}

		// Corrects the binary code
		bin = bin[:errPos] + c + bin[errPos+1:]
	}

	return bin, errPos + 1
}

// Returns the data code inside an Hamming code.
// This function first corrects the code using correct function.
func decode(bin string) (code string) {
	bin, _ = correct(bin)

	for pos, _ := range bin {
		if !isPerfectSquare(pos + 1) {
			code += string(bin[pos])
		}
	}

	return
}

// Returns encoded data code as an Hamming code
func encode(data string) (hcode string) {
	bits := make([]int, 0)

	for pos, i := 0, 0; i < len(data); pos++ {
		if isPerfectSquare(pos + 1) {
			bits = append(bits, 0)
		} else {
			bits = append(bits, cToB(data[i]))
			i++
		}
	}

	for pos, _ := range bits {
		if isPerfectSquare(pos + 1) {
			p := 0
			for i, _ := range bits {
				// Checks if the bit should be calculated
				if i+1 != pos+1 && ((i+1)&(pos+1) != 0) {
					p ^= bits[i]
				}
			}

			bits[pos] = p
		}
	}

	for _, bit := range bits {
		hcode += string(bit + 48)
	}

	return

}

// Prints help to standard output
func printHelp() {
	fmt.Println(`Encode, decode and correct (single bit) a binary code using Hamming code.

Usage: hamcode [command] [binary code]

Available Commands:
  correct            Print the corrected binary code
  encode             Print the encoded data binary using Hamming code
  decode             Print the data binary code inside the input Hamming code
  help, -h, --help   Print the help

Author: Hamidreza Mahdavipanah
Repository: http://github.com/mahdavipanah/hamcode`)
}

// Checks if all characters in a strings are 0 or 1
func isValidBinary(bin string) bool {
	// Checks if all chars are 0 or 1
	for _, char := range bin {
		// Checks if character is 0 or 1
		if char != '0' && char != '1' {
			return false
		}
	}

	return true
}

// Checks if a number is perfect square
func isPerfectSquare(n int) bool {
	return n == (n & -n)
}

// Converts char{0,1} to int{0,1}
func cToB(b byte) int {
	if b == '1' {
		return 1
	}
	return 0
}

// Returns the error bit position based of p slice
func errorPosition(p []int) int {
	str := ""
	for _, val := range p {
		str = string(val+48) + str
	}

	number, _ := strconv.ParseInt(str, 2, 0)

	return int(number)
}
<commit_msg>Add options to help output<commit_after>// Encode, decode and correct (single bit) a binary code using Hamming code.
// Usage: hamcode [command] [binary code]
//
// Available Commands:
//  correct            Print the corrected binary code
//  encode             Print the encoded data binary using Hamming code
//  decode             Print the data binary code inside the input Hamming code
//  help, -h, --help   Print the help
//
// Available Options:
//   --rtl	       Interpret the code from right to left
//
// Author: Hamidreza Mahdavipanah
// Repository: github.com/mahdavipanah/hamcode
package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	// Checks if program has any argument
	if len(os.Args) < 2 {
		printHelp()
		return
	}

	// Checks if help output is asked
	if os.Args[1] == "--help" || os.Args[1] == "-h" || os.Args[1] == "help" {
		printHelp()
		return
	}

	// Checks if no valid command entered
	if os.Args[1] != "correct" && os.Args[1] != "decode" && os.Args[1] != "encode" {
		fmt.Fprintf(os.Stderr, "%v: Unknown command\nSee 'hamcode help'.\n", os.Args[1])
		os.Exit(1)
	}

	// Checks if program has at least 3 arguments
	if len(os.Args) < 3 {
		fmt.Fprintln(os.Stderr, "%v: Command needs a binary code.", os.Args[1])
		os.Exit(1)
	}

	bin := os.Args[2]

	if !isValidBinary(bin) {
		fmt.Fprintln(os.Stderr, "Invalid binary code.")
		os.Exit(1)
	}

	// Determines if the code should interpreted right to left
	rtl := false

	// Checks if there is any option for the command
	if len(os.Args) > 3 {
		if os.Args[3] == "--rtl" {
			rtl = true
		} else {
			fmt.Fprintf(os.Stderr, "Unknown option '%v'.\nSee 'hamcode help'.\n", os.Args[3])
			os.Exit(1)
		}
	}

	switch os.Args[1] {
	case "correct":
		corrected, _ := correct(bin)
		fmt.Println(corrected)
	case "encode":
		if rtl {
			bin = Reverse(bin)
		}
		fmt.Println(encode(bin))
	case "decode":
		decoded := decode(bin)
		if rtl {
			decoded = Reverse(decoded)
		}
		fmt.Println(decoded)
	}
}

// Reverse returns its argument string reversed rune-wise left to right.
func Reverse(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

// Corrects the bin and returns the corrected binary and error bit position.
// Returns bin and 0 if there are no errors.
func correct(bin string) (string, int) {
	// Parities
	p := make([]int, 0)

	// Finds parities and calculates their values
	for pos, _ := range bin {
		pos++

		if isPerfectSquare(pos) {
			c := 0
			for i, _ := range bin {
				i++
				// Checks if the bit should be calculated
				if i != pos && (i&pos != 0) {
					c ^= cToB(bin[i-1])
				}
			}

			// Appends the calculated parity bit
			p = append(p, c^cToB(bin[pos-1]))
		}
	}

	errPos := errorPosition(p) - 1

	// Checks if there was any error in the binary code
	if errPos != -1 {
		c := "0"

		if bin[errPos] == '0' {
			c = "1"
		}

		// Corrects the binary code
		bin = bin[:errPos] + c + bin[errPos+1:]
	}

	return bin, errPos + 1
}

// Returns the data code inside an Hamming code.
// This function first corrects the code using correct function.
func decode(bin string) (code string) {
	bin, _ = correct(bin)

	for pos, _ := range bin {
		if !isPerfectSquare(pos + 1) {
			code += string(bin[pos])
		}
	}

	return
}

// Returns encoded data code as an Hamming code
func encode(data string) (hcode string) {
	bits := make([]int, 0)

	for pos, i := 0, 0; i < len(data); pos++ {
		if isPerfectSquare(pos + 1) {
			bits = append(bits, 0)
		} else {
			bits = append(bits, cToB(data[i]))
			i++
		}
	}

	for pos, _ := range bits {
		if isPerfectSquare(pos + 1) {
			p := 0
			for i, _ := range bits {
				// Checks if the bit should be calculated
				if i+1 != pos+1 && ((i+1)&(pos+1) != 0) {
					p ^= bits[i]
				}
			}

			bits[pos] = p
		}
	}

	for _, bit := range bits {
		hcode += string(bit + 48)
	}

	return

}

// Prints help to standard output
func printHelp() {
	fmt.Println(`Encode, decode and correct (single bit) a binary code using Hamming code.

Usage: hamcode [command] [binary code]

Available Commands:
  correct            Print the corrected binary code
  encode             Print the encoded data binary using Hamming code
  decode             Print the data binary code inside the input Hamming code
  help, -h, --help   Print the help

Available Options:
  --rtl		     Interpret the code from right to left

Author: Hamidreza Mahdavipanah
Repository: http://github.com/mahdavipanah/hamcode`)
}

// Checks if all characters in a strings are 0 or 1
func isValidBinary(bin string) bool {
	// Checks if all chars are 0 or 1
	for _, char := range bin {
		// Checks if character is 0 or 1
		if char != '0' && char != '1' {
			return false
		}
	}

	return true
}

// Checks if a number is perfect square
func isPerfectSquare(n int) bool {
	return n == (n & -n)
}

// Converts char{0,1} to int{0,1}
func cToB(b byte) int {
	if b == '1' {
		return 1
	}
	return 0
}

// Returns the error bit position based of p slice
func errorPosition(p []int) int {
	str := ""
	for _, val := range p {
		str = string(val+48) + str
	}

	number, _ := strconv.ParseInt(str, 2, 0)

	return int(number)
}
<|endoftext|>


<commit_before>package handler

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/graphql-go/graphql"

	"context"
	"github.com/graphql-go/graphql/gqlerrors"
)

const (
	ContentTypeJSON           = "application/json"
	ContentTypeGraphQL        = "application/graphql"
	ContentTypeFormURLEncoded = "application/x-www-form-urlencoded"
)

type ResultCallbackFn func(ctx context.Context, params *graphql.Params, result *graphql.Result, responseBody []byte)

type Handler struct {
	Schema           *graphql.Schema
	pretty           bool
	graphiql         bool
	playground       bool
	rootObjectFn     RootObjectFn
	resultCallbackFn ResultCallbackFn
	customErrorFormatter func(err error) gqlerrors.FormattedError
}
type RequestOptions struct {
	Query         string                 `json:"query" url:"query" schema:"query"`
	Variables     map[string]interface{} `json:"variables" url:"variables" schema:"variables"`
	OperationName string                 `json:"operationName" url:"operationName" schema:"operationName"`
}

// a workaround for getting`variables` as a JSON string
type requestOptionsCompatibility struct {
	Query         string `json:"query" url:"query" schema:"query"`
	Variables     string `json:"variables" url:"variables" schema:"variables"`
	OperationName string `json:"operationName" url:"operationName" schema:"operationName"`
}

func getFromForm(values url.Values) *RequestOptions {
	query := values.Get("query")
	if query != "" {
		// get variables map
		variables := make(map[string]interface{}, len(values))
		variablesStr := values.Get("variables")
		json.Unmarshal([]byte(variablesStr), &variables)

		return &RequestOptions{
			Query:         query,
			Variables:     variables,
			OperationName: values.Get("operationName"),
		}
	}

	return nil
}

// RequestOptions Parses a http.Request into GraphQL request options struct
func NewRequestOptions(r *http.Request) *RequestOptions {
	if reqOpt := getFromForm(r.URL.Query()); reqOpt != nil {
		return reqOpt
	}

	if r.Method != http.MethodPost {
		return &RequestOptions{}
	}

	if r.Body == nil {
		return &RequestOptions{}
	}

	// TODO: improve Content-Type handling
	contentTypeStr := r.Header.Get("Content-Type")
	contentTypeTokens := strings.Split(contentTypeStr, ";")
	contentType := contentTypeTokens[0]

	switch contentType {
	case ContentTypeGraphQL:
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return &RequestOptions{}
		}
		return &RequestOptions{
			Query: string(body),
		}
	case ContentTypeFormURLEncoded:
		if err := r.ParseForm(); err != nil {
			return &RequestOptions{}
		}

		if reqOpt := getFromForm(r.PostForm); reqOpt != nil {
			return reqOpt
		}

		return &RequestOptions{}

	case ContentTypeJSON:
		fallthrough
	default:
		var opts RequestOptions
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return &opts
		}
		err = json.Unmarshal(body, &opts)
		if err != nil {
			// Probably `variables` was sent as a string instead of an object.
			// So, we try to be polite and try to parse that as a JSON string
			var optsCompatible requestOptionsCompatibility
			json.Unmarshal(body, &optsCompatible)
			json.Unmarshal([]byte(optsCompatible.Variables), &opts.Variables)
		}
		return &opts
	}
}

// ContextHandler provides an entrypoint into executing graphQL queries with a
// user-provided context.
func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	// get query
	opts := NewRequestOptions(r)

	// execute graphql query
	params := graphql.Params{
		Schema:              *h.Schema,
		RequestString:       opts.Query,
		VariableValues:      opts.Variables,
		OperationName:       opts.OperationName,
		Context:             ctx,
		CustomErrorFomatter: h.customErrorFormatter,
	}
	if h.rootObjectFn != nil {
		params.RootObject = h.rootObjectFn(ctx, r)
	}
	result := graphql.Do(params)

	if h.graphiql {
		acceptHeader := r.Header.Get("Accept")
		_, raw := r.URL.Query()["raw"]
		if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") {
			renderGraphiQL(w, params)
			return
		}
	}

	if h.playground {
		acceptHeader := r.Header.Get("Accept")
		_, raw := r.URL.Query()["raw"]
		if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") {
			renderPlayground(w, r)
			return
		}
	}

	// use proper JSON Header
	w.Header().Add("Content-Type", "application/json; charset=utf-8")

	var buff []byte
	if h.pretty {
		w.WriteHeader(http.StatusOK)
		buff, _ = json.MarshalIndent(result, "", "\t")

		w.Write(buff)
	} else {
		w.WriteHeader(http.StatusOK)
		buff, _ = json.Marshal(result)

		w.Write(buff)
	}

	if h.resultCallbackFn != nil {
		h.resultCallbackFn(ctx, &params, result, buff)
	}
}

// ServeHTTP provides an entrypoint into executing graphQL queries.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.ContextHandler(r.Context(), w, r)
}

// RootObjectFn allows a user to generate a RootObject per request
type RootObjectFn func(ctx context.Context, r *http.Request) map[string]interface{}

type Config struct {
	Schema           *graphql.Schema
	Pretty           bool
	GraphiQL         bool
	Playground       bool
	RootObjectFn     RootObjectFn
	ResultCallbackFn ResultCallbackFn
	CustomErrorFormatter func(err error) gqlerrors.FormattedError
}

func NewConfig() *Config {
	return &Config{
		Schema:     nil,
		Pretty:     true,
		GraphiQL:   true,
		Playground: false,
	}
}

func New(p *Config) *Handler {
	if p == nil {
		p = NewConfig()
	}

	if p.Schema == nil {
		panic("undefined GraphQL schema")
	}

	return &Handler{
		Schema:           p.Schema,
		pretty:           p.Pretty,
		graphiql:         p.GraphiQL,
		playground:       p.Playground,
		rootObjectFn:     p.RootObjectFn,
		resultCallbackFn: p.ResultCallbackFn,
		customErrorFormatter: p.CustomErrorFormatter,
	}
}
<commit_msg>Updated CustomErrorFormatter to use originalError<commit_after>package handler

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/graphql-go/graphql"

	"context"
	"github.com/graphql-go/graphql/gqlerrors"
)

const (
	ContentTypeJSON           = "application/json"
	ContentTypeGraphQL        = "application/graphql"
	ContentTypeFormURLEncoded = "application/x-www-form-urlencoded"
)

type ResultCallbackFn func(ctx context.Context, params *graphql.Params, result *graphql.Result, responseBody []byte)

type Handler struct {
	Schema           *graphql.Schema
	pretty           bool
	graphiql         bool
	playground       bool
	rootObjectFn     RootObjectFn
	resultCallbackFn ResultCallbackFn
	customErrorFormatter func(err error) gqlerrors.FormattedError
}
type RequestOptions struct {
	Query         string                 `json:"query" url:"query" schema:"query"`
	Variables     map[string]interface{} `json:"variables" url:"variables" schema:"variables"`
	OperationName string                 `json:"operationName" url:"operationName" schema:"operationName"`
}

// a workaround for getting`variables` as a JSON string
type requestOptionsCompatibility struct {
	Query         string `json:"query" url:"query" schema:"query"`
	Variables     string `json:"variables" url:"variables" schema:"variables"`
	OperationName string `json:"operationName" url:"operationName" schema:"operationName"`
}

func getFromForm(values url.Values) *RequestOptions {
	query := values.Get("query")
	if query != "" {
		// get variables map
		variables := make(map[string]interface{}, len(values))
		variablesStr := values.Get("variables")
		json.Unmarshal([]byte(variablesStr), &variables)

		return &RequestOptions{
			Query:         query,
			Variables:     variables,
			OperationName: values.Get("operationName"),
		}
	}

	return nil
}

// RequestOptions Parses a http.Request into GraphQL request options struct
func NewRequestOptions(r *http.Request) *RequestOptions {
	if reqOpt := getFromForm(r.URL.Query()); reqOpt != nil {
		return reqOpt
	}

	if r.Method != http.MethodPost {
		return &RequestOptions{}
	}

	if r.Body == nil {
		return &RequestOptions{}
	}

	// TODO: improve Content-Type handling
	contentTypeStr := r.Header.Get("Content-Type")
	contentTypeTokens := strings.Split(contentTypeStr, ";")
	contentType := contentTypeTokens[0]

	switch contentType {
	case ContentTypeGraphQL:
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return &RequestOptions{}
		}
		return &RequestOptions{
			Query: string(body),
		}
	case ContentTypeFormURLEncoded:
		if err := r.ParseForm(); err != nil {
			return &RequestOptions{}
		}

		if reqOpt := getFromForm(r.PostForm); reqOpt != nil {
			return reqOpt
		}

		return &RequestOptions{}

	case ContentTypeJSON:
		fallthrough
	default:
		var opts RequestOptions
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return &opts
		}
		err = json.Unmarshal(body, &opts)
		if err != nil {
			// Probably `variables` was sent as a string instead of an object.
			// So, we try to be polite and try to parse that as a JSON string
			var optsCompatible requestOptionsCompatibility
			json.Unmarshal(body, &optsCompatible)
			json.Unmarshal([]byte(optsCompatible.Variables), &opts.Variables)
		}
		return &opts
	}
}

// ContextHandler provides an entrypoint into executing graphQL queries with a
// user-provided context.
func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	// get query
	opts := NewRequestOptions(r)

	// execute graphql query
	params := graphql.Params{
		Schema:              *h.Schema,
		RequestString:       opts.Query,
		VariableValues:      opts.Variables,
		OperationName:       opts.OperationName,
		Context:             ctx,
	}
	if h.rootObjectFn != nil {
		params.RootObject = h.rootObjectFn(ctx, r)
	}
	result := graphql.Do(params)

	if customFormatter := h.customErrorFormatter; customFormatter != nil {
		formatted := make([]gqlerrors.FormattedError, len(result.Errors))
		for i, err := range result.Errors {
			formatted[i] = customFormatter(err.OriginalError())
		}
		result.Errors = formatted
	}

	if h.graphiql {
		acceptHeader := r.Header.Get("Accept")
		_, raw := r.URL.Query()["raw"]
		if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") {
			renderGraphiQL(w, params)
			return
		}
	}

	if h.playground {
		acceptHeader := r.Header.Get("Accept")
		_, raw := r.URL.Query()["raw"]
		if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") {
			renderPlayground(w, r)
			return
		}
	}

	// use proper JSON Header
	w.Header().Add("Content-Type", "application/json; charset=utf-8")

	var buff []byte
	if h.pretty {
		w.WriteHeader(http.StatusOK)
		buff, _ = json.MarshalIndent(result, "", "\t")

		w.Write(buff)
	} else {
		w.WriteHeader(http.StatusOK)
		buff, _ = json.Marshal(result)

		w.Write(buff)
	}

	if h.resultCallbackFn != nil {
		h.resultCallbackFn(ctx, &params, result, buff)
	}
}

// ServeHTTP provides an entrypoint into executing graphQL queries.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.ContextHandler(r.Context(), w, r)
}

// RootObjectFn allows a user to generate a RootObject per request
type RootObjectFn func(ctx context.Context, r *http.Request) map[string]interface{}

type Config struct {
	Schema           *graphql.Schema
	Pretty           bool
	GraphiQL         bool
	Playground       bool
	RootObjectFn     RootObjectFn
	ResultCallbackFn ResultCallbackFn
	CustomErrorFormatter func(err error) gqlerrors.FormattedError
}

func NewConfig() *Config {
	return &Config{
		Schema:     nil,
		Pretty:     true,
		GraphiQL:   true,
		Playground: false,
	}
}

func New(p *Config) *Handler {
	if p == nil {
		p = NewConfig()
	}

	if p.Schema == nil {
		panic("undefined GraphQL schema")
	}

	return &Handler{
		Schema:           p.Schema,
		pretty:           p.Pretty,
		graphiql:         p.GraphiQL,
		playground:       p.Playground,
		rootObjectFn:     p.RootObjectFn,
		resultCallbackFn: p.ResultCallbackFn,
		customErrorFormatter: p.CustomErrorFormatter,
	}
}
<|endoftext|>


<commit_before>/*
   Hockeypuck - OpenPGP key server
   Copyright (C) 2012-2014  Casey Marshall

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, version 3.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package hkp

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/julienschmidt/httprouter"
	"golang.org/x/crypto/openpgp/armor"
	"gopkg.in/errgo.v1"

	"gopkg.in/hockeypuck/conflux.v2/recon"
	log "gopkg.in/hockeypuck/logrus.v0"
	"gopkg.in/hockeypuck/openpgp.v0"

	"gopkg.in/hockeypuck/hkp.v0/storage"
)

func httpError(w http.ResponseWriter, statusCode int, err error) {
	log.Errorf("HTTP %d: %v", statusCode, err)
	http.Error(w, http.StatusText(statusCode), statusCode)
}

type Handler struct {
	storage storage.Storage
}

type HandlerOption func(h *Handler) error

func NewHandler(storage storage.Storage) *Handler {
	return &Handler{storage: storage}
}

func (h *Handler) Register(r *httprouter.Router) {
	r.GET("/pks/lookup", h.Lookup)
	r.POST("/pks/add", h.Add)
	r.POST("/pks/hashquery", h.HashQuery)
}

func (h *Handler) Lookup(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	l, err := ParseLookup(r)
	if err != nil {
		httpError(w, http.StatusBadRequest, err)
		return
	}
	switch l.Op {
	case OperationGet, OperationHGet:
		h.get(w, l)
	case OperationIndex, OperationVIndex:
		h.index(w, l)
	default:
		httpError(w, http.StatusNotFound, errgo.Newf("operation not found: %v", l.Op))
	}
}

func (h *Handler) HashQuery(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	hq, err := ParseHashQuery(r)
	if err != nil {
		httpError(w, http.StatusBadRequest, errgo.Mask(err))
		return
	}
	var result []*openpgp.Pubkey
	for _, digest := range hq.Digests {
		rfps, err := h.storage.MatchMD5([]string{digest})
		if err != nil {
			log.Errorf("error resolving hashquery digest %q", digest)
			continue
		}
		keys, err := h.storage.FetchKeys(rfps)
		if err != nil {
			log.Errorf("error fetching hashquery key %q", digest)
			continue
		}
		result = append(result, keys...)
	}

	w.Header().Set("Content-Type", "pgp/keys")

	// Write the number of keys
	err = recon.WriteInt(w, len(result))
	for _, key := range result {
		// Write each key in binary packet format, prefixed with length
		err = writeHashqueryKey(w, key)
		if err != nil {
			log.Errorf("error writing hashquery key %q: %v", key.RFingerprint, err)
			return
		}
	}

	// SKS expects hashquery response to terminate with a CRLF
	_, err = w.Write([]byte{0x0d, 0x0a})
	if err != nil {
		log.Errorf("error writing hashquery terminator: %v", err)
	}
}

func writeHashqueryKey(w http.ResponseWriter, key *openpgp.Pubkey) error {
	var buf bytes.Buffer
	err := openpgp.WritePackets(&buf, key)
	if err != nil {
		return errgo.Mask(err)
	}
	err = recon.WriteInt(w, buf.Len())
	if err != nil {
		return errgo.Mask(err)
	}
	_, err = w.Write(buf.Bytes())
	if err != nil {
		return errgo.Mask(err)
	}
	return nil
}

func (h *Handler) resolve(l *Lookup) ([]string, error) {
	if l.Op == OperationHGet {
		return h.storage.MatchMD5([]string{l.Search})
	}
	if strings.HasPrefix(l.Search, "0x") {
		keyID := openpgp.Reverse(strings.ToLower(l.Search[2:]))
		return h.storage.Resolve([]string{keyID})
	}
	return h.storage.MatchKeyword([]string{l.Search})
}

func (h *Handler) keys(l *Lookup) ([]*openpgp.Pubkey, error) {
	rfps, err := h.resolve(l)
	if err != nil {
		return nil, err
	}
	return h.storage.FetchKeys(rfps)
}

func (h *Handler) get(w http.ResponseWriter, l *Lookup) {
	keys, err := h.keys(l)
	if err != nil {
		httpError(w, http.StatusInternalServerError, errgo.Mask(err))
	}

	w.Header().Set("Content-Type", "text/plain")
	err = openpgp.WriteArmoredPackets(w, keys)
	if err != nil {
		log.Errorf("get %q: error writing armored keys: %v", l.Search, err)
	}
}

func (h *Handler) index(w http.ResponseWriter, l *Lookup) {
	keys, err := h.keys(l)
	if err != nil {
		httpError(w, http.StatusInternalServerError, errgo.Mask(err))
	}

	if l.Options[OptionMachineReadable] {
		h.indexMR(w, keys, l)
	} else {
		h.indexJSON(w, keys)
	}
}

func (h *Handler) indexJSON(w http.ResponseWriter, keys []*openpgp.Pubkey) {
	w.Header().Set("Content-Type", "application/json")
	enc := json.NewEncoder(w)
	err := enc.Encode(&keys)
	if err != nil {
		httpError(w, http.StatusInternalServerError, errgo.Mask(err))
	}
}

func mrTimeString(t time.Time) string {
	if t.IsZero() {
		return ""
	}
	return fmt.Sprintf("%d", t.Unix())
}

func (h *Handler) indexMR(w http.ResponseWriter, keys []*openpgp.Pubkey, l *Lookup) {
	w.Header().Set("Content-Type", "text/plain")

	fmt.Fprintf(w, "info:1:%d\n", len(keys))
	for _, key := range keys {
		selfsigs := key.SelfSigs()
		if !selfsigs.Valid() {
			continue
		}

		var keyID string
		if l.Fingerprint {
			keyID = key.Fingerprint()
		} else {
			keyID = key.KeyID()
		}
		keyID = strings.ToUpper(keyID)

		expiresAt, _ := selfsigs.ExpiresAt()

		fmt.Fprintf(w, "pub:%s:%d:%d:%d:%s:\n", keyID, key.Algorithm, key.BitLen,
			key.Creation.Unix(), mrTimeString(expiresAt))

		for _, uid := range key.UserIDs {
			selfsigs := uid.SelfSigs(key)
			validSince, ok := selfsigs.ValidSince()
			if !ok {
				continue
			}
			expiresAt, _ := selfsigs.ExpiresAt()
			fmt.Fprintf(w, "uid:%s:%d:%s:\n", strings.Replace(uid.Keywords, ":", "%3a", -1),
				validSince.Unix(), mrTimeString(expiresAt))
		}
	}
}

type AddResponse struct {
	Inserted []string `json:"inserted"`
	Updated  []string `json:"updated"`
	Ignored  []string `json:"ignored"`
}

func (h *Handler) Add(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	add, err := ParseAdd(r)
	if err != nil {
		httpError(w, http.StatusBadRequest, errgo.Mask(err))
		return
	}

	// Check and decode the armor
	armorBlock, err := armor.Decode(bytes.NewBufferString(add.Keytext))
	if err != nil {
		httpError(w, http.StatusBadRequest, errgo.Mask(err))
		return
	}

	var result AddResponse
	for readKey := range openpgp.ReadKeys(armorBlock.Body) {
		if readKey.Error != nil {
			httpError(w, http.StatusBadRequest, errgo.Mask(err))
			return
		}
		err := openpgp.DropDuplicates(readKey.Pubkey)
		if err != nil {
			httpError(w, http.StatusInternalServerError, errgo.Mask(err))
			return
		}
		change, err := storage.UpsertKey(h.storage, readKey.Pubkey)
		if err != nil {
			httpError(w, http.StatusInternalServerError, errgo.Mask(err))
			return
		}

		fp := readKey.Pubkey.QualifiedFingerprint()
		switch change.(type) {
		case storage.KeyAdded:
			result.Inserted = append(result.Inserted, fp)
		case storage.KeyReplaced:
			result.Updated = append(result.Updated, fp)
		case storage.KeyNotChanged:
			result.Ignored = append(result.Ignored, fp)
		}
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	enc := json.NewEncoder(w)
	enc.Encode(&result)
}
<commit_msg>Respond with 404 if no keys found for ops: get, hget, index, vindex.<commit_after>/*
   Hockeypuck - OpenPGP key server
   Copyright (C) 2012-2014  Casey Marshall

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, version 3.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package hkp

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/julienschmidt/httprouter"
	"golang.org/x/crypto/openpgp/armor"
	"gopkg.in/errgo.v1"

	"gopkg.in/hockeypuck/conflux.v2/recon"
	log "gopkg.in/hockeypuck/logrus.v0"
	"gopkg.in/hockeypuck/openpgp.v0"

	"gopkg.in/hockeypuck/hkp.v0/storage"
)

func httpError(w http.ResponseWriter, statusCode int, err error) {
	log.Errorf("HTTP %d: %v", statusCode, err)
	http.Error(w, http.StatusText(statusCode), statusCode)
}

type Handler struct {
	storage storage.Storage
}

type HandlerOption func(h *Handler) error

func NewHandler(storage storage.Storage) *Handler {
	return &Handler{storage: storage}
}

func (h *Handler) Register(r *httprouter.Router) {
	r.GET("/pks/lookup", h.Lookup)
	r.POST("/pks/add", h.Add)
	r.POST("/pks/hashquery", h.HashQuery)
}

func (h *Handler) Lookup(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	l, err := ParseLookup(r)
	if err != nil {
		httpError(w, http.StatusBadRequest, err)
		return
	}
	switch l.Op {
	case OperationGet, OperationHGet:
		h.get(w, l)
	case OperationIndex, OperationVIndex:
		h.index(w, l)
	default:
		httpError(w, http.StatusNotFound, errgo.Newf("operation not found: %v", l.Op))
		return
	}
}

func (h *Handler) HashQuery(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	hq, err := ParseHashQuery(r)
	if err != nil {
		httpError(w, http.StatusBadRequest, errgo.Mask(err))
		return
	}
	var result []*openpgp.Pubkey
	for _, digest := range hq.Digests {
		rfps, err := h.storage.MatchMD5([]string{digest})
		if err != nil {
			log.Errorf("error resolving hashquery digest %q", digest)
			continue
		}
		keys, err := h.storage.FetchKeys(rfps)
		if err != nil {
			log.Errorf("error fetching hashquery key %q", digest)
			continue
		}
		result = append(result, keys...)
	}

	w.Header().Set("Content-Type", "pgp/keys")

	// Write the number of keys
	err = recon.WriteInt(w, len(result))
	for _, key := range result {
		// Write each key in binary packet format, prefixed with length
		err = writeHashqueryKey(w, key)
		if err != nil {
			log.Errorf("error writing hashquery key %q: %v", key.RFingerprint, err)
			return
		}
	}

	// SKS expects hashquery response to terminate with a CRLF
	_, err = w.Write([]byte{0x0d, 0x0a})
	if err != nil {
		log.Errorf("error writing hashquery terminator: %v", err)
	}
}

func writeHashqueryKey(w http.ResponseWriter, key *openpgp.Pubkey) error {
	var buf bytes.Buffer
	err := openpgp.WritePackets(&buf, key)
	if err != nil {
		return errgo.Mask(err)
	}
	err = recon.WriteInt(w, buf.Len())
	if err != nil {
		return errgo.Mask(err)
	}
	_, err = w.Write(buf.Bytes())
	if err != nil {
		return errgo.Mask(err)
	}
	return nil
}

func (h *Handler) resolve(l *Lookup) ([]string, error) {
	if l.Op == OperationHGet {
		return h.storage.MatchMD5([]string{l.Search})
	}
	if strings.HasPrefix(l.Search, "0x") {
		keyID := openpgp.Reverse(strings.ToLower(l.Search[2:]))
		return h.storage.Resolve([]string{keyID})
	}
	return h.storage.MatchKeyword([]string{l.Search})
}

func (h *Handler) keys(l *Lookup) ([]*openpgp.Pubkey, error) {
	rfps, err := h.resolve(l)
	if err != nil {
		return nil, err
	}
	return h.storage.FetchKeys(rfps)
}

func (h *Handler) get(w http.ResponseWriter, l *Lookup) {
	keys, err := h.keys(l)
	if err != nil {
		httpError(w, http.StatusInternalServerError, errgo.Mask(err))
		return
	}
	if len(keys) == 0 {
		httpError(w, http.StatusNotFound, errgo.New("not found"))
		return
	}

	w.Header().Set("Content-Type", "text/plain")
	err = openpgp.WriteArmoredPackets(w, keys)
	if err != nil {
		log.Errorf("get %q: error writing armored keys: %v", l.Search, err)
	}
}

func (h *Handler) index(w http.ResponseWriter, l *Lookup) {
	keys, err := h.keys(l)
	if err != nil {
		httpError(w, http.StatusInternalServerError, errgo.Mask(err))
		return
	}
	if len(keys) == 0 {
		httpError(w, http.StatusNotFound, errgo.New("not found"))
		return
	}

	if l.Options[OptionMachineReadable] {
		h.indexMR(w, keys, l)
	} else {
		h.indexJSON(w, keys)
	}
}

func (h *Handler) indexJSON(w http.ResponseWriter, keys []*openpgp.Pubkey) {
	w.Header().Set("Content-Type", "application/json")
	enc := json.NewEncoder(w)
	err := enc.Encode(&keys)
	if err != nil {
		httpError(w, http.StatusInternalServerError, errgo.Mask(err))
		return
	}
}

func mrTimeString(t time.Time) string {
	if t.IsZero() {
		return ""
	}
	return fmt.Sprintf("%d", t.Unix())
}

func (h *Handler) indexMR(w http.ResponseWriter, keys []*openpgp.Pubkey, l *Lookup) {
	w.Header().Set("Content-Type", "text/plain")

	fmt.Fprintf(w, "info:1:%d\n", len(keys))
	for _, key := range keys {
		selfsigs := key.SelfSigs()
		if !selfsigs.Valid() {
			continue
		}

		var keyID string
		if l.Fingerprint {
			keyID = key.Fingerprint()
		} else {
			keyID = key.KeyID()
		}
		keyID = strings.ToUpper(keyID)

		expiresAt, _ := selfsigs.ExpiresAt()

		fmt.Fprintf(w, "pub:%s:%d:%d:%d:%s:\n", keyID, key.Algorithm, key.BitLen,
			key.Creation.Unix(), mrTimeString(expiresAt))

		for _, uid := range key.UserIDs {
			selfsigs := uid.SelfSigs(key)
			validSince, ok := selfsigs.ValidSince()
			if !ok {
				continue
			}
			expiresAt, _ := selfsigs.ExpiresAt()
			fmt.Fprintf(w, "uid:%s:%d:%s:\n", strings.Replace(uid.Keywords, ":", "%3a", -1),
				validSince.Unix(), mrTimeString(expiresAt))
		}
	}
}

type AddResponse struct {
	Inserted []string `json:"inserted"`
	Updated  []string `json:"updated"`
	Ignored  []string `json:"ignored"`
}

func (h *Handler) Add(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	add, err := ParseAdd(r)
	if err != nil {
		httpError(w, http.StatusBadRequest, errgo.Mask(err))
		return
	}

	// Check and decode the armor
	armorBlock, err := armor.Decode(bytes.NewBufferString(add.Keytext))
	if err != nil {
		httpError(w, http.StatusBadRequest, errgo.Mask(err))
		return
	}

	var result AddResponse
	for readKey := range openpgp.ReadKeys(armorBlock.Body) {
		if readKey.Error != nil {
			httpError(w, http.StatusBadRequest, errgo.Mask(err))
			return
		}
		err := openpgp.DropDuplicates(readKey.Pubkey)
		if err != nil {
			httpError(w, http.StatusInternalServerError, errgo.Mask(err))
			return
		}
		change, err := storage.UpsertKey(h.storage, readKey.Pubkey)
		if err != nil {
			httpError(w, http.StatusInternalServerError, errgo.Mask(err))
			return
		}

		fp := readKey.Pubkey.QualifiedFingerprint()
		switch change.(type) {
		case storage.KeyAdded:
			result.Inserted = append(result.Inserted, fp)
		case storage.KeyReplaced:
			result.Updated = append(result.Updated, fp)
		case storage.KeyNotChanged:
			result.Ignored = append(result.Ignored, fp)
		}
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	enc := json.NewEncoder(w)
	enc.Encode(&result)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"io"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	auth := r.Header.Get("X-Vip-Token")
	if auth != authToken {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	h(w, r)
}

func fileKey(bucket string) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard 5mb limit on files
	if r.ContentLength > 5<<20 {
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		return
	}

	key := fileKey(bucket)
	err := storage.PutReader(bucket, key, r.Body,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

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

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		uri.Scheme = "http"
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, key)

	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<commit_msg>Enable CORS<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"io"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	if origin := r.Header.Get("Origin"); origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers",
			"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
	}

	if r.Method == "OPTIONS" {
		return
	}

	auth := r.Header.Get("X-Vip-Token")
	if auth != authToken {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	h(w, r)
}

func fileKey(bucket string) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard 5mb limit on files
	if r.ContentLength > 5<<20 {
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		return
	}

	key := fileKey(bucket)
	err := storage.PutReader(bucket, key, r.Body,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

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

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		uri.Scheme = "http"
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, key)

	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<|endoftext|>


<commit_before>package rest

import (
	"log"
	"net/http"
	"os"
)

// Handler describes an HTTP request handler.
//
// A simple contract guarantees robust request handling: if a handler returns an
// error status (>= 400), then the response has not yet been written and the
// client must still be shown an error message. If the error value is not nil,
// then something went wrong on the server and it should be logged/reported.
//
// In other words, the first return value is for the client's benefit, and the
// second return value is for the server. They are completely independent; an
// error status doesn't always mean the error will be non-nil. (For example,
// 404 Not Found is not usually a server error.)
//
// Use code 0 if the response is not sent. Or -1, if the response is sent, but
// it is made by a third-party processor and the status code of the sent
// response is not known.
type Handler func(w http.ResponseWriter, r *http.Request) (code int, err error)

// ServeHTTP implements http.Handler interface.
func (fn Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	code, err := fn(w, r)
	if code == 0 {
		w.WriteHeader(http.StatusNoContent)
	} else if code >= 400 {
		http.Error(w, http.StatusText(code), code)
	}
	if err != nil {
		log.Println("handler error:", err)
	}
}

// Handlers Handler.
func Handlers(handlers ...Handler) Handler {
	if len(handlers) == 1 {
		return handlers[0]
	}
	return func(w http.ResponseWriter, r *http.Request) (code int, err error) {
		for _, handler := range handlers {
			code, err = handler(w, r)
			if code != 0 || err != nil {
				break
			}
		}
		return
	}
}

// ServeFile replies to the request with the contents of the named file.
func ServeFile(filename string) Handler {
	return func(w http.ResponseWriter, r *http.Request) (code int, err error) {
		var fi os.FileInfo
		file, err := os.Open(filename)
		if err == nil {
			defer file.Close()
			fi, err = file.Stat()
		}
		switch {
		case err == nil:
			http.ServeContent(w, r, filename, fi.ModTime(), file)
			return http.StatusOK, nil
		case os.IsNotExist(err):
			return http.StatusNotFound, nil
		case os.IsPermission(err):
			return http.StatusForbidden, nil
		default:
			return http.StatusInternalServerError, err
		}
	}
}
<commit_msg>ServeFile rewrite<commit_after>package rest

import (
	"log"
	"net/http"
	"os"
)

// Handler describes an HTTP request handler.
//
// A simple contract guarantees robust request handling: if a handler returns an
// error status (>= 400), then the response has not yet been written and the
// client must still be shown an error message. If the error value is not nil,
// then something went wrong on the server and it should be logged/reported.
//
// In other words, the first return value is for the client's benefit, and the
// second return value is for the server. They are completely independent; an
// error status doesn't always mean the error will be non-nil. (For example,
// 404 Not Found is not usually a server error.)
//
// Use code 0 if the response is not sent. Or -1, if the response is sent, but
// it is made by a third-party processor and the status code of the sent
// response is not known.
type Handler func(w http.ResponseWriter, r *http.Request) (code int, err error)

// ServeHTTP implements http.Handler interface.
func (fn Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	code, err := fn(w, r)
	if code == 0 {
		w.WriteHeader(http.StatusNoContent)
	} else if code >= 400 {
		http.Error(w, http.StatusText(code), code)
	}
	if err != nil {
		log.Println("handler error:", err)
	}
}

// Handlers Handler.
func Handlers(handlers ...Handler) Handler {
	if len(handlers) == 1 {
		return handlers[0]
	}
	return func(w http.ResponseWriter, r *http.Request) (code int, err error) {
		for _, handler := range handlers {
			code, err = handler(w, r)
			if code != 0 || err != nil {
				break
			}
		}
		return
	}
}

// ServeFile replies to the request with the contents of the named file.
func ServeFile(filename string) Handler {
	return func(w http.ResponseWriter, r *http.Request) (code int, err error) {
		switch file, err := os.Open(filename); {
		case err == nil:
			fi, _ := file.Stat()
			http.ServeContent(w, r, filename, fi.ModTime(), file)
			file.Close()
			return http.StatusOK, nil
		case os.IsNotExist(err):
			return http.StatusNotFound, err
		case os.IsPermission(err):
			return http.StatusForbidden, err
		default:
			return http.StatusInternalServerError, err
		}
	}
}
<|endoftext|>


<commit_before>package app

import (
	"html/template"
	"net/http"

	"appengine"

	"github.com/twinj/uuid"
	"github.com/murrekatt/go-s3presigned-post"
)

const (
	regionName = "FILL-ME-IN"
	bucketName = "FILL-ME-IN"
	accessKeyID = "FILL-ME-IN"
	secretAccessKey = "FILL-ME-IN"
)

// HTML form for pre-signed POST
const htmlDocument = `

  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

AWS S3 File Uploader

  <form action="{{.Action}}" method="post" enctype="multipart/form-data">
    <input type="hidden" name="key" value="{{.Key}}" />
    <input type="hidden" name="acl" value="public-read" />
    <input type="hidden" name="X-Amz-Credential" value="{{.Credential}}" />
    <input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" />
    <input type="hidden" name="X-Amz-Date" value="{{.Date}}" />

Tags

    <input type="hidden" name="Policy" value="{{.Policy}}" />
    <input type="hidden" name="X-Amz-Signature" value="{{.Signature}}" />

File

    <input type="file"   name="file" />

    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>

`

// Handles upload requests.
func handleUpload(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	// parse HTML template
	t, err := template.New("presign").Parse(htmlDocument)
	if err != nil {
		c.Errorf("Error parsing template: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	// a unique key to upload
	id := uuid.NewV4().String()

	// AWS S3 credentials
	creds := &s3.Credentials{
		Region: regionName,
		Bucket: bucketName,
		AccessKeyID: accessKeyID,
		SecretAccessKey: secretAccessKey,
	}

	// create pre-signed POST details
	post, err := s3.NewPresignedPOST(id, creds)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	// render HTML form
	t.Execute(w, post)
}
<commit_msg>Updates handler to provide expiration time and max file size.<commit_after>package app

import (
	"html/template"
	"net/http"

	"appengine"

	"github.com/twinj/uuid"
	"github.com/murrekatt/go-s3presigned-post"
)

const (
	regionName = "FILL-ME-IN"
	bucketName = "FILL-ME-IN"
	accessKeyID = "FILL-ME-IN"
	secretAccessKey = "FILL-ME-IN"
)

// HTML form for pre-signed POST
const htmlDocument = `

  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

AWS S3 File Uploader

  <form action="{{.Action}}" method="post" enctype="multipart/form-data">
    <input type="hidden" name="key" value="{{.Key}}" />
    <input type="hidden" name="acl" value="public-read" />
    <input type="hidden" name="X-Amz-Credential" value="{{.Credential}}" />
    <input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" />
    <input type="hidden" name="X-Amz-Date" value="{{.Date}}" />
    <input type="hidden" name="Policy" value="{{.Policy}}" />
    <input type="hidden" name="X-Amz-Signature" value="{{.Signature}}" />

File

    <input type="file"   name="file" />

    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>

`

// Handles upload requests.
func handleUpload(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	// parse HTML template
	t, err := template.New("presign").Parse(htmlDocument)
	if err != nil {
		c.Errorf("Error parsing template: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	// a unique key to upload
	id := uuid.NewV4().String()

	// AWS S3 credentials
	creds := &s3.Credentials{
		Region: regionName,
		Bucket: bucketName,
		AccessKeyID: accessKeyID,
		SecretAccessKey: secretAccessKey,
	}

	// create pre-signed POST details
	opts := &s3.PolicyOptions{ 5, 1024000 }
	post, err := s3.NewPresignedPOST(id, creds, opts)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	// render HTML form
	t.Execute(w, post)
}
<|endoftext|>


<commit_before>package httpcache

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"sync"
	"time"
)

const (
	CacheHeader = "X-Cache"
)

var Writes sync.WaitGroup

type Handler struct {
	Shared   bool
	upstream http.Handler
	cache    *Cache
}

func NewHandler(cache *Cache, upstream http.Handler) *Handler {
	return &Handler{
		upstream: upstream,
		cache:    cache,
		Shared:   false,
	}
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	req := &request{Request: r}

	if bad, reason := req.isBadRequest(); bad {
		http.Error(rw, "invalid request: "+reason,
			http.StatusBadRequest)
		return
	}

	if !req.isCacheable() {
		Debugf("request not cacheable")
		rw.Header().Set(CacheHeader, "SKIP")
		h.pipeUpstream(rw, r)
		return
	}

	res, err := h.lookup(req)
	if err != nil && err != ErrNotFoundInCache {
		http.Error(rw, "lookup error: "+err.Error(),
			http.StatusInternalServerError)
		return
	}

	if err == ErrNotFoundInCache {
		Debugf("%s %s not in cache", r.Method, r.URL.String())
		h.passUpstream(rw, r)
		return
	} else {
		Debugf("%s %s found in cache", r.Method, r.URL.String())
	}

	if h.needsValidation(res, req) {
		Debugf("validating cached response")

		if !h.validate(req, res) {
			Debugf("response is changed")
			h.passUpstream(rw, r)
			return
		} else {
			Debugf("response is valid")
			h.cache.Freshen(res, NewRequestKey(r).String())
		}
	}

	h.serveResource(res, rw, r)
	rw.Header().Set(CacheHeader, "HIT")

	if err := res.Close(); err != nil {
		Errorf("Error closing resource: %s", err.Error())
	}
}

func (h *Handler) needsValidation(res *Resource, req *request) bool {
	if req.mustValidate() || res.MustValidate() {
		return true
	}

	if res.IsStale() {
		return true
	}

	maxAge, err := res.MaxAge(h.Shared)
	if err != nil {
		Debugf("error calculating max-age: %s", err.Error())
		return true
	}

	age, err := res.Age()
	if err != nil {
		Debugf("error calculating age: %s", err.Error())
		return true
	}

	if hFresh := res.HeuristicFreshness(); hFresh > age {
		Debugf("heuristic freshness of %q", hFresh)
		return false
	}

	if age > maxAge {
		Debugf("age %q > max-age %q", age, maxAge)
		maxStale, _ := req.maxStale()
		if maxStale > (age - maxAge) {
			log.Printf("stale, but within allowed max-stale period of %s", maxStale)
			return false
		}
		return true
	}

	return false
}

// pipeUpstream makes the request via the upstream handler, the response is not stored or modified
func (h *Handler) pipeUpstream(w http.ResponseWriter, r *http.Request) {
	rw := newResponseWriter(w)

	Debugf("piping request upstream")
	h.upstream.ServeHTTP(rw, r)

	if r.Method == "HEAD" {
		res := rw.Resource()
		h.cache.Freshen(res, NewRequestKey(r).ForMethod("GET").String())
	}
}

// passUpstream makes the request via the upstream handler and stores the result
func (h *Handler) passUpstream(w http.ResponseWriter, r *http.Request) {
	rw := newResponseWriter(w)

	t := time.Now()
	Debugf("passing request upstream")
	h.upstream.ServeHTTP(rw, r)
	res := rw.Resource()

	Debugf("upstream responded in %s", time.Now().Sub(t))

	if !h.isCacheable(r, res) {
		Debugf("resource is uncacheable")
		rw.Header().Set(CacheHeader, "SKIP")
		return
	}

	rw.Header().Set(CacheHeader, "MISS")
	h.storeResource(r, res)
}

func isStatusCacheableByDefault(status int) bool {
	allowed := []int{
		http.StatusOK,
		http.StatusFound,
		http.StatusNotModified,
		http.StatusNonAuthoritativeInfo,
		http.StatusMultipleChoices,
		http.StatusMovedPermanently,
		http.StatusGone,
		http.StatusPartialContent,
	}

	for _, a := range allowed {
		if a == status {
			return true
		}
	}

	return false
}

func (h *Handler) isCacheable(r *http.Request, res *Resource) bool {
	cc, err := res.cacheControl()
	if err != nil {
		Errorf("Error parsing cache-control: %s", err.Error())
		return false
	}

	if cc.Has("no-cache") || cc.Has("no-store") || (cc.Has("private") && h.Shared) {
		return false
	}

	if r.Header.Get("Authorization") != "" && h.Shared {
		return false
	}

	if res.HasExplicitExpiration() {
		return true
	}

	// if isStatusCacheableByDefault(res.Status()) {
	if res.Status() == http.StatusOK {
		if cc.Has("public") {
			return true
		} else if res.HasValidators() {
			return true
		} else if res.HeuristicFreshness() > time.Duration(0) {
			return true
		}
	}

	return false
}

func (h *Handler) serveResource(res *Resource, w http.ResponseWriter, req *http.Request) {
	for key, headers := range res.Header() {
		for _, header := range headers {
			w.Header().Add(key, header)
		}
	}

	age, err := res.Age()
	if err != nil {
		http.Error(w, "Error calculating age: "+err.Error(),
			http.StatusInternalServerError)
		return
	}

	warnings, _ := res.Warnings()
	for _, warn := range warnings {
		w.Header().Add("Warning", warn)
	}

	w.Header().Set("Age", fmt.Sprintf("%.f", age.Seconds()))

	// hacky handler for non-ok statuses
	if res.Status() != http.StatusOK {
		w.WriteHeader(res.Status())
		io.Copy(w, res)
	} else {
		http.ServeContent(w, req, "", res.LastModified(), res)
	}
}

func (h *Handler) storeResource(r *http.Request, res *Resource) {
	Writes.Add(1)

	go func() {
		defer Writes.Done()
		t := time.Now()
		keys := []string{NewRequestKey(r).String()}
		headers := res.Header()

		// store a secondary vary version
		if vary := headers.Get("Vary"); vary != "" {
			keys = append(keys, NewRequestKey(r).Vary(vary, r).String())
		}

		if err := h.cache.Store(res, keys...); err != nil {
			Errorf("storing resource %#v failed: %s", err.Error(), keys)
		}

		Debugf("stored resources %+v in %s", keys, time.Now().Sub(t))
	}()
}

func (h *Handler) validate(r *request, res *Resource) (valid bool) {
	req := r.cloneRequest()
	resHeaders := res.Header()

	if etag := resHeaders.Get("Etag"); etag != "" {
		req.Header.Set("If-None-Match", etag)
	} else if lastMod := resHeaders.Get("Last-Modified"); lastMod != "" {
		req.Header.Set("If-Modified-Since", lastMod)
	}

	resp := httptest.NewRecorder()
	h.upstream.ServeHTTP(resp, req)
	resp.Flush()

	return validateHeaders(resHeaders, resp.HeaderMap)
}

var validationHeaders = []string{"ETag", "Content-MD5", "Last-Modified", "Content-Length"}

func validateHeaders(h1, h2 http.Header) bool {
	for _, header := range validationHeaders {
		if value := h2.Get(header); value != "" {
			if h1.Get(header) != value {
				Debugf("%s changed, %q != %q", header, value, h1.Get(header))
				return false
			}
		}
	}

	return true
}

// lookupResource finds the best matching Resource for the
// request, or nil and ErrNotFoundInCache if none is found
func (h *Handler) lookup(req *request) (*Resource, error) {
	key := req.key()
	res, err := h.cache.Retrieve(key.String())

	// HEAD requests can possibly be served from GET
	if err == ErrNotFoundInCache && req.Method == "HEAD" {
		res, err = h.cache.Retrieve(key.ForMethod("GET").String())
		if err != nil {
			return nil, err
		}

		if res.HasExplicitExpiration() && req.isCacheable() {
			Debugf("using cached GET request for serving HEAD")
			return res, nil
		} else {
			return nil, ErrNotFoundInCache
		}
	} else if err != nil {
		return res, err
	}

	// Secondary lookup for Vary
	if vary := res.Header().Get("Vary"); vary != "" {
		res, err = h.cache.Retrieve(key.Vary(vary, req.Request).String())
		if err != nil {
			return res, err
		}
	}

	return res, nil
}

type request struct {
	*http.Request
	cc CacheControl
}

func (r *request) isCacheable() bool {
	if !(r.Method == "GET" || r.Method == "HEAD") {
		return false
	}

	cc, err := r.cacheControl()
	if err != nil {
		return false
	}

	maxAge, _ := cc.Get("max-age")

	if cc.Has("no-store") || cc.Has("no-cache") || maxAge == "0" {
		return false
	}

	return true
}

func (r *request) isBadRequest() (valid bool, reason string) {
	if _, err := r.cacheControl(); err != nil {
		return false, "Failed to parse Cache-Control header"
	}

	if r.Request.Proto == "HTTP/1.1" && r.Request.Host == "" {
		return true, "Host header can't be empty"
	}
	return false, ""
}

func (r *request) mustValidate() bool {
	if r.Request.Header.Get("If-Modified-Since") != "" {
		return true
	}

	cc, err := r.cacheControl()
	if err != nil {
		return false
	}

	return cc.Has("no-cache")
}

func (r *request) maxStale() (time.Duration, error) {
	cc, err := r.cacheControl()
	if err != nil {
		return time.Duration(0), err
	}

	if cc.Has("max-stale") {
		return cc.Duration("max-stale")
	}

	return time.Duration(0), nil
}

func (r *request) cacheControl() (CacheControl, error) {
	if r.cc != nil {
		return r.cc, nil
	}

	cc, err := ParseCacheControl(r.Request.Header.Get("Cache-Control"))
	if err != nil {
		return cc, err
	}

	r.cc = cc
	return cc, nil
}

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

func (r *request) key() Key {
	return NewRequestKey(r.Request)
}

func newResponseWriter(w http.ResponseWriter) *responseWriter {
	return &responseWriter{
		ResponseWriter: w,
		buf:            &bytes.Buffer{},
	}
}

type responseWriter struct {
	http.ResponseWriter
	buf        *bytes.Buffer
	statusCode int
}

func (rw *responseWriter) WriteHeader(status int) {
	rw.statusCode = status
	rw.ResponseWriter.WriteHeader(status)
}

func (rw *responseWriter) Write(b []byte) (int, error) {
	rw.buf.Write(b)
	return rw.ResponseWriter.Write(b)
}

// Resource returns a copy of the responseWriter as a Resource object
func (rw *responseWriter) Resource() *Resource {
	return NewResourceBytes(rw.statusCode, rw.buf.Bytes(), rw.Header())
}
<commit_msg>Revert "Only cache HTTP 200 OK until cache stores status codes"<commit_after>package httpcache

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"sync"
	"time"
)

const (
	CacheHeader = "X-Cache"
)

var Writes sync.WaitGroup

type Handler struct {
	Shared   bool
	upstream http.Handler
	cache    *Cache
}

func NewHandler(cache *Cache, upstream http.Handler) *Handler {
	return &Handler{
		upstream: upstream,
		cache:    cache,
		Shared:   false,
	}
}

func (h *Handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	req := &request{Request: r}

	if bad, reason := req.isBadRequest(); bad {
		http.Error(rw, "invalid request: "+reason,
			http.StatusBadRequest)
		return
	}

	if !req.isCacheable() {
		Debugf("request not cacheable")
		rw.Header().Set(CacheHeader, "SKIP")
		h.pipeUpstream(rw, r)
		return
	}

	res, err := h.lookup(req)
	if err != nil && err != ErrNotFoundInCache {
		http.Error(rw, "lookup error: "+err.Error(),
			http.StatusInternalServerError)
		return
	}

	if err == ErrNotFoundInCache {
		Debugf("%s %s not in cache", r.Method, r.URL.String())
		h.passUpstream(rw, r)
		return
	} else {
		Debugf("%s %s found in cache", r.Method, r.URL.String())
	}

	if h.needsValidation(res, req) {
		Debugf("validating cached response")

		if !h.validate(req, res) {
			Debugf("response is changed")
			h.passUpstream(rw, r)
			return
		} else {
			Debugf("response is valid")
			h.cache.Freshen(res, NewRequestKey(r).String())
		}
	}

	h.serveResource(res, rw, r)
	rw.Header().Set(CacheHeader, "HIT")

	if err := res.Close(); err != nil {
		Errorf("Error closing resource: %s", err.Error())
	}
}

func (h *Handler) needsValidation(res *Resource, req *request) bool {
	if req.mustValidate() || res.MustValidate() {
		return true
	}

	if res.IsStale() {
		return true
	}

	maxAge, err := res.MaxAge(h.Shared)
	if err != nil {
		Debugf("error calculating max-age: %s", err.Error())
		return true
	}

	age, err := res.Age()
	if err != nil {
		Debugf("error calculating age: %s", err.Error())
		return true
	}

	if hFresh := res.HeuristicFreshness(); hFresh > age {
		Debugf("heuristic freshness of %q", hFresh)
		return false
	}

	if age > maxAge {
		Debugf("age %q > max-age %q", age, maxAge)
		maxStale, _ := req.maxStale()
		if maxStale > (age - maxAge) {
			log.Printf("stale, but within allowed max-stale period of %s", maxStale)
			return false
		}
		return true
	}

	return false
}

// pipeUpstream makes the request via the upstream handler, the response is not stored or modified
func (h *Handler) pipeUpstream(w http.ResponseWriter, r *http.Request) {
	rw := newResponseWriter(w)

	Debugf("piping request upstream")
	h.upstream.ServeHTTP(rw, r)

	if r.Method == "HEAD" {
		res := rw.Resource()
		h.cache.Freshen(res, NewRequestKey(r).ForMethod("GET").String())
	}
}

// passUpstream makes the request via the upstream handler and stores the result
func (h *Handler) passUpstream(w http.ResponseWriter, r *http.Request) {
	rw := newResponseWriter(w)

	t := time.Now()
	Debugf("passing request upstream")
	h.upstream.ServeHTTP(rw, r)
	res := rw.Resource()

	Debugf("upstream responded in %s", time.Now().Sub(t))

	if !h.isCacheable(r, res) {
		Debugf("resource is uncacheable")
		rw.Header().Set(CacheHeader, "SKIP")
		return
	}

	rw.Header().Set(CacheHeader, "MISS")
	h.storeResource(r, res)
}

func isStatusCacheableByDefault(status int) bool {
	allowed := []int{
		http.StatusOK,
		http.StatusFound,
		http.StatusNotModified,
		http.StatusNonAuthoritativeInfo,
		http.StatusMultipleChoices,
		http.StatusMovedPermanently,
		http.StatusGone,
		http.StatusPartialContent,
	}

	for _, a := range allowed {
		if a == status {
			return true
		}
	}

	return false
}

func (h *Handler) isCacheable(r *http.Request, res *Resource) bool {
	cc, err := res.cacheControl()
	if err != nil {
		Errorf("Error parsing cache-control: %s", err.Error())
		return false
	}

	if cc.Has("no-cache") || cc.Has("no-store") || (cc.Has("private") && h.Shared) {
		return false
	}

	if r.Header.Get("Authorization") != "" && h.Shared {
		return false
	}

	if res.HasExplicitExpiration() {
		return true
	}

	if isStatusCacheableByDefault(res.Status()) {
		if cc.Has("public") {
			return true
		} else if res.HasValidators() {
			return true
		} else if res.HeuristicFreshness() > time.Duration(0) {
			return true
		}
	}

	return false
}

func (h *Handler) serveResource(res *Resource, w http.ResponseWriter, req *http.Request) {
	for key, headers := range res.Header() {
		for _, header := range headers {
			w.Header().Add(key, header)
		}
	}

	age, err := res.Age()
	if err != nil {
		http.Error(w, "Error calculating age: "+err.Error(),
			http.StatusInternalServerError)
		return
	}

	warnings, _ := res.Warnings()
	for _, warn := range warnings {
		w.Header().Add("Warning", warn)
	}

	w.Header().Set("Age", fmt.Sprintf("%.f", age.Seconds()))

	// hacky handler for non-ok statuses
	if res.Status() != http.StatusOK {
		w.WriteHeader(res.Status())
		io.Copy(w, res)
	} else {
		http.ServeContent(w, req, "", res.LastModified(), res)
	}
}

func (h *Handler) storeResource(r *http.Request, res *Resource) {
	Writes.Add(1)

	go func() {
		defer Writes.Done()
		t := time.Now()
		keys := []string{NewRequestKey(r).String()}
		headers := res.Header()

		// store a secondary vary version
		if vary := headers.Get("Vary"); vary != "" {
			keys = append(keys, NewRequestKey(r).Vary(vary, r).String())
		}

		if err := h.cache.Store(res, keys...); err != nil {
			Errorf("storing resource %#v failed: %s", err.Error(), keys)
		}

		Debugf("stored resources %+v in %s", keys, time.Now().Sub(t))
	}()
}

func (h *Handler) validate(r *request, res *Resource) (valid bool) {
	req := r.cloneRequest()
	resHeaders := res.Header()

	if etag := resHeaders.Get("Etag"); etag != "" {
		req.Header.Set("If-None-Match", etag)
	} else if lastMod := resHeaders.Get("Last-Modified"); lastMod != "" {
		req.Header.Set("If-Modified-Since", lastMod)
	}

	resp := httptest.NewRecorder()
	h.upstream.ServeHTTP(resp, req)
	resp.Flush()

	return validateHeaders(resHeaders, resp.HeaderMap)
}

var validationHeaders = []string{"ETag", "Content-MD5", "Last-Modified", "Content-Length"}

func validateHeaders(h1, h2 http.Header) bool {
	for _, header := range validationHeaders {
		if value := h2.Get(header); value != "" {
			if h1.Get(header) != value {
				Debugf("%s changed, %q != %q", header, value, h1.Get(header))
				return false
			}
		}
	}

	return true
}

// lookupResource finds the best matching Resource for the
// request, or nil and ErrNotFoundInCache if none is found
func (h *Handler) lookup(req *request) (*Resource, error) {
	key := req.key()
	res, err := h.cache.Retrieve(key.String())

	// HEAD requests can possibly be served from GET
	if err == ErrNotFoundInCache && req.Method == "HEAD" {
		res, err = h.cache.Retrieve(key.ForMethod("GET").String())
		if err != nil {
			return nil, err
		}

		if res.HasExplicitExpiration() && req.isCacheable() {
			Debugf("using cached GET request for serving HEAD")
			return res, nil
		} else {
			return nil, ErrNotFoundInCache
		}
	} else if err != nil {
		return res, err
	}

	// Secondary lookup for Vary
	if vary := res.Header().Get("Vary"); vary != "" {
		res, err = h.cache.Retrieve(key.Vary(vary, req.Request).String())
		if err != nil {
			return res, err
		}
	}

	return res, nil
}

type request struct {
	*http.Request
	cc CacheControl
}

func (r *request) isCacheable() bool {
	if !(r.Method == "GET" || r.Method == "HEAD") {
		return false
	}

	cc, err := r.cacheControl()
	if err != nil {
		return false
	}

	maxAge, _ := cc.Get("max-age")

	if cc.Has("no-store") || cc.Has("no-cache") || maxAge == "0" {
		return false
	}

	return true
}

func (r *request) isBadRequest() (valid bool, reason string) {
	if _, err := r.cacheControl(); err != nil {
		return false, "Failed to parse Cache-Control header"
	}

	if r.Request.Proto == "HTTP/1.1" && r.Request.Host == "" {
		return true, "Host header can't be empty"
	}
	return false, ""
}

func (r *request) mustValidate() bool {
	if r.Request.Header.Get("If-Modified-Since") != "" {
		return true
	}

	cc, err := r.cacheControl()
	if err != nil {
		return false
	}

	return cc.Has("no-cache")
}

func (r *request) maxStale() (time.Duration, error) {
	cc, err := r.cacheControl()
	if err != nil {
		return time.Duration(0), err
	}

	if cc.Has("max-stale") {
		return cc.Duration("max-stale")
	}

	return time.Duration(0), nil
}

func (r *request) cacheControl() (CacheControl, error) {
	if r.cc != nil {
		return r.cc, nil
	}

	cc, err := ParseCacheControl(r.Request.Header.Get("Cache-Control"))
	if err != nil {
		return cc, err
	}

	r.cc = cc
	return cc, nil
}

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

func (r *request) key() Key {
	return NewRequestKey(r.Request)
}

func newResponseWriter(w http.ResponseWriter) *responseWriter {
	return &responseWriter{
		ResponseWriter: w,
		buf:            &bytes.Buffer{},
	}
}

type responseWriter struct {
	http.ResponseWriter
	buf        *bytes.Buffer
	statusCode int
}

func (rw *responseWriter) WriteHeader(status int) {
	rw.statusCode = status
	rw.ResponseWriter.WriteHeader(status)
}

func (rw *responseWriter) Write(b []byte) (int, error) {
	rw.buf.Write(b)
	return rw.ResponseWriter.Write(b)
}

// Resource returns a copy of the responseWriter as a Resource object
func (rw *responseWriter) Resource() *Resource {
	return NewResourceBytes(rw.statusCode, rw.buf.Bytes(), rw.Header())
}
<|endoftext|>


<commit_before>package bigtiff

import "github.com/jonathanpittman/tiff"

/* Field type definitions
16 = LONG8	64-bit unsigned integer.
17 = SLONG8	64-bit signed integer.
18 = IFD8	64-bit unsigned integer offset value
*/

var (
	FTLong8  = tiff.NewFieldType(16, "LONG8", 8, false, nil)
	FTSLong8 = tiff.NewFieldType(17, "SLONG8", 8, true, nil)
	FTIFD8   = tiff.NewFieldType(18, "IFD8", 8, false, nil)
)
<commit_msg>Ensure that bigtiff registers its new FieldTypes with tiff.<commit_after>package bigtiff

import "github.com/jonathanpittman/tiff"

/* Field type definitions
16 = LONG8	64-bit unsigned integer.
17 = SLONG8	64-bit signed integer.
18 = IFD8	64-bit unsigned integer offset value
*/

var (
	FTLong8  = tiff.NewFieldType(16, "LONG8", 8, false, nil)
	FTSLong8 = tiff.NewFieldType(17, "SLONG8", 8, true, nil)
	FTIFD8   = tiff.NewFieldType(18, "IFD8", 8, false, nil)
)

func init() {
	tiff.RegisterFieldType(FTLong8)
	tiff.RegisterFieldType(FTSLong8)
	tiff.RegisterFieldType(FTIFD8)
}
<|endoftext|>