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

package oracle_test

// This file defines a test framework for oracle queries.
//
// The files beneath testdata/src/main contain Go programs containing
// query annotations of the form:
//
//   @verb id "select"
//
// where verb is the query mode (e.g. "callers"), id is a unique name
// for this query, and "select" is a regular expression matching the
// substring of the current line that is the query's input selection.
//
// The expected output for each query is provided in the accompanying
// .golden file.
//
// (Location information is not included because it's too fragile to
// display as text.  TODO(adonovan): think about how we can test its
// correctness, since it is critical information.)
//
// Run this test with:
// 	% go test code.google.com/p/go.tools/oracle -update
// to update the golden files.

// TODO(adonovan): improve coverage:
// - output of @callgraph is nondeterministic.
// - as are lists of labels.

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"testing"

	"code.google.com/p/go.tools/oracle"
)

var updateFlag = flag.Bool("update", false, "Update the golden files.")

type query struct {
	id         string         // unique id
	verb       string         // query mode, e.g. "callees"
	posn       token.Position // position of of query
	filename   string
	start, end int // selection of file to pass to oracle
}

func parseRegexp(text string) (*regexp.Regexp, error) {
	pattern, err := strconv.Unquote(text)
	if err != nil {
		return nil, fmt.Errorf("can't unquote %s", text)
	}
	return regexp.Compile(pattern)
}

// parseQueries parses and returns the queries in the named file.
func parseQueries(t *testing.T, filename string) []*query {
	filedata, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}

	// Parse the file once to discover the test queries.
	var fset token.FileSet
	f, err := parser.ParseFile(&fset, filename, filedata,
		parser.DeclarationErrors|parser.ParseComments)
	if err != nil {
		t.Fatal(err)
	}

	lines := bytes.Split(filedata, []byte("\n"))

	var queries []*query
	queriesById := make(map[string]*query)

	// Find all annotations of these forms:
	expectRe := regexp.MustCompile(`@([a-z]+)\s+(\S+)\s+(\".*)$`) // @verb id "regexp"
	for _, c := range f.Comments {
		text := strings.TrimSpace(c.Text())
		if text == "" || text[0] != '@' {
			continue
		}
		posn := fset.Position(c.Pos())

		// @verb id "regexp"
		match := expectRe.FindStringSubmatch(text)
		if match == nil {
			t.Errorf("%s: ill-formed query: %s", posn, text)
			continue
		}

		id := match[2]
		if prev, ok := queriesById[id]; ok {
			t.Errorf("%s: duplicate id %s", posn, id)
			t.Errorf("%s: previously used here", prev.posn)
			continue
		}

		selectRe, err := parseRegexp(match[3])
		if err != nil {
			t.Errorf("%s: %s", posn, err)
			continue
		}

		// Find text of the current line, sans query.
		// (Queries must be // not /**/ comments.)
		line := lines[posn.Line-1][:posn.Column-1]

		// Apply regexp to current line to find input selection.
		loc := selectRe.FindIndex(line)
		if loc == nil {
			t.Errorf("%s: selection pattern %s doesn't match line %q",
				posn, match[3], string(line))
			continue
		}

		// Assumes ASCII. TODO(adonovan): test on UTF-8.
		linestart := posn.Offset - (posn.Column - 1)

		// Compute the file offsets
		q := &query{
			id:       id,
			verb:     match[1],
			posn:     posn,
			filename: filename,
			start:    linestart + loc[0],
			end:      linestart + loc[1],
		}
		queries = append(queries, q)
		queriesById[id] = q
	}

	// Return the slice, not map, for deterministic iteration.
	return queries
}

// stripLocation removes a "file:line: " prefix.
func stripLocation(line string) string {
	if i := strings.Index(line, ": "); i >= 0 {
		line = line[i+2:]
	}
	return line
}

// doQuery poses query q to the oracle and writes its response and
// error (if any) to out.
func doQuery(out io.Writer, q *query) {
	fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id)

	capture := new(bytes.Buffer) // capture standard output
	var buildContext = build.Default
	buildContext.GOPATH = "testdata"
	err := oracle.Main([]string{q.filename},
		q.verb,
		fmt.Sprintf("%s %d-%d", q.filename, q.start, q.end),
		/*PTA-log=*/ nil, capture, &buildContext)

	for _, line := range strings.Split(capture.String(), "\n") {
		fmt.Fprintf(out, "%s\n", stripLocation(line))
	}

	if err != nil {
		fmt.Fprintf(out, "Error: %s\n", stripLocation(err.Error()))
	}
}

func TestOracle(t *testing.T) {
	switch runtime.GOOS {
	case "windows":
		t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS)
	}

	for _, filename := range []string{
		"testdata/src/main/calls.go",
		"testdata/src/main/describe.go",
		"testdata/src/main/freevars.go",
		"testdata/src/main/implements.go",
		"testdata/src/main/imports.go",
		"testdata/src/main/peers.go",
	} {
		queries := parseQueries(t, filename)
		golden := filename + "lden"
		got := filename + "t"
		gotfh, err := os.Create(got)
		if err != nil {
			t.Errorf("Create(%s) failed: %s", got, err)
			continue
		}
		defer gotfh.Close()

		// Run the oracle on each query, redirecting its output
		// and error (if any) to the foo.got file.
		for _, q := range queries {
			doQuery(gotfh, q)
		}

		// Compare foo.got with foo.golden.
		cmd := exec.Command("/usr/bin/diff", "-u3", golden, got) // assumes POSIX
		buf := new(bytes.Buffer)
		cmd.Stdout = buf
		if err := cmd.Run(); err != nil {
			t.Errorf("Oracle tests for %s failed: %s.\n%s\n",
				filename, err, buf)

			if *updateFlag {
				t.Logf("Updating %s...", golden)
				if err := exec.Command("/bin/cp", got, golden).Run(); err != nil {
					t.Errorf("Update failed: %s", err)
				}
			}
		}
	}
}
<commit_msg>go.tools/oracle: fix build on darwin<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package oracle_test

// This file defines a test framework for oracle queries.
//
// The files beneath testdata/src/main contain Go programs containing
// query annotations of the form:
//
//   @verb id "select"
//
// where verb is the query mode (e.g. "callers"), id is a unique name
// for this query, and "select" is a regular expression matching the
// substring of the current line that is the query's input selection.
//
// The expected output for each query is provided in the accompanying
// .golden file.
//
// (Location information is not included because it's too fragile to
// display as text.  TODO(adonovan): think about how we can test its
// correctness, since it is critical information.)
//
// Run this test with:
// 	% go test code.google.com/p/go.tools/oracle -update
// to update the golden files.

// TODO(adonovan): improve coverage:
// - output of @callgraph is nondeterministic.
// - as are lists of labels.

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"testing"

	"code.google.com/p/go.tools/oracle"
)

var updateFlag = flag.Bool("update", false, "Update the golden files.")

type query struct {
	id         string         // unique id
	verb       string         // query mode, e.g. "callees"
	posn       token.Position // position of of query
	filename   string
	start, end int // selection of file to pass to oracle
}

func parseRegexp(text string) (*regexp.Regexp, error) {
	pattern, err := strconv.Unquote(text)
	if err != nil {
		return nil, fmt.Errorf("can't unquote %s", text)
	}
	return regexp.Compile(pattern)
}

// parseQueries parses and returns the queries in the named file.
func parseQueries(t *testing.T, filename string) []*query {
	filedata, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}

	// Parse the file once to discover the test queries.
	var fset token.FileSet
	f, err := parser.ParseFile(&fset, filename, filedata,
		parser.DeclarationErrors|parser.ParseComments)
	if err != nil {
		t.Fatal(err)
	}

	lines := bytes.Split(filedata, []byte("\n"))

	var queries []*query
	queriesById := make(map[string]*query)

	// Find all annotations of these forms:
	expectRe := regexp.MustCompile(`@([a-z]+)\s+(\S+)\s+(\".*)$`) // @verb id "regexp"
	for _, c := range f.Comments {
		text := strings.TrimSpace(c.Text())
		if text == "" || text[0] != '@' {
			continue
		}
		posn := fset.Position(c.Pos())

		// @verb id "regexp"
		match := expectRe.FindStringSubmatch(text)
		if match == nil {
			t.Errorf("%s: ill-formed query: %s", posn, text)
			continue
		}

		id := match[2]
		if prev, ok := queriesById[id]; ok {
			t.Errorf("%s: duplicate id %s", posn, id)
			t.Errorf("%s: previously used here", prev.posn)
			continue
		}

		selectRe, err := parseRegexp(match[3])
		if err != nil {
			t.Errorf("%s: %s", posn, err)
			continue
		}

		// Find text of the current line, sans query.
		// (Queries must be // not /**/ comments.)
		line := lines[posn.Line-1][:posn.Column-1]

		// Apply regexp to current line to find input selection.
		loc := selectRe.FindIndex(line)
		if loc == nil {
			t.Errorf("%s: selection pattern %s doesn't match line %q",
				posn, match[3], string(line))
			continue
		}

		// Assumes ASCII. TODO(adonovan): test on UTF-8.
		linestart := posn.Offset - (posn.Column - 1)

		// Compute the file offsets
		q := &query{
			id:       id,
			verb:     match[1],
			posn:     posn,
			filename: filename,
			start:    linestart + loc[0],
			end:      linestart + loc[1],
		}
		queries = append(queries, q)
		queriesById[id] = q
	}

	// Return the slice, not map, for deterministic iteration.
	return queries
}

// stripLocation removes a "file:line: " prefix.
func stripLocation(line string) string {
	if i := strings.Index(line, ": "); i >= 0 {
		line = line[i+2:]
	}
	return line
}

// doQuery poses query q to the oracle and writes its response and
// error (if any) to out.
func doQuery(out io.Writer, q *query) {
	fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id)

	capture := new(bytes.Buffer) // capture standard output
	var buildContext = build.Default
	buildContext.GOPATH = "testdata"
	err := oracle.Main([]string{q.filename},
		q.verb,
		fmt.Sprintf("%s %d-%d", q.filename, q.start, q.end),
		/*PTA-log=*/ nil, capture, &buildContext)

	for _, line := range strings.Split(capture.String(), "\n") {
		fmt.Fprintf(out, "%s\n", stripLocation(line))
	}

	if err != nil {
		fmt.Fprintf(out, "Error: %s\n", stripLocation(err.Error()))
	}
}

