<commit_before>// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// 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 General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program.  If not, see <http://www.gnu.org/licenses/>.

// An example command-line tool that uses opennota/markdown to process markdown input.
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/opennota/html"
	"github.com/opennota/markdown"

	"github.com/pkg/browser"
)

var (
	allowhtml   bool
	tables      bool
	linkify     bool
	typographer bool
	xhtml       bool

	title          string
	rendererOutput string

	wg sync.WaitGroup
)

func readFromFile(fn string) ([]byte, error) {
	f, err := os.Open(fn)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return ioutil.ReadAll(f)
}

func readFromWeb(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func readInput(input string) ([]byte, error) {
	if strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://") {
		return readFromWeb(input)
	}
	return readFromFile(input)
}

func extractText(tok markdown.Token) string {
	switch tok := tok.(type) {
	case *markdown.Text:
		return tok.Content
	case *markdown.Inline:
		text := ""
		for _, tok := range tok.Children {
			text += extractText(tok)
		}
		return text
	}
	return ""
}

func writePreamble(w io.Writer) error {
	var opening string
	var ending string
	if xhtml {
		opening = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
`
		ending = " /"
	} else {
		opening = `<!DOCTYPE html>
`
	}
	_, err := fmt.Fprintf(w, `%s

<meta charset="utf-8"%s>
<title>%s</title>

`, opening, ending, html.EscapeString(title))

	return err
}

func writePostamble(w io.Writer) error {
	_, err := fmt.Fprint(w, `

`)
	return err
}

func handler(w http.ResponseWriter, r *http.Request) {
	defer wg.Done()

	err := writePreamble(w)
	if err != nil {
		log.Println(err)
	}

	_, err = fmt.Fprint(w, rendererOutput)
	if err != nil {
		log.Println(err)
	}

	err = writePostamble(w)
	if err != nil {
		log.Println(err)
	}

	time.Sleep(1)
}

func main() {
	log.SetFlags(log.Lshortfile)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `Usage: mdtool [options] {inputfile|URL} [outputfile]

Options:
  +h[tml]         Enable HTML
  +l[inkify]      Enable autolinking
  +ta[bles]       Enable GFM tables
  +ty[pographer]  Enable typographic replacements
  +x[html]        XHTML output

  -help           Display help

Use 'browser:' in place of the output file to get the output in a browser.
`)
	}
	flag.Parse()
	var documents []string
	for _, arg := range flag.Args() {
		switch arg {
		case "+html", "+h":
			allowhtml = true
		case "+linkify", "+l":
			linkify = true
		case "+tables", "+ta":
			tables = true
		case "+typographer", "+ty":
			typographer = true
		case "+t":
			fmt.Fprintf(os.Stderr, "ambiguous option: +t; did you mean +ta[bles] or +ty[pographer]?")
			os.Exit(1)
		case "+xhtml", "+x":
			xhtml = true
		default:
			documents = append(documents, arg)
		}
	}
	if len(documents) == 0 || len(documents) > 2 {
		flag.Usage()
		os.Exit(1)
	}

	data, err := readInput(documents[0])
	if err != nil {
		log.Fatal(err)
	}

	md := markdown.New(
		markdown.HTML(allowhtml),
		markdown.Tables(tables),
		markdown.Linkify(linkify),
		markdown.Typographer(typographer),
		markdown.XHTMLOutput(xhtml),
	)

	tokens := md.Parse(data)
	if len(tokens) > 0 {
		if heading, ok := tokens[0].(*markdown.HeadingOpen); ok {
			for i := 1; i < len(tokens); i++ {
				if tok, ok := tokens[i].(*markdown.HeadingClose); ok && tok.Lvl == heading.Lvl {
					break
				}
				title += extractText(tokens[i])
			}
		}
	}

	rendererOutput = md.RenderTokensToString(tokens)

	if len(documents) == 1 {
		writePreamble(os.Stdout)
		fmt.Println(rendererOutput)
		writePostamble(os.Stdout)
	} else if documents[1] == "browser:" {
		srv := httptest.NewServer(http.HandlerFunc(handler))
		wg.Add(1)
		err = browser.OpenURL(srv.URL)
		if err != nil {
			log.Fatal(err)
		}
		wg.Wait()
	} else {
		f, err := os.OpenFile(documents[1], os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
		if err != nil {
			log.Fatal(err)
		}
		defer func() {
			err := f.Close()
			if err != nil {
				log.Println(err)
			}
		}()

		err = writePreamble(f)
		if err != nil {
			log.Fatal(err)
		}

		_, err = f.WriteString(rendererOutput)
		if err != nil {
			log.Fatal(err)
		}

		err = writePostamble(f)
		if err != nil {
			log.Fatal(err)
		}
	}
}
<commit_msg>Trim space from title<commit_after>// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// 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 General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program.  If not, see <http://www.gnu.org/licenses/>.

// An example command-line tool that uses opennota/markdown to process markdown input.
package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/opennota/html"
	"github.com/opennota/markdown"

	"github.com/pkg/browser"
)

var (
	allowhtml   bool
	tables      bool
	linkify     bool
	typographer bool
	xhtml       bool

	title          string
	rendererOutput string

	wg sync.WaitGroup
)

func readFromFile(fn string) ([]byte, error) {
	f, err := os.Open(fn)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	return ioutil.ReadAll(f)
}

func readFromWeb(url string) ([]byte, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func readInput(input string) ([]byte, error) {
	if strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://") {
		return readFromWeb(input)
	}
	return readFromFile(input)
}

func extractText(tok markdown.Token) string {
	switch tok := tok.(type) {
	case *markdown.Text:
		return tok.Content
	case *markdown.Inline:
		text := ""
		for _, tok := range tok.Children {
			text += extractText(tok)
		}
		return text
	}
	return ""
}

func writePreamble(w io.Writer) error {
	var opening string
	var ending string
	if xhtml {
		opening = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
`
		ending = " /"
	} else {
		opening = `<!DOCTYPE html>
`
	}
	_, err := fmt.Fprintf(w, `%s

<meta charset="utf-8"%s>
<title>%s</title>

`, opening, ending, html.EscapeString(title))

	return err
}

func writePostamble(w io.Writer) error {
	_, err := fmt.Fprint(w, `

`)
	return err
}

func handler(w http.ResponseWriter, r *http.Request) {
	defer wg.Done()

	err := writePreamble(w)
	if err != nil {
		log.Println(err)
	}

	_, err = fmt.Fprint(w, rendererOutput)
	if err != nil {
		log.Println(err)
	}

	err = writePostamble(w)
	if err != nil {
		log.Println(err)
	}

	time.Sleep(1)
}

func main() {
	log.SetFlags(log.Lshortfile)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `Usage: mdtool [options] {inputfile|URL} [outputfile]

Options:
  +h[tml]         Enable HTML
  +l[inkify]      Enable autolinking
  +ta[bles]       Enable GFM tables
  +ty[pographer]  Enable typographic replacements
  +x[html]        XHTML output

  -help           Display help

Use 'browser:' in place of the output file to get the output in a browser.
`)
	}
	flag.Parse()
	var documents []string
	for _, arg := range flag.Args() {
		switch arg {
		case "+html", "+h":
			allowhtml = true
		case "+linkify", "+l":
			linkify = true
		case "+tables", "+ta":
			tables = true
		case "+typographer", "+ty":
			typographer = true
		case "+t":
			fmt.Fprintf(os.Stderr, "ambiguous option: +t; did you mean +ta[bles] or +ty[pographer]?")
			os.Exit(1)
		case "+xhtml", "+x":
			xhtml = true
		default:
			documents = append(documents, arg)
		}
	}
	if len(documents) == 0 || len(documents) > 2 {
		flag.Usage()
		os.Exit(1)
	}

	data, err := readInput(documents[0])
	if err != nil {
		log.Fatal(err)
	}

	md := markdown.New(
		markdown.HTML(allowhtml),
		markdown.Tables(tables),
		markdown.Linkify(linkify),
		markdown.Typographer(typographer),
		markdown.XHTMLOutput(xhtml),
	)

	tokens := md.Parse(data)
	if len(tokens) > 0 {
		if heading, ok := tokens[0].(*markdown.HeadingOpen); ok {
			for i := 1; i < len(tokens); i++ {
				if tok, ok := tokens[i].(*markdown.HeadingClose); ok && tok.Lvl == heading.Lvl {
					break
				}
				title += extractText(tokens[i])
			}
			title = strings.TrimSpace(title)
		}
	}

	rendererOutput = md.RenderTokensToString(tokens)

	if len(documents) == 1 {
		writePreamble(os.Stdout)
		fmt.Println(rendererOutput)
		writePostamble(os.Stdout)
	} else if documents[1] == "browser:" {
		srv := httptest.NewServer(http.HandlerFunc(handler))
		wg.Add(1)
		err = browser.OpenURL(srv.URL)
		if err != nil {
			log.Fatal(err)
		}
		wg.Wait()
	} else {
		f, err := os.OpenFile(documents[1], os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
		if err != nil {
			log.Fatal(err)
		}
		defer func() {
			err := f.Close()
			if err != nil {
				log.Println(err)
			}
		}()

		err = writePreamble(f)
		if err != nil {
			log.Fatal(err)
		}

		_, err = f.WriteString(rendererOutput)
		if err != nil {
			log.Fatal(err)
		}

		err = writePostamble(f)
		if err != nil {
			log.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>possible race condition in directory creation<commit_after><|endoftext|>


<commit_before><commit_msg>FinalUrlでもすでにアノテーション済みかを判別するように<commit_after><|endoftext|>


<commit_before>package lfs

import (
	"bytes"
	"fmt"
)

var (
	// prePushHook invokes `git lfs push` at the pre-push phase.
	prePushHook = &Hook{
		Type:     "pre-push",
		Contents: "#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/pre-push.\\n\"; exit 2; }\ngit lfs pre-push \"$@\"",
		Upgradeables: []string{
			"#!/bin/sh\ngit lfs push --stdin $*",
			"#!/bin/sh\ngit lfs push --stdin \"$@\"",
			"#!/bin/sh\ngit lfs pre-push \"$@\"",
			"#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 0; }\ngit lfs pre-push \"$@\"",
			"#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 2; }\ngit lfs pre-push \"$@\"",
		},
	}

	hooks = []*Hook{
		prePushHook,
	}

	filters = &Attribute{
		Section: "filter.lfs",
		Properties: map[string]string{
			"clean":    "git-lfs clean -- %f",
			"smudge":   "git-lfs smudge -- %f",
			"process":  "git-lfs filter",
			"required": "true",
		},
		Upgradeables: map[string][]string{
			"clean":  []string{"git-lfs clean %f"},
			"smudge": []string{"git-lfs smudge %f"},
			// TODO: process here, too?
		},
	}

	passFilters = &Attribute{
		Section: "filter.lfs",
		Properties: map[string]string{
			"clean":    "git-lfs clean -- %f",
			"smudge":   "git-lfs smudge --skip -- %f",
			"process":  "git-lfs filter --skip",
			"required": "true",
		},
		Upgradeables: map[string][]string{
			"clean":  []string{"git-lfs clean %f"},
			"smudge": []string{"git-lfs smudge --skip %f"},
		},
	}
)

// Get user-readable manual install steps for hooks
func GetHookInstallSteps() string {

	var buf bytes.Buffer
	for _, h := range hooks {
		buf.WriteString(fmt.Sprintf("Add the following to .git/hooks/%s :\n\n", h.Type))
		buf.WriteString(h.Contents)
		buf.WriteString("\n")
	}
	return buf.String()
}

// InstallHooks installs all hooks in the `hooks` var.
func InstallHooks(force bool) error {
	for _, h := range hooks {
		if err := h.Install(force); err != nil {
			return err
		}
	}

	return nil
}

// UninstallHooks removes all hooks in range of the `hooks` var.
func UninstallHooks() error {
	for _, h := range hooks {
		if err := h.Uninstall(); err != nil {
			return err
		}
	}

	return nil
}

// InstallFilters installs filters necessary for git-lfs to process normal git
// operations. Currently, that list includes:
//   - smudge filter
//   - clean filter
//
// An error will be returned if a filter is unable to be set, or if the required
// filters were not present.
func InstallFilters(opt InstallOptions, passThrough bool) error {
	if passThrough {
		return passFilters.Install(opt)
	}
	return filters.Install(opt)
}

// UninstallFilters proxies into the Uninstall method on the Filters type to
// remove all installed filters.
func UninstallFilters() error {
	filters.Uninstall()
	return nil
}
<commit_msg>install filter on upgrade<commit_after>package lfs

import (
	"bytes"
	"fmt"
)

var (
	// prePushHook invokes `git lfs push` at the pre-push phase.
	prePushHook = &Hook{
		Type:     "pre-push",
		Contents: "#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/pre-push.\\n\"; exit 2; }\ngit lfs pre-push \"$@\"",
		Upgradeables: []string{
			"#!/bin/sh\ngit lfs push --stdin $*",
			"#!/bin/sh\ngit lfs push --stdin \"$@\"",
			"#!/bin/sh\ngit lfs pre-push \"$@\"",
			"#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 0; }\ngit lfs pre-push \"$@\"",
			"#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 2; }\ngit lfs pre-push \"$@\"",
		},
	}

	hooks = []*Hook{
		prePushHook,
	}

	filters = &Attribute{
		Section: "filter.lfs",
		Properties: map[string]string{
			"clean":    "git-lfs clean -- %f",
			"smudge":   "git-lfs smudge -- %f",
			"process":  "git-lfs filter",
			"required": "true",
		},
		Upgradeables: map[string][]string{
			"clean":   []string{"git-lfs clean %f"},
			"smudge":  []string{"git-lfs smudge %f"},
			"process": []string{"git-lfs filter"},
		},
	}

	passFilters = &Attribute{
		Section: "filter.lfs",
		Properties: map[string]string{
			"clean":    "git-lfs clean -- %f",
			"smudge":   "git-lfs smudge --skip -- %f",
			"process":  "git-lfs filter --skip",
			"required": "true",
		},
		Upgradeables: map[string][]string{
			"clean":   []string{"git-lfs clean %f"},
			"smudge":  []string{"git-lfs smudge --skip %f"},
			"process": []string{"git-lfs filter --skip"},
		},
	}
)

// Get user-readable manual install steps for hooks
func GetHookInstallSteps() string {

	var buf bytes.Buffer
	for _, h := range hooks {
		buf.WriteString(fmt.Sprintf("Add the following to .git/hooks/%s :\n\n", h.Type))
		buf.WriteString(h.Contents)
		buf.WriteString("\n")
	}
	return buf.String()
}

// InstallHooks installs all hooks in the `hooks` var.
func InstallHooks(force bool) error {
	for _, h := range hooks {
		if err := h.Install(force); err != nil {
			return err
		}
	}

	return nil
}

// UninstallHooks removes all hooks in range of the `hooks` var.
func UninstallHooks() error {
	for _, h := range hooks {
		if err := h.Uninstall(); err != nil {
			return err
		}
	}

	return nil
}

// InstallFilters installs filters necessary for git-lfs to process normal git
// operations. Currently, that list includes:
//   - smudge filter
//   - clean filter
//
// An error will be returned if a filter is unable to be set, or if the required
// filters were not present.
func InstallFilters(opt InstallOptions, passThrough bool) error {
	if passThrough {
		return passFilters.Install(opt)
	}
	return filters.Install(opt)
}

// UninstallFilters proxies into the Uninstall method on the Filters type to
// remove all installed filters.
func UninstallFilters() error {
	filters.Uninstall()
	return nil
}
<|endoftext|>


<commit_before>package application

import (
	"os"

	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"

	"github.com/ovh/cds/sdk"
)

func applicationListCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:     "list",
		Short:   "cds application list <projectkey>",
		Long:    ``,
		Aliases: []string{"ls", "ps"},
		Run:     listApplications,
	}

	return cmd
}

func listApplications(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		sdk.Exit("Wrong usage: see %s\n", cmd.Short)
	}

	apps, err := sdk.ListApplications(args[0])
	if err != nil {
		sdk.Exit("%s\n", err)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Name"})
	table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
	table.SetCenterSeparator("|")

	for _, a := range apps {
		table.Append([]string{a.Name})
	}
	table.Render()
}
<commit_msg>fix (cli): consistent list display #748 (#768)<commit_after>package application

import (
	"fmt"
	"os"

	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"

	"github.com/ovh/cds/sdk"
)

var pretty *bool

func applicationListCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:     "list",
		Short:   "cds application list <projectkey>",
		Long:    ``,
		Aliases: []string{"ls", "ps"},
		Run:     listApplications,
	}

	pretty = cmd.Flags().BoolP("pretty", "", false, "display with a pretty table")

	return cmd
}

