<commit_before>package stage0

//
// Rocket is a reference implementation of the app container specification.
//
// Execution on Rocket is divided into a number of stages, and the `rkt`
// binary implements the first stage (stage 0)
//

import (
	"archive/tar"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"syscall"

	"github.com/coreos/rocket/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/coreos/rocket/app-container/aci"
	"github.com/coreos/rocket/app-container/schema"
	"github.com/coreos/rocket/app-container/schema/types"
	"github.com/coreos/rocket/cas"
	rktpath "github.com/coreos/rocket/path"
	ptar "github.com/coreos/rocket/pkg/tar"
	"github.com/coreos/rocket/version"

	"github.com/coreos/rocket/stage0/stage1_init"
	"github.com/coreos/rocket/stage0/stage1_rootfs"
)

const (
	initPath = "stage1/init"
)

type Config struct {
	Store         *cas.Store
	ContainersDir string // root directory for rocket containers
	Stage1Init    string // binary to be execed as stage1
	Stage1Rootfs  string // compressed bundle containing a rootfs for stage1
	Debug         bool
	Images        []string          // application images
	Volumes       map[string]string // map of volumes that rocket can provide to applications
}

func init() {
	log.SetOutput(ioutil.Discard)
}

// Setup sets up a filesystem for a container based on the given config.
// The directory containing the filesystem is returned, and any error encountered.
func Setup(cfg Config) (string, error) {
	if cfg.Debug {
		log.SetOutput(os.Stderr)
	}

	cuuid, err := types.NewUUID(uuid.New())
	if err != nil {
		return "", fmt.Errorf("error creating UID: %v", err)
	}

	// TODO(jonboulle): collision detection/mitigation
	// Create a directory for this container
	dir := filepath.Join(cfg.ContainersDir, cuuid.String())

	if err := os.MkdirAll(dir, 0700); err != nil {
		return "", fmt.Errorf("error creating directory: %v", err)
	}

	log.Printf("Unpacking stage1 rootfs")
	if cfg.Stage1Rootfs != "" {
		if err = unpackRootfs(cfg.Stage1Rootfs, rktpath.Stage1RootfsPath(dir)); err != nil {
			return "", fmt.Errorf("error unpacking rootfs: %v", err)
		}
	} else {
		if err = unpackBuiltinRootfs(rktpath.Stage1RootfsPath(dir)); err != nil {
			return "", fmt.Errorf("error unpacking rootfs: %v", err)
		}
	}

	log.Printf("Writing stage1 init")
	var in io.Reader
	if cfg.Stage1Init != "" {
		in, err = os.Open(cfg.Stage1Init)
		if err != nil {
			return "", fmt.Errorf("error loading stage1 init binary: %v", err)
		}
	} else {
		init_bin, err := stage1_init.Asset("s1init")
		if err != nil {
			return "", fmt.Errorf("error accessing stage1 init bindata: %v", err)
		}
		in = bytes.NewBuffer(init_bin)
	}
	fn := filepath.Join(dir, initPath)
	out, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0555)
	if err != nil {
		return "", fmt.Errorf("error opening stage1 init for writing: %v", err)
	}
	if _, err := io.Copy(out, in); err != nil {
		return "", fmt.Errorf("error writing stage1 init: %v", err)
	}
	if err := out.Close(); err != nil {
		return "", fmt.Errorf("error closing stage1 init: %v", err)
	}

	log.Printf("Wrote filesystem to %s\n", dir)

	cm := schema.ContainerRuntimeManifest{
		ACKind: "ContainerRuntimeManifest",
		UUID:   *cuuid,
		Apps:   make(schema.AppList, 0),
	}

	v, err := types.NewSemVer(version.Version)
	if err != nil {
		return "", fmt.Errorf("error creating version: %v", err)
	}
	cm.ACVersion = *v

	for _, img := range cfg.Images {
		h, err := types.NewHash(img)
		if err != nil {
			return "", fmt.Errorf("error: bad image hash %q: %v", img, err)
		}
		am, err := setupImage(cfg, img, *h, dir)
		if err != nil {
			return "", fmt.Errorf("error setting up image %s: %v", img, err)
		}
		if cm.Apps.Get(am.Name) != nil {
			return "", fmt.Errorf("error: multiple apps with name %s", am.Name)
		}
		a := schema.App{
			Name:        am.Name,
			ImageID:     *h,
			Isolators:   am.Isolators,
			Annotations: am.Annotations,
		}
		cm.Apps = append(cm.Apps, a)
	}

	var sVols []types.Volume
	for key, path := range cfg.Volumes {
		v := types.Volume{
			Kind:     "host",
			Source:   path,
			ReadOnly: true,
			Fulfills: []types.ACName{
				types.ACName(key),
			},
		}
		sVols = append(sVols, v)
	}
	// TODO(jonboulle): check that app mountpoint expectations are
	// satisfied here, rather than waiting for stage1
	cm.Volumes = sVols

	cdoc, err := json.Marshal(cm)
	if err != nil {
		return "", fmt.Errorf("error marshalling container manifest: %v", err)
	}

	log.Printf("Writing container manifest")
	fn = rktpath.ContainerManifestPath(dir)
	if err := ioutil.WriteFile(fn, cdoc, 0700); err != nil {
		return "", fmt.Errorf("error writing container manifest: %v", err)
	}
	return dir, nil
}

// Run actually runs the container by exec()ing the stage1 init inside
// the container filesystem.
func Run(dir string, debug bool) {
	log.Printf("Pivoting to filesystem %s", dir)
	if err := os.Chdir(dir); err != nil {
		log.Fatalf("failed changing to dir: %v", err)
	}

	log.Printf("Execing %s", initPath)
	args := []string{initPath}
	if debug {
		args = append(args, "debug")
	}
	if err := syscall.Exec(initPath, args, os.Environ()); err != nil {
		log.Fatalf("error execing init: %v", err)
	}
}

func untarRootfs(r io.Reader, dir string) error {
	tr := tar.NewReader(r)
	if err := os.MkdirAll(dir, 0776); err != nil {
		return fmt.Errorf("error creating stage1 rootfs directory: %v", err)
	}

	if err := ptar.ExtractTar(tr, dir); err != nil {
		return fmt.Errorf("error extracting rootfs: %v", err)
	}
	return nil
}

// unpackRootfs unpacks a stage1 rootfs (compressed file, pointed to by rfs)
// into dir, returning any error encountered
func unpackRootfs(rfs string, dir string) error {
	fh, err := os.Open(rfs)
	if err != nil {
		return fmt.Errorf("error opening stage1 rootfs: %v", err)
	}
	typ, err := aci.DetectFileType(fh)
	if err != nil {
		return fmt.Errorf("error detecting image type: %v", err)
	}
	if _, err := fh.Seek(0, 0); err != nil {
		return fmt.Errorf("error seeking image: %v", err)
	}
	var r io.Reader
	switch typ {
	case aci.TypeGzip:
		r, err = gzip.NewReader(fh)
		if err != nil {
			return fmt.Errorf("error reading gzip: %v", err)
		}
	case aci.TypeBzip2:
		r = bzip2.NewReader(fh)
	case aci.TypeXz:
		r = aci.XzReader(fh)
	case aci.TypeUnknown:
		return fmt.Errorf("error: unknown image filetype")
	default:
		// should never happen
		panic("no type returned from DetectFileType?")
	}

	if err := untarRootfs(r, dir); err != nil {
		return fmt.Errorf("error untarring rootfs")
	}

	return nil
}

// unpackBuiltinRootfs unpacks the included stage1 rootfs into dir
func unpackBuiltinRootfs(dir string) error {
	b, err := stage1_rootfs.Asset("s1rootfs.tar")
	if err != nil {
		return fmt.Errorf("error accessing rootfs asset: %v", err)
	}
	buf := bytes.NewBuffer(b)

	if err = untarRootfs(buf, dir); err != nil {
		return fmt.Errorf("error untarring rootfs")
	}

	return nil
}

// setupImage attempts to load the image by the given hash from the store,
// verifies that the image matches the given hash and then extracts the image
// into a directory in the given dir.
// It returns the AppManifest that the image contains
func setupImage(cfg Config, img string, h types.Hash, dir string) (*schema.AppManifest, error) {
	log.Println("Loading image", img)

	rs, err := cfg.Store.ReadStream(img)
	if err != nil {
		return nil, err
	}

	ad := rktpath.AppImagePath(dir, h)
	err = os.MkdirAll(ad, 0776)
	if err != nil {
		return nil, fmt.Errorf("error creating image directory: %v", err)
	}

	// Sanity check: provided image name matches image ID
	hash := sha256.New()
	tr, tw := io.Pipe()
	w := io.MultiWriter(hash, tw)

	errc := make(chan error, 1)
	go func() {
		errc <- ptar.ExtractTar(tar.NewReader(tr), ad)
	}()
	if _, err := io.Copy(w, rs); err != nil {
		return nil, fmt.Errorf("error reading image: %v", err)
	}

	if err := <-errc; err != nil {
		return nil, fmt.Errorf("error extracting ACI: %v", err)
	}
	sum := hash.Sum(nil)
	if id := fmt.Sprintf("%x", sum); id != h.Val {
		if err := os.RemoveAll(ad); err != nil {
			fmt.Fprintf(os.Stderr, "error cleaning up directory: %v\n", err)
		}
		return nil, fmt.Errorf("image hash does not match expected")
	}

	err = os.MkdirAll(filepath.Join(ad, "rootfs/tmp"), 0777)
	if err != nil {
		return nil, fmt.Errorf("error creating tmp directory: %v", err)
	}

	mpath := rktpath.AppManifestPath(dir, h)
	f, err := os.Open(mpath)
	if err != nil {
		return nil, fmt.Errorf("error opening app manifest: %v", err)
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, fmt.Errorf("error reading app manifest: %v", err)
	}
	var am schema.AppManifest
	if err := json.Unmarshal(b, &am); err != nil {
		return nil, fmt.Errorf("error unmarshaling app manifest: %v", err)
	}
	return &am, nil
}
<commit_msg>stage0: use TeeReader instead of MultiWriter<commit_after>package stage0

//
// Rocket is a reference implementation of the app container specification.
//
// Execution on Rocket is divided into a number of stages, and the `rkt`
// binary implements the first stage (stage 0)
//

import (
	"archive/tar"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"syscall"

	"github.com/coreos/rocket/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid"
	"github.com/coreos/rocket/app-container/aci"
	"github.com/coreos/rocket/app-container/schema"
	"github.com/coreos/rocket/app-container/schema/types"
	"github.com/coreos/rocket/cas"
	rktpath "github.com/coreos/rocket/path"
	ptar "github.com/coreos/rocket/pkg/tar"
	"github.com/coreos/rocket/version"

	"github.com/coreos/rocket/stage0/stage1_init"
	"github.com/coreos/rocket/stage0/stage1_rootfs"
)

const (
	initPath = "stage1/init"
)

type Config struct {
	Store         *cas.Store
	ContainersDir string // root directory for rocket containers
	Stage1Init    string // binary to be execed as stage1
	Stage1Rootfs  string // compressed bundle containing a rootfs for stage1
	Debug         bool
	Images        []string          // application images
	Volumes       map[string]string // map of volumes that rocket can provide to applications
}

func init() {
	log.SetOutput(ioutil.Discard)
}