func TestOracle(t *testing.T) {
	switch runtime.GOOS {
	case "windows":
		t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS)
	}

	for _, filename := range []string{
		"testdata/src/main/calls.go",
		"testdata/src/main/describe.go",
		"testdata/src/main/freevars.go",
		"testdata/src/main/implements.go",
		"testdata/src/main/imports.go",
		"testdata/src/main/peers.go",
	} {
		queries := parseQueries(t, filename)
		golden := filename + "lden"
		got := filename + "t"
		gotfh, err := os.Create(got)
		if err != nil {
			t.Errorf("Create(%s) failed: %s", got, err)
			continue
		}
		defer gotfh.Close()

		// Run the oracle on each query, redirecting its output
		// and error (if any) to the foo.got file.
		for _, q := range queries {
			doQuery(gotfh, q)
		}

		// Compare foo.got with foo.golden.
		cmd := exec.Command("/usr/bin/diff", "-u", golden, got) // assumes POSIX
		buf := new(bytes.Buffer)
		cmd.Stdout = buf
		if err := cmd.Run(); err != nil {
			t.Errorf("Oracle tests for %s failed: %s.\n%s\n",
				filename, err, buf)

			if *updateFlag {
				t.Logf("Updating %s...", golden)
				if err := exec.Command("/bin/cp", got, golden).Run(); err != nil {
					t.Errorf("Update failed: %s", err)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package check

import "fmt"

var baseTemplate = `# Save as MyRule.yml on your StylesPath
# See https://valelint.github.io/styles/#check-types for more info
# "suggestion", "warning" or "error"
level: warning
# Text describing this rule (generally longer than 'message').
description: '...'
# A link the source or reference.
link: '...'
%s`

var existenceTemplate = `extends: existence
# "%s" will be replaced by the active token
message: "found '%s'!"
ignorecase: false
tokens:
  - XXX
  - FIXME
  - TODO
  - NOTE`

var substitutionTemplate = `extends: substitution
message: Consider using '%s' instead of '%s'
ignorecase: false
# swap maps tokens in form of bad: good
swap:
  abundance: plenty
  accelerate: speed up`

var occurrenceTemplate = `extends: occurrence
message: "More than 3 commas!"
# Here, we're counting the number of times a comma appears in a sentence.
# If it occurs more than 3 times, we'll flag it.
scope: sentence
ignorecase: false
max: 3
token: ','`

var conditionalTemplate = `extends: conditional
message: "'%s' has no definition"
scope: text
ignorecase: false
# Ensures that the existence of 'first' implies the existence of 'second'.
first: \b([A-Z]{3,5})\b
second: (?:\b[A-Z][a-z]+ )+\(([A-Z]{3,5})\)
# ... with the exception of these:
exceptions:
  - ABC
  - ADD`

var consistencyTemplate = `extends: consistency
message: "Inconsistent spelling of '%s'"
scope: text
ignorecase: true
nonword: false
# We only want one of these to appear.
either:
  advisor: adviser
  centre: center`

var repetitionTemplate = `extends: repetition
message: "'%s' is repeated!"
scope: paragraph
ignorecase: false
# Will flag repeated occurrences of the same token (e.g., "this this").
tokens:
  - '[^\s]+'`

var capitalizationTemplate = `extends: capitalization
message: "'%s' should be in title case"
scope: heading
# $title, $sentence, $lower, $upper, or a pattern.
match: $title
style: AP # AP or Chicago; only applies when match is set to $title.`

var checkToTemplate = map[string]string{
	"existence":      existenceTemplate,
	"substitution":   substitutionTemplate,
	"occurrence":     occurrenceTemplate,
	"conditional":    conditionalTemplate,
	"consistency":    consistencyTemplate,
	"repetition":     repetitionTemplate,
	"capitalization": capitalizationTemplate,
}

// GetTemplate makes a template for the given extension point.
func GetTemplate(name string) string {
	if template, ok := checkToTemplate[name]; ok {
		return fmt.Sprintf(baseTemplate, template)
	}
	return ""
}
<commit_msg>refactor: update documentation link in templates<commit_after>package check

import "fmt"

var baseTemplate = `# Save as MyRule.yml on your StylesPath
# See https://valelint.github.io/docs/styles/ for more info
# "suggestion", "warning" or "error"
level: warning
# Text describing this rule (generally longer than 'message').
description: '...'
# A link the source or reference.
link: '...'
%s`

var existenceTemplate = `extends: existence
# "%s" will be replaced by the active token
message: "found '%s'!"
ignorecase: false
tokens:
  - XXX
  - FIXME
  - TODO
  - NOTE`

var substitutionTemplate = `extends: substitution
message: Consider using '%s' instead of '%s'
ignorecase: false
# swap maps tokens in form of bad: good
swap:
  abundance: plenty
  accelerate: speed up`

var occurrenceTemplate = `extends: occurrence
message: "More than 3 commas!"
# Here, we're counting the number of times a comma appears in a sentence.
# If it occurs more than 3 times, we'll flag it.
scope: sentence
ignorecase: false
max: 3
token: ','`

var conditionalTemplate = `extends: conditional
message: "'%s' has no definition"
scope: text
ignorecase: false
# Ensures that the existence of 'first' implies the existence of 'second'.
first: \b([A-Z]{3,5})\b
second: (?:\b[A-Z][a-z]+ )+\(([A-Z]{3,5})\)
# ... with the exception of these:
exceptions:
  - ABC
  - ADD`

var consistencyTemplate = `extends: consistency
message: "Inconsistent spelling of '%s'"
scope: text
ignorecase: true
nonword: false
# We only want one of these to appear.
either:
  advisor: adviser
  centre: center`

var repetitionTemplate = `extends: repetition
message: "'%s' is repeated!"
scope: paragraph
ignorecase: false
# Will flag repeated occurrences of the same token (e.g., "this this").
tokens:
  - '[^\s]+'`

var capitalizationTemplate = `extends: capitalization
message: "'%s' should be in title case"
scope: heading
# $title, $sentence, $lower, $upper, or a pattern.
match: $title
style: AP # AP or Chicago; only applies when match is set to $title.`

var checkToTemplate = map[string]string{
	"existence":      existenceTemplate,
	"substitution":   substitutionTemplate,
	"occurrence":     occurrenceTemplate,
	"conditional":    conditionalTemplate,
	"consistency":    consistencyTemplate,
	"repetition":     repetitionTemplate,
	"capitalization": capitalizationTemplate,
}

// GetTemplate makes a template for the given extension point.
func GetTemplate(name string) string {
	if template, ok := checkToTemplate[name]; ok {
		return fmt.Sprintf(baseTemplate, template)
	}
	return ""
}
<|endoftext|>


<commit_before>//go:generate easyjson -all

package rpc

// StatusInfo represents response of aria2.tellStatus
type StatusInfo struct {
	Gid             string     `json:"gid"`             // GID of the download.
	Status          string     `json:"status"`          // active for currently downloading/seeding downloads. waiting for downloads in the queue; download is not started. paused for paused downloads. error for downloads that were stopped because of error. complete for stopped and completed downloads. removed for the downloads removed by user.
	TotalLength     string     `json:"totalLength"`     // Total length of the download in bytes.
	CompletedLength string     `json:"completedLength"` // Completed length of the download in bytes.
	UploadLength    string     `json:"uploadLength"`    // Uploaded length of the download in bytes.
	BitField        string     `json:"bitfield"`        // Hexadecimal representation of the download progress. The highest bit corresponds to the piece at index 0. Any set bits indicate loaded pieces, while unset bits indicate not yet loaded and/or missing pieces. Any overflow bits at the end are set to zero. When the download was not started yet, this key will not be included in the response.
	DownloadSpeed   string     `json:"downloadSpeed"`   // Download speed of this download measured in bytes/sec.
	UploadSpeed     string     `json:"uploadSpeed"`     // Upload speed of this download measured in bytes/sec.
	InfoHash        string     `json:"infoHash"`        // InfoHash. BitTorrent only.
	NumSeeders      string     `json:"numSeeders"`      // The number of seeders aria2 has connected to. BitTorrent only.
	Seeder          string     `json:"seeder"`          // true if the local endpoint is a seeder. Otherwise false. BitTorrent only.
	PieceLength     string     `json:"pieceLength"`     // Piece length in bytes.
	NumPieces       string     `json:"numPieces"`       // The number of pieces.
	Connections     string     `json:"connections"`     // The number of peers/servers aria2 has connected to.
	ErrorCode       string     `json:"errorCode"`       // The code of the last error for this item, if any. The value is a string. The error codes are defined in the EXIT STATUS section. This value is only available for stopped/completed downloads.
	ErrorMessage    string     `json:"errorMessage"`    // The (hopefully) human readable error message associated to errorCode.
	FollowedBy      []string   `json:"followedBy"`      // List of GIDs which are generated as the result of this download. For example, when aria2 downloads a Metalink file, it generates downloads described in the Metalink (see the --follow-metalink option). This value is useful to track auto-generated downloads. If there are no such downloads, this key will not be included in the response.
	BelongsTo       string     `json:"belongsTo"`       // GID of a parent download. Some downloads are a part of another download. For example, if a file in a Metalink has BitTorrent resources, the downloads of ".torrent" files are parts of that parent. If this download has no parent, this key will not be included in the response.
	Dir             string     `json:"dir"`             // Directory to save files.
	Files           []FileInfo `json:"files"`           // Returns the list of files. The elements of this list are the same structs used in aria2.getFiles() method.
	BitTorrent      struct {
		AnnounceList string `json:"announceList"` // List of lists of announce URIs. If the torrent contains announce and no announce-list, announce is converted to the announce-list format.
		Comment      string `json:"comment"`      // The comment of the torrent. comment.utf-8 is used if available.
		CreationDate string `json:"creationDate"` // The creation time of the torrent. The value is an integer since the epoch, measured in seconds.
		Mode         string `json:"mode"`         // File mode of the torrent. The value is either single or multi.
		Info         struct {
			Name string `json:"name"` // name in info dictionary. name.utf-8 is used if available.
		} `json:"info"` // Struct which contains data from Info dictionary. It contains following keys.
	} `json:"bittorrent"` // Struct which contains information retrieved from the .torrent (file). BitTorrent only. It contains following keys.
}

// URIInfo represents an element of response of aria2.getUris
type URIInfo struct {
	URI    string `json:"uri"`    // URI
	Status string `json:"status"` // 'used' if the URI is in use. 'waiting' if the URI is still waiting in the queue.
}

// FileInfo represents an element of response of aria2.getFiles
type FileInfo struct {
	Index           string    `json:"index"`     // Index of the file, starting at 1, in the same order as files appear in the multi-file torrent.
	Path            string    `json:"path"`      // File path.
	Length          string    `json:"length"`    // File size in bytes.
	CompletedLength string    `json:"completed"` // Completed length of this file in bytes. Please note that it is possible that sum of completedLength is less than the completedLength returned by the aria2.tellStatus() method. This is because completedLength in aria2.getFiles() only includes completed pieces. On the other hand, completedLength in aria2.tellStatus() also includes partially completed pieces.
	Selected        string    `json:"selected"`  // true if this file is selected by --select-file option. If --select-file is not specified or this is single-file torrent or not a torrent download at all, this value is always true. Otherwise false.
	URIs            []URIInfo `json:"uris"`      // Returns a list of URIs for this file. The element type is the same struct used in the aria2.getUris() method.
}

// PeerInfo represents an element of response of aria2.getPeers
type PeerInfo struct {
	PeerId        string `json:"peerId"`        // Percent-encoded peer ID.
	IP            string `json:"ip"`            // IP address of the peer.
	Port          string `json:"port"`          // Port number of the peer.
	BitField      string `json:"bitfield"`      // Hexadecimal representation of the download progress of the peer. The highest bit corresponds to the piece at index 0. Set bits indicate the piece is available and unset bits indicate the piece is missing. Any spare bits at the end are set to zero.
	AmChoking     string `json:"amChoking"`     // true if aria2 is choking the peer. Otherwise false.
	PeerChoking   string `json:"peerChoking"`   // true if the peer is choking aria2. Otherwise false.
	DownloadSpeed string `json:"downloadSpeed"` // Download speed (byte/sec) that this client obtains from the peer.
	UploadSpeed   string `json:"uploadSpeed"`   // Upload speed(byte/sec) that this client uploads to the peer.
	Seeder        string `json:"seeder"`        // true if this peer is a seeder. Otherwise false.
}

// ServerInfo represents an element of response of aria2.getServers
type ServerInfo struct {
	Index   string `json:"index"` // Index of the file, starting at 1, in the same order as files appear in the multi-file metalink.
	Servers []struct {
		URI           string `json:"uri"`           // Original URI.
		CurrentURI    string `json:"currentUri"`    // This is the URI currently used for downloading. If redirection is involved, currentUri and uri may differ.
		DownloadSpeed string `json:"downloadSpeed"` // Download speed (byte/sec)
	} `json:"servers"` // A list of structs which contain the following keys.
}

// GlobalStatInfo represents response of aria2.getGlobalStat
type GlobalStatInfo struct {
	DownloadSpeed   string `json:"downloadSpeed"`   // Overall download speed (byte/sec).
	UploadSpeed     string `json:"uploadSpeed"`     // Overall upload speed(byte/sec).
	NumActive       string `json:"numActive"`       // The number of active downloads.
	NumWaiting      string `json:"numWaiting"`      // The number of waiting downloads.
	NumStopped      string `json:"numStopped"`      // The number of stopped downloads in the current session. This value is capped by the --max-download-result option.
	NumStoppedTotal string `json:"numStoppedTotal"` // The number of stopped downloads in the current session and not capped by the --max-download-result option.
}

// VersionInfo represents response of aria2.getVersion
type VersionInfo struct {
	Version  string   `json:"version"`         // Version number of aria2 as a string.
	Features []string `json:"enabledFeatures"` // List of enabled features. Each feature is given as a string.
}

// SessionInfo represents response of aria2.getSessionInfo
type SessionInfo struct {
	Id string `json:"sessionId"` // Session ID, which is generated each time when aria2 is invoked.
}

// Method is an element of parameters used in system.multicall
type Method struct {
	Name   string        `json:"methodName"` // Method name to call
	Params []interface{} `json:"params"`     // Array containing parameters to the method call
}
<commit_msg>fix json format error:<commit_after>//go:generate easyjson -all

package rpc

// StatusInfo represents response of aria2.tellStatus
type StatusInfo struct {
	Gid             string     `json:"gid"`             // GID of the download.
	Status          string     `json:"status"`          // active for currently downloading/seeding downloads. waiting for downloads in the queue; download is not started. paused for paused downloads. error for downloads that were stopped because of error. complete for stopped and completed downloads. removed for the downloads removed by user.
	TotalLength     string     `json:"totalLength"`     // Total length of the download in bytes.
	CompletedLength string     `json:"completedLength"` // Completed length of the download in bytes.
	UploadLength    string     `json:"uploadLength"`    // Uploaded length of the download in bytes.
	BitField        string     `json:"bitfield"`        // Hexadecimal representation of the download progress. The highest bit corresponds to the piece at index 0. Any set bits indicate loaded pieces, while unset bits indicate not yet loaded and/or missing pieces. Any overflow bits at the end are set to zero. When the download was not started yet, this key will not be included in the response.
	DownloadSpeed   string     `json:"downloadSpeed"`   // Download speed of this download measured in bytes/sec.
	UploadSpeed     string     `json:"uploadSpeed"`     // Upload speed of this download measured in bytes/sec.
	InfoHash        string     `json:"infoHash"`        // InfoHash. BitTorrent only.
	NumSeeders      string     `json:"numSeeders"`      // The number of seeders aria2 has connected to. BitTorrent only.
	Seeder          string     `json:"seeder"`          // true if the local endpoint is a seeder. Otherwise false. BitTorrent only.
	PieceLength     string     `json:"pieceLength"`     // Piece length in bytes.
	NumPieces       string     `json:"numPieces"`       // The number of pieces.
	Connections     string     `json:"connections"`     // The number of peers/servers aria2 has connected to.
	ErrorCode       string     `json:"errorCode"`       // The code of the last error for this item, if any. The value is a string. The error codes are defined in the EXIT STATUS section. This value is only available for stopped/completed downloads.
	ErrorMessage    string     `json:"errorMessage"`    // The (hopefully) human readable error message associated to errorCode.
	FollowedBy      []string   `json:"followedBy"`      // List of GIDs which are generated as the result of this download. For example, when aria2 downloads a Metalink file, it generates downloads described in the Metalink (see the --follow-metalink option). This value is useful to track auto-generated downloads. If there are no such downloads, this key will not be included in the response.
	BelongsTo       string     `json:"belongsTo"`       // GID of a parent download. Some downloads are a part of another download. For example, if a file in a Metalink has BitTorrent resources, the downloads of ".torrent" files are parts of that parent. If this download has no parent, this key will not be included in the response.
	Dir             string     `json:"dir"`             // Directory to save files.
	Files           []FileInfo `json:"files"`           // Returns the list of files. The elements of this list are the same structs used in aria2.getFiles() method.
	BitTorrent      struct {
		AnnounceList [][]string `json:"announceList"` // List of lists of announce URIs. If the torrent contains announce and no announce-list, announce is converted to the announce-list format.
		Comment      string     `json:"comment"`      // The comment of the torrent. comment.utf-8 is used if available.
		CreationDate string     `json:"creationDate"` // The creation time of the torrent. The value is an integer since the epoch, measured in seconds.
		Mode         string     `json:"mode"`         // File mode of the torrent. The value is either single or multi.
		Info         struct {
			Name string `json:"name"` // name in info dictionary. name.utf-8 is used if available.
		} `json:"info"` // Struct which contains data from Info dictionary. It contains following keys.
	} `json:"bittorrent"` // Struct which contains information retrieved from the .torrent (file). BitTorrent only. It contains following keys.
}

// URIInfo represents an element of response of aria2.getUris
type URIInfo struct {
	URI    string `json:"uri"`    // URI
	Status string `json:"status"` // 'used' if the URI is in use. 'waiting' if the URI is still waiting in the queue.
}

// FileInfo represents an element of response of aria2.getFiles
type FileInfo struct {
	Index           string    `json:"index"`     // Index of the file, starting at 1, in the same order as files appear in the multi-file torrent.
	Path            string    `json:"path"`      // File path.
	Length          string    `json:"length"`    // File size in bytes.
	CompletedLength string    `json:"completed"` // Completed length of this file in bytes. Please note that it is possible that sum of completedLength is less than the completedLength returned by the aria2.tellStatus() method. This is because completedLength in aria2.getFiles() only includes completed pieces. On the other hand, completedLength in aria2.tellStatus() also includes partially completed pieces.
	Selected        string    `json:"selected"`  // true if this file is selected by --select-file option. If --select-file is not specified or this is single-file torrent or not a torrent download at all, this value is always true. Otherwise false.
	URIs            []URIInfo `json:"uris"`      // Returns a list of URIs for this file. The element type is the same struct used in the aria2.getUris() method.
}

// PeerInfo represents an element of response of aria2.getPeers
type PeerInfo struct {
	PeerId        string `json:"peerId"`        // Percent-encoded peer ID.
	IP            string `json:"ip"`            // IP address of the peer.
	Port          string `json:"port"`          // Port number of the peer.
	BitField      string `json:"bitfield"`      // Hexadecimal representation of the download progress of the peer. The highest bit corresponds to the piece at index 0. Set bits indicate the piece is available and unset bits indicate the piece is missing. Any spare bits at the end are set to zero.
	AmChoking     string `json:"amChoking"`     // true if aria2 is choking the peer. Otherwise false.
	PeerChoking   string `json:"peerChoking"`   // true if the peer is choking aria2. Otherwise false.
	DownloadSpeed string `json:"downloadSpeed"` // Download speed (byte/sec) that this client obtains from the peer.
	UploadSpeed   string `json:"uploadSpeed"`   // Upload speed(byte/sec) that this client uploads to the peer.
	Seeder        string `json:"seeder"`        // true if this peer is a seeder. Otherwise false.
}

// ServerInfo represents an element of response of aria2.getServers
type ServerInfo struct {
	Index   string `json:"index"` // Index of the file, starting at 1, in the same order as files appear in the multi-file metalink.
	Servers []struct {
		URI           string `json:"uri"`           // Original URI.
		CurrentURI    string `json:"currentUri"`    // This is the URI currently used for downloading. If redirection is involved, currentUri and uri may differ.
		DownloadSpeed string `json:"downloadSpeed"` // Download speed (byte/sec)
	} `json:"servers"` // A list of structs which contain the following keys.
}

// GlobalStatInfo represents response of aria2.getGlobalStat
type GlobalStatInfo struct {
	DownloadSpeed   string `json:"downloadSpeed"`   // Overall download speed (byte/sec).
	UploadSpeed     string `json:"uploadSpeed"`     // Overall upload speed(byte/sec).
	NumActive       string `json:"numActive"`       // The number of active downloads.
	NumWaiting      string `json:"numWaiting"`      // The number of waiting downloads.
	NumStopped      string `json:"numStopped"`      // The number of stopped downloads in the current session. This value is capped by the --max-download-result option.
	NumStoppedTotal string `json:"numStoppedTotal"` // The number of stopped downloads in the current session and not capped by the --max-download-result option.
}

// VersionInfo represents response of aria2.getVersion
type VersionInfo struct {
	Version  string   `json:"version"`         // Version number of aria2 as a string.
	Features []string `json:"enabledFeatures"` // List of enabled features. Each feature is given as a string.
}

// SessionInfo represents response of aria2.getSessionInfo
type SessionInfo struct {
	Id string `json:"sessionId"` // Session ID, which is generated each time when aria2 is invoked.
}

// Method is an element of parameters used in system.multicall
type Method struct {
	Name   string        `json:"methodName"` // Method name to call
	Params []interface{} `json:"params"`     // Array containing parameters to the method call
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Space Monkey, Inc.
//
// 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.

// +build cgo

package openssl

// #include <openssl/conf.h>
// #include <openssl/ssl.h>
// #include <openssl/x509v3.h>
//
// void OPENSSL_free_not_a_macro(void *ref) { OPENSSL_free(ref); }
//
import "C"

import (
	"errors"
	"io/ioutil"
	"runtime"
	"time"
	"unsafe"
)

type EVP_MD int

const (
	EVP_NULL      EVP_MD = iota
	EVP_MD5       EVP_MD = iota
	EVP_SHA       EVP_MD = iota
	EVP_SHA1      EVP_MD = iota
	EVP_DSS       EVP_MD = iota
	EVP_DSS1      EVP_MD = iota
	EVP_MDC2      EVP_MD = iota
	EVP_RIPEMD160 EVP_MD = iota
	EVP_SHA224    EVP_MD = iota
	EVP_SHA256    EVP_MD = iota
	EVP_SHA384    EVP_MD = iota
	EVP_SHA512    EVP_MD = iota
)

type Certificate struct {
	x      *C.X509
	Issuer *Certificate
	ref    interface{}
	pubKey PublicKey
}

type CertificateInfo struct {
	Serial       int64
	Issued       time.Duration
	Expires      time.Duration
	Country      string
	Organization string
	CommonName   string
}

type Name struct {
	name *C.X509_NAME
}

// Allocate and return a new Name object.
func NewName() (*Name, error) {
	n := C.X509_NAME_new()
	if n == nil {
		return nil, errors.New("could not create x509 name")
	}
	name := &Name{name: n}
	runtime.SetFinalizer(name, func(n *Name) {
		C.X509_NAME_free(n.name)
	})
	return name, nil
}

// AddTextEntry appends a text entry to an X509 NAME.
func (n *Name) AddTextEntry(field, value string) error {
	cfield := C.CString(field)
	defer C.free(unsafe.Pointer(cfield))
	cvalue := (*C.uchar)(unsafe.Pointer(C.CString(value)))
	defer C.free(unsafe.Pointer(cvalue))
	ret := C.X509_NAME_add_entry_by_txt(
		n.name, cfield, C.MBSTRING_ASC, cvalue, -1, -1, 0)
	if ret != 1 {
		return errors.New("failed to add x509 name text entry")
	}
	return nil
}

// AddTextEntries allows adding multiple entries to a name in one call.
func (n *Name) AddTextEntries(entries map[string]string) error {
	for f, v := range entries {
		if err := n.AddTextEntry(f, v); err != nil {
			return err
		}
	}
	return nil
}

// NewCertificate generates a basic certificate based
// on the provided CertificateInfo struct
func NewCertificate(info *CertificateInfo, key PublicKey) (*Certificate, error) {
	c := &Certificate{x: C.X509_new()}
	runtime.SetFinalizer(c, func(c *Certificate) {
		C.X509_free(c.x)
	})

	name, err := c.GetSubjectName()
	if err != nil {
		return nil, err
	}

	entries := make(map[string]string)
	if len(info.Country) > 0 {
		entries["C"] = info.Country
	}
	if len(info.Organization) > 0 {
		entries["O"] = info.Organization
	}
	if len(info.CommonName) > 0 {
		entries["CN"] = info.CommonName
	}

	err = name.AddTextEntries(entries)
	if err != nil {
		return nil, err
	}

	// self-issue for now
	if err := c.SetIssuerName(name); err != nil {
		return nil, err
	}
	if err := c.SetSerial(info.Serial); err != nil {
		return nil, err
	}
	if err := c.SetIssueDate(info.Issued); err != nil {
		return nil, err
	}
	if err := c.SetExpireDate(info.Expires); err != nil {
		return nil, err
	}
	if err := c.SetPubKey(key); err != nil {
		return nil, err
	}
	return c, nil
}

func (c *Certificate) GetSubjectName() (*Name, error) {
	n := C.X509_get_subject_name(c.x)
	if n == nil {
		return nil, errors.New("failed to get subject name")
	}
	return &Name{name: n}, nil
}

func (c *Certificate) GetIssuerName() (*Name, error) {
	n := C.X509_get_issuer_name(c.x)
	if n == nil {
		return nil, errors.New("failed to get issuer name")
	}
	return &Name{name: n}, nil
}

func (c *Certificate) SetSubjectName(name *Name) error {
	if C.X509_set_subject_name(c.x, name.name) != 1 {
		return errors.New("failed to set subject name")
	}
	return nil
}

// SetIssuer updates the stored Issuer cert
// and the internal x509 Issuer Name of a certificate.
// The stored Issuer reference is used when adding extensions.
func (c *Certificate) SetIssuer(issuer *Certificate) error {
	name, err := issuer.GetSubjectName()
	if err != nil {
		return err
	}
	if err = c.SetIssuerName(name); err != nil {
		return err
	}
	c.Issuer = issuer
	return nil
}

// SetIssuerName populates the issuer name of a certificate.
// Use SetIssuer instead, if possible.
func (c *Certificate) SetIssuerName(name *Name) error {
	if C.X509_set_issuer_name(c.x, name.name) != 1 {
		return errors.New("failed to set subject name")
	}
	return nil
}

// SetSerial sets the serial of a certificate.
func (c *Certificate) SetSerial(serial int64) error {
	if C.ASN1_INTEGER_set(C.X509_get_serialNumber(c.x), C.long(serial)) != 1 {
		return errors.New("failed to set serial")
	}
	return nil
}

// SetIssueDate sets the certificate issue date relative to the current time.
func (c *Certificate) SetIssueDate(when time.Duration) error {
	offset := C.long(when / time.Second)
	result := C.X509_gmtime_adj(c.x.cert_info.validity.notBefore, offset)
	if result == nil {
		return errors.New("failed to set issue date")
	}
	return nil
}

// SetExpireDate sets the certificate issue date relative to the current time.
func (c *Certificate) SetExpireDate(when time.Duration) error {
	offset := C.long(when / time.Second)
	result := C.X509_gmtime_adj(c.x.cert_info.validity.notAfter, offset)
	if result == nil {
		return errors.New("failed to set expire date")
	}
	return nil
}

// SetPubKey assigns a new public key to a certificate.
func (c *Certificate) SetPubKey(pubKey PublicKey) error {
	c.pubKey = pubKey
	if C.X509_set_pubkey(c.x, pubKey.evpPKey()) != 1 {
		return errors.New("failed to set public key")
	}
	return nil
}

// Sign a certificate using a private key and a digest name.
// Accepted digest names are 'sha256', 'sha384', and 'sha512'.
func (c *Certificate) Sign(privKey PrivateKey, digest EVP_MD) error {
	var md *C.EVP_MD
	switch digest {
	// please don't use these digest functions
	case EVP_NULL:
		md = C.EVP_md_null()
	case EVP_MD5:
		md = C.EVP_md5()
	case EVP_SHA:
		md = C.EVP_sha()
	case EVP_SHA1:
		md = C.EVP_sha1()
	case EVP_DSS:
		md = C.EVP_dss()
	case EVP_DSS1:
		md = C.EVP_dss1()
	case EVP_RIPEMD160:
		md = C.EVP_ripemd160()
	case EVP_SHA224:
		md = C.EVP_sha224()
	// you actually want one of these
	case EVP_SHA256:
		md = C.EVP_sha256()
	case EVP_SHA384:
		md = C.EVP_sha384()
	case EVP_SHA512:
		md = C.EVP_sha512()
	}
	if C.X509_sign(c.x, privKey.evpPKey(), md) <= 0 {
		return errors.New("failed to sign certificate")
	}
	return nil
}

// Add an extension to a certificate.
// Extension constants are NID_* as found in openssl.
func (c *Certificate) AddExtension(nid NID, value string) error {
	issuer := c
	if c.Issuer != nil {
		issuer = c.Issuer
	}
	var ctx C.X509V3_CTX
	C.X509V3_set_ctx(&ctx, c.x, issuer.x, nil, nil, 0)
	ex := C.X509V3_EXT_conf_nid(nil, &ctx, C.int(nid), C.CString(value))
	if ex == nil {
		return errors.New("failed to create x509v3 extension")
	}
	defer C.X509_EXTENSION_free(ex)
	if C.X509_add_ext(c.x, ex, -1) <= 0 {
		return errors.New("failed to add x509v3 extension")
	}
	return nil
}

// Wraps AddExtension using a map of NID to text extension.
// Will return without finishing if it encounters an error.
func (c *Certificate) AddExtensions(extensions map[NID]string) error {
	for nid, value := range extensions {
		if err := c.AddExtension(nid, value); err != nil {
			return err
		}
	}
	return nil
}

// LoadCertificateFromPEM loads an X509 certificate from a PEM-encoded block.
func LoadCertificateFromPEM(pem_block []byte) (*Certificate, error) {
	if len(pem_block) == 0 {
		return nil, errors.New("empty pem block")
	}
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()
	bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]),
		C.int(len(pem_block)))
	cert := C.PEM_read_bio_X509(bio, nil, nil, nil)
	C.BIO_free(bio)
	if cert == nil {
		return nil, errorFromErrorQueue()
	}
	x := &Certificate{x: cert}
	runtime.SetFinalizer(x, func(x *Certificate) {
		C.X509_free(x.x)
	})
	return x, nil
}

// MarshalPEM converts the X509 certificate to PEM-encoded format
func (c *Certificate) MarshalPEM() (pem_block []byte, err error) {
	bio := C.BIO_new(C.BIO_s_mem())
	if bio == nil {
		return nil, errors.New("failed to allocate memory BIO")
	}
	defer C.BIO_free(bio)
	if int(C.PEM_write_bio_X509(bio, c.x)) != 1 {
		return nil, errors.New("failed dumping certificate")
	}
	return ioutil.ReadAll(asAnyBio(bio))
}

// PublicKey returns the public key embedded in the X509 certificate.
func (c *Certificate) PublicKey() (PublicKey, error) {
	pkey := C.X509_get_pubkey(c.x)
	if pkey == nil {
		return nil, errors.New("no public key found")
	}
	key := &pKey{key: pkey}
	runtime.SetFinalizer(key, func(key *pKey) {
		C.EVP_PKEY_free(key.key)
	})
	return key, nil
}

// GetSerialNumberHex returns the certificate's serial number in hex format
func (c *Certificate) GetSerialNumberHex() (serial string) {
	asn1_i := C.X509_get_serialNumber(c.x)
	bignum := C.ASN1_INTEGER_to_BN(asn1_i, nil)
	hex := C.BN_bn2hex(bignum)
	serial = C.GoString(hex)
	C.BN_free(bignum)
	C.OPENSSL_free_not_a_macro(unsafe.Pointer(hex))
	return
}
<commit_msg>Allow encoding certificates as DER<commit_after>// Copyright (C) 2014 Space Monkey, Inc.
//
// 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.

// +build cgo

package openssl

// #include <openssl/conf.h>
// #include <openssl/ssl.h>
// #include <openssl/x509v3.h>
//
// void OPENSSL_free_not_a_macro(void *ref) { OPENSSL_free(ref); }
//
import "C"

import (
	"errors"
	"io/ioutil"
	"runtime"
	"time"
	"unsafe"
)

type EVP_MD int

const (
	EVP_NULL      EVP_MD = iota
	EVP_MD5       EVP_MD = iota
	EVP_SHA       EVP_MD = iota
	EVP_SHA1      EVP_MD = iota
	EVP_DSS       EVP_MD = iota
	EVP_DSS1      EVP_MD = iota
	EVP_MDC2      EVP_MD = iota
	EVP_RIPEMD160 EVP_MD = iota
	EVP_SHA224    EVP_MD = iota
	EVP_SHA256    EVP_MD = iota
	EVP_SHA384    EVP_MD = iota
	EVP_SHA512    EVP_MD = iota
)

type Certificate struct {
	x      *C.X509
	Issuer *Certificate
	ref    interface{}
	pubKey PublicKey
}

type CertificateInfo struct {
	Serial       int64
	Issued       time.Duration
	Expires      time.Duration
	Country      string
	Organization string
	CommonName   string
}

type Name struct {
	name *C.X509_NAME
}

// Allocate and return a new Name object.
func NewName() (*Name, error) {
	n := C.X509_NAME_new()
	if n == nil {
		return nil, errors.New("could not create x509 name")
	}
	name := &Name{name: n}
	runtime.SetFinalizer(name, func(n *Name) {
		C.X509_NAME_free(n.name)
	})
	return name, nil
}

// AddTextEntry appends a text entry to an X509 NAME.
func (n *Name) AddTextEntry(field, value string) error {
	cfield := C.CString(field)
	defer C.free(unsafe.Pointer(cfield))
	cvalue := (*C.uchar)(unsafe.Pointer(C.CString(value)))
	defer C.free(unsafe.Pointer(cvalue))
	ret := C.X509_NAME_add_entry_by_txt(
		n.name, cfield, C.MBSTRING_ASC, cvalue, -1, -1, 0)
	if ret != 1 {
		return errors.New("failed to add x509 name text entry")
	}
	return nil
}

// AddTextEntries allows adding multiple entries to a name in one call.
func (n *Name) AddTextEntries(entries map[string]string) error {
	for f, v := range entries {
		if err := n.AddTextEntry(f, v); err != nil {
			return err
		}
	}
	return nil
}

// NewCertificate generates a basic certificate based
// on the provided CertificateInfo struct
func NewCertificate(info *CertificateInfo, key PublicKey) (*Certificate, error) {
	c := &Certificate{x: C.X509_new()}
	runtime.SetFinalizer(c, func(c *Certificate) {
		C.X509_free(c.x)
	})

	name, err := c.GetSubjectName()
	if err != nil {
		return nil, err
	}

	entries := make(map[string]string)
	if len(info.Country) > 0 {
		entries["C"] = info.Country
	}
	if len(info.Organization) > 0 {
		entries["O"] = info.Organization
	}
	if len(info.CommonName) > 0 {
		entries["CN"] = info.CommonName
	}

	err = name.AddTextEntries(entries)
	if err != nil {
		return nil, err
	}

	// self-issue for now
	if err := c.SetIssuerName(name); err != nil {
		return nil, err
	}
	if err := c.SetSerial(info.Serial); err != nil {
		return nil, err
	}
	if err := c.SetIssueDate(info.Issued); err != nil {
		return nil, err
	}
	if err := c.SetExpireDate(info.Expires); err != nil {
		return nil, err
	}
	if err := c.SetPubKey(key); err != nil {
		return nil, err
	}
	return c, nil
}

func (c *Certificate) GetSubjectName() (*Name, error) {
	n := C.X509_get_subject_name(c.x)
	if n == nil {
		return nil, errors.New("failed to get subject name")
	}
	return &Name{name: n}, nil
}

func (c *Certificate) GetIssuerName() (*Name, error) {
	n := C.X509_get_issuer_name(c.x)
	if n == nil {
		return nil, errors.New("failed to get issuer name")
	}
	return &Name{name: n}, nil
}

func (c *Certificate) SetSubjectName(name *Name) error {
	if C.X509_set_subject_name(c.x, name.name) != 1 {
		return errors.New("failed to set subject name")
	}
	return nil
}

// SetIssuer updates the stored Issuer cert
// and the internal x509 Issuer Name of a certificate.
// The stored Issuer reference is used when adding extensions.
func (c *Certificate) SetIssuer(issuer *Certificate) error {
	name, err := issuer.GetSubjectName()
	if err != nil {
		return err
	}
	if err = c.SetIssuerName(name); err != nil {
		return err
	}
	c.Issuer = issuer
	return nil
}

// SetIssuerName populates the issuer name of a certificate.
// Use SetIssuer instead, if possible.
func (c *Certificate) SetIssuerName(name *Name) error {
	if C.X509_set_issuer_name(c.x, name.name) != 1 {
		return errors.New("failed to set subject name")
	}
	return nil
}

// SetSerial sets the serial of a certificate.
func (c *Certificate) SetSerial(serial int64) error {
	if C.ASN1_INTEGER_set(C.X509_get_serialNumber(c.x), C.long(serial)) != 1 {
		return errors.New("failed to set serial")
	}
	return nil
}

// SetIssueDate sets the certificate issue date relative to the current time.
func (c *Certificate) SetIssueDate(when time.Duration) error {
	offset := C.long(when / time.Second)
	result := C.X509_gmtime_adj(c.x.cert_info.validity.notBefore, offset)
	if result == nil {
		return errors.New("failed to set issue date")
	}
	return nil
}

// SetExpireDate sets the certificate issue date relative to the current time.
func (c *Certificate) SetExpireDate(when time.Duration) error {
	offset := C.long(when / time.Second)
	result := C.X509_gmtime_adj(c.x.cert_info.validity.notAfter, offset)
	if result == nil {
		return errors.New("failed to set expire date")
	}
	return nil
}

// SetPubKey assigns a new public key to a certificate.
func (c *Certificate) SetPubKey(pubKey PublicKey) error {
	c.pubKey = pubKey
	if C.X509_set_pubkey(c.x, pubKey.evpPKey()) != 1 {
		return errors.New("failed to set public key")
	}
	return nil
}

// Sign a certificate using a private key and a digest name.
// Accepted digest names are 'sha256', 'sha384', and 'sha512'.
func (c *Certificate) Sign(privKey PrivateKey, digest EVP_MD) error {
	var md *C.EVP_MD
	switch digest {
	// please don't use these digest functions
	case EVP_NULL:
		md = C.EVP_md_null()
	case EVP_MD5:
		md = C.EVP_md5()
	case EVP_SHA:
		md = C.EVP_sha()
	case EVP_SHA1:
		md = C.EVP_sha1()
	case EVP_DSS:
		md = C.EVP_dss()
	case EVP_DSS1:
		md = C.EVP_dss1()
	case EVP_RIPEMD160:
		md = C.EVP_ripemd160()
	case EVP_SHA224:
		md = C.EVP_sha224()
	// you actually want one of these
	case EVP_SHA256:
		md = C.EVP_sha256()
	case EVP_SHA384:
		md = C.EVP_sha384()
	case EVP_SHA512:
		md = C.EVP_sha512()
	}
	if C.X509_sign(c.x, privKey.evpPKey(), md) <= 0 {
		return errors.New("failed to sign certificate")
	}
	return nil
}

// Add an extension to a certificate.
// Extension constants are NID_* as found in openssl.
func (c *Certificate) AddExtension(nid NID, value string) error {
	issuer := c
	if c.Issuer != nil {
		issuer = c.Issuer
	}
	var ctx C.X509V3_CTX
	C.X509V3_set_ctx(&ctx, c.x, issuer.x, nil, nil, 0)
	ex := C.X509V3_EXT_conf_nid(nil, &ctx, C.int(nid), C.CString(value))
	if ex == nil {
		return errors.New("failed to create x509v3 extension")
	}
	defer C.X509_EXTENSION_free(ex)
	if C.X509_add_ext(c.x, ex, -1) <= 0 {
		return errors.New("failed to add x509v3 extension")
	}
	return nil
}

// Wraps AddExtension using a map of NID to text extension.
// Will return without finishing if it encounters an error.
func (c *Certificate) AddExtensions(extensions map[NID]string) error {
	for nid, value := range extensions {
		if err := c.AddExtension(nid, value); err != nil {
			return err
		}
	}
	return nil
}

// LoadCertificateFromPEM loads an X509 certificate from a PEM-encoded block.
func LoadCertificateFromPEM(pem_block []byte) (*Certificate, error) {
	if len(pem_block) == 0 {
		return nil, errors.New("empty pem block")
	}
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()
	bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]),
		C.int(len(pem_block)))
	cert := C.PEM_read_bio_X509(bio, nil, nil, nil)
	C.BIO_free(bio)
	if cert == nil {
		return nil, errorFromErrorQueue()
	}
	x := &Certificate{x: cert}
	runtime.SetFinalizer(x, func(x *Certificate) {
		C.X509_free(x.x)
	})
	return x, nil
}