func listApplications(cmd *cobra.Command, args []string) {
	var apps []sdk.Application
	if len(args) != 1 {
		sdk.Exit("Wrong usage: see %s\n", cmd.Short)
	}

	apps, err := sdk.ListApplications(args[0])
	if err != nil {
		sdk.Exit("%s\n", err)
	}

	if *pretty {
		table := tablewriter.NewWriter(os.Stdout)
		table.SetHeader([]string{"Name"})
		table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
		table.SetCenterSeparator("|")

		for _, a := range apps {
			table.Append([]string{a.Name})
		}
		table.Render()
		return
	}

	fmt.Println("NAME")
	for _, app := range apps {
		fmt.Println(app.Name)
	}
}
<|endoftext|>


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

package parsers

import (
	"regexp"
)

// AllRecipesDotComParser is a RecipeParser for the site
// allrecipes.com.
type AllRecipesDotComParser struct{}

// GetName gets the title of the recipe.
func (a AllRecipesDotComParser) GetName(data []byte) string {
	return getFirstH1(data)
}

// GetDirections gets the directions of the recipe.
func (a AllRecipesDotComParser) GetDirections(data []byte) []string {

	// It looks like the directions are the only ordered list, so just
	// get the first.
	re := regexp.MustCompile(`(?sU)<ol>.*</ol>`)
	ol := re.Find(data)

	// Get each list item, removing HTML cruft.
	re = regexp.MustCompile(`(?sU)<li><span class="plaincharacterwrap break">(.*)</span></li>`)
	directions := re.FindAllSubmatch(ol, -1)

	list := []string{}
	for _, dir := range directions {
		// If there is a submatch, append it. Otherwise skip it.
		if len(dir) > 1 {
			list = append(list, cleanField(string(dir[1])))
		}
	}

	return list
}

// GetIngredients gets the ingredients of the recipe.
func (a AllRecipesDotComParser) GetIngredients(data []byte) []string {

	// We can find the unordered list by classname.
	re := regexp.MustCompile(`(?sU)<ul class="ingredient-wrap">.*</ul>`)
	ul := re.Find(data)

	// Each item in the list has a <p> tag with the ingredients.
	re = regexp.MustCompile(`(?sU)<p class="fl-ing" itemprop="ingredients">.*</p>`)
	ingredients := re.FindAll(ul, -1)

	// These are the regular expressions that parse out the amount and
	// name from the <p> ingredients.
	reamt := regexp.MustCompile(`(?sU)<span id="lblIngAmount" class="ingredient-amount">(.*)</span>`)
	rename := regexp.MustCompile(`(?sU)<span id="lblIngName" class="ingredient-name">(.*)</span>`)

	list := []string{}
	for _, ingredient := range ingredients {
		amount := reamt.FindSubmatch(ingredient)
		name := rename.FindSubmatch(ingredient)

		// This will eventuall be the entire item as a single string.
		item := ""

		// Only add the amount if the submatch was found.
		if len(amount) > 1 {
			item = string(amount[1])
		}

		// Only add the name if the submatch was found.
		if len(name) > 1 {
			// Add a space if we got an amount.
			if item != "" {
				item += " "
			}

			// Append the name.
			item += string(name[1])
		}

		// Add the complete item.
		if item != "" {
			list = append(list, cleanField(item))
		}
	}

	return list
}
<commit_msg>we now get the second column of a recipe list.<commit_after>// Copyright 2013 Joshua Marsh. All rights reserved.  Use of this
// source code is governed by a BSD-style license that can be found in
// the LICENSE file.

package parsers

import (
	"regexp"
)

// AllRecipesDotComParser is a RecipeParser for the site
// allrecipes.com.
type AllRecipesDotComParser struct{}

// GetName gets the title of the recipe.
func (a AllRecipesDotComParser) GetName(data []byte) string {
	return getFirstH1(data)
}

// GetDirections gets the directions of the recipe.
func (a AllRecipesDotComParser) GetDirections(data []byte) []string {

	// It looks like the directions are the only ordered list, so just
	// get the first.
	re := regexp.MustCompile(`(?sU)<ol>.*</ol>`)
	ol := re.Find(data)

	// Get each list item, removing HTML cruft.
	re = regexp.MustCompile(`(?sU)<li><span class="plaincharacterwrap break">(.*)</span></li>`)
	directions := re.FindAllSubmatch(ol, -1)

	list := []string{}
	for _, dir := range directions {
		// If there is a submatch, append it. Otherwise skip it.
		if len(dir) > 1 {
			list = append(list, cleanField(string(dir[1])))
		}
	}

	return list
}

// GetIngredients gets the ingredients of the recipe.
func (a AllRecipesDotComParser) GetIngredients(data []byte) []string {

	// We can find the unordered list by classname.
	re := regexp.MustCompile(`(?sU)<ul class="ingredient-wrap.*</ul>`)
	uls := re.FindAll(data, -1)

	list := []string{}
	for _, ul := range uls {

		// Each item in the list has a <p> tag with the ingredients.
		re = regexp.MustCompile(`(?sU)<p class="fl-ing" itemprop="ingredients">.*</p>`)
		ingredients := re.FindAll(ul, -1)

		// These are the regular expressions that parse out the amount and
		// name from the <p> ingredients.
		reamt := regexp.MustCompile(`(?sU)<span id="lblIngAmount" class="ingredient-amount">(.*)</span>`)
		rename := regexp.MustCompile(`(?sU)<span id="lblIngName" class="ingredient-name">(.*)</span>`)

		for _, ingredient := range ingredients {
			amount := reamt.FindSubmatch(ingredient)
			name := rename.FindSubmatch(ingredient)

			// This will eventually be the entire item as a single string.
			item := ""

			// Only add the amount if the submatch was found.
			if len(amount) > 1 {
				item = string(amount[1])
			}

			// Only add the name if the submatch was found.
			if len(name) > 1 {
				// Add a space if we got an amount.
				if item != "" {
					item += " "
				}

				// Append the name.
				item += string(name[1])
			}

			// Add the complete item.
			if item != "" {
				list = append(list, cleanField(item))
			}
		}
	}
	return list
}
<|endoftext|>


<commit_before>// Copyright 2016 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.

// ctclient is a command-line utility for interacting with CT logs.
package main

import (
	"context"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	ct "github.com/google/certificate-transparency-go"
	"github.com/google/certificate-transparency-go/client"
	"github.com/google/certificate-transparency-go/jsonclient"
	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/certificate-transparency-go/x509util"
)

var logURI = flag.String("log_uri", "http://ct.googleapis.com/aviator", "CT log base URI")
var pubKey = flag.String("pub_key", "", "Name of file containing log's public key")
var certChain = flag.String("cert_chain", "", "Name of file containing certificate chain as concatenated PEM files")
var textOut = flag.Bool("text", true, "Display certificates as text")
var getFirst = flag.Int64("first", -1, "First entry to get")
var getLast = flag.Int64("last", -1, "Last entry to get")