// Setup sets up a filesystem for a container based on the given config.
// The directory containing the filesystem is returned, and any error encountered.
func Setup(cfg Config) (string, error) {
	if cfg.Debug {
		log.SetOutput(os.Stderr)
	}

	cuuid, err := types.NewUUID(uuid.New())
	if err != nil {
		return "", fmt.Errorf("error creating UID: %v", err)
	}

	// TODO(jonboulle): collision detection/mitigation
	// Create a directory for this container
	dir := filepath.Join(cfg.ContainersDir, cuuid.String())

	if err := os.MkdirAll(dir, 0700); err != nil {
		return "", fmt.Errorf("error creating directory: %v", err)
	}

	log.Printf("Unpacking stage1 rootfs")
	if cfg.Stage1Rootfs != "" {
		if err = unpackRootfs(cfg.Stage1Rootfs, rktpath.Stage1RootfsPath(dir)); err != nil {
			return "", fmt.Errorf("error unpacking rootfs: %v", err)
		}
	} else {
		if err = unpackBuiltinRootfs(rktpath.Stage1RootfsPath(dir)); err != nil {
			return "", fmt.Errorf("error unpacking rootfs: %v", err)
		}
	}

	log.Printf("Writing stage1 init")
	var in io.Reader
	if cfg.Stage1Init != "" {
		in, err = os.Open(cfg.Stage1Init)
		if err != nil {
			return "", fmt.Errorf("error loading stage1 init binary: %v", err)
		}
	} else {
		init_bin, err := stage1_init.Asset("s1init")
		if err != nil {
			return "", fmt.Errorf("error accessing stage1 init bindata: %v", err)
		}
		in = bytes.NewBuffer(init_bin)
	}
	fn := filepath.Join(dir, initPath)
	out, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0555)
	if err != nil {
		return "", fmt.Errorf("error opening stage1 init for writing: %v", err)
	}
	if _, err := io.Copy(out, in); err != nil {
		return "", fmt.Errorf("error writing stage1 init: %v", err)
	}
	if err := out.Close(); err != nil {
		return "", fmt.Errorf("error closing stage1 init: %v", err)
	}

	log.Printf("Wrote filesystem to %s\n", dir)

	cm := schema.ContainerRuntimeManifest{
		ACKind: "ContainerRuntimeManifest",
		UUID:   *cuuid,
		Apps:   make(schema.AppList, 0),
	}

	v, err := types.NewSemVer(version.Version)
	if err != nil {
		return "", fmt.Errorf("error creating version: %v", err)
	}
	cm.ACVersion = *v

	for _, img := range cfg.Images {
		h, err := types.NewHash(img)
		if err != nil {
			return "", fmt.Errorf("error: bad image hash %q: %v", img, err)
		}
		am, err := setupImage(cfg, img, *h, dir)
		if err != nil {
			return "", fmt.Errorf("error setting up image %s: %v", img, err)
		}
		if cm.Apps.Get(am.Name) != nil {
			return "", fmt.Errorf("error: multiple apps with name %s", am.Name)
		}
		a := schema.App{
			Name:        am.Name,
			ImageID:     *h,
			Isolators:   am.Isolators,
			Annotations: am.Annotations,
		}
		cm.Apps = append(cm.Apps, a)
	}

	var sVols []types.Volume
	for key, path := range cfg.Volumes {
		v := types.Volume{
			Kind:     "host",
			Source:   path,
			ReadOnly: true,
			Fulfills: []types.ACName{
				types.ACName(key),
			},
		}
		sVols = append(sVols, v)
	}
	// TODO(jonboulle): check that app mountpoint expectations are
	// satisfied here, rather than waiting for stage1
	cm.Volumes = sVols

	cdoc, err := json.Marshal(cm)
	if err != nil {
		return "", fmt.Errorf("error marshalling container manifest: %v", err)
	}

	log.Printf("Writing container manifest")
	fn = rktpath.ContainerManifestPath(dir)
	if err := ioutil.WriteFile(fn, cdoc, 0700); err != nil {
		return "", fmt.Errorf("error writing container manifest: %v", err)
	}
	return dir, nil
}

// Run actually runs the container by exec()ing the stage1 init inside
// the container filesystem.
func Run(dir string, debug bool) {
	log.Printf("Pivoting to filesystem %s", dir)
	if err := os.Chdir(dir); err != nil {
		log.Fatalf("failed changing to dir: %v", err)
	}

	log.Printf("Execing %s", initPath)
	args := []string{initPath}
	if debug {
		args = append(args, "debug")
	}
	if err := syscall.Exec(initPath, args, os.Environ()); err != nil {
		log.Fatalf("error execing init: %v", err)
	}
}

func untarRootfs(r io.Reader, dir string) error {
	tr := tar.NewReader(r)
	if err := os.MkdirAll(dir, 0776); err != nil {
		return fmt.Errorf("error creating stage1 rootfs directory: %v", err)
	}

	if err := ptar.ExtractTar(tr, dir); err != nil {
		return fmt.Errorf("error extracting rootfs: %v", err)
	}
	return nil
}

// unpackRootfs unpacks a stage1 rootfs (compressed file, pointed to by rfs)
// into dir, returning any error encountered
func unpackRootfs(rfs string, dir string) error {
	fh, err := os.Open(rfs)
	if err != nil {
		return fmt.Errorf("error opening stage1 rootfs: %v", err)
	}
	typ, err := aci.DetectFileType(fh)
	if err != nil {
		return fmt.Errorf("error detecting image type: %v", err)
	}
	if _, err := fh.Seek(0, 0); err != nil {
		return fmt.Errorf("error seeking image: %v", err)
	}
	var r io.Reader
	switch typ {
	case aci.TypeGzip:
		r, err = gzip.NewReader(fh)
		if err != nil {
			return fmt.Errorf("error reading gzip: %v", err)
		}
	case aci.TypeBzip2:
		r = bzip2.NewReader(fh)
	case aci.TypeXz:
		r = aci.XzReader(fh)
	case aci.TypeUnknown:
		return fmt.Errorf("error: unknown image filetype")
	default:
		// should never happen
		panic("no type returned from DetectFileType?")
	}

	if err := untarRootfs(r, dir); err != nil {
		return fmt.Errorf("error untarring rootfs")
	}

	return nil
}

// unpackBuiltinRootfs unpacks the included stage1 rootfs into dir
func unpackBuiltinRootfs(dir string) error {
	b, err := stage1_rootfs.Asset("s1rootfs.tar")
	if err != nil {
		return fmt.Errorf("error accessing rootfs asset: %v", err)
	}
	buf := bytes.NewBuffer(b)

	if err = untarRootfs(buf, dir); err != nil {
		return fmt.Errorf("error untarring rootfs")
	}

	return nil
}

// setupImage attempts to load the image by the given hash from the store,
// verifies that the image matches the given hash and extracts the image
// into a directory in the given dir.
// It returns the AppManifest that the image contains
func setupImage(cfg Config, img string, h types.Hash, dir string) (*schema.AppManifest, error) {
	log.Println("Loading image", img)

	rs, err := cfg.Store.ReadStream(img)
	if err != nil {
		return nil, err
	}

	ad := rktpath.AppImagePath(dir, h)
	err = os.MkdirAll(ad, 0776)
	if err != nil {
		return nil, fmt.Errorf("error creating image directory: %v", err)
	}

	hash := sha256.New()
	r := io.TeeReader(rs, hash)

	if err := ptar.ExtractTar(tar.NewReader(r), ad); err != nil {
		return nil, fmt.Errorf("error extracting ACI: %v", err)
	}

	if id := fmt.Sprintf("%x", hash.Sum(nil)); id != h.Val {
		if err := os.RemoveAll(ad); err != nil {
			fmt.Fprintf(os.Stderr, "error cleaning up directory: %v\n", err)
		}
		return nil, fmt.Errorf("image hash does not match expected (%v != %v)", id, h.Val)
	}

	err = os.MkdirAll(filepath.Join(ad, "rootfs/tmp"), 0777)
	if err != nil {
		return nil, fmt.Errorf("error creating tmp directory: %v", err)
	}

	mpath := rktpath.AppManifestPath(dir, h)
	f, err := os.Open(mpath)
	if err != nil {
		return nil, fmt.Errorf("error opening app manifest: %v", err)
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, fmt.Errorf("error reading app manifest: %v", err)
	}
	var am schema.AppManifest
	if err := json.Unmarshal(b, &am); err != nil {
		return nil, fmt.Errorf("error unmarshaling app manifest: %v", err)
	}
	return &am, nil
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"path"
	"strings"

	"github.com/readium/r2-streamer-go/parser/epub"
)

// Publication Main structure for a publication
type Publication struct {
	Context   []string `json:"@context,omitempty"`
	Metadata  Metadata `json:"metadata"`
	Links     []Link   `json:"links"`
	Spine     []Link   `json:"spine,omitempty"`
	Resources []Link   `json:"resources,omitempty"` //Replaces the manifest but less redundant
	TOC       []Link   `json:"toc,omitempty"`
	PageList  []Link   `json:"page-list,omitempty"`
	Landmarks []Link   `json:"landmarks,omitempty"`
	LOI       []Link   `json:"loi,omitempty"` //List of illustrations
	LOA       []Link   `json:"loa,omitempty"` //List of audio files
	LOV       []Link   `json:"lov,omitempty"` //List of videos
	LOT       []Link   `json:"lot,omitempty"` //List of tables

	OtherLinks       []Link                  `json:"-"` //Extension point for links that shouldn't show up in the manifest
	OtherCollections []PublicationCollection `json:"-"` //Extension point for collections that shouldn't show up in the manifest
	Internal         []Internal              `json:"-"`
	LCP              epub.LCP                `json:"-"`
}

// Internal TODO
type Internal struct {
	Name  string
	Value interface{}
}

// Link object used in collections and links
type Link struct {
	Href          string             `json:"href"`
	TypeLink      string             `json:"type,omitempty"`
	Rel           []string           `json:"rel,omitempty"`
	Height        int                `json:"height,omitempty"`
	Width         int                `json:"width,omitempty"`
	Title         string             `json:"title,omitempty"`
	Properties    *Properties        `json:"properties,omitempty"`
	Duration      string             `json:"duration,omitempty"`
	Templated     bool               `json:"templated,omitempty"`
	Children      []Link             `json:"children,omitempty"`
	Bitrate       int                `json:"bitrate,omitempty"`
	MediaOverlays []MediaOverlayNode `json:"-"`
}

// PublicationCollection is used as an extension points for other collections in a Publication
type PublicationCollection struct {
	Role     string
	Metadata []Meta
	Links    []Link
	Children []PublicationCollection
}

// LCPHandler struct to generate json to return to the navigator for the lcp information
type LCPHandler struct {
	Identifier string `json:"identifier,omitempty"`
	Profile    string `json:"profile,omitempty"`
	Key        struct {
		Ready bool   `json:"ready,omitempty"`
		Check string `json:"check,omitempty"`
	} `json:"key,omitempty"`
	Hint struct {
		Text string `json:"text,omitempty"`
		URL  string `json:"url,omitempty"`
	} `json:"hint,omitempty"`
	Support struct {
		Mail string `json:"mail,omitempty"`
		URL  string `json:"url,omitempty"`
		Tel  string `json:"tel,omitempty"`
	} `json:"support"`
}

// LCPHandlerPost struct to unmarshal hash send for decrypting lcp
type LCPHandlerPost struct {
	Key struct {
		Hash string `json:"hash"`
	} `json:"key"`
}

// GetCover return the link for the cover
func (publication *Publication) GetCover() (Link, error) {
	return publication.searchLinkByRel("cover")
}

// GetNavDoc return the link for the navigation document
func (publication *Publication) GetNavDoc() (Link, error) {
	return publication.searchLinkByRel("contents")
}

func (publication *Publication) searchLinkByRel(rel string) (Link, error) {
	for _, resource := range publication.Resources {
		for _, resRel := range resource.Rel {
			if resRel == rel {
				return resource, nil
			}
		}
	}

	for _, item := range publication.Spine {
		for _, spineRel := range item.Rel {
			if spineRel == rel {
				return item, nil
			}
		}
	}

	for _, link := range publication.Links {
		for _, linkRel := range link.Rel {
			if linkRel == rel {
				return link, nil
			}
		}
	}

	return Link{}, errors.New("Can't find " + rel + " in publication")
}

// AddLink Add link in publication link self or search
func (publication *Publication) AddLink(typeLink string, rel []string, url string, templated bool) {
	link := Link{
		Href:     url,
		TypeLink: typeLink,
	}
	if len(rel) > 0 {
		link.Rel = rel
	}

	if templated == true {
		link.Templated = true
	}

	publication.Links = append(publication.Links, link)
}

// FindAllMediaOverlay return all media overlay structure from struct
func (publication *Publication) FindAllMediaOverlay() []MediaOverlayNode {
	var overlay []MediaOverlayNode

	for _, l := range publication.Spine {
		if len(l.MediaOverlays) > 0 {
			for _, ov := range l.MediaOverlays {
				overlay = append(overlay, ov)
			}
		}
	}

	return overlay
}

// FindMediaOverlayByHref search in media overlay structure for url that match
func (publication *Publication) FindMediaOverlayByHref(href string) []MediaOverlayNode {
	var overlay []MediaOverlayNode

	for _, l := range publication.Spine {
		if strings.Contains(l.Href, href) {
			if len(l.MediaOverlays) > 0 {
				for _, ov := range l.MediaOverlays {
					overlay = append(overlay, ov)
				}
			}
		}
	}

	return overlay
}

// AddLCPPassphrase function to add internal metadata for decrypting LCP resources
func (publication *Publication) AddLCPPassphrase(passphrase string) {
	publication.Internal = append(publication.Internal, Internal{Name: "lcp_passphrase", Value: passphrase})
}