// MarshalPEM converts the X509 certificate to PEM-encoded format
func (c *Certificate) MarshalPEM() (pem_block []byte, err error) {
	bio := C.BIO_new(C.BIO_s_mem())
	if bio == nil {
		return nil, errors.New("failed to allocate memory BIO")
	}
	defer C.BIO_free(bio)
	if int(C.PEM_write_bio_X509(bio, c.x)) != 1 {
		return nil, errors.New("failed dumping certificate")
	}
	return ioutil.ReadAll(asAnyBio(bio))
}

// MarshalDER converts the X509 certificate to DER-encoded format
func (c *Certificate) MarshalDER() (pem_block []byte, err error) {
	bio := C.BIO_new(C.BIO_s_mem())
	if bio == nil {
		return nil, errors.New("failed to allocate memory BIO")
	}
	defer C.BIO_free(bio)
	if int(C.i2d_X509_bio(bio, c.x)) != 1 {
		return nil, errors.New("failed dumping certificate")
	}
	return ioutil.ReadAll(asAnyBio(bio))
}

// PublicKey returns the public key embedded in the X509 certificate.
func (c *Certificate) PublicKey() (PublicKey, error) {
	pkey := C.X509_get_pubkey(c.x)
	if pkey == nil {
		return nil, errors.New("no public key found")
	}
	key := &pKey{key: pkey}
	runtime.SetFinalizer(key, func(key *pKey) {
		C.EVP_PKEY_free(key.key)
	})
	return key, nil
}

