<commit_before>// Copyright (c) 2014, Ben Morgan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package main

import (
	"bufio"
	"bytes"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/goulash/util"
)

// packageExtensions lists the file extensions by which we recognize packages.
// It is only used by the function HasPackageFormat, which is useful for quickly
// separating the wheat from the chaff (e.g. filter out all the packages in a
// directory.)
var packageExtensions = []string{".pkg.tar.xz", ".pkg.tar.gz", ".pkg.tar.bz2"}

// The Package datatype represents all the information that encompasses a Pacman
// package, including the filename of the package.
type Package struct {
	Filename string

	Name            string    // pkgname
	Version         string    // pkgver
	Description     string    // pkgdesc
	Base            string    // pkgbase
	Epoch           uint64    // epoch
	URL             string    // url
	BuildDate       time.Time // builddate
	Packager        string    // packager
	Size            uint64    // size
	Arch            string    // arch: one of any, i686, or x86_64
	License         string    // license
	Backups         []string  // backup
	Replaces        []string  // replaces
	Provides        []string  // provides
	Conflicts       []string  // conflict
	Groups          []string  // group
	Depends         []string  // depend
	OptionalDepends []string  // optdepend
	MakeDepends     []string  // makedepend
	CheckDepends    []string  // checkdepend
	MakeOptions     []string  // makepkgopt
}

// VersionLess compares the version of pi to pkg, and returns true if pi is
// older. It takes the Epoch value in account.
func (pkg *Package) VersionLess(alt *Package) bool {
	// If the Epoch values are different, the package with the higher
	// Epoch value is always more recent.
	if pkg.Epoch != alt.Epoch {
		return pkg.Epoch < alt.Epoch
	}
	return pkg.Version < alt.Version
}

// HasPackageFormat returns true if the filename matches a known
// pacman package format.
func HasPackageFormat(path string) bool {
	for _, ext := range packageExtensions {
		if strings.HasSuffix(path, ext) {
			return true
		}
	}
	return false
}

// ReadPackage reads the package information from a pacman package
// and returns it in the Package datatype.
func ReadPackage(path string) (*Package, error) {
	bs, err := util.ReadFileFromArchive(path, ".PKGINFO")
	if err != nil {
		return nil, err
	}

	r := bytes.NewReader(bs)
	info, err := readPackageInfo(r)
	if err != nil {
		return nil, err
	}

	info.Filename = path
	return info, nil
}

// readPackageInfo reads the package information from a pacman package.
//
// We don't do any specific controlling for you, so you should use
// HasPackageFormat on a path string before using this function on it.
// Even if you don't, nothing bad should happen, but just in case.
func readPackageInfo(r io.Reader) (*Package, error) {
	var info Package
	var err error

	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix("#", line) {
			continue
		}

		kv := strings.Split(line, " = ")
		if len(kv) != 2 {
			continue
		}

		switch kv[0] {
		case "pkgname":
			info.Name = kv[1]
		case "pkgver":
			info.Version = kv[1]
		case "pkgdesc":
			info.Description = kv[1]
		case "pkgbase":
			info.Base = kv[1]
		case "epoch":
			info.Epoch, err = strconv.ParseUint(kv[1], 10, 64)
			if err != nil {
				log.Printf("Warning: cannot parse epoch value '%s'\n", kv[1])
			}
		case "url":
			info.URL = kv[1]
		case "builddate":
			n, err := strconv.ParseInt(kv[1], 10, 64)
			if err != nil {
				log.Printf("Warning: cannot parse build time '%s'\n", kv[1])
			}
			info.BuildDate = time.Unix(n, 0)
		case "packager":
			info.Packager = kv[1]
		case "size":
			info.Size, err = strconv.ParseUint(kv[1], 10, 64)
			if err != nil {
				log.Printf("Warning: cannot parse size value '%s'\n", kv[1])
			}
		case "arch":
			info.Arch = kv[1]
		case "license":
			info.License = kv[1]
		case "depend":
			info.Depends = append(info.Depends, kv[1])
		case "optdepend":
			info.OptionalDepends = append(info.OptionalDepends, kv[1])
		case "makedepend":
			info.MakeDepends = append(info.MakeDepends, kv[1])
		case "checkdepend":
			info.CheckDepends = append(info.CheckDepends, kv[1])
		case "makepkgopt":
			info.MakeOptions = append(info.MakeOptions, kv[1])
		case "backup":
			info.Backups = append(info.Backups, kv[1])
		case "replaces":
			info.Replaces = append(info.Replaces, kv[1])
		case "provides":
			info.Provides = append(info.Provides, kv[1])
		case "conflict":
			info.Conflicts = append(info.Conflicts, kv[1])
		case "group":
			info.Groups = append(info.Groups, kv[1])
		default:
			log.Printf("Warning: unknown field '%s' in .PKGINFO\n", kv[0])
		}
	}
	if err = scanner.Err(); err != nil {
		return nil, err
	}

	return &info, nil
}

// GetAllPackages takes a directory path as an argument, and
// then reads all the package information into a list.
func GetAllPackages(path string) []*Package {
	var pkgs []*Package

	filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			log.Printf("Warning: %s\n", err)
			return nil
		}
		if !info.Mode().IsDir() && HasPackageFormat(path) {
			p, err := ReadPackage(path)
			if err != nil {
				log.Printf("Warning: %s\n", err)
				return nil
			}

			pkgs = append(pkgs, p)
		}

		return nil
	})

	return pkgs
}

// Note that we recurse into subdirectories.
func GetMatchingPackages(path, pkgname string) []*Package {
	var pkgs []*Package

	filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			log.Printf("Warning: %s\n", err)
			return nil
		}
		if !info.Mode().IsDir() && strings.HasPrefix(path, pkgname) && HasPackageFormat(path) {
			p, err := ReadPackage(path)
			if err != nil {
				log.Printf("Warning: %s\n", err)
				return nil
			}

			if p.Name == pkgname {
				pkgs = append(pkgs, p)
			}
		}

		return nil
	})

	return pkgs
}

// Note that we do not recurse into subdirectories!
func GetAllMatchingPackages(path string, pkgnames []string) []*Package {
	var pkgs []*Package

	for _, n := range pkgnames {
		matches, err := filepath.Glob(filepath.Join(path, n+"-*.pkg.tar.*"))
		if err != nil {
			log.Printf("Warning: cannot find package %s.\n", n)
			continue
		}

		for _, fp := range matches {
			p, err := ReadPackage(fp)
			if err != nil {
				log.Printf("Warning: %s\n.", err)
				continue
			}

			if p.Name == n {
				pkgs = append(pkgs, p)
			}
		}
	}

	return pkgs
}

// SplitOldPackages splits the input array into one containing the newest
// packages and another containing the outdated packages.
func SplitOldPackages(pkgs []*Package) (updated []*Package, old []*Package) {
	var m = make(map[string]*Package)

	// Find out which packages are newest and put the others in the old array.
	for _, p := range pkgs {
		if cur, ok := m[p.Name]; ok {
			if cur.VersionLess(p) {
				old = append(old, cur)
			} else {
				old = append(old, p)
				continue
			}
		}
		m[p.Name] = p
	}

	// Add the newest packages to the updated array and return.
	updated = make([]*Package, 0, len(m))
	for _, v := range m {
		updated = append(updated, v)
	}

	return updated, old
}
<commit_msg>Adding SearchAUR function stub<commit_after>// Copyright (c) 2014, Ben Morgan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package main

import (
	"bufio"
	"bytes"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/goulash/util"
)

// packageExtensions lists the file extensions by which we recognize packages.
// It is only used by the function HasPackageFormat, which is useful for quickly
// separating the wheat from the chaff (e.g. filter out all the packages in a
// directory.)
var packageExtensions = []string{".pkg.tar.xz", ".pkg.tar.gz", ".pkg.tar.bz2"}

// The Package datatype represents all the information that encompasses a Pacman
// package, including the filename of the package.
type Package struct {
	Filename string

	Name            string    // pkgname
	Version         string    // pkgver
	Description     string    // pkgdesc
	Base            string    // pkgbase
	Epoch           uint64    // epoch
	URL             string    // url
	BuildDate       time.Time // builddate
	Packager        string    // packager
	Size            uint64    // size
	Arch            string    // arch: one of any, i686, or x86_64
	License         string    // license
	Backups         []string  // backup
	Replaces        []string  // replaces
	Provides        []string  // provides
	Conflicts       []string  // conflict
	Groups          []string  // group
	Depends         []string  // depend
	OptionalDepends []string  // optdepend
	MakeDepends     []string  // makedepend
	CheckDepends    []string  // checkdepend
	MakeOptions     []string  // makepkgopt
}

// VersionLess compares the version of pi to pkg, and returns true if pi is
// older. It takes the Epoch value in account.
func (pkg *Package) VersionLess(alt *Package) bool {
	// If the Epoch values are different, the package with the higher
	// Epoch value is always more recent.
	if pkg.Epoch != alt.Epoch {
		return pkg.Epoch < alt.Epoch
	}
	return pkg.Version < alt.Version
}

// HasPackageFormat returns true if the filename matches a known
// pacman package format.
func HasPackageFormat(path string) bool {
	for _, ext := range packageExtensions {
		if strings.HasSuffix(path, ext) {
			return true
		}
	}
	return false
}

// ReadPackage reads the package information from a pacman package
// and returns it in the Package datatype.
func ReadPackage(path string) (*Package, error) {
	bs, err := util.ReadFileFromArchive(path, ".PKGINFO")
	if err != nil {
		return nil, err
	}

	r := bytes.NewReader(bs)
	info, err := readPackageInfo(r)
	if err != nil {
		return nil, err
	}

	info.Filename = path
	return info, nil
}

// readPackageInfo reads the package information from a pacman package.
//
// We don't do any specific controlling for you, so you should use
// HasPackageFormat on a path string before using this function on it.
// Even if you don't, nothing bad should happen, but just in case.
func readPackageInfo(r io.Reader) (*Package, error) {
	var info Package
	var err error

	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix("#", line) {
			continue
		}

		kv := strings.Split(line, " = ")
		if len(kv) != 2 {
			continue
		}

		switch kv[0] {
		case "pkgname":
			info.Name = kv[1]
		case "pkgver":
			info.Version = kv[1]
		case "pkgdesc":
			info.Description = kv[1]
		case "pkgbase":
			info.Base = kv[1]
		case "epoch":
			info.Epoch, err = strconv.ParseUint(kv[1], 10, 64)
			if err != nil {
				log.Printf("Warning: cannot parse epoch value '%s'\n", kv[1])
			}
		case "url":
			info.URL = kv[1]
		case "builddate":
			n, err := strconv.ParseInt(kv[1], 10, 64)
			if err != nil {
				log.Printf("Warning: cannot parse build time '%s'\n", kv[1])
			}
			info.BuildDate = time.Unix(n, 0)
		case "packager":
			info.Packager = kv[1]
		case "size":
			info.Size, err = strconv.ParseUint(kv[1], 10, 64)
			if err != nil {
				log.Printf("Warning: cannot parse size value '%s'\n", kv[1])
			}
		case "arch":
			info.Arch = kv[1]
		case "license":
			info.License = kv[1]
		case "depend":
			info.Depends = append(info.Depends, kv[1])
		case "optdepend":
			info.OptionalDepends = append(info.OptionalDepends, kv[1])
		case "makedepend":
			info.MakeDepends = append(info.MakeDepends, kv[1])
		case "checkdepend":
			info.CheckDepends = append(info.CheckDepends, kv[1])
		case "makepkgopt":
			info.MakeOptions = append(info.MakeOptions, kv[1])
		case "backup":
			info.Backups = append(info.Backups, kv[1])
		case "replaces":
			info.Replaces = append(info.Replaces, kv[1])
		case "provides":
			info.Provides = append(info.Provides, kv[1])
		case "conflict":
			info.Conflicts = append(info.Conflicts, kv[1])
		case "group":
			info.Groups = append(info.Groups, kv[1])
		default:
			log.Printf("Warning: unknown field '%s' in .PKGINFO\n", kv[0])
		}
	}
	if err = scanner.Err(); err != nil {
		return nil, err
	}

	return &info, nil
}

// GetAllPackages takes a directory path as an argument, and
// then reads all the package information into a list.
func GetAllPackages(path string) []*Package {
	var pkgs []*Package

	filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			log.Printf("Warning: %s\n", err)
			return nil
		}
		if !info.Mode().IsDir() && HasPackageFormat(path) {
			p, err := ReadPackage(path)
			if err != nil {
				log.Printf("Warning: %s\n", err)
				return nil
			}

			pkgs = append(pkgs, p)
		}

		return nil
	})

	return pkgs
}