// AddLCPHash function to add internal metadata for decrypting LCP resources
func (publication *Publication) AddLCPHash(token []byte) {
	publication.AddToInternal("lcp_hash_passphrase", token)
}

func (publication *Publication) findFromInternal(key string) Internal {
	for _, data := range publication.Internal {
		if data.Name == key {
			return data
		}
	}
	return Internal{}
}

// GetStringFromInternal get data store in internal struct in string
func (publication *Publication) GetStringFromInternal(key string) string {

	data := publication.findFromInternal(key)
	if data.Name != "" {
		return data.Value.(string)
	}
	return ""
}

// GetBytesFromInternal get data store in internal structure in byte
func (publication *Publication) GetBytesFromInternal(key string) []byte {

	data := publication.findFromInternal(key)
	if data.Name != "" {
		return data.Value.([]byte)
	}
	return []byte("")
}

// AddToInternal push data to internal struct in publication
func (publication *Publication) AddToInternal(key string, value interface{}) {
	publication.Internal = append(publication.Internal, Internal{Name: key, Value: value})
}

// GetLCPJSON return the raw lcp license json from META-INF/license.lcpl
// if the data is present else return emtpy string
func (publication *Publication) GetLCPJSON() []byte {
	data := publication.GetBytesFromInternal("lcpl")

	return data
}

// GetLCPHandlerInfo return the lcp handler struct for marshalling
func (publication *Publication) GetLCPHandlerInfo() (LCPHandler, error) {
	var info LCPHandler

	if publication.LCP.ID != "" {
		info.Identifier = publication.LCP.ID
		info.Hint.Text = publication.LCP.Encryption.UserKey.TextHint
		info.Key.Check = publication.LCP.Encryption.UserKey.KeyCheck
		info.Key.Ready = false
		info.Profile = publication.LCP.Encryption.Profile
		for _, l := range publication.LCP.Links {
			if l.Rel == "hint" {
				info.Hint.URL = l.Href
			}
		}

		return info, nil
	}

	return info, errors.New("no LCP information")
}

// GetPreFetchResources select resources that match media type we want to
// prefetch with the manifest
func (publication *Publication) GetPreFetchResources() []Link {
	var resources []Link

	mediaTypes := []string{"text/css", "application/vnd.ms-opentype", "text/javascript"}

	for _, l := range publication.Resources {
		for _, m := range mediaTypes {
			if l.TypeLink == m {
				resources = append(resources, l)
			}
		}
	}

	return resources
}

// AddRel add rel information to Link, will check if the
func (link *Link) AddRel(rel string) {
	relAlreadyPresent := false

	for _, r := range link.Rel {
		if r == rel {
			relAlreadyPresent = true
		}
	}

	if relAlreadyPresent == false {
		link.Rel = append(link.Rel, rel)
	}
}

// AddHrefAbsolute modify Href field with a calculated path based on a
// referend file
func (link *Link) AddHrefAbsolute(href string, baseFile string) {
	link.Href = path.Join(path.Dir(baseFile), href)
}

//TransformLinkToFullURL concatenate a base url to all links
func (publication *Publication) TransformLinkToFullURL(baseURL string) {

	for i := range publication.Spine {
		publication.Spine[i].Href = baseURL + publication.Spine[i].Href
	}

	for i := range publication.Resources {
		publication.Resources[i].Href = baseURL + publication.Resources[i].Href
	}

	for i := range publication.TOC {
		publication.TOC[i].Href = baseURL + publication.TOC[i].Href
	}

	for i := range publication.Landmarks {
		publication.Landmarks[i].Href = baseURL + publication.Landmarks[i].Href
	}
}
<commit_msg>related #36, avoid multiple concat when using function to transform url with full url<commit_after>package models

import (
	"errors"
	"path"
	"strings"

	"github.com/readium/r2-streamer-go/parser/epub"
)

// Publication Main structure for a publication
type Publication struct {
	Context   []string `json:"@context,omitempty"`
	Metadata  Metadata `json:"metadata"`
	Links     []Link   `json:"links"`
	Spine     []Link   `json:"spine,omitempty"`
	Resources []Link   `json:"resources,omitempty"` //Replaces the manifest but less redundant
	TOC       []Link   `json:"toc,omitempty"`
	PageList  []Link   `json:"page-list,omitempty"`
	Landmarks []Link   `json:"landmarks,omitempty"`
	LOI       []Link   `json:"loi,omitempty"` //List of illustrations
	LOA       []Link   `json:"loa,omitempty"` //List of audio files
	LOV       []Link   `json:"lov,omitempty"` //List of videos
	LOT       []Link   `json:"lot,omitempty"` //List of tables

	OtherLinks       []Link                  `json:"-"` //Extension point for links that shouldn't show up in the manifest
	OtherCollections []PublicationCollection `json:"-"` //Extension point for collections that shouldn't show up in the manifest
	Internal         []Internal              `json:"-"`
	LCP              epub.LCP                `json:"-"`
}

// Internal TODO
type Internal struct {
	Name  string
	Value interface{}
}

// Link object used in collections and links
type Link struct {
	Href          string             `json:"href"`
	TypeLink      string             `json:"type,omitempty"`
	Rel           []string           `json:"rel,omitempty"`
	Height        int                `json:"height,omitempty"`
	Width         int                `json:"width,omitempty"`
	Title         string             `json:"title,omitempty"`
	Properties    *Properties        `json:"properties,omitempty"`
	Duration      string             `json:"duration,omitempty"`
	Templated     bool               `json:"templated,omitempty"`
	Children      []Link             `json:"children,omitempty"`
	Bitrate       int                `json:"bitrate,omitempty"`
	MediaOverlays []MediaOverlayNode `json:"-"`
}

// PublicationCollection is used as an extension points for other collections in a Publication
type PublicationCollection struct {
	Role     string
	Metadata []Meta
	Links    []Link
	Children []PublicationCollection
}

// LCPHandler struct to generate json to return to the navigator for the lcp information
type LCPHandler struct {
	Identifier string `json:"identifier,omitempty"`
	Profile    string `json:"profile,omitempty"`
	Key        struct {
		Ready bool   `json:"ready,omitempty"`
		Check string `json:"check,omitempty"`
	} `json:"key,omitempty"`
	Hint struct {
		Text string `json:"text,omitempty"`
		URL  string `json:"url,omitempty"`
	} `json:"hint,omitempty"`
	Support struct {
		Mail string `json:"mail,omitempty"`
		URL  string `json:"url,omitempty"`
		Tel  string `json:"tel,omitempty"`
	} `json:"support"`
}

// LCPHandlerPost struct to unmarshal hash send for decrypting lcp
type LCPHandlerPost struct {
	Key struct {
		Hash string `json:"hash"`
	} `json:"key"`
}

// GetCover return the link for the cover
func (publication *Publication) GetCover() (Link, error) {
	return publication.searchLinkByRel("cover")
}

// GetNavDoc return the link for the navigation document
func (publication *Publication) GetNavDoc() (Link, error) {
	return publication.searchLinkByRel("contents")
}

func (publication *Publication) searchLinkByRel(rel string) (Link, error) {
	for _, resource := range publication.Resources {
		for _, resRel := range resource.Rel {
			if resRel == rel {
				return resource, nil
			}
		}
	}

	for _, item := range publication.Spine {
		for _, spineRel := range item.Rel {
			if spineRel == rel {
				return item, nil
			}
		}
	}

	for _, link := range publication.Links {
		for _, linkRel := range link.Rel {
			if linkRel == rel {
				return link, nil
			}
		}
	}

	return Link{}, errors.New("Can't find " + rel + " in publication")
}

// AddLink Add link in publication link self or search
func (publication *Publication) AddLink(typeLink string, rel []string, url string, templated bool) {
	link := Link{
		Href:     url,
		TypeLink: typeLink,
	}
	if len(rel) > 0 {
		link.Rel = rel
	}

	if templated == true {
		link.Templated = true
	}

	publication.Links = append(publication.Links, link)
}

// FindAllMediaOverlay return all media overlay structure from struct
func (publication *Publication) FindAllMediaOverlay() []MediaOverlayNode {
	var overlay []MediaOverlayNode

	for _, l := range publication.Spine {
		if len(l.MediaOverlays) > 0 {
			for _, ov := range l.MediaOverlays {
				overlay = append(overlay, ov)
			}
		}
	}

	return overlay
}

// FindMediaOverlayByHref search in media overlay structure for url that match
func (publication *Publication) FindMediaOverlayByHref(href string) []MediaOverlayNode {
	var overlay []MediaOverlayNode

	for _, l := range publication.Spine {
		if strings.Contains(l.Href, href) {
			if len(l.MediaOverlays) > 0 {
				for _, ov := range l.MediaOverlays {
					overlay = append(overlay, ov)
				}
			}
		}
	}

	return overlay
}

// AddLCPPassphrase function to add internal metadata for decrypting LCP resources
func (publication *Publication) AddLCPPassphrase(passphrase string) {
	publication.Internal = append(publication.Internal, Internal{Name: "lcp_passphrase", Value: passphrase})
}

// AddLCPHash function to add internal metadata for decrypting LCP resources
func (publication *Publication) AddLCPHash(token []byte) {
	publication.AddToInternal("lcp_hash_passphrase", token)
}

func (publication *Publication) findFromInternal(key string) Internal {
	for _, data := range publication.Internal {
		if data.Name == key {
			return data
		}
	}
	return Internal{}
}

// GetStringFromInternal get data store in internal struct in string
func (publication *Publication) GetStringFromInternal(key string) string {

	data := publication.findFromInternal(key)
	if data.Name != "" {
		return data.Value.(string)
	}
	return ""
}

// GetBytesFromInternal get data store in internal structure in byte
func (publication *Publication) GetBytesFromInternal(key string) []byte {

	data := publication.findFromInternal(key)
	if data.Name != "" {
		return data.Value.([]byte)
	}
	return []byte("")
}

// AddToInternal push data to internal struct in publication
func (publication *Publication) AddToInternal(key string, value interface{}) {
	publication.Internal = append(publication.Internal, Internal{Name: key, Value: value})
}

// GetLCPJSON return the raw lcp license json from META-INF/license.lcpl
// if the data is present else return emtpy string
func (publication *Publication) GetLCPJSON() []byte {
	data := publication.GetBytesFromInternal("lcpl")

	return data
}

// GetLCPHandlerInfo return the lcp handler struct for marshalling
func (publication *Publication) GetLCPHandlerInfo() (LCPHandler, error) {
	var info LCPHandler

	if publication.LCP.ID != "" {
		info.Identifier = publication.LCP.ID
		info.Hint.Text = publication.LCP.Encryption.UserKey.TextHint
		info.Key.Check = publication.LCP.Encryption.UserKey.KeyCheck
		info.Key.Ready = false
		info.Profile = publication.LCP.Encryption.Profile
		for _, l := range publication.LCP.Links {
			if l.Rel == "hint" {
				info.Hint.URL = l.Href
			}
		}

		return info, nil
	}

	return info, errors.New("no LCP information")
}

// GetPreFetchResources select resources that match media type we want to
// prefetch with the manifest
func (publication *Publication) GetPreFetchResources() []Link {
	var resources []Link

	mediaTypes := []string{"text/css", "application/vnd.ms-opentype", "text/javascript"}

	for _, l := range publication.Resources {
		for _, m := range mediaTypes {
			if l.TypeLink == m {
				resources = append(resources, l)
			}
		}
	}

	return resources
}

// AddRel add rel information to Link, will check if the
func (link *Link) AddRel(rel string) {
	relAlreadyPresent := false

	for _, r := range link.Rel {
		if r == rel {
			relAlreadyPresent = true
		}
	}

	if relAlreadyPresent == false {
		link.Rel = append(link.Rel, rel)
	}
}

// AddHrefAbsolute modify Href field with a calculated path based on a
// referend file
func (link *Link) AddHrefAbsolute(href string, baseFile string) {
	link.Href = path.Join(path.Dir(baseFile), href)
}

//TransformLinkToFullURL concatenate a base url to all links
func (publication *Publication) TransformLinkToFullURL(baseURL string) {

	for i := range publication.Spine {
		if !(strings.Contains(publication.Spine[i].Href, "http://") || strings.Contains(publication.Spine[i].Href, "https://")) {
			publication.Spine[i].Href = baseURL + publication.Spine[i].Href
		}
	}

	for i := range publication.Resources {
		if !(strings.Contains(publication.Resources[i].Href, "http://") || strings.Contains(publication.Resources[i].Href, "https://")) {
			publication.Resources[i].Href = baseURL + publication.Resources[i].Href
		}
	}

	for i := range publication.TOC {
		if !(strings.Contains(publication.TOC[i].Href, "http://") || strings.Contains(publication.TOC[i].Href, "https://")) {
			publication.TOC[i].Href = baseURL + publication.TOC[i].Href
		}
	}

	for i := range publication.Landmarks {
		if !(strings.Contains(publication.Landmarks[i].Href, "http://") || strings.Contains(publication.Landmarks[i].Href, "https://")) {
			publication.Landmarks[i].Href = baseURL + publication.Landmarks[i].Href
		}
	}
}
<|endoftext|>