// GetSerialNumberHex returns the certificate's serial number in hex format
func (c *Certificate) GetSerialNumberHex() (serial string) {
	asn1_i := C.X509_get_serialNumber(c.x)
	bignum := C.ASN1_INTEGER_to_BN(asn1_i, nil)
	hex := C.BN_bn2hex(bignum)
	serial = C.GoString(hex)
	C.BN_free(bignum)
	C.OPENSSL_free_not_a_macro(unsafe.Pointer(hex))
	return
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"net"
	"strings"
	"sync"
)

var outputChannel = make(chan chan string, 5)
var peers []Peer
var messagesReceivedAlready = make(map[string]bool)
var messagesReceivedAlreadyLock = &sync.Mutex{}
var peerChannel chan Peer

type Peer struct {
	conn     net.Conn
	username string
}
func createConnection(ip string){
	go func(){
		conn,_ := net.Dial("tcp",ip)
		handleConn(conn)
	}()
}
func onMessageReceived(message string, peerFrom Peer) {
	messagesReceivedAlreadyLock.Lock()
	_, found := messagesReceivedAlready[message]
	if found {
		fmt.Println("Lol wait. " + peerFrom.username + " sent us something we already has. Ignoring...")
		messagesReceivedAlreadyLock.Unlock()
		return
	}
	messagesReceivedAlready[message] = true
	messagesReceivedAlreadyLock.Unlock()
	messageChannel := make(chan string, 100)
	outputChannel <- messageChannel
	go func() {
		defer close(messageChannel)
		processMessage(message, messageChannel, peerFrom)
	}()
}
func processMessage(message string, messageChannel chan string, peerFrom Peer) {
	messageChannel <- "Hey, a message from " + peerFrom.username + ". "
	messageChannel <- "Beginning decryption. "
	msg,err:=decrypt(message)
	if err!=nil{
		messageChannel<-"Unable to decrypt =("
		messageChannel<-err.Error()
		return
	}
	messageChannel <- "Done decrypting. "
	messageChannel <- "Here's the message: "
	messageChannel <- msg
}

