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

package oglemock

import (
	"errors"
	"fmt"
	"reflect"
)

var intType = reflect.TypeOf(int(0))
var float64Type = reflect.TypeOf(float64(0))
var complex128Type = reflect.TypeOf(complex128(0))

// Return creates an Action that returns the values passed to Return as
// arguments, after suitable legal type conversions. The following rules apply.
// Given an argument x to Return and a corresponding type T in the method's
// signature, at least one of the following must hold:
//
//  *  x is assignable to T. (See "Assignability" in the language spec.) Note
//     that this in particular applies that x may be a type that implements an
//     interface T. It also implies that the nil literal can be used if T is a
//     pointer, function, interface, slice, channel, or map type.
//
//  *  T is any numeric type, and x is an int that is in-range for that type.
//     This facilities using raw integer constants: Return(17).
//
//  *  T is a floating-point or complex number type, and x is a float64.  This
//     facilities using raw floating-point constants: Return(17.5).
//
//  *  T is a complex number type, and x is a complex128. This facilities using
//     raw complex constants: Return(17+2i).
//
func Return(vals ...interface{}) Action {
	return &returnAction{vals, nil}
}

type returnAction struct {
	returnVals []interface{}
	signature reflect.Type
}

func (a *returnAction) Invoke(vals []interface{}) []interface{} {
	return a.returnVals
}

func (a *returnAction) SetSignature(signature reflect.Type) error {
	if _, err := a.buildInvokeResult(signature); err != nil {
		return err
	}

	a.signature = signature
	return nil
}

// A version of Invoke that does error checking, used by both public methods.
func (a *returnAction) buildInvokeResult(
	sig reflect.Type) (res []interface{}, err error) {
	// Check the length of the return value.
	numOut := sig.NumOut()
	numVals := len(a.returnVals)

	if numOut != numVals {
		err = errors.New(
			fmt.Sprintf("Return given %d vals; expected %d.", numVals, numOut))
		return
	}

	// Attempt to coerce each return value.
	res = make([]interface{}, numOut)

	for i, val := range a.returnVals {
		resType := sig.Out(i)
		res[i], err = a.coerce(val, resType)

		if err != nil {
			res = nil
			err = errors.New(fmt.Sprintf("Return: arg %d: %v", i, err))
			return
		}
	}

	return
}

func (a *returnAction) coerce(x interface{}, t reflect.Type) (interface{}, error) {
	xv := reflect.ValueOf(x)
	rv := reflect.New(t).Elem()

	// Special case: the language spec says that the predeclared identifier nil
	// is assignable to pointers, functions, interface, slices, channels, and map
	// types. However, reflect.ValueOf(nil) returns an invalid value that will
	// not cooperate below. So handle invalid values here, assuming that they
	// resulted from Return(nil).
	if !xv.IsValid() {
		switch t.Kind() {
		case reflect.Ptr, reflect.Func, reflect.Interface, reflect.Chan, reflect.Slice, reflect.Map:
			return rv.Interface(), nil
		}

		return nil, errors.New(fmt.Sprintf("expected %v, given <nil>", t))
	}

	// If x is assignable to type t, let the reflect package do the heavy
	// lifting.
	if reflect.TypeOf(x).AssignableTo(t) {
		rv.Set(xv)
		return rv.Interface(), nil
	}

	// Handle numeric types as described in the documentation on Return.
	switch {
	case t == intType && a.isNumeric(xv.Type()):
		return a.coerceInt(xv.Int(), t)

	case t == float64Type && (a.isFloatingPoint(xv.Type()) || a.isComplex(xv.Type())):
		return a.coerceFloat(xv.Float(), t)

	case t == complex128Type && a.isComplex(xv.Type()):
		return a.coerceComplex(xv.Complex(), t)
	}

	// The value wasn't of a legal type.
	return nil, errors.New(fmt.Sprintf("expected %v, given %v", t, xv.Type()))
}

func (a *returnAction) isNumeric(t reflect.Type) bool {
	return (t.Kind() >= reflect.Int && t.Kind() <= reflect.Uint64) ||
		a.isFloatingPoint(t) ||
		a.isComplex(t)
}

func (a *returnAction) isFloatingPoint(t reflect.Type) bool {
	return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64
}

func (a *returnAction) isComplex(t reflect.Type) bool {
	return t.Kind() == reflect.Complex64 || t.Kind() == reflect.Complex128
}

func (a *returnAction) coerceInt(x int64, t reflect.Type) (interface{}, error) {
	return nil, errors.New("TODO")
}

func (a *returnAction) coerceFloat(x float64, t reflect.Type) (interface{}, error) {
	return nil, errors.New("TODO")
}

func (a *returnAction) coerceComplex(x complex128, t reflect.Type) (interface{}, error) {
	return nil, errors.New("TODO")
}
<commit_msg>Fixed some bugs.<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package oglemock

import (
	"errors"
	"fmt"
	"reflect"
)

var intType = reflect.TypeOf(int(0))
var float64Type = reflect.TypeOf(float64(0))
var complex128Type = reflect.TypeOf(complex128(0))

// Return creates an Action that returns the values passed to Return as
// arguments, after suitable legal type conversions. The following rules apply.
// Given an argument x to Return and a corresponding type T in the method's
// signature, at least one of the following must hold:
//
//  *  x is assignable to T. (See "Assignability" in the language spec.) Note
//     that this in particular applies that x may be a type that implements an
//     interface T. It also implies that the nil literal can be used if T is a
//     pointer, function, interface, slice, channel, or map type.
//
//  *  T is any numeric type, and x is an int that is in-range for that type.
//     This facilities using raw integer constants: Return(17).
//
//  *  T is a floating-point or complex number type, and x is a float64.  This
//     facilities using raw floating-point constants: Return(17.5).
//
//  *  T is a complex number type, and x is a complex128. This facilities using
//     raw complex constants: Return(17+2i).
//
func Return(vals ...interface{}) Action {
	return &returnAction{vals, nil}
}

type returnAction struct {
	returnVals []interface{}
	signature reflect.Type
}

func (a *returnAction) Invoke(vals []interface{}) []interface{} {
	return a.returnVals
}

func (a *returnAction) SetSignature(signature reflect.Type) error {
	if _, err := a.buildInvokeResult(signature); err != nil {
		return err
	}

	a.signature = signature
	return nil
}

// A version of Invoke that does error checking, used by both public methods.
func (a *returnAction) buildInvokeResult(
	sig reflect.Type) (res []interface{}, err error) {
	// Check the length of the return value.
	numOut := sig.NumOut()
	numVals := len(a.returnVals)

	if numOut != numVals {
		err = errors.New(
			fmt.Sprintf("Return given %d vals; expected %d.", numVals, numOut))
		return
	}

	// Attempt to coerce each return value.
	res = make([]interface{}, numOut)

	for i, val := range a.returnVals {
		resType := sig.Out(i)
		res[i], err = a.coerce(val, resType)

		if err != nil {
			res = nil
			err = errors.New(fmt.Sprintf("Return: arg %d: %v", i, err))
			return
		}
	}

	return
}

func (a *returnAction) coerce(x interface{}, t reflect.Type) (interface{}, error) {
	xv := reflect.ValueOf(x)
	rv := reflect.New(t).Elem()

	// Special case: the language spec says that the predeclared identifier nil
	// is assignable to pointers, functions, interface, slices, channels, and map
	// types. However, reflect.ValueOf(nil) returns an invalid value that will
	// not cooperate below. So handle invalid values here, assuming that they
	// resulted from Return(nil).
	if !xv.IsValid() {
		switch t.Kind() {
		case reflect.Ptr, reflect.Func, reflect.Interface, reflect.Chan, reflect.Slice, reflect.Map:
			return rv.Interface(), nil
		}

		return nil, errors.New(fmt.Sprintf("expected %v, given <nil>", t))
	}

	// If x is assignable to type t, let the reflect package do the heavy
	// lifting.
	if reflect.TypeOf(x).AssignableTo(t) {
		rv.Set(xv)
		return rv.Interface(), nil
	}

	// Handle numeric types as described in the documentation on Return.
	switch {
	case xv.Type() == intType && a.isNumeric(t):
		return a.coerceInt(xv.Int(), t)

	case xv.Type() == float64Type && (a.isFloatingPoint(t) || a.isComplex(t)):
		return a.coerceFloat(xv.Float(), t)

	case xv.Type() == complex128Type && a.isComplex(t):
		return a.coerceComplex(xv.Complex(), t)
	}

	// The value wasn't of a legal type.
	return nil, errors.New(fmt.Sprintf("expected %v, given %v", t, xv.Type()))
}

func (a *returnAction) isNumeric(t reflect.Type) bool {
	return (t.Kind() >= reflect.Int && t.Kind() <= reflect.Uint64) ||
		a.isFloatingPoint(t) ||
		a.isComplex(t)
}

func (a *returnAction) isFloatingPoint(t reflect.Type) bool {
	return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64
}

func (a *returnAction) isComplex(t reflect.Type) bool {
	return t.Kind() == reflect.Complex64 || t.Kind() == reflect.Complex128
}

func (a *returnAction) coerceInt(x int64, t reflect.Type) (interface{}, error) {
	return nil, errors.New("TODO")
}

func (a *returnAction) coerceFloat(x float64, t reflect.Type) (interface{}, error) {
	return nil, errors.New("TODO")
}

func (a *returnAction) coerceComplex(x complex128, t reflect.Type) (interface{}, error) {
	return nil, errors.New("TODO")
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package cmd

import (
	"fmt"

	"github.com/skydive-project/skydive/config"
	"github.com/skydive-project/skydive/logging"
	"github.com/spf13/cobra"
)

var (
	cfgFiles   []string
	cfgBackend string
)

// LoadConfiguration from a configuration file
func LoadConfiguration() {
	if len(cfgFiles) != 0 {
		if err := config.InitConfig(cfgBackend, cfgFiles); err != nil {
			panic(fmt.Sprintf("Failed to initialize config: %s", err.Error()))
		}

		if err := logging.InitLogging(); err != nil {
			panic(fmt.Sprintf("Failed to initialize logging system: %s", err.Error()))
		}
	}
}

// RootCmd skydive root command
var RootCmd = &cobra.Command{
	Use:          "skydive [sub]",
	Short:        "Skydive",
	SilenceUsage: true,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		LoadConfiguration()
	},
}

func init() {
	RootCmd.PersistentFlags().StringArrayVarP(&cfgFiles, "conf", "c", []string{}, "location of Skydive agent config files")
	RootCmd.PersistentFlags().StringVarP(&cfgBackend, "config-backend", "b", "file", "configuration backend (defaults to file)")
	RootCmd.AddCommand(VersionCmd)
	RootCmd.AddCommand(Agent)
	RootCmd.AddCommand(Analyzer)
	RootCmd.AddCommand(Client)
	RootCmd.AddCommand(AllInOne)
}
<commit_msg>cmd: do not panic on config read error<commit_after>/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

package cmd

import (
	"fmt"
	"os"

	"github.com/skydive-project/skydive/config"
	"github.com/skydive-project/skydive/logging"
	"github.com/spf13/cobra"
)

var (
	cfgFiles   []string
	cfgBackend string
)

// LoadConfiguration from a configuration file
func LoadConfiguration() {
	if len(cfgFiles) != 0 {
		if err := config.InitConfig(cfgBackend, cfgFiles); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to initialize config: %s", err.Error())
			os.Exit(1)
		}

		if err := logging.InitLogging(); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to initialize logging system: %s", err.Error())
			os.Exit(1)
		}
	}
}

// RootCmd skydive root command
var RootCmd = &cobra.Command{
	Use:          "skydive [sub]",
	Short:        "Skydive",
	SilenceUsage: true,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		LoadConfiguration()
	},
}

func init() {
	RootCmd.PersistentFlags().StringArrayVarP(&cfgFiles, "conf", "c", []string{}, "location of Skydive agent config files")
	RootCmd.PersistentFlags().StringVarP(&cfgBackend, "config-backend", "b", "file", "configuration backend (defaults to file)")
	RootCmd.AddCommand(VersionCmd)
	RootCmd.AddCommand(Agent)
	RootCmd.AddCommand(Analyzer)
	RootCmd.AddCommand(Client)
	RootCmd.AddCommand(AllInOne)
}
<|endoftext|>