func ctTimestampToTime(ts uint64) time.Time {
	secs := int64(ts / 1000)
	msecs := int64(ts % 1000)
	return time.Unix(secs, msecs*1000000)
}

func signatureToString(signed *ct.DigitallySigned) string {
	return fmt.Sprintf("Signature: Hash=%v Sign=%v Value=%x", signed.Algorithm.Hash, signed.Algorithm.Signature, signed.Signature)
}

func getSTH(ctx context.Context, logClient *client.LogClient) {
	sth, err := logClient.GetSTH(ctx)
	if err != nil {
		log.Fatal(err)
	}
	// Display the STH
	when := ctTimestampToTime(sth.Timestamp)
	fmt.Printf("%v: Got STH for %v log (size=%d) at %v, hash %x\n", when, sth.Version, sth.TreeSize, *logURI, sth.SHA256RootHash)
	fmt.Printf("%v\n", signatureToString(&sth.TreeHeadSignature))
}

func addChain(ctx context.Context, logClient *client.LogClient) {
	if *certChain == "" {
		log.Fatalf("No certificate chain file specified with -cert_chain")
	}
	rest, err := ioutil.ReadFile(*certChain)
	if err != nil {
		log.Fatalf("Failed to read certificate file: %v", err)
	}
	var chain []ct.ASN1Cert
	for {
		var block *pem.Block
		block, rest = pem.Decode(rest)
		if block == nil {
			break
		}
		if block.Type == "CERTIFICATE" {
			chain = append(chain, ct.ASN1Cert{Data: block.Bytes})
		}
	}
	if len(chain) == 0 {
		log.Fatalf("No certificates found in %s", *certChain)
	}

	// Examine the leaf to see if it looks like a pre-certificate.
	isPrecert := false
	leaf, err := x509.ParseCertificate(chain[0].Data)
	if err == nil {
		count, _ := x509util.OidInExtensions(x509.OIDExtensionCTPoison, leaf.Extensions)
		if count > 0 {
			isPrecert = true
			fmt.Print("Uploading pre-certificate to log\n")
		}
	}

	var sct *ct.SignedCertificateTimestamp
	if isPrecert {
		sct, err = logClient.AddPreChain(ctx, chain)
	} else {
		sct, err = logClient.AddChain(ctx, chain)
	}
	if err != nil {
		log.Fatal(err)
	}
	// Display the SCT
	when := ctTimestampToTime(sct.Timestamp)
	fmt.Printf("%v: Uploaded chain of %d certs to %v log at %v\n", when, len(chain), sct.SCTVersion, *logURI)
	fmt.Printf("%v\n", signatureToString(&sct.Signature))
}

func getRoots(ctx context.Context, logClient *client.LogClient) {
	roots, err := logClient.GetAcceptedRoots(ctx)
	if err != nil {
		log.Fatal(err)
	}
	for _, root := range roots {
		showRawCert(root)
	}
}

func getEntries(ctx context.Context, logClient *client.LogClient) {
	if *getFirst == -1 {
		log.Fatal("No -first option supplied")
	}
	if *getLast == -1 {
		log.Fatal("No -last option supplied")
	}
	entries, err := logClient.GetEntries(ctx, *getFirst, *getLast)
	if err != nil {
		log.Fatal(err)
	}
	for _, entry := range entries {
		ts := entry.Leaf.TimestampedEntry
		when := ctTimestampToTime(ts.Timestamp)
		fmt.Printf("Index=%d Timestamp=%v ", entry.Index, when)
		switch ts.EntryType {
		case ct.X509LogEntryType:
			fmt.Printf("X.509 certificate:\n")
			showParsedCert(entry.X509Cert)
		case ct.PrecertLogEntryType:
			fmt.Printf("pre-certificate from issuer with keyhash %x:\n", entry.Precert.IssuerKeyHash)
			showRawCert(entry.Precert.Submitted)
		default:
			log.Fatalf("Unhandled log entry type %d", entry.Leaf.TimestampedEntry.EntryType)
		}
	}
}

func showRawCert(cert ct.ASN1Cert) {
	if *textOut {
		c, err := x509.ParseCertificate(cert.Data)
		if err != nil {
			log.Printf("Error parsing certificate: %q", err.Error())
			return
		}
		showParsedCert(c)
	} else {
		showPEMData(cert.Data)
	}
}

func showParsedCert(cert *x509.Certificate) {
	if *textOut {
		fmt.Printf("%s\n", x509util.CertificateToString(cert))
	} else {
		showPEMData(cert.Raw)
	}
}

func showPEMData(data []byte) {
	if err := pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: data}); err != nil {
		log.Printf("Failed to PEM encode cert: %q", err.Error())
	}
}

func dieWithUsage(msg string) {
	fmt.Fprintln(os.Stderr, msg)
	fmt.Fprintf(os.Stderr, "Usage: ctclient [options] <cmd>\n"+
		"where cmd is one of:\n"+
		"   sth         retrieve signed tree head\n"+
		"   upload      upload cert chain and show SCT (needs -cert_chain)\n"+
		"   getroots    show accepted roots\n"+
		"   getentries  get log entries (needs -first and -last)\n")
	os.Exit(1)
}

func main() {
	flag.Parse()
	httpClient := &http.Client{
		Timeout: 10 * time.Second,
		Transport: &http.Transport{
			TLSHandshakeTimeout:   30 * time.Second,
			ResponseHeaderTimeout: 30 * time.Second,
			MaxIdleConnsPerHost:   10,
			DisableKeepAlives:     false,
			MaxIdleConns:          100,
			IdleConnTimeout:       90 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}
	var opts jsonclient.Options
	if *pubKey != "" {
		pubkey, err := ioutil.ReadFile(*pubKey)
		if err != nil {
			log.Fatal(err)
		}
		opts.PublicKey = string(pubkey)
	}
	logClient, err := client.New(*logURI, httpClient, opts)
	if err != nil {
		log.Fatal(err)
	}
	args := flag.Args()
	if len(args) != 1 {
		dieWithUsage("Need command argument")
	}
	ctx := context.Background()
	cmd := args[0]
	switch cmd {
	case "sth":
		getSTH(ctx, logClient)
	case "upload":
		addChain(ctx, logClient)
	case "getroots", "get_roots", "get-roots":
		getRoots(ctx, logClient)
	case "getentries", "get_entries", "get-entries":
		getEntries(ctx, logClient)
	default:
		dieWithUsage(fmt.Sprintf("Unknown command '%s'", cmd))
	}
}
<commit_msg>ctclient: emit leaf hash on upload<commit_after>// Copyright 2016 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.

// ctclient is a command-line utility for interacting with CT logs.
package main

import (
	"context"
	"crypto/sha256"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	ct "github.com/google/certificate-transparency-go"
	"github.com/google/certificate-transparency-go/client"
	"github.com/google/certificate-transparency-go/jsonclient"
	"github.com/google/certificate-transparency-go/merkletree"
	"github.com/google/certificate-transparency-go/tls"
	"github.com/google/certificate-transparency-go/x509"
	"github.com/google/certificate-transparency-go/x509util"
)

var logURI = flag.String("log_uri", "http://ct.googleapis.com/aviator", "CT log base URI")
var pubKey = flag.String("pub_key", "", "Name of file containing log's public key")
var certChain = flag.String("cert_chain", "", "Name of file containing certificate chain as concatenated PEM files")
var textOut = flag.Bool("text", true, "Display certificates as text")
var getFirst = flag.Int64("first", -1, "First entry to get")
var getLast = flag.Int64("last", -1, "Last entry to get")

func ctTimestampToTime(ts uint64) time.Time {
	secs := int64(ts / 1000)
	msecs := int64(ts % 1000)
	return time.Unix(secs, msecs*1000000)
}

func signatureToString(signed *ct.DigitallySigned) string {
	return fmt.Sprintf("Signature: Hash=%v Sign=%v Value=%x", signed.Algorithm.Hash, signed.Algorithm.Signature, signed.Signature)
}

func getSTH(ctx context.Context, logClient *client.LogClient) {
	sth, err := logClient.GetSTH(ctx)
	if err != nil {
		log.Fatal(err)
	}
	// Display the STH
	when := ctTimestampToTime(sth.Timestamp)
	fmt.Printf("%v: Got STH for %v log (size=%d) at %v, hash %x\n", when, sth.Version, sth.TreeSize, *logURI, sth.SHA256RootHash)
	fmt.Printf("%v\n", signatureToString(&sth.TreeHeadSignature))
}

func addChain(ctx context.Context, logClient *client.LogClient) {
	if *certChain == "" {
		log.Fatalf("No certificate chain file specified with -cert_chain")
	}
	rest, err := ioutil.ReadFile(*certChain)
	if err != nil {
		log.Fatalf("Failed to read certificate file: %v", err)
	}
	var chain []ct.ASN1Cert
	for {
		var block *pem.Block
		block, rest = pem.Decode(rest)
		if block == nil {
			break
		}
		if block.Type == "CERTIFICATE" {
			chain = append(chain, ct.ASN1Cert{Data: block.Bytes})
		}
	}
	if len(chain) == 0 {
		log.Fatalf("No certificates found in %s", *certChain)
	}

	// Examine the leaf to see if it looks like a pre-certificate.
	isPrecert := false
	leaf, err := x509.ParseCertificate(chain[0].Data)
	if err == nil {
		count, _ := x509util.OidInExtensions(x509.OIDExtensionCTPoison, leaf.Extensions)
		if count > 0 {
			isPrecert = true
			fmt.Print("Uploading pre-certificate to log\n")
		}
	}

	var sct *ct.SignedCertificateTimestamp
	if isPrecert {
		sct, err = logClient.AddPreChain(ctx, chain)
	} else {
		sct, err = logClient.AddChain(ctx, chain)
	}
	if err != nil {
		log.Fatal(err)
	}
	// Calculate the leaf hash
	leafEntry := ct.CreateX509MerkleTreeLeaf(chain[0], sct.Timestamp)
	leafData, err := tls.Marshal(*leafEntry)
	if err != nil {
		log.Fatalf("Failed to tls.Marshal leaf: %v", err)
	}
	leafHash := sha256.Sum256(append([]byte{merkletree.LeafPrefix}, leafData...))

	// Display the SCT
	when := ctTimestampToTime(sct.Timestamp)
	fmt.Printf("%v: Uploaded chain of %d certs to %v log at %v\n", when, len(chain), sct.SCTVersion, *logURI)
	fmt.Printf("LeafHash: %x\n", leafHash)
	fmt.Printf("Signature: %v\n", signatureToString(&sct.Signature))
}

func getRoots(ctx context.Context, logClient *client.LogClient) {
	roots, err := logClient.GetAcceptedRoots(ctx)
	if err != nil {
		log.Fatal(err)
	}
	for _, root := range roots {
		showRawCert(root)
	}
}

func getEntries(ctx context.Context, logClient *client.LogClient) {
	if *getFirst == -1 {
		log.Fatal("No -first option supplied")
	}
	if *getLast == -1 {
		log.Fatal("No -last option supplied")
	}
	entries, err := logClient.GetEntries(ctx, *getFirst, *getLast)
	if err != nil {
		log.Fatal(err)
	}
	for _, entry := range entries {
		ts := entry.Leaf.TimestampedEntry
		when := ctTimestampToTime(ts.Timestamp)
		fmt.Printf("Index=%d Timestamp=%v ", entry.Index, when)
		switch ts.EntryType {
		case ct.X509LogEntryType:
			fmt.Printf("X.509 certificate:\n")
			showParsedCert(entry.X509Cert)
		case ct.PrecertLogEntryType:
			fmt.Printf("pre-certificate from issuer with keyhash %x:\n", entry.Precert.IssuerKeyHash)
			showRawCert(entry.Precert.Submitted)
		default:
			log.Fatalf("Unhandled log entry type %d", entry.Leaf.TimestampedEntry.EntryType)
		}
	}
}

func showRawCert(cert ct.ASN1Cert) {
	if *textOut {
		c, err := x509.ParseCertificate(cert.Data)
		if err != nil {
			log.Printf("Error parsing certificate: %q", err.Error())
			return
		}
		showParsedCert(c)
	} else {
		showPEMData(cert.Data)
	}
}

func showParsedCert(cert *x509.Certificate) {
	if *textOut {
		fmt.Printf("%s\n", x509util.CertificateToString(cert))
	} else {
		showPEMData(cert.Raw)
	}
}

func showPEMData(data []byte) {
	if err := pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: data}); err != nil {
		log.Printf("Failed to PEM encode cert: %q", err.Error())
	}
}