func handleConn(conn net.Conn) {
	fmt.Println("CONNECTION BABE. Sending our name")
	conn.Write([]byte(config.Username + "\n"))
	username, err := bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		return
	}
	username = strings.TrimSpace(username)
	fmt.Println("Received username: " + username)
	//here make sure that username is valid
	peerObj := Peer{conn: conn, username: username}
	peerChannel <- peerObj
}
func onConnClose(peer Peer) {
	//remove from list of peers, but idk how to do that in go =(
	fmt.Println("Disconnected from " + peer.username)
}
func peerListen(peer Peer) {
	defer peer.conn.Close()
	defer onConnClose(peer)
	conn := peer.conn
	username := peer.username
	fmt.Println("Beginning to listen to " + username)
	for {
		message, err := bufio.NewReader(conn).ReadString('\n')
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		message = strings.TrimSpace(message)
		onMessageReceived(message, peer)
	}
}
func peerWithName(name string) int {
	for i := 0; i < len(peers); i++ {
		if peers[i].username == name {
			return i
		}
	}
	return -1
}
func listen() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		panic(err)
	}
	defer ln.Close()
	peerChannel := make(chan Peer)
	defer close(peerChannel)
	go func() {
		for {
			peer, ok := <-peerChannel
			if ok {
				if peerWithName(peer.username) == -1 {
					peers = append(peers, peer)
					go peerListen(peer)
				} else {
					peer.conn.Close()
					fmt.Println("Sadly we are already connected to " + peer.username + ". Disconnecting")
				}
			} else {
				return
			}
		}
	}()
	for {
		conn, err := ln.Accept()
		if err != nil {
			panic(err)
		}
		go handleConn(conn)
	}
}
<commit_msg>updated<commit_after>package main