<commit_before>// Package cmd implements cloudflare cli commands.
package cmd

import (
	"github.com/codegangsta/cli"
	"github.com/crackcomm/cloudflare"
)

// New - Returns cloudflare cli commands.
func New() []cli.Command {
	return []cli.Command{
		cmdZones,
		cmdRecords,
	}
}

func client(c *cli.Context) *cloudflare.Client {
	return cloudflare.New(&cloudflare.Options{
		Key:   c.GlobalString("key"),
		Email: c.GlobalString("email"),
	})
}

func yesOrNo(b bool) string {
	if b {
		return "yes"
	}
	return "no"
}
<commit_msg>Usage error when no API Key or Email are set<commit_after>// Package cmd implements cloudflare cli commands.
package cmd

import (
	"log"
	"os"

	"github.com/codegangsta/cli"
	"github.com/crackcomm/cloudflare"
)

// New - Returns cloudflare cli commands.
func New() []cli.Command {
	return []cli.Command{
		cmdZones,
		cmdRecords,
	}
}

func client(c *cli.Context) *cloudflare.Client {
	opts := &cloudflare.Options{
		Key:   c.GlobalString("key"),
		Email: c.GlobalString("email"),
	}
	if opts.Key == "" || opts.Email == "" {
		log.Println("You have to provide Cloudflare Email and API key.")
		log.Println("Use CLOUDFLARE_EMAIL and CLOUDFLARE_KEY environment variables.")
		log.Println("Or alternatively provide them in -email and -key flags in each call.")
		os.Exit(255)
	}
	return cloudflare.New(opts)
}

func yesOrNo(b bool) string {
	if b {
		return "yes"
	}
	return "no"
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"gopkg.in/telegram-bot-api.v4"
)

// RegisterVLAN adds "vlan", "in", "out" commands to bot
func RegisterVLAN(bot Bot, ifaces *Interfaces) {
	ifaces.Update()
	v := &vlan{Interfaces: ifaces}
	bot.Add("vlan", func(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
		return v.replyToVLAN(bot, msg, tokens)
	})
	bot.Add("in", func(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
		return v.replyToIn(bot, msg, tokens)
	})
	bot.Add("out", func(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
		return v.replyToOut(bot, msg, tokens)
	})
}

// VLAN data
type vlan struct {
	Selected   int         // Currently selected vlan
	Interfaces *Interfaces // Enumeration of interfaces
	Device     string      // Device name for selected VLAN
	IFB        string      // IFB device name for selected vlan
}

// Impairment parameters
type params struct {
	delay, jitter     int
	loss, correlation float64
}

// ReplyToVLAN selects a particular VLAN
func (v *vlan) replyToVLAN(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
	if tokens.Remaining() < 1 {
		return "Error: must provide the VLAN number (vlan <vlan_number>)"
	}
	vlan, err := strconv.Atoi(tokens.Next())
	if err != nil {
		return err.Error()
	}
	if vlan < 1 || vlan > 4094 {
		return "Error: VLAN number must be between 1 and 4094"
	}
	suffix := fmt.Sprintf(".%d", vlan)
	found := ""
	for name := range v.Interfaces.Current {
		if strings.HasSuffix(name, suffix) {
			found = name
			break
		}
	}
	if found == "" {
		return fmt.Sprintf("Error: VLAN %d is not found. Run \"ip\" for more info", vlan)
	}
	v.Selected = vlan
	v.Device = found
	ifb, err := v.getIFB()
	if err != nil {
		return fmt.Sprintf("Could not get IFB: %s", err.Error())
	}
	v.IFB = ifb
	return fmt.Sprintf("VLAN %d selected", vlan)
}

// ReplyToIn adds delay in the outbound direction
func (v *vlan) replyToIn(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
	if v.IFB == "" {
		return "Current VLAN does not have IFB device assigned"
	}
	params, err := v.getParams(msg, tokens)
	if err != nil {
		return err.Error()
	}
	return v.impair(v.IFB, params)
}

// ReplyToOut adds delay in the outbound direction
func (v *vlan) replyToOut(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
	params, err := v.getParams(msg, tokens)
	if err != nil {
		return err.Error()
	}
	return v.impair(v.Device, params)
}

// Get Delay, Jitter, PL and PL correlation from command
func (v *vlan) getParams(msg *tgbotapi.Message, tokens *Tokens) (params, error) {
	result := params{}
	if v.Selected == 0 {
		return result, errors.New("No VLAN selected. Run \"vlan\" for more info")
	}
	if tokens.Remaining() <= 0 {
		return result, errors.New("Error: must at least provide delay (ms). Format: [in|out] <delay_ms> <jitter_ms> <PL %> <correlation %>")
	}
	msDelay, err := strconv.Atoi(tokens.Next())
	if err != nil {
		return result, fmt.Errorf("delay is not an int: %s", err.Error())
	}
	if msDelay < 0 || msDelay > 4094 {
		return result, errors.New("Error: Delay must be between 0 and 4094 milliseconds")
	}
	result.delay = msDelay
	if tokens.Remaining() > 0 {
		msJitter, err := strconv.Atoi(tokens.Next())
		if err != nil {
			tokens.Back()
			return result, nil
		}
		if msJitter < 0 || msJitter > 4094 {
			return result, errors.New("Error: Jitter must be between 0 and 4094 milliseconds")
		}
		result.jitter = msJitter
	}
	if tokens.Remaining() > 0 {
		pl, err := strconv.ParseFloat(tokens.Next(), 32)
		if err != nil {
			tokens.Back()
			return result, nil
		}
		if pl < 0 || pl > 100 {
			return result, errors.New("Error: Packet loss must be between 0.0 and 100.0 percent")
		}
		result.loss = pl
	}
	if tokens.Remaining() > 0 {
		corr, err := strconv.ParseFloat(tokens.Next(), 32)
		if err != nil {
			tokens.Back()
			return result, nil
		}
		if corr < 0 || corr > 100 {
			return result, errors.New("Error: Correlation must be between 0.0 and 100.0 percent")
		}
		result.correlation = corr
	}
	return result, nil
}

// Add impairments (delay, jitter, loss...) to an interface
func (v *vlan) impair(iface string, p params) string {
	messages := make([]string, 0, 10)
	// Remove any qdisc
	cmd := exec.Command("tc", "qdisc", "del", "dev", iface, "root")
	var outDel bytes.Buffer
	cmd.Stdout = &outDel
	if err := cmd.Run(); err != nil {
		messages = append(messages, fmt.Sprintf("(Ignore) Error at qdisc del: %s", err.Error()))
	}
	messages = append(messages, fmt.Sprintf("Cleared interface %s", iface))
	messages = append(messages, outDel.String())
	// Prepare for adding jitter and packet loss
	cmdLine := fmt.Sprintf("tc qdisc add dev %s root netem", iface)
	doApply := false
	if p.delay != 0 {
		doApply = true
		cmdLine = fmt.Sprintf("%s delay %dms", cmdLine, p.delay)
		if p.jitter != 0 {
			cmdLine = fmt.Sprintf("%s %dms distribution normal", cmdLine, p.jitter)
		}
	}
	if p.loss != 0 {
		doApply = true
		cmdLine = fmt.Sprintf("%s loss %f%%", cmdLine, p.loss)
		if p.correlation != 0 {
			cmdLine = fmt.Sprintf("%s %f%%", cmdLine, p.correlation)
		}
	}
	// If delay != 0, add it
	var outAdd bytes.Buffer
	if doApply {
		messages = append(messages, fmt.Sprintf("Policy for interface %s: %dms delay (%dms jitter), %f%% PL (%f%% correlation)", iface, p.delay, p.jitter, p.loss, p.correlation))
		fields := strings.Fields(cmdLine)
		cmd = exec.Command(fields[0], fields[1:]...)
		cmd.Stdout = &outAdd
		if err := cmd.Run(); err != nil {
			messages = append(messages, fmt.Sprintf("Error at qdisc add: %s", err.Error()))
		}
		messages = append(messages, outAdd.String())

	}
	// Return the output of the qdisc commands
	return strings.Join(messages, "\n")
}

// Gets the IFB interface associated to the selected VLAN
func (v *vlan) getIFB() (string, error) {
	cmd := exec.Command("tc", "filter", "show", "dev", v.Device, "root")
	var outShow bytes.Buffer
	cmd.Stdout = &outShow
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("Error at filter show: %s", err.Error())
	}
	data := outShow.String()
	re := regexp.MustCompile("Egress Redirect to device ifb[0-9]")
	match := re.FindString(data)
	if match == "" {
		return "", fmt.Errorf("Missing IFB device for %s in %s", v.Device, data)
	}
	ifbFields := strings.Fields(match)
	return ifbFields[len(ifbFields)-1], nil
}
<commit_msg>Replaced misleading error message with warning<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"os/exec"
	"regexp"
	"strconv"
	"strings"

	"gopkg.in/telegram-bot-api.v4"
)

// RegisterVLAN adds "vlan", "in", "out" commands to bot
func RegisterVLAN(bot Bot, ifaces *Interfaces) {
	ifaces.Update()
	v := &vlan{Interfaces: ifaces}
	bot.Add("vlan", func(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
		return v.replyToVLAN(bot, msg, tokens)
	})
	bot.Add("in", func(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
		return v.replyToIn(bot, msg, tokens)
	})
	bot.Add("out", func(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
		return v.replyToOut(bot, msg, tokens)
	})
}

// VLAN data
type vlan struct {
	Selected   int         // Currently selected vlan
	Interfaces *Interfaces // Enumeration of interfaces
	Device     string      // Device name for selected VLAN
	IFB        string      // IFB device name for selected vlan
}

// Impairment parameters
type params struct {
	delay, jitter     int
	loss, correlation float64
}

// ReplyToVLAN selects a particular VLAN
func (v *vlan) replyToVLAN(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
	if tokens.Remaining() < 1 {
		return "Error: must provide the VLAN number (vlan <vlan_number>)"
	}
	vlan, err := strconv.Atoi(tokens.Next())
	if err != nil {
		return err.Error()
	}
	if vlan < 1 || vlan > 4094 {
		return "Error: VLAN number must be between 1 and 4094"
	}
	suffix := fmt.Sprintf(".%d", vlan)
	found := ""
	for name := range v.Interfaces.Current {
		if strings.HasSuffix(name, suffix) {
			found = name
			break
		}
	}
	if found == "" {
		return fmt.Sprintf("Error: VLAN %d is not found. Run \"ip\" for more info", vlan)
	}
	v.Selected = vlan
	v.Device = found
	ifb, err := v.getIFB()
	if err != nil {
		return fmt.Sprintf("Could not get IFB: %s", err.Error())
	}
	v.IFB = ifb
	return fmt.Sprintf("VLAN %d selected", vlan)
}

// ReplyToIn adds delay in the outbound direction
func (v *vlan) replyToIn(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
	if v.IFB == "" {
		return "Current VLAN does not have IFB device assigned"
	}
	params, err := v.getParams(msg, tokens)
	if err != nil {
		return err.Error()
	}
	return v.impair(v.IFB, params)
}

// ReplyToOut adds delay in the outbound direction
func (v *vlan) replyToOut(bot Bot, msg *tgbotapi.Message, tokens *Tokens) string {
	params, err := v.getParams(msg, tokens)
	if err != nil {
		return err.Error()
	}
	return v.impair(v.Device, params)
}