<commit_before>/*

f is a tiny command line too to interact with BlobStash filetree extension.

Support BlobFS natively.

*/
package main

import (
	"flag"
	"fmt"
	"os"
	_ "syscall"

	"github.com/kuba--/xattr"
	"github.com/tsileo/blobstash/client/blobstore"
)

// TODO(tsileo): zsh autocomplete support

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: f [options] [<path>]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func getRef(path string) (string, bool) {
	ref, _ := xattr.Getxattr(path, "ref")
	if len(ref) > 0 {
		return string(ref), true
	}
	return "", false
}

func getLink(blobstore *blobstore.BlobStore, host, ref string) (string, error) {
	resp, err := blobstore.Client().DoReq("HEAD", fmt.Sprintf("/api/ext/filetree/v1/node/%s?bewit=1", ref), nil, nil)
	if err != nil {
		panic(err)
	}
	// fmt.Printf("resp:%+v", resp)
	if resp.StatusCode != 200 {
		panic("failed to fetch Hawk key")
	}
	if resp.Header.Get("Blobstash-Filetree-Public") == "1" {
		return fmt.Sprintf("%s%s\n", host, resp.Header.Get("Blobstash-Filetree-Public-Path")), nil
	}
	return fmt.Sprintf("%s%s\n", host, resp.Header.Get("Blobstash-Filetree-Semiprivate-Path")), nil
}

func main() {
	// var ns string
	// var comment string
	// var save bool
	// flag.StringVar(&ns, "ns", "", "Optional namespace (for upload)")
	// flag.StringVar(&comment, "comment", "", "Optional comment (for upload)")
	// flag.BoolVar(&save, "save", false, "Save the hash in the log (for upload)")
	// TODO(tsileo):
	// Store the latest uploaded blob as feature? log the uploaded? with an optional comment (import vkv)
	// sharing feature (with a blob app to register?)
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 {
		usage()
	}

	opts := blobstore.DefaultOpts()
	host := "http://localhost:8050"
	if h := os.Getenv("BLOBSTASH_API_HOST"); h != "" {
		host = h
	}
	opts.SetHost(host, os.Getenv("BLOBSTASH_API_KEY"))
	blobstore := blobstore.New(opts)

	// TODO(tsileo): an "extra" sub command
	path := flag.Arg(0)
	ref, _ := getRef(path)
	fmt.Printf("%s\n", ref)
	if ref == "" {
		fmt.Printf("Not inside a BlobFS\n")
		os.Exit(1)
	}

	link, err := getLink(blobstore, host, ref)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", link)

	// switch flag.Arg(0) {
	// case "extra":
	// 	if flag.NArg() == 1 {
	// 		fmt.Printf("get")
	// 	}
	// 	switch flag.Arg(1) {
	// 	case "get":
	// 		fmt.Printf("extra get")
	// 	}
	// }
	// opts := blobstore.DefaultOpts()
	// opts.SetHost(os.Getenv("BLOBSTASH_API_HOST"), os.Getenv("BLOBSTASH_API_KEY"))
	// blobstore := blobstore.New(opts)
	// if flag.Arg(0) == "-" {
	// blob, err := blobstore.Get(flag.Arg(0))
	os.Exit(0)
}
<commit_msg>cmd/f: fixed import<commit_after>/*

f is a tiny command line too to interact with BlobStash filetree extension.

Support BlobFS natively.

*/
package main

import (
	"flag"
	"fmt"
	"os"
	_ "syscall"

	"github.com/kuba--/xattr"
	_ "golang.org/x/net/context"

	"github.com/tsileo/blobstash/pkg/client/blobstore"
)

// TODO(tsileo): zsh autocomplete support

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: f [options] [<path>]\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func getRef(path string) (string, bool) {
	ref, _ := xattr.Getxattr(path, "ref")
	if len(ref) > 0 {
		return string(ref), true
	}
	return "", false
}

func getLink(blobstore *blobstore.BlobStore, host, ref string) (string, error) {
	resp, err := blobstore.Client().DoReq("HEAD", fmt.Sprintf("/api/ext/filetree/v1/node/%s?bewit=1", ref), nil, nil)
	if err != nil {
		panic(err)
	}
	// fmt.Printf("resp:%+v", resp)
	if resp.StatusCode != 200 {
		panic("failed to fetch Hawk key")
	}
	if resp.Header.Get("Blobstash-Filetree-Public") == "1" {
		return fmt.Sprintf("%s%s\n", host, resp.Header.Get("Blobstash-Filetree-Public-Path")), nil
	}
	return fmt.Sprintf("%s%s\n", host, resp.Header.Get("Blobstash-Filetree-Semiprivate-Path")), nil
}

func main() {
	// var ns string
	// var comment string
	// var save bool
	// flag.StringVar(&ns, "ns", "", "Optional namespace (for upload)")
	// flag.StringVar(&comment, "comment", "", "Optional comment (for upload)")
	// flag.BoolVar(&save, "save", false, "Save the hash in the log (for upload)")
	// TODO(tsileo):
	// Store the latest uploaded blob as feature? log the uploaded? with an optional comment (import vkv)
	// sharing feature (with a blob app to register?)
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 {
		usage()
	}

	opts := blobstore.DefaultOpts()
	host := "http://localhost:8050"
	if h := os.Getenv("BLOBSTASH_API_HOST"); h != "" {
		host = h
	}
	opts.SetHost(host, os.Getenv("BLOBSTASH_API_KEY"))
	blobstore := blobstore.New(opts)

	// TODO(tsileo): an "extra" sub command
	path := flag.Arg(0)
	ref, _ := getRef(path)
	fmt.Printf("%s\n", ref)
	if ref == "" {
		fmt.Printf("Not inside a BlobFS\n")
		os.Exit(1)
	}

	link, err := getLink(blobstore, host, ref)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", link)

	// switch flag.Arg(0) {
	// case "extra":
	// 	if flag.NArg() == 1 {
	// 		fmt.Printf("get")
	// 	}
	// 	switch flag.Arg(1) {
	// 	case "get":
	// 		fmt.Printf("extra get")
	// 	}
	// }
	// opts := blobstore.DefaultOpts()
	// opts.SetHost(os.Getenv("BLOBSTASH_API_HOST"), os.Getenv("BLOBSTASH_API_KEY"))
	// blobstore := blobstore.New(opts)
	// if flag.Arg(0) == "-" {
	// blob, err := blobstore.Get(flag.Arg(0))
	os.Exit(0)
}
<|endoftext|>


<commit_before>package pstoremem

import (
	"testing"

	pstore "github.com/libp2p/go-libp2p-core/peerstore"
	pt "github.com/libp2p/go-libp2p-peerstore/test"
)

func TestInMemoryPeerstore(t *testing.T) {
	pt.TestPeerstore(t, func() (pstore.Peerstore, func()) {
		return NewPeerstore(), nil
	})
}

func TestInMemoryAddrBook(t *testing.T) {
	pt.TestAddrBook(t, func() (pstore.AddrBook, func()) {
		return NewAddrBook(), nil
	})
}

func TestInMemoryKeyBook(t *testing.T) {
	pt.TestKeyBook(t, func() (pstore.KeyBook, func()) {
		return NewKeyBook(), nil
	})
}

func BenchmarkInMemoryPeerstore(b *testing.B) {
	pt.BenchmarkPeerstore(b, func() (pstore.Peerstore, func()) {
		return NewPeerstore(), nil
	}, "InMem")
}

func BenchmarkInMemoryKeyBook(b *testing.B) {
	pt.BenchmarkKeyBook(b, func() (pstore.KeyBook, func()) {
		return NewKeyBook(), nil
	})
}
<commit_msg>test: check for goroutine leaks when closing the peerstore<commit_after>package pstoremem

import (
	"testing"

	pstore "github.com/libp2p/go-libp2p-core/peerstore"
	pt "github.com/libp2p/go-libp2p-peerstore/test"

	"go.uber.org/goleak"
)

func TestFuzzInMemoryPeerstore(t *testing.T) {
	// Just create and close a bunch of peerstores. If this leaks, we'll
	// catch it in the leak check below.
	for i := 0; i < 100; i++ {
		ps := NewPeerstore()
		ps.Close()
	}
}

func TestInMemoryPeerstore(t *testing.T) {
	pt.TestPeerstore(t, func() (pstore.Peerstore, func()) {
		ps := NewPeerstore()
		return ps, func() { ps.Close() }
	})
}

func TestInMemoryAddrBook(t *testing.T) {
	pt.TestAddrBook(t, func() (pstore.AddrBook, func()) {
		ps := NewPeerstore()
		return ps, func() { ps.Close() }
	})
}

func TestInMemoryKeyBook(t *testing.T) {
	pt.TestKeyBook(t, func() (pstore.KeyBook, func()) {
		ps := NewPeerstore()
		return ps, func() { ps.Close() }
	})
}

func BenchmarkInMemoryPeerstore(b *testing.B) {
	pt.BenchmarkPeerstore(b, func() (pstore.Peerstore, func()) {
		ps := NewPeerstore()
		return ps, func() { ps.Close() }
	}, "InMem")
}

func BenchmarkInMemoryKeyBook(b *testing.B) {
	pt.BenchmarkKeyBook(b, func() (pstore.KeyBook, func()) {
		ps := NewPeerstore()
		return ps, func() { ps.Close() }
	})
}

func TestMain(m *testing.M) {
	goleak.VerifyTestMain(
		m,
		goleak.IgnoreTopFunction("github.com/ipfs/go-log/writer.(*MirrorWriter).logRoutine"),
	)
}
<|endoftext|>


<commit_before>package cmd

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/b4b4r07/gist/pkg/gist"
	"github.com/caarlos0/spin"
	"github.com/manifoldco/promptui"
	"github.com/spf13/cobra"
)

type newCmd struct {
	meta

	private bool
}

// newNewCmd creates a new new command
func newNewCmd() *cobra.Command {
	c := &newCmd{}

	newCmd := &cobra.Command{
		Use:                   "new",
		Short:                 "Create gist file",
		Aliases:               []string{},
		DisableFlagsInUseLine: true,
		SilenceUsage:          true,
		SilenceErrors:         true,
		Args:                  cobra.MinimumNArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			// TODO:
			// new command doesn't need to fetch all repos data
			// so divide this function to init and load.
			if err := c.meta.init(args); err != nil {
				return err
			}
			return c.run(args)
		},
	}

	f := newCmd.Flags()
	f.BoolVarP(&c.private, "private", "p", false, "make private")

	return newCmd
}

func (c *newCmd) run(args []string) error {
	validate := func(input string) error {
		if len(input) < 3 {
			return errors.New("Filename must have more than 3 characters")
		}
		return nil
	}

	var files []gist.File
	for _, arg := range args {
		f, err := os.Open(arg)
		if err != nil {
			return err
		}
		defer f.Close()
		b, err := ioutil.ReadAll(f)
		if err != nil {
			return err
		}
		prompt := promptui.Prompt{
			Label:     "Filename",
			Validate:  validate,
			AllowEdit: true,
			Default:   filepath.Base(arg),
		}
		name, err := prompt.Run()
		if err != nil {
			return err
		}
		files = append(files, gist.File{
			Name:    name,
			Content: string(b),
		})
	}

	prompt := promptui.Prompt{Label: "Description"}
	desc, err := prompt.Run()
	if err != nil {
		return err
	}

	s := spin.New("%s Creating page...")
	s.Start()
	defer s.Stop()

	url, err := c.gist.Create(gist.Page{
		Files:       files,
		Description: desc,
		Public:      !c.private,
	})
	if err != nil {
		return err
	}

	s.Stop()
	fmt.Println(url)

	c.cache.Delete()
	return nil
}
<commit_msg>Accept no arg in new command<commit_after>package cmd

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/b4b4r07/gist/pkg/gist"
	"github.com/b4b4r07/gist/pkg/shell"
	"github.com/caarlos0/spin"
	"github.com/manifoldco/promptui"
	"github.com/spf13/cobra"
)