// Note that we recurse into subdirectories.
func GetMatchingPackages(path, pkgname string) []*Package {
	var pkgs []*Package

	filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			log.Printf("Warning: %s\n", err)
			return nil
		}
		if !info.Mode().IsDir() && strings.HasPrefix(path, pkgname) && HasPackageFormat(path) {
			p, err := ReadPackage(path)
			if err != nil {
				log.Printf("Warning: %s\n", err)
				return nil
			}

			if p.Name == pkgname {
				pkgs = append(pkgs, p)
			}
		}

		return nil
	})

	return pkgs
}

// Note that we do not recurse into subdirectories!
func GetAllMatchingPackages(path string, pkgnames []string) []*Package {
	var pkgs []*Package

	for _, n := range pkgnames {
		matches, err := filepath.Glob(filepath.Join(path, n+"-*.pkg.tar.*"))
		if err != nil {
			log.Printf("Warning: cannot find package %s.\n", n)
			continue
		}

		for _, fp := range matches {
			p, err := ReadPackage(fp)
			if err != nil {
				log.Printf("Warning: %s\n.", err)
				continue
			}

			if p.Name == n {
				pkgs = append(pkgs, p)
			}
		}
	}

	return pkgs
}

// SplitOldPackages splits the input array into one containing the newest
// packages and another containing the outdated packages.
func SplitOldPackages(pkgs []*Package) (updated []*Package, old []*Package) {
	var m = make(map[string]*Package)

	// Find out which packages are newest and put the others in the old array.
	for _, p := range pkgs {
		if cur, ok := m[p.Name]; ok {
			if cur.VersionLess(p) {
				old = append(old, cur)
			} else {
				old = append(old, p)
				continue
			}
		}
		m[p.Name] = p
	}

	// Add the newest packages to the updated array and return.
	updated = make([]*Package, 0, len(m))
	for _, v := range m {
		updated = append(updated, v)
	}

	return updated, old
}

func SearchAUR(pkgname string) []*Package {
	//https://aur.archlinux.org/rpc.php?type=info&arg=dropbox
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net"
)

type Envelope struct {
	env_type byte
	sequence uint64 // big endian?
	packet   []byte
}

const ENV_DATA byte = 1
const ENV_PING byte = 2
const ENV_RECV byte = 3

const IP4_ICMP byte = 1
const IP4_UDP byte = 17
const IP4_TCP byte = 6

const ENVELOPE_LENGTH int = 5 // 5 byte envelope

func replace_sender_ip(pkt []byte, new_ip net.IP) []byte {
	pkt[0] = new_ip[0]
	pkt[1] = new_ip[1]
	pkt[2] = new_ip[2]
	pkt[3] = new_ip[3]
	return pkt
}

func parse_envelope(raw []byte) Envelope {
	return Envelope{
		env_type: raw[0],
		sequence: uint64((raw[0])<<3) + uint64((raw[1])<<2) + uint64((raw[2])<<1) + uint64(raw[3]),
		packet:   raw[ENVELOPE_LENGTH:],
	}
}

// returns 4 for IPv4, 6 for IPv6
func get_ip_version(pkt []byte) byte {
	// get first 4 bits
	return (pkt[0] & 0xF0) / 0x10
}

func get_ip_src(pkt []byte) net.IP {
	switch get_ip_version(pkt) {
	case 4:
		return net.IP(pkt[12:16])
	default:
		log.Printf("IPv%d packets not supported", get_ip_version(pkt))
	}
	return net.IP{0, 0, 0, 0}
}

func get_ip_dest(pkt []byte) net.IP {
	switch get_ip_version(pkt) {
	case 4:
		return net.IP(pkt[16:20])
	default:
		log.Printf("IPv%d packets not supported", get_ip_version(pkt))
	}
	return net.IP{0, 0, 0, 0}
}

func replace_src_addr(pkt []byte, new_ip net.IP) {
	switch get_ip_version(pkt) {
	case 4:
		copy(pkt[12:], new_ip.To4()[0:4])
	default:
		log.Printf("IPv%d packets not supported", get_ip_version(pkt))
	}
}

// zero out the IP header checksum
func clear_checksum(pkt []byte) {
	switch get_ip_version(pkt) {
	case 4:
		pkt[10] = 0
		pkt[11] = 0
	default:
		log.Printf("IPv%d packets not supported", get_ip_version(pkt))
	}
}

func get_ip_proto(pkt []byte) byte {
	return pkt[9]
}
<commit_msg>IP header checksum function<commit_after>package main

import (
	"log"
	"net"
)

type Envelope struct {
	env_type byte
	sequence uint64 // big endian?
	packet   []byte
}

const ENV_DATA byte = 1
const ENV_PING byte = 2
const ENV_RECV byte = 3

const IP4_ICMP byte = 1
const IP4_UDP byte = 17
const IP4_TCP byte = 6

const ENVELOPE_LENGTH int = 5 // 5 byte envelope

func replace_sender_ip(pkt []byte, new_ip net.IP) []byte {
	pkt[0] = new_ip[0]
	pkt[1] = new_ip[1]
	pkt[2] = new_ip[2]
	pkt[3] = new_ip[3]
	return pkt
}

func parse_envelope(raw []byte) Envelope {
	return Envelope{
		env_type: raw[0],
		// TODO: replace with encoding/binary
		sequence: uint64((raw[0])<<24) + uint64((raw[1])<<16) + uint64((raw[2])<<8) + uint64(raw[3]),
		packet:   raw[ENVELOPE_LENGTH:],
	}
}

// returns 4 for IPv4, 6 for IPv6
func get_ip_version(pkt []byte) byte {
	// get first 4 bits
	return (pkt[0] & 0xF0) / 0x10
}

func get_ip_src(pkt []byte) net.IP {
	switch get_ip_version(pkt) {
	case 4:
		return net.IP(pkt[12:16])
	default:
		log.Printf("IPv%d packets not supported", get_ip_version(pkt))
	}
	return net.IP{0, 0, 0, 0}
}

func get_ip_dest(pkt []byte) net.IP {
	switch get_ip_version(pkt) {
	case 4:
		return net.IP(pkt[16:20])
	default:
		log.Printf("IPv%d packets not supported", get_ip_version(pkt))
	}
	return net.IP{0, 0, 0, 0}
}

func replace_src_addr(pkt []byte, new_ip net.IP) {
	switch get_ip_version(pkt) {
	case 4:
		copy(pkt[12:], new_ip.To4()[0:4])
	default:
		log.Printf("IPv%d packets not supported", get_ip_version(pkt))
	}
}

// zero out the IP header checksum
func clear_checksum(pkt []byte) {
	switch get_ip_version(pkt) {
	case 4:
		pkt[10] = 0
		pkt[11] = 0
	default:
		log.Printf("IPv%d packets not supported", get_ip_version(pkt))
	}
}

func calculateIPHeaderChecksum(pkt []byte) uint16 {
	// loop through the 10 16-bit values
	var sum uint32 = 0
	for i := 0; i < 10; i++ {
		if i == 5 {
			continue
		}
		// sum up each 16-bit value
		sum = sum + (uint32(pkt[i*2]) << 8) + uint32(pkt[i*2+1])
	}

	// carry over first bits beyond lower 16
	var checksum uint16 = uint16(sum) + uint16(sum>>16)
	checksum = ^checksum
	return checksum
}

func get_ip_proto(pkt []byte) byte {
	return pkt[9]
}
<|endoftext|>


<commit_before>//       ___  _____  ____
//      / _ \/  _/ |/_/ /____ ______ _
//     / ___// /_>  </ __/ -_) __/  ' \
//    /_/  /___/_/|_|\__/\__/_/ /_/_/_/
//
//    Copyright 2017 Eliuk Blau
//
//    This Source Code Form is subject to the terms of the Mozilla Public
//    License, v. 2.0. If a copy of the MPL was not distributed with this
//    file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/eliukblau/pixterm/ansimage"
	"github.com/lucasb-eyer/go-colorful"
	"golang.org/x/crypto/ssh/terminal"
)

const (
	pxtVersion = "1.2.4"
	pxtLogo    = `

   ___  _____  ____
  / _ \/  _/ |/_/ /____ ______ _    Made with love by Eliuk Blau
 / ___// /_>  </ __/ -_) __/  ' \   github.com/eliukblau/pixterm
/_/  /___/_/|_|\__/\__/_/ /_/_/_/   v{{VERSION}}

`
)

var (
	flagCredits bool
	flagDither  uint
	flagMatte   string
	flagScale   uint
	flagRows    uint
	flagCols    uint
)

func main() {
	validateFlags()
	runPixterm()
}

func printLogo() {
	fmt.Print(strings.Trim(strings.Replace(pxtLogo, "{{VERSION}}", pxtVersion, 1), "\n"), "\n\n")
}

func printCredits() {
	printLogo()
	fmt.Print("CONTRIBUTORS:\n\n")

	fmt.Print("  > @disq - http://github.com/disq\n")
	fmt.Print("      Original code for image transparency support.\n")
	fmt.Println()

	fmt.Print("  > @timob - http://github.com/timob\n")
	fmt.Print("      Fix for ANSIpixel type: use 8bit color component for output.\n")
	fmt.Println()

	fmt.Print("  > @HongjiangHuang - http://github.com/HongjiangHuang\n")
	fmt.Print("      Original code for image download support.\n")
	fmt.Println()

	fmt.Print("  > @diamondburned - http://github.com/diamondburned\n")
	fmt.Print("      NewFromImage()/NewScaledFromImage() functions for ANSImage API.\n")
	fmt.Println()

	fmt.Print("  > @danirod - http://github.com/danirod\n")
	fmt.Print("  > @Xpktro - http://github.com/Xpktro\n")
	fmt.Print("      Moral support.\n")
	fmt.Println()
}

func throwError(code int, v ...interface{}) {
	printLogo()
	log.New(os.Stderr, "[PIXTERM ERROR] ", log.LstdFlags).Println(v...)
	os.Exit(code)
}

func configureFlags() {
	flag.CommandLine.Usage = func() {
		printLogo()

		_, file := filepath.Split(os.Args[0])
		fmt.Print("USAGE:\n\n")
		fmt.Printf("  %s [options] image/url\n\n", file)

		fmt.Print("  Supported image formats: JPEG, PNG, GIF, BMP, TIFF, WebP.\n")
		fmt.Print("  Supported URL protocols: HTTP, HTTPS.\n\n")

		fmt.Print("OPTIONS:\n\n")
		flag.CommandLine.SetOutput(os.Stdout)
		flag.CommandLine.PrintDefaults()
		flag.CommandLine.SetOutput(ioutil.Discard) // hide flag errors
		fmt.Print("  -help\n\tprints this message :D LOL\n")
		fmt.Println()
	}

	flag.CommandLine.SetOutput(ioutil.Discard) // hide flag errors
	flag.CommandLine.Init(os.Args[0], flag.ExitOnError)

	flag.CommandLine.BoolVar(&flagCredits, "credits", false, "shows some love to contributors <3")
	flag.CommandLine.UintVar(&flagDither, "d", 0, "dithering `mode`:\n   0 - no dithering (default)\n   1 - with blocks\n   2 - with chars")
	flag.CommandLine.StringVar(&flagMatte, "m", "", "matte `color` for transparency or background\n(optional, hex format, default: 000000)")
	flag.CommandLine.UintVar(&flagScale, "s", 0, "scale `method`:\n   0 - resize (default)\n   1 - fill\n   2 - fit")
	flag.CommandLine.UintVar(&flagRows, "tr", 0, "terminal `rows` (optional, >=2; when piping, default: 24)")
	flag.CommandLine.UintVar(&flagCols, "tc", 0, "terminal `columns` (optional, >=2; when piping, default: 80)")

	flag.CommandLine.Parse(os.Args[1:])
}

func validateFlags() {
	if flagCredits {
		printCredits()
		os.Exit(0)
	}

	if flagDither != 0 && flagDither != 1 && flagDither != 2 {
		flag.CommandLine.Usage()
		os.Exit(2)
	}

	if flagScale != 0 && flagScale != 1 && flagScale != 2 {
		flag.CommandLine.Usage()
		os.Exit(2)
	}

	if (flagRows > 0 && flagRows < 2) || (flagCols > 0 && flagCols < 2) {
		flag.CommandLine.Usage()
		os.Exit(2)
	}

	// this is image filename
	if flag.CommandLine.Arg(0) == "" {
		flag.CommandLine.Usage()
		os.Exit(2)
	}
}

func isTerminal() bool {
	return terminal.IsTerminal(int(os.Stdout.Fd()))
}

func getTerminalSize() (width, height int, err error) {
	if isTerminal() {
		return terminal.GetSize(int(os.Stdout.Fd()))
	}
	// fallback when piping to a file!
	return 80, 24, nil // VT100 terminal size
}