// Get Delay, Jitter, PL and PL correlation from command
func (v *vlan) getParams(msg *tgbotapi.Message, tokens *Tokens) (params, error) {
	result := params{}
	if v.Selected == 0 {
		return result, errors.New("No VLAN selected. Run \"vlan\" for more info")
	}
	if tokens.Remaining() <= 0 {
		return result, errors.New("Error: must at least provide delay (ms). Format: [in|out] <delay_ms> <jitter_ms> <PL %> <correlation %>")
	}
	msDelay, err := strconv.Atoi(tokens.Next())
	if err != nil {
		return result, fmt.Errorf("delay is not an int: %s", err.Error())
	}
	if msDelay < 0 || msDelay > 4094 {
		return result, errors.New("Error: Delay must be between 0 and 4094 milliseconds")
	}
	result.delay = msDelay
	if tokens.Remaining() > 0 {
		msJitter, err := strconv.Atoi(tokens.Next())
		if err != nil {
			tokens.Back()
			return result, nil
		}
		if msJitter < 0 || msJitter > 4094 {
			return result, errors.New("Error: Jitter must be between 0 and 4094 milliseconds")
		}
		result.jitter = msJitter
	}
	if tokens.Remaining() > 0 {
		pl, err := strconv.ParseFloat(tokens.Next(), 32)
		if err != nil {
			tokens.Back()
			return result, nil
		}
		if pl < 0 || pl > 100 {
			return result, errors.New("Error: Packet loss must be between 0.0 and 100.0 percent")
		}
		result.loss = pl
	}
	if tokens.Remaining() > 0 {
		corr, err := strconv.ParseFloat(tokens.Next(), 32)
		if err != nil {
			tokens.Back()
			return result, nil
		}
		if corr < 0 || corr > 100 {
			return result, errors.New("Error: Correlation must be between 0.0 and 100.0 percent")
		}
		result.correlation = corr
	}
	return result, nil
}

// Add impairments (delay, jitter, loss...) to an interface
func (v *vlan) impair(iface string, p params) string {
	messages := make([]string, 0, 10)
	// Remove any qdisc
	cmd := exec.Command("tc", "qdisc", "del", "dev", iface, "root")
	var outDel bytes.Buffer
	cmd.Stdout = &outDel
	if err := cmd.Run(); err != nil {
		messages = append(messages, fmt.Sprintf("Warn: failed to clear interface settings, proceeding anyway (%s)", err.Error()))
	} else {
		messages = append(messages, fmt.Sprintf("Cleared interface %s", iface))
	}
	messages = append(messages, outDel.String())
	// Prepare for adding jitter and packet loss
	cmdLine := fmt.Sprintf("tc qdisc add dev %s root netem", iface)
	doApply := false
	if p.delay != 0 {
		doApply = true
		cmdLine = fmt.Sprintf("%s delay %dms", cmdLine, p.delay)
		if p.jitter != 0 {
			cmdLine = fmt.Sprintf("%s %dms distribution normal", cmdLine, p.jitter)
		}
	}
	if p.loss != 0 {
		doApply = true
		cmdLine = fmt.Sprintf("%s loss %f%%", cmdLine, p.loss)
		if p.correlation != 0 {
			cmdLine = fmt.Sprintf("%s %f%%", cmdLine, p.correlation)
		}
	}
	// If delay != 0, add it
	var outAdd bytes.Buffer
	if doApply {
		messages = append(messages, fmt.Sprintf("Policy for interface %s: %dms delay (%dms jitter), %f%% PL (%f%% correlation)", iface, p.delay, p.jitter, p.loss, p.correlation))
		fields := strings.Fields(cmdLine)
		cmd = exec.Command(fields[0], fields[1:]...)
		cmd.Stdout = &outAdd
		if err := cmd.Run(); err != nil {
			messages = append(messages, fmt.Sprintf("Error at qdisc add: %s", err.Error()))
		}
		messages = append(messages, outAdd.String())

	}
	// Return the output of the qdisc commands
	return strings.Join(messages, "\n")
}

// Gets the IFB interface associated to the selected VLAN
func (v *vlan) getIFB() (string, error) {
	cmd := exec.Command("tc", "filter", "show", "dev", v.Device, "root")
	var outShow bytes.Buffer
	cmd.Stdout = &outShow
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("Error at filter show: %s", err.Error())
	}
	data := outShow.String()
	re := regexp.MustCompile("Egress Redirect to device ifb[0-9]")
	match := re.FindString(data)
	if match == "" {
		return "", fmt.Errorf("Missing IFB device for %s in %s", v.Device, data)
	}
	ifbFields := strings.Fields(match)
	return ifbFields[len(ifbFields)-1], nil
}
<|endoftext|>


<commit_before>package ackhandlernew

import (
	"github.com/lucas-clemente/quic-go/frames"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/utils"
)

type receivedPacketHistory struct {
	ranges *utils.PacketIntervalList
}

// newReceivedPacketHistory creates a new received packet history
func newReceivedPacketHistory() *receivedPacketHistory {
	return &receivedPacketHistory{
		ranges: utils.NewPacketIntervalList(),
	}
}

// ReceivedPacket registers a packet with PacketNumber p and updates the ranges
func (h *receivedPacketHistory) ReceivedPacket(p protocol.PacketNumber) {
	if h.ranges.Len() == 0 {
		h.ranges.PushBack(utils.PacketInterval{Start: p, End: p})
		return
	}

	for el := h.ranges.Back(); el != nil; el = el.Prev() {
		// p already included in an existing range. Nothing to do here
		if p >= el.Value.Start && p <= el.Value.End {
			return
		}

		var rangeExtended bool
		if el.Value.End == p-1 { // extend a range at the end
			rangeExtended = true
			el.Value.End = p
		} else if el.Value.Start == p+1 { // extend a range at the beginning
			rangeExtended = true
			el.Value.Start = p
		}

		// if a range was extended (either at the beginning or at the end, maybe it is possible to merge two ranges into one)
		if rangeExtended {
			prev := el.Prev()
			if prev != nil && prev.Value.End+1 == el.Value.Start { // merge two ranges
				prev.Value.End = el.Value.End
				h.ranges.Remove(el)
				return
			}
			return // if the two ranges were not merge, we're done here
		}

		// create a new range at the end
		if p > el.Value.End {
			h.ranges.InsertAfter(utils.PacketInterval{Start: p, End: p}, el)
			return
		}
	}

	// create a new range at the beginning
	h.ranges.InsertBefore(utils.PacketInterval{Start: p, End: p}, h.ranges.Front())
}

func (h *receivedPacketHistory) DeleteBelow(leastUnacked protocol.PacketNumber) {
	nextEl := h.ranges.Front()
	for el := h.ranges.Front(); nextEl != nil; el = nextEl {
		nextEl = el.Next()

		if leastUnacked > el.Value.Start && leastUnacked <= el.Value.End {
			el.Value.Start = leastUnacked
		}
		if el.Value.End < leastUnacked { // delete a whole range
			h.ranges.Remove(el)
		} else {
			return
		}
	}
}

// GetAckRanges gets a slice of all AckRanges that can be used in an AckFrame
func (h *receivedPacketHistory) GetAckRanges() []frames.AckRange {
	if h.ranges.Len() == 0 {
		return nil
	}

	var ackRanges []frames.AckRange

	for el := h.ranges.Back(); el != nil; el = el.Prev() {
		ackRanges = append(ackRanges, frames.AckRange{FirstPacketNumber: el.Value.Start, LastPacketNumber: el.Value.End})
	}

	return ackRanges
}
<commit_msg>fix race condition in ReceivedPacketHistory<commit_after>package ackhandlernew

import (
	"sync"

	"github.com/lucas-clemente/quic-go/frames"
	"github.com/lucas-clemente/quic-go/protocol"
	"github.com/lucas-clemente/quic-go/utils"
)

type receivedPacketHistory struct {
	ranges *utils.PacketIntervalList

	mutex sync.RWMutex
}

// newReceivedPacketHistory creates a new received packet history
func newReceivedPacketHistory() *receivedPacketHistory {
	return &receivedPacketHistory{
		ranges: utils.NewPacketIntervalList(),
	}
}

// ReceivedPacket registers a packet with PacketNumber p and updates the ranges
func (h *receivedPacketHistory) ReceivedPacket(p protocol.PacketNumber) {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	if h.ranges.Len() == 0 {
		h.ranges.PushBack(utils.PacketInterval{Start: p, End: p})
		return
	}

	for el := h.ranges.Back(); el != nil; el = el.Prev() {
		// p already included in an existing range. Nothing to do here
		if p >= el.Value.Start && p <= el.Value.End {
			return
		}

		var rangeExtended bool
		if el.Value.End == p-1 { // extend a range at the end
			rangeExtended = true
			el.Value.End = p
		} else if el.Value.Start == p+1 { // extend a range at the beginning
			rangeExtended = true
			el.Value.Start = p
		}

		// if a range was extended (either at the beginning or at the end, maybe it is possible to merge two ranges into one)
		if rangeExtended {
			prev := el.Prev()
			if prev != nil && prev.Value.End+1 == el.Value.Start { // merge two ranges
				prev.Value.End = el.Value.End
				h.ranges.Remove(el)
				return
			}
			return // if the two ranges were not merge, we're done here
		}

		// create a new range at the end
		if p > el.Value.End {
			h.ranges.InsertAfter(utils.PacketInterval{Start: p, End: p}, el)
			return
		}
	}

	// create a new range at the beginning
	h.ranges.InsertBefore(utils.PacketInterval{Start: p, End: p}, h.ranges.Front())
}

func (h *receivedPacketHistory) DeleteBelow(leastUnacked protocol.PacketNumber) {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	nextEl := h.ranges.Front()
	for el := h.ranges.Front(); nextEl != nil; el = nextEl {
		nextEl = el.Next()

		if leastUnacked > el.Value.Start && leastUnacked <= el.Value.End {
			el.Value.Start = leastUnacked
		}
		if el.Value.End < leastUnacked { // delete a whole range
			h.ranges.Remove(el)
		} else {
			return
		}
	}
}

// GetAckRanges gets a slice of all AckRanges that can be used in an AckFrame
func (h *receivedPacketHistory) GetAckRanges() []frames.AckRange {
	h.mutex.RLock()
	defer h.mutex.RUnlock()

	if h.ranges.Len() == 0 {
		return nil
	}

	var ackRanges []frames.AckRange

	for el := h.ranges.Back(); el != nil; el = el.Prev() {
		ackRanges = append(ackRanges, frames.AckRange{FirstPacketNumber: el.Value.Start, LastPacketNumber: el.Value.End})
	}

	return ackRanges
}
<|endoftext|>


<commit_before>package ccv3

import (
	"bytes"
	"encoding/json"
	"fmt"

	"code.cloudfoundry.org/cli/api/cloudcontroller"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal"
	"code.cloudfoundry.org/cli/types"
)

// Metadata is used for custom tagging of API resources
type Metadata struct {
	Labels map[string]types.NullString `json:"labels,omitempty"`
}

type ResourceMetadata struct {
	Metadata *Metadata `json:"metadata,omitempty"`
}

func (client *Client) UpdateResourceMetadata(resource string, resourceGUID string, metadata Metadata) (ResourceMetadata, Warnings, error) {
	metadataBytes, err := json.Marshal(ResourceMetadata{Metadata: &metadata})
	if err != nil {
		return ResourceMetadata{}, nil, err
	}

	var request *cloudcontroller.Request

	switch resource {
	case "app":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchApplicationRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"app_guid": resourceGUID},
		})
	case "buildpack":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchBuildpackRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"buildpack_guid": resourceGUID},
		})
	case "domain":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchDomainRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"domain_guid": resourceGUID},
		})
	case "org":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchOrganizationRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"organization_guid": resourceGUID},
		})
	case "route":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchRouteRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"route_guid": resourceGUID},
		})
	case "space":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchSpaceRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"space_guid": resourceGUID},
		})
	case "stack":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchStackRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"stack_guid": resourceGUID},
		})
	default:
		return ResourceMetadata{}, nil, fmt.Errorf("unknown resource type (%s) requested", resource)
	}

	if err != nil {
		return ResourceMetadata{}, nil, err
	}

	var responseMetadata ResourceMetadata
	response := cloudcontroller.Response{
		DecodeJSONResponseInto: &responseMetadata,
	}
	err = client.connection.Make(request, &response)
	return responseMetadata, response.Warnings, err
}

func (client *Client) UpdateResourceMetadataAsync(resource string, resourceGUID string, metadata Metadata) (JobURL, Warnings, error) {
	metadataBytes, err := json.Marshal(ResourceMetadata{Metadata: &metadata})
	if err != nil {
		return "", nil, err
	}

	var request *cloudcontroller.Request

	switch resource {
	case "service-broker":
		request, _ = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchServiceBrokerRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"service_broker_guid": resourceGUID},
		})
	default:
		return "", nil, fmt.Errorf("unknown async resource type (%s) requested", resource)
	}

	if err != nil {
		return "", nil, err
	}

	response := cloudcontroller.Response{}
	err = client.connection.Make(request, &response)
	return JobURL(response.ResourceLocationURL), response.Warnings, err
}
<commit_msg>Fix lint error<commit_after>package ccv3