type newCmd struct {
	meta

	private bool

	validator promptui.ValidateFunc
}

// newNewCmd creates a new new command
func newNewCmd() *cobra.Command {
	c := &newCmd{}

	newCmd := &cobra.Command{
		Use:                   "new",
		Short:                 "Create gist file",
		Aliases:               []string{},
		DisableFlagsInUseLine: true,
		SilenceUsage:          true,
		SilenceErrors:         true,
		RunE: func(cmd *cobra.Command, args []string) error {
			// TODO:
			// new command doesn't need to fetch all repos data
			// so divide this function to init and load.
			if err := c.meta.init(args); err != nil {
				return err
			}
			return c.run(args)
		},
	}

	f := newCmd.Flags()
	f.BoolVarP(&c.private, "private", "p", false, "make private")

	return newCmd
}

func (c *newCmd) run(args []string) error {
	c.validator = func(input string) error {
		if len(input) == 0 {
			return errors.New("Filename must have more than 1 characters")
		}
		return nil
	}

	var files []gist.File
	var err error

	switch len(args) {
	case 0:
		files, err = c.withNoArg()
	default:
		files, err = c.withArgs(args)
	}
	if err != nil {
		return err
	}

	prompt := promptui.Prompt{
		Label:    "Description",
		Validate: c.validator,
	}
	desc, err := prompt.Run()
	if err != nil {
		return err
	}

	s := spin.New("%s Creating page...")
	s.Start()
	defer s.Stop()

	url, err := c.gist.Create(gist.Page{
		Files:       files,
		Description: desc,
		Public:      !c.private,
	})
	if err != nil {
		return err
	}

	s.Stop()
	fmt.Println(url)

	c.cache.Delete()
	return nil
}

func (c *newCmd) withNoArg() ([]gist.File, error) {
	var files []gist.File
	tmpfile, err := ioutil.TempFile("", "gist")
	if err != nil {
		return files, err
	}
	defer os.Remove(tmpfile.Name())
	defer tmpfile.Close()
	vim := shell.New("vim", tmpfile.Name())
	if err := vim.Run(context.Background()); err != nil {
		return files, err
	}
	content, err := ioutil.ReadFile(tmpfile.Name())
	if err != nil {
		return files, err
	}
	prompt := promptui.Prompt{
		Label:    "Filename",
		Validate: c.validator,
	}
	name, err := prompt.Run()
	if err != nil {
		return files, err
	}
	files = append(files, gist.File{
		Name:    name,
		Content: string(content),
	})
	return files, nil
}

func (c *newCmd) withArgs(args []string) ([]gist.File, error) {
	var files []gist.File
	for _, arg := range args {
		f, err := os.Open(arg)
		if err != nil {
			return files, err
		}
		defer f.Close()
		content, err := ioutil.ReadAll(f)
		if err != nil {
			return files, err
		}
		prompt := promptui.Prompt{
			Label:     "Filename",
			Validate:  c.validator,
			AllowEdit: true,
			Default:   filepath.Base(arg),
		}
		name, err := prompt.Run()
		if err != nil {
			return files, err
		}
		files = append(files, gist.File{
			Name:    name,
			Content: string(content),
		})
	}
	return files, nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	awscloud "github.com/wallix/awless/cloud/aws"
	"github.com/wallix/awless/database"
	"github.com/wallix/awless/script"
	"github.com/wallix/awless/script/ast"
	"github.com/wallix/awless/script/driver/aws"
)

func init() {
	RootCmd.AddCommand(runCmd)
	for action, entities := range aws.DriverSupportedActions() {
		RootCmd.AddCommand(
			createDriverCommands(action, entities),
		)
	}
}

var runCmd = &cobra.Command{
	Use:   "run",
	Short: "Run an awless script file given as the only argument. Ex: awless run mycloud.awless",

	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) != 1 {
			return errors.New("missing awless script file path")
		}

		content, err := ioutil.ReadFile(args[0])
		if err != nil {
			return err
		}

		scrpt, err := script.Parse(string(content))
		exitOn(err)

		return runScript(scrpt)
	},
}

func runScript(scrpt *script.Script) error {
	defaults, err := database.Current.GetDefaults()
	exitOn(err)

	scrpt.ResolveTemplate(defaults)

	prompt := func(question string) interface{} {
		var resp string
		fmt.Printf("%s ? ", question)
		_, err := fmt.Scanln(&resp)
		exitOn(err)

		return resp
	}
	scrpt.InteractiveResolveTemplate(prompt)

	awsDriver := aws.NewDriver(awscloud.InfraService)
	if verboseFlag {
		awsDriver.SetLogger(log.New(os.Stdout, "[aws driver] ", log.Ltime))
	}

	_, err = scrpt.Compile(awsDriver)
	exitOn(err)

	fmt.Println()
	fmt.Println(scrpt)
	fmt.Println()
	fmt.Print("Run compiled script above? (y/n): ")
	var yesorno string
	_, err = fmt.Scanln(&yesorno)

	if strings.TrimSpace(yesorno) == "y" {
		executedScript, err := scrpt.Run(awsDriver)
		exitOn(err)

		fmt.Println()
		green := color.New(color.FgGreen).SprintFunc()
		for _, stat := range executedScript.Statements {
			fmt.Printf("%s -> %s\n", stat, green("DONE"))
		}
	}

	return nil
}

func createDriverCommands(action string, entities []string) *cobra.Command {
	actionCmd := &cobra.Command{
		Use:   action,
		Short: fmt.Sprintf("Allow to %s: %v", action, strings.Join(entities, ", ")),
	}

	for _, entity := range entities {
		actionCmd.AddCommand(
			&cobra.Command{
				Use:   entity,
				Short: fmt.Sprintf("Use it to %s a %s", action, entity),

				RunE: func(cmd *cobra.Command, args []string) error {
					text := fmt.Sprintf("%s %s %s", action, entity, strings.Join(args, " "))

					scrpt, err := script.Parse(text)
					exitOn(err)

					expr, ok := scrpt.Statements[0].(*ast.ExpressionNode)
					if !ok {
						return errors.New("Expecting an script expression not a script declaration")
					}

					templName := fmt.Sprintf("%s%s", expr.Action, expr.Entity)
					templ, ok := aws.AWSDriverTemplates[templName]
					if !ok {
						exitOn(errors.New("command unsupported on inline mode"))
					}

					if scrpt, err = script.Parse(templ); err != nil {
						exitOn(fmt.Errorf("internal error parsing known template\n`%s`\n%s", templ, err))
					}

					scrpt.ResolveTemplate(expr.Params)

					return runScript(scrpt)
				},
			},
		)
	}

	return actionCmd
}
<commit_msg>Flexible params naming for awless scripts commands<commit_after>package cmd

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	awscloud "github.com/wallix/awless/cloud/aws"
	"github.com/wallix/awless/database"
	"github.com/wallix/awless/script"
	"github.com/wallix/awless/script/ast"
	"github.com/wallix/awless/script/driver/aws"
)

func init() {
	RootCmd.AddCommand(runCmd)
	for action, entities := range aws.DriverSupportedActions() {
		RootCmd.AddCommand(
			createDriverCommands(action, entities),
		)
	}
}

var runCmd = &cobra.Command{
	Use:   "run",
	Short: "Run an awless script file given as the only argument. Ex: awless run mycloud.awless",

	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) != 1 {
			return errors.New("missing awless script file path")
		}

		content, err := ioutil.ReadFile(args[0])
		if err != nil {
			return err
		}

		scrpt, err := script.Parse(string(content))
		exitOn(err)

		return runScript(scrpt)
	},
}

func runScript(scrpt *script.Script) error {
	defaults, err := database.Current.GetDefaults()
	exitOn(err)

	scrpt.ResolveTemplate(defaults)

	prompt := func(question string) interface{} {
		var resp string
		fmt.Printf("%s ? ", question)
		_, err := fmt.Scanln(&resp)
		exitOn(err)

		return resp
	}
	scrpt.InteractiveResolveTemplate(prompt)

	awsDriver := aws.NewDriver(awscloud.InfraService)
	if verboseFlag {
		awsDriver.SetLogger(log.New(os.Stdout, "[aws driver] ", log.Ltime))
	}

	_, err = scrpt.Compile(awsDriver)
	exitOn(err)

	green := color.New(color.FgGreen).SprintFunc()

	fmt.Println()
	fmt.Printf("%s\n", green(scrpt))
	fmt.Println()
	fmt.Print("Run compiled script above? (y/n): ")
	var yesorno string
	_, err = fmt.Scanln(&yesorno)

	if strings.TrimSpace(yesorno) == "y" {
		executedScript, err := scrpt.Run(awsDriver)
		exitOn(err)

		fmt.Println()
		for _, stat := range executedScript.Statements {
			fmt.Printf("%s -> %s\n", stat, green("DONE"))
		}
	}

	return nil
}

func createDriverCommands(action string, entities []string) *cobra.Command {
	actionCmd := &cobra.Command{
		Use:   action,
		Short: fmt.Sprintf("Allow to %s: %v", action, strings.Join(entities, ", ")),
	}

	for _, entity := range entities {
		actionCmd.AddCommand(
			&cobra.Command{
				Use:   entity,
				Short: fmt.Sprintf("Use it to %s a %s", action, entity),

				RunE: func(cmd *cobra.Command, args []string) error {
					text := fmt.Sprintf("%s %s %s", action, entity, strings.Join(args, " "))

					scrpt, err := script.Parse(text)
					exitOn(err)

					expr, ok := scrpt.Statements[0].(*ast.ExpressionNode)
					if !ok {
						return errors.New("Expecting an script expression not a script declaration")
					}

					templName := fmt.Sprintf("%s%s", expr.Action, expr.Entity)
					templ, ok := aws.AWSDriverTemplates[templName]
					if !ok {
						exitOn(errors.New("command unsupported on inline mode"))
					}

					if scrpt, err = script.Parse(templ); err != nil {
						exitOn(fmt.Errorf("internal error parsing known template\n`%s`\n%s", templ, err))
					}

					for k, v := range expr.Params {
						if !strings.Contains(k, ".") {
							expr.Params[fmt.Sprintf("%s.%s", expr.Entity, k)] = v
							delete(expr.Params, k)
						}
					}

					scrpt.ResolveTemplate(expr.Params)

					return runScript(scrpt)
				},
			},
		)
	}

	return actionCmd
}
<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package handler

import (
	"sync"
	"testing"

	pb_broker "github.com/TheThingsNetwork/ttn/api/broker"
	"github.com/TheThingsNetwork/ttn/core"
	"github.com/TheThingsNetwork/ttn/core/handler/application"
	"github.com/TheThingsNetwork/ttn/core/handler/device"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/TheThingsNetwork/ttn/mqtt"
	. "github.com/TheThingsNetwork/ttn/utils/testing"
	. "github.com/smartystreets/assertions"
)

func TestEnqueueDownlink(t *testing.T) {
	a := New(t)
	appID := "app1"
	devID := "dev1"
	h := &handler{
		Component: &core.Component{Ctx: GetLogger(t, "TestEnqueueDownlink")},
		devices:   device.NewDeviceStore(),
	}
	err := h.EnqueueDownlink(&mqtt.DownlinkMessage{
		AppID: appID,
		DevID: devID,
	})
	a.So(err, ShouldNotBeNil)
	h.devices.Set(&device.Device{
		AppID: appID,
		DevID: devID,
	})
	err = h.EnqueueDownlink(&mqtt.DownlinkMessage{
		AppID: appID,
		DevID: devID,
		Fields: map[string]interface{}{
			"string": "hello!",
			"int":    42,
			"bool":   true,
		},
	})
	a.So(err, ShouldBeNil)
	dev, _ := h.devices.Get(appID, devID)
	a.So(dev.NextDownlink, ShouldNotBeEmpty)
	a.So(dev.NextDownlink.Fields, ShouldHaveLength, 3)
}