func dieWithUsage(msg string) {
	fmt.Fprintln(os.Stderr, msg)
	fmt.Fprintf(os.Stderr, "Usage: ctclient [options] <cmd>\n"+
		"where cmd is one of:\n"+
		"   sth         retrieve signed tree head\n"+
		"   upload      upload cert chain and show SCT (needs -cert_chain)\n"+
		"   getroots    show accepted roots\n"+
		"   getentries  get log entries (needs -first and -last)\n")
	os.Exit(1)
}

func main() {
	flag.Parse()
	httpClient := &http.Client{
		Timeout: 10 * time.Second,
		Transport: &http.Transport{
			TLSHandshakeTimeout:   30 * time.Second,
			ResponseHeaderTimeout: 30 * time.Second,
			MaxIdleConnsPerHost:   10,
			DisableKeepAlives:     false,
			MaxIdleConns:          100,
			IdleConnTimeout:       90 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}
	var opts jsonclient.Options
	if *pubKey != "" {
		pubkey, err := ioutil.ReadFile(*pubKey)
		if err != nil {
			log.Fatal(err)
		}
		opts.PublicKey = string(pubkey)
	}
	logClient, err := client.New(*logURI, httpClient, opts)
	if err != nil {
		log.Fatal(err)
	}
	args := flag.Args()
	if len(args) != 1 {
		dieWithUsage("Need command argument")
	}
	ctx := context.Background()
	cmd := args[0]
	switch cmd {
	case "sth":
		getSTH(ctx, logClient)
	case "upload":
		addChain(ctx, logClient)
	case "getroots", "get_roots", "get-roots":
		getRoots(ctx, logClient)
	case "getentries", "get_entries", "get-entries":
		getEntries(ctx, logClient)
	default:
		dieWithUsage(fmt.Sprintf("Unknown command '%s'", cmd))
	}
}
<|endoftext|>


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

package dosa

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

type SinglePrimaryKeyNoParen struct {
	Entity     `dosa:"primaryKey=PrimaryKey"`
	PrimaryKey int64
	Data       string
}

// happy path: A single primaryKey becomes the partition key
func TestSinglePrimaryKeyNoParen(t *testing.T) {
	dosaTable, err := TableFromInstance(&SinglePrimaryKeyNoParen{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PrimaryKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, 0, len(dosaTable.Key.ClusteringKeys))
}

type SinglePrimaryKey struct {
	Entity     `dosa:"primaryKey=(PrimaryKey)"`
	PrimaryKey int64
	Data       string
}

// happy path: A single primaryKey becomes the partition key
func TestSinglePrimaryKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&SinglePrimaryKey{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PrimaryKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, 0, len(dosaTable.Key.ClusteringKeys))
}

func BenchmarkSingleKey(b *testing.B) {
	for i := 0; i < b.N; i++ {
		TableFromInstance(&SinglePrimaryKey{})
	}
}

type SinglePartitionKey struct {
	Entity     `dosa:"primaryKey=PrimaryKey"`
	PrimaryKey int64
	data       string
}

func TestSinglePartitionKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&SinglePartitionKey{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PrimaryKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, 0, len(dosaTable.Key.ClusteringKeys))
}

// unhappy path: this struct doesn't have anything specified for pk
type NoPrimaryKey struct {
	Entity     `dosa:"primaryKey="`
	PrimaryKey int64
	data       string
}

// unhappy path: If there is no field marked with a primary nor partition key, throw an error
func TestNoPrimaryKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&NoPrimaryKey{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "\"NoPrimaryKey\"")
}

// unhappy path: this struct has an empty primary key
type EmptyPrimaryKey struct {
	Entity     `dosa:"primaryKey=()"`
	PrimaryKey int64
	data       string
}

// unhappy path: If there is no field marked with a primary nor partition key, throw an error
func TestEmptyPrimaryKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&EmptyPrimaryKey{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "\"EmptyPrimaryKey\"")
}

type PrimaryKeyWithSecondaryRange struct {
	Entity     `dosa:"primaryKey=(PartKey,PrimaryKey  )"`
	PartKey    int64
	PrimaryKey int64
	data       string
}