func runPixterm() {
	var (
		pix *ansimage.ANSImage
		err error
	)

	// get terminal size
	tx, ty, err := getTerminalSize()
	if err != nil {
		throwError(1, err)
	}

	// use custom terminal size (if applies)
	if ty--; flagRows != 0 { // no custom rows? subtract 1 for prompt spacing
		ty = int(flagRows) + 1 // weird, but in this case is necessary to add 1 :O
	}
	if flagCols != 0 {
		tx = int(flagCols)
	}

	// get scale mode from flag
	sm := ansimage.ScaleMode(flagScale)

	// get dithering mode from flag
	dm := ansimage.DitheringMode(flagDither)

	// set image scale factor for ANSIPixel grid
	sfy, sfx := ansimage.BlockSizeY, ansimage.BlockSizeX // 8x4 --> with dithering
	if ansimage.DitheringMode(flagDither) == ansimage.NoDithering {
		sfy, sfx = 2, 1 // 2x1 --> without dithering
	}

	// get matte color
	if flagMatte == "" {
		flagMatte = "000000" // black background
	}
	mc, err := colorful.Hex("#" + flagMatte) // RGB color from Hex format
	if err != nil {
		throwError(2, fmt.Sprintf("matte color : %s is not a hex-color", flagMatte))
	}

	// create new ANSImage from file
	file := flag.CommandLine.Arg(0)
	if matched, _ := regexp.MatchString(`^https?://`, file); matched {
		pix, err = ansimage.NewScaledFromURL(file, sfy*ty, sfx*tx, mc, sm, dm)
	} else {
		pix, err = ansimage.NewScaledFromFile(file, sfy*ty, sfx*tx, mc, sm, dm)
	}
	if err != nil {
		throwError(1, err)
	}

	// draw ANSImage to terminal
	if isTerminal() {
		ansimage.ClearTerminal()
	}
	pix.SetMaxProcs(runtime.NumCPU()) // maximum number of parallel goroutines!
	pix.Draw()
	if isTerminal() {
		fmt.Println()
	}
}

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU()) // use paralelism for goroutines!
	configureFlags()
}
<commit_msg>New options and Credits to contributors :)<commit_after>//       ___  _____  ____
//      / _ \/  _/ |/_/ /____ ______ _
//     / ___// /_>  </ __/ -_) __/  ' \
//    /_/  /___/_/|_|\__/\__/_/ /_/_/_/
//
//    Copyright 2017 Eliuk Blau
//
//    This Source Code Form is subject to the terms of the Mozilla Public
//    License, v. 2.0. If a copy of the MPL was not distributed with this
//    file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/eliukblau/pixterm/ansimage"
	"github.com/lucasb-eyer/go-colorful"
	"golang.org/x/crypto/ssh/terminal"
)

const (
	pxtVersion = "1.2.4"
	pxtLogo    = `

   ___  _____  ____
  / _ \/  _/ |/_/ /____ ______ _    Made with love by Eliuk Blau
 / ___// /_>  </ __/ -_) __/  ' \   github.com/eliukblau/pixterm
/_/  /___/_/|_|\__/\__/_/ /_/_/_/   v{{VERSION}}

`
)

var (
	flagCredits bool
	flagDither  uint
	flagGo      bool
	flagMatte   string
	flagNoBg    bool
	flagScale   uint
	flagRows    uint
	flagCols    uint
)

func main() {
	validateFlags()
	runPixterm()
}

func printLogo() {
	fmt.Print(strings.Trim(strings.Replace(pxtLogo, "{{VERSION}}", pxtVersion, 1), "\n"), "\n\n")
}

func printCredits() {
	printLogo()
	fmt.Print("CONTRIBUTORS:\n\n")

	fmt.Print("  > @disq - http://github.com/disq\n")
	fmt.Print("      + Original code for image transparency support.\n")
	fmt.Println()

	fmt.Print("  > @timob - http://github.com/timob\n")
	fmt.Print("      + Fix for ANSIpixel type: use 8bit color component for output.\n")
	fmt.Println()

	fmt.Print("  > @HongjiangHuang - http://github.com/HongjiangHuang\n")
	fmt.Print("      + Original code for image download support.\n")
	fmt.Println()

	fmt.Print("  > @brutestack - http://github.com/brutestack\n")
	fmt.Print("      + Color support for Windows (Command Prompt & PowerShell).\n")
	fmt.Print("      + Option to disable background color in dithering mode.\n")
	fmt.Print("      + Option to output Go code to 'fmt.Printf()' the image.\n")
	fmt.Println()

	fmt.Print("  > @diamondburned - http://github.com/diamondburned\n")
	fmt.Print("      + NewFromImage()/NewScaledFromImage() functions for ANSImage API.\n")
	fmt.Println()

	fmt.Print("  > @danirod - http://github.com/danirod\n")
	fmt.Print("  > @Xpktro - http://github.com/Xpktro\n")
	fmt.Print("      + Moral support.\n")
	fmt.Println()
}

func throwError(code int, v ...interface{}) {
	printLogo()
	log.New(os.Stderr, "[PIXTERM ERROR] ", log.LstdFlags).Println(v...)
	os.Exit(code)
}

func configureFlags() {
	flag.CommandLine.Usage = func() {
		printLogo()

		_, file := filepath.Split(os.Args[0])
		fmt.Print("USAGE:\n\n")
		fmt.Printf("  %s [options] image/url\n\n", file)

		fmt.Print("  Supported image formats: JPEG, PNG, GIF, BMP, TIFF, WebP.\n")
		fmt.Print("  Supported URL protocols: HTTP, HTTPS.\n\n")

		fmt.Print("OPTIONS:\n\n")
		flag.CommandLine.SetOutput(os.Stdout)
		flag.CommandLine.PrintDefaults()
		flag.CommandLine.SetOutput(ioutil.Discard) // hide flag errors
		fmt.Print("  -help\n\tprints this message :D LOL\n")
		fmt.Println()
	}

	flag.CommandLine.SetOutput(ioutil.Discard) // hide flag errors
	flag.CommandLine.Init(os.Args[0], flag.ExitOnError)

	flag.CommandLine.BoolVar(&flagCredits, "credits", false, "shows some love to contributors <3")
	flag.CommandLine.UintVar(&flagDither, "d", 0, "dithering `mode`:\n   0 - no dithering (default)\n   1 - with blocks\n   2 - with chars")
	flag.CommandLine.BoolVar(&flagGo, "go", false, "output Go code to 'fmt.Printf()' the image")
	flag.CommandLine.StringVar(&flagMatte, "m", "", "matte `color` for transparency or background\n(optional, hex format, default: 000000)")
	flag.CommandLine.BoolVar(&flagNoBg, "nobg", false, "disable background color\n(optional, only using dithering, ignores matte color)")
	flag.CommandLine.UintVar(&flagScale, "s", 0, "scale `method`:\n   0 - resize (default)\n   1 - fill\n   2 - fit")
	flag.CommandLine.UintVar(&flagRows, "tr", 0, "terminal `rows` (optional, >=2; when piping, default: 24)")
	flag.CommandLine.UintVar(&flagCols, "tc", 0, "terminal `columns` (optional, >=2; when piping, default: 80)")

	flag.CommandLine.Parse(os.Args[1:])
}

func validateFlags() {
	if flagCredits {
		printCredits()
		os.Exit(0)
	}

	if flagDither != 0 && flagDither != 1 && flagDither != 2 {
		flag.CommandLine.Usage()
		os.Exit(2)
	}

	if flagScale != 0 && flagScale != 1 && flagScale != 2 {
		flag.CommandLine.Usage()
		os.Exit(2)
	}

	if (flagRows > 0 && flagRows < 2) || (flagCols > 0 && flagCols < 2) {
		flag.CommandLine.Usage()
		os.Exit(2)
	}

	// this is image filename
	if flag.CommandLine.Arg(0) == "" {
		flag.CommandLine.Usage()
		os.Exit(2)
	}
}

func isTerminal() bool {
	return terminal.IsTerminal(int(os.Stdout.Fd()))
}

func getTerminalSize() (width, height int, err error) {
	if isTerminal() {
		return terminal.GetSize(int(os.Stdout.Fd()))
	}
	// fallback when piping to a file!
	return 80, 24, nil // VT100 terminal size
}

func runPixterm() {
	var (
		pix *ansimage.ANSImage
		err error
	)

	// get terminal size
	tx, ty, err := getTerminalSize()
	if err != nil {
		throwError(1, err)
	}

	// use custom terminal size (if applies)
	if ty--; flagRows != 0 { // no custom rows? subtract 1 for prompt spacing
		ty = int(flagRows) + 1 // weird, but in this case is necessary to add 1 :O
	}
	if flagCols != 0 {
		tx = int(flagCols)
	}

	// get scale mode from flag
	sm := ansimage.ScaleMode(flagScale)

	// get dithering mode from flag
	dm := ansimage.DitheringMode(flagDither)

	// set image scale factor for ANSIPixel grid
	sfy, sfx := ansimage.BlockSizeY, ansimage.BlockSizeX // 8x4 --> with dithering
	if ansimage.DitheringMode(flagDither) == ansimage.NoDithering {
		sfy, sfx = 2, 1 // 2x1 --> without dithering
	}

	// get matte color
	if flagMatte == "" {
		flagMatte = "000000" // black background
	}
	mc, err := colorful.Hex("#" + flagMatte) // RGB color from Hex format
	if err != nil {
		throwError(2, fmt.Sprintf("matte color : %s is not a hex-color", flagMatte))
	}

	// create new ANSImage from file
	file := flag.CommandLine.Arg(0)
	if matched, _ := regexp.MatchString(`^https?://`, file); matched {
		pix, err = ansimage.NewScaledFromURL(file, sfy*ty, sfx*tx, mc, sm, dm)
	} else {
		pix, err = ansimage.NewScaledFromFile(file, sfy*ty, sfx*tx, mc, sm, dm)
	}
	if err != nil {
		throwError(1, err)
	}

	// draw ANSImage to terminal
	if isTerminal() {
		ansimage.ClearTerminal()
	}
	pix.SetMaxProcs(runtime.NumCPU()) // maximum number of parallel goroutines!
	pix.Draw()
	if isTerminal() {
		fmt.Println()
	}
}

func init() {
	runtime.GOMAXPROCS(runtime.NumCPU()) // use paralelism for goroutines!
	configureFlags()
}
<|endoftext|>


<commit_before>package env

import (
	"fmt"
	"strconv"
	"strings"

	hargs "github.com/hashicorp/nomad/helper/args"
	"github.com/hashicorp/nomad/nomad/structs"
)

// A set of environment variables that are exported by each driver.
const (
	// The path to the alloc directory that is shared across tasks within a task
	// group.
	AllocDir = "NOMAD_ALLOC_DIR"

	// The path to the tasks local directory where it can store data that is
	// persisted to the alloc is removed.
	TaskLocalDir = "NOMAD_TASK_DIR"

	// The tasks memory limit in MBs.
	MemLimit = "NOMAD_MEMORY_LIMIT"

	// The tasks limit in MHz.
	CpuLimit = "NOMAD_CPU_LIMIT"

	// The IP address for the task.
	TaskIP = "NOMAD_IP"

	// Prefix for passing both dynamic and static port allocations to
	// tasks.
	// E.g. $NOMAD_PORT_1 or $NOMAD_PORT_http
	PortPrefix = "NOMAD_PORT_"

	// Prefix for passing task meta data.
	MetaPrefix = "NOMAD_META_"
)

// The node values that can be interpreted.
const (
	nodeIdKey    = "node.id"
	nodeDcKey    = "node.datacenter"
	nodeNameKey  = "node.name"
	nodeClassKey = "node.class"

	// Prefixes used for lookups.
	nodeAttributePrefix = "attr."
	nodeMetaPrefix      = "meta."
)

// TaskEnvironment is used to expose information to a task via environment
// variables and provide interpolation of Nomad variables.
type TaskEnvironment struct {
	env      map[string]string
	meta     map[string]string
	ports    map[string]int
	allocDir string
	taskDir  string
	cpuLimit int
	memLimit int
	ip       string
	node     *structs.Node

	// taskEnv is the variables that will be set in the tasks environment
	taskEnv map[string]string

	// nodeValues is the values that are allowed for interprolation from the
	// node.
	nodeValues map[string]string
}

func NewTaskEnvironment(node *structs.Node) *TaskEnvironment {
	return &TaskEnvironment{node: node}
}

// ParseAndReplace takes the user supplied args replaces any instance of an
// environment variable or nomad variable in the args with the actual value.
func (t *TaskEnvironment) ParseAndReplace(args []string) []string {
	replaced := make([]string, len(args))
	for i, arg := range args {
		replaced[i] = hargs.ReplaceEnv(arg, t.taskEnv, t.nodeValues)
	}

	return replaced
}

// ReplaceEnv takes an arg and replaces all occurences of environment variables
// and nomad variables.  If the variable is found in the passed map it is
// replaced, otherwise the original string is returned.
func (t *TaskEnvironment) ReplaceEnv(arg string) string {
	return hargs.ReplaceEnv(arg, t.taskEnv, t.nodeValues)
}