import (
	"bytes"
	"encoding/json"
	"fmt"

	"code.cloudfoundry.org/cli/api/cloudcontroller"
	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal"
	"code.cloudfoundry.org/cli/types"
)

// Metadata is used for custom tagging of API resources
type Metadata struct {
	Labels map[string]types.NullString `json:"labels,omitempty"`
}

type ResourceMetadata struct {
	Metadata *Metadata `json:"metadata,omitempty"`
}

func (client *Client) UpdateResourceMetadata(resource string, resourceGUID string, metadata Metadata) (ResourceMetadata, Warnings, error) {
	metadataBytes, err := json.Marshal(ResourceMetadata{Metadata: &metadata})
	if err != nil {
		return ResourceMetadata{}, nil, err
	}

	var request *cloudcontroller.Request

	switch resource {
	case "app":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchApplicationRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"app_guid": resourceGUID},
		})
	case "buildpack":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchBuildpackRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"buildpack_guid": resourceGUID},
		})
	case "domain":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchDomainRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"domain_guid": resourceGUID},
		})
	case "org":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchOrganizationRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"organization_guid": resourceGUID},
		})
	case "route":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchRouteRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"route_guid": resourceGUID},
		})
	case "space":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchSpaceRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"space_guid": resourceGUID},
		})
	case "stack":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchStackRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"stack_guid": resourceGUID},
		})
	default:
		return ResourceMetadata{}, nil, fmt.Errorf("unknown resource type (%s) requested", resource)
	}

	if err != nil {
		return ResourceMetadata{}, nil, err
	}

	var responseMetadata ResourceMetadata
	response := cloudcontroller.Response{
		DecodeJSONResponseInto: &responseMetadata,
	}
	err = client.connection.Make(request, &response)
	return responseMetadata, response.Warnings, err
}

func (client *Client) UpdateResourceMetadataAsync(resource string, resourceGUID string, metadata Metadata) (JobURL, Warnings, error) {
	metadataBytes, err := json.Marshal(ResourceMetadata{Metadata: &metadata})
	if err != nil {
		return "", nil, err
	}

	var request *cloudcontroller.Request

	switch resource {
	case "service-broker":
		request, err = client.newHTTPRequest(requestOptions{
			RequestName: internal.PatchServiceBrokerRequest,
			Body:        bytes.NewReader(metadataBytes),
			URIParams:   map[string]string{"service_broker_guid": resourceGUID},
		})
		if err != nil {
			return "", nil, err
		}
	default:
		return "", nil, fmt.Errorf("unknown async resource type (%s) requested", resource)
	}

	if err != nil {
		return "", nil, err
	}

	response := cloudcontroller.Response{}
	err = client.connection.Make(request, &response)
	return JobURL(response.ResourceLocationURL), response.Warnings, err
}
<|endoftext|>


<commit_before>package epos

import (
	"fmt"
	"testing"
)

func TestStore(t *testing.T) {
	db, err := OpenDatabase("testdb1", STORAGE_AUTO)
	if err != nil {
		t.Fatalf("couldn't open testdb1: %v", err)
	}
	defer db.Close()

	id, err := db.Coll("foo").Insert([]string{"hello", "world!"})
	if err != nil {
		t.Errorf("couldn't insert string slice: %v", err)
	}
	if id != 1 {
		t.Errorf("string slice id = %d (expected 1)", id)
	}

	id, err = db.Coll("foo").Insert(struct{ X, Y string }{X: "pan-galactic", Y: "gargle-blaster"})
	if err != nil {
		t.Errorf("couldn't insert struct: %v", err)
	}
	if id != 2 {
		t.Errorf("struct id = %d (expected 2)", id)
	}

	if err = db.Remove(); err != nil {
		t.Errorf("db.Remove failed: %v", err)
	}
}

var benchmarkData = struct {
	Name         string
	Age          uint
	SSN          string
	LuckyNumbers []int
}{
	Name:         "John J. McWhackadoodle",
	Age:          29,
	SSN:          "078-05-1120",
	LuckyNumbers: []int{23, 43},
}

func BenchmarkInsertDiskv(b *testing.B) {
	benchmarkInsert(b, STORAGE_DISKV)
}

func BenchmarkInsertLevelDB(b *testing.B) {
	benchmarkInsert(b, STORAGE_LEVELDB)
}

func BenchmarkInsertGoLevelDB(b *testing.B) {
	benchmarkInsert(b, STORAGE_GOLEVELDB)
}

func benchmarkInsert(b *testing.B, typ StorageType) {
	b.StopTimer()

	db, _ := OpenDatabase(fmt.Sprintf("testdb_bench_insert_%d", typ), typ)

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		_, err := db.Coll("bench").Insert(benchmarkData)
		if err != nil {
			b.Fatal("insert failed: ", err)
		}
	}

	b.StopTimer()
	db.Close()
	db.Remove()
}

func BenchmarkUpdateDiskv(b *testing.B) {
	benchmarkUpdate(b, STORAGE_DISKV)
}

func BenchmarkUpdateLevelDB(b *testing.B) {
	benchmarkUpdate(b, STORAGE_LEVELDB)
}

func BenchmarkUpdateGoLevelDB(b *testing.B) {
	benchmarkUpdate(b, STORAGE_GOLEVELDB)
}

func benchmarkUpdate(b *testing.B, typ StorageType) {
	b.StopTimer()

	db, _ := OpenDatabase(fmt.Sprintf("testdb_bench_update_%d", typ), typ)

	id, err := db.Coll("bench").Insert(benchmarkData)
	if err != nil {
		b.Fatal("insert failed: ", err)
	}

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		benchmarkData.LuckyNumbers[0], benchmarkData.LuckyNumbers[1] = benchmarkData.LuckyNumbers[1], benchmarkData.LuckyNumbers[0]
		if err = db.Coll("bench").Update(id, benchmarkData); err != nil {
			b.Fatal("update failed: ", err)
		}
	}

	b.StopTimer()
	db.Close()
	db.Remove()
}

/*
func BenchmarkDeleteDiskv(b *testing.B) {
	benchmarkDelete(b, STORAGE_DISKV)
}
*/

func BenchmarkDeleteLevelDB(b *testing.B) {
	benchmarkDelete(b, STORAGE_LEVELDB)
}

func BenchmarkDeleteGoLevelDB(b *testing.B) {
	benchmarkDelete(b, STORAGE_GOLEVELDB)
}

func benchmarkDelete(b *testing.B, typ StorageType) {
	b.StopTimer()

	db, _ := OpenDatabase(fmt.Sprintf("testdb_bench_delete_%s", typ), typ)

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		b.StopTimer()
		id, err := db.Coll("bench").Insert(benchmarkData)
		if err != nil {
			b.Fatal("insert failed: ", err)
		}
		b.StartTimer()
		if err = db.Coll("bench").Delete(id); err != nil {
			b.Fatal("delete failed: ", err)
		}
	}

	b.StopTimer()
	db.Close()
	db.Remove()
}
<commit_msg>re-enable diskv insert benchmark.<commit_after>package epos

import (
	"fmt"
	"testing"
)

func TestStore(t *testing.T) {
	db, err := OpenDatabase("testdb1", STORAGE_AUTO)
	if err != nil {
		t.Fatalf("couldn't open testdb1: %v", err)
	}
	defer db.Close()

	id, err := db.Coll("foo").Insert([]string{"hello", "world!"})
	if err != nil {
		t.Errorf("couldn't insert string slice: %v", err)
	}
	if id != 1 {
		t.Errorf("string slice id = %d (expected 1)", id)
	}

	id, err = db.Coll("foo").Insert(struct{ X, Y string }{X: "pan-galactic", Y: "gargle-blaster"})
	if err != nil {
		t.Errorf("couldn't insert struct: %v", err)
	}
	if id != 2 {
		t.Errorf("struct id = %d (expected 2)", id)
	}

	if err = db.Remove(); err != nil {
		t.Errorf("db.Remove failed: %v", err)
	}
}

var benchmarkData = struct {
	Name         string
	Age          uint
	SSN          string
	LuckyNumbers []int
}{
	Name:         "John J. McWhackadoodle",
	Age:          29,
	SSN:          "078-05-1120",
	LuckyNumbers: []int{23, 43},
}

func BenchmarkInsertDiskv(b *testing.B) {
	benchmarkInsert(b, STORAGE_DISKV)
}

func BenchmarkInsertLevelDB(b *testing.B) {
	benchmarkInsert(b, STORAGE_LEVELDB)
}

func BenchmarkInsertGoLevelDB(b *testing.B) {
	benchmarkInsert(b, STORAGE_GOLEVELDB)
}

func benchmarkInsert(b *testing.B, typ StorageType) {
	b.StopTimer()

	db, _ := OpenDatabase(fmt.Sprintf("testdb_bench_insert_%d", typ), typ)

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		_, err := db.Coll("bench").Insert(benchmarkData)
		if err != nil {
			b.Fatal("insert failed: ", err)
		}
	}

	b.StopTimer()
	db.Close()
	db.Remove()
}

func BenchmarkUpdateDiskv(b *testing.B) {
	benchmarkUpdate(b, STORAGE_DISKV)
}

func BenchmarkUpdateLevelDB(b *testing.B) {
	benchmarkUpdate(b, STORAGE_LEVELDB)
}

func BenchmarkUpdateGoLevelDB(b *testing.B) {
	benchmarkUpdate(b, STORAGE_GOLEVELDB)
}

func benchmarkUpdate(b *testing.B, typ StorageType) {
	b.StopTimer()

	db, _ := OpenDatabase(fmt.Sprintf("testdb_bench_update_%d", typ), typ)

	id, err := db.Coll("bench").Insert(benchmarkData)
	if err != nil {
		b.Fatal("insert failed: ", err)
	}

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		benchmarkData.LuckyNumbers[0], benchmarkData.LuckyNumbers[1] = benchmarkData.LuckyNumbers[1], benchmarkData.LuckyNumbers[0]
		if err = db.Coll("bench").Update(id, benchmarkData); err != nil {
			b.Fatal("update failed: ", err)
		}
	}

	b.StopTimer()
	db.Close()
	db.Remove()
}

func BenchmarkDeleteDiskv(b *testing.B) {
	benchmarkDelete(b, STORAGE_DISKV)
}

func BenchmarkDeleteLevelDB(b *testing.B) {
	benchmarkDelete(b, STORAGE_LEVELDB)
}

func BenchmarkDeleteGoLevelDB(b *testing.B) {
	benchmarkDelete(b, STORAGE_GOLEVELDB)
}

func benchmarkDelete(b *testing.B, typ StorageType) {
	b.StopTimer()

	db, _ := OpenDatabase(fmt.Sprintf("testdb_bench_delete_%s", typ), typ)

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		b.StopTimer()
		id, err := db.Coll("bench").Insert(benchmarkData)
		if err != nil {
			b.Fatal("insert failed: ", err)
		}
		b.StartTimer()
		if err = db.Coll("bench").Delete(id); err != nil {
			b.Fatal("delete failed: ", err)
		}
	}

	b.StopTimer()
	db.Close()
	db.Remove()
}
<|endoftext|>


<commit_before>package mqtt

import (
	"bytes"
	"reflect"
	"testing"

	gbt "github.com/huin/gobinarytest"
)

var bitCnt = uint32(0)