func TestHandleDownlink(t *testing.T) {
	a := New(t)
	var err error
	var wg sync.WaitGroup
	appID := "app2"
	devID := "dev2"
	appEUI := types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8})
	devEUI := types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8})
	h := &handler{
		Component:    &core.Component{Ctx: GetLogger(t, "TestHandleDownlink")},
		devices:      device.NewDeviceStore(),
		applications: application.NewApplicationStore(),
		downlink:     make(chan *pb_broker.DownlinkMessage),
	}
	// Neither payload nor Fields provided : ERROR
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		AppID: appID,
		DevID: devID,
	}, &pb_broker.DownlinkMessage{
		AppEui: &appEUI,
		DevEui: &devEUI,
	})
	a.So(err, ShouldNotBeNil)

	h.devices.Set(&device.Device{
		AppID: appID,
		DevID: devID,
	})
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		AppID: appID,
		DevID: devID,
	}, &pb_broker.DownlinkMessage{
		AppEui:  &appEUI,
		DevEui:  &devEUI,
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
	})
	a.So(err, ShouldBeNil)

	// Payload provided
	wg.Add(1)
	go func() {
		dl := <-h.downlink
		a.So(dl.Payload, ShouldNotBeEmpty)
		wg.Done()
	}()
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		AppID:   appID,
		DevID:   devID,
		Payload: []byte{0xAA, 0xBC},
	}, &pb_broker.DownlinkMessage{
		AppEui:  &appEUI,
		DevEui:  &devEUI,
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
	})
	a.So(err, ShouldBeNil)
	wg.Wait()

	// Both Payload and Fields provided
	h.applications.Set(&application.Application{
		AppID: appID,
		Encoder: `function (payload){
	  		return [96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0]
			}`,
	})
	jsonFields := map[string]interface{}{"temperature": 11}
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		FPort:   1,
		AppID:   appID,
		DevID:   devID,
		Fields:  jsonFields,
		Payload: []byte{0xAA, 0xBC},
	}, &pb_broker.DownlinkMessage{
		AppEui: &appEUI,
		DevEui: &devEUI,
	})
	a.So(err, ShouldNotBeNil)

	// JSON Fields provided
	wg.Add(1)
	go func() {
		dl := <-h.downlink
		a.So(dl.Payload, ShouldNotBeEmpty)
		wg.Done()
	}()
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		FPort:  1,
		AppID:  appID,
		DevID:  devID,
		Fields: jsonFields,
	}, &pb_broker.DownlinkMessage{
		AppEui: &appEUI,
		DevEui: &devEUI,
	})
	a.So(err, ShouldBeNil)
	wg.Wait()
}
<commit_msg>Fix the other test for d53cdabe<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package handler

import (
	"testing"
	"time"

	pb_broker "github.com/TheThingsNetwork/ttn/api/broker"
	"github.com/TheThingsNetwork/ttn/core"
	"github.com/TheThingsNetwork/ttn/core/handler/application"
	"github.com/TheThingsNetwork/ttn/core/handler/device"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/TheThingsNetwork/ttn/mqtt"
	. "github.com/TheThingsNetwork/ttn/utils/testing"
	. "github.com/smartystreets/assertions"
)

func TestEnqueueDownlink(t *testing.T) {
	a := New(t)
	appID := "app1"
	devID := "dev1"
	h := &handler{
		Component: &core.Component{Ctx: GetLogger(t, "TestEnqueueDownlink")},
		devices:   device.NewDeviceStore(),
	}
	err := h.EnqueueDownlink(&mqtt.DownlinkMessage{
		AppID: appID,
		DevID: devID,
	})
	a.So(err, ShouldNotBeNil)
	h.devices.Set(&device.Device{
		AppID: appID,
		DevID: devID,
	})
	err = h.EnqueueDownlink(&mqtt.DownlinkMessage{
		AppID: appID,
		DevID: devID,
		Fields: map[string]interface{}{
			"string": "hello!",
			"int":    42,
			"bool":   true,
		},
	})
	a.So(err, ShouldBeNil)
	dev, _ := h.devices.Get(appID, devID)
	a.So(dev.NextDownlink, ShouldNotBeEmpty)
	a.So(dev.NextDownlink.Fields, ShouldHaveLength, 3)
}

func TestHandleDownlink(t *testing.T) {
	a := New(t)
	var err error
	var wg WaitGroup
	appID := "app2"
	devID := "dev2"
	appEUI := types.AppEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8})
	devEUI := types.DevEUI([8]byte{1, 2, 3, 4, 5, 6, 7, 8})
	h := &handler{
		Component:    &core.Component{Ctx: GetLogger(t, "TestHandleDownlink")},
		devices:      device.NewDeviceStore(),
		applications: application.NewApplicationStore(),
		downlink:     make(chan *pb_broker.DownlinkMessage),
	}
	// Neither payload nor Fields provided : ERROR
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		AppID: appID,
		DevID: devID,
	}, &pb_broker.DownlinkMessage{
		AppEui: &appEUI,
		DevEui: &devEUI,
	})
	a.So(err, ShouldNotBeNil)

	h.devices.Set(&device.Device{
		AppID: appID,
		DevID: devID,
	})
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		AppID: appID,
		DevID: devID,
	}, &pb_broker.DownlinkMessage{
		AppEui:  &appEUI,
		DevEui:  &devEUI,
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
	})
	a.So(err, ShouldBeNil)

	// Payload provided
	wg.Add(1)
	go func() {
		dl := <-h.downlink
		a.So(dl.Payload, ShouldNotBeEmpty)
		wg.Done()
	}()
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		AppID:   appID,
		DevID:   devID,
		Payload: []byte{0xAA, 0xBC},
	}, &pb_broker.DownlinkMessage{
		AppEui:  &appEUI,
		DevEui:  &devEUI,
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
	})
	a.So(err, ShouldBeNil)
	wg.WaitFor(100 * time.Millisecond)

	// Both Payload and Fields provided
	h.applications.Set(&application.Application{
		AppID: appID,
		Encoder: `function (payload){
	  		return [96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0]
			}`,
	})
	jsonFields := map[string]interface{}{"temperature": 11}
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		FPort:   1,
		AppID:   appID,
		DevID:   devID,
		Fields:  jsonFields,
		Payload: []byte{0xAA, 0xBC},
	}, &pb_broker.DownlinkMessage{
		AppEui:  &appEUI,
		DevEui:  &devEUI,
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
	})
	a.So(err, ShouldNotBeNil)

	// JSON Fields provided
	wg.Add(1)
	go func() {
		dl := <-h.downlink
		a.So(dl.Payload, ShouldNotBeEmpty)
		wg.Done()
	}()
	err = h.HandleDownlink(&mqtt.DownlinkMessage{
		FPort:  1,
		AppID:  appID,
		DevID:  devID,
		Fields: jsonFields,
	}, &pb_broker.DownlinkMessage{
		AppEui:  &appEUI,
		DevEui:  &devEUI,
		Payload: []byte{96, 4, 3, 2, 1, 0, 1, 0, 1, 0, 0, 0, 0},
	})
	a.So(err, ShouldBeNil)
	wg.WaitFor(100 * time.Millisecond)
}
<|endoftext|>


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

package optigo

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type actionType int

const (
	atINCREMENT actionType = iota
	atAPPEND
	atASSIGN
	atMAP
)

type dataType int

const (
	dtSTRING dataType = iota
	dtINTEGER
	dtFLOAT
	dtBOOLEAN
)

type option struct {
	name     string
	unary    bool
	dest     reflect.Value
	action   actionType
	dataType dataType
}

type keyVal struct {
	key string
	val interface{}
}

func (o *option) parseValue(val string) (interface{}, error) {
	var keyval keyVal
	if o.action == atMAP {
		parts := strings.SplitN(val, "=", 2)
		val = parts[1]
		keyval = keyVal{key: parts[0]}
	}

	var parsed interface{}
	switch o.dataType {
	case dtSTRING:
		parsed = val
	case dtINTEGER:
		if i, err := strconv.ParseInt(val, 10, 64); err == nil {
			parsed = i
		} else {
			return nil, err
		}
	case dtFLOAT:
		if f, err := strconv.ParseFloat(val, 64); err == nil {
			parsed = f
		} else {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("Unable to parse value: %s", val)
	}

	if o.action == atMAP {
		keyval.val = parsed
		return keyval, nil
	} else {
		return parsed, nil
	}
}

type actions map[string]option

func parseAction(spec string, dest interface{}, actions map[string]option) error {
	unary := false
	var a actionType
	var t dataType
	if spec[len(spec)-1] == '+' {
		unary = true
		a = atINCREMENT
		spec = spec[0 : len(spec)-1]
	} else if spec[len(spec)-1] == '@' {
		a = atAPPEND
		spec = spec[0 : len(spec)-1]
	} else if spec[len(spec)-2:] == "[]" {
		a = atAPPEND
		spec = spec[0 : len(spec)-2]
	} else if spec[len(spec)-1] == '%' {
		a = atMAP
		spec = spec[0 : len(spec)-1]
	} else if spec[len(spec)-2:] == "{}" {
		a = atMAP
		spec = spec[0 : len(spec)-2]
	} else {
		a = atASSIGN
	}

	switch spec[len(spec)-2:] {
	case "=s":
		t = dtSTRING
		spec = spec[0 : len(spec)-2]
	case "=i":
		t = dtINTEGER
		spec = spec[0 : len(spec)-2]
	case "=f":
		t = dtFLOAT
		spec = spec[0 : len(spec)-2]
	default:
		if a == atINCREMENT {
			t = dtINTEGER
		} else {
			t = dtBOOLEAN
		}
		unary = true
	}

	if unary && a == atAPPEND {
		return fmt.Errorf("invalid spec, using @ to parse repeated options, but not specifying type with either =i =s or =f: %s", spec)
	}

	optionNames := strings.Split(spec, "|")
	name := optionNames[len(optionNames)-1]
	for _, opt := range optionNames {
		var dashName string
		if len(opt) == 1 {
			dashName = "-" + opt
		} else {
			dashName = "--" + opt
		}
		if _, ok := actions[dashName]; ok {
			return fmt.Errorf("invalid option spec: %s is not unique from %s", dashName, spec)
		}
		actions[dashName] = option{name, unary, reflect.ValueOf(dest), a, t}
	}
	return nil
}

func increment(val reflect.Value) reflect.Value {
	return reflect.ValueOf(val.Int() + 1)
}

func push(arr reflect.Value, val interface{}) reflect.Value {
	rVal := reflect.ValueOf(val)
	if rVal.Type() != arr.Type().Elem() {
		// The value type is not the same as the array value type
		// so try to convert the passed in value to the array value type
		newValPtr := reflect.New(arr.Type().Elem())
		newValPtr.Elem().Set(rVal.Convert(arr.Type().Elem()))
		rVal = newValPtr.Elem()
	}
	return reflect.Append(arr, rVal)
}

func initResultMap(actions actions) map[string]interface{} {
	results := make(map[string]interface{})
	for _, opt := range actions {
		if opt.unary {
			if opt.action == atINCREMENT {
				results[opt.name] = int64(0)
			} else {
				results[opt.name] = false
			}
		} else {
			if opt.action == atAPPEND {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = make([]string, 0)
				case dtINTEGER:
					results[opt.name] = make([]int64, 0)
				case dtFLOAT:
					results[opt.name] = make([]float64, 0)
				}
			} else if opt.action == atMAP {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = make(map[string]string)
				case dtINTEGER:
					results[opt.name] = make(map[string]int64)
				case dtFLOAT:
					results[opt.name] = make(map[string]float64)
				}
			} else {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = ""
				case dtINTEGER:
					results[opt.name] = int64(0)
				case dtFLOAT:
					results[opt.name] = float64(0)
				}
			}
		}
	}
	return results
}

// OptionParser struct will contain the `Results` and `Args` after
// one of the Process routines is called.  A OptionParser object
// is created with either NewParser or NewDirectAssignParser
type OptionParser struct {
	actions actions
	Results map[string]interface{}
	Args    []string
}

// NewParser generates an OptionParser object from the opts passed in.
// After calling OptionParser.Parser([]string) the option results will
// be stored in OptionParser.Results
func NewParser(opts []string) OptionParser {
	actions := make(actions)
	for _, spec := range opts {
		if err := parseAction(spec, nil, actions); err != nil {
			panic(err)
		}
	}
	results := initResultMap(actions)
	return OptionParser{actions, results, nil}
}