// Build must be called after all the tasks environment values have been set.
func (t *TaskEnvironment) Build() *TaskEnvironment {
	t.nodeValues = make(map[string]string)
	t.taskEnv = make(map[string]string)

	// Build the task metadata
	for k, v := range t.meta {
		t.taskEnv[fmt.Sprintf("%s%s", MetaPrefix, strings.ToUpper(k))] = v
	}

	// Build the ports
	for label, port := range t.ports {
		t.taskEnv[fmt.Sprintf("%s%s", PortPrefix, label)] = strconv.Itoa(port)
	}

	// Build the directories
	if t.allocDir != "" {
		t.taskEnv[AllocDir] = t.allocDir
	}
	if t.taskDir != "" {
		t.taskEnv[TaskLocalDir] = t.taskDir
	}

	// Build the resource limits
	if t.memLimit != 0 {
		t.taskEnv[MemLimit] = strconv.Itoa(t.memLimit)
	}
	if t.cpuLimit != 0 {
		t.taskEnv[CpuLimit] = strconv.Itoa(t.cpuLimit)
	}

	// Build the IP
	if t.ip != "" {
		t.taskEnv[TaskIP] = t.ip
	}

	// Build the node
	if t.node != nil {
		// Set up the node values.
		t.nodeValues[nodeIdKey] = t.node.ID
		t.nodeValues[nodeDcKey] = t.node.Datacenter
		t.nodeValues[nodeNameKey] = t.node.Name
		t.nodeValues[nodeClassKey] = t.node.NodeClass

		// Set up the attributes.
		for k, v := range t.node.Attributes {
			t.nodeValues[fmt.Sprintf("%s%s", nodeAttributePrefix, k)] = v
		}

		// Set up the meta.
		for k, v := range t.node.Meta {
			t.nodeValues[fmt.Sprintf("%s%s", nodeMetaPrefix, k)] = v
		}
	}

	// Interpret the environment variables
	interpreted := make(map[string]string, len(t.env))
	for k, v := range t.env {
		interpreted[k] = hargs.ReplaceEnv(v, t.nodeValues, t.taskEnv)
	}

	for k, v := range interpreted {
		t.taskEnv[k] = v
	}

	return t
}

// EnvList returns a list of strings with NAME=value pairs.
func (t *TaskEnvironment) EnvList() []string {
	env := []string{}
	for k, v := range t.taskEnv {
		env = append(env, fmt.Sprintf("%s=%s", k, v))
	}

	return env
}

// EnvMap returns a copy of the tasks environment variables.
func (t *TaskEnvironment) EnvMap() map[string]string {
	m := make(map[string]string, len(t.taskEnv))
	for k, v := range t.taskEnv {
		m[k] = v
	}

	return m
}

// Builder methods to build the TaskEnvironment
func (t *TaskEnvironment) SetAllocDir(dir string) *TaskEnvironment {
	t.allocDir = dir
	return t
}

func (t *TaskEnvironment) ClearAllocDir() *TaskEnvironment {
	t.allocDir = ""
	return t
}

func (t *TaskEnvironment) SetTaskLocalDir(dir string) *TaskEnvironment {
	t.taskDir = dir
	return t
}

func (t *TaskEnvironment) ClearTaskLocalDir() *TaskEnvironment {
	t.taskDir = ""
	return t
}

func (t *TaskEnvironment) SetMemLimit(limit int) *TaskEnvironment {
	t.memLimit = limit
	return t
}

func (t *TaskEnvironment) ClearMemLimit() *TaskEnvironment {
	t.memLimit = 0
	return t
}

func (t *TaskEnvironment) SetCpuLimit(limit int) *TaskEnvironment {
	t.cpuLimit = limit
	return t
}

func (t *TaskEnvironment) ClearCpuLimit() *TaskEnvironment {
	t.cpuLimit = 0
	return t
}

func (t *TaskEnvironment) SetTaskIp(ip string) *TaskEnvironment {
	t.ip = ip
	return t
}

func (t *TaskEnvironment) ClearTaskIp() *TaskEnvironment {
	t.ip = ""
	return t
}

// Takes a map of port labels to their port value.
func (t *TaskEnvironment) SetPorts(ports map[string]int) *TaskEnvironment {
	t.ports = ports
	return t
}

func (t *TaskEnvironment) ClearPorts() *TaskEnvironment {
	t.ports = nil
	return t
}

// Takes a map of meta values to be passed to the task. The keys are capatilized
// when the environent variable is set.
func (t *TaskEnvironment) SetMeta(m map[string]string) *TaskEnvironment {
	t.meta = m
	return t
}

func (t *TaskEnvironment) ClearMeta() *TaskEnvironment {
	t.meta = nil
	return t
}

func (t *TaskEnvironment) SetEnvvars(m map[string]string) *TaskEnvironment {
	t.env = m
	return t
}

// Appends the given environment variables.
func (t *TaskEnvironment) AppendEnvvars(m map[string]string) *TaskEnvironment {
	if t.env == nil {
		t.env = make(map[string]string, len(m))
	}

	for k, v := range m {
		t.env[k] = v
	}
	return t
}

func (t *TaskEnvironment) ClearEnvvars() *TaskEnvironment {
	t.env = nil
	return t
}
<commit_msg>Update the interpretation of node.id/name on the client<commit_after>package env

import (
	"fmt"
	"strconv"
	"strings"

	hargs "github.com/hashicorp/nomad/helper/args"
	"github.com/hashicorp/nomad/nomad/structs"
)

// A set of environment variables that are exported by each driver.
const (
	// The path to the alloc directory that is shared across tasks within a task
	// group.
	AllocDir = "NOMAD_ALLOC_DIR"

	// The path to the tasks local directory where it can store data that is
	// persisted to the alloc is removed.
	TaskLocalDir = "NOMAD_TASK_DIR"

	// The tasks memory limit in MBs.
	MemLimit = "NOMAD_MEMORY_LIMIT"

	// The tasks limit in MHz.
	CpuLimit = "NOMAD_CPU_LIMIT"

	// The IP address for the task.
	TaskIP = "NOMAD_IP"

	// Prefix for passing both dynamic and static port allocations to
	// tasks.
	// E.g. $NOMAD_PORT_1 or $NOMAD_PORT_http
	PortPrefix = "NOMAD_PORT_"

	// Prefix for passing task meta data.
	MetaPrefix = "NOMAD_META_"
)

// The node values that can be interpreted.
const (
	nodeIdKey    = "node.unique.id"
	nodeDcKey    = "node.datacenter"
	nodeNameKey  = "node.unique.name"
	nodeClassKey = "node.class"

	// Prefixes used for lookups.
	nodeAttributePrefix = "attr."
	nodeMetaPrefix      = "meta."
)

// TaskEnvironment is used to expose information to a task via environment
// variables and provide interpolation of Nomad variables.
type TaskEnvironment struct {
	env      map[string]string
	meta     map[string]string
	ports    map[string]int
	allocDir string
	taskDir  string
	cpuLimit int
	memLimit int
	ip       string
	node     *structs.Node

	// taskEnv is the variables that will be set in the tasks environment
	taskEnv map[string]string

	// nodeValues is the values that are allowed for interprolation from the
	// node.
	nodeValues map[string]string
}

func NewTaskEnvironment(node *structs.Node) *TaskEnvironment {
	return &TaskEnvironment{node: node}
}

// ParseAndReplace takes the user supplied args replaces any instance of an
// environment variable or nomad variable in the args with the actual value.
func (t *TaskEnvironment) ParseAndReplace(args []string) []string {
	replaced := make([]string, len(args))
	for i, arg := range args {
		replaced[i] = hargs.ReplaceEnv(arg, t.taskEnv, t.nodeValues)
	}

	return replaced
}

// ReplaceEnv takes an arg and replaces all occurences of environment variables
// and nomad variables.  If the variable is found in the passed map it is
// replaced, otherwise the original string is returned.
func (t *TaskEnvironment) ReplaceEnv(arg string) string {
	return hargs.ReplaceEnv(arg, t.taskEnv, t.nodeValues)
}

// Build must be called after all the tasks environment values have been set.
func (t *TaskEnvironment) Build() *TaskEnvironment {
	t.nodeValues = make(map[string]string)
	t.taskEnv = make(map[string]string)

	// Build the task metadata
	for k, v := range t.meta {
		t.taskEnv[fmt.Sprintf("%s%s", MetaPrefix, strings.ToUpper(k))] = v
	}

	// Build the ports
	for label, port := range t.ports {
		t.taskEnv[fmt.Sprintf("%s%s", PortPrefix, label)] = strconv.Itoa(port)
	}

	// Build the directories
	if t.allocDir != "" {
		t.taskEnv[AllocDir] = t.allocDir
	}
	if t.taskDir != "" {
		t.taskEnv[TaskLocalDir] = t.taskDir
	}

	// Build the resource limits
	if t.memLimit != 0 {
		t.taskEnv[MemLimit] = strconv.Itoa(t.memLimit)
	}
	if t.cpuLimit != 0 {
		t.taskEnv[CpuLimit] = strconv.Itoa(t.cpuLimit)
	}

	// Build the IP
	if t.ip != "" {
		t.taskEnv[TaskIP] = t.ip
	}

	// Build the node
	if t.node != nil {
		// Set up the node values.
		t.nodeValues[nodeIdKey] = t.node.ID
		t.nodeValues[nodeDcKey] = t.node.Datacenter
		t.nodeValues[nodeNameKey] = t.node.Name
		t.nodeValues[nodeClassKey] = t.node.NodeClass

		// Set up the attributes.
		for k, v := range t.node.Attributes {
			t.nodeValues[fmt.Sprintf("%s%s", nodeAttributePrefix, k)] = v
		}

		// Set up the meta.
		for k, v := range t.node.Meta {
			t.nodeValues[fmt.Sprintf("%s%s", nodeMetaPrefix, k)] = v
		}
	}

	// Interpret the environment variables
	interpreted := make(map[string]string, len(t.env))
	for k, v := range t.env {
		interpreted[k] = hargs.ReplaceEnv(v, t.nodeValues, t.taskEnv)
	}

	for k, v := range interpreted {
		t.taskEnv[k] = v
	}

	return t
}

// EnvList returns a list of strings with NAME=value pairs.
func (t *TaskEnvironment) EnvList() []string {
	env := []string{}
	for k, v := range t.taskEnv {
		env = append(env, fmt.Sprintf("%s=%s", k, v))
	}

	return env
}

// EnvMap returns a copy of the tasks environment variables.
func (t *TaskEnvironment) EnvMap() map[string]string {
	m := make(map[string]string, len(t.taskEnv))
	for k, v := range t.taskEnv {
		m[k] = v
	}

	return m
}

// Builder methods to build the TaskEnvironment
func (t *TaskEnvironment) SetAllocDir(dir string) *TaskEnvironment {
	t.allocDir = dir
	return t
}

func (t *TaskEnvironment) ClearAllocDir() *TaskEnvironment {
	t.allocDir = ""
	return t
}

func (t *TaskEnvironment) SetTaskLocalDir(dir string) *TaskEnvironment {
	t.taskDir = dir
	return t
}

func (t *TaskEnvironment) ClearTaskLocalDir() *TaskEnvironment {
	t.taskDir = ""
	return t
}

func (t *TaskEnvironment) SetMemLimit(limit int) *TaskEnvironment {
	t.memLimit = limit
	return t
}

func (t *TaskEnvironment) ClearMemLimit() *TaskEnvironment {
	t.memLimit = 0
	return t
}

func (t *TaskEnvironment) SetCpuLimit(limit int) *TaskEnvironment {
	t.cpuLimit = limit
	return t
}

func (t *TaskEnvironment) ClearCpuLimit() *TaskEnvironment {
	t.cpuLimit = 0
	return t
}

func (t *TaskEnvironment) SetTaskIp(ip string) *TaskEnvironment {
	t.ip = ip
	return t
}

func (t *TaskEnvironment) ClearTaskIp() *TaskEnvironment {
	t.ip = ""
	return t
}

// Takes a map of port labels to their port value.
func (t *TaskEnvironment) SetPorts(ports map[string]int) *TaskEnvironment {
	t.ports = ports
	return t
}

func (t *TaskEnvironment) ClearPorts() *TaskEnvironment {
	t.ports = nil
	return t
}

// Takes a map of meta values to be passed to the task. The keys are capatilized
// when the environent variable is set.
func (t *TaskEnvironment) SetMeta(m map[string]string) *TaskEnvironment {
	t.meta = m
	return t
}

func (t *TaskEnvironment) ClearMeta() *TaskEnvironment {
	t.meta = nil
	return t
}

func (t *TaskEnvironment) SetEnvvars(m map[string]string) *TaskEnvironment {
	t.env = m
	return t
}

// Appends the given environment variables.
func (t *TaskEnvironment) AppendEnvvars(m map[string]string) *TaskEnvironment {
	if t.env == nil {
		t.env = make(map[string]string, len(m))
	}

	for k, v := range m {
		t.env[k] = v
	}
	return t
}