import (
	"bufio"
	"fmt"
	"net"
	"strings"
	"sync"
)

var outputChannel = make(chan chan string, 5)
var peers []Peer
var messagesReceivedAlready = make(map[string]bool)
var messagesReceivedAlreadyLock = &sync.Mutex{}
var peerChannel chan Peer

type Peer struct {
	conn     net.Conn
	username string
}
func createConnection(ip string){
	go func(){
		conn,_ := net.Dial("tcp",ip)
		handleConn(conn)
	}()
}
func broadcastMessage(message string){
	encrypted,err:=encrypt(message,[]string{"slaidan_lt","leijurv"})
	if err!=nil{
		panic(err)
	}
	for i:=range peers {
		fmt.Println("Sending "+message+" to "+peers[i].username)
		peers[i].conn.Write([]byte(encrypted+"\n"))
	}
}
func onMessageReceived(message string, peerFrom Peer) {
	messagesReceivedAlreadyLock.Lock()
	_, found := messagesReceivedAlready[message]
	if found {
		fmt.Println("Lol wait. " + peerFrom.username + " sent us something we already has. Ignoring...")
		messagesReceivedAlreadyLock.Unlock()
		return
	}
	messagesReceivedAlready[message] = true
	messagesReceivedAlreadyLock.Unlock()
	messageChannel := make(chan string, 100)
	outputChannel <- messageChannel
	go func() {
		defer close(messageChannel)
		processMessage(message, messageChannel, peerFrom)
	}()
}
func processMessage(message string, messageChannel chan string, peerFrom Peer) {
	messageChannel <- "Hey, a message from " + peerFrom.username + ". "
	messageChannel <- "Beginning decryption. "
	msg,err:=decrypt(message)
	if err!=nil{
		messageChannel<-"Unable to decrypt =("
		messageChannel<-err.Error()
		return
	}
	messageChannel <- "Done decrypting. "
	messageChannel <- "Here's the message: "
	messageChannel <- msg
}

func handleConn(conn net.Conn) {
	fmt.Println("CONNECTION BABE. Sending our name")
	conn.Write([]byte(config.Username + "\n"))
	username, err := bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		return
	}
	username = strings.TrimSpace(username)
	fmt.Println("Received username: " + username)
	//here make sure that username is valid
	peerObj := Peer{conn: conn, username: username}
	peerChannel <- peerObj
}
func onConnClose(peer Peer) {
	//remove from list of peers, but idk how to do that in go =(
	fmt.Println("Disconnected from " + peer.username)
}
func peerListen(peer Peer) {
	defer peer.conn.Close()
	defer onConnClose(peer)
	conn := peer.conn
	username := peer.username
	fmt.Println("Beginning to listen to " + username)
	for {
		message, err := bufio.NewReader(conn).ReadString('\n')
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		message = strings.TrimSpace(message)
		onMessageReceived(message, peer)
	}
}
func peerWithName(name string) int {
	for i := 0; i < len(peers); i++ {
		if peers[i].username == name {
			return i
		}
	}
	return -1
}
func listen() {
	ln, err := net.Listen("tcp", ":8081")
	if err != nil {
		panic(err)
	}
	defer ln.Close()
	peerChannel = make(chan Peer)
	defer close(peerChannel)
	go func() {
		for {
			peer, ok := <-peerChannel
			if ok {
				if peerWithName(peer.username) == -1 {
					peers = append(peers, peer)
					broadcastMessage("lol dank memes")
					go peerListen(peer)
				} else {
					peer.conn.Close()
					fmt.Println("Sadly we are already connected to " + peer.username + ". Disconnecting")
				}
			} else {
				fmt.Println("Peers over")
				return
			}
		}
	}()
	for {
		conn, err := ln.Accept()
		if err != nil {
			panic(err)
		}
		go handleConn(conn)
	}
}
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME     = ""
	DEFAULT_MESSAGE_ASUSER       = false
	DEFAULT_MESSAGE_PARSE        = ""
	DEFAULT_MESSAGE_LINK_NAMES   = 0
	DEFAULT_MESSAGE_UNFURL_LINKS = true
	DEFAULT_MESSAGE_UNFURL_MEDIA = false
	DEFAULT_MESSAGE_ICON_URL     = ""
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
	DEFAULT_MESSAGE_MARKDOWN     = true
	DEFAULT_MESSAGE_ESCAPE_TEXT  = true
)