// NewDirectAssignParser generates an OptionParser object from the `opts` passed in.
// After calling OptionParser.Parser([]string) the options will be assigned directly
// to the references passed in `opts`.
func NewDirectAssignParser(opts map[string]interface{}) OptionParser {
	actions := make(actions)
	for spec, ref := range opts {
		if err := parseAction(spec, ref, actions); err != nil {
			panic(err)
		}
	}
	return OptionParser{actions, nil, nil}
}

// ProcessAll will parse all arguments in args.  If there are any
// arguments in args that start with '-' and are not known
// options then an error will be returned.  Any non-options will
// be available in OptionParser.Args.
func (o *OptionParser) ProcessAll(args []string) error {
	err := o.ProcessSome(args)
	if err != nil {
		return err
	} else {
		for _, opt := range o.Args {
			if opt[0] == '-' {
				return fmt.Errorf("Unknown option: %s", opt)
			}
		}
	}
	return nil
}

// ProcessSome will parse all known arguments in args.  Any non-options
// and unknown options will be available in OPtionParser.Args.  This
// can be used to implement multple pass options parsing, for example
// perhaps sub-commands options are parsed seperately from global options.
func (o *OptionParser) ProcessSome(args []string) error {
	o.Args = make([]string, 0)
	for len(args) > 0 {
		if args[0] == "--" {
			o.Args = append(o.Args, args[1:]...)
			return nil
		}

		var err error
		if opt, ok := o.actions[args[0]]; ok {
			var value interface{}
			if opt.unary {
				value = true
				args = args[1:]
			} else {
				if len(args) < 2 {
					return fmt.Errorf("missing argument value for option: --%s", opt.name)
				} else {
					if value, err = opt.parseValue(args[1]); err != nil {
						return err
					}
				}
				args = args[2:]
			}
			o.setParsedOption(opt, value)
		} else {
			if args[0][0] == '-' {
				var arg, val string
				if args[0][1] != '-' {
					arg = args[0][0:2]
					val = args[0][2:]
				} else {
					ix := strings.Index(args[0], "=")
					if ix != -1 {
						arg = args[0][0:ix]
						val = args[0][ix+1:]
					}
				}
				if opt, ok := o.actions[arg]; ok {
					var value interface{} = true
					if len(val) <= 0 {
						return fmt.Errorf("missing argument value for option: --%s", opt.name)
					} else {
						if value, err = opt.parseValue(val); err != nil {
							return err
						}
					}
					o.setParsedOption(opt, value)
				} else {
					o.Args = append(o.Args, args[0])
				}
				args = args[1:]
			} else {
				o.Args = append(o.Args, args[0])
				args = args[1:]
			}
		}
	}
	return nil
}

func (o *OptionParser) setParsedOption(opt option, value interface{}) {
	if opt.dest.IsValid() {
		switch opt.action {
		case atINCREMENT:
			opt.dest.Elem().Set(increment(opt.dest.Elem()))
		case atAPPEND:
			opt.dest.Elem().Set(push(opt.dest.Elem(), value))
		case atMAP:
			kv := value.(keyVal)
			opt.dest.Elem().SetMapIndex(reflect.ValueOf(kv.key), reflect.ValueOf(kv.val))
		case atASSIGN:
			if opt.dest.Kind() == reflect.Func {
				t := reflect.TypeOf(opt.dest.Interface())
				var cbArgs []reflect.Value
				if t.NumIn() == 1 {
					cbArgs = make([]reflect.Value, 1)
					cbArgs[0] = reflect.ValueOf(value)
				} else if t.NumIn() == 2 {
					cbArgs = make([]reflect.Value, 2)
					cbArgs[0] = reflect.ValueOf(opt.name)
					cbArgs[1] = reflect.ValueOf(value)
				}
				opt.dest.Call(cbArgs)
			} else {
				opt.dest.Elem().Set(reflect.ValueOf(value))
			}
		}
	} else {
		switch opt.action {
		case atINCREMENT:
			o.Results[opt.name] = increment(reflect.ValueOf(o.Results[opt.name])).Interface()
		case atAPPEND:
			o.Results[opt.name] = push(reflect.ValueOf(o.Results[opt.name]), value).Interface()
		case atMAP:
			kv := value.(keyVal)
			reflect.ValueOf(o.Results[opt.name]).SetMapIndex(reflect.ValueOf(kv.key), reflect.ValueOf(kv.val))
		case atASSIGN:
			o.Results[opt.name] = reflect.ValueOf(value).Interface()
		}
	}
}
<commit_msg>gofmt<commit_after>/*
 *
 *  Copyright 2015 Netflix, Inc.
 *
 *     Licensed under the Apache License, Version 2.0 (the "License");
 *     you may not use this file except in compliance with the License.
 *     You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 *
 */

package optigo

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type actionType int

const (
	atINCREMENT actionType = iota
	atAPPEND
	atASSIGN
	atMAP
)

type dataType int

const (
	dtSTRING dataType = iota
	dtINTEGER
	dtFLOAT
	dtBOOLEAN
)

type option struct {
	name     string
	unary    bool
	dest     reflect.Value
	action   actionType
	dataType dataType
}

type keyVal struct {
	key string
	val interface{}
}

func (o *option) parseValue(val string) (interface{}, error) {
	var keyval keyVal
	if o.action == atMAP {
		parts := strings.SplitN(val, "=", 2)
		val = parts[1]
		keyval = keyVal{key: parts[0]}
	}

	var parsed interface{}
	switch o.dataType {
	case dtSTRING:
		parsed = val
	case dtINTEGER:
		if i, err := strconv.ParseInt(val, 10, 64); err == nil {
			parsed = i
		} else {
			return nil, err
		}
	case dtFLOAT:
		if f, err := strconv.ParseFloat(val, 64); err == nil {
			parsed = f
		} else {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("Unable to parse value: %s", val)
	}

	if o.action == atMAP {
		keyval.val = parsed
		return keyval, nil
	} else {
		return parsed, nil
	}
}

type actions map[string]option

func parseAction(spec string, dest interface{}, actions map[string]option) error {
	unary := false
	var a actionType
	var t dataType
	if spec[len(spec)-1] == '+' {
		unary = true
		a = atINCREMENT
		spec = spec[0 : len(spec)-1]
	} else if spec[len(spec)-1] == '@' {
		a = atAPPEND
		spec = spec[0 : len(spec)-1]
	} else if spec[len(spec)-2:] == "[]" {
		a = atAPPEND
		spec = spec[0 : len(spec)-2]
	} else if spec[len(spec)-1] == '%' {
		a = atMAP
		spec = spec[0 : len(spec)-1]
	} else if spec[len(spec)-2:] == "{}" {
		a = atMAP
		spec = spec[0 : len(spec)-2]
	} else {
		a = atASSIGN
	}

	switch spec[len(spec)-2:] {
	case "=s":
		t = dtSTRING
		spec = spec[0 : len(spec)-2]
	case "=i":
		t = dtINTEGER
		spec = spec[0 : len(spec)-2]
	case "=f":
		t = dtFLOAT
		spec = spec[0 : len(spec)-2]
	default:
		if a == atINCREMENT {
			t = dtINTEGER
		} else {
			t = dtBOOLEAN
		}
		unary = true
	}

	if unary && a == atAPPEND {
		return fmt.Errorf("invalid spec, using @ to parse repeated options, but not specifying type with either =i =s or =f: %s", spec)
	}

	optionNames := strings.Split(spec, "|")
	name := optionNames[len(optionNames)-1]
	for _, opt := range optionNames {
		var dashName string
		if len(opt) == 1 {
			dashName = "-" + opt
		} else {
			dashName = "--" + opt
		}
		if _, ok := actions[dashName]; ok {
			return fmt.Errorf("invalid option spec: %s is not unique from %s", dashName, spec)
		}
		actions[dashName] = option{name, unary, reflect.ValueOf(dest), a, t}
	}
	return nil
}

func increment(val reflect.Value) reflect.Value {
	return reflect.ValueOf(val.Int() + 1)
}

func push(arr reflect.Value, val interface{}) reflect.Value {
	rVal := reflect.ValueOf(val)
	if rVal.Type() != arr.Type().Elem() {
		// The value type is not the same as the array value type
		// so try to convert the passed in value to the array value type
		newValPtr := reflect.New(arr.Type().Elem())
		newValPtr.Elem().Set(rVal.Convert(arr.Type().Elem()))
		rVal = newValPtr.Elem()
	}
	return reflect.Append(arr, rVal)
}

func initResultMap(actions actions) map[string]interface{} {
	results := make(map[string]interface{})
	for _, opt := range actions {
		if opt.unary {
			if opt.action == atINCREMENT {
				results[opt.name] = int64(0)
			} else {
				results[opt.name] = false
			}
		} else {
			if opt.action == atAPPEND {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = make([]string, 0)
				case dtINTEGER:
					results[opt.name] = make([]int64, 0)
				case dtFLOAT:
					results[opt.name] = make([]float64, 0)
				}
			} else if opt.action == atMAP {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = make(map[string]string)
				case dtINTEGER:
					results[opt.name] = make(map[string]int64)
				case dtFLOAT:
					results[opt.name] = make(map[string]float64)
				}
			} else {
				switch opt.dataType {
				case dtSTRING:
					results[opt.name] = ""
				case dtINTEGER:
					results[opt.name] = int64(0)
				case dtFLOAT:
					results[opt.name] = float64(0)
				}
			}
		}
	}
	return results
}

// OptionParser struct will contain the `Results` and `Args` after
// one of the Process routines is called.  A OptionParser object
// is created with either NewParser or NewDirectAssignParser
type OptionParser struct {
	actions actions
	Results map[string]interface{}
	Args    []string
}

// NewParser generates an OptionParser object from the opts passed in.
// After calling OptionParser.Parser([]string) the option results will
// be stored in OptionParser.Results
func NewParser(opts []string) OptionParser {
	actions := make(actions)
	for _, spec := range opts {
		if err := parseAction(spec, nil, actions); err != nil {
			panic(err)
		}
	}
	results := initResultMap(actions)
	return OptionParser{actions, results, nil}
}

// NewDirectAssignParser generates an OptionParser object from the `opts` passed in.
// After calling OptionParser.Parser([]string) the options will be assigned directly
// to the references passed in `opts`.
func NewDirectAssignParser(opts map[string]interface{}) OptionParser {
	actions := make(actions)
	for spec, ref := range opts {
		if err := parseAction(spec, ref, actions); err != nil {
			panic(err)
		}
	}
	return OptionParser{actions, nil, nil}
}

// ProcessAll will parse all arguments in args.  If there are any
// arguments in args that start with '-' and are not known
// options then an error will be returned.  Any non-options will
// be available in OptionParser.Args.
func (o *OptionParser) ProcessAll(args []string) error {
	err := o.ProcessSome(args)
	if err != nil {
		return err
	} else {
		for _, opt := range o.Args {
			if opt[0] == '-' {
				return fmt.Errorf("Unknown option: %s", opt)
			}
		}
	}
	return nil
}

// ProcessSome will parse all known arguments in args.  Any non-options
// and unknown options will be available in OPtionParser.Args.  This
// can be used to implement multple pass options parsing, for example
// perhaps sub-commands options are parsed seperately from global options.
func (o *OptionParser) ProcessSome(args []string) error {
	o.Args = make([]string, 0)
	for len(args) > 0 {
		if args[0] == "--" {
			o.Args = append(o.Args, args[1:]...)
			return nil
		}

		var err error
		if opt, ok := o.actions[args[0]]; ok {
			var value interface{}
			if opt.unary {
				value = true
				args = args[1:]
			} else {
				if len(args) < 2 {
					return fmt.Errorf("missing argument value for option: --%s", opt.name)
				} else {
					if value, err = opt.parseValue(args[1]); err != nil {
						return err
					}
				}
				args = args[2:]
			}
			o.setParsedOption(opt, value)
		} else {
			if args[0][0] == '-' {
				var arg, val string
				if args[0][1] != '-' {
					arg = args[0][0:2]
					val = args[0][2:]
				} else {
					ix := strings.Index(args[0], "=")
					if ix != -1 {
						arg = args[0][0:ix]
						val = args[0][ix+1:]
					}
				}
				if opt, ok := o.actions[arg]; ok {
					var value interface{} = true
					if len(val) <= 0 {
						return fmt.Errorf("missing argument value for option: --%s", opt.name)
					} else {
						if value, err = opt.parseValue(val); err != nil {
							return err
						}
					}
					o.setParsedOption(opt, value)
				} else {
					o.Args = append(o.Args, args[0])
				}
				args = args[1:]
			} else {
				o.Args = append(o.Args, args[0])
				args = args[1:]
			}
		}
	}
	return nil
}