func (t *TaskEnvironment) ClearEnvvars() *TaskEnvironment {
	t.env = nil
	return t
}
<|endoftext|>


<commit_before>package server

import (
	"anonymous-messaging/config"
	"anonymous-messaging/helpers"
	"anonymous-messaging/networker"
	"anonymous-messaging/node"
	"anonymous-messaging/sphinx"

	"github.com/protobuf/proto"

	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"os"
)

const (
	assigneFlag = "\xa2"
	commFlag    = "\xc6"
	tokenFlag   = "xa9"
	pullFlag    = "\xff"
)

type ProviderIt interface {
	networker.NetworkServer
	networker.NetworkClient
	Start() error
	GetConfig() config.MixConfig
}

type ProviderServer struct {
	id   string
	host string
	port string
	*node.Mix
	listener *net.TCPListener

	assignedClients map[string]ClientRecord
	config          config.MixConfig
}

type ClientRecord struct {
	id     string
	host   string
	port   string
	pubKey []byte
	token  []byte
}

// Start function creates the loggers for capturing the info and error logs
// and starts the listening server. Function returns an error
// signaling whether any operation was unsuccessful
func (p *ProviderServer) Start() error {
	p.run()

	return nil
}

func (p *ProviderServer) GetConfig() config.MixConfig {
	return p.config
}

// Function opens the listener to start listening on provider's host and port
func (p *ProviderServer) run() {

	defer p.listener.Close()
	finish := make(chan bool)

	go func() {
		logLocal.Infof("Listening on %s", p.host+":"+p.port)
		p.listenForIncomingConnections()
	}()

	<-finish
}

// Function processes the received sphinx packet, performs the
// unwrapping operation and checks whether the packet should be
// forwarded or stored. If the processing was unsuccessful and error is returned.
func (p *ProviderServer) receivedPacket(packet []byte) error {
	logLocal.Info("Received new sphinx packet")

	c := make(chan []byte)
	cAdr := make(chan sphinx.Hop)
	cFlag := make(chan string)
	errCh := make(chan error)

	go p.ProcessPacket(packet, c, cAdr, cFlag, errCh)
	dePacket := <-c
	nextHop := <-cAdr
	flag := <-cFlag
	err := <-errCh

	if err != nil {
		return err
	}

	switch flag {
	case "\xF1":
		err = p.forwardPacket(dePacket, nextHop.Address)
		if err != nil {
			return err
		}
	case "\xF0":
		err = p.storeMessage(dePacket, nextHop.Id, "TMP_MESSAGE_ID")
		if err != nil {
			return err
		}
	default:
		logLocal.Info("Sphinx packet flag not recognised")
	}

	return nil
}

func (p *ProviderServer) forwardPacket(sphinxPacket []byte, address string) error {
	packetBytes, err := config.WrapWithFlag(commFlag, sphinxPacket)
	if err != nil {
		return err
	}

	err = p.send(packetBytes, address)
	if err != nil {
		return err
	}
	logLocal.Info("Forwarded sphinx packet")
	return nil
}

// Function opens a connection with selected network address
// and send the passed packet. If connection failed or
// the packet could not be send, an error is returned
func (p *ProviderServer) send(packet []byte, address string) error {

	conn, err := net.Dial("tcp", address)
	if err != nil {
		return err
	}
	defer conn.Close()

	conn.Write(packet)
	return nil
}

// Function responsible for running the listening process of the server;
// The providers listener accepts incoming connections and
// passes the incoming packets to the packet handler.
// If the connection could not be accepted an error
// is logged into the log files, but the function is not stopped
func (p *ProviderServer) listenForIncomingConnections() {
	for {
		conn, err := p.listener.Accept()

		if err != nil {
			logLocal.WithError(err).Error(err)
		} else {
			logLocal.Infof("Received new connection from %s", conn.RemoteAddr())
			errs := make(chan error, 1)
			go p.handleConnection(conn, errs)
			err = <-errs
			if err != nil {
				logLocal.WithError(err).Error(err)
			}
		}
	}
}

// HandleConnection handles the received packets; it checks the flag of the
// packet and schedules a corresponding process function and returns an error.
func (p *ProviderServer) handleConnection(conn net.Conn, errs chan<- error) {

	buff := make([]byte, 1024)
	reqLen, err := conn.Read(buff)
	defer conn.Close()

	if err != nil {
		errs <- err
	}

	var packet config.GeneralPacket
	err = proto.Unmarshal(buff[:reqLen], &packet)
	if err != nil {
		errs <- err
	}

	switch packet.Flag {
	case assigneFlag:
		err = p.handleAssignRequest(packet.Data)
		if err != nil {
			errs <- err
		}
	case commFlag:
		err = p.receivedPacket(packet.Data)
		if err != nil {
			errs <- err
		}
	case pullFlag:
		err = p.handlePullRequest(packet.Data)
		if err != nil {
			errs <- err
		}
	default:
		logLocal.Info(packet.Flag)
		logLocal.Info("Packet flag not recognised. Packet dropped")
		errs <- nil
	}
	errs <- nil
}

// RegisterNewClient generates a fresh authentication token and saves it together with client's public configuration data
// in the list of all registered clients. After the client is registered the function creates an inbox directory
// for the client's inbox, in which clients messages will be stored.
func (p *ProviderServer) registerNewClient(clientBytes []byte) ([]byte, string, error) {
	var clientConf config.ClientConfig
	err := proto.Unmarshal(clientBytes, &clientConf)
	if err != nil {
		return nil, "", err
	}

	token := helpers.SHA256([]byte("TMP_Token" + clientConf.Id))
	record := ClientRecord{id: clientConf.Id, host: clientConf.Host, port: clientConf.Port, pubKey: clientConf.PubKey, token: token}
	p.assignedClients[clientConf.Id] = record
	address := clientConf.Host + ":" + clientConf.Port

	path := fmt.Sprintf("./inboxes/%s", clientConf.Id)
	exists, err := helpers.DirExists(path)
	if err != nil {
		return nil, "", err
	}
	if exists == false {
		if err := os.MkdirAll(path, 0775); err != nil {
			return nil, "", err
		}
	}

	return token, address, nil
}

// Function is responsible for handling the registration request from the client.
// it registers the client in the list of all registered clients and send
// an authentication token back to the client.
func (p *ProviderServer) handleAssignRequest(packet []byte) error {
	logLocal.Info("Received assign request from the client")

	token, adr, err := p.registerNewClient(packet)
	if err != nil {
		return err
	}

	tokenBytes, err := config.WrapWithFlag(tokenFlag, token)
	if err != nil {
		return err
	}
	err = p.send(tokenBytes, adr)
	if err != nil {
		return err
	}
	return nil
}

// Function is responsible for handling the pull request received from the client.
// It first authenticates the client, by checking if the received token is valid.
// If yes, the function triggers the function for checking client's inbox
// and sending buffered messages. Otherwise, an error is returned.
func (p *ProviderServer) handlePullRequest(rqsBytes []byte) error {
	var request config.PullRequest
	err := proto.Unmarshal(rqsBytes, &request)
	if err != nil {
		return err
	}

	logLocal.Infof("Processing pull request: %s %s", request.ClientId, string(request.Token))

	if p.authenticateUser(request.ClientId, request.Token) == true {
		signal, err := p.fetchMessages(request.ClientId)
		if err != nil {
			return err
		}
		switch signal {
		case "NI":
			logLocal.Info("Inbox does not exist. Sending signal to client.")
		case "EI":
			logLocal.Info("Inbox is empty. Sending info to the client.")
		case "SI":
			logLocal.Info("All messages from the inbox succesfuly sent to the client.")
		}
	} else {
		logLocal.Warning("Authentication went wrong")
		return errors.New("authentication went wrong")
	}
	return nil
}

// AuthenticateUser compares the authentication token received from the client with
// the one stored by the provider. If tokens are the same, it returns true
// and false otherwise.
func (p *ProviderServer) authenticateUser(clientId string, clientToken []byte) bool {

	if bytes.Compare(p.assignedClients[clientId].token, clientToken) == 0 {
		return true
	}
	logLocal.Warningf("Non matching token: %s, %s", p.assignedClients[clientId].token, clientToken)
	return false
}

// FetchMessages fetches messages from the requested inbox.
// FetchMessages checks whether an inbox exists and if it contains
// stored messages. If inbox contains any stored messages, all of them
// are send to the client one by one. FetchMessages returns a code
// signaling whether (NI) inbox does not exist, (EI) inbox is empty,
// (SI) messages were send to the client; and an error.
func (p *ProviderServer) fetchMessages(clientId string) (string, error) {

	path := fmt.Sprintf("./inboxes/%s", clientId)
	exist, err := helpers.DirExists(path)
	if err != nil {
		return "", err
	}
	if exist == false {
		return "NI", nil
	}
	files, err := ioutil.ReadDir(path)
	if err != nil {
		return "", err
	}
	if len(files) == 0 {
		return "EI", nil
	}

	for _, f := range files {
		dat, err := ioutil.ReadFile(path + "/" + f.Name())
		if err != nil {
			return "", err
		}

		address := p.assignedClients[clientId].host + ":" + p.assignedClients[clientId].port
		logLocal.Infof("Found stored message for address %s", address)
		msgBytes, err := config.WrapWithFlag(commFlag, dat)
		if err != nil {
			return "", err
		}
		err = p.send(msgBytes, address)
		if err != nil {
			return "", err
		}
	}
	return "SI", nil
}

// StoreMessage saves the given message in the inbox defined by the given id.
// If the inbox address does not exist or writing into the inbox was unsuccessful
// the function returns an error
func (p *ProviderServer) storeMessage(message []byte, inboxId string, messageId string) error {
	path := fmt.Sprintf("./inboxes/%s", inboxId)
	fileName := path + "/" + messageId + ".txt"

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

	_, err = file.Write(message)
	if err != nil {
		return err
	}

	logLocal.Infof("Stored message for %s", inboxId)
	return nil
}

// NewProviderServer constructs a new provider object.
// NewProviderServer returns a new provider object and an error.
func NewProviderServer(id string, host string, port string, pubKey []byte, prvKey []byte, pkiPath string) (*ProviderServer, error) {
	node := node.NewMix(pubKey, prvKey)
	providerServer := ProviderServer{host: host, port: port, Mix: node, listener: nil}
	providerServer.config = config.MixConfig{Id: providerServer.id, Host: providerServer.host, Port: providerServer.port, PubKey: providerServer.GetPublicKey()}
	providerServer.assignedClients = make(map[string]ClientRecord)

	configBytes, err := proto.Marshal(&providerServer.config)
	if err != nil {
		return nil, err
	}
	err = helpers.AddToDatabase(pkiPath, "Pki", providerServer.id, "Provider", configBytes)
	if err != nil {
		return nil, err
	}

	addr, err := helpers.ResolveTCPAddress(providerServer.host, providerServer.port)

	if err != nil {
		return nil, err
	}
	providerServer.listener, err = net.ListenTCP("tcp", addr)

	if err != nil {
		return nil, err
	}

	return &providerServer, nil
}
<commit_msg>Fix bug because of unregistered provider id<commit_after>package server

import (
	"anonymous-messaging/config"
	"anonymous-messaging/helpers"
	"anonymous-messaging/networker"
	"anonymous-messaging/node"
	"anonymous-messaging/sphinx"

	"github.com/protobuf/proto"

	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"os"
)

const (
	assigneFlag = "\xa2"
	commFlag    = "\xc6"
	tokenFlag   = "xa9"
	pullFlag    = "\xff"
)

type ProviderIt interface {
	networker.NetworkServer
	networker.NetworkClient
	Start() error
	GetConfig() config.MixConfig
}

type ProviderServer struct {
	id   string
	host string
	port string
	*node.Mix
	listener *net.TCPListener

	assignedClients map[string]ClientRecord
	config          config.MixConfig
}

type ClientRecord struct {
	id     string
	host   string
	port   string
	pubKey []byte
	token  []byte
}

// Start function creates the loggers for capturing the info and error logs
// and starts the listening server. Function returns an error
// signaling whether any operation was unsuccessful
func (p *ProviderServer) Start() error {
	p.run()

	return nil
}

func (p *ProviderServer) GetConfig() config.MixConfig {
	return p.config
}

// Function opens the listener to start listening on provider's host and port
func (p *ProviderServer) run() {

	defer p.listener.Close()
	finish := make(chan bool)

	go func() {
		logLocal.Infof("Listening on %s", p.host+":"+p.port)
		p.listenForIncomingConnections()
	}()

	<-finish
}