type chatResponseFull struct {
	ChannelId string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// AttachmentField contains information for an attachment field
// An Attachment can contain multiple of these
type AttachmentField struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// Attachment contains all the information for an attachment
type Attachment struct {
	Fallback string `json:"fallback"`

	Color string `json:"color,omitempty"`

	Pretext string `json:"pretext,omitempty"`

	AuthorName string `json:"author_name,omitempty"`
	AuthorLink string `json:"author_link,omitempty"`
	AuthorIcon string `json:"author_icon,omitempty"`

	Title     string `json:"title,omitempty"`
	TitleLink string `json:"title_link,omitempty"`

	Text string `json:"text"`

	ImageURL string `json:"image_url,omitempty"`

	Fields []AttachmentField `json:"fields,omitempty"`

	MarkdownIn []string `json:"mrkdwn_in,omitempty"`
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text        string
	Username    string
	AsUser      bool
	Parse       string
	LinkNames   int
	Attachments []Attachment
	UnfurlLinks bool
	UnfurlMedia bool
	IconURL     string
	IconEmoji   string
	Markdown    bool `json:"mrkdwn,omitempty"`
	EscapeText  bool
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		AsUser:      DEFAULT_MESSAGE_ASUSER,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
		Markdown:    DEFAULT_MESSAGE_MARKDOWN,
		EscapeText:  DEFAULT_MESSAGE_ESCAPE_TEXT,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Slack) DeleteMessage(channelId, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

func escapeMessage(message string) string {
	/*
		& replaced with &
		< replaced with <
		> replaced with >
	*/
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel
// Message is escaped by default according to https://api.slack.com/docs/formatting
func (api *Slack) PostMessage(channelId string, text string, params PostMessageParameters) (channel string, timestamp string, err error) {
	if params.EscapeText {
		text = escapeMessage(text)
	}
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {text},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.AsUser != DEFAULT_MESSAGE_ASUSER {
		values.Set("as_user", "true")
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "true")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}
	if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
		values.Set("mrkdwn", "false")
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Slack) UpdateMessage(channelId, timestamp, text string, escape bool) (string, string, string, error) {
	if escape {
		text = escapeMessage(text)
	}
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {text},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.ChannelId, response.Timestamp, response.Text, nil
}
<commit_msg>Remove escape argument from UpdateMessage The updateMessage API call doesn't support formatted links anyway<commit_after>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME     = ""
	DEFAULT_MESSAGE_ASUSER       = false
	DEFAULT_MESSAGE_PARSE        = ""
	DEFAULT_MESSAGE_LINK_NAMES   = 0
	DEFAULT_MESSAGE_UNFURL_LINKS = true
	DEFAULT_MESSAGE_UNFURL_MEDIA = false
	DEFAULT_MESSAGE_ICON_URL     = ""
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
	DEFAULT_MESSAGE_MARKDOWN     = true
	DEFAULT_MESSAGE_ESCAPE_TEXT  = true
)

type chatResponseFull struct {
	ChannelId string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// AttachmentField contains information for an attachment field
// An Attachment can contain multiple of these
type AttachmentField struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// Attachment contains all the information for an attachment
type Attachment struct {
	Fallback string `json:"fallback"`

	Color string `json:"color,omitempty"`

	Pretext string `json:"pretext,omitempty"`

	AuthorName string `json:"author_name,omitempty"`
	AuthorLink string `json:"author_link,omitempty"`
	AuthorIcon string `json:"author_icon,omitempty"`

	Title     string `json:"title,omitempty"`
	TitleLink string `json:"title_link,omitempty"`

	Text string `json:"text"`

	ImageURL string `json:"image_url,omitempty"`

	Fields []AttachmentField `json:"fields,omitempty"`

	MarkdownIn []string `json:"mrkdwn_in,omitempty"`
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text        string
	Username    string
	AsUser      bool
	Parse       string
	LinkNames   int
	Attachments []Attachment
	UnfurlLinks bool
	UnfurlMedia bool
	IconURL     string
	IconEmoji   string
	Markdown    bool `json:"mrkdwn,omitempty"`
	EscapeText  bool
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		AsUser:      DEFAULT_MESSAGE_ASUSER,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
		Markdown:    DEFAULT_MESSAGE_MARKDOWN,
		EscapeText:  DEFAULT_MESSAGE_ESCAPE_TEXT,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := parseResponse(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Slack) DeleteMessage(channelId, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

func escapeMessage(message string) string {
	/*
		& replaced with &
		< replaced with <
		> replaced with >
	*/
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel
// Message is escaped by default according to https://api.slack.com/docs/formatting
func (api *Slack) PostMessage(channelId string, text string, params PostMessageParameters) (channel string, timestamp string, err error) {
	if params.EscapeText {
		text = escapeMessage(text)
	}
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {text},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.AsUser != DEFAULT_MESSAGE_ASUSER {
		values.Set("as_user", "true")
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "true")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}
	if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
		values.Set("mrkdwn", "false")
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.ChannelId, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Slack) UpdateMessage(channelId, timestamp, text string) (string, string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channelId},
		"text":    {escapeMessage(text)},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.ChannelId, response.Timestamp, response.Text, nil
}
<|endoftext|>


<commit_before>package telebot

import (
	"encoding/json"
	"strconv"
	"time"
)

// User object represents a Telegram user, bot.
type User struct {
	ID int64 `json:"id"`

	FirstName    string `json:"first_name"`
	LastName     string `json:"last_name"`
	Username     string `json:"username"`
	LanguageCode string `json:"language_code"`
	IsBot        bool   `json:"is_bot"`
	IsPremium    bool   `json:"is_premium"`
	AddedToMenu  bool   `json:"added_to_attachment_menu"`

	// Returns only in getMe
	CanJoinGroups   bool `json:"can_join_groups"`
	CanReadMessages bool `json:"can_read_all_group_messages"`
	SupportsInline  bool `json:"supports_inline_queries"`
}

// Recipient returns user ID (see Recipient interface).
func (u *User) Recipient() string {
	return strconv.FormatInt(u.ID, 10)
}

// Chat object represents a Telegram user, bot, group or a channel.
type Chat struct {
	ID int64 `json:"id"`

	// See ChatType and consts.
	Type ChatType `json:"type"`

	// Won't be there for ChatPrivate.
	Title string `json:"title"`

	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Username  string `json:"username"`

	// Returns only in getChat
	Bio              string        `json:"bio,omitempty"`
	Photo            *ChatPhoto    `json:"photo,omitempty"`
	Description      string        `json:"description,omitempty"`
	InviteLink       string        `json:"invite_link,omitempty"`
	PinnedMessage    *Message      `json:"pinned_message,omitempty"`
	Permissions      *Rights       `json:"permissions,omitempty"`
	SlowMode         int           `json:"slow_mode_delay,omitempty"`
	StickerSet       string        `json:"sticker_set_name,omitempty"`
	CanSetStickerSet bool          `json:"can_set_sticker_set,omitempty"`
	LinkedChatID     int64         `json:"linked_chat_id,omitempty"`
	ChatLocation     *ChatLocation `json:"location,omitempty"`
	Private          bool          `json:"has_private_forwards,omitempty"`
	Protected        bool          `json:"has_protected_content,omitempty"`
}

type ChatLocation struct {
	Location Location `json:"location,omitempty"`
	Address  string   `json:"address,omitempty"`
}

// ChatPhoto object represents a chat photo.
type ChatPhoto struct {
	// File identifiers of small (160x160) chat photo
	SmallFileID   string `json:"small_file_id"`
	SmallUniqueID string `json:"small_file_unique_id"`

	// File identifiers of big (640x640) chat photo
	BigFileID   string `json:"big_file_id"`
	BigUniqueID string `json:"big_file_unique_id"`
}

// Recipient returns chat ID (see Recipient interface).
func (c *Chat) Recipient() string {
	return strconv.FormatInt(c.ID, 10)
}

// ChatMember object represents information about a single chat member.
type ChatMember struct {
	Rights

	User      *User        `json:"user"`
	Role      MemberStatus `json:"status"`
	Title     string       `json:"custom_title"`
	Anonymous bool         `json:"is_anonymous"`

	// Date when restrictions will be lifted for the user, unix time.
	//
	// If user is restricted for more than 366 days or less than
	// 30 seconds from the current time, they are considered to be
	// restricted forever.
	//
	// Use tele.Forever().
	//
	RestrictedUntil int64 `json:"until_date,omitempty"`

	JoinToSend    string `json:"join_to_send_messages"`
	JoinByRequest string `json:"join_by_request"`
}

// ChatID represents a chat or an user integer ID, which can be used
// as recipient in bot methods. It is very useful in cases where
// you have special group IDs, for example in your config, and don't
// want to wrap it into *tele.Chat every time you send messages.
//
// Example:
//
//		group := tele.ChatID(-100756389456)
//		b.Send(group, "Hello!")
//
//		type Config struct {
//			AdminGroup tele.ChatID `json:"admin_group"`
//		}
//		b.Send(conf.AdminGroup, "Hello!")
//
type ChatID int64

// Recipient returns chat ID (see Recipient interface).
func (i ChatID) Recipient() string {
	return strconv.FormatInt(int64(i), 10)
}

// ChatJoinRequest represents a join request sent to a chat.
type ChatJoinRequest struct {
	// Chat to which the request was sent.
	Chat *Chat `json:"chat"`

	// Sender is the user that sent the join request.
	Sender *User `json:"from"`

	// Unixtime, use ChatJoinRequest.Time() to get time.Time.
	Unixtime int64 `json:"date"`

	// Bio of the user, optional.
	Bio string `json:"bio"`

	// InviteLink is the chat invite link that was used by
	//the user to send the join request, optional.
	InviteLink *ChatInviteLink `json:"invite_link"`
}

// Time returns the moment of chat join request sending in local time.
func (r ChatJoinRequest) Time() time.Time {
	return time.Unix(r.Unixtime, 0)
}

// CreateInviteLink creates an additional invite link for a chat.
func (b *Bot) CreateInviteLink(chat Recipient, link *ChatInviteLink) (*ChatInviteLink, error) {
	params := map[string]string{
		"chat_id": chat.Recipient(),
	}
	if link != nil {
		params["name"] = link.Name

		if link.ExpireUnixtime != 0 {
			params["expire_date"] = strconv.FormatInt(link.ExpireUnixtime, 10)
		}
		if link.MemberLimit > 0 {
			params["member_limit"] = strconv.Itoa(link.MemberLimit)
		} else if link.JoinRequest {
			params["creates_join_request"] = "true"
		}
	}

	data, err := b.Raw("createChatInviteLink", params)
	if err != nil {
		return nil, err
	}

	var resp struct {
		Result ChatInviteLink `json:"result"`
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return nil, wrapError(err)
	}

	return &resp.Result, nil
}

// EditInviteLink edits a non-primary invite link created by the bot.
func (b *Bot) EditInviteLink(chat Recipient, link *ChatInviteLink) (*ChatInviteLink, error) {
	params := map[string]string{
		"chat_id": chat.Recipient(),
	}
	if link != nil {
		params["invite_link"] = link.InviteLink
		params["name"] = link.Name

		if link.ExpireUnixtime != 0 {
			params["expire_date"] = strconv.FormatInt(link.ExpireUnixtime, 10)
		}
		if link.MemberLimit > 0 {
			params["member_limit"] = strconv.Itoa(link.MemberLimit)
		} else if link.JoinRequest {
			params["creates_join_request"] = "true"
		}
	}

	data, err := b.Raw("editChatInviteLink", params)
	if err != nil {
		return nil, err
	}

	var resp struct {
		Result ChatInviteLink `json:"result"`
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return nil, wrapError(err)
	}

	return &resp.Result, nil
}

// RevokeInviteLink revokes an invite link created by the bot.
func (b *Bot) RevokeInviteLink(chat Recipient, link string) (*ChatInviteLink, error) {
	params := map[string]string{
		"chat_id":     chat.Recipient(),
		"invite_link": link,
	}

	data, err := b.Raw("revokeChatInviteLink", params)
	if err != nil {
		return nil, err
	}

	var resp struct {
		Result ChatInviteLink `json:"result"`
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return nil, wrapError(err)
	}

	return &resp.Result, nil
}

// ApproveChatJoinRequest approves a chat join request.
func (b *Bot) ApproveChatJoinRequest(chat Recipient, user *User) error {
	params := map[string]string{
		"chat_id": chat.Recipient(),
		"user_id": user.Recipient(),
	}

	data, err := b.Raw("approveChatJoinRequest", params)
	if err != nil {
		return err
	}

	return extractOk(data)
}

// DeclineChatJoinRequest declines a chat join request.
func (b *Bot) DeclineChatJoinRequest(chat Recipient, user *User) error {
	params := map[string]string{
		"chat_id": chat.Recipient(),
		"user_id": user.Recipient(),
	}

	data, err := b.Raw("declineChatJoinRequest", params)
	if err != nil {
		return err
	}

	return extractOk(data)
}
<commit_msg>chat: add has_restricted_voice_and_video_messages field<commit_after>package telebot

import (
	"encoding/json"
	"strconv"
	"time"
)

// User object represents a Telegram user, bot.
type User struct {
	ID int64 `json:"id"`

	FirstName    string `json:"first_name"`
	LastName     string `json:"last_name"`
	Username     string `json:"username"`
	LanguageCode string `json:"language_code"`
	IsBot        bool   `json:"is_bot"`
	IsPremium    bool   `json:"is_premium"`
	AddedToMenu  bool   `json:"added_to_attachment_menu"`

	// Returns only in getMe
	CanJoinGroups   bool `json:"can_join_groups"`
	CanReadMessages bool `json:"can_read_all_group_messages"`
	SupportsInline  bool `json:"supports_inline_queries"`
}

// Recipient returns user ID (see Recipient interface).
func (u *User) Recipient() string {
	return strconv.FormatInt(u.ID, 10)
}

// Chat object represents a Telegram user, bot, group or a channel.
type Chat struct {
	ID int64 `json:"id"`

	// See ChatType and consts.
	Type ChatType `json:"type"`

	// Won't be there for ChatPrivate.
	Title string `json:"title"`

	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Username  string `json:"username"`

	// Returns only in getChat
	Bio              string        `json:"bio,omitempty"`
	Photo            *ChatPhoto    `json:"photo,omitempty"`
	Description      string        `json:"description,omitempty"`
	InviteLink       string        `json:"invite_link,omitempty"`
	PinnedMessage    *Message      `json:"pinned_message,omitempty"`
	Permissions      *Rights       `json:"permissions,omitempty"`
	SlowMode         int           `json:"slow_mode_delay,omitempty"`
	StickerSet       string        `json:"sticker_set_name,omitempty"`
	CanSetStickerSet bool          `json:"can_set_sticker_set,omitempty"`
	LinkedChatID     int64         `json:"linked_chat_id,omitempty"`
	ChatLocation     *ChatLocation `json:"location,omitempty"`
	Private          bool          `json:"has_private_forwards,omitempty"`
	Protected        bool          `json:"has_protected_content,omitempty"`
	NoVoiceAndVideo  bool          `json:"has_restricted_voice_and_video_messages"`
}

type ChatLocation struct {
	Location Location `json:"location,omitempty"`
	Address  string   `json:"address,omitempty"`
}

// ChatPhoto object represents a chat photo.
type ChatPhoto struct {
	// File identifiers of small (160x160) chat photo
	SmallFileID   string `json:"small_file_id"`
	SmallUniqueID string `json:"small_file_unique_id"`

	// File identifiers of big (640x640) chat photo
	BigFileID   string `json:"big_file_id"`
	BigUniqueID string `json:"big_file_unique_id"`
}

// Recipient returns chat ID (see Recipient interface).
func (c *Chat) Recipient() string {
	return strconv.FormatInt(c.ID, 10)
}

// ChatMember object represents information about a single chat member.
type ChatMember struct {
	Rights

	User      *User        `json:"user"`
	Role      MemberStatus `json:"status"`
	Title     string       `json:"custom_title"`
	Anonymous bool         `json:"is_anonymous"`

	// Date when restrictions will be lifted for the user, unix time.
	//
	// If user is restricted for more than 366 days or less than
	// 30 seconds from the current time, they are considered to be
	// restricted forever.
	//
	// Use tele.Forever().
	//
	RestrictedUntil int64 `json:"until_date,omitempty"`

	JoinToSend    string `json:"join_to_send_messages"`
	JoinByRequest string `json:"join_by_request"`
}

// ChatID represents a chat or an user integer ID, which can be used
// as recipient in bot methods. It is very useful in cases where
// you have special group IDs, for example in your config, and don't
// want to wrap it into *tele.Chat every time you send messages.
//
// Example:
//
//		group := tele.ChatID(-100756389456)
//		b.Send(group, "Hello!")
//
//		type Config struct {
//			AdminGroup tele.ChatID `json:"admin_group"`
//		}
//		b.Send(conf.AdminGroup, "Hello!")
//
type ChatID int64

// Recipient returns chat ID (see Recipient interface).
func (i ChatID) Recipient() string {
	return strconv.FormatInt(int64(i), 10)
}

// ChatJoinRequest represents a join request sent to a chat.
type ChatJoinRequest struct {
	// Chat to which the request was sent.
	Chat *Chat `json:"chat"`

	// Sender is the user that sent the join request.
	Sender *User `json:"from"`

	// Unixtime, use ChatJoinRequest.Time() to get time.Time.
	Unixtime int64 `json:"date"`

	// Bio of the user, optional.
	Bio string `json:"bio"`

	// InviteLink is the chat invite link that was used by
	//the user to send the join request, optional.
	InviteLink *ChatInviteLink `json:"invite_link"`
}

// Time returns the moment of chat join request sending in local time.
func (r ChatJoinRequest) Time() time.Time {
	return time.Unix(r.Unixtime, 0)
}

// CreateInviteLink creates an additional invite link for a chat.
func (b *Bot) CreateInviteLink(chat Recipient, link *ChatInviteLink) (*ChatInviteLink, error) {
	params := map[string]string{
		"chat_id": chat.Recipient(),
	}
	if link != nil {
		params["name"] = link.Name

		if link.ExpireUnixtime != 0 {
			params["expire_date"] = strconv.FormatInt(link.ExpireUnixtime, 10)
		}
		if link.MemberLimit > 0 {
			params["member_limit"] = strconv.Itoa(link.MemberLimit)
		} else if link.JoinRequest {
			params["creates_join_request"] = "true"
		}
	}

	data, err := b.Raw("createChatInviteLink", params)
	if err != nil {
		return nil, err
	}

	var resp struct {
		Result ChatInviteLink `json:"result"`
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return nil, wrapError(err)
	}

	return &resp.Result, nil
}

// EditInviteLink edits a non-primary invite link created by the bot.
func (b *Bot) EditInviteLink(chat Recipient, link *ChatInviteLink) (*ChatInviteLink, error) {
	params := map[string]string{
		"chat_id": chat.Recipient(),
	}
	if link != nil {
		params["invite_link"] = link.InviteLink
		params["name"] = link.Name

		if link.ExpireUnixtime != 0 {
			params["expire_date"] = strconv.FormatInt(link.ExpireUnixtime, 10)
		}
		if link.MemberLimit > 0 {
			params["member_limit"] = strconv.Itoa(link.MemberLimit)
		} else if link.JoinRequest {
			params["creates_join_request"] = "true"
		}
	}

	data, err := b.Raw("editChatInviteLink", params)
	if err != nil {
		return nil, err
	}

	var resp struct {
		Result ChatInviteLink `json:"result"`
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return nil, wrapError(err)
	}

	return &resp.Result, nil
}

// RevokeInviteLink revokes an invite link created by the bot.
func (b *Bot) RevokeInviteLink(chat Recipient, link string) (*ChatInviteLink, error) {
	params := map[string]string{
		"chat_id":     chat.Recipient(),
		"invite_link": link,
	}

	data, err := b.Raw("revokeChatInviteLink", params)
	if err != nil {
		return nil, err
	}

	var resp struct {
		Result ChatInviteLink `json:"result"`
	}
	if err := json.Unmarshal(data, &resp); err != nil {
		return nil, wrapError(err)
	}

	return &resp.Result, nil
}

// ApproveChatJoinRequest approves a chat join request.
func (b *Bot) ApproveChatJoinRequest(chat Recipient, user *User) error {
	params := map[string]string{
		"chat_id": chat.Recipient(),
		"user_id": user.Recipient(),
	}

	data, err := b.Raw("approveChatJoinRequest", params)
	if err != nil {
		return err
	}

	return extractOk(data)
}

// DeclineChatJoinRequest declines a chat join request.
func (b *Bot) DeclineChatJoinRequest(chat Recipient, user *User) error {
	params := map[string]string{
		"chat_id": chat.Recipient(),
		"user_id": user.Recipient(),
	}

	data, err := b.Raw("declineChatJoinRequest", params)
	if err != nil {
		return err
	}

	return extractOk(data)
}
<|endoftext|>


<commit_before>package lldbpi

import "github.com/maxymania/go-nntp-backends/groupdb"
import "github.com/cznic/exp/lldb"
import "io"
import "bytes"
import "os"
import "errors"

func openWAL(fn string) (lldb.Filer,io.Closer,bool,error) {
	first := false
	f,e := os.OpenFile(fn,os.O_RDWR,0660)
	if e!=nil {
		first = true
		f,e = os.OpenFile(fn,os.O_RDWR|os.O_CREATE,0660)
		if e!=nil { return nil,nil,false,e }
	}
	fl,e := os.OpenFile(fn+".wal",os.O_RDWR|os.O_CREATE,0660)
	if e!=nil { f.Close(); return nil,nil,false,e }
	filer,e := lldb.NewACIDFiler(lldb.NewSimpleFileFiler(f),fl)
	if e!=nil { f.Close(); fl.Close(); return nil,nil,false,e }
	return filer,fl,first,nil
}

func Open(opts *groupdb.Options) (groupdb.GroupDB,error) {
	var h1,h2,h3 int64
	f,c,first,e := openWAL(opts.FileName)
	if e!=nil { return nil,e }
	g := new(groupDB)
	g.dprov = opts.DayProvider
	g.filer = f
	g.closr = c
	g.alloc,e = lldb.NewAllocator(g.filer,&lldb.Options{})
	if e!=nil { f.Close(); c.Close(); return nil,e }
	if first {
		g.filer.BeginUpdate()
		defer g.filer.EndUpdate()
		i,e := g.alloc.Alloc([]byte("......"))
		if e!=nil { f.Close(); c.Close(); return nil,e }
		if i!=1 { f.Close(); c.Close(); return nil,errors.New("corrupted") }
		g.group,h1,e = lldb.CreateBTree(g.alloc,bytes.Compare)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.grass,h2,e = lldb.CreateBTree(g.alloc,bytes.Compare)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.tmlog,h3,e = lldb.CreateBTree(g.alloc,reverseComp)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		v,e := lldb.EncodeScalars(h1,h2,h3)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		e = g.alloc.Realloc(1,v)
		if e!=nil { f.Close(); c.Close(); return nil,e }
	} else {
		v,e := g.alloc.Get(nil,1)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		s,e := lldb.DecodeScalars(v)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.group,e = lldb.OpenBTree(g.alloc,bytes.Compare,s[0].(int64))
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.grass,e = lldb.OpenBTree(g.alloc,bytes.Compare,s[1].(int64))
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.tmlog,e = lldb.OpenBTree(g.alloc,reverseComp,s[2].(int64))
		if e!=nil { f.Close(); c.Close(); return nil,e }
	}
	return g,nil
}

func init() {
	groupdb.RegisterPlugin("lldb",Open)
}

<commit_msg>init bug<commit_after>package lldbpi

import "github.com/maxymania/go-nntp-backends/groupdb"
import "github.com/cznic/exp/lldb"
import "io"
import "bytes"
import "os"
import "errors"

func openWAL(fn string) (lldb.Filer,io.Closer,bool,error) {
	first := false
	f,e := os.OpenFile(fn,os.O_RDWR,0660)
	if e!=nil {
		first = true
		f,e = os.OpenFile(fn,os.O_RDWR|os.O_CREATE,0660)
		if e!=nil { return nil,nil,false,e }
	}
	fl,e := os.OpenFile(fn+".wal",os.O_RDWR|os.O_CREATE,0660)
	if e!=nil { f.Close(); return nil,nil,false,e }
	filer,e := lldb.NewACIDFiler(lldb.NewSimpleFileFiler(f),fl)
	if e!=nil { f.Close(); fl.Close(); return nil,nil,false,e }
	return filer,fl,first,nil
}

func Open(opts *groupdb.Options) (groupdb.GroupDB,error) {
	var h1,h2,h3 int64
	f,c,first,e := openWAL(opts.FileName)
	if e!=nil { return nil,e }
	g := new(groupDB)
	g.dprov = opts.DayProvider
	g.filer = f
	g.closr = c
	g.alloc,e = lldb.NewAllocator(g.filer,&lldb.Options{})
	if e!=nil { f.Close(); c.Close(); return nil,e }
	if first {
		g.filer.BeginUpdate()
		i,e := g.alloc.Alloc([]byte("......"))
		if e!=nil { f.Close(); c.Close(); return nil,e }
		if i!=1 { f.Close(); c.Close(); return nil,errors.New("corrupted") }
		g.group,h1,e = lldb.CreateBTree(g.alloc,bytes.Compare)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.grass,h2,e = lldb.CreateBTree(g.alloc,bytes.Compare)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.tmlog,h3,e = lldb.CreateBTree(g.alloc,reverseComp)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		v,e := lldb.EncodeScalars(h1,h2,h3)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		e = g.alloc.Realloc(1,v)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.filer.EndUpdate()
	} else {
		v,e := g.alloc.Get(nil,1)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		s,e := lldb.DecodeScalars(v)
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.group,e = lldb.OpenBTree(g.alloc,bytes.Compare,s[0].(int64))
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.grass,e = lldb.OpenBTree(g.alloc,bytes.Compare,s[1].(int64))
		if e!=nil { f.Close(); c.Close(); return nil,e }
		g.tmlog,e = lldb.OpenBTree(g.alloc,reverseComp,s[2].(int64))
		if e!=nil { f.Close(); c.Close(); return nil,e }
	}
	return g,nil
}

func init() {
	groupdb.RegisterPlugin("lldb",Open)
}

<|endoftext|>