func Test(t *testing.T) {
	tests := []struct {
		Comment  string
		Msg      Message
		Expected gbt.Matcher
	}{
		{
			Comment: "CONNECT message",
			Msg: &Connect{
				ProtocolName:    "MQIsdp",
				ProtocolVersion: 3,
				UsernameFlag:    true,
				PasswordFlag:    true,
				WillRetain:      false,
				WillQos:         1,
				WillFlag:        true,
				CleanSession:    true,
				KeepAliveTimer:  10,
				ClientId:        "xixihaha",
				WillTopic:       "topic",
				WillMessage:     "message",
				Username:        "name",
				Password:        "pwd",
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x10}},
				gbt.Named{"Remaining length", gbt.Literal{12 + 5*2 + 8 + 5 + 7 + 4 + 3}},

				// Extended headers for CONNECT:
				gbt.Named{"Protocol name", gbt.InOrder{gbt.Literal{0x00, 0x06}, gbt.Literal("MQIsdp")}},
				gbt.Named{
					"Extended headers for CONNECT",
					gbt.Literal{
						0x03,       // Protocol version number
						0xce,       // Connect flags
						0x00, 0x0a, // Keep alive timer
					},
				},

				// CONNECT payload:
				gbt.Named{"Client identifier", gbt.InOrder{gbt.Literal{0x00, 0x08}, gbt.Literal("xixihaha")}},
				gbt.Named{"Will topic", gbt.InOrder{gbt.Literal{0x00, 0x05}, gbt.Literal("topic")}},
				gbt.Named{"Will message", gbt.InOrder{gbt.Literal{0x00, 0x07}, gbt.Literal("message")}},
				gbt.Named{"Username", gbt.InOrder{gbt.Literal{0x00, 0x04}, gbt.Literal("name")}},
				gbt.Named{"Password", gbt.InOrder{gbt.Literal{0x00, 0x03}, gbt.Literal("pwd")}},
			},
		},

		{
			Comment: "CONNACK message",
			Msg: &ConnAck{
				ReturnCode: RetCodeBadUsernameOrPassword,
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x20}},
				gbt.Named{"Remaining length", gbt.Literal{2}},

				gbt.Named{"Reserved byte", gbt.Literal{0}},
				gbt.Named{"Return code", gbt.Literal{4}},
			},
		},

		{
			Comment: "PUBLISH message with QoS = QosAtMostOnce",
			Msg: &Publish{
				Header: Header{
					DupFlag:  false,
					QosLevel: QosAtMostOnce,
					Retain:   false,
				},
				TopicName: "a/b",
				Data:      []byte{1, 2, 3},
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x30}},
				gbt.Named{"Remaining length", gbt.Literal{5 + 3}},

				gbt.Named{"Topic", gbt.Literal{0x00, 0x03, 'a', '/', 'b'}},
				// No MessageId should be present.
				gbt.Named{"Data", gbt.Literal{1, 2, 3}},
			},
		},

		{
			Comment: "PUBLISH message with QoS = QosAtLeastOnce",
			Msg: &Publish{
				Header: Header{
					DupFlag:  true,
					QosLevel: QosAtLeastOnce,
					Retain:   false,
				},
				TopicName: "a/b",
				MessageId: 0x1234,
				Data:      []byte{1, 2, 3},
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x3a}},
				gbt.Named{"Remaining length", gbt.Literal{7 + 3}},

				gbt.Named{"Topic", gbt.Literal{0x00, 0x03, 'a', '/', 'b'}},
				gbt.Named{"MessageId", gbt.Literal{0x12, 0x34}},
				gbt.Named{"Data", gbt.Literal{1, 2, 3}},
			},
		},

		{
			Comment: "PUBACK message",
			Msg: &PubAck{
				AckCommon: AckCommon{
					MessageId: 0x1234,
				},
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x40}},
				gbt.Named{"Remaining length", gbt.Literal{2}},

				gbt.Named{"MessageId", gbt.Literal{0x12, 0x34}},
			},
		},

		{
			Comment: "SUBSCRIBE message",
			Msg: &Subscribe{
				Header: Header{
					DupFlag:  false,
					QosLevel: QosAtLeastOnce,
				},
				MessageId: 0x4321,
				Topics: []TopicQos{
					{"a/b", QosAtLeastOnce},
					{"c/d", QosExactlyOnce},
				},
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x82}},
				gbt.Named{"Remaining length", gbt.Literal{2 + 5 + 1 + 5 + 1}},

				gbt.Named{"MessageId", gbt.Literal{0x43, 0x21}},
				gbt.Named{"First topic", gbt.Literal{0x00, 0x03, 'a', '/', 'b'}},
				gbt.Named{"First topic QoS", gbt.Literal{1}},
				gbt.Named{"Second topic", gbt.Literal{0x00, 0x03, 'c', '/', 'd'}},
				gbt.Named{"Second topic QoS", gbt.Literal{2}},
			},
		},
	}

	for _, test := range tests {
		encodedBuf := new(bytes.Buffer)
		if err := test.Msg.Encode(encodedBuf); err != nil {
			t.Errorf("%s: Unexpected error during encoding: %v", test.Comment, err)
		} else if err = gbt.Matches(test.Expected, encodedBuf.Bytes()); err != nil {
			t.Errorf("%s: Unexpected encoding output: %v", test.Comment, err)
		}

		expectedBuf := new(bytes.Buffer)
		test.Expected.Write(expectedBuf)

		if decodedMsg, err := DecodeOneMessage(expectedBuf); err != nil {
			t.Errorf("%s: Unexpected error during decoding: %v", test.Comment, err)
		} else if !reflect.DeepEqual(test.Msg, decodedMsg) {
			t.Errorf("%s: Decoded value mismatch\n     got = %#v\nexpected = %#v",
				test.Comment, decodedMsg, test.Msg)
		}
	}
}

func TestDecodeLength(t *testing.T) {
	tests := []struct {
		Expected int32
		Bytes    []byte
	}{
		{0, []byte{0}},
		{1, []byte{1}},
		{20, []byte{20}},

		// Boundary conditions used as tests taken from MQTT 3.1 spec.
		{0, []byte{0x00}},
		{127, []byte{0x7F}},
		{128, []byte{0x80, 0x01}},
		{16383, []byte{0xFF, 0x7F}},
		{16384, []byte{0x80, 0x80, 0x01}},
		{2097151, []byte{0xFF, 0xFF, 0x7F}},
		{2097152, []byte{0x80, 0x80, 0x80, 0x01}},
		{268435455, []byte{0xFF, 0xFF, 0xFF, 0x7F}},
	}

	for _, test := range tests {
		buf := bytes.NewBuffer(test.Bytes)
		if result := decodeLength(buf); test.Expected != result {
			t.Errorf("Test %v: got %d", test, result)
		}
	}
}
<commit_msg>Add test for UNSUBSCRIBE message.<commit_after>package mqtt

import (
	"bytes"
	"reflect"
	"testing"

	gbt "github.com/huin/gobinarytest"
)

var bitCnt = uint32(0)

func Test(t *testing.T) {
	tests := []struct {
		Comment  string
		Msg      Message
		Expected gbt.Matcher
	}{
		{
			Comment: "CONNECT message",
			Msg: &Connect{
				ProtocolName:    "MQIsdp",
				ProtocolVersion: 3,
				UsernameFlag:    true,
				PasswordFlag:    true,
				WillRetain:      false,
				WillQos:         1,
				WillFlag:        true,
				CleanSession:    true,
				KeepAliveTimer:  10,
				ClientId:        "xixihaha",
				WillTopic:       "topic",
				WillMessage:     "message",
				Username:        "name",
				Password:        "pwd",
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x10}},
				gbt.Named{"Remaining length", gbt.Literal{12 + 5*2 + 8 + 5 + 7 + 4 + 3}},

				// Extended headers for CONNECT:
				gbt.Named{"Protocol name", gbt.InOrder{gbt.Literal{0x00, 0x06}, gbt.Literal("MQIsdp")}},
				gbt.Named{
					"Extended headers for CONNECT",
					gbt.Literal{
						0x03,       // Protocol version number
						0xce,       // Connect flags
						0x00, 0x0a, // Keep alive timer
					},
				},

				// CONNECT payload:
				gbt.Named{"Client identifier", gbt.InOrder{gbt.Literal{0x00, 0x08}, gbt.Literal("xixihaha")}},
				gbt.Named{"Will topic", gbt.InOrder{gbt.Literal{0x00, 0x05}, gbt.Literal("topic")}},
				gbt.Named{"Will message", gbt.InOrder{gbt.Literal{0x00, 0x07}, gbt.Literal("message")}},
				gbt.Named{"Username", gbt.InOrder{gbt.Literal{0x00, 0x04}, gbt.Literal("name")}},
				gbt.Named{"Password", gbt.InOrder{gbt.Literal{0x00, 0x03}, gbt.Literal("pwd")}},
			},
		},

		{
			Comment: "CONNACK message",
			Msg: &ConnAck{
				ReturnCode: RetCodeBadUsernameOrPassword,
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x20}},
				gbt.Named{"Remaining length", gbt.Literal{2}},

				gbt.Named{"Reserved byte", gbt.Literal{0}},
				gbt.Named{"Return code", gbt.Literal{4}},
			},
		},

		{
			Comment: "PUBLISH message with QoS = QosAtMostOnce",
			Msg: &Publish{
				Header: Header{
					DupFlag:  false,
					QosLevel: QosAtMostOnce,
					Retain:   false,
				},
				TopicName: "a/b",
				Data:      []byte{1, 2, 3},
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x30}},
				gbt.Named{"Remaining length", gbt.Literal{5 + 3}},

				gbt.Named{"Topic", gbt.Literal{0x00, 0x03, 'a', '/', 'b'}},
				// No MessageId should be present.
				gbt.Named{"Data", gbt.Literal{1, 2, 3}},
			},
		},

		{
			Comment: "PUBLISH message with QoS = QosAtLeastOnce",
			Msg: &Publish{
				Header: Header{
					DupFlag:  true,
					QosLevel: QosAtLeastOnce,
					Retain:   false,
				},
				TopicName: "a/b",
				MessageId: 0x1234,
				Data:      []byte{1, 2, 3},
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x3a}},
				gbt.Named{"Remaining length", gbt.Literal{7 + 3}},

				gbt.Named{"Topic", gbt.Literal{0x00, 0x03, 'a', '/', 'b'}},
				gbt.Named{"MessageId", gbt.Literal{0x12, 0x34}},
				gbt.Named{"Data", gbt.Literal{1, 2, 3}},
			},
		},

		{
			Comment: "PUBACK message",
			Msg: &PubAck{
				AckCommon: AckCommon{
					MessageId: 0x1234,
				},
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x40}},
				gbt.Named{"Remaining length", gbt.Literal{2}},

				gbt.Named{"MessageId", gbt.Literal{0x12, 0x34}},
			},
		},

		{
			Comment: "SUBSCRIBE message",
			Msg: &Subscribe{
				Header: Header{
					DupFlag:  false,
					QosLevel: QosAtLeastOnce,
				},
				MessageId: 0x4321,
				Topics: []TopicQos{
					{"a/b", QosAtLeastOnce},
					{"c/d", QosExactlyOnce},
				},
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0x82}},
				gbt.Named{"Remaining length", gbt.Literal{2 + 5 + 1 + 5 + 1}},

				gbt.Named{"MessageId", gbt.Literal{0x43, 0x21}},
				gbt.Named{"First topic", gbt.Literal{0x00, 0x03, 'a', '/', 'b'}},
				gbt.Named{"First topic QoS", gbt.Literal{1}},
				gbt.Named{"Second topic", gbt.Literal{0x00, 0x03, 'c', '/', 'd'}},
				gbt.Named{"Second topic QoS", gbt.Literal{2}},
			},
		},

		{
			Comment: "UNSUBSCRIBE message",
			Msg: &Unsubscribe{
				Header: Header{
					DupFlag:  false,
					QosLevel: QosAtLeastOnce,
				},
				MessageId: 0x4321,
				Topics:    []string{"a/b", "c/d"},
			},
			Expected: gbt.InOrder{
				gbt.Named{"Header byte", gbt.Literal{0xa2}},
				gbt.Named{"Remaining length", gbt.Literal{2 + 5 + 5}},

				gbt.Named{"MessageId", gbt.Literal{0x43, 0x21}},
				gbt.Named{"First topic", gbt.Literal{0x00, 0x03, 'a', '/', 'b'}},
				gbt.Named{"Second topic", gbt.Literal{0x00, 0x03, 'c', '/', 'd'}},
			},
		},
	}

	for _, test := range tests {
		encodedBuf := new(bytes.Buffer)
		if err := test.Msg.Encode(encodedBuf); err != nil {
			t.Errorf("%s: Unexpected error during encoding: %v", test.Comment, err)
		} else if err = gbt.Matches(test.Expected, encodedBuf.Bytes()); err != nil {
			t.Errorf("%s: Unexpected encoding output: %v", test.Comment, err)
		}

		expectedBuf := new(bytes.Buffer)
		test.Expected.Write(expectedBuf)

		if decodedMsg, err := DecodeOneMessage(expectedBuf); err != nil {
			t.Errorf("%s: Unexpected error during decoding: %v", test.Comment, err)
		} else if !reflect.DeepEqual(test.Msg, decodedMsg) {
			t.Errorf("%s: Decoded value mismatch\n     got = %#v\nexpected = %#v",
				test.Comment, decodedMsg, test.Msg)
		}
	}
}