// Function processes the received sphinx packet, performs the
// unwrapping operation and checks whether the packet should be
// forwarded or stored. If the processing was unsuccessful and error is returned.
func (p *ProviderServer) receivedPacket(packet []byte) error {
	logLocal.Info("Received new sphinx packet")

	c := make(chan []byte)
	cAdr := make(chan sphinx.Hop)
	cFlag := make(chan string)
	errCh := make(chan error)

	go p.ProcessPacket(packet, c, cAdr, cFlag, errCh)
	dePacket := <-c
	nextHop := <-cAdr
	flag := <-cFlag
	err := <-errCh

	if err != nil {
		return err
	}

	switch flag {
	case "\xF1":
		err = p.forwardPacket(dePacket, nextHop.Address)
		if err != nil {
			return err
		}
	case "\xF0":
		err = p.storeMessage(dePacket, nextHop.Id, "TMP_MESSAGE_ID")
		if err != nil {
			return err
		}
	default:
		logLocal.Info("Sphinx packet flag not recognised")
	}

	return nil
}

func (p *ProviderServer) forwardPacket(sphinxPacket []byte, address string) error {
	packetBytes, err := config.WrapWithFlag(commFlag, sphinxPacket)
	if err != nil {
		return err
	}

	err = p.send(packetBytes, address)
	if err != nil {
		return err
	}
	logLocal.Info("Forwarded sphinx packet")
	return nil
}

// Function opens a connection with selected network address
// and send the passed packet. If connection failed or
// the packet could not be send, an error is returned
func (p *ProviderServer) send(packet []byte, address string) error {

	conn, err := net.Dial("tcp", address)
	if err != nil {
		return err
	}
	defer conn.Close()

	conn.Write(packet)
	return nil
}

// Function responsible for running the listening process of the server;
// The providers listener accepts incoming connections and
// passes the incoming packets to the packet handler.
// If the connection could not be accepted an error
// is logged into the log files, but the function is not stopped
func (p *ProviderServer) listenForIncomingConnections() {
	for {
		conn, err := p.listener.Accept()

		if err != nil {
			logLocal.WithError(err).Error(err)
		} else {
			logLocal.Infof("Received new connection from %s", conn.RemoteAddr())
			errs := make(chan error, 1)
			go p.handleConnection(conn, errs)
			err = <-errs
			if err != nil {
				logLocal.WithError(err).Error(err)
			}
		}
	}
}

// HandleConnection handles the received packets; it checks the flag of the
// packet and schedules a corresponding process function and returns an error.
func (p *ProviderServer) handleConnection(conn net.Conn, errs chan<- error) {

	buff := make([]byte, 1024)
	reqLen, err := conn.Read(buff)
	defer conn.Close()

	if err != nil {
		errs <- err
	}

	var packet config.GeneralPacket
	err = proto.Unmarshal(buff[:reqLen], &packet)
	if err != nil {
		errs <- err
	}

	switch packet.Flag {
	case assigneFlag:
		err = p.handleAssignRequest(packet.Data)
		if err != nil {
			errs <- err
		}
	case commFlag:
		err = p.receivedPacket(packet.Data)
		if err != nil {
			errs <- err
		}
	case pullFlag:
		err = p.handlePullRequest(packet.Data)
		if err != nil {
			errs <- err
		}
	default:
		logLocal.Info(packet.Flag)
		logLocal.Info("Packet flag not recognised. Packet dropped")
		errs <- nil
	}
	errs <- nil
}

// RegisterNewClient generates a fresh authentication token and saves it together with client's public configuration data
// in the list of all registered clients. After the client is registered the function creates an inbox directory
// for the client's inbox, in which clients messages will be stored.
func (p *ProviderServer) registerNewClient(clientBytes []byte) ([]byte, string, error) {
	var clientConf config.ClientConfig
	err := proto.Unmarshal(clientBytes, &clientConf)
	if err != nil {
		return nil, "", err
	}

	token := helpers.SHA256([]byte("TMP_Token" + clientConf.Id))
	record := ClientRecord{id: clientConf.Id, host: clientConf.Host, port: clientConf.Port, pubKey: clientConf.PubKey, token: token}
	p.assignedClients[clientConf.Id] = record
	address := clientConf.Host + ":" + clientConf.Port

	path := fmt.Sprintf("./inboxes/%s", clientConf.Id)
	exists, err := helpers.DirExists(path)
	if err != nil {
		return nil, "", err
	}
	if exists == false {
		if err := os.MkdirAll(path, 0775); err != nil {
			return nil, "", err
		}
	}

	return token, address, nil
}

// Function is responsible for handling the registration request from the client.
// it registers the client in the list of all registered clients and send
// an authentication token back to the client.
func (p *ProviderServer) handleAssignRequest(packet []byte) error {
	logLocal.Info("Received assign request from the client")

	token, adr, err := p.registerNewClient(packet)
	if err != nil {
		return err
	}

	tokenBytes, err := config.WrapWithFlag(tokenFlag, token)
	if err != nil {
		return err
	}
	err = p.send(tokenBytes, adr)
	if err != nil {
		return err
	}
	return nil
}

// Function is responsible for handling the pull request received from the client.
// It first authenticates the client, by checking if the received token is valid.
// If yes, the function triggers the function for checking client's inbox
// and sending buffered messages. Otherwise, an error is returned.
func (p *ProviderServer) handlePullRequest(rqsBytes []byte) error {
	var request config.PullRequest
	err := proto.Unmarshal(rqsBytes, &request)
	if err != nil {
		return err
	}

	logLocal.Infof("Processing pull request: %s %s", request.ClientId, string(request.Token))

	if p.authenticateUser(request.ClientId, request.Token) == true {
		signal, err := p.fetchMessages(request.ClientId)
		if err != nil {
			return err
		}
		switch signal {
		case "NI":
			logLocal.Info("Inbox does not exist. Sending signal to client.")
		case "EI":
			logLocal.Info("Inbox is empty. Sending info to the client.")
		case "SI":
			logLocal.Info("All messages from the inbox succesfuly sent to the client.")
		}
	} else {
		logLocal.Warning("Authentication went wrong")
		return errors.New("authentication went wrong")
	}
	return nil
}

// AuthenticateUser compares the authentication token received from the client with
// the one stored by the provider. If tokens are the same, it returns true
// and false otherwise.
func (p *ProviderServer) authenticateUser(clientId string, clientToken []byte) bool {

	if bytes.Compare(p.assignedClients[clientId].token, clientToken) == 0 {
		return true
	}
	logLocal.Warningf("Non matching token: %s, %s", p.assignedClients[clientId].token, clientToken)
	return false
}

// FetchMessages fetches messages from the requested inbox.
// FetchMessages checks whether an inbox exists and if it contains
// stored messages. If inbox contains any stored messages, all of them
// are send to the client one by one. FetchMessages returns a code
// signaling whether (NI) inbox does not exist, (EI) inbox is empty,
// (SI) messages were send to the client; and an error.
func (p *ProviderServer) fetchMessages(clientId string) (string, error) {

	path := fmt.Sprintf("./inboxes/%s", clientId)
	exist, err := helpers.DirExists(path)
	if err != nil {
		return "", err
	}
	if exist == false {
		return "NI", nil
	}
	files, err := ioutil.ReadDir(path)
	if err != nil {
		return "", err
	}
	if len(files) == 0 {
		return "EI", nil
	}

	for _, f := range files {
		dat, err := ioutil.ReadFile(path + "/" + f.Name())
		if err != nil {
			return "", err
		}

		address := p.assignedClients[clientId].host + ":" + p.assignedClients[clientId].port
		logLocal.Infof("Found stored message for address %s", address)
		msgBytes, err := config.WrapWithFlag(commFlag, dat)
		if err != nil {
			return "", err
		}
		err = p.send(msgBytes, address)
		if err != nil {
			return "", err
		}
	}
	return "SI", nil
}

// StoreMessage saves the given message in the inbox defined by the given id.
// If the inbox address does not exist or writing into the inbox was unsuccessful
// the function returns an error
func (p *ProviderServer) storeMessage(message []byte, inboxId string, messageId string) error {
	path := fmt.Sprintf("./inboxes/%s", inboxId)
	fileName := path + "/" + messageId + ".txt"

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

	_, err = file.Write(message)
	if err != nil {
		return err
	}

	logLocal.Infof("Stored message for %s", inboxId)
	return nil
}

// NewProviderServer constructs a new provider object.
// NewProviderServer returns a new provider object and an error.
func NewProviderServer(id string, host string, port string, pubKey []byte, prvKey []byte, pkiPath string) (*ProviderServer, error) {
	node := node.NewMix(pubKey, prvKey)
	providerServer := ProviderServer{id: id, host: host, port: port, Mix: node, listener: nil}
	providerServer.config = config.MixConfig{Id: providerServer.id, Host: providerServer.host, Port: providerServer.port, PubKey: providerServer.GetPublicKey()}
	providerServer.assignedClients = make(map[string]ClientRecord)

	configBytes, err := proto.Marshal(&providerServer.config)
	if err != nil {
		return nil, err
	}
	err = helpers.AddToDatabase(pkiPath, "Pki", providerServer.id, "Provider", configBytes)
	if err != nil {
		return nil, err
	}

	addr, err := helpers.ResolveTCPAddress(providerServer.host, providerServer.port)

	if err != nil {
		return nil, err
	}
	providerServer.listener, err = net.ListenTCP("tcp", addr)

	if err != nil {
		return nil, err
	}

	return &providerServer, nil
}
<|endoftext|>


<commit_before>package params

import (
	"math/big"

	"github.com/ethereum/go-ethereum/common"
)

const (
	// DefaultClientIdentifier is client identifier to advertise over the network
	DefaultClientIdentifier = "status"

	// DefaultIPCFile is filename of exposed IPC RPC Server
	DefaultIPCFile = "geth.ipc"

	// DefaultHTTPHost is host interface for the HTTP RPC server
	DefaultHTTPHost = "localhost"

	// DefaultHTTPPort is HTTP-RPC port (replaced in unit tests)
	DefaultHTTPPort = 8545

	// DefaultAPIModules is a list of modules to expose vie HTTP RPC
	// TODO remove "admin" on main net
	DefaultAPIModules = "db,eth,net,web3,shh,personal,admin"

	// DefaultWSHost is a host interface for the websocket RPC server
	DefaultWSHost = "localhost"

	// DefaultWSPort is a WS-RPC port (replaced in unit tests)
	DefaultWSPort = 8546

	// DefaultMaxPeers is the maximum number of global peers
	DefaultMaxPeers = 25

	// DefaultMaxPendingPeers is the maximum number of peers that can be pending in the
	// handshake phase, counted separately for inbound and outbound connections.
	DefaultMaxPendingPeers = 0

	// DefaultGas default amount of gas used for transactions
	DefaultGas = 180000

	// DefaultFileDescriptorLimit is fd limit that database can use
	DefaultFileDescriptorLimit = uint64(2048)

	// DefaultDatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
	DefaultDatabaseCache = 128

	// TestNetworkId is id of a test network
	TestNetworkId = 3
)

// Gas price settings
var (
	GasPrice                = new(big.Int).Mul(big.NewInt(20), common.Shannon)  // Minimal gas price to accept for mining a transactions
	GpoMinGasPrice          = new(big.Int).Mul(big.NewInt(20), common.Shannon)  // Minimum suggested gas price
	GpoMaxGasPrice          = new(big.Int).Mul(big.NewInt(500), common.Shannon) // Maximum suggested gas price
	GpoFullBlockRatio       = 80                                                // Full block threshold for gas price calculation (%)
	GpobaseStepDown         = 10                                                // Suggested gas price base step down ratio (1/1000)
	GpobaseStepUp           = 100                                               // Suggested gas price base step up ratio (1/1000)
	GpobaseCorrectionFactor = 110                                               // Suggested gas price base correction factor (%)
)
<commit_msg>geth/params: status -> StatusIM<commit_after>package params

import (
	"math/big"

	"github.com/ethereum/go-ethereum/common"
)

const (
	// DefaultClientIdentifier is client identifier to advertise over the network
	DefaultClientIdentifier = "StatusIM"

	// DefaultIPCFile is filename of exposed IPC RPC Server
	DefaultIPCFile = "geth.ipc"

	// DefaultHTTPHost is host interface for the HTTP RPC server
	DefaultHTTPHost = "localhost"

	// DefaultHTTPPort is HTTP-RPC port (replaced in unit tests)
	DefaultHTTPPort = 8545

	// DefaultAPIModules is a list of modules to expose vie HTTP RPC
	// TODO remove "admin" on main net
	DefaultAPIModules = "db,eth,net,web3,shh,personal,admin"

	// DefaultWSHost is a host interface for the websocket RPC server
	DefaultWSHost = "localhost"

	// DefaultWSPort is a WS-RPC port (replaced in unit tests)
	DefaultWSPort = 8546

	// DefaultMaxPeers is the maximum number of global peers
	DefaultMaxPeers = 25

	// DefaultMaxPendingPeers is the maximum number of peers that can be pending in the
	// handshake phase, counted separately for inbound and outbound connections.
	DefaultMaxPendingPeers = 0

	// DefaultGas default amount of gas used for transactions
	DefaultGas = 180000

	// DefaultFileDescriptorLimit is fd limit that database can use
	DefaultFileDescriptorLimit = uint64(2048)

	// DefaultDatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
	DefaultDatabaseCache = 128

	// TestNetworkId is id of a test network
	TestNetworkId = 3
)