func (o *OptionParser) setParsedOption(opt option, value interface{}) {
	if opt.dest.IsValid() {
		switch opt.action {
		case atINCREMENT:
			opt.dest.Elem().Set(increment(opt.dest.Elem()))
		case atAPPEND:
			opt.dest.Elem().Set(push(opt.dest.Elem(), value))
		case atMAP:
			kv := value.(keyVal)
			opt.dest.Elem().SetMapIndex(reflect.ValueOf(kv.key), reflect.ValueOf(kv.val))
		case atASSIGN:
			if opt.dest.Kind() == reflect.Func {
				t := reflect.TypeOf(opt.dest.Interface())
				var cbArgs []reflect.Value
				if t.NumIn() == 1 {
					cbArgs = make([]reflect.Value, 1)
					cbArgs[0] = reflect.ValueOf(value)
				} else if t.NumIn() == 2 {
					cbArgs = make([]reflect.Value, 2)
					cbArgs[0] = reflect.ValueOf(opt.name)
					cbArgs[1] = reflect.ValueOf(value)
				}
				opt.dest.Call(cbArgs)
			} else {
				opt.dest.Elem().Set(reflect.ValueOf(value))
			}
		}
	} else {
		switch opt.action {
		case atINCREMENT:
			o.Results[opt.name] = increment(reflect.ValueOf(o.Results[opt.name])).Interface()
		case atAPPEND:
			o.Results[opt.name] = push(reflect.ValueOf(o.Results[opt.name]), value).Interface()
		case atMAP:
			kv := value.(keyVal)
			reflect.ValueOf(o.Results[opt.name]).SetMapIndex(reflect.ValueOf(kv.key), reflect.ValueOf(kv.val))
		case atASSIGN:
			o.Results[opt.name] = reflect.ValueOf(value).Interface()
		}
	}
}
<|endoftext|>


<commit_before>package network

import (
	"errors"
	"net"
)

type Network struct{}

const name = "network"

func (self *Network) Name() string {
	return name
}

func (self *Network) Collect() (result interface{}, err error) {
	result, err = getNetworkInfo()
	interfaces := getMultiNetworkInfo()
	if interfaces != nil {
		result["interfaces"] = interfaces
	}
	return
}

func getMultiNetworkInfo() (multiNetworkInfo []map[string]interface{}) {
	ifaces, err := net.Interfaces()

	if err != nil {
		return nil
	}
	_iface := make(map[string]interface{})
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return nil
		}
		for _, addr := range addrs {
			ip, network, _ := net.ParseCIDR(addr.String())
			if ip == nil || ip.IsLoopback() {
				continue
			}
			if ip.To4() == nil {
				_iface["ipv6"] = ip.String()
				_iface["ipv6-network"] = network.String()
			} else {
				_iface["ipv4"] = ip.String()
				_iface["ipv4-network"] = network.String()
			}
			if len(iface.HardwareAddr.String()) > 0 {
				_iface["macaddress"] = iface.HardwareAddr.String()
			}
		}
		if len(_iface) > 0 {
			_iface["name"] = iface.Name
			multiNetworkInfo = append(multiNetworkInfo, _iface)
		}
	}
	if len(multiNetworkInfo) > 0 {
		return multiNetworkInfo
	}
	return nil
}

type Ipv6Address struct{}

func externalIpv6Address() (string, error) {
	ifaces, err := net.Interfaces()

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

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			if ip.To4() != nil {
				// ipv4 address
				continue
			}
			return ip.String(), nil
		}
	}

	// We don't return an error if no IPv6 interface has been found. Indeed,
	// some orgs just don't have IPv6 enabled. If there's a network error, it
	// will pop out when getting the Mac address and/or the IPv4 address
	// (before this function's call; see network.go -> getNetworkInfo())
	return "", nil
}

type IpAddress struct{}

func externalIpAddress() (string, error) {
	ifaces, err := net.Interfaces()

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

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				// not an ipv4 address
				continue
			}
			return ip.String(), nil
		}
	}
	return "", errors.New("not connected to the network")
}

type MacAddress struct{}

func macAddress() (string, error) {
	ifaces, err := net.Interfaces()

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

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() || ip.To4() == nil {
				continue
			}
			return iface.HardwareAddr.String(), nil
		}
	}
	return "", errors.New("not connected to the network")
}
<commit_msg>fixes some quirks<commit_after>package network

import (
	"errors"
	"net"
)

type Network struct{}

const name = "network"

func (self *Network) Name() string {
	return name
}

func (self *Network) Collect() (result interface{}, err error) {
	result, err = getNetworkInfo()
	interfaces := getMultiNetworkInfo()
	if interfaces != nil {
		interfaceMap, ok := result.(map[string]interface{})
		if !ok {
			return
		}
		interfaceMap["interfaces"] = interfaces
	}
	return
}

func getMultiNetworkInfo() (multiNetworkInfo []map[string]interface{}) {
	ifaces, err := net.Interfaces()

	if err != nil {
		return nil
	}
	for _, iface := range ifaces {
		_iface := make(map[string]interface{})
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return nil
		}
		for _, addr := range addrs {
			ip, network, _ := net.ParseCIDR(addr.String())
			if ip == nil || ip.IsLoopback() {
				continue
			}
			if ip.To4() == nil {
				_iface["ipv6"] = ip.String()
				_iface["ipv6-network"] = network.String()
			} else {
				_iface["ipv4"] = ip.String()
				_iface["ipv4-network"] = network.String()
			}
			if len(iface.HardwareAddr.String()) > 0 {
				_iface["macaddress"] = iface.HardwareAddr.String()
			}
		}
		if len(_iface) > 0 {
			_iface["name"] = iface.Name
			multiNetworkInfo = append(multiNetworkInfo, _iface)
		}
	}
	if len(multiNetworkInfo) > 0 {
		return multiNetworkInfo
	}
	return nil
}

type Ipv6Address struct{}

func externalIpv6Address() (string, error) {
	ifaces, err := net.Interfaces()

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

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			if ip.To4() != nil {
				// ipv4 address
				continue
			}
			return ip.String(), nil
		}
	}

	// We don't return an error if no IPv6 interface has been found. Indeed,
	// some orgs just don't have IPv6 enabled. If there's a network error, it
	// will pop out when getting the Mac address and/or the IPv4 address
	// (before this function's call; see network.go -> getNetworkInfo())
	return "", nil
}

type IpAddress struct{}

func externalIpAddress() (string, error) {
	ifaces, err := net.Interfaces()

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

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				// not an ipv4 address
				continue
			}
			return ip.String(), nil
		}
	}
	return "", errors.New("not connected to the network")
}

type MacAddress struct{}

func macAddress() (string, error) {
	ifaces, err := net.Interfaces()

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

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
			// interface down or loopback interface
			continue
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() || ip.To4() == nil {
				continue
			}
			return iface.HardwareAddr.String(), nil
		}
	}
	return "", errors.New("not connected to the network")
}
<|endoftext|>


<commit_before>package bslack

import (
	"fmt"
	"html"
	"time"

	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/nlopes/slack"
)

func (b *Bslack) handleSlack() {
	messages := make(chan *config.Message)
	if b.GetString(incomingWebhookConfig) != "" {
		b.Log.Debugf("Choosing webhooks based receiving")
		go b.handleMatterHook(messages)
	} else {
		b.Log.Debugf("Choosing token based receiving")
		go b.handleSlackClient(messages)
	}
	time.Sleep(time.Second)
	b.Log.Debug("Start listening for Slack messages")
	for message := range messages {
		if message.Event != config.EventUserTyping {
			b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
		}

		// cleanup the message
		message.Text = b.replaceMention(message.Text)
		message.Text = b.replaceVariable(message.Text)
		message.Text = b.replaceChannel(message.Text)
		message.Text = b.replaceURL(message.Text)
		message.Text = html.UnescapeString(message.Text)

		// Add the avatar
		message.Avatar = b.users.getAvatar(message.UserID)

		b.Log.Debugf("<= Message is %#v", message)
		b.Remote <- *message
	}
}

func (b *Bslack) handleSlackClient(messages chan *config.Message) {
	for msg := range b.rtm.IncomingEvents {
		if msg.Type != sUserTyping && msg.Type != sLatencyReport {
			b.Log.Debugf("== Receiving event %#v", msg.Data)
		}
		switch ev := msg.Data.(type) {
		case *slack.UserTypingEvent:
			if !b.GetBool("ShowUserTyping") {
				continue
			}
			rmsg, err := b.handleTypingEvent(ev)
			if err != nil {
				b.Log.Errorf("%#v", err)
				continue
			}

			messages <- rmsg
		case *slack.MessageEvent:
			if b.skipMessageEvent(ev) {
				b.Log.Debugf("Skipped message: %#v", ev)
				continue
			}
			rmsg, err := b.handleMessageEvent(ev)
			if err != nil {
				b.Log.Errorf("%#v", err)
				continue
			}
			messages <- rmsg
		case *slack.OutgoingErrorEvent:
			b.Log.Debugf("%#v", ev.Error())
		case *slack.ChannelJoinedEvent:
			// When we join a channel we update the full list of users as
			// well as the information for the channel that we joined as this
			// should now tell that we are a member of it.
			b.channels.registerChannel(ev.Channel)
		case *slack.ConnectedEvent:
			b.si = ev.Info
			b.channels.populateChannels(true)
			b.users.populateUsers(true)
		case *slack.InvalidAuthEvent:
			b.Log.Fatalf("Invalid Token %#v", ev)
		case *slack.ConnectionErrorEvent:
			b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
		case *slack.MemberJoinedChannelEvent:
			b.users.populateUser(ev.User)
		case *slack.LatencyReport:
			continue
		default:
			b.Log.Debugf("Unhandled incoming event: %T", ev)
		}
	}
}

func (b *Bslack) handleMatterHook(messages chan *config.Message) {
	for {
		message := b.mh.Receive()
		b.Log.Debugf("receiving from matterhook (slack) %#v", message)
		if message.UserName == "slackbot" {
			continue
		}
		messages <- &config.Message{
			Username: message.UserName,
			Text:     message.Text,
			Channel:  message.ChannelName,
		}
	}
}

// skipMessageEvent skips event that need to be skipped :-)
func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
	switch ev.SubType {
	case sChannelLeave, sChannelJoin:
		return b.GetBool(noSendJoinConfig)
	case sPinnedItem, sUnpinnedItem:
		return true
	case sChannelTopic, sChannelPurpose:
		// Skip the event if our bot/user account changed the topic/purpose
		if ev.User == b.si.User.ID {
			return true
		}
	}

	// Skip any messages that we made ourselves or from 'slackbot' (see #527).
	if ev.Username == sSlackBotUser ||
		(b.rtm != nil && ev.Username == b.si.User.Name) ||
		(len(ev.Attachments) > 0 && ev.Attachments[0].CallbackID == "matterbridge_"+b.uuid) {
		return true
	}

	if ev.SubMessage != nil {
		// It seems ev.SubMessage.Edited == nil when slack unfurls.
		// Do not forward these messages. See Github issue #266.
		if ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp &&
			ev.SubMessage.Edited == nil {
			return true
		}
		// see hidden subtypes at https://api.slack.com/events/message
		// these messages are sent when we add a message to a thread #709
		if ev.SubType == "message_replied" && ev.Hidden {
			return true
		}
	}

	if len(ev.Files) > 0 {
		return b.filesCached(ev.Files)
	}
	return false
}