func TestPrimaryKeyWithSecondaryRange(t *testing.T) {
	dosaTable, err := TableFromInstance(&PrimaryKeyWithSecondaryRange{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PartKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, []*ClusteringKey{{"PrimaryKey", false}}, dosaTable.Key.ClusteringKeys)
}

type PrimaryKeyWithDescendingRange struct {
	Entity     `dosa:"primaryKey=(PartKey,PrimaryKey desc )"`
	PartKey    int64
	PrimaryKey int64
	data       string
}

func TestPrimaryKeyWithDescendingRange(t *testing.T) {
	dosaTable, err := TableFromInstance(&PrimaryKeyWithDescendingRange{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PartKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, []ClusteringKey{{"PrimaryKey", true}}, dosaTable.Key.ClusteringKeys)
}

type MultiComponentPrimaryKey struct {
	Entity         `dosa:"primaryKey=((PartKey, AnotherPartKey))"`
	PartKey        int64
	AnotherPartKey int64
	data           string
}

func TestMultiComponentPrimaryKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&MultiComponentPrimaryKey{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PartKey", "AnotherPartKey"}, dosaTable.Key.PartitionKeys)
	assert.Nil(t, dosaTable.Key.ClusteringKeys)
}

type InvalidDosaAttribute struct {
	Entity `dosa:"oopsie,primaryKey=Oops"`
	Oops   int64
}

func TestInvalidDosaAttribute(t *testing.T) {
	dosaTable, err := TableFromInstance(&InvalidDosaAttribute{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "\"oopsie\"")
}

func TestStringify(t *testing.T) {
	dosaTable, _ := TableFromInstance(&SinglePrimaryKey{})
	assert.Contains(t, dosaTable.String(), dosaTable.Name)
	assert.Contains(t, dosaTable.String(), "primarykey")
}

type MissingCloseParen struct {
	Entity            `dosa:"primaryKey=(MissingCloseParen"`
	MissingCloseParen int64
}

func TestMissingCloseParen(t *testing.T) {
	dosaTable, err := TableFromInstance(&MissingCloseParen{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "MissingCloseParen")
}

type MissingAnnotation struct {
	Entity
	Oops int64
}

func TestMissingAnnotation(t *testing.T) {
	dosaTable, err := TableFromInstance(&MissingAnnotation{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "struct")
	assert.Contains(t, err.Error(), "tag")
}

type AllTypes struct {
	Entity      `dosa:"primaryKey=BoolType"`
	BoolType    bool
	Int32Type   int32
	Int64Type   int64
	Float64Type float64
}

func TestAllTypes(t *testing.T) {
	dosaTable, err := TableFromInstance(&AllTypes{})
	assert.NotNil(t, dosaTable)
	assert.Nil(t, err)
}

type UnsupportedType struct {
	Entity    `dosa:"primaryKey=BoolType"`
	BoolType  bool
	UnsupType float32
}

func TestUnsupportedType(t *testing.T) {
	dosaTable, err := TableFromInstance(&UnsupportedType{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "float32")
	assert.Contains(t, err.Error(), "UnsupType")
}

type KeyFieldNameTypo struct {
	Entity   `dosa:"primaryKey=BoolHype"`
	BoolType bool
}

func TestKeyFieldNameTypo(t *testing.T) {
	dosaTable, err := TableFromInstance(&KeyFieldNameTypo{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "BoolHype")
}
<commit_msg>fix the build<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package dosa

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

type SinglePrimaryKeyNoParen struct {
	Entity     `dosa:"primaryKey=PrimaryKey"`
	PrimaryKey int64
	Data       string
}

// happy path: A single primaryKey becomes the partition key
func TestSinglePrimaryKeyNoParen(t *testing.T) {
	dosaTable, err := TableFromInstance(&SinglePrimaryKeyNoParen{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PrimaryKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, 0, len(dosaTable.Key.ClusteringKeys))
}

type SinglePrimaryKey struct {
	Entity     `dosa:"primaryKey=(PrimaryKey)"`
	PrimaryKey int64
	Data       string
}

// happy path: A single primaryKey becomes the partition key
func TestSinglePrimaryKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&SinglePrimaryKey{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PrimaryKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, 0, len(dosaTable.Key.ClusteringKeys))
}

func BenchmarkSingleKey(b *testing.B) {
	for i := 0; i < b.N; i++ {
		TableFromInstance(&SinglePrimaryKey{})
	}
}

type SinglePartitionKey struct {
	Entity     `dosa:"primaryKey=PrimaryKey"`
	PrimaryKey int64
	data       string
}

func TestSinglePartitionKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&SinglePartitionKey{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PrimaryKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, 0, len(dosaTable.Key.ClusteringKeys))
}

// unhappy path: this struct doesn't have anything specified for pk
type NoPrimaryKey struct {
	Entity     `dosa:"primaryKey="`
	PrimaryKey int64
	data       string
}

// unhappy path: If there is no field marked with a primary nor partition key, throw an error
func TestNoPrimaryKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&NoPrimaryKey{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "\"NoPrimaryKey\"")
}

// unhappy path: this struct has an empty primary key
type EmptyPrimaryKey struct {
	Entity     `dosa:"primaryKey=()"`
	PrimaryKey int64
	data       string
}

// unhappy path: If there is no field marked with a primary nor partition key, throw an error
func TestEmptyPrimaryKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&EmptyPrimaryKey{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "\"EmptyPrimaryKey\"")
}

type PrimaryKeyWithSecondaryRange struct {
	Entity     `dosa:"primaryKey=(PartKey,PrimaryKey  )"`
	PartKey    int64
	PrimaryKey int64
	data       string
}

func TestPrimaryKeyWithSecondaryRange(t *testing.T) {
	dosaTable, err := TableFromInstance(&PrimaryKeyWithSecondaryRange{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PartKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, []*ClusteringKey{{"PrimaryKey", false}}, dosaTable.Key.ClusteringKeys)
}

type PrimaryKeyWithDescendingRange struct {
	Entity     `dosa:"primaryKey=(PartKey,PrimaryKey desc )"`
	PartKey    int64
	PrimaryKey int64
	data       string
}

func TestPrimaryKeyWithDescendingRange(t *testing.T) {
	dosaTable, err := TableFromInstance(&PrimaryKeyWithDescendingRange{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PartKey"}, dosaTable.Key.PartitionKeys)
	assert.Equal(t, []*ClusteringKey{{"PrimaryKey", true}}, dosaTable.Key.ClusteringKeys)
}

type MultiComponentPrimaryKey struct {
	Entity         `dosa:"primaryKey=((PartKey, AnotherPartKey))"`
	PartKey        int64
	AnotherPartKey int64
	data           string
}

func TestMultiComponentPrimaryKey(t *testing.T) {
	dosaTable, err := TableFromInstance(&MultiComponentPrimaryKey{})
	assert.Nil(t, err)
	assert.Equal(t, []string{"PartKey", "AnotherPartKey"}, dosaTable.Key.PartitionKeys)
	assert.Nil(t, dosaTable.Key.ClusteringKeys)
}

type InvalidDosaAttribute struct {
	Entity `dosa:"oopsie,primaryKey=Oops"`
	Oops   int64
}

func TestInvalidDosaAttribute(t *testing.T) {
	dosaTable, err := TableFromInstance(&InvalidDosaAttribute{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "\"oopsie\"")
}

func TestStringify(t *testing.T) {
	dosaTable, _ := TableFromInstance(&SinglePrimaryKey{})
	assert.Contains(t, dosaTable.String(), dosaTable.Name)
	assert.Contains(t, dosaTable.String(), "primarykey")
}

type MissingCloseParen struct {
	Entity            `dosa:"primaryKey=(MissingCloseParen"`
	MissingCloseParen int64
}

func TestMissingCloseParen(t *testing.T) {
	dosaTable, err := TableFromInstance(&MissingCloseParen{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "MissingCloseParen")
}

type MissingAnnotation struct {
	Entity
	Oops int64
}

func TestMissingAnnotation(t *testing.T) {
	dosaTable, err := TableFromInstance(&MissingAnnotation{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "struct")
	assert.Contains(t, err.Error(), "tag")
}

type AllTypes struct {
	Entity      `dosa:"primaryKey=BoolType"`
	BoolType    bool
	Int32Type   int32
	Int64Type   int64
	Float64Type float64
}

func TestAllTypes(t *testing.T) {
	dosaTable, err := TableFromInstance(&AllTypes{})
	assert.NotNil(t, dosaTable)
	assert.Nil(t, err)
}

type UnsupportedType struct {
	Entity    `dosa:"primaryKey=BoolType"`
	BoolType  bool
	UnsupType float32
}

func TestUnsupportedType(t *testing.T) {
	dosaTable, err := TableFromInstance(&UnsupportedType{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "float32")
	assert.Contains(t, err.Error(), "UnsupType")
}

type KeyFieldNameTypo struct {
	Entity   `dosa:"primaryKey=BoolHype"`
	BoolType bool
}

func TestKeyFieldNameTypo(t *testing.T) {
	dosaTable, err := TableFromInstance(&KeyFieldNameTypo{})
	assert.Nil(t, dosaTable)
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "BoolHype")
}
<|endoftext|>


<commit_before><commit_msg>closing the html tags<commit_after><|endoftext|>


<commit_before>package network

import (
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/subgraph/oz/ns"

	"github.com/op/go-logging"
)

type ProxyType string

const (
	PROXY_CLIENT ProxyType = "client"
	PROXY_SERVER ProxyType = "server"
)

type ProtoType string

const (
	PROTO_TCP        ProtoType = "tcp"
	PROTO_UDP        ProtoType = "udp"
	PROTO_UNIX       ProtoType = "unix"
	PROTO_UNIXGRAM   ProtoType = "unixgram"
	PROTO_UNIXPACKET ProtoType = "unixpacket"
)

// Socket list, used to hold ports that should be forwarded
type ProxyConfig struct {
	// One of client, server
	Nettype ProxyType `json:"type"`

	// One of tcp, udp, socket
	Proto ProtoType

	// TCP or UDP port number
	Port int

	// Optional: Destination address
	// In client mode: the host side address to connect to
	// In server mode: the sandbox side address to bind to
	// For unix sockets this is an abstract path
	// If left empty, localhost is used
	Destination string
}

var wgProxy sync.WaitGroup

func ProxySetup(childPid int, ozSockets []ProxyConfig, log *logging.Logger, ready sync.WaitGroup) error {
	for _, socket := range ozSockets {
		if socket.Nettype == "" {
			continue
		}
		if socket.Nettype == PROXY_CLIENT {
			err := newProxyClient(childPid, &socket, log, ready)
			if err != nil {
				return fmt.Errorf("%+v, %s", socket, err)
			}
		} else if socket.Nettype == PROXY_SERVER {
			err := newProxyServer(childPid, &socket, log, ready)
			if err != nil {
				return fmt.Errorf("%+s, %s", socket, err)
			}
		}
	}

	return nil
}

/**
 * Listener/Client
**/
func proxyClientConn(conn *net.Conn, proto ProtoType, rAddr string, ready sync.WaitGroup) error {
	rConn, err := net.Dial(string(proto), rAddr)
	if err != nil {
		return fmt.Errorf("Socket: %+v.\n", err)
	}

	var wg sync.WaitGroup
	wg.Add(2)

	copyLoop := func(dst, src net.Conn) {
		defer wg.Done()
		defer dst.Close()
		io.Copy(dst, src)
	}

	go copyLoop(*conn, rConn)
	go copyLoop(rConn, *conn)

	return nil
}

func newProxyClient(pid int, config *ProxyConfig, log *logging.Logger, ready sync.WaitGroup) error {
	if config.Destination == "" {
		config.Destination = "127.0.0.1"
	}

	var lAddr, rAddr string
	if !strings.HasPrefix(string(config.Proto), "unix") {
		lAddr = net.JoinHostPort("127.0.0.1", strconv.Itoa(config.Port))
		rAddr = net.JoinHostPort(config.Destination, strconv.Itoa(config.Port))
	} else {
		if !strings.HasPrefix(config.Destination, "@") {
			log.Warning("Only abstract unix socket are supported!")
			return nil
		}
		lAddr = config.Destination
		rAddr = config.Destination
	}

	log.Info("Starting socket client forwarding: %s://%s.", config.Proto, rAddr)

	listen, err := proxySocketListener(pid, config.Proto, lAddr)
	if err != nil {
		return err
	}

	wgProxy.Add(1)
	go func() {
		defer wgProxy.Done()
		for {
			conn, err := listen.Accept()
			if err != nil {
				log.Error("Socket: %+v.", err)
				//panic(err)
				continue
			}

			go proxyClientConn(&conn, config.Proto, rAddr, ready)
		}
	}()

	return nil
}

func proxySocketListener(pid int, proto ProtoType, lAddr string) (net.Listener, error) {
	fd, err := ns.OpenProcess(pid, ns.CLONE_NEWNET)
	defer ns.Close(fd)
	if err != nil {
		return nil, err
	}

	return nsSocketListener(fd, proto, lAddr)
}

func nsSocketListener(fd uintptr, proto ProtoType, lAddr string) (net.Listener, error) {
	origNs, _ := ns.OpenProcess(os.Getpid(), ns.CLONE_NEWNET)
	defer ns.Close(origNs)
	defer ns.Set(origNs, ns.CLONE_NEWNET)

	err := ns.Set(uintptr(fd), ns.CLONE_NEWNET)
	if err != nil {
		return nil, err
	}

	return net.Listen(string(proto), lAddr)

}

/**
 * Connect/Server
**/
func proxyServerConn(pid int, conn *net.Conn, proto ProtoType, rAddr string, log *logging.Logger, ready sync.WaitGroup) error {
	rConn, err := socketConnect(pid, proto, rAddr)
	if err != nil {
		log.Error("Socket: %+v.", err)
		return err
	}

	var wg sync.WaitGroup
	wg.Add(2)

	copyLoop := func(dst, src net.Conn) {
		defer wg.Done()
		defer dst.Close()
		io.Copy(dst, src)
	}

	go copyLoop(*conn, rConn)
	go copyLoop(rConn, *conn)

	return nil
}

func newProxyServer(pid int, config *ProxyConfig, log *logging.Logger, ready sync.WaitGroup) error {
	if config.Destination == "" {
		config.Destination = "127.0.0.1"
	}

	var lAddr, rAddr string
	if !strings.HasPrefix(string(config.Proto), "unix") {
		lAddr = net.JoinHostPort(config.Destination, strconv.Itoa(config.Port))
		rAddr = net.JoinHostPort("127.0.0.1", strconv.Itoa(config.Port))
	} else {
		if !strings.HasPrefix(config.Destination, "@") {
			log.Warning("Only abstract unix socket are supported!")
			return nil
		}
		lAddr = config.Destination
		rAddr = config.Destination
	}

	log.Info("Starting socket server forwarding: %s://%s.", config.Proto, lAddr)

	listen, err := net.Listen(string(config.Proto), lAddr)
	if err != nil {
		return err
	}

	wgProxy.Add(1)
	go func() {
		defer wgProxy.Done()
		for {
			conn, err := listen.Accept()
			if err != nil {
				log.Error("Socket: %+v.", err)
				//panic(err)
				continue
			}

			go proxyServerConn(pid, &conn, config.Proto, rAddr, log, ready)
		}
	}()

	return nil
}

func socketConnect(pid int, proto ProtoType, rAddr string) (net.Conn, error) {
	fd, err := ns.OpenProcess(pid, ns.CLONE_NEWNET)
	defer ns.Close(fd)
	if err != nil {
		return nil, err
	}

	return nsProxySocketConnect(fd, proto, rAddr)
}

func nsProxySocketConnect(fd uintptr, proto ProtoType, rAddr string) (net.Conn, error) {
	origNs, _ := ns.OpenProcess(os.Getpid(), ns.CLONE_NEWNET)
	defer ns.Close(origNs)
	defer ns.Set(origNs, ns.CLONE_NEWNET)

	err := ns.Set(uintptr(fd), ns.CLONE_NEWNET)
	if err != nil {
		return nil, err
	}

	return net.Dial(string(proto), rAddr)

}
<commit_msg>Backport proxy features (tcp2unix, dport) from master<commit_after>package network

import (
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/subgraph/oz/ns"

	"github.com/op/go-logging"
)

type ProxyType string

const (
	PROXY_CLIENT ProxyType = "client"
	PROXY_SERVER ProxyType = "server"
)

type ProtoType string

const (
	PROTO_TCP         ProtoType = "tcp"
	PROTO_UDP         ProtoType = "udp"
	PROTO_UNIX        ProtoType = "unix"
	PROTO_TCP_TO_UNIX ProtoType = "tcp2unix"
	PROTO_UNIXGRAM    ProtoType = "unixgram"
	PROTO_UNIXPACKET  ProtoType = "unixpacket"
)

// Socket list, used to hold ports that should be forwarded
type ProxyConfig struct {
	// One of client, server
	Nettype ProxyType `json:"type"`

	// One of tcp, udp, socket
	Proto ProtoType

	// TCP or UDP port number
	Port int

	// Destination port number
	DPort int
	// Optional: Destination address
	// In client mode: the host side address to connect to
	// In server mode: the sandbox side address to bind to
	// For unix sockets this is an abstract path
	// If left empty, localhost is used
	Destination string
}

var wgProxy sync.WaitGroup

func ProxySetup(childPid int, ozSockets []ProxyConfig, log *logging.Logger, ready sync.WaitGroup) error {
	for _, socket := range ozSockets {
		if socket.Nettype == "" {
			continue
		}
		if socket.Nettype == PROXY_CLIENT {
			err := newProxyClient(childPid, &socket, log, ready)
			if err != nil {
				return fmt.Errorf("%+v, %s", socket, err)
			}
		} else if socket.Nettype == PROXY_SERVER {
			err := newProxyServer(childPid, &socket, log, ready)
			if err != nil {
				return fmt.Errorf("%+s, %s", socket, err)
			}
		}
	}

	return nil
}

/**
 * Listener/Client
**/
func proxyClientConn(conn *net.Conn, proto ProtoType, rAddr string, ready sync.WaitGroup) error {
	rConn, err := net.Dial(string(proto), rAddr)
	if err != nil {
		return fmt.Errorf("Socket: %+v.\n", err)
	}

	var wg sync.WaitGroup
	wg.Add(2)

	copyLoop := func(dst, src net.Conn) {
		defer wg.Done()
		defer dst.Close()
		io.Copy(dst, src)
	}

	go copyLoop(*conn, rConn)
	go copyLoop(rConn, *conn)

	return nil
}

func newProxyClient(pid int, config *ProxyConfig, log *logging.Logger, ready sync.WaitGroup) error {
	if config.Destination == "" {
		config.Destination = "127.0.0.1"
	}

	var lAddr, rAddr, dport string
	if strings.HasPrefix(string(config.Proto), "tcp") && config.Proto != PROTO_TCP_TO_UNIX {
		if config.DPort != 0 {
			dport = strconv.Itoa(config.DPort)
		} else {
			dport = strconv.Itoa(config.Port)
		}
		lAddr = net.JoinHostPort("127.0.0.1", strconv.Itoa(config.Port))
		rAddr = net.JoinHostPort(config.Destination, dport)
	} else if strings.HasPrefix(string(config.Proto), "unix") {
		if !strings.HasPrefix(config.Destination, "@") {
			log.Warning("Only abstract unix socket are supported!")
			return nil
		}
		lAddr = config.Destination
		rAddr = config.Destination
	} else if config.Proto == PROTO_TCP_TO_UNIX {
		lAddr = net.JoinHostPort("127.0.0.1", strconv.Itoa(config.Port))
		rAddr = config.Destination
	} else {
		log.Warning("Unsupported proxy protocol specified!")
		return nil
	}

	var listenProto ProtoType
	if config.Proto == PROTO_TCP_TO_UNIX {
		listenProto = PROTO_TCP
		log.Info("Starting socket client forwarding: %s://%s -> unix://%s.", listenProto, lAddr, config.Destination)
	} else {
		listenProto = config.Proto
		log.Info("Starting socket client forwarding: %s://%s.", listenProto, rAddr)
	}
	listen, err := proxySocketListener(pid, listenProto, lAddr)
	if err != nil {
		return err
	}

	wgProxy.Add(1)
	c := *config

	go func() {
		defer wgProxy.Done()
		for {
			conn, err := listen.Accept()
			if err != nil {
				log.Error("Socket: %+v.", err)
				//panic(err)
				continue
			}

			var dialProto ProtoType
			if c.Proto == PROTO_TCP_TO_UNIX {
				dialProto = PROTO_UNIX
			} else {
				dialProto = c.Proto
			}

			go proxyClientConn(&conn, dialProto, rAddr, ready)
		}
	}()

	return nil
}

func proxySocketListener(pid int, proto ProtoType, lAddr string) (net.Listener, error) {
	fd, err := ns.OpenProcess(pid, ns.CLONE_NEWNET)
	defer ns.Close(fd)
	if err != nil {
		return nil, err
	}

	return nsSocketListener(fd, proto, lAddr)
}

func nsSocketListener(fd uintptr, proto ProtoType, lAddr string) (net.Listener, error) {
	origNs, _ := ns.OpenProcess(os.Getpid(), ns.CLONE_NEWNET)
	defer ns.Close(origNs)
	defer ns.Set(origNs, ns.CLONE_NEWNET)

	err := ns.Set(uintptr(fd), ns.CLONE_NEWNET)
	if err != nil {
		return nil, err
	}

	return net.Listen(string(proto), lAddr)

}

/**
 * Connect/Server
**/
func proxyServerConn(pid int, conn *net.Conn, proto ProtoType, rAddr string, log *logging.Logger, ready sync.WaitGroup) error {
	rConn, err := socketConnect(pid, proto, rAddr)
	if err != nil {
		log.Error("Socket: %+v.", err)
		return err
	}

	var wg sync.WaitGroup
	wg.Add(2)

	copyLoop := func(dst, src net.Conn) {
		defer wg.Done()
		defer dst.Close()
		io.Copy(dst, src)
	}

	go copyLoop(*conn, rConn)
	go copyLoop(rConn, *conn)

	return nil
}

func newProxyServer(pid int, config *ProxyConfig, log *logging.Logger, ready sync.WaitGroup) error {
	if config.Destination == "" {
		config.Destination = "127.0.0.1"
	}

	var lAddr, rAddr string
	if !strings.HasPrefix(string(config.Proto), "unix") {
		lAddr = net.JoinHostPort(config.Destination, strconv.Itoa(config.Port))
		rAddr = net.JoinHostPort("127.0.0.1", strconv.Itoa(config.Port))
	} else {
		if !strings.HasPrefix(config.Destination, "@") {
			log.Warning("Only abstract unix socket are supported!")
			return nil
		}
		lAddr = config.Destination
		rAddr = config.Destination
	}

	log.Info("Starting socket server forwarding: %s://%s.", config.Proto, lAddr)

	listen, err := net.Listen(string(config.Proto), lAddr)
	if err != nil {
		return err
	}

	wgProxy.Add(1)
	go func() {
		defer wgProxy.Done()
		for {
			conn, err := listen.Accept()
			if err != nil {
				log.Error("Socket: %+v.", err)
				//panic(err)
				continue
			}

			go proxyServerConn(pid, &conn, config.Proto, rAddr, log, ready)
		}
	}()

	return nil
}

func socketConnect(pid int, proto ProtoType, rAddr string) (net.Conn, error) {
	fd, err := ns.OpenProcess(pid, ns.CLONE_NEWNET)
	defer ns.Close(fd)
	if err != nil {
		return nil, err
	}

	return nsProxySocketConnect(fd, proto, rAddr)
}

func nsProxySocketConnect(fd uintptr, proto ProtoType, rAddr string) (net.Conn, error) {
	origNs, _ := ns.OpenProcess(os.Getpid(), ns.CLONE_NEWNET)
	defer ns.Close(origNs)
	defer ns.Set(origNs, ns.CLONE_NEWNET)

	err := ns.Set(uintptr(fd), ns.CLONE_NEWNET)
	if err != nil {
		return nil, err
	}

	return net.Dial(string(proto), rAddr)

}
<|endoftext|>


<commit_before>package tgbotapi

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

// APIResponse is a response from the Telegram API with the result
// stored raw.
type APIResponse struct {
	Ok          bool            `json:"ok"`
	Result      json.RawMessage `json:"result"`
	ErrorCode   int             `json:"error_code"`
	Description string          `json:"description"`
}

// Update is an update response, from GetUpdates.
type Update struct {
	UpdateID           int                `json:"update_id"`
	Message            Message            `json:"message"`
	InlineQuery        InlineQuery        `json:"inline_query"`
	ChosenInlineResult ChosenInlineResult `json:"chosen_inline_result"`
}

// User is a user on Telegram.
type User struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"` // optional
	UserName  string `json:"username"`  // optional
}

// String displays a simple text version of a user.
//
// It is normally a user's username, but falls back to a first/last
// name as available.
func (u *User) String() string {
	if u.UserName != "" {
		return u.UserName
	}

	name := u.FirstName
	if u.LastName != "" {
		name += " " + u.LastName
	}

	return name
}

// GroupChat is a group chat.
type GroupChat struct {
	ID    int    `json:"id"`
	Title string `json:"title"`
}

// Chat contains information about the place a message was sent.
type Chat struct {
	ID        int    `json:"id"`
	Type      string `json:"type"`
	Title     string `json:"title"`      // optional
	UserName  string `json:"username"`   // optional
	FirstName string `json:"first_name"` // optional
	LastName  string `json:"last_name"`  // optional
}

// IsPrivate returns if the Chat is a private conversation.
func (c *Chat) IsPrivate() bool {
	return c.Type == "private"
}

// IsGroup returns if the Chat is a group.
func (c *Chat) IsGroup() bool {
	return c.Type == "group"
}

// IsSuperGroup returns if the Chat is a supergroup.
func (c *Chat) IsSuperGroup() bool {
	return c.Type == "supergroup"
}

// IsChannel returns if the Chat is a channel.
func (c *Chat) IsChannel() bool {
	return c.Type == "channel"
}

// Message is returned by almost every request, and contains data about
// almost anything.
type Message struct {
	MessageID             int         `json:"message_id"`
	From                  User        `json:"from"` // optional
	Date                  int         `json:"date"`
	Chat                  Chat        `json:"chat"`
	ForwardFrom           User        `json:"forward_from"`            // optional
	ForwardDate           int         `json:"forward_date"`            // optional
	ReplyToMessage        *Message    `json:"reply_to_message"`        // optional
	Text                  string      `json:"text"`                    // optional
	Audio                 Audio       `json:"audio"`                   // optional
	Document              Document    `json:"document"`                // optional
	Photo                 []PhotoSize `json:"photo"`                   // optional
	Sticker               Sticker     `json:"sticker"`                 // optional
	Video                 Video       `json:"video"`                   // optional
	Voice                 Voice       `json:"voice"`                   // optional
	Caption               string      `json:"caption"`                 // optional
	Contact               Contact     `json:"contact"`                 // optional
	Location              Location    `json:"location"`                // optional
	NewChatParticipant    User        `json:"new_chat_participant"`    // optional
	LeftChatParticipant   User        `json:"left_chat_participant"`   // optional
	NewChatTitle          string      `json:"new_chat_title"`          // optional
	NewChatPhoto          []PhotoSize `json:"new_chat_photo"`          // optional
	DeleteChatPhoto       bool        `json:"delete_chat_photo"`       // optional
	GroupChatCreated      bool        `json:"group_chat_created"`      // optional
	SuperGroupChatCreated bool        `json:"supergroup_chat_created"` // optional
	ChannelChatCreated    bool        `json:"channel_chat_created"`    // optional
	MigrateToChatID       int         `json:"migrate_to_chat_id"`      // optional
	MigrateFromChatID     int         `json:"migrate_from_chat_id"`    // optional
}

// Time converts the message timestamp into a Time.
func (m *Message) Time() time.Time {
	return time.Unix(int64(m.Date), 0)
}

// IsCommand returns true if message starts with '/'.
func (m *Message) IsCommand() bool {
	return m.Text != "" && m.Text[0] == '/'
}

// Command checks if the message was a command and if it was, returns the
// command. If the Message was not a command, it returns an empty string.
//
// If the command contains the at bot syntax, it removes the bot name.
func (m *Message) Command() string {
	if !m.IsCommand() {
		return ""
	}

	command := strings.SplitN(m.Text, " ", 2)[0][1:]

	if i := strings.Index(command, "@"); i != -1 {
		command = command[:i]
	}

	return command
}

// CommandArguments checks if the message was a command and if it was,
// returns all text after the command name. If the Message was not a
// command, it returns an empty string.
func (m *Message) CommandArguments() string {
	if !m.IsCommand() {
		return ""
	}

	split := strings.SplitN(m.Text, " ", 2)
	if len(split) != 2 {
		return ""
	}

	return strings.SplitN(m.Text, " ", 2)[1]
}

// PhotoSize contains information about photos.
type PhotoSize struct {
	FileID   string `json:"file_id"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	FileSize int    `json:"file_size"` // optional
}

// Audio contains information about audio.
type Audio struct {
	FileID    string `json:"file_id"`
	Duration  int    `json:"duration"`
	Performer string `json:"performer"` // optional
	Title     string `json:"title"`     // optional
	MimeType  string `json:"mime_type"` // optional
	FileSize  int    `json:"file_size"` // optional
}

// Document contains information about a document.
type Document struct {
	FileID    string    `json:"file_id"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	FileName  string    `json:"file_name"` // optional
	MimeType  string    `json:"mime_type"` // optional
	FileSize  int       `json:"file_size"` // optional
}

// Sticker contains information about a sticker.
type Sticker struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	FileSize  int       `json:"file_size"` // optional
}

// Video contains information about a video.
type Video struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Duration  int       `json:"duration"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	MimeType  string    `json:"mime_type"` // optional
	FileSize  int       `json:"file_size"` // optional
}

// Voice contains information about a voice.
type Voice struct {
	FileID   string `json:"file_id"`
	Duration int    `json:"duration"`
	MimeType string `json:"mime_type"` // optional
	FileSize int    `json:"file_size"` // optional
}

// Contact contains information about a contact.
//
// Note that LastName and UserID may be empty.
type Contact struct {
	PhoneNumber string `json:"phone_number"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"` // optional
	UserID      int    `json:"user_id"`   // optional
}

// Location contains information about a place.
type Location struct {
	Longitude float32 `json:"longitude"`
	Latitude  float32 `json:"latitude"`
}

// UserProfilePhotos contains information a set of user profile photos.
type UserProfilePhotos struct {
	TotalCount int         `json:"total_count"`
	Photos     []PhotoSize `json:"photos"`
}

// File contains information about a file to download from Telegram.
type File struct {
	FileID   string `json:"file_id"`
	FileSize int    `json:"file_size"` // optional
	FilePath string `json:"file_path"` // optional
}

// Link returns a full path to the download URL for a File.
//
// It requires the Bot Token to create the link.
func (f *File) Link(token string) string {
	return fmt.Sprintf(FileEndpoint, token, f.FilePath)
}

// ReplyKeyboardMarkup allows the Bot to set a custom keyboard.
type ReplyKeyboardMarkup struct {
	Keyboard        [][]string `json:"keyboard"`
	ResizeKeyboard  bool       `json:"resize_keyboard"`   // optional
	OneTimeKeyboard bool       `json:"one_time_keyboard"` // optional
	Selective       bool       `json:"selective"`         // optional
}

// ReplyKeyboardHide allows the Bot to hide a custom keyboard.
type ReplyKeyboardHide struct {
	HideKeyboard bool `json:"hide_keyboard"`
	Selective    bool `json:"selective"` // optional
}

// ForceReply allows the Bot to have users directly reply to it without
// additional interaction.
type ForceReply struct {
	ForceReply bool `json:"force_reply"`
	Selective  bool `json:"selective"` // optional
}

// InlineQuery is a Query from Telegram for an inline request.
type InlineQuery struct {
	ID     string `json:"id"`
	From   User   `json:"user"`
	Query  string `json:"query"`
	Offset string `json:"offset"`
}

// InlineQueryResultArticle is an inline query response article.
type InlineQueryResultArticle struct {
	Type                  string `json:"type"`         // required
	ID                    string `json:"id"`           // required
	Title                 string `json:"title"`        // required
	MessageText           string `json:"message_text"` // required
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
	URL                   string `json:"url"`
	HideURL               bool   `json:"hide_url"`
	Description           string `json:"description"`
	ThumbURL              string `json:"thumb_url"`
	ThumbWidth            int    `json:"thumb_width"`
	ThumbHeight           int    `json:"thumb_height"`
}

// InlineQueryResultPhoto is an inline query response photo.
type InlineQueryResultPhoto struct {
	Type                  string `json:"type"`      // required
	ID                    string `json:"id"`        // required
	URL                   string `json:"photo_url"` // required
	MimeType              string `json:"mime_type"`
	Width                 int    `json:"photo_width"`
	Height                int    `json:"photo_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Description           string `json:"description"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultGIF is an inline query response GIF.
type InlineQueryResultGIF struct {
	Type                  string `json:"type"`    // required
	ID                    string `json:"id"`      // required
	URL                   string `json:"gif_url"` // required
	Width                 int    `json:"gif_width"`
	Height                int    `json:"gif_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultMPEG4GIF is an inline query response MPEG4 GIF.
type InlineQueryResultMPEG4GIF struct {
	Type                  string `json:"type"`      // required
	ID                    string `json:"id"`        // required
	URL                   string `json:"mpeg4_url"` // required
	Width                 int    `json:"mpeg4_width"`
	Height                int    `json:"mpeg4_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultVideo is an inline query response video.
type InlineQueryResultVideo struct {
	Type                  string `json:"type"`         // required
	ID                    string `json:"id"`           // required
	URL                   string `json:"video_url"`    // required
	MimeType              string `json:"mime_type"`    // required
	MessageText           string `json:"message_text"` // required
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
	Width                 int    `json:"video_width"`
	Height                int    `json:"video_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Description           string `json:"description"`
}

// ChosenInlineResult is an inline query result chosen by a User
type ChosenInlineResult struct {
	ResultID string `json:"result_id"`
	From     User   `json:"from"`
	Query    string `json:"query"`
}
<commit_msg>Fix the From field in the Inline Query.<commit_after>package tgbotapi

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

// APIResponse is a response from the Telegram API with the result
// stored raw.
type APIResponse struct {
	Ok          bool            `json:"ok"`
	Result      json.RawMessage `json:"result"`
	ErrorCode   int             `json:"error_code"`
	Description string          `json:"description"`
}

// Update is an update response, from GetUpdates.
type Update struct {
	UpdateID           int                `json:"update_id"`
	Message            Message            `json:"message"`
	InlineQuery        InlineQuery        `json:"inline_query"`
	ChosenInlineResult ChosenInlineResult `json:"chosen_inline_result"`
}

// User is a user on Telegram.
type User struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"` // optional
	UserName  string `json:"username"`  // optional
}

// String displays a simple text version of a user.
//
// It is normally a user's username, but falls back to a first/last
// name as available.
func (u *User) String() string {
	if u.UserName != "" {
		return u.UserName
	}

	name := u.FirstName
	if u.LastName != "" {
		name += " " + u.LastName
	}

	return name
}

// GroupChat is a group chat.
type GroupChat struct {
	ID    int    `json:"id"`
	Title string `json:"title"`
}

// Chat contains information about the place a message was sent.
type Chat struct {
	ID        int    `json:"id"`
	Type      string `json:"type"`
	Title     string `json:"title"`      // optional
	UserName  string `json:"username"`   // optional
	FirstName string `json:"first_name"` // optional
	LastName  string `json:"last_name"`  // optional
}

// IsPrivate returns if the Chat is a private conversation.
func (c *Chat) IsPrivate() bool {
	return c.Type == "private"
}

// IsGroup returns if the Chat is a group.
func (c *Chat) IsGroup() bool {
	return c.Type == "group"
}

// IsSuperGroup returns if the Chat is a supergroup.
func (c *Chat) IsSuperGroup() bool {
	return c.Type == "supergroup"
}

// IsChannel returns if the Chat is a channel.
func (c *Chat) IsChannel() bool {
	return c.Type == "channel"
}

// Message is returned by almost every request, and contains data about
// almost anything.
type Message struct {
	MessageID             int         `json:"message_id"`
	From                  User        `json:"from"` // optional
	Date                  int         `json:"date"`
	Chat                  Chat        `json:"chat"`
	ForwardFrom           User        `json:"forward_from"`            // optional
	ForwardDate           int         `json:"forward_date"`            // optional
	ReplyToMessage        *Message    `json:"reply_to_message"`        // optional
	Text                  string      `json:"text"`                    // optional
	Audio                 Audio       `json:"audio"`                   // optional
	Document              Document    `json:"document"`                // optional
	Photo                 []PhotoSize `json:"photo"`                   // optional
	Sticker               Sticker     `json:"sticker"`                 // optional
	Video                 Video       `json:"video"`                   // optional
	Voice                 Voice       `json:"voice"`                   // optional
	Caption               string      `json:"caption"`                 // optional
	Contact               Contact     `json:"contact"`                 // optional
	Location              Location    `json:"location"`                // optional
	NewChatParticipant    User        `json:"new_chat_participant"`    // optional
	LeftChatParticipant   User        `json:"left_chat_participant"`   // optional
	NewChatTitle          string      `json:"new_chat_title"`          // optional
	NewChatPhoto          []PhotoSize `json:"new_chat_photo"`          // optional
	DeleteChatPhoto       bool        `json:"delete_chat_photo"`       // optional
	GroupChatCreated      bool        `json:"group_chat_created"`      // optional
	SuperGroupChatCreated bool        `json:"supergroup_chat_created"` // optional
	ChannelChatCreated    bool        `json:"channel_chat_created"`    // optional
	MigrateToChatID       int         `json:"migrate_to_chat_id"`      // optional
	MigrateFromChatID     int         `json:"migrate_from_chat_id"`    // optional
}

// Time converts the message timestamp into a Time.
func (m *Message) Time() time.Time {
	return time.Unix(int64(m.Date), 0)
}

// IsCommand returns true if message starts with '/'.
func (m *Message) IsCommand() bool {
	return m.Text != "" && m.Text[0] == '/'
}

// Command checks if the message was a command and if it was, returns the
// command. If the Message was not a command, it returns an empty string.
//
// If the command contains the at bot syntax, it removes the bot name.
func (m *Message) Command() string {
	if !m.IsCommand() {
		return ""
	}

	command := strings.SplitN(m.Text, " ", 2)[0][1:]

	if i := strings.Index(command, "@"); i != -1 {
		command = command[:i]
	}

	return command
}

// CommandArguments checks if the message was a command and if it was,
// returns all text after the command name. If the Message was not a
// command, it returns an empty string.
func (m *Message) CommandArguments() string {
	if !m.IsCommand() {
		return ""
	}

	split := strings.SplitN(m.Text, " ", 2)
	if len(split) != 2 {
		return ""
	}

	return strings.SplitN(m.Text, " ", 2)[1]
}

// PhotoSize contains information about photos.
type PhotoSize struct {
	FileID   string `json:"file_id"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	FileSize int    `json:"file_size"` // optional
}

// Audio contains information about audio.
type Audio struct {
	FileID    string `json:"file_id"`
	Duration  int    `json:"duration"`
	Performer string `json:"performer"` // optional
	Title     string `json:"title"`     // optional
	MimeType  string `json:"mime_type"` // optional
	FileSize  int    `json:"file_size"` // optional
}

// Document contains information about a document.
type Document struct {
	FileID    string    `json:"file_id"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	FileName  string    `json:"file_name"` // optional
	MimeType  string    `json:"mime_type"` // optional
	FileSize  int       `json:"file_size"` // optional
}

// Sticker contains information about a sticker.
type Sticker struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	FileSize  int       `json:"file_size"` // optional
}

// Video contains information about a video.
type Video struct {
	FileID    string    `json:"file_id"`
	Width     int       `json:"width"`
	Height    int       `json:"height"`
	Duration  int       `json:"duration"`
	Thumbnail PhotoSize `json:"thumb"`     // optional
	MimeType  string    `json:"mime_type"` // optional
	FileSize  int       `json:"file_size"` // optional
}

// Voice contains information about a voice.
type Voice struct {
	FileID   string `json:"file_id"`
	Duration int    `json:"duration"`
	MimeType string `json:"mime_type"` // optional
	FileSize int    `json:"file_size"` // optional
}

// Contact contains information about a contact.
//
// Note that LastName and UserID may be empty.
type Contact struct {
	PhoneNumber string `json:"phone_number"`
	FirstName   string `json:"first_name"`
	LastName    string `json:"last_name"` // optional
	UserID      int    `json:"user_id"`   // optional
}

// Location contains information about a place.
type Location struct {
	Longitude float32 `json:"longitude"`
	Latitude  float32 `json:"latitude"`
}

// UserProfilePhotos contains information a set of user profile photos.
type UserProfilePhotos struct {
	TotalCount int         `json:"total_count"`
	Photos     []PhotoSize `json:"photos"`
}

// File contains information about a file to download from Telegram.
type File struct {
	FileID   string `json:"file_id"`
	FileSize int    `json:"file_size"` // optional
	FilePath string `json:"file_path"` // optional
}

// Link returns a full path to the download URL for a File.
//
// It requires the Bot Token to create the link.
func (f *File) Link(token string) string {
	return fmt.Sprintf(FileEndpoint, token, f.FilePath)
}

// ReplyKeyboardMarkup allows the Bot to set a custom keyboard.
type ReplyKeyboardMarkup struct {
	Keyboard        [][]string `json:"keyboard"`
	ResizeKeyboard  bool       `json:"resize_keyboard"`   // optional
	OneTimeKeyboard bool       `json:"one_time_keyboard"` // optional
	Selective       bool       `json:"selective"`         // optional
}

// ReplyKeyboardHide allows the Bot to hide a custom keyboard.
type ReplyKeyboardHide struct {
	HideKeyboard bool `json:"hide_keyboard"`
	Selective    bool `json:"selective"` // optional
}

// ForceReply allows the Bot to have users directly reply to it without
// additional interaction.
type ForceReply struct {
	ForceReply bool `json:"force_reply"`
	Selective  bool `json:"selective"` // optional
}

// InlineQuery is a Query from Telegram for an inline request.
type InlineQuery struct {
	ID     string `json:"id"`
	From   User   `json:"from"`
	Query  string `json:"query"`
	Offset string `json:"offset"`
}

// InlineQueryResultArticle is an inline query response article.
type InlineQueryResultArticle struct {
	Type                  string `json:"type"`         // required
	ID                    string `json:"id"`           // required
	Title                 string `json:"title"`        // required
	MessageText           string `json:"message_text"` // required
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
	URL                   string `json:"url"`
	HideURL               bool   `json:"hide_url"`
	Description           string `json:"description"`
	ThumbURL              string `json:"thumb_url"`
	ThumbWidth            int    `json:"thumb_width"`
	ThumbHeight           int    `json:"thumb_height"`
}

// InlineQueryResultPhoto is an inline query response photo.
type InlineQueryResultPhoto struct {
	Type                  string `json:"type"`      // required
	ID                    string `json:"id"`        // required
	URL                   string `json:"photo_url"` // required
	MimeType              string `json:"mime_type"`
	Width                 int    `json:"photo_width"`
	Height                int    `json:"photo_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Description           string `json:"description"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultGIF is an inline query response GIF.
type InlineQueryResultGIF struct {
	Type                  string `json:"type"`    // required
	ID                    string `json:"id"`      // required
	URL                   string `json:"gif_url"` // required
	Width                 int    `json:"gif_width"`
	Height                int    `json:"gif_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultMPEG4GIF is an inline query response MPEG4 GIF.
type InlineQueryResultMPEG4GIF struct {
	Type                  string `json:"type"`      // required
	ID                    string `json:"id"`        // required
	URL                   string `json:"mpeg4_url"` // required
	Width                 int    `json:"mpeg4_width"`
	Height                int    `json:"mpeg4_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Caption               string `json:"caption"`
	MessageText           string `json:"message_text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}

// InlineQueryResultVideo is an inline query response video.
type InlineQueryResultVideo struct {
	Type                  string `json:"type"`         // required
	ID                    string `json:"id"`           // required
	URL                   string `json:"video_url"`    // required
	MimeType              string `json:"mime_type"`    // required
	MessageText           string `json:"message_text"` // required
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
	Width                 int    `json:"video_width"`
	Height                int    `json:"video_height"`
	ThumbURL              string `json:"thumb_url"`
	Title                 string `json:"title"`
	Description           string `json:"description"`
}

// ChosenInlineResult is an inline query result chosen by a User
type ChosenInlineResult struct {
	ResultID string `json:"result_id"`
	From     User   `json:"from"`
	Query    string `json:"query"`
}
<|endoftext|>


<commit_before>package cmargin

import (
	"fmt"
	"runtime"
	"testing"
)

var (
	client    Client
	keystr    = "hz"
	hkeystr   = "2018-01-02"
	hkeyfield = "shanghai"
	hfieldval = "rainy"
)

func init() {
	runtime.GOMAXPROCS(4)
	//client.Addr = "192.168.206.110:6380"
	client.Addr = "127.0.0.1:6380"
	client.MaxPoolSize = 5
}

func Benchmark_Hget(b *testing.B) {
	client.Hset(hkeystr, hkeyfield, []byte(hfieldval))
	for i := 0; i < b.N; i++ {
		_, err := client.Hget(hkeystr, hkeyfield)
		if err != nil {
			b.Fatal(err.Error())
			return
		}
	}
}
func TestMsg(t *testing.T) {
	b := commandBytes("set", "hello", "world")
	t.Logf("%q", string(b))
}
func TestBasic(t *testing.T) {
	var val []byte
	var err error
	//var value = `hello\r\naaaaaaaaaaaaaaa
	//	bbbb`
	var value [128]byte
	for i := 0; i < 128; i++ {
		if i%2 == 0 {
			value[i] = 13
		} else {
			value[i] = 10
		}
	}

	err = client.Set("a", value[:128])
	if err != nil {
		t.Fatal("set failed", err.Error())
	}

	if val, err = client.Get("a"); err != nil {
		t.Fatal("get failed")
	}
	if string(val) != string(value[:]) {
		t.Fatal("set not equal get")
	}

	_, err = client.Keys("*")
	if err != nil {
		t.Fatal("keys * failed", err.Error())
	}
	client.Del("a")
	if ok, _ := client.Exists("a"); ok {
		t.Fatal(" delete  failed")
	}
	//v,_:=client.Decr("age")
	//v,_:=client.Incr("age")
	var age int64 = 11
	oringin, _ := client.Incr("age")
	t.Logf("incr:%d", oringin)
	v, _ := client.Incrby("age", age)
	if v != age+oringin {
		t.Fatalf("incrby failed,%d", v)
	}

	tm, err := client.Ping()
	if err != nil {
		t.Fatal("server out of connection", err.Error())
	}
	t.Log("status:%d\n", tm)
	//hset("chun","chun")
	//hset("xia","xia")
	hmset(t)
	hgetall(t)
	hdestroy(t)
}

func hdestroy(t *testing.T) {
	err := client.Hdestroy("2016")
	if err != nil {
		t.Fatalf("hdestory return:%v", err)
	}
}

func hmset(t *testing.T) {
	src := make(map[string]string)
	src["paris"] = "sunny"
	src["beijing"] = "cloudy"
	err := client.Hmset("2016", src)
	if err != nil {
		t.Fatalf("hmset failed:%s", err.Error())
	} else {
		t.Log("hmset ok")
	}
	v, err := client.Hmget("2016", "paris", "beijing")
	if err != nil {
		t.Fatalf("hmget failed:%s", err.Error())
	} else if len(v) != 2 || string(v[0]) != "sunny" || string(v[1]) != "cloudy" {
		t.Fatalf("hmget value not match hmset")
	}
	t.Log("hmset ok")
}

func hgetall(t *testing.T) {
	result := make(map[string][]byte)
	err := client.Hgetall("2016", result)
	if err != nil {
		t.Fatalf("hgetall failed:%s", err.Error())
	}
	t.Logf("%+v", result)
}

func exists(t *testing.T) {
	client.Hset("groupa", "192.168.4.1", []byte("alive"))
	b, err := client.Hexists("groupa", "192.168.4.1")
	if err != nil {
		t.Error(err)
	} else {
		t.Logf("bool:%v", b)
	}
}

func hdel() {
	b, err := client.Hdel("2015", "zhang")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("bool:%v\n", b)
	}
}

func del() {
	b, err := client.Del("hello")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("bool:%v\n", b)
	}
}

func hget() {
	val, err := client.Hget("2015", "zhang")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("1->hget:%s\n", string(val))
}

func hset(t *testing.T) {
	_, err := client.Hset(hkeystr, hkeyfield, []byte("rainy"))
	if err != nil {
		t.Error(err)
	}
}
<commit_msg>up test<commit_after>package cmargin

import (
	"fmt"
	"runtime"
	"testing"

	"github.com/branthz/margin-cache/handle"
)

var (
	client    Client
	keystr    = "hz"
	hkeystr   = "2018-01-02"
	hkeyfield = "shanghai"
	hfieldval = "rainy"
)

func init() {
	runtime.GOMAXPROCS(4)
	//client.Addr = "192.168.206.110:6380"
	client.Addr = "127.0.0.1:6380"
	client.MaxPoolSize = 5
	go handle.Start(6380)
}

func Benchmark_Hget(b *testing.B) {
	client.Hset(hkeystr, hkeyfield, []byte(hfieldval))
	for i := 0; i < b.N; i++ {
		_, err := client.Hget(hkeystr, hkeyfield)
		if err != nil {
			b.Fatal(err.Error())
			return
		}
	}
}
func TestMsg(t *testing.T) {
	b := commandBytes("set", "hello", "world")
	t.Logf("%q", string(b))
}
func TestBasic(t *testing.T) {
	var val []byte
	var err error
	//var value = `hello\r\naaaaaaaaaaaaaaa
	//	bbbb`
	var value [128]byte
	for i := 0; i < 128; i++ {
		if i%2 == 0 {
			value[i] = 13
		} else {
			value[i] = 10
		}
	}

	err = client.Set("a", value[:128])
	if err != nil {
		t.Fatal("set failed", err.Error())
	}

	if val, err = client.Get("a"); err != nil {
		t.Fatal("get failed")
	}
	if string(val) != string(value[:]) {
		t.Fatal("set not equal get")
	}

	_, err = client.Keys("*")
	if err != nil {
		t.Fatal("keys * failed", err.Error())
	}
	client.Del("a")
	if ok, _ := client.Exists("a"); ok {
		t.Fatal(" delete  failed")
	}
	//v,_:=client.Decr("age")
	//v,_:=client.Incr("age")
	var age int64 = 11
	oringin, _ := client.Incr("age")
	t.Logf("incr:%d", oringin)
	v, _ := client.Incrby("age", age)
	if v != age+oringin {
		t.Fatalf("incrby failed,%d", v)
	}

	tm, err := client.Ping()
	if err != nil {
		t.Fatal("server out of connection", err.Error())
	}
	t.Log("status:%d\n", tm)
	//hset("chun","chun")
	//hset("xia","xia")
	hmset(t)
	hgetall(t)
	hdestroy(t)
}

func hdestroy(t *testing.T) {
	err := client.Hdestroy("2016")
	if err != nil {
		t.Fatalf("hdestory return:%v", err)
	}
}

func hmset(t *testing.T) {
	src := make(map[string]string)
	src["paris"] = "sunny"
	src["beijing"] = "cloudy"
	err := client.Hmset("2016", src)
	if err != nil {
		t.Fatalf("hmset failed:%s", err.Error())
	} else {
		t.Log("hmset ok")
	}
	v, err := client.Hmget("2016", "paris", "beijing")
	if err != nil {
		t.Fatalf("hmget failed:%s", err.Error())
	} else if len(v) != 2 || string(v[0]) != "sunny" || string(v[1]) != "cloudy" {
		t.Fatalf("hmget value not match hmset")
	}
	t.Log("hmset ok")
}

func hgetall(t *testing.T) {
	result := make(map[string][]byte)
	err := client.Hgetall("2016", result)
	if err != nil {
		t.Fatalf("hgetall failed:%s", err.Error())
	}
	t.Logf("%+v", result)
}

func exists(t *testing.T) {
	client.Hset("groupa", "192.168.4.1", []byte("alive"))
	b, err := client.Hexists("groupa", "192.168.4.1")
	if err != nil {
		t.Error(err)
	} else {
		t.Logf("bool:%v", b)
	}
}

func hdel() {
	b, err := client.Hdel("2015", "zhang")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("bool:%v\n", b)
	}
}

func del() {
	b, err := client.Del("hello")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("bool:%v\n", b)
	}
}

func hget() {
	val, err := client.Hget("2015", "zhang")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("1->hget:%s\n", string(val))
}

func hset(t *testing.T) {
	_, err := client.Hset(hkeystr, hkeyfield, []byte("rainy"))
	if err != nil {
		t.Error(err)
	}
}
<|endoftext|>