// Gas price settings
var (
	GasPrice                = new(big.Int).Mul(big.NewInt(20), common.Shannon)  // Minimal gas price to accept for mining a transactions
	GpoMinGasPrice          = new(big.Int).Mul(big.NewInt(20), common.Shannon)  // Minimum suggested gas price
	GpoMaxGasPrice          = new(big.Int).Mul(big.NewInt(500), common.Shannon) // Maximum suggested gas price
	GpoFullBlockRatio       = 80                                                // Full block threshold for gas price calculation (%)
	GpobaseStepDown         = 10                                                // Suggested gas price base step down ratio (1/1000)
	GpobaseStepUp           = 100                                               // Suggested gas price base step up ratio (1/1000)
	GpobaseCorrectionFactor = 110                                               // Suggested gas price base correction factor (%)
)
<|endoftext|>


<commit_before>// Package jsonrpc provides an jsonrpc 2.0 client that sends jsonrpc requests and receives jsonrpc responses using http.
package jsonrpc

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"sync"
)

// RPCRequest represents a jsonrpc request object.
//
// See: http://www.jsonrpc.org/specification#request_object
type RPCRequest struct {
	JSONRPC string      `json:"jsonrpc"`
	Method  string      `json:"method"`
	Params  interface{} `json:"params,omitempty"`
	ID      uint        `json:"id"`
}

// RPCNotification represents a jsonrpc notification object.
// A notification object omits the id field since there will be no server response.
//
// See: http://www.jsonrpc.org/specification#notification
type RPCNotification struct {
	JSONRPC string      `json:"jsonrpc"`
	Method  string      `json:"method"`
	Params  interface{} `json:"params,omitempty"`
}

// RPCResponse represents a jsonrpc response object.
// If no rpc specific error occurred Error field is nil.
//
// See: http://www.jsonrpc.org/specification#response_object
type RPCResponse struct {
	JSONRPC string      `json:"jsonrpc"`
	Result  interface{} `json:"result,omitempty"`
	Error   *RPCError   `json:"error,omitempty"`
	ID      uint        `json:"id"`
}

// BatchResponse a list of jsonrpc response objects as a result of a batch request
//
// if you are interested in the response of a specific request use: GetResponseOf(request)
type BatchResponse struct {
	rpcResponses []RPCResponse
}

// RPCError represents a jsonrpc error object if an rpc error occurred.
//
// See: http://www.jsonrpc.org/specification#error_object
type RPCError struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

// RPCClient sends jsonrpc requests over http to the provided rpc backend.
// RPCClient is created using the factory function NewRPCClient().
type RPCClient struct {
	endpoint        string
	httpClient      *http.Client
	customHeaders   map[string]string
	autoIncrementID bool
	nextID          uint
	idMutex         *sync.Mutex
}

// NewRPCClient returns a new RPCClient instance with default configuration (no custom headers, default http.Client, autoincrement ids).
// Endpoint is the rpc-service url to which the rpc requests are sent.
func NewRPCClient(endpoint string) *RPCClient {
	return &RPCClient{
		endpoint:        endpoint,
		httpClient:      http.DefaultClient,
		autoIncrementID: true,
		nextID:          0,
		customHeaders:   make(map[string]string),
		idMutex:         &sync.Mutex{},
	}
}

// NewRPCRequestObject creates and returns a raw RPCRequest structure.
// It is mainly used when building batch requests. For single requests use RPCClient.Call().
// RPCRequest struct can also be created directly, but this function sets the ID and the jsonrpc field to the correct values.
func (client *RPCClient) NewRPCRequestObject(method string, params ...interface{}) *RPCRequest {
	rpcRequest := RPCRequest{
		ID:      client.nextID,
		JSONRPC: "2.0",
		Method:  method,
		Params:  params,
	}
	if client.autoIncrementID == true {
		client.nextID++
	}

	if len(params) == 0 {
		rpcRequest.Params = nil
	}

	return &rpcRequest
}

// NewRPCNotificationObject creates and returns a raw RPCNotification structure.
// It is mainly used when building batch requests. For single notifications use RPCClient.Notification().
// NewRPCNotificationObject struct can also be created directly, but this function sets the ID and the jsonrpc field to the correct values.
func (client *RPCClient) NewRPCNotificationObject(method string, params ...interface{}) *RPCNotification {
	rpcNotification := RPCNotification{
		JSONRPC: "2.0",
		Method:  method,
		Params:  params,
	}

	if len(params) == 0 {
		rpcNotification.Params = nil
	}

	return &rpcNotification
}

// Call sends an jsonrpc request over http to the rpc-service url that was provided on client creation.
//
// If something went wrong on the network / http level or if json parsing failed it returns an error.
//
// If something went wrong on the rpc-service / protocol level the Error field of the returned RPCResponse is set
// and contains information about the error.
//
// If the request was successful the Error field is nil and the Result field of the RPCRespnse struct contains the rpc result.
func (client *RPCClient) Call(method string, params ...interface{}) (*RPCResponse, error) {
	httpRequest, err := client.newRequest(false, method, params...)
	if err != nil {
		return nil, err
	}

	httpResponse, err := client.httpClient.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer httpResponse.Body.Close()

	rpcResponse := RPCResponse{}
	decoder := json.NewDecoder(httpResponse.Body)
	decoder.UseNumber()
	err = decoder.Decode(&rpcResponse)
	if err != nil {
		return nil, err
	}

	return &rpcResponse, nil
}

// Notification sends a jsonrpc request to the rpc-service. The difference to Call() is that this request does not expect a response.
// The ID field of the request is omitted.
func (client *RPCClient) Notification(method string, params ...interface{}) error {
	httpRequest, err := client.newRequest(true, method, params...)
	if err != nil {
		return err
	}

	httpResponse, err := client.httpClient.Do(httpRequest)
	if err != nil {
		return err
	}
	defer httpResponse.Body.Close()
	return nil
}

// Batch sends a jsonrpc batch request to the rpc-service.
// The parameter is a list of requests the could be one of:
//	RPCRequest
//	RPCNotification.
//
// The batch requests returns a list of RPCResponse structs.
func (client *RPCClient) Batch(requests ...interface{}) (*BatchResponse, error) {
	for _, r := range requests {
		switch r := r.(type) {
		default:
			return nil, fmt.Errorf("Invalid parameter: %s", r)
		case *RPCRequest:
		case *RPCNotification:
		}
	}

	httpRequest, err := client.newBatchRequest(requests...)
	if err != nil {
		return nil, err
	}

	httpResponse, err := client.httpClient.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer httpResponse.Body.Close()

	rpcResponses := []RPCResponse{}
	decoder := json.NewDecoder(httpResponse.Body)
	decoder.UseNumber()
	err = decoder.Decode(&rpcResponses)
	if err != nil {
		return nil, err
	}

	return &BatchResponse{rpcResponses: rpcResponses}, nil
}

// SetAutoIncrementID if set to true, the id field of an rpcjson request will be incremented automatically
func (client *RPCClient) SetAutoIncrementID(flag bool) {
	client.autoIncrementID = flag
}

// SetNextID can be used to manually set the next id / reset the id.
func (client *RPCClient) SetNextID(id uint) {
	client.nextID = id
}

// SetCustomHeader is used to set a custom header for each rpc request.
// You could for example set the Authorization Bearer here.
func (client *RPCClient) SetCustomHeader(key string, value string) {
	client.customHeaders[key] = value
}

// UnsetCustomHeader is used to removes a custom header that was added before.
func (client *RPCClient) UnsetCustomHeader(key string) {
	delete(client.customHeaders, key)
}