func (b *Bslack) filesCached(files []slack.File) bool {
	for i := range files {
		if !b.fileCached(&files[i]) {
			return false
		}
	}
	return true
}

// handleMessageEvent handles the message events. Together with any called sub-methods,
// this method implements the following event processing pipeline:
//
// 1. Check if the message should be ignored.
//    NOTE: This is not actually part of the method below but is done just before it
//          is called via the 'skipMessageEvent()' method.
// 2. Populate the Matterbridge message that will be sent to the router based on the
//    received event and logic that is common to all events that are not skipped.
// 3. Detect and handle any message that is "status" related (think join channel, etc.).
//    This might result in an early exit from the pipeline and passing of the
//    pre-populated message to the Matterbridge router.
// 4. Handle the specific case of messages that edit existing messages depending on
//    configuration.
// 5. Handle any attachments of the received event.
// 6. Check that the Matterbridge message that we end up with after at the end of the
//    pipeline is valid before sending it to the Matterbridge router.
func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, error) {
	rmsg, err := b.populateReceivedMessage(ev)
	if err != nil {
		return nil, err
	}

	// Handle some message types early.
	if b.handleStatusEvent(ev, rmsg) {
		return rmsg, nil
	}

	b.handleAttachments(ev, rmsg)

	// Verify that we have the right information and the message
	// is well-formed before sending it out to the router.
	if len(ev.Files) == 0 && (rmsg.Text == "" || rmsg.Username == "") {
		if ev.BotID != "" {
			// This is probably a webhook we couldn't resolve.
			return nil, fmt.Errorf("message handling resulted in an empty bot message (probably an incoming webhook we couldn't resolve): %#v", ev)
		}
		if ev.SubMessage != nil {
			return nil, fmt.Errorf("message handling resulted in an empty message: %#v with submessage %#v", ev, ev.SubMessage)
		}
		return nil, fmt.Errorf("message handling resulted in an empty message: %#v", ev)
	}
	return rmsg, nil
}

func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message) bool {
	switch ev.SubType {
	case sChannelJoined, sMemberJoined:
		// There's no further processing needed on channel events
		// so we return 'true'.
		return true
	case sChannelJoin, sChannelLeave:
		rmsg.Username = sSystemUser
		rmsg.Event = config.EventJoinLeave
	case sChannelTopic, sChannelPurpose:
		b.channels.populateChannels(false)
		rmsg.Event = config.EventTopicChange
	case sMessageChanged:
		rmsg.Text = ev.SubMessage.Text
		// handle deleted thread starting messages
		if ev.SubMessage.Text == "This message was deleted." {
			rmsg.Event = config.EventMsgDelete
			return true
		}
	case sMessageDeleted:
		rmsg.Text = config.EventMsgDelete
		rmsg.Event = config.EventMsgDelete
		rmsg.ID = ev.DeletedTimestamp
		// If a message is being deleted we do not need to process
		// the event any further so we return 'true'.
		return true
	case sMeMessage:
		rmsg.Event = config.EventUserAction
	}
	return false
}

func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message) {
	// File comments are set by the system (because there is no username given).
	if ev.SubType == sFileComment {
		rmsg.Username = sSystemUser
	}

	// See if we have some text in the attachments.
	if rmsg.Text == "" {
		for _, attach := range ev.Attachments {
			if attach.Text != "" {
				if attach.Title != "" {
					rmsg.Text = attach.Title + "\n"
				}
				rmsg.Text += attach.Text
			} else {
				rmsg.Text = attach.Fallback
			}
		}
	}

	// Save the attachments, so that we can send them to other slack (compatible) bridges.
	if len(ev.Attachments) > 0 {
		rmsg.Extra[sSlackAttachment] = append(rmsg.Extra[sSlackAttachment], ev.Attachments)
	}

	// If we have files attached, download them (in memory) and put a pointer to it in msg.Extra.
	for i := range ev.Files {
		if err := b.handleDownloadFile(rmsg, &ev.Files[i], false); err != nil {
			b.Log.Errorf("Could not download incoming file: %#v", err)
		}
	}
}

func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) {
	channelInfo, err := b.channels.getChannelByID(ev.Channel)
	if err != nil {
		return nil, err
	}
	return &config.Message{
		Channel: channelInfo.Name,
		Account: b.Account,
		Event:   config.EventUserTyping,
	}, nil
}

// handleDownloadFile handles file download
func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slack.File, retry bool) error {
	if b.fileCached(file) {
		return nil
	}
	// Check that the file is neither too large nor blacklisted.
	if err := helper.HandleDownloadSize(b.Log, rmsg, file.Name, int64(file.Size), b.General); err != nil {
		b.Log.WithError(err).Infof("Skipping download of incoming file.")
		return nil
	}

	// Actually download the file.
	data, err := helper.DownloadFileAuth(file.URLPrivateDownload, "Bearer "+b.GetString(tokenConfig))
	if err != nil {
		return fmt.Errorf("download %s failed %#v", file.URLPrivateDownload, err)
	}

	if len(*data) != file.Size && !retry {
		b.Log.Debugf("Data size (%d) is not equal to size declared (%d)\n", len(*data), file.Size)
		time.Sleep(1 * time.Second)
		return b.handleDownloadFile(rmsg, file, true)
	}

	// If a comment is attached to the file(s) it is in the 'Text' field of the Slack messge event
	// and should be added as comment to only one of the files. We reset the 'Text' field to ensure
	// that the comment is not duplicated.
	comment := rmsg.Text
	rmsg.Text = ""
	helper.HandleDownloadData(b.Log, rmsg, file.Name, comment, file.URLPrivateDownload, data, b.General)
	return nil
}

// handleGetChannelMembers handles messages containing the GetChannelMembers event
// Sends a message to the router containing *config.ChannelMembers
func (b *Bslack) handleGetChannelMembers(rmsg *config.Message) bool {
	if rmsg.Event != config.EventGetChannelMembers {
		return false
	}

	cMembers := b.channels.getChannelMembers(b.users)

	extra := make(map[string][]interface{})
	extra[config.EventGetChannelMembers] = append(extra[config.EventGetChannelMembers], cMembers)
	msg := config.Message{
		Extra:   extra,
		Event:   config.EventGetChannelMembers,
		Account: b.Account,
	}

	b.Log.Debugf("sending msg to remote %#v", msg)
	b.Remote <- msg

	return true
}

// fileCached implements Matterbridge's caching logic for files
// shared via Slack.
//
// We consider that a file was cached if its ID was added in the last minute or
// it's name was registered in the last 10 seconds. This ensures that an
// identically named file but with different content will be uploaded correctly
// (the assumption is that such name collisions will not occur within the given
// timeframes).
func (b *Bslack) fileCached(file *slack.File) bool {
	if ts, ok := b.cache.Get("file" + file.ID); ok && time.Since(ts.(time.Time)) < time.Minute {
		return true
	} else if ts, ok = b.cache.Get("filename" + file.Name); ok && time.Since(ts.(time.Time)) < 10*time.Second {
		return true
	}
	return false
}
<commit_msg>Disable user lookups on delete messages (slack) (#812)<commit_after>package bslack

import (
	"fmt"
	"html"
	"time"

	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/nlopes/slack"
)

func (b *Bslack) handleSlack() {
	messages := make(chan *config.Message)
	if b.GetString(incomingWebhookConfig) != "" {
		b.Log.Debugf("Choosing webhooks based receiving")
		go b.handleMatterHook(messages)
	} else {
		b.Log.Debugf("Choosing token based receiving")
		go b.handleSlackClient(messages)
	}
	time.Sleep(time.Second)
	b.Log.Debug("Start listening for Slack messages")
	for message := range messages {
		// don't do any action on deleted/typing messages
		if message.Event != config.EventUserTyping && message.Event != config.EventMsgDelete {
			b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
			// cleanup the message
			message.Text = b.replaceMention(message.Text)
			message.Text = b.replaceVariable(message.Text)
			message.Text = b.replaceChannel(message.Text)
			message.Text = b.replaceURL(message.Text)
			message.Text = html.UnescapeString(message.Text)

			// Add the avatar
			message.Avatar = b.users.getAvatar(message.UserID)
		}

		b.Log.Debugf("<= Message is %#v", message)
		b.Remote <- *message
	}
}

func (b *Bslack) handleSlackClient(messages chan *config.Message) {
	for msg := range b.rtm.IncomingEvents {
		if msg.Type != sUserTyping && msg.Type != sLatencyReport {
			b.Log.Debugf("== Receiving event %#v", msg.Data)
		}
		switch ev := msg.Data.(type) {
		case *slack.UserTypingEvent:
			if !b.GetBool("ShowUserTyping") {
				continue
			}
			rmsg, err := b.handleTypingEvent(ev)
			if err != nil {
				b.Log.Errorf("%#v", err)
				continue
			}

			messages <- rmsg
		case *slack.MessageEvent:
			if b.skipMessageEvent(ev) {
				b.Log.Debugf("Skipped message: %#v", ev)
				continue
			}
			rmsg, err := b.handleMessageEvent(ev)
			if err != nil {
				b.Log.Errorf("%#v", err)
				continue
			}
			messages <- rmsg
		case *slack.OutgoingErrorEvent:
			b.Log.Debugf("%#v", ev.Error())
		case *slack.ChannelJoinedEvent:
			// When we join a channel we update the full list of users as
			// well as the information for the channel that we joined as this
			// should now tell that we are a member of it.
			b.channels.registerChannel(ev.Channel)
		case *slack.ConnectedEvent:
			b.si = ev.Info
			b.channels.populateChannels(true)
			b.users.populateUsers(true)
		case *slack.InvalidAuthEvent:
			b.Log.Fatalf("Invalid Token %#v", ev)
		case *slack.ConnectionErrorEvent:
			b.Log.Errorf("Connection failed %#v %#v", ev.Error(), ev.ErrorObj)
		case *slack.MemberJoinedChannelEvent:
			b.users.populateUser(ev.User)
		case *slack.LatencyReport:
			continue
		default:
			b.Log.Debugf("Unhandled incoming event: %T", ev)
		}
	}
}

func (b *Bslack) handleMatterHook(messages chan *config.Message) {
	for {
		message := b.mh.Receive()
		b.Log.Debugf("receiving from matterhook (slack) %#v", message)
		if message.UserName == "slackbot" {
			continue
		}
		messages <- &config.Message{
			Username: message.UserName,
			Text:     message.Text,
			Channel:  message.ChannelName,
		}
	}
}

// skipMessageEvent skips event that need to be skipped :-)
func (b *Bslack) skipMessageEvent(ev *slack.MessageEvent) bool {
	switch ev.SubType {
	case sChannelLeave, sChannelJoin:
		return b.GetBool(noSendJoinConfig)
	case sPinnedItem, sUnpinnedItem:
		return true
	case sChannelTopic, sChannelPurpose:
		// Skip the event if our bot/user account changed the topic/purpose
		if ev.User == b.si.User.ID {
			return true
		}
	}

	// Skip any messages that we made ourselves or from 'slackbot' (see #527).
	if ev.Username == sSlackBotUser ||
		(b.rtm != nil && ev.Username == b.si.User.Name) ||
		(len(ev.Attachments) > 0 && ev.Attachments[0].CallbackID == "matterbridge_"+b.uuid) {
		return true
	}

	if ev.SubMessage != nil {
		// It seems ev.SubMessage.Edited == nil when slack unfurls.
		// Do not forward these messages. See Github issue #266.
		if ev.SubMessage.ThreadTimestamp != ev.SubMessage.Timestamp &&
			ev.SubMessage.Edited == nil {
			return true
		}
		// see hidden subtypes at https://api.slack.com/events/message
		// these messages are sent when we add a message to a thread #709
		if ev.SubType == "message_replied" && ev.Hidden {
			return true
		}
	}

	if len(ev.Files) > 0 {
		return b.filesCached(ev.Files)
	}
	return false
}

func (b *Bslack) filesCached(files []slack.File) bool {
	for i := range files {
		if !b.fileCached(&files[i]) {
			return false
		}
	}
	return true
}

// handleMessageEvent handles the message events. Together with any called sub-methods,
// this method implements the following event processing pipeline:
//
// 1. Check if the message should be ignored.
//    NOTE: This is not actually part of the method below but is done just before it
//          is called via the 'skipMessageEvent()' method.
// 2. Populate the Matterbridge message that will be sent to the router based on the
//    received event and logic that is common to all events that are not skipped.
// 3. Detect and handle any message that is "status" related (think join channel, etc.).
//    This might result in an early exit from the pipeline and passing of the
//    pre-populated message to the Matterbridge router.
// 4. Handle the specific case of messages that edit existing messages depending on
//    configuration.
// 5. Handle any attachments of the received event.
// 6. Check that the Matterbridge message that we end up with after at the end of the
//    pipeline is valid before sending it to the Matterbridge router.
func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, error) {
	rmsg, err := b.populateReceivedMessage(ev)
	if err != nil {
		return nil, err
	}

	// Handle some message types early.
	if b.handleStatusEvent(ev, rmsg) {
		return rmsg, nil
	}

	b.handleAttachments(ev, rmsg)

	// Verify that we have the right information and the message
	// is well-formed before sending it out to the router.
	if len(ev.Files) == 0 && (rmsg.Text == "" || rmsg.Username == "") {
		if ev.BotID != "" {
			// This is probably a webhook we couldn't resolve.
			return nil, fmt.Errorf("message handling resulted in an empty bot message (probably an incoming webhook we couldn't resolve): %#v", ev)
		}
		if ev.SubMessage != nil {
			return nil, fmt.Errorf("message handling resulted in an empty message: %#v with submessage %#v", ev, ev.SubMessage)
		}
		return nil, fmt.Errorf("message handling resulted in an empty message: %#v", ev)
	}
	return rmsg, nil
}