func TestDecodeLength(t *testing.T) {
	tests := []struct {
		Expected int32
		Bytes    []byte
	}{
		{0, []byte{0}},
		{1, []byte{1}},
		{20, []byte{20}},

		// Boundary conditions used as tests taken from MQTT 3.1 spec.
		{0, []byte{0x00}},
		{127, []byte{0x7F}},
		{128, []byte{0x80, 0x01}},
		{16383, []byte{0xFF, 0x7F}},
		{16384, []byte{0x80, 0x80, 0x01}},
		{2097151, []byte{0xFF, 0xFF, 0x7F}},
		{2097152, []byte{0x80, 0x80, 0x80, 0x01}},
		{268435455, []byte{0xFF, 0xFF, 0xFF, 0x7F}},
	}

	for _, test := range tests {
		buf := bytes.NewBuffer(test.Bytes)
		if result := decodeLength(buf); test.Expected != result {
			t.Errorf("Test %v: got %d", test, result)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/urfave/cli"
)

var ErrWrongStateFile = errors.New("fconf: wrong state file")

type WifiState struct {
	Enabled bool  `json:"enabled"`
	Configg *Wifi `json:"config"`
}

func WifiClientCMD(ctx *cli.Context) error {
	if ctx.IsSet(enableFlag) {
		return EnableWifiClient(ctx)
	}
	if ctx.IsSet(disableFlag) {
		return DisableWifi(ctx)
	}
	if ctx.IsSet(removeFlag) {
		return RemoveWifi(ctx)
	}
	if ctx.IsSet(configFlag) {
		return configWifiClient(ctx)
	}
	return nil
}

//EnableWifiClient enables wifi client. If the config flag is set, wifi is
//configured before being enabled.
func EnableWifiClient(ctx *cli.Context) error {
	if ctx.IsSet(configFlag) {
		err := configWifiClient(ctx)
		if err != nil {
			return err
		}
	}
	i := getInterface(ctx)
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	if w.Configg.Interface == "" {
		w.Configg.Interface = "wlan0"
	}
	service := "wpa_supplicant@" + w.Configg.Interface
	err = restartService(service)
	if err != nil {
		return err
	}
	unit := filepath.Join(networkBase,
		fmt.Sprintf(wirelessService, w.Configg.Interface))
	_, err = os.Stat(unit)
	if os.IsNotExist(err) {
		err = CreateSystemdFile(w.Configg, unit, 0644)
		if err != nil {
			return err
		}
	}
	err = enableService(service)
	if err != nil {
		return err
	}
	err = restartService("systemd-networkd")
	if err != nil {
		return err
	}
	w.Enabled = true
	data, err := json.Marshal(w)
	if err != nil {
		return err
	}
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, w.Configg.Interface), data)

}

func wifiClientState(i string) (*WifiState, error) {
	dir := os.Getenv("FCONF_CONFIGDIR")
	if dir == "" {
		dir = fconfConfigDir
	}
	b, err := ioutil.ReadFile(filepath.Join(dir,
		fmt.Sprintf(defaultWifiClientConfig, i)))
	if err != nil {
		return nil, err
	}
	w := &WifiState{}
	err = json.Unmarshal(b, w)
	if err != nil {
		return nil, err
	}
	if w.Configg == nil {
		return nil, ErrWrongStateFile
	}
	return w, nil
}

func configWifiClient(ctx *cli.Context) error {
	base := ctx.String("dir")
	name := ctx.String("name")
	src := ctx.String("config")
	if src == "" {
		return errors.New("fconf: missing configuration source file")
	}
	var b []byte
	var err error
	if src == "stdin" {
		b, err = ReadFromStdin()
		if err != nil {
			return err
		}
	} else {
		b, err = ioutil.ReadFile(src)
		if err != nil {
			return err
		}
	}
	e := Wifi{}
	err = json.Unmarshal(b, &e)
	if err != nil {
		return err
	}
	if e.Interface == "" {
		e.Interface = "wlan0"
	}
	err = checkDir(base)
	if err != nil {
		return err
	}
	if strings.Contains(name, "%s") {
		name = fmt.Sprintf(name, e.Interface)
	}
	filename := filepath.Join(base, name)
	err = CreateSystemdFile(e, filename, 0644)
	if err != nil {
		return err
	}
	fmt.Printf("successful written wifi configuration to %s \n", filename)
	path := "/etc/wpa_supplicant/"
	err = checkDir(path)
	if err != nil {
		return err
	}
	cname := "wpa_supplicant-" + e.Interface + ".conf"
	s, err := wifiConfig(e.Username, e.Password)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(path, cname), []byte(s), 0644)
	if err != nil {
		return err
	}
	state := &WifiState{Configg: &e}
	ws, err := wifiClientState(e.Interface)
	if err == nil {
		state.Enabled = ws.Enabled
	}
	b, _ = json.Marshal(state)
	fmt.Printf("successful written wifi connection  configuration to %s \n", filepath.Join(path, cname))
	setInterface(ctx, e.Interface)
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, e.Interface), b)
}

func wifiConfig(username, password string) (string, error) {
	cmd := "/usr/bin/wpa_passphrase"
	firstLine := "ctrl_interface=/run/wpa_supplicant_fconf"
	o, err := exec.Command(cmd, username, password).Output()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s \n \n%s\n", firstLine, string(o)), nil
}

func DisableWifi(ctx *cli.Context) error {
	if ctx.IsSet(configFlag) {
		fmt.Println("WARN: config flag will be ignored when diable flag is used")
	}
	i := getInterface(ctx)
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	service := "wpa_supplicant@" + w.Configg.Interface
	err = disableService(service)
	if err != nil {
		return err
	}
	err = stopService(service)
	if err != nil {
		return err
	}
	err = restartService("systemd-networkd")
	if err != nil {
		return err
	}
	w.Enabled = false
	data, err := json.Marshal(w)
	if err != nil {
		return err
	}
	_, err = exec.Command("ip", "addr", "flush", "dev", w.Configg.Interface).Output()
	if err != nil {
		return fmt.Errorf("ERROR: running ip addr flush dev %s %v",
			w.Configg.Interface, err,
		)
	}
	unit := filepath.Join(networkBase,
		fmt.Sprintf(wirelessService, w.Configg.Interface))
	err = removeFile(unit)
	if err != nil {
		return err
	}
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, i), data)
}

func RemoveWifi(ctx *cli.Context) error {
	i := getInterface(ctx)
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	if w.Enabled {
		err = DisableWifi(ctx)
		if err != nil {
			return err
		}
	}

	// remove systemd file
	unit := filepath.Join(networkBase, wirelessService)
	err = removeFile(unit)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	}
	path := "/etc/wpa_supplicant/"
	cname := "wpa_supplicant-" + w.Configg.Interface + ".conf"

	// remove client connection
	err = removeFile(filepath.Join(path, cname))
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	}

	// Remove any interface settings
	err = FlushInterface(w.Configg.Interface)
	if err != nil {
		return err
	}

	// remove the state file
	stateFile := filepath.Join(stateDir(),
		fmt.Sprintf(defaultWifiClientConfig, i))
	return removeFile(stateFile)
}
<commit_msg>wifi: review remove command<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/urfave/cli"
)

var ErrWrongStateFile = errors.New("fconf: wrong state file")

type WifiState struct {
	Enabled bool  `json:"enabled"`
	Configg *Wifi `json:"config"`
}

func WifiClientCMD(ctx *cli.Context) error {
	if ctx.IsSet(enableFlag) {
		return EnableWifiClient(ctx)
	}
	if ctx.IsSet(disableFlag) {
		return DisableWifi(ctx)
	}
	if ctx.IsSet(removeFlag) {
		return RemoveWifi(ctx)
	}
	if ctx.IsSet(configFlag) {
		return configWifiClient(ctx)
	}
	return nil
}

//EnableWifiClient enables wifi client. If the config flag is set, wifi is
//configured before being enabled.
func EnableWifiClient(ctx *cli.Context) error {
	if ctx.IsSet(configFlag) {
		err := configWifiClient(ctx)
		if err != nil {
			return err
		}
	}
	i := getInterface(ctx)
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	if w.Configg.Interface == "" {
		w.Configg.Interface = "wlan0"
	}
	service := "wpa_supplicant@" + w.Configg.Interface
	err = restartService(service)
	if err != nil {
		return err
	}
	unit := filepath.Join(networkBase,
		fmt.Sprintf(wirelessService, w.Configg.Interface))
	_, err = os.Stat(unit)
	if os.IsNotExist(err) {
		err = CreateSystemdFile(w.Configg, unit, 0644)
		if err != nil {
			return err
		}
	}
	err = enableService(service)
	if err != nil {
		return err
	}
	err = restartService("systemd-networkd")
	if err != nil {
		return err
	}
	w.Enabled = true
	data, err := json.Marshal(w)
	if err != nil {
		return err
	}
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, w.Configg.Interface), data)

}

func wifiClientState(i string) (*WifiState, error) {
	dir := os.Getenv("FCONF_CONFIGDIR")
	if dir == "" {
		dir = fconfConfigDir
	}
	b, err := ioutil.ReadFile(filepath.Join(dir,
		fmt.Sprintf(defaultWifiClientConfig, i)))
	if err != nil {
		return nil, err
	}
	w := &WifiState{}
	err = json.Unmarshal(b, w)
	if err != nil {
		return nil, err
	}
	if w.Configg == nil {
		return nil, ErrWrongStateFile
	}
	return w, nil
}

func configWifiClient(ctx *cli.Context) error {
	base := ctx.String("dir")
	name := ctx.String("name")
	src := ctx.String("config")
	if src == "" {
		return errors.New("fconf: missing configuration source file")
	}
	var b []byte
	var err error
	if src == "stdin" {
		b, err = ReadFromStdin()
		if err != nil {
			return err
		}
	} else {
		b, err = ioutil.ReadFile(src)
		if err != nil {
			return err
		}
	}
	e := Wifi{}
	err = json.Unmarshal(b, &e)
	if err != nil {
		return err
	}
	if e.Interface == "" {
		e.Interface = "wlan0"
	}
	err = checkDir(base)
	if err != nil {
		return err
	}
	if strings.Contains(name, "%s") {
		name = fmt.Sprintf(name, e.Interface)
	}
	filename := filepath.Join(base, name)
	err = CreateSystemdFile(e, filename, 0644)
	if err != nil {
		return err
	}
	fmt.Printf("successful written wifi configuration to %s \n", filename)
	path := "/etc/wpa_supplicant/"
	err = checkDir(path)
	if err != nil {
		return err
	}
	cname := "wpa_supplicant-" + e.Interface + ".conf"
	s, err := wifiConfig(e.Username, e.Password)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(path, cname), []byte(s), 0644)
	if err != nil {
		return err
	}
	state := &WifiState{Configg: &e}
	ws, err := wifiClientState(e.Interface)
	if err == nil {
		state.Enabled = ws.Enabled
	}
	b, _ = json.Marshal(state)
	fmt.Printf("successful written wifi connection  configuration to %s \n", filepath.Join(path, cname))
	setInterface(ctx, e.Interface)
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, e.Interface), b)
}

func wifiConfig(username, password string) (string, error) {
	cmd := "/usr/bin/wpa_passphrase"
	firstLine := "ctrl_interface=/run/wpa_supplicant_fconf"
	o, err := exec.Command(cmd, username, password).Output()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s \n \n%s\n", firstLine, string(o)), nil
}

func DisableWifi(ctx *cli.Context) error {
	if ctx.IsSet(configFlag) {
		fmt.Println("WARN: config flag will be ignored when diable flag is used")
	}
	i := getInterface(ctx)
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	service := "wpa_supplicant@" + w.Configg.Interface
	err = disableService(service)
	if err != nil {
		return err
	}
	err = stopService(service)
	if err != nil {
		return err
	}
	err = restartService("systemd-networkd")
	if err != nil {
		return err
	}
	w.Enabled = false
	data, err := json.Marshal(w)
	if err != nil {
		return err
	}
	err = FlushInterface(w.Configg.Interface)
	if err != nil {
		return fmt.Errorf("ERROR: running ip addr flush dev %s %v",
			w.Configg.Interface, err,
		)
	}

	// remove unit file
	unit := filepath.Join(networkBase,
		fmt.Sprintf(wirelessService, w.Configg.Interface))
	err = removeFile(unit)
	if err != nil {
		return err
	}
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, i), data)
}

func RemoveWifi(ctx *cli.Context) error {
	i := getInterface(ctx)
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	if w.Enabled {
		err = DisableWifi(ctx)
		if err != nil {
			return err
		}
	}

	path := "/etc/wpa_supplicant/"
	cname := "wpa_supplicant-" + w.Configg.Interface + ".conf"

	// remove client connection
	err = removeFile(filepath.Join(path, cname))
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	}

	// remove the state file
	stateFile := filepath.Join(stateDir(),
		fmt.Sprintf(defaultWifiClientConfig, i))
	return removeFile(stateFile)
}
<|endoftext|>