// SetBasicAuth is a helper function that sets the header for the given basic authentication credentials.
// To reset / disable authentication just set username or password to an empty string value.
func (client *RPCClient) SetBasicAuth(username string, password string) {
	if username == "" || password == "" {
		delete(client.customHeaders, "Authorization")
		return
	}
	auth := username + ":" + password
	client.customHeaders["Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
}

// SetHTTPClient can be used to set a custom http.Client.
// This can be useful for example if you want to customize the http.Client behaviour (e.g. proxy settings)
func (client *RPCClient) SetHTTPClient(httpClient *http.Client) {
	if httpClient == nil {
		panic("httpClient cannot be nil")
	}
	client.httpClient = httpClient
}

func (client *RPCClient) newRequest(notification bool, method string, params ...interface{}) (*http.Request, error) {

	// TODO: easier way to remove ID from RPCRequest without extra struct
	var rpcRequest interface{}
	if notification {
		rpcNotification := RPCNotification{
			JSONRPC: "2.0",
			Method:  method,
			Params:  params,
		}
		if len(params) == 0 {
			rpcNotification.Params = nil
		}
		rpcRequest = rpcNotification
	} else {
		request := RPCRequest{
			ID:      client.nextID,
			JSONRPC: "2.0",
			Method:  method,
			Params:  params,
		}
		if client.autoIncrementID == true {
			client.nextID++
		}
		if len(params) == 0 {
			request.Params = nil
		}
		rpcRequest = request
	}

	body, err := json.Marshal(rpcRequest)
	if err != nil {
		return nil, err
	}

	request, err := http.NewRequest("POST", client.endpoint, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}

	for k, v := range client.customHeaders {
		request.Header.Add(k, v)
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept", "application/json")

	return request, nil
}

func (client *RPCClient) newBatchRequest(requests ...interface{}) (*http.Request, error) {

	body, err := json.Marshal(requests)
	if err != nil {
		return nil, err
	}

	request, err := http.NewRequest("POST", client.endpoint, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}

	for k, v := range client.customHeaders {
		request.Header.Add(k, v)
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept", "application/json")

	return request, nil
}

// UpdateRequestID updates the ID of an RPCRequest structure.
//
// This is used if a request is sent another time and the request should get an updated id.
//
// This does only make sense when used on with Batch() since Call() and Notififcation() do update the id automatically.
func (client *RPCClient) UpdateRequestID(rpcRequest *RPCRequest) {
	if rpcRequest == nil {
		return
	}
	rpcRequest.ID = client.nextID
	if client.autoIncrementID == true {
		client.nextID++
	}
}

// GetInt converts the rpc response to an int and returns it.
//
// This is a convenient function. Int could be 32 or 64 bit, depending on the architecture the code is running on.
// For a deterministic result use GetInt64().
//
// If result was not an integer an error is returned.
func (rpcResponse *RPCResponse) GetInt() (int, error) {
	i, err := rpcResponse.GetInt64()
	return int(i), err
}

// GetInt64 converts the rpc response to an int64 and returns it.
//
// If result was not an integer an error is returned.
func (rpcResponse *RPCResponse) GetInt64() (int64, error) {
	val, ok := rpcResponse.Result.(json.Number)
	if !ok {
		return 0, fmt.Errorf("could not parse int64 from %s", rpcResponse.Result)
	}

	i, err := val.Int64()
	if err != nil {
		return 0, err
	}

	return i, nil
}

// GetFloat64 converts the rpc response to an float64 and returns it.
//
// If result was not an float64 an error is returned.
func (rpcResponse *RPCResponse) GetFloat64() (float64, error) {
	val, ok := rpcResponse.Result.(json.Number)
	if !ok {
		return 0, fmt.Errorf("could not parse float64 from %s", rpcResponse.Result)
	}

	f, err := val.Float64()
	if err != nil {
		return 0, err
	}

	return f, nil
}

// GetBool converts the rpc response to a bool and returns it.
//
// If result was not a bool an error is returned.
func (rpcResponse *RPCResponse) GetBool() (bool, error) {
	val, ok := rpcResponse.Result.(bool)
	if !ok {
		return false, fmt.Errorf("could not parse int from %s", rpcResponse.Result)
	}

	return val, nil
}

// GetString converts the rpc response to a string and returns it.
//
// If result was not a string an error is returned.
func (rpcResponse *RPCResponse) GetString() (string, error) {
	val, ok := rpcResponse.Result.(string)
	if !ok {
		return "", fmt.Errorf("could not parse int from %s", rpcResponse.Result)
	}

	return val, nil
}

// GetObject converts the rpc response to an object (e.g. a struct) and returns it.
// The parameter should be a structure that can hold the data of the response object.
//
// For example if the following json return value is expected: {"name": "alex", age: 33, "country": "Germany"}
// the struct should look like
//  type Person struct {
//    Name string
//    Age int
//    Country string
//  }
func (rpcResponse *RPCResponse) GetObject(toType interface{}) error {
	js, err := json.Marshal(rpcResponse.Result)
	if err != nil {
		return err
	}

	err = json.Unmarshal(js, toType)
	if err != nil {
		return err
	}

	return nil
}

// GetResponseOf returns the rpc response of the corresponding request by matching the id.
//
// For this method to work, autoincrementID should be set to true (default).
func (batchResponse *BatchResponse) GetResponseOf(request *RPCRequest) (*RPCResponse, error) {
	if request == nil {
		return nil, errors.New("parameter cannot be nil")
	}
	for _, elem := range batchResponse.rpcResponses {
		if elem.ID == request.ID {
			return &elem, nil
		}
	}

	return nil, fmt.Errorf("element with id %d not found", request.ID)
}
<commit_msg>revert changes<commit_after>// Package jsonrpc provides an jsonrpc 2.0 client that sends jsonrpc requests and receives jsonrpc responses using http.
package jsonrpc

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"sync"
)

// RPCRequest represents a jsonrpc request object.
//
// See: http://www.jsonrpc.org/specification#request_object
type RPCRequest struct {
	JSONRPC string      `json:"jsonrpc"`
	Method  string      `json:"method"`
	Params  interface{} `json:"params,omitempty"`
	ID      uint        `json:"id"`
}

// RPCNotification represents a jsonrpc notification object.
// A notification object omits the id field since there will be no server response.
//
// See: http://www.jsonrpc.org/specification#notification
type RPCNotification struct {
	JSONRPC string      `json:"jsonrpc"`
	Method  string      `json:"method"`
	Params  interface{} `json:"params,omitempty"`
}

// RPCResponse represents a jsonrpc response object.
// If no rpc specific error occurred Error field is nil.
//
// See: http://www.jsonrpc.org/specification#response_object
type RPCResponse struct {
	JSONRPC string      `json:"jsonrpc"`
	Result  interface{} `json:"result,omitempty"`
	Error   *RPCError   `json:"error,omitempty"`
	ID      uint        `json:"id"`
}

// BatchResponse a list of jsonrpc response objects as a result of a batch request
//
// if you are interested in the response of a specific request use: GetResponseOf(request)
type BatchResponse struct {
	rpcResponses []RPCResponse
}

// RPCError represents a jsonrpc error object if an rpc error occurred.
//
// See: http://www.jsonrpc.org/specification#error_object
type RPCError struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

// RPCClient sends jsonrpc requests over http to the provided rpc backend.
// RPCClient is created using the factory function NewRPCClient().
type RPCClient struct {
	endpoint        string
	httpClient      *http.Client
	customHeaders   map[string]string
	autoIncrementID bool
	nextID          uint
	idMutex         *sync.Mutex
}

// NewRPCClient returns a new RPCClient instance with default configuration (no custom headers, default http.Client, autoincrement ids).
// Endpoint is the rpc-service url to which the rpc requests are sent.
func NewRPCClient(endpoint string) *RPCClient {
	return &RPCClient{
		endpoint:        endpoint,
		httpClient:      http.DefaultClient,
		autoIncrementID: true,
		nextID:          0,
		customHeaders:   make(map[string]string),
		idMutex:         &sync.Mutex{},
	}
}

// NewRPCRequestObject creates and returns a raw RPCRequest structure.
// It is mainly used when building batch requests. For single requests use RPCClient.Call().
// RPCRequest struct can also be created directly, but this function sets the ID and the jsonrpc field to the correct values.
func (client *RPCClient) NewRPCRequestObject(method string, params ...interface{}) *RPCRequest {
	client.idMutex.Lock()
	rpcRequest := RPCRequest{
		ID:      client.nextID,
		JSONRPC: "2.0",
		Method:  method,
		Params:  params,
	}
	if client.autoIncrementID == true {
		client.nextID++
	}
	client.idMutex.Unlock()

	if len(params) == 0 {
		rpcRequest.Params = nil
	}

	return &rpcRequest
}

// NewRPCNotificationObject creates and returns a raw RPCNotification structure.
// It is mainly used when building batch requests. For single notifications use RPCClient.Notification().
// NewRPCNotificationObject struct can also be created directly, but this function sets the ID and the jsonrpc field to the correct values.
func (client *RPCClient) NewRPCNotificationObject(method string, params ...interface{}) *RPCNotification {
	rpcNotification := RPCNotification{
		JSONRPC: "2.0",
		Method:  method,
		Params:  params,
	}

	if len(params) == 0 {
		rpcNotification.Params = nil
	}

	return &rpcNotification
}

// Call sends an jsonrpc request over http to the rpc-service url that was provided on client creation.
//
// If something went wrong on the network / http level or if json parsing failed it returns an error.
//
// If something went wrong on the rpc-service / protocol level the Error field of the returned RPCResponse is set
// and contains information about the error.
//
// If the request was successful the Error field is nil and the Result field of the RPCRespnse struct contains the rpc result.
func (client *RPCClient) Call(method string, params ...interface{}) (*RPCResponse, error) {
	httpRequest, err := client.newRequest(false, method, params...)
	if err != nil {
		return nil, err
	}

	httpResponse, err := client.httpClient.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer httpResponse.Body.Close()

	rpcResponse := RPCResponse{}
	decoder := json.NewDecoder(httpResponse.Body)
	decoder.UseNumber()
	err = decoder.Decode(&rpcResponse)
	if err != nil {
		return nil, err
	}

	return &rpcResponse, nil
}

// Notification sends a jsonrpc request to the rpc-service. The difference to Call() is that this request does not expect a response.
// The ID field of the request is omitted.
func (client *RPCClient) Notification(method string, params ...interface{}) error {
	httpRequest, err := client.newRequest(true, method, params...)
	if err != nil {
		return err
	}

	httpResponse, err := client.httpClient.Do(httpRequest)
	if err != nil {
		return err
	}
	defer httpResponse.Body.Close()
	return nil
}

// Batch sends a jsonrpc batch request to the rpc-service.
// The parameter is a list of requests the could be one of:
//	RPCRequest
//	RPCNotification.
//
// The batch requests returns a list of RPCResponse structs.
func (client *RPCClient) Batch(requests ...interface{}) (*BatchResponse, error) {
	for _, r := range requests {
		switch r := r.(type) {
		default:
			return nil, fmt.Errorf("Invalid parameter: %s", r)
		case *RPCRequest:
		case *RPCNotification:
		}
	}

	httpRequest, err := client.newBatchRequest(requests...)
	if err != nil {
		return nil, err
	}

	httpResponse, err := client.httpClient.Do(httpRequest)
	if err != nil {
		return nil, err
	}
	defer httpResponse.Body.Close()

	rpcResponses := []RPCResponse{}
	decoder := json.NewDecoder(httpResponse.Body)
	decoder.UseNumber()
	err = decoder.Decode(&rpcResponses)
	if err != nil {
		return nil, err
	}

	return &BatchResponse{rpcResponses: rpcResponses}, nil
}

// SetAutoIncrementID if set to true, the id field of an rpcjson request will be incremented automatically
func (client *RPCClient) SetAutoIncrementID(flag bool) {
	client.autoIncrementID = flag
}

// SetNextID can be used to manually set the next id / reset the id.
func (client *RPCClient) SetNextID(id uint) {
	client.idMutex.Lock()
	client.nextID = id
	client.idMutex.Unlock()
}

// SetCustomHeader is used to set a custom header for each rpc request.
// You could for example set the Authorization Bearer here.
func (client *RPCClient) SetCustomHeader(key string, value string) {
	client.customHeaders[key] = value
}

// UnsetCustomHeader is used to removes a custom header that was added before.
func (client *RPCClient) UnsetCustomHeader(key string) {
	delete(client.customHeaders, key)
}

// SetBasicAuth is a helper function that sets the header for the given basic authentication credentials.
// To reset / disable authentication just set username or password to an empty string value.
func (client *RPCClient) SetBasicAuth(username string, password string) {
	if username == "" || password == "" {
		delete(client.customHeaders, "Authorization")
		return
	}
	auth := username + ":" + password
	client.customHeaders["Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
}

// SetHTTPClient can be used to set a custom http.Client.
// This can be useful for example if you want to customize the http.Client behaviour (e.g. proxy settings)
func (client *RPCClient) SetHTTPClient(httpClient *http.Client) {
	if httpClient == nil {
		panic("httpClient cannot be nil")
	}
	client.httpClient = httpClient
}

func (client *RPCClient) newRequest(notification bool, method string, params ...interface{}) (*http.Request, error) {

	// TODO: easier way to remove ID from RPCRequest without extra struct
	var rpcRequest interface{}
	if notification {
		rpcNotification := RPCNotification{
			JSONRPC: "2.0",
			Method:  method,
			Params:  params,
		}
		if len(params) == 0 {
			rpcNotification.Params = nil
		}
		rpcRequest = rpcNotification
	} else {
		client.idMutex.Lock()
		request := RPCRequest{
			ID:      client.nextID,
			JSONRPC: "2.0",
			Method:  method,
			Params:  params,
		}
		if client.autoIncrementID == true {
			client.nextID++
		}
		client.idMutex.Unlock()
		if len(params) == 0 {
			request.Params = nil
		}
		rpcRequest = request
	}

	body, err := json.Marshal(rpcRequest)
	if err != nil {
		return nil, err
	}

	request, err := http.NewRequest("POST", client.endpoint, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}

	for k, v := range client.customHeaders {
		request.Header.Add(k, v)
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept", "application/json")

	return request, nil
}

func (client *RPCClient) newBatchRequest(requests ...interface{}) (*http.Request, error) {

	body, err := json.Marshal(requests)
	if err != nil {
		return nil, err
	}

	request, err := http.NewRequest("POST", client.endpoint, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}

	for k, v := range client.customHeaders {
		request.Header.Add(k, v)
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept", "application/json")

	return request, nil
}

// UpdateRequestID updates the ID of an RPCRequest structure.
//
// This is used if a request is sent another time and the request should get an updated id.
//
// This does only make sense when used on with Batch() since Call() and Notififcation() do update the id automatically.
func (client *RPCClient) UpdateRequestID(rpcRequest *RPCRequest) {
	if rpcRequest == nil {
		return
	}
	client.idMutex.Lock()
	defer client.idMutex.Unlock()
	rpcRequest.ID = client.nextID
	if client.autoIncrementID == true {
		client.nextID++
	}
}

// GetInt converts the rpc response to an int and returns it.
//
// This is a convenient function. Int could be 32 or 64 bit, depending on the architecture the code is running on.
// For a deterministic result use GetInt64().
//
// If result was not an integer an error is returned.
func (rpcResponse *RPCResponse) GetInt() (int, error) {
	i, err := rpcResponse.GetInt64()
	return int(i), err
}

// GetInt64 converts the rpc response to an int64 and returns it.
//
// If result was not an integer an error is returned.
func (rpcResponse *RPCResponse) GetInt64() (int64, error) {
	val, ok := rpcResponse.Result.(json.Number)
	if !ok {
		return 0, fmt.Errorf("could not parse int64 from %s", rpcResponse.Result)
	}

	i, err := val.Int64()
	if err != nil {
		return 0, err
	}

	return i, nil
}

// GetFloat64 converts the rpc response to an float64 and returns it.
//
// If result was not an float64 an error is returned.
func (rpcResponse *RPCResponse) GetFloat64() (float64, error) {
	val, ok := rpcResponse.Result.(json.Number)
	if !ok {
		return 0, fmt.Errorf("could not parse float64 from %s", rpcResponse.Result)
	}

	f, err := val.Float64()
	if err != nil {
		return 0, err
	}

	return f, nil
}

// GetBool converts the rpc response to a bool and returns it.
//
// If result was not a bool an error is returned.
func (rpcResponse *RPCResponse) GetBool() (bool, error) {
	val, ok := rpcResponse.Result.(bool)
	if !ok {
		return false, fmt.Errorf("could not parse int from %s", rpcResponse.Result)
	}

	return val, nil
}

// GetString converts the rpc response to a string and returns it.
//
// If result was not a string an error is returned.
func (rpcResponse *RPCResponse) GetString() (string, error) {
	val, ok := rpcResponse.Result.(string)
	if !ok {
		return "", fmt.Errorf("could not parse int from %s", rpcResponse.Result)
	}

	return val, nil
}

// GetObject converts the rpc response to an object (e.g. a struct) and returns it.
// The parameter should be a structure that can hold the data of the response object.
//
// For example if the following json return value is expected: {"name": "alex", age: 33, "country": "Germany"}
// the struct should look like
//  type Person struct {
//    Name string
//    Age int
//    Country string
//  }
func (rpcResponse *RPCResponse) GetObject(toType interface{}) error {
	js, err := json.Marshal(rpcResponse.Result)
	if err != nil {
		return err
	}

	err = json.Unmarshal(js, toType)
	if err != nil {
		return err
	}

	return nil
}

// GetResponseOf returns the rpc response of the corresponding request by matching the id.
//
// For this method to work, autoincrementID should be set to true (default).
func (batchResponse *BatchResponse) GetResponseOf(request *RPCRequest) (*RPCResponse, error) {
	if request == nil {
		return nil, errors.New("parameter cannot be nil")
	}
	for _, elem := range batchResponse.rpcResponses {
		if elem.ID == request.ID {
			return &elem, nil
		}
	}

	return nil, fmt.Errorf("element with id %d not found", request.ID)
}
<|endoftext|>