func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message) bool {
	switch ev.SubType {
	case sChannelJoined, sMemberJoined:
		// There's no further processing needed on channel events
		// so we return 'true'.
		return true
	case sChannelJoin, sChannelLeave:
		rmsg.Username = sSystemUser
		rmsg.Event = config.EventJoinLeave
	case sChannelTopic, sChannelPurpose:
		b.channels.populateChannels(false)
		rmsg.Event = config.EventTopicChange
	case sMessageChanged:
		rmsg.Text = ev.SubMessage.Text
		// handle deleted thread starting messages
		if ev.SubMessage.Text == "This message was deleted." {
			rmsg.Event = config.EventMsgDelete
			return true
		}
	case sMessageDeleted:
		rmsg.Text = config.EventMsgDelete
		rmsg.Event = config.EventMsgDelete
		rmsg.ID = ev.DeletedTimestamp
		// If a message is being deleted we do not need to process
		// the event any further so we return 'true'.
		return true
	case sMeMessage:
		rmsg.Event = config.EventUserAction
	}
	return false
}

func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message) {
	// File comments are set by the system (because there is no username given).
	if ev.SubType == sFileComment {
		rmsg.Username = sSystemUser
	}

	// See if we have some text in the attachments.
	if rmsg.Text == "" {
		for _, attach := range ev.Attachments {
			if attach.Text != "" {
				if attach.Title != "" {
					rmsg.Text = attach.Title + "\n"
				}
				rmsg.Text += attach.Text
			} else {
				rmsg.Text = attach.Fallback
			}
		}
	}

	// Save the attachments, so that we can send them to other slack (compatible) bridges.
	if len(ev.Attachments) > 0 {
		rmsg.Extra[sSlackAttachment] = append(rmsg.Extra[sSlackAttachment], ev.Attachments)
	}

	// If we have files attached, download them (in memory) and put a pointer to it in msg.Extra.
	for i := range ev.Files {
		if err := b.handleDownloadFile(rmsg, &ev.Files[i], false); err != nil {
			b.Log.Errorf("Could not download incoming file: %#v", err)
		}
	}
}

func (b *Bslack) handleTypingEvent(ev *slack.UserTypingEvent) (*config.Message, error) {
	channelInfo, err := b.channels.getChannelByID(ev.Channel)
	if err != nil {
		return nil, err
	}
	return &config.Message{
		Channel: channelInfo.Name,
		Account: b.Account,
		Event:   config.EventUserTyping,
	}, nil
}

// handleDownloadFile handles file download
func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slack.File, retry bool) error {
	if b.fileCached(file) {
		return nil
	}
	// Check that the file is neither too large nor blacklisted.
	if err := helper.HandleDownloadSize(b.Log, rmsg, file.Name, int64(file.Size), b.General); err != nil {
		b.Log.WithError(err).Infof("Skipping download of incoming file.")
		return nil
	}

	// Actually download the file.
	data, err := helper.DownloadFileAuth(file.URLPrivateDownload, "Bearer "+b.GetString(tokenConfig))
	if err != nil {
		return fmt.Errorf("download %s failed %#v", file.URLPrivateDownload, err)
	}

	if len(*data) != file.Size && !retry {
		b.Log.Debugf("Data size (%d) is not equal to size declared (%d)\n", len(*data), file.Size)
		time.Sleep(1 * time.Second)
		return b.handleDownloadFile(rmsg, file, true)
	}

	// If a comment is attached to the file(s) it is in the 'Text' field of the Slack messge event
	// and should be added as comment to only one of the files. We reset the 'Text' field to ensure
	// that the comment is not duplicated.
	comment := rmsg.Text
	rmsg.Text = ""
	helper.HandleDownloadData(b.Log, rmsg, file.Name, comment, file.URLPrivateDownload, data, b.General)
	return nil
}

// handleGetChannelMembers handles messages containing the GetChannelMembers event
// Sends a message to the router containing *config.ChannelMembers
func (b *Bslack) handleGetChannelMembers(rmsg *config.Message) bool {
	if rmsg.Event != config.EventGetChannelMembers {
		return false
	}

	cMembers := b.channels.getChannelMembers(b.users)

	extra := make(map[string][]interface{})
	extra[config.EventGetChannelMembers] = append(extra[config.EventGetChannelMembers], cMembers)
	msg := config.Message{
		Extra:   extra,
		Event:   config.EventGetChannelMembers,
		Account: b.Account,
	}

	b.Log.Debugf("sending msg to remote %#v", msg)
	b.Remote <- msg

	return true
}

// fileCached implements Matterbridge's caching logic for files
// shared via Slack.
//
// We consider that a file was cached if its ID was added in the last minute or
// it's name was registered in the last 10 seconds. This ensures that an
// identically named file but with different content will be uploaded correctly
// (the assumption is that such name collisions will not occur within the given
// timeframes).
func (b *Bslack) fileCached(file *slack.File) bool {
	if ts, ok := b.cache.Get("file" + file.ID); ok && time.Since(ts.(time.Time)) < time.Minute {
		return true
	} else if ts, ok = b.cache.Get("filename" + file.Name); ok && time.Since(ts.(time.Time)) < 10*time.Second {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package sphere

import (
	"errors"
	"fmt"
	"strconv"
)

const (
	// PacketEventMessage denotes a regular text data message.
	PacketEventMessage = 0
	// PacketEventChannel denotes a channel request.
	PacketEventChannel = 1
	// PacketEventSubscribe denotes a subscribe request.
	PacketEventSubscribe = 6
	// PacketEventUnsubscribe denotes an unsubscribe request.
	PacketEventUnsubscribe = 7
	// PacketEventPing denotes an ping message.
	PacketEventPing = 8
	// PacketEventPong denotes an pong message.
	PacketEventPong = 9
)

// NewPacket creates new packet instance
func NewPacket(event int, data string) *Packet {
	return &Packet{event, data, nil}
}

// ParsePacket parses string and return packet
func ParsePacket(str string) (*Packet, error) {
	if str == "" {
		return nil, errors.New("packet is empty")
	}
	code := string(str[0])
	i, err := strconv.Atoi(code)
	if err != nil {
		return nil, errors.New("packet is invalid")
	}
	if i != PacketEventMessage && i != PacketEventSubscribe && i != PacketEventUnsubscribe && i != PacketEventPing && i != PacketEventPong {
		return nil, errors.New("packet is invalid")
	}
	data := str[1:len(str)]
	if i == PacketEventSubscribe || i == PacketEventUnsubscribe || i == PacketEventMessage {
		if data == "" {
			return nil, errors.New("packet is invalid")
		}
	}
	return NewPacket(i, data), nil
}

// Packet indicates the data of the message
type Packet struct {
	event int
	data  string
	err   error
}

// String converts Packet object to a string format
func (packet *Packet) String() string {
	return fmt.Sprintf("%d%s", packet.event, packet.data)
}

// Byte converts Packet object to byte array format
func (packet *Packet) Byte() []byte {
	return []byte(packet.String())
}
<commit_msg>add json support<commit_after>package sphere

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"errors"
)

// Packet indicates the data of the message
type Packet struct {
	Success bool       `json:"success"`
	Type    PacketType `json:"type"`
	Channel string     `json:"channel,omitempty"`
	Cid     int        `json:"cid,omitempty"`
	Rid     int        `json:"rid,omitempty"`
	Error   error      `json:"error,omitempty"`
	Message *Message   `json:"message,omitempty"`
}

// ParsePacket returns Packet from bytes
func ParsePacket(data []byte) (*Packet, error) {
	var p *Packet
	if err := json.Unmarshal(data, &p); err != nil {
		return nil, errors.New("packet format is invalid")
	}
	return p, nil
}

// Packet.toJSON returns json byte array from Packet
func (p *Packet) toJSON() ([]byte, error) {
	return json.Marshal(p)
}

// Packet.toBytes returns byte array from Packet
func (p *Packet) toBytes() ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(p)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
<|endoftext|>


<commit_before>package medtronic

import (
	"fmt"

	"github.com/ecc1/radio"
)

func (pump *Pump) DecodePacket(packet radio.Packet) ([]byte, error) {
	data, err := Decode6b4b(packet.Data)
	if err != nil {
		pump.DecodingErrors++
		return nil, err
	}
	crc := Crc8(data[:len(data)-1])
	if data[len(data)-1] != crc {
		pump.CrcErrors++
		return data, fmt.Errorf("CRC should be %X, not %X", crc, data[len(data)-1])
	}
	return data, nil
}

func EncodePacket(packet []byte) radio.Packet {
	return radio.Packet{Data: Encode4b6b(append(packet, Crc8(packet)))}
}

func (pump *Pump) PrintStats() {
	stats := pump.Radio.Statistics()
	good := stats.Packets.Received - pump.DecodingErrors - pump.CrcErrors
	fmt.Printf("\nTX: %6d    RX: %6d    decode errs: %6d    CRC errs: %6d\n", stats.Packets.Sent, good, pump.DecodingErrors, pump.CrcErrors)
	fmt.Printf("State: %s\n", pump.Radio.State())
}
<commit_msg>Avoid clobbering caller's data witha append()<commit_after>package medtronic

import (
	"fmt"

	"github.com/ecc1/radio"
)

func (pump *Pump) DecodePacket(packet radio.Packet) ([]byte, error) {
	data, err := Decode6b4b(packet.Data)
	if err != nil {
		pump.DecodingErrors++
		return nil, err
	}
	crc := Crc8(data[:len(data)-1])
	if data[len(data)-1] != crc {
		pump.CrcErrors++
		return data, fmt.Errorf("CRC should be %X, not %X", crc, data[len(data)-1])
	}
	return data, nil
}

func EncodePacket(data []byte) radio.Packet {
	// Don't use append() to add the CRC, because append
	// may write into the array underlying the caller's slice.
	buf := make([]byte, len(data)+1)
	copy(buf, data)
	buf[len(data)] = Crc8(data)
	return radio.Packet{Data: Encode4b6b(buf)}
}

func (pump *Pump) PrintStats() {
	stats := pump.Radio.Statistics()
	good := stats.Packets.Received - pump.DecodingErrors - pump.CrcErrors
	fmt.Printf("\nTX: %6d    RX: %6d    decode errs: %6d    CRC errs: %6d\n", stats.Packets.Sent, good, pump.DecodingErrors, pump.CrcErrors)
	fmt.Printf("State: %s\n", pump.Radio.State())
}
<|endoftext|>