<commit_before>// Copyright 2014 Unknwon
//
// 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 cmd

import (
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/Unknwon/com"
	"github.com/Unknwon/log"
	"github.com/urfave/cli"
	"gopkg.in/fsnotify/fsnotify.v1"

	"github.com/Unknwon/bra/modules/setting"
)

var (
	lastBuild time.Time
	eventTime = make(map[string]int64)

	runningCmd  *exec.Cmd
	runningLock = &sync.Mutex{}
	shutdown    = make(chan bool)
)

var CmdRun = cli.Command{
	Name:   "run",
	Usage:  "start monitoring and notifying",
	Action: runRun,
	Flags:  []cli.Flag{},
}

// isTmpFile returns true if the event was for temporary files.
func isTmpFile(name string) bool {
	if strings.HasSuffix(strings.ToLower(name), ".tmp") {
		return true
	}
	return false
}

// hasWatchExt returns true if the file name has watched extension.
func hasWatchExt(name string) bool {
	for _, ext := range setting.Cfg.Run.WatchExts {
		if strings.HasSuffix(name, ext) {
			return true
		}
	}
	return false
}

// runCommand represents a command to run after notified.
type runCommand struct {
	Envs []string
	Name string
	Args []string
}

func (cmd *runCommand) String() string {
	if len(cmd.Envs) > 0 {
		return fmt.Sprintf("%v %s %v", cmd.Envs, cmd.Name, cmd.Args)
	}
	return fmt.Sprintf("%s %v", cmd.Name, cmd.Args)
}

func parseRunCommand(args []string) *runCommand {
	runCmd := new(runCommand)
	i := 0
	for _, arg := range args {
		if !strings.Contains(arg, "=") {
			break
		}
		runCmd.Envs = append(runCmd.Envs, arg)
		i++
	}

	if len(runCmd.Envs) > 0 {
		runCmd.Envs = append(runCmd.Envs, os.Environ()...)
	}

	runCmd.Name = args[i]
	runCmd.Args = args[i+1:]
	return runCmd
}

func parseRunCommands(cmds [][]string) []*runCommand {
	runCmds := make([]*runCommand, len(cmds))
	for i, args := range cmds {
		runCmds[i] = parseRunCommand(args)
	}
	return runCmds
}

func notify(cmds []*runCommand) {
	runningLock.Lock()
	defer func() {
		runningCmd = nil
		runningLock.Unlock()
	}()

	for _, cmd := range cmds {
		command := exec.Command(cmd.Name, cmd.Args...)
		command.Env = cmd.Envs
		command.Stdout = os.Stdout
		command.Stderr = os.Stderr
		if err := command.Start(); err != nil {
			log.Error("Fail to start command: %v - %v", cmd, err)
			fmt.Print("\x07")
			return
		}

		log.Debug("Running: %s", cmd)
		runningCmd = command
		done := make(chan error)
		go func() {
			done <- command.Wait()
		}()

		isShutdown := false
		select {
		case err := <-done:
			if isShutdown {
				return
			} else if err != nil {
				log.Warn("Fail to execute command: %v - %v", cmd, err)
				fmt.Print("\x07")
				return
			}
		case <-shutdown:
			isShutdown = true
			gracefulKill()
			return
		}
	}
	log.Info("Notify operations are done!")
}

func gracefulKill() {
	// Directly kill the process on Windows or under request.
	if runtime.GOOS == "windows" || !setting.Cfg.Run.GracefulKill {
		runningCmd.Process.Kill()
		return
	}

	// Given process a chance to exit itself.
	runningCmd.Process.Signal(os.Interrupt)

	// Wait for timeout, and force kill after that.
	for i := 0; i < setting.Cfg.Run.InterruptTimeout; i++ {
		time.Sleep(1 * time.Second)

		if runningCmd.ProcessState == nil || runningCmd.ProcessState.Exited() {
			return
		}
	}

	log.Info("Fail to graceful kill, force killing...")
	runningCmd.Process.Kill()
}

func catchSignals() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGTERM)
	<-sigs

	if runningCmd != nil {
		shutdown <- true
	}
	os.Exit(0)
}

func runRun(ctx *cli.Context) error {
	setup(ctx)

	go catchSignals()
	go notify(parseRunCommands(setting.Cfg.Run.InitCmds))

	watchPathes := append([]string{setting.WorkDir}, setting.Cfg.Run.WatchDirs...)
	if setting.Cfg.Run.WatchAll {
		subdirs := make([]string, 0, 10)
		for _, dir := range watchPathes[1:] {
			dirs, err := com.GetAllSubDirs(setting.UnpackPath(dir))
			if err != nil {
				log.Fatal("Fail to get sub-directories: %v", err)
			}
			for i := range dirs {
				if !setting.IgnoreDir(dirs[i]) {
					subdirs = append(subdirs, path.Join(dir, dirs[i]))
				}
			}
		}
		watchPathes = append(watchPathes, subdirs...)
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal("Fail to create new watcher: %v", err)
	}
	defer watcher.Close()

	go func() {
		runCmds := parseRunCommands(setting.Cfg.Run.Cmds)

		for {
			select {
			case e := <-watcher.Events:
				needsNotify := true

				if isTmpFile(e.Name) || !hasWatchExt(e.Name) || setting.IgnoreFile(e.Name) {
					continue
				}

				// Prevent duplicated builds.
				if lastBuild.Add(time.Duration(setting.Cfg.Run.BuildDelay) * time.Millisecond).
					After(time.Now()) {
					continue
				}
				lastBuild = time.Now()

				showName := e.String()
				if !log.NonColor {
					showName = strings.Replace(showName, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1)
				}

				if e.Op&fsnotify.Remove != fsnotify.Remove {
					mt, err := com.FileMTime(e.Name)
					if err != nil {
						log.Error("Fail to get file modify time: %v", err)
						continue
					}
					if eventTime[e.Name] == mt {
						log.Debug("Skipped %s", showName)
						needsNotify = false
					}
					eventTime[e.Name] = mt
				}

				if needsNotify {
					log.Info(showName)
					if runningCmd != nil && runningCmd.Process != nil {
						if runningCmd.Args[0] == "sudo" && runtime.GOOS == "linux" {
							// 给父进程发送一个TERM信号，试图杀死它和它的子进程
							rootCmd := exec.Command("sudo", "kill", "-TERM", com.ToStr(runningCmd.Process.Pid))
							rootCmd.Stdout = os.Stdout
							rootCmd.Stderr = os.Stderr
							if err := rootCmd.Run(); err != nil {
								log.Error("Fail to start rootCmd %s", err.Error())
								fmt.Print("\x07")
							}
						} else {
							shutdown <- true
						}
					}
					go notify(runCmds)
				}
			}
		}
	}()

	log.Info("Following directories are monitored:")
	for i, p := range watchPathes {
		if err = watcher.Add(setting.UnpackPath(p)); err != nil {
			log.Fatal("Fail to watch directory(%s): %v", p, err)
		}
		if i > 0 && !log.NonColor {
			p = strings.Replace(p, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1)
			p = strings.Replace(p, "$WORKDIR", "\033[47;30m$WORKDIR\033[0m", 1)
		}
		fmt.Printf("-> %s\n", p)
	}
	select {}
	return nil
}
<commit_msg>[RFC] Fix file not found error on Windows (#24)<commit_after>// Copyright 2014 Unknwon
//
// 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 cmd

import (
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/Unknwon/com"
	"github.com/Unknwon/log"
	"github.com/urfave/cli"
	"gopkg.in/fsnotify/fsnotify.v1"

	"github.com/Unknwon/bra/modules/setting"
)

var (
	lastBuild time.Time
	eventTime = make(map[string]int64)

	runningCmd  *exec.Cmd
	runningLock = &sync.Mutex{}
	shutdown    = make(chan bool)
)

var CmdRun = cli.Command{
	Name:   "run",
	Usage:  "start monitoring and notifying",
	Action: runRun,
	Flags:  []cli.Flag{},
}

// isTmpFile returns true if the event was for temporary files.
func isTmpFile(name string) bool {
	if strings.HasSuffix(strings.ToLower(name), ".tmp") {
		return true
	}
	return false
}

// hasWatchExt returns true if the file name has watched extension.
func hasWatchExt(name string) bool {
	for _, ext := range setting.Cfg.Run.WatchExts {
		if strings.HasSuffix(name, ext) {
			return true
		}
	}
	return false
}

// runCommand represents a command to run after notified.
type runCommand struct {
	Envs []string
	Name string
	Args []string
}

func (cmd *runCommand) String() string {
	if len(cmd.Envs) > 0 {
		return fmt.Sprintf("%v %s %v", cmd.Envs, cmd.Name, cmd.Args)
	}
	return fmt.Sprintf("%s %v", cmd.Name, cmd.Args)
}

func parseRunCommand(args []string) *runCommand {
	runCmd := new(runCommand)
	i := 0
	for _, arg := range args {
		if !strings.Contains(arg, "=") {
			break
		}
		runCmd.Envs = append(runCmd.Envs, arg)
		i++
	}

	if len(runCmd.Envs) > 0 {
		runCmd.Envs = append(runCmd.Envs, os.Environ()...)
	}

	runCmd.Name = args[i]
	runCmd.Args = args[i+1:]
	return runCmd
}

func parseRunCommands(cmds [][]string) []*runCommand {
	runCmds := make([]*runCommand, len(cmds))
	for i, args := range cmds {
		runCmds[i] = parseRunCommand(args)
	}
	return runCmds
}

func notify(cmds []*runCommand) {
	runningLock.Lock()
	defer func() {
		runningCmd = nil
		runningLock.Unlock()
	}()

	for _, cmd := range cmds {
		command := exec.Command(cmd.Name, cmd.Args...)
		command.Env = cmd.Envs
		command.Stdout = os.Stdout
		command.Stderr = os.Stderr
		if err := command.Start(); err != nil {
			log.Error("Fail to start command: %v - %v", cmd, err)
			fmt.Print("\x07")
			return
		}

		log.Debug("Running: %s", cmd)
		runningCmd = command
		done := make(chan error)
		go func() {
			done <- command.Wait()
		}()

		isShutdown := false
		select {
		case err := <-done:
			if isShutdown {
				return
			} else if err != nil {
				log.Warn("Fail to execute command: %v - %v", cmd, err)
				fmt.Print("\x07")
				return
			}
		case <-shutdown:
			isShutdown = true
			gracefulKill()
			return
		}
	}
	log.Info("Notify operations are done!")
}

func gracefulKill() {
	// Directly kill the process on Windows or under request.
	if runtime.GOOS == "windows" || !setting.Cfg.Run.GracefulKill {
		runningCmd.Process.Kill()
		return
	}

	// Given process a chance to exit itself.
	runningCmd.Process.Signal(os.Interrupt)

	// Wait for timeout, and force kill after that.
	for i := 0; i < setting.Cfg.Run.InterruptTimeout; i++ {
		time.Sleep(1 * time.Second)

		if runningCmd.ProcessState == nil || runningCmd.ProcessState.Exited() {
			return
		}
	}

	log.Info("Fail to graceful kill, force killing...")
	runningCmd.Process.Kill()
}

func catchSignals() {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGTERM)
	<-sigs

	if runningCmd != nil {
		shutdown <- true
	}
	os.Exit(0)
}

func runRun(ctx *cli.Context) error {
	setup(ctx)

	go catchSignals()
	go notify(parseRunCommands(setting.Cfg.Run.InitCmds))

	watchPathes := append([]string{setting.WorkDir}, setting.Cfg.Run.WatchDirs...)
	if setting.Cfg.Run.WatchAll {
		subdirs := make([]string, 0, 10)
		for _, dir := range watchPathes[1:] {
			dirs, err := com.GetAllSubDirs(setting.UnpackPath(dir))
			if err != nil {
				log.Fatal("Fail to get sub-directories: %v", err)
			}
			for i := range dirs {
				if !setting.IgnoreDir(dirs[i]) {
					subdirs = append(subdirs, path.Join(dir, dirs[i]))
				}
			}
		}
		watchPathes = append(watchPathes, subdirs...)
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal("Fail to create new watcher: %v", err)
	}
	defer watcher.Close()

	go func() {
		runCmds := parseRunCommands(setting.Cfg.Run.Cmds)

		for {
			select {
			case e := <-watcher.Events:
				needsNotify := true

				if isTmpFile(e.Name) || !hasWatchExt(e.Name) || setting.IgnoreFile(e.Name) {
					continue
				}

				// Prevent duplicated builds.
				if lastBuild.Add(time.Duration(setting.Cfg.Run.BuildDelay) * time.Millisecond).
					After(time.Now()) {
					continue
				}
				lastBuild = time.Now()

				showName := e.String()
				if !log.NonColor {
					showName = strings.Replace(showName, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1)
				}

				if e.Op&fsnotify.Remove != fsnotify.Remove && e.Op&fsnotify.Rename != fsnotify.Rename {
					mt, err := com.FileMTime(e.Name)
					if err != nil {
						log.Error("Fail to get file modify time: %v", err)
						continue
					}
					if eventTime[e.Name] == mt {
						log.Debug("Skipped %s", showName)
						needsNotify = false
					}
					eventTime[e.Name] = mt
				}

				if needsNotify {
					log.Info(showName)
					if runningCmd != nil && runningCmd.Process != nil {
						if runningCmd.Args[0] == "sudo" && runtime.GOOS == "linux" {
							// 给父进程发送一个TERM信号，试图杀死它和它的子进程
							rootCmd := exec.Command("sudo", "kill", "-TERM", com.ToStr(runningCmd.Process.Pid))
							rootCmd.Stdout = os.Stdout
							rootCmd.Stderr = os.Stderr
							if err := rootCmd.Run(); err != nil {
								log.Error("Fail to start rootCmd %s", err.Error())
								fmt.Print("\x07")
							}
						} else {
							shutdown <- true
						}
					}
					go notify(runCmds)
				}
			}
		}
	}()

	log.Info("Following directories are monitored:")
	for i, p := range watchPathes {
		if err = watcher.Add(setting.UnpackPath(p)); err != nil {
			log.Fatal("Fail to watch directory(%s): %v", p, err)
		}
		if i > 0 && !log.NonColor {
			p = strings.Replace(p, setting.WorkDir, "\033[47;30m$WORKDIR\033[0m", 1)
			p = strings.Replace(p, "$WORKDIR", "\033[47;30m$WORKDIR\033[0m", 1)
		}
		fmt.Printf("-> %s\n", p)
	}
	select {}
	return nil
}
<|endoftext|>


<commit_before>package ctx

import (
	"github.com/funkygao/jsconf"
)

type zone struct {
	Name        string // prod
	Zk          string // localhost:2181,localhost:2182
	InfluxAddr  string // localhost:8086
	SwfEndpoint string // http://192.168.10.134:9195/v1

	// smoke test related
	PubEndpoint, SubEndpoint string // the load balancer addr
	SmokeApp                 string
	SmokeHisApp              string
	SmokeSecret              string
	SmokeTopic               string
	SmokeTopicVersion        string
	SmokeGroup               string
	HaProxyStatsUri          []string
}

func (this *zone) loadConfig(section *ljconf.Conf) {
	this.Name = section.String("name", "")
	this.Zk = section.String("zk", "")
	this.InfluxAddr = section.String("influxdb", "")
	this.SwfEndpoint = section.String("swf", "")
	this.PubEndpoint = section.String("pub_entry", "")
	this.SubEndpoint = section.String("sub_entry", "")
	this.SmokeApp = section.String("smoke_app", "")
	this.SmokeSecret = section.String("smoke_secret", "")
	this.SmokeTopic = section.String("smoke_topic", "smoketestonly")
	this.SmokeTopicVersion = section.String("smoke_topic_ver", "v1")
	this.SmokeHisApp = section.String("smoke_app_his", this.SmokeApp)
	this.SmokeGroup = section.String("smoke_group", "__smoketestonly__")
	this.HaProxyStatsUri = section.StringList("haproxy_stats", nil)
	if this.Name == "" {
		panic("empty zone name not allowed")
	}
}
<commit_msg>add helix zk connecton string<commit_after>package ctx

import (
	"github.com/funkygao/jsconf"
)

type zone struct {
	Name        string // prod
	Zk          string // localhost:2181,localhost:2182
	InfluxAddr  string // localhost:8086
	SwfEndpoint string // http://192.168.10.134:9195/v1

	ZkHelix string // localhost:2181/helix

	// smoke test related
	PubEndpoint, SubEndpoint string // the load balancer addr
	SmokeApp                 string
	SmokeHisApp              string
	SmokeSecret              string
	SmokeTopic               string
	SmokeTopicVersion        string
	SmokeGroup               string
	HaProxyStatsUri          []string
}

func (this *zone) loadConfig(section *ljconf.Conf) {
	this.Name = section.String("name", "")
	this.Zk = section.String("zk", "")
	this.ZkHelix = section.String("zk_helix", "")
	this.InfluxAddr = section.String("influxdb", "")
	this.SwfEndpoint = section.String("swf", "")
	this.PubEndpoint = section.String("pub_entry", "")
	this.SubEndpoint = section.String("sub_entry", "")
	this.SmokeApp = section.String("smoke_app", "")
	this.SmokeSecret = section.String("smoke_secret", "")
	this.SmokeTopic = section.String("smoke_topic", "smoketestonly")
	this.SmokeTopicVersion = section.String("smoke_topic_ver", "v1")
	this.SmokeHisApp = section.String("smoke_app_his", this.SmokeApp)
	this.SmokeGroup = section.String("smoke_group", "__smoketestonly__")
	this.HaProxyStatsUri = section.StringList("haproxy_stats", nil)
	if this.Name == "" {
		panic("empty zone name not allowed")
	}
}
<|endoftext|>


<commit_before>// Package configfile reads and writes gocryptfs.conf does the key
// wrapping.
package configfile

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"

	"github.com/rfjakob/gocryptfs/internal/contentenc"
	"github.com/rfjakob/gocryptfs/internal/cryptocore"
	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)
import "os"

const (
	// ConfDefaultName is the default configuration file name.
	// The dot "." is not used in base64url (RFC4648), hence
	// we can never clash with an encrypted file.
	ConfDefaultName = "gocryptfs.conf"
	// ConfReverseName is the default configuration file name in reverse mode,
	// the config file gets stored next to the plain-text files. Make it hidden
	// (start with dot) to not annoy the user.
	ConfReverseName = ".gocryptfs.reverse.conf"
)

// ConfFile is the content of a config file.
type ConfFile struct {
	// Creator is the gocryptfs version string.
	// This only documents the config file for humans who look at it. The actual
	// technical info is contained in FeatureFlags.
	Creator string
	// EncryptedKey holds an encrypted AES key, unlocked using a password
	// hashed with scrypt
	EncryptedKey []byte
	// ScryptObject stores parameters for scrypt hashing (key derivation)
	ScryptObject ScryptKDF
	// Version is the On-Disk-Format version this filesystem uses
	Version uint16
	// FeatureFlags is a list of feature flags this filesystem has enabled.
	// If gocryptfs encounters a feature flag it does not support, it will refuse
	// mounting. This mechanism is analogous to the ext4 feature flags that are
	// stored in the superblock.
	FeatureFlags []string
	// Filename is the name of the config file. Not exported to JSON.
	filename string
}

// randBytesDevRandom gets "n" random bytes from /dev/random or panics
func randBytesDevRandom(n int) []byte {
	f, err := os.Open("/dev/random")
	if err != nil {
		log.Panic("Failed to open /dev/random: " + err.Error())
	}
	defer f.Close()
	b := make([]byte, n)
	_, err = io.ReadFull(f, b)
	if err != nil {
		log.Panic("Failed to read random bytes: " + err.Error())
	}
	return b
}

// CreateConfFile - create a new config with a random key encrypted with
// "password" and write it to "filename".
// Uses scrypt with cost parameter logN.
func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, aessiv bool, devrandom bool) error {
	var cf ConfFile
	cf.filename = filename
	cf.Creator = creator
	cf.Version = contentenc.CurrentVersion

	// Set feature flags
	cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128])
	cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagHKDF])
	if plaintextNames {
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagPlaintextNames])
	} else {
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagDirIV])
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagRaw64])
	}
	if aessiv {
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV])
	}

	// Generate new random master key
	var key []byte
	if devrandom {
		key = randBytesDevRandom(cryptocore.KeyLen)
	} else {
		key = cryptocore.RandBytes(cryptocore.KeyLen)
	}

	// Encrypt it using the password
	// This sets ScryptObject and EncryptedKey
	// Note: this looks at the FeatureFlags, so call it AFTER setting them.
	cf.EncryptKey(key, password, logN)

	// Write file to disk
	return cf.WriteFile()
}

// LoadConfFile - read config file from disk and decrypt the
// contained key using "password".
// Returns the decrypted key and the ConfFile object
//
// If "password" is empty, the config file is read
// but the key is not decrypted (returns nil in its place).
func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {
	var cf ConfFile
	cf.filename = filename

	// Read from disk
	js, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Printf("LoadConfFile: ReadFile: %#v\n", err)
		return nil, nil, err
	}

	// Unmarshal
	err = json.Unmarshal(js, &cf)
	if err != nil {
		tlog.Warn.Printf("Failed to unmarshal config file")
		return nil, nil, err
	}

	if cf.Version != contentenc.CurrentVersion {
		return nil, nil, fmt.Errorf("Unsupported on-disk format %d", cf.Version)
	}

	// Check that all set feature flags are known
	for _, flag := range cf.FeatureFlags {
		if !cf.isFeatureFlagKnown(flag) {
			return nil, nil, fmt.Errorf("Unsupported feature flag %q", flag)
		}
	}

	// Check that all required feature flags are set
	var requiredFlags []flagIota
	if cf.IsFeatureFlagSet(FlagPlaintextNames) {
		requiredFlags = requiredFlagsPlaintextNames
	} else {
		requiredFlags = requiredFlagsNormal
	}
	deprecatedFs := false
	for _, i := range requiredFlags {
		if !cf.IsFeatureFlagSet(i) {
			fmt.Fprintf(os.Stderr, "Required feature flag %q is missing\n", knownFlags[i])
			deprecatedFs = true
		}
	}
	if deprecatedFs {
		fmt.Fprintf(os.Stderr, "\033[33m"+`
    The filesystem was created by gocryptfs v0.6 or earlier. This version of
    gocryptfs can no longer mount the filesystem.
    Please download gocryptfs v0.11 and upgrade your filesystem,
    see https://github.com/rfjakob/gocryptfs/wiki/Upgrading for instructions.

    If you have trouble upgrading, join the discussion at
    https://github.com/rfjakob/gocryptfs/issues/29 .

`+"\033[0m")

		return nil, nil, fmt.Errorf("Deprecated filesystem")
	}
	if password == "" {
		// We have validated the config file, but without a password we cannot
		// decrypt the master key. Return only the parsed config.
		return nil, &cf, nil
	}

	// Generate derived key from password
	scryptHash := cf.ScryptObject.DeriveKey(password)

	// Unlock master key using password-based key
	useHKDF := cf.IsFeatureFlagSet(FlagHKDF)
	ce := getKeyEncrypter(scryptHash, useHKDF)

	tlog.Warn.Enabled = false // Silence DecryptBlock() error messages on incorrect password
	key, err := ce.DecryptBlock(cf.EncryptedKey, 0, nil)
	tlog.Warn.Enabled = true
	if err != nil {
		tlog.Warn.Printf("failed to unlock master key: %s", err.Error())
		return nil, nil, exitcodes.NewErr("Password incorrect.", exitcodes.PasswordIncorrect)
	}

	return key, &cf, err
}

// EncryptKey - encrypt "key" using an scrypt hash generated from "password"
// and store it in cf.EncryptedKey.
// Uses scrypt with cost parameter logN and stores the scrypt parameters in
// cf.ScryptObject.
func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {
	// Generate derived key from password
	cf.ScryptObject = NewScryptKDF(logN)
	scryptHash := cf.ScryptObject.DeriveKey(password)

	// Lock master key using password-based key
	useHKDF := cf.IsFeatureFlagSet(FlagHKDF)
	ce := getKeyEncrypter(scryptHash, useHKDF)
	cf.EncryptedKey = ce.EncryptBlock(key, 0, nil)
}

// WriteFile - write out config in JSON format to file "filename.tmp"
// then rename over "filename".
// This way a password change atomically replaces the file.
func (cf *ConfFile) WriteFile() error {
	tmp := cf.filename + ".tmp"
	// 0400 permissions: gocryptfs.conf should be kept secret and never be written to.
	fd, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400)
	if err != nil {
		return err
	}
	js, err := json.MarshalIndent(cf, "", "\t")
	if err != nil {
		return err
	}
	// For convenience for the user, add a newline at the end.
	js = append(js, '\n')
	_, err = fd.Write(js)
	if err != nil {
		return err
	}
	err = fd.Sync()
	if err != nil {
		return err
	}
	err = fd.Close()
	if err != nil {
		return err
	}
	err = os.Rename(tmp, cf.filename)
	return err
}

// getKeyEncrypter is a helper function that returns the right ContentEnc
// instance for the "useHKDF" setting.
func getKeyEncrypter(scryptHash []byte, useHKDF bool) *contentenc.ContentEnc {
	IVLen := 96
	// gocryptfs v1.2 and older used 96-bit IVs for master key encryption.
	// v1.3 adds the "HKDF" feature flag, which also enables 128-bit nonces.
	if useHKDF {
		IVLen = contentenc.DefaultIVBits
	}
	cc := cryptocore.New(scryptHash, cryptocore.BackendGoGCM, IVLen, useHKDF, false)
	ce := contentenc.New(cc, 4096, false)
	return ce
}
<commit_msg>configfile: overwrite and let keys run out of scope<commit_after>// Package configfile reads and writes gocryptfs.conf does the key
// wrapping.
package configfile

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"

	"github.com/rfjakob/gocryptfs/internal/contentenc"
	"github.com/rfjakob/gocryptfs/internal/cryptocore"
	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)
import "os"

const (
	// ConfDefaultName is the default configuration file name.
	// The dot "." is not used in base64url (RFC4648), hence
	// we can never clash with an encrypted file.
	ConfDefaultName = "gocryptfs.conf"
	// ConfReverseName is the default configuration file name in reverse mode,
	// the config file gets stored next to the plain-text files. Make it hidden
	// (start with dot) to not annoy the user.
	ConfReverseName = ".gocryptfs.reverse.conf"
)

// ConfFile is the content of a config file.
type ConfFile struct {
	// Creator is the gocryptfs version string.
	// This only documents the config file for humans who look at it. The actual
	// technical info is contained in FeatureFlags.
	Creator string
	// EncryptedKey holds an encrypted AES key, unlocked using a password
	// hashed with scrypt
	EncryptedKey []byte
	// ScryptObject stores parameters for scrypt hashing (key derivation)
	ScryptObject ScryptKDF
	// Version is the On-Disk-Format version this filesystem uses
	Version uint16
	// FeatureFlags is a list of feature flags this filesystem has enabled.
	// If gocryptfs encounters a feature flag it does not support, it will refuse
	// mounting. This mechanism is analogous to the ext4 feature flags that are
	// stored in the superblock.
	FeatureFlags []string
	// Filename is the name of the config file. Not exported to JSON.
	filename string
}

// randBytesDevRandom gets "n" random bytes from /dev/random or panics
func randBytesDevRandom(n int) []byte {
	f, err := os.Open("/dev/random")
	if err != nil {
		log.Panic("Failed to open /dev/random: " + err.Error())
	}
	defer f.Close()
	b := make([]byte, n)
	_, err = io.ReadFull(f, b)
	if err != nil {
		log.Panic("Failed to read random bytes: " + err.Error())
	}
	return b
}

// CreateConfFile - create a new config with a random key encrypted with
// "password" and write it to "filename".
// Uses scrypt with cost parameter logN.
func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, aessiv bool, devrandom bool) error {
	var cf ConfFile
	cf.filename = filename
	cf.Creator = creator
	cf.Version = contentenc.CurrentVersion

	// Set feature flags
	cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128])
	cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagHKDF])
	if plaintextNames {
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagPlaintextNames])
	} else {
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagDirIV])
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagRaw64])
	}
	if aessiv {
		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV])
	}
	{
		// Generate new random master key
		var key []byte
		if devrandom {
			key = randBytesDevRandom(cryptocore.KeyLen)
		} else {
			key = cryptocore.RandBytes(cryptocore.KeyLen)
		}
		// Encrypt it using the password
		// This sets ScryptObject and EncryptedKey
		// Note: this looks at the FeatureFlags, so call it AFTER setting them.
		cf.EncryptKey(key, password, logN)
		for i := range key {
			key[i] = 0
		}
		// key runs out of scope here
	}
	// Write file to disk
	return cf.WriteFile()
}

// LoadConfFile - read config file from disk and decrypt the
// contained key using "password".
// Returns the decrypted key and the ConfFile object
//
// If "password" is empty, the config file is read
// but the key is not decrypted (returns nil in its place).
func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {
	var cf ConfFile
	cf.filename = filename

	// Read from disk
	js, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Printf("LoadConfFile: ReadFile: %#v\n", err)
		return nil, nil, err
	}

	// Unmarshal
	err = json.Unmarshal(js, &cf)
	if err != nil {
		tlog.Warn.Printf("Failed to unmarshal config file")
		return nil, nil, err
	}

	if cf.Version != contentenc.CurrentVersion {
		return nil, nil, fmt.Errorf("Unsupported on-disk format %d", cf.Version)
	}

	// Check that all set feature flags are known
	for _, flag := range cf.FeatureFlags {
		if !cf.isFeatureFlagKnown(flag) {
			return nil, nil, fmt.Errorf("Unsupported feature flag %q", flag)
		}
	}

	// Check that all required feature flags are set
	var requiredFlags []flagIota
	if cf.IsFeatureFlagSet(FlagPlaintextNames) {
		requiredFlags = requiredFlagsPlaintextNames
	} else {
		requiredFlags = requiredFlagsNormal
	}
	deprecatedFs := false
	for _, i := range requiredFlags {
		if !cf.IsFeatureFlagSet(i) {
			fmt.Fprintf(os.Stderr, "Required feature flag %q is missing\n", knownFlags[i])
			deprecatedFs = true
		}
	}
	if deprecatedFs {
		fmt.Fprintf(os.Stderr, "\033[33m"+`
    The filesystem was created by gocryptfs v0.6 or earlier. This version of
    gocryptfs can no longer mount the filesystem.
    Please download gocryptfs v0.11 and upgrade your filesystem,
    see https://github.com/rfjakob/gocryptfs/wiki/Upgrading for instructions.

    If you have trouble upgrading, join the discussion at
    https://github.com/rfjakob/gocryptfs/issues/29 .

`+"\033[0m")

		return nil, nil, fmt.Errorf("Deprecated filesystem")
	}
	if password == "" {
		// We have validated the config file, but without a password we cannot
		// decrypt the master key. Return only the parsed config.
		return nil, &cf, nil
	}

	// Generate derived key from password
	scryptHash := cf.ScryptObject.DeriveKey(password)

	// Unlock master key using password-based key
	useHKDF := cf.IsFeatureFlagSet(FlagHKDF)
	ce := getKeyEncrypter(scryptHash, useHKDF)

	tlog.Warn.Enabled = false // Silence DecryptBlock() error messages on incorrect password
	key, err := ce.DecryptBlock(cf.EncryptedKey, 0, nil)
	tlog.Warn.Enabled = true
	if err != nil {
		tlog.Warn.Printf("failed to unlock master key: %s", err.Error())
		return nil, nil, exitcodes.NewErr("Password incorrect.", exitcodes.PasswordIncorrect)
	}

	return key, &cf, err
}

// EncryptKey - encrypt "key" using an scrypt hash generated from "password"
// and store it in cf.EncryptedKey.
// Uses scrypt with cost parameter logN and stores the scrypt parameters in
// cf.ScryptObject.
func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {
	// Generate scrypt-derived key from password
	cf.ScryptObject = NewScryptKDF(logN)
	scryptHash := cf.ScryptObject.DeriveKey(password)
	// Lock master key using password-based key
	useHKDF := cf.IsFeatureFlagSet(FlagHKDF)
	ce := getKeyEncrypter(scryptHash, useHKDF)
	cf.EncryptedKey = ce.EncryptBlock(key, 0, nil)
	// Purge scrypt-derived key
	for i := range scryptHash {
		scryptHash[i] = 0
	}
}

// WriteFile - write out config in JSON format to file "filename.tmp"
// then rename over "filename".
// This way a password change atomically replaces the file.
func (cf *ConfFile) WriteFile() error {
	tmp := cf.filename + ".tmp"
	// 0400 permissions: gocryptfs.conf should be kept secret and never be written to.
	fd, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400)
	if err != nil {
		return err
	}
	js, err := json.MarshalIndent(cf, "", "\t")
	if err != nil {
		return err
	}
	// For convenience for the user, add a newline at the end.
	js = append(js, '\n')
	_, err = fd.Write(js)
	if err != nil {
		return err
	}
	err = fd.Sync()
	if err != nil {
		return err
	}
	err = fd.Close()
	if err != nil {
		return err
	}
	err = os.Rename(tmp, cf.filename)
	return err
}

// getKeyEncrypter is a helper function that returns the right ContentEnc
// instance for the "useHKDF" setting.
func getKeyEncrypter(scryptHash []byte, useHKDF bool) *contentenc.ContentEnc {
	IVLen := 96
	// gocryptfs v1.2 and older used 96-bit IVs for master key encryption.
	// v1.3 adds the "HKDF" feature flag, which also enables 128-bit nonces.
	if useHKDF {
		IVLen = contentenc.DefaultIVBits
	}
	cc := cryptocore.New(scryptHash, cryptocore.BackendGoGCM, IVLen, useHKDF, false)
	ce := contentenc.New(cc, 4096, false)
	return ce
}
<|endoftext|>


<commit_before>/**
 * (C) Copyright 2013, Deft Labs
 *
 * 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 dlshared

import (
	"fmt"
	"net/http"
	"io/ioutil"
	"time"
	"bytes"
	"strings"
	"errors"
	"labix.org/v2/mgo/bson"
	"encoding/json"
	"github.com/mreiferson/go-httpclient"
)

const (
	SocketTimeout = 40
	HttpPostMethod = "POST"
	ContentTypeHeader = "Content-Type"
	ContentTypeTextPlain = "text/plain; charset=utf-8"
)

func HttpPostStr(url string, value string) ([]byte, error) {

	httpClient, httpTransport := getDefaultHttpClient()
	defer httpTransport.Close()

	request, err := http.NewRequest("POST", url, bytes.NewReader([]byte(value)))
	if err != nil {
		return nil, err
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	response, err := httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	if data, err := ioutil.ReadAll(response.Body); err != nil {
		return nil, err
	} else {
		return data, nil
	}
}

func HttpPostJson(url string, value interface{}) ([]byte, error) {

	rawJson, err := json.Marshal(value)
	if err != nil {
		return nil, err
	}

	httpClient, httpTransport := getDefaultHttpClient()
	defer httpTransport.Close()

	request, err := http.NewRequest("POST", url, bytes.NewReader(rawJson))
	if err != nil {
		return nil, err
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	response, err := httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	if data, err := ioutil.ReadAll(response.Body); err != nil {
		return nil, err
	} else {
		return data, nil
	}
}

func HttpPostBson(url string, bsonDoc interface{}) ([]byte, error) {

	rawBson, err := bson.Marshal(bsonDoc)
	if err != nil {
		return nil, err
	}

	httpClient, httpTransport := getDefaultHttpClient()
	defer httpTransport.Close()

	request, err := http.NewRequest("POST", url, bytes.NewReader(rawBson))
	if err != nil {
		return nil, err
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	response, err := httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	// We do not return the response so don't report if there is an error.
	if data, err := ioutil.ReadAll(response.Body); err != nil {
		return nil, err
	} else {
		return data, nil
	}
}

func getDefaultHttpClient() (*http.Client, *httpclient.Transport) {
	transport := getDefaultHttpTransport()
	return &http.Client{ Transport: transport }, transport
}

func getDefaultHttpTransport() *httpclient.Transport {
	return &httpclient.Transport {
		ConnectTimeout:        SocketTimeout * time.Second,
		RequestTimeout:        SocketTimeout * time.Second,
		ResponseHeaderTimeout: SocketTimeout * time.Second,
	}
}

func HttpGetBson(url string) (bson.M, error) {

	httpClient, httpTransport := getDefaultHttpClient()
	defer httpTransport.Close()

	request, requestErr := http.NewRequest("GET", url, nil)
	if requestErr != nil {
		return nil, requestErr
	}

	response, err := httpClient.Do(request)

	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	rawBson, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	var bsonDoc bson.M
	if err := bson.Unmarshal(rawBson, &bsonDoc); err != nil {
		return nil, err
	}

	return bsonDoc, nil
}

// This method returns true if the http request method is a HTTP post. If the
// field missing or incorrect, false is returned. This method will panic if the request
// is nil.
func IsHttpMethodPost(request *http.Request) bool {
	if request == nil {
		panic("request param is nil")
	}
	return len(request.Method) > 0 && strings.ToUpper(request.Method) == HttpPostMethod
}

// Write an http ok response string. The content type is text/plain.
func WriteOkResponseString(response http.ResponseWriter, msg string) error {
	if response == nil {
		panic("response param is nil")
	}

	msgLength := len(msg)

	if msgLength == 0 {
		panic("do not write an empty string to the response")
	}

	response.Header().Set(ContentTypeHeader, ContentTypeTextPlain)

	written, err := response.Write([]byte(msg))

	if err != nil {
		return err
	}

	if written != msgLength {
		return errors.New(fmt.Sprintf("Did not write full message - bytes written %d - expected %d", written, msgLength))
	}

	return nil
}

<commit_msg>fixed the content type for post json<commit_after>/**
 * (C) Copyright 2013, Deft Labs
 *
 * 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 dlshared

import (
	"fmt"
	"net/http"
	"io/ioutil"
	"time"
	"bytes"
	"strings"
	"errors"
	"labix.org/v2/mgo/bson"
	"encoding/json"
	"github.com/mreiferson/go-httpclient"
)

const (
	SocketTimeout = 40
	HttpPostMethod = "POST"
	ContentTypeHeader = "Content-Type"
	ContentTypeTextPlain = "text/plain; charset=utf-8"
)

func HttpPostStr(url string, value string) ([]byte, error) {

	httpClient, httpTransport := getDefaultHttpClient()
	defer httpTransport.Close()

	request, err := http.NewRequest("POST", url, bytes.NewReader([]byte(value)))
	if err != nil {
		return nil, err
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	response, err := httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	if data, err := ioutil.ReadAll(response.Body); err != nil {
		return nil, err
	} else {
		return data, nil
	}
}

func HttpPostJson(url string, value interface{}) ([]byte, error) {

	rawJson, err := json.Marshal(value)
	if err != nil {
		return nil, err
	}

	httpClient, httpTransport := getDefaultHttpClient()
	defer httpTransport.Close()

	request, err := http.NewRequest("POST", url, bytes.NewReader(rawJson))
	if err != nil {
		return nil, err
	}
	request.Header.Set("Content-Type", "application/json")

	response, err := httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	if data, err := ioutil.ReadAll(response.Body); err != nil {
		return nil, err
	} else {
		return data, nil
	}
}

func HttpPostBson(url string, bsonDoc interface{}) ([]byte, error) {

	rawBson, err := bson.Marshal(bsonDoc)
	if err != nil {
		return nil, err
	}

	httpClient, httpTransport := getDefaultHttpClient()
	defer httpTransport.Close()

	request, err := http.NewRequest("POST", url, bytes.NewReader(rawBson))
	if err != nil {
		return nil, err
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	response, err := httpClient.Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	// We do not return the response so don't report if there is an error.
	if data, err := ioutil.ReadAll(response.Body); err != nil {
		return nil, err
	} else {
		return data, nil
	}
}

func getDefaultHttpClient() (*http.Client, *httpclient.Transport) {
	transport := getDefaultHttpTransport()
	return &http.Client{ Transport: transport }, transport
}

func getDefaultHttpTransport() *httpclient.Transport {
	return &httpclient.Transport {
		ConnectTimeout:        SocketTimeout * time.Second,
		RequestTimeout:        SocketTimeout * time.Second,
		ResponseHeaderTimeout: SocketTimeout * time.Second,
	}
}

func HttpGetBson(url string) (bson.M, error) {

	httpClient, httpTransport := getDefaultHttpClient()
	defer httpTransport.Close()

	request, requestErr := http.NewRequest("GET", url, nil)
	if requestErr != nil {
		return nil, requestErr
	}

	response, err := httpClient.Do(request)

	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	rawBson, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	var bsonDoc bson.M
	if err := bson.Unmarshal(rawBson, &bsonDoc); err != nil {
		return nil, err
	}

	return bsonDoc, nil
}

// This method returns true if the http request method is a HTTP post. If the
// field missing or incorrect, false is returned. This method will panic if the request
// is nil.
func IsHttpMethodPost(request *http.Request) bool {
	if request == nil {
		panic("request param is nil")
	}
	return len(request.Method) > 0 && strings.ToUpper(request.Method) == HttpPostMethod
}

// Write an http ok response string. The content type is text/plain.
func WriteOkResponseString(response http.ResponseWriter, msg string) error {
	if response == nil {
		panic("response param is nil")
	}

	msgLength := len(msg)

	if msgLength == 0 {
		panic("do not write an empty string to the response")
	}

	response.Header().Set(ContentTypeHeader, ContentTypeTextPlain)

	written, err := response.Write([]byte(msg))

	if err != nil {
		return err
	}

	if written != msgLength {
		return errors.New(fmt.Sprintf("Did not write full message - bytes written %d - expected %d", written, msgLength))
	}

	return nil
}

<|endoftext|>


<commit_before>// Copyright (C) 2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.

package lib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/mssola/dym"
)

type Topic struct {
	Id         string    `json:"id,omitempty"`
	Name       string    `json:"name,omitempty"`
	Contents   string    `json:"contents,omitempty"`
	Created_at time.Time `json:"created_at,omitempty"`
	Markdown   string    `json:"markdown,omitempty"`
}

// TODO
func requestUrl(path string, token bool) string {
	u, _ := url.Parse(config.Server)
	u.Path = path
	if token {
		v := url.Values{}
		v.Set("token", config.Token)
		u.RawQuery = v.Encode()
	}
	return u.String()
}

func getResponse(method, url string, body io.Reader) (*http.Response, error) {
	client := &http.Client{}
	str := requestUrl(url, true)
	req, _ := http.NewRequest(method, str, body)
	req.Header.Set("Content-Type", "application/json")
	return client.Do(req)
}

func unknownTopic(name string) {
	var topics []Topic
	var names []string

	getTopics(&topics)
	for _, v := range topics {
		names = append(names, v.Name)
	}

	msg := fmt.Sprintf("td: the topic '%v' does not exist.", name)
	similars := dym.Similar(names, name)
	if len(similars) == 0 {
		fmt.Printf(msg)
	} else {
		msg += "\n\nDid you mean one of these?\n"
		for _, v := range similars {
			msg += "\t" + v + "\n"
		}
		fmt.Printf(msg)
	}
}

func Edit() error {
	cmd := exec.Command(editor())
	cmd.Dir = filepath.Join(home(), dirName, newDir)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func Fetch() error {
	// Perform the HTTP request.
	res, err := getResponse("GET", "/topics", nil)
	if err != nil {
		return fromError(err)
	}

	// Parse the given topics.
	var topics []Topic
	body, _ := ioutil.ReadAll(res.Body)
	if err := json.Unmarshal(body, &topics); err != nil {
		return fromError(err)
	}

	// And save the results.
	if err := save(topics); err != nil {
		return err
	}
	fmt.Printf("Topics updated.\n")
	return nil
}

func List() error {
	var topics []Topic
	getTopics(&topics)
	for _, v := range topics {
		fmt.Printf("%v\n", v.Name)
	}
	return nil
}

func Push() error {
	var success, fails []string
	var topics []Topic
	getTopics(&topics)

	total := len(topics)
	for k, v := range topics {
		// Print the status.
		fmt.Printf("\rPushing... %v/%v\r", k+1, total)

		// Get the contents.
		file := filepath.Join(home(), dirName, newDir, v.Name+".md")
		body, _ := ioutil.ReadFile(file)
		t := &Topic{Contents: string(body)}
		if t.Contents == "" {
			success = append(success, v.Name)
			continue
		}

		// Perform the request.
		body, _ = json.Marshal(t)
		path := "/topics/" + v.Id
		_, err := getResponse("PUT", path, bytes.NewReader(body))
		if err == nil {
			success = append(success, v.Name)
		} else {
			fails = append(fails, v.Name)
		}
	}

	update(success, fails)
	fmt.Printf("Success!\n")
	return nil
}

func Create(name string) error {
	// Perform the HTTP request.
	t := &Topic{Name: name}
	body, _ := json.Marshal(t)
	res, err := getResponse("POST", "/topics", bytes.NewReader(body))
	if err != nil {
		return fromError(err)
	}

	// Parse the newly created topic and add it to the list.
	body, _ = ioutil.ReadAll(res.Body)
	if err := json.Unmarshal(body, &t); err != nil {
		return fromError(err)
	}
	return addTopic(t)
}

// TODO: tname is in fact name on success.
func Delete(name string) error {
	var topics, actual []Topic
	var tid, tname string

	// Get the list of topics straight.
	getTopics(&topics)
	for _, v := range topics {
		if v.Name == name {
			tid, tname = v.Id, v.Name
		} else {
			actual = append(actual, v)
		}
	}
	if tid == "" {
		unknownTopic(name)
		os.Exit(1)
	}

	// Perform the HTTP request.
	_, err := getResponse("DELETE", "/topics/"+tid, nil)
	if err != nil {
		return fromError(err)
	}

	// On the system.
	writeJson(actual)
	file := filepath.Join(home(), dirName, oldDir, tname+".md")
	os.RemoveAll(file)
	file = filepath.Join(home(), dirName, newDir, tname+".md")
	os.RemoveAll(file)
	return nil
}

func Rename(oldName, newName string) error {
	var topics []Topic
	var id, name string

	getTopics(&topics)
	for k, v := range topics {
		if v.Name == oldName {
			id = v.Id
			topics[k].Name = newName
		}
	}
	if id == "" {
		unknownTopic(name)
		os.Exit(1)
	}

	// Perform the HTTP Request.
	t := &Topic{Name: newName}
	body, _ := json.Marshal(t)
	_, err := getResponse("PUT", "/topics/"+id, bytes.NewReader(body))
	if err != nil {
		return fromError(err)
	}

	// Update the system.
	writeJson(topics)
	file := filepath.Join(home(), dirName, oldDir)
	os.Rename(filepath.Join(file, oldName), filepath.Join(file, newName))
	file = filepath.Join(home(), dirName, newDir)
	os.Rename(filepath.Join(file, oldName), filepath.Join(file, newName))
	return nil
}
<commit_msg>lib: polishing.<commit_after>// Copyright (C) 2014 Miquel Sabaté Solà <mikisabate@gmail.com>
// This file is licensed under the MIT license.
// See the LICENSE file.

package lib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/mssola/dym"
)

type Topic struct {
	Id         string    `json:"id,omitempty"`
	Name       string    `json:"name,omitempty"`
	Contents   string    `json:"contents,omitempty"`
	Created_at time.Time `json:"created_at,omitempty"`
	Markdown   string    `json:"markdown,omitempty"`
}

// TODO
func requestUrl(path string, token bool) string {
	u, _ := url.Parse(config.Server)
	u.Path = path
	if token {
		v := url.Values{}
		v.Set("token", config.Token)
		u.RawQuery = v.Encode()
	}
	return u.String()
}

func getResponse(method, url string, body io.Reader) (*http.Response, error) {
	client := &http.Client{}
	str := requestUrl(url, true)
	req, _ := http.NewRequest(method, str, body)
	req.Header.Set("Content-Type", "application/json")
	return client.Do(req)
}

func unknownTopic(name string) {
	var topics []Topic
	var names []string

	getTopics(&topics)
	for _, v := range topics {
		names = append(names, v.Name)
	}

	msg := fmt.Sprintf("td: the topic '%v' does not exist.", name)
	similars := dym.Similar(names, name)
	if len(similars) == 0 {
		fmt.Printf(msg)
	} else {
		msg += "\n\nDid you mean one of these?\n"
		for _, v := range similars {
			msg += "\t" + v + "\n"
		}
		fmt.Printf(msg)
	}
}

func Edit() error {
	cmd := exec.Command(editor())
	cmd.Dir = filepath.Join(home(), dirName, newDir)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func Fetch() error {
	// Perform the HTTP request.
	res, err := getResponse("GET", "/topics", nil)
	if err != nil {
		return fromError(err)
	}

	// Parse the given topics.
	var topics []Topic
	body, _ := ioutil.ReadAll(res.Body)
	if err := json.Unmarshal(body, &topics); err != nil {
		return fromError(err)
	}

	// And save the results.
	if err := save(topics); err != nil {
		return err
	}
	fmt.Printf("Topics updated.\n")
	return nil
}

func List() error {
	var topics []Topic
	getTopics(&topics)
	for _, v := range topics {
		fmt.Printf("%v\n", v.Name)
	}
	return nil
}

func Push() error {
	var success, fails []string
	var topics []Topic
	getTopics(&topics)

	total := len(topics)
	for k, v := range topics {
		// Print the status.
		fmt.Printf("\rPushing... %v/%v\r", k+1, total)

		// Get the contents.
		file := filepath.Join(home(), dirName, newDir, v.Name+".md")
		body, _ := ioutil.ReadFile(file)
		t := &Topic{Contents: string(body)}
		if t.Contents == "" {
			success = append(success, v.Name)
			continue
		}

		// Perform the request.
		body, _ = json.Marshal(t)
		path := "/topics/" + v.Id
		_, err := getResponse("PUT", path, bytes.NewReader(body))
		if err == nil {
			success = append(success, v.Name)
		} else {
			fails = append(fails, v.Name)
		}
	}

	update(success, fails)
	fmt.Printf("Success!\n")
	return nil
}

func Create(name string) error {
	// Perform the HTTP request.
	t := &Topic{Name: name}
	body, _ := json.Marshal(t)
	res, err := getResponse("POST", "/topics", bytes.NewReader(body))
	if err != nil {
		return fromError(err)
	}

	// Parse the newly created topic and add it to the list.
	body, _ = ioutil.ReadAll(res.Body)
	if err := json.Unmarshal(body, &t); err != nil {
		return fromError(err)
	}
	return addTopic(t)
}

func Delete(name string) error {
	var topics, actual []Topic
	var id string

	// Get the list of topics straight.
	getTopics(&topics)
	for _, v := range topics {
		if v.Name == name {
			id = v.Id
		} else {
			actual = append(actual, v)
		}
	}
	if id == "" {
		unknownTopic(name)
		os.Exit(1)
	}

	// Perform the HTTP request.
	_, err := getResponse("DELETE", "/topics/"+id, nil)
	if err != nil {
		return fromError(err)
	}

	// On the system.
	writeJson(actual)
	file := filepath.Join(home(), dirName, oldDir, name+".md")
	os.RemoveAll(file)
	file = filepath.Join(home(), dirName, newDir, name+".md")
	os.RemoveAll(file)
	return nil
}

func Rename(oldName, newName string) error {
	var topics []Topic
	var id, name string

	getTopics(&topics)
	for k, v := range topics {
		if v.Name == oldName {
			id = v.Id
			topics[k].Name = newName
		}
	}
	if id == "" {
		unknownTopic(name)
		os.Exit(1)
	}

	// Perform the HTTP Request.
	t := &Topic{Name: newName}
	body, _ := json.Marshal(t)
	_, err := getResponse("PUT", "/topics/"+id, bytes.NewReader(body))
	if err != nil {
		return fromError(err)
	}

	// Update the system.
	writeJson(topics)
	file := filepath.Join(home(), dirName, oldDir)
	os.Rename(filepath.Join(file, oldName), filepath.Join(file, newName))
	file = filepath.Join(home(), dirName, newDir)
	os.Rename(filepath.Join(file, oldName), filepath.Join(file, newName))
	return nil
}
<|endoftext|>


<commit_before>// Author(s): Michael Koeppl

package dinero

import "github.com/shopspring/decimal"

const (
	// The currencies listed below are the ones supported
	// by the ExchangeRates API and are listed with their
	// symbols and ISO codes.

	// USD -> USDollar ($ / USD)
	USD Currency = iota

	// EUR -> Euro (€ / EUR)
	EUR

	// JPY -> JapaneseYen (¥ / JPY)
	JPY

	// BGN -> BulgarianLev (лв. / BGN)
	BGN

	// CZK -> CzechKoruna (Kč / CZK)
	CZK

	// DKK -> DanishKrone (kr. / DKK)
	DKK

	// GBP -> GreatBritainPound (£ / GBP)
	GBP

	// HUF -> HungarianForint (Ft / HUF)
	HUF

	// LTL -> LithuanianLitas (Lt / LTL)
	LTL

	// LVL -> LatvianLats (Ls / LVL)
	LVL

	// PLN -> PolishZloty (zł / PLN)
	PLN

	// RON -> RomanianLeu (? / RON)
	RON

	// SEK -> SwedishKrona (kr / SEK)
	SEK

	// CHF -> SwissFranc (CHF / CHF)
	CHF

	// NOK -> NorwegianKrone (kr / NOK)
	NOK

	// HRK -> CroatianKuna (kn / HRK)
	HRK

	// RUB -> RussianRubel (₽ / RUB)
	RUB

	// TRY -> TurkishLira (₺ / TRY)
	TRY

	// AUD -> AustralianDollar ($, A$ or AUD / AUD)
	AUD

	// BRL -> BrazilianReal (R$ / BRL)
	BRL

	// CAD -> CanadianDollar ($, Can$, C$ or CAD / CAD)
	CAD

	// CNY -> ChineseYuan (¥ / CNY)
	CNY

	// HKD -> HongKongDollar ($ or HK$ / HKD)
	HKD

	// IDR -> IndonesianRupiah (Rp / IDR)
	IDR

	// ILS -> IsraeliNewShekel (₪  / ILS)
	ILS

	// INR -> IndianRupee (₹ / INR)
	INR

	// KRW -> SouthKoreanWon (₩ / KRW)
	KRW

	// MXN -> MexicanPeso ($ or Mex$ / MXN)
	MXN

	// MYR -> MalaysianRinggit (RM / MYR)
	MYR

	// NZD -> NewZealandDollar ($ / NZD)
	NZD

	// PHP -> PhilippinePeso (₱ / PHP)
	PHP

	// SGD -> SingaporeDollar (S$ or $ / SGD)
	SGD

	// THB -> ThaiBaht (฿ / THB)
	THB

	// ZAR -> SouthAfricanRand (R / ZAR)
	ZAR

	// ISK -> IcelandicKrona (kr or Íkr / ISK)
	ISK
)

var currencyCodes = [...]string{
	"USD",
	"EUR",
	"JPY",
	"BGN",
	"CZK",
	"DKK",
	"GBP",
	"HUF",
	"LTL",
	"LVL",
	"PLN",
	"RON",
	"SEK",
	"CHF",
	"NOK",
	"HRK",
	"RUB",
	"TRY",
	"AUD",
	"BRL",
	"CAD",
	"CNY",
	"HKD",
	"IDR",
	"ILS",
	"INR",
	"KRW",
	"MXN",
	"MYR",
	"NZD",
	"PHP",
	"SGD",
	"THB",
	"ZAR",
	"ISK",
}

var currencySymbols = [...]string{
	"$",
	"€",
	"¥",
	"лв.",
	"Kč",
	"kr.",
	"£",
	"Ft",
	"Lt",
	"Ls",
	"zł",
	"RON",
	"kr",
	"CHF",
	"kr",
	"kn",
	"₽",
	"₺",
	"A$",
	"R$",
	"C$",
	"¥",
	"HK$",
	"Rp",
	"₪",
	"₹",
	"₩",
	"Mex$",
	"RM",
	"$",
	"₱",
	"S$",
	"฿",
	"R",
	"kr",
}

// Currency reprents a currency as an integer value. This is used
// as sort of an enum in which the constant values defined as currencies
// may be used by users of the library and at the same time are used
// as indeces for the currency code array.
type Currency uint8

// ExchangeRate returns the exchance rate when exchanging money from
// the currency c to the target currency or an error.
func (c Currency) ExchangeRate(target Currency) (decimal.Decimal, error) {
	rate, err := rate(currencyCodes[c], currencyCodes[target])
	if err != nil {
		return decimal.Zero, err
	}
	return rate, nil
}

// ExchangeRateFloat returns the exchange rate when exchanging money
// from the currency c to the target currency as a float64 or an error.
func (c Currency) ExchangeRateFloat(target Currency) (rate float64, exact bool, err error) {
	dec, err := c.ExchangeRate(target)
	if err != nil {
		return -1, false, err
	}
	rate, exact = dec.Float64()
	return rate, exact, nil
}

// Amount creates an Amount for the given currency unit and amount.
func (c Currency) Amount(amount decimal.Decimal) Amount {
	return Amount{Value: amount, Currency: c}
}
<commit_msg>Update creation of Amounts from Currency<commit_after>// Author(s): Michael Koeppl

package dinero

import "github.com/shopspring/decimal"

const (
	// The currencies listed below are the ones supported
	// by the ExchangeRates API and are listed with their
	// symbols and ISO codes.

	// USD -> USDollar ($ / USD)
	USD Currency = iota

	// EUR -> Euro (€ / EUR)
	EUR

	// JPY -> JapaneseYen (¥ / JPY)
	JPY

	// BGN -> BulgarianLev (лв. / BGN)
	BGN

	// CZK -> CzechKoruna (Kč / CZK)
	CZK

	// DKK -> DanishKrone (kr. / DKK)
	DKK

	// GBP -> GreatBritainPound (£ / GBP)
	GBP

	// HUF -> HungarianForint (Ft / HUF)
	HUF

	// LTL -> LithuanianLitas (Lt / LTL)
	LTL

	// LVL -> LatvianLats (Ls / LVL)
	LVL

	// PLN -> PolishZloty (zł / PLN)
	PLN

	// RON -> RomanianLeu (? / RON)
	RON

	// SEK -> SwedishKrona (kr / SEK)
	SEK

	// CHF -> SwissFranc (CHF / CHF)
	CHF

	// NOK -> NorwegianKrone (kr / NOK)
	NOK

	// HRK -> CroatianKuna (kn / HRK)
	HRK

	// RUB -> RussianRubel (₽ / RUB)
	RUB

	// TRY -> TurkishLira (₺ / TRY)
	TRY

	// AUD -> AustralianDollar ($, A$ or AUD / AUD)
	AUD

	// BRL -> BrazilianReal (R$ / BRL)
	BRL

	// CAD -> CanadianDollar ($, Can$, C$ or CAD / CAD)
	CAD

	// CNY -> ChineseYuan (¥ / CNY)
	CNY

	// HKD -> HongKongDollar ($ or HK$ / HKD)
	HKD

	// IDR -> IndonesianRupiah (Rp / IDR)
	IDR

	// ILS -> IsraeliNewShekel (₪  / ILS)
	ILS

	// INR -> IndianRupee (₹ / INR)
	INR

	// KRW -> SouthKoreanWon (₩ / KRW)
	KRW

	// MXN -> MexicanPeso ($ or Mex$ / MXN)
	MXN

	// MYR -> MalaysianRinggit (RM / MYR)
	MYR

	// NZD -> NewZealandDollar ($ / NZD)
	NZD

	// PHP -> PhilippinePeso (₱ / PHP)
	PHP

	// SGD -> SingaporeDollar (S$ or $ / SGD)
	SGD

	// THB -> ThaiBaht (฿ / THB)
	THB

	// ZAR -> SouthAfricanRand (R / ZAR)
	ZAR

	// ISK -> IcelandicKrona (kr or Íkr / ISK)
	ISK
)

var currencyCodes = [...]string{
	"USD",
	"EUR",
	"JPY",
	"BGN",
	"CZK",
	"DKK",
	"GBP",
	"HUF",
	"LTL",
	"LVL",
	"PLN",
	"RON",
	"SEK",
	"CHF",
	"NOK",
	"HRK",
	"RUB",
	"TRY",
	"AUD",
	"BRL",
	"CAD",
	"CNY",
	"HKD",
	"IDR",
	"ILS",
	"INR",
	"KRW",
	"MXN",
	"MYR",
	"NZD",
	"PHP",
	"SGD",
	"THB",
	"ZAR",
	"ISK",
}

var currencySymbols = [...]string{
	"$",
	"€",
	"¥",
	"лв.",
	"Kč",
	"kr.",
	"£",
	"Ft",
	"Lt",
	"Ls",
	"zł",
	"RON",
	"kr",
	"CHF",
	"kr",
	"kn",
	"₽",
	"₺",
	"A$",
	"R$",
	"C$",
	"¥",
	"HK$",
	"Rp",
	"₪",
	"₹",
	"₩",
	"Mex$",
	"RM",
	"$",
	"₱",
	"S$",
	"฿",
	"R",
	"kr",
}

// Currency reprents a currency as an integer value. This is used
// as sort of an enum in which the constant values defined as currencies
// may be used by users of the library and at the same time are used
// as indeces for the currency code array.
type Currency uint8

// ExchangeRate returns the exchance rate when exchanging money from
// the currency c to the target currency or an error.
func (c Currency) ExchangeRate(target Currency) (decimal.Decimal, error) {
	rate, err := rate(currencyCodes[c], currencyCodes[target])
	if err != nil {
		return decimal.Zero, err
	}
	return rate, nil
}

// ExchangeRateFloat returns the exchange rate when exchanging money
// from the currency c to the target currency as a float64 or an error.
func (c Currency) ExchangeRateFloat(target Currency) (rate float64, exact bool, err error) {
	dec, err := c.ExchangeRate(target)
	if err != nil {
		return -1, false, err
	}
	rate, exact = dec.Float64()
	return rate, exact, nil
}

// Amount creates an Amount for the given currency unit and amount.
func (c Currency) Amount(value decimal.Decimal) Amount {
	return NewAmount(value, c)
}

// AmountFromFloat creates an amount for the given currency unit and amount from a float.
func (c Currency) AmountFromFloat(amount float64) Amount {
	return NewAmountFromFloat(amount, c)
}
<|endoftext|>


<commit_before>package hadfield

import (
	"flag"
	"strings"
	"os"
)

type Interface interface {
	Name()      string
	Data()      interface{}
	Category()  string
	Callable()  bool
	Call(cmd Interface, templates Templates, args []string)
}

func PrintUsage(c Interface, templates Templates) {
	templates.Help.Render(os.Stdout, c.Data())
	os.Exit(0)
}

type Commands []Interface

func (cs Commands) PrintUsage(templates Templates) {
	templates.Usage.Render(os.Stderr, cs)
	os.Exit(0)
}

func (cs Commands) Data() []interface{} {
	is := make([]interface{}, len(cs))

	for i,c := range cs {
		is[i] = c.Data()
	}

	return is
}

type Command struct {
	// Run runs the command. It is passed the list of args that came after the
	// command name.
	Run          func(cmd *Command, args []string)

	// Usage returns the one-line usage string. The first word on the line is
	// taken to be the command name.
	Usage        string

	// Short is a single line description used in the help listing.
	Short        string

	// Long is the detailed and formatted message shown in the full help for the
	// command.
	Long         string

	Flag         flag.FlagSet
	CustomFlags  bool
}

func (c *Command) Name() string {
	name := c.Usage
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

func (c *Command) Category() string {
	if c.Run != nil {
		return "Command"
	}
	return "Documentation"
}

func (c *Command) Callable() bool {
	return c.Category() == "Command"
}

func (c *Command) Call(cmd Interface, templates Templates, args []string) {
	c.Flag.Usage = func() { PrintUsage(cmd, templates) }

	if c.CustomFlags {
		args = args[1:]
	} else {
		c.Flag.Parse(args[1:])
		args = c.Flag.Args()
	}

	c.Run(c, args)
	os.Exit(0)
}

func (c *Command) Data() interface{} {
	return map[string]interface{}{
		"Callable": c.Callable(),
		"Category": c.Category(),
		"Usage":    c.Usage,
		"Short":    c.Short,
		"Long":     c.Long,
		"Name":     c.Name(),
	}
}
<commit_msg>Remove Commands#PrintUsage<commit_after>package hadfield

import (
	"flag"
	"strings"
	"os"
)

type Interface interface {
	Name()      string
	Data()      interface{}
	Category()  string
	Callable()  bool
	Call(cmd Interface, templates Templates, args []string)
}

func PrintUsage(c Interface, templates Templates) {
	templates.Help.Render(os.Stdout, c.Data())
	os.Exit(0)
}

type Commands []Interface

func (cs Commands) Data() []interface{} {
	is := make([]interface{}, len(cs))

	for i,c := range cs {
		is[i] = c.Data()
	}

	return is
}

type Command struct {
	// Run runs the command. It is passed the list of args that came after the
	// command name.
	Run          func(cmd *Command, args []string)

	// Usage returns the one-line usage string. The first word on the line is
	// taken to be the command name.
	Usage        string

	// Short is a single line description used in the help listing.
	Short        string

	// Long is the detailed and formatted message shown in the full help for the
	// command.
	Long         string

	Flag         flag.FlagSet
	CustomFlags  bool
}

func (c *Command) Name() string {
	name := c.Usage
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

func (c *Command) Category() string {
	if c.Run != nil {
		return "Command"
	}
	return "Documentation"
}

func (c *Command) Callable() bool {
	return c.Category() == "Command"
}

func (c *Command) Call(cmd Interface, templates Templates, args []string) {
	c.Flag.Usage = func() { PrintUsage(cmd, templates) }

	if c.CustomFlags {
		args = args[1:]
	} else {
		c.Flag.Parse(args[1:])
		args = c.Flag.Args()
	}

	c.Run(c, args)
	os.Exit(0)
}

func (c *Command) Data() interface{} {
	return map[string]interface{}{
		"Callable": c.Callable(),
		"Category": c.Category(),
		"Usage":    c.Usage,
		"Short":    c.Short,
		"Long":     c.Long,
		"Name":     c.Name(),
	}
}
<|endoftext|>


<commit_before>// +build integration

package test

import (
	"fmt"
	"os/exec"
	"strings"
	"testing"
)

func TestNew(t *testing.T) {
	trySuite(t, testNew, retryCount)
}

func testNew(t *t) {
	t.Parallel()

	tcs := []struct {
		svcName    string
		sType      string
		skipBuild  bool
		skipProtoc bool
	}{
		{svcName: "foobarsvc", sType: "service"},
		{svcName: "foobarfn", sType: "function"},
		{svcName: "foobarweb", sType: "web", skipProtoc: true, skipBuild: true}, // web service has no proto generated
		{svcName: "foobarapi", sType: "api", skipBuild: true},                   // api service actually fails build out of the box because it's supposed to point to a service proto
		{svcName: "foo-bar", sType: "service"},
		{svcName: "foo-barfn", sType: "function"},
		{svcName: "foo-barweb", sType: "web", skipProtoc: true, skipBuild: true}, // web service has no proto generated
		{svcName: "foo-barapi", sType: "api", skipBuild: true},                   // api service actually fails build out of the box because it's supposed to point to a service proto
		{svcName: "foo-bar-baz", sType: "service"},
	}

	for _, tc := range tcs {
		t.t.Run(tc.svcName, func(t *testing.T) {
			defer func() {
				exec.Command("rm", "-r", "./"+tc.svcName).CombinedOutput()
			}()
			outp, err := exec.Command("micro", "new", "--type", tc.sType, tc.svcName).CombinedOutput()
			if err != nil {
				t.Fatal(err)
				return
			}
			if !tc.skipProtoc && !strings.Contains(string(outp), "protoc") {
				t.Fatalf("micro new lacks protobuf install instructions %v", string(outp))
				return
			}

			lines := strings.Split(string(outp), "\n")
			// executing install instructions
			for _, line := range lines {
				if strings.HasPrefix(line, "go get") {
					parts := strings.Split(line, " ")
					getOutp, getErr := exec.Command(parts[0], parts[1:]...).CombinedOutput()
					if getErr != nil {
						t.Fatal(string(getOutp))
						return
					}
				}
				if !tc.skipProtoc && strings.HasPrefix(line, "make proto") {
					mp := strings.Split(line, " ")
					protocCmd := exec.Command(mp[0], mp[1:]...)
					protocCmd.Dir = "./" + tc.svcName
					pOutp, pErr := protocCmd.CombinedOutput()
					if pErr != nil {
						t.Log("That didn't work ", pErr)
						t.Fatal(string(pOutp))
						return
					}
				}
			}
			if tc.skipBuild {
				return
			}
			buildCommand := exec.Command("go", "build")
			buildCommand.Dir = "./" + tc.svcName
			outp, err = buildCommand.CombinedOutput()
			if err != nil {
				t.Fatal(string(outp))
				return
			}

		})
	}

}

func TestWrongCommands(t *testing.T) {
	trySuite(t, testWrongCommands, retryCount)
}

func testWrongCommands(t *t) {
	// @TODO this is obviously bad that we have to start a server for this. Why?
	// What happens is in `cmd/cmd.go` `/service/store/cli/util.go`.SetupCommand is called
	// which does not run for builtin services and help etc but there is no such exception for
	// missing/unrecognized commands, so the behaviour below will only happen if a `micro server`
	// is running. This is most likely because some config/auth wrapper in the background failing.
	// Fix this later.
	serv := newServer(t)
	defer serv.close()
	if err := serv.launch(); err != nil {
		return
	}

	t.Parallel()

	comm := exec.Command("micro", serv.envFlag())
	outp, err := comm.CombinedOutput()
	if err == nil {
		t.Fatal("Missing command should error")
	}

	if !strings.Contains(string(outp), "No command") {
		t.Fatalf("Unexpected output for no command: %v", string(outp))
	}

	comm = exec.Command("micro", serv.envFlag(), "asdasd")
	outp, err = comm.CombinedOutput()
	if err == nil {
		t.Fatal("Wrong command should error")
	}

	if !strings.Contains(string(outp), "Unrecognized micro command") {
		t.Fatalf("Unexpected output for unrecognized command: %v", string(outp))
	}

	comm = exec.Command("micro", serv.envFlag(), "config", "asdasd")
	outp, err = comm.CombinedOutput()
	if err == nil {
		t.Fatal("Wrong subcommand should error")
	}

	// @todod for some reason this one returns multiple lines so we don't check for line count now
	if !strings.Contains(string(outp), "Unrecognized subcommand for micro config") {
		t.Fatalf("Unexpected output for unrecognized subcommand: %v", string(outp))
	}
}

// TestHelps ensures all `micro [command name] help` && `micro [command name] --help` commands are working.
func TestHelps(t *testing.T) {
	trySuite(t, testHelps, retryCount)
}

func testHelps(t *t) {
	comm := exec.Command("micro", "help")
	outp, err := comm.CombinedOutput()
	if err != nil {
		t.Fatal(err)
	}
	commands := strings.Split(strings.Split(string(outp), "COMMANDS:")[1], "GLOBAL OPTIONS:")[0]
	for _, line := range strings.Split(commands, "\n") {
		trimmed := strings.TrimSpace(line)
		if len(trimmed) == 0 {
			continue
		}
		// no help for help ;)
		if strings.Contains(trimmed, "help") {
			continue
		}
		commandName := strings.Split(trimmed, " ")[0]
		comm = exec.Command("micro", commandName, "--help")
		outp, err = comm.CombinedOutput()

		if err != nil {
			t.Fatal(fmt.Errorf("Command %v output is wrong: %v", commandName, string(outp)))
			break
		}
		if !strings.Contains(string(outp), "micro "+commandName+" -") {
			t.Fatal(commandName + " output is wrong: " + string(outp))
			break
		}
	}
}

func TestUnrecognisedCommand(t *testing.T) {
	t.Parallel()
	outp, _ := exec.Command("micro", "foobar").CombinedOutput()
	if !strings.Contains(string(outp), "Unrecognized micro command: foobar. Please refer to 'micro --help'") {
		t.Fatalf("micro foobar does not return correct error %v", string(outp))
		return
	}
}

func TestPlatformErrorLocalSource(t *testing.T) {
	t.Parallel()
	// @todo reintroduce this test as a change after the creation of this test broke it
	return
	outp, _ := exec.Command("micro", "-env=platform", "run", "example-service").CombinedOutput()
	if !strings.Contains(string(outp), "Local sources are not yet supported on m3o") {
		t.Fatalf("Local source does not return expected error %v", string(outp))
		return
	}
}
<commit_msg>test: update no command found test<commit_after>// +build integration

package test

import (
	"fmt"
	"os/exec"
	"strings"
	"testing"
)

func TestNew(t *testing.T) {
	trySuite(t, testNew, retryCount)
}

func testNew(t *t) {
	t.Parallel()

	tcs := []struct {
		svcName    string
		sType      string
		skipBuild  bool
		skipProtoc bool
	}{
		{svcName: "foobarsvc", sType: "service"},
		{svcName: "foobarfn", sType: "function"},
		{svcName: "foobarweb", sType: "web", skipProtoc: true, skipBuild: true}, // web service has no proto generated
		{svcName: "foobarapi", sType: "api", skipBuild: true},                   // api service actually fails build out of the box because it's supposed to point to a service proto
		{svcName: "foo-bar", sType: "service"},
		{svcName: "foo-barfn", sType: "function"},
		{svcName: "foo-barweb", sType: "web", skipProtoc: true, skipBuild: true}, // web service has no proto generated
		{svcName: "foo-barapi", sType: "api", skipBuild: true},                   // api service actually fails build out of the box because it's supposed to point to a service proto
		{svcName: "foo-bar-baz", sType: "service"},
	}

	for _, tc := range tcs {
		t.t.Run(tc.svcName, func(t *testing.T) {
			defer func() {
				exec.Command("rm", "-r", "./"+tc.svcName).CombinedOutput()
			}()
			outp, err := exec.Command("micro", "new", "--type", tc.sType, tc.svcName).CombinedOutput()
			if err != nil {
				t.Fatal(err)
				return
			}
			if !tc.skipProtoc && !strings.Contains(string(outp), "protoc") {
				t.Fatalf("micro new lacks protobuf install instructions %v", string(outp))
				return
			}

			lines := strings.Split(string(outp), "\n")
			// executing install instructions
			for _, line := range lines {
				if strings.HasPrefix(line, "go get") {
					parts := strings.Split(line, " ")
					getOutp, getErr := exec.Command(parts[0], parts[1:]...).CombinedOutput()
					if getErr != nil {
						t.Fatal(string(getOutp))
						return
					}
				}
				if !tc.skipProtoc && strings.HasPrefix(line, "make proto") {
					mp := strings.Split(line, " ")
					protocCmd := exec.Command(mp[0], mp[1:]...)
					protocCmd.Dir = "./" + tc.svcName
					pOutp, pErr := protocCmd.CombinedOutput()
					if pErr != nil {
						t.Log("That didn't work ", pErr)
						t.Fatal(string(pOutp))
						return
					}
				}
			}
			if tc.skipBuild {
				return
			}
			buildCommand := exec.Command("go", "build")
			buildCommand.Dir = "./" + tc.svcName
			outp, err = buildCommand.CombinedOutput()
			if err != nil {
				t.Fatal(string(outp))
				return
			}

		})
	}

}

func TestWrongCommands(t *testing.T) {
	trySuite(t, testWrongCommands, retryCount)
}

func testWrongCommands(t *t) {
	// @TODO this is obviously bad that we have to start a server for this. Why?
	// What happens is in `cmd/cmd.go` `/service/store/cli/util.go`.SetupCommand is called
	// which does not run for builtin services and help etc but there is no such exception for
	// missing/unrecognized commands, so the behaviour below will only happen if a `micro server`
	// is running. This is most likely because some config/auth wrapper in the background failing.
	// Fix this later.
	serv := newServer(t)
	defer serv.close()
	if err := serv.launch(); err != nil {
		return
	}

	t.Parallel()

	comm := exec.Command("micro", serv.envFlag())
	outp, err := comm.CombinedOutput()
	if err == nil {
		t.Fatal("Missing command should error")
	}

	if !strings.Contains(string(outp), "No command") {
		t.Fatalf("Unexpected output for no command: %v", string(outp))
	}

	comm = exec.Command("micro", serv.envFlag(), "asdasd")
	outp, err = comm.CombinedOutput()
	if err == nil {
		t.Fatal("Wrong command should error")
	}

	if !strings.Contains(string(outp), "Unrecognized micro command") {
		t.Fatalf("Unexpected output for unrecognized command: %v", string(outp))
	}

	comm = exec.Command("micro", serv.envFlag(), "config", "asdasd")
	outp, err = comm.CombinedOutput()
	if err == nil {
		t.Fatal("Wrong subcommand should error")
	}

	// @todod for some reason this one returns multiple lines so we don't check for line count now
	if !strings.Contains(string(outp), "Unrecognized subcommand for micro config") {
		t.Fatalf("Unexpected output for unrecognized subcommand: %v", string(outp))
	}
}

// TestHelps ensures all `micro [command name] help` && `micro [command name] --help` commands are working.
func TestHelps(t *testing.T) {
	trySuite(t, testHelps, retryCount)
}

func testHelps(t *t) {
	comm := exec.Command("micro", "help")
	outp, err := comm.CombinedOutput()
	if err != nil {
		t.Fatal(err)
	}
	commands := strings.Split(strings.Split(string(outp), "COMMANDS:")[1], "GLOBAL OPTIONS:")[0]
	for _, line := range strings.Split(commands, "\n") {
		trimmed := strings.TrimSpace(line)
		if len(trimmed) == 0 {
			continue
		}
		// no help for help ;)
		if strings.Contains(trimmed, "help") {
			continue
		}
		commandName := strings.Split(trimmed, " ")[0]
		comm = exec.Command("micro", commandName, "--help")
		outp, err = comm.CombinedOutput()

		if err != nil {
			t.Fatal(fmt.Errorf("Command %v output is wrong: %v", commandName, string(outp)))
			break
		}
		if !strings.Contains(string(outp), "micro "+commandName+" -") {
			t.Fatal(commandName + " output is wrong: " + string(outp))
			break
		}
	}
}

func TestUnrecognisedCommand(t *testing.T) {
	t.Parallel()
	outp, _ := exec.Command("micro", "foobar").CombinedOutput()
	if !strings.Contains(string(outp), "No command provided to micro. Please refer to 'micro --help'") {
		t.Fatalf("micro foobar does not return correct error %v", string(outp))
		return
	}
}

func TestPlatformErrorLocalSource(t *testing.T) {
	t.Parallel()
	// @todo reintroduce this test as a change after the creation of this test broke it
	return
	outp, _ := exec.Command("micro", "-env=platform", "run", "example-service").CombinedOutput()
	if !strings.Contains(string(outp), "Local sources are not yet supported on m3o") {
		t.Fatalf("Local source does not return expected error %v", string(outp))
		return
	}
}
<|endoftext|>


<commit_before>package device

import (
	"sync"

	"github.com/tarm/serial"
)

// Manager manages devices that are plugged into the system. It supports auto
// detection of devices.
//
// Serial ports are opened each for a device, and a clean API for communicating
// is provided via Read, Write and Flush methods.
//
// The devices are monitored via udev, and any changes that requires reloading
// of the  ports are handled by reloading the ports to the devices.
//
// This is safe to use concurrently in multiple goroutines
type Manager struct {
	devices map[string]serial.Config
	conn    []*Conn
	mu      sync.RWMutex
}

// AddDevice adds device name to the manager
func (m *Manager) AddDevice(name string) error {
	cfg := serial.Config{Name: name}
	m.mu.Lock()
	m.devices[name] = cfg
	m.mu.Unlock()
	return nil
}

// RemoveDevice removes device name from the manager
func (m *Manager) RemoveDevice(name string) error {
	m.mu.RLock()
	delete(m.devices, name)
	m.mu.RUnlock()
	return nil
}

// Conn is a device serial connection
type Conn struct {
	device serial.Config
	port   *serial.Port
	isOpen bool
}

// Open opens a serial port to the undelying device
func (c *Conn) Open() error {
	p, err := serial.OpenPort(&c.device)
	if err != nil {
		return nil
	}
	c.port = p
	c.isOpen = true
	return nil
}

// Close closes the port helt by *Conn.
func (c *Conn) Close() error {
	if c.isOpen {
		return c.port.Close()
	}
	return nil
}
<commit_msg>Add logic for device monitoring<commit_after>package device

import (
	"fmt"
	"sync"

	"github.com/jochenvg/go-udev"
	"github.com/tarm/serial"
)

// Manager manages devices that are plugged into the system. It supports auto
// detection of devices.
//
// Serial ports are opened each for a device, and a clean API for communicating
// is provided via Read, Write and Flush methods.
//
// The devices are monitored via udev, and any changes that requires reloading
// of the  ports are handled by reloading the ports to the devices.
//
// This is safe to use concurrently in multiple goroutines
type Manager struct {
	devices map[string]serial.Config
	conn    []*Conn
	mu      sync.RWMutex
	monitor *udev.Monitor
	done    chan struct{}
	stop    chan struct{}
}

func (m *Manager) Init() {
	u := udev.Udev{}
	monitor := u.NewMonitorFromNetlink("udev")
	devCh, err := monitor.DeviceChan(m.done)
	if err != nil {
		panic(err)
	}
	m.monitor = monitor
	go func() {
	stop:
		for {
			select {
			case d := <-devCh:
				switch d.Action() {
				case "add":
					fmt.Printf(" new device added ad %s\n", d.Devpath())
					m.AddDevice(d.Devpath())
				case "remove":
					fmt.Printf(" %s was removed\n", d.Devpath())
					m.RemoveDevice(d.Devpath())
				default:
					fmt.Println(d.Action())
				}
			case quit := <-m.stop:
				m.done <- quit
				break stop
			}
		}
	}()
}

// AddDevice adds device name to the manager
func (m *Manager) AddDevice(name string) error {
	cfg := serial.Config{Name: name}
	m.mu.Lock()
	m.devices[name] = cfg
	m.mu.Unlock()
	return nil
}

// RemoveDevice removes device name from the manager
func (m *Manager) RemoveDevice(name string) error {
	m.mu.RLock()
	delete(m.devices, name)
	m.mu.RUnlock()
	return nil
}

// Conn is a device serial connection
type Conn struct {
	device serial.Config
	port   *serial.Port
	isOpen bool
}

// Open opens a serial port to the undelying device
func (c *Conn) Open() error {
	p, err := serial.OpenPort(&c.device)
	if err != nil {
		return nil
	}
	c.port = p
	c.isOpen = true
	return nil
}

// Close closes the port helt by *Conn.
func (c *Conn) Close() error {
	if c.isOpen {
		return c.port.Close()
	}
	return nil
}
<|endoftext|>


<commit_before>package device

import (
	"errors"
	"fmt"
	"github.com/antonienko/goandroid/adbutility"
	"strings"
	"time"
)

const (
	PROP_BOOT_STATUS = "sys.boot_completed" // Property that represents devices boot status
)

// Device struct represents adb capable android device, its serial number and
// associated adb endpoint. Device timeout represents all device specific adb operation timeouts.
type Device struct {
	Serial      string                 // Device serial number
	Timeout     int                    // Timeout in seconds for all adb and shell operations
	AdbEndpoint adbutility.AdbEndpoint // Adb endpoint for this device
}

// NewDevice method creates a new Device based on given serial number, adb operation timeout and
// connecting adb enpoint for this device.
func NewDevice(serial string, timeout int, endPoint adbutility.AdbEndpoint) Device {
	return Device{Serial: serial, Timeout: timeout, AdbEndpoint: endPoint}
}

// IsAvailable method checkes availability of the device in the adb endpoint. It returns
// boolean value indicating availibility status of the device and error in case of
// something went wrong while doing adb operations.
func (dev Device) IsAvailable() (bool, error) {
	devices, err := dev.AdbEndpoint.GetAttachedDevices(dev.Timeout)
	if err != nil {
		return false, err
	}
	for index := range devices {
		if dev.Serial == devices[index] {
			return true, nil
		}
	}
	return false, nil
}

// Adb method allows to execute adb command on this device instance. It takes a adb command
// and optional list of arguments. It returns outout of the adb command and error in case
// of something went wrong. Please note that adb will timeout within default adb operation
// timeout.
func (dev Device) Adb(command string, args ...string) (string, error) {
	return dev.AdbEndpoint.Adb(dev.Timeout, append([]string{"-s", dev.Serial, command}, args...)...)
}

// Shell method allows to execute adb shell commands on associated device instance. It takes
// a shell comand and a list of opetional command arguments. It returns adb shell command output
// and stderr output combiled as string and error in case of adb operation failure or timeout.
func (dev Device) Shell(command string, args ...string) (string, error) {
	return dev.Adb("shell", append([]string{command}, args...)...)
}

// GetProperty method is used to extract a device property value based on the specified
// key. It returns string representation of the property value and error in case of
// adb operation failure or specified key is not located.
func (dev Device) GetProperty(key string) (string, error) {
	prop, err := dev.GetAllProperties()
	if err != nil {
		return "", err
	}
	val, ok := prop[key]
	if !ok {
		return "", errors.New(fmt.Sprintf("Key [%s] is not found in device properties", key))
	}
	return val, nil
}

// GetAllProperties method returns a map of [key: value] pairs of all device
// properties. It also returns error in case of adb operation failure.
func (dev Device) GetAllProperties() (map[string]string, error) {
	prop_map := make(map[string]string)
	prop, err := dev.Shell("getprop")
	if err != nil {
		return prop_map, err
	}
	lines := strings.Split(prop, "\n")
	for index := range lines {
		parts := strings.Split(lines[index], ":")
		if len(parts) == 2 {
			key := strings.TrimSpace(strings.Replace(strings.Replace(parts[0], "[", "", -1), "]", "", -1))
			value := strings.TrimSpace(strings.Replace(strings.Replace(parts[1], "[", "", -1), "]", "", -1))
			prop_map[key] = value
		}
	}
	return prop_map, nil
}

// Pull method pulls a file or directory from specified source path to specified
// destination path. Specified path must exist on device and specified destination path
// must exist on local machine. It returns command output and error in case of adb operation
// failed.
func (dev Device) Pull(src string, dst string) (string, error) {
	return dev.Adb("pull", src, dst)
}

// Push method pushes a file or directory from local machine from specified source
// path to device in specified destination path. Specified source path mast exist on
// local machine and destinaltion path mast exist on device. It returns command output
// and error in case of adb operation failed.
func (dev Device) Push(src string, dst string) (string, error) {
	return dev.Adb("push", src, dst)
}

// WaitForAvailability method waits for associatred device to be available or
// attached to the adb endpoint within specified timeout period. It returns
// error in case of adb operation failure or timeout.
func (dev Device) WaitForAvailability(timeout int) error {
	_, err := dev.AdbEndpoint.Adb(timeout, "-s", dev.Serial, "wait-for-device")
	return err
}

// Root method gains root access to the device, for this the device must be rooted.
// It returns error in case of root failed or adb operation failed.
func (dev Device) Root() error {
	out, err := dev.Adb("root")
	if err != nil {
		return err
	}
	if !strings.Contains(out, "restarting adbd as root") && !strings.Contains(out, "adbd is already running as root") {
		return errors.New("Unable to gain root access to device")
	}
	return dev.WaitForAvailability(dev.Timeout)
}

// Reboot method reboots associated device. It waits for the device to become
// available again within specified restart timeout and the device to complete
// its boot sequence within specified boot timeout. It returns error in case
// of adb operation failure or timeout has been reached.
func (dev Device) Reboot(restartTimeout int, bootTimeout int) error {
	_, err := dev.Adb("reboot")
	if err != nil {
		return err
	}
	err = dev.WaitForAvailability(restartTimeout)
	if err != nil {
		return err
	}
	return dev.WaitForBootToComplete(bootTimeout)
}

// WaitForBootToComplete method waits for the device to complete its boot sequence
// within specified ammount of timeout. It returns error in case of adb operation failure
// or timeout has been reached.
func (dev Device) WaitForBootToComplete(bootTimeout int) error {
	startTime := time.Now()
	for {
		currentTime := time.Now()
		delta := currentTime.Sub(startTime)
		if delta.Seconds() >= float64(bootTimeout) {
			break
		}
		val, err := dev.GetProperty(PROP_BOOT_STATUS)
		if err != nil {
			continue
		}
		if val == "1" {
			return nil
		}
	}
	return errors.New("Device not completed boot sequence in timeout")
}
<commit_msg>Check internet connection<commit_after>package device

import (
	"errors"
	"fmt"
	"github.com/antonienko/goandroid/adbutility"
	"strings"
	"time"
)

const (
	PROP_BOOT_STATUS = "sys.boot_completed" // Property that represents devices boot status
)

// Device struct represents adb capable android device, its serial number and
// associated adb endpoint. Device timeout represents all device specific adb operation timeouts.
type Device struct {
	Serial      string                 // Device serial number
	Timeout     int                    // Timeout in seconds for all adb and shell operations
	AdbEndpoint adbutility.AdbEndpoint // Adb endpoint for this device
}

// NewDevice method creates a new Device based on given serial number, adb operation timeout and
// connecting adb enpoint for this device.
func NewDevice(serial string, timeout int, endPoint adbutility.AdbEndpoint) Device {
	return Device{Serial: serial, Timeout: timeout, AdbEndpoint: endPoint}
}

// IsAvailable method checkes availability of the device in the adb endpoint. It returns
// boolean value indicating availibility status of the device and error in case of
// something went wrong while doing adb operations.
func (dev Device) IsAvailable() (bool, error) {
	devices, err := dev.AdbEndpoint.GetAttachedDevices(dev.Timeout)
	if err != nil {
		return false, err
	}
	for index := range devices {
		if dev.Serial == devices[index] {
			return true, nil
		}
	}
	return false, nil
}

// Adb method allows to execute adb command on this device instance. It takes a adb command
// and optional list of arguments. It returns outout of the adb command and error in case
// of something went wrong. Please note that adb will timeout within default adb operation
// timeout.
func (dev Device) Adb(command string, args ...string) (string, error) {
	return dev.AdbEndpoint.Adb(dev.Timeout, append([]string{"-s", dev.Serial, command}, args...)...)
}

// Shell method allows to execute adb shell commands on associated device instance. It takes
// a shell comand and a list of opetional command arguments. It returns adb shell command output
// and stderr output combiled as string and error in case of adb operation failure or timeout.
func (dev Device) Shell(command string, args ...string) (string, error) {
	return dev.Adb("shell", append([]string{command}, args...)...)
}

// GetProperty method is used to extract a device property value based on the specified
// key. It returns string representation of the property value and error in case of
// adb operation failure or specified key is not located.
func (dev Device) GetProperty(key string) (string, error) {
	prop, err := dev.GetAllProperties()
	if err != nil {
		return "", err
	}
	val, ok := prop[key]
	if !ok {
		return "", errors.New(fmt.Sprintf("Key [%s] is not found in device properties", key))
	}
	return val, nil
}

// GetAllProperties method returns a map of [key: value] pairs of all device
// properties. It also returns error in case of adb operation failure.
func (dev Device) GetAllProperties() (map[string]string, error) {
	prop_map := make(map[string]string)
	prop, err := dev.Shell("getprop")
	if err != nil {
		return prop_map, err
	}
	lines := strings.Split(prop, "\n")
	for index := range lines {
		parts := strings.Split(lines[index], ":")
		if len(parts) == 2 {
			key := strings.TrimSpace(strings.Replace(strings.Replace(parts[0], "[", "", -1), "]", "", -1))
			value := strings.TrimSpace(strings.Replace(strings.Replace(parts[1], "[", "", -1), "]", "", -1))
			prop_map[key] = value
		}
	}
	return prop_map, nil
}

// Pull method pulls a file or directory from specified source path to specified
// destination path. Specified path must exist on device and specified destination path
// must exist on local machine. It returns command output and error in case of adb operation
// failed.
func (dev Device) Pull(src string, dst string) (string, error) {
	return dev.Adb("pull", src, dst)
}

// Push method pushes a file or directory from local machine from specified source
// path to device in specified destination path. Specified source path mast exist on
// local machine and destinaltion path mast exist on device. It returns command output
// and error in case of adb operation failed.
func (dev Device) Push(src string, dst string) (string, error) {
	return dev.Adb("push", src, dst)
}

// WaitForAvailability method waits for associatred device to be available or
// attached to the adb endpoint within specified timeout period. It returns
// error in case of adb operation failure or timeout.
func (dev Device) WaitForAvailability(timeout int) error {
	_, err := dev.AdbEndpoint.Adb(timeout, "-s", dev.Serial, "wait-for-device")
	return err
}

// Root method gains root access to the device, for this the device must be rooted.
// It returns error in case of root failed or adb operation failed.
func (dev Device) Root() error {
	out, err := dev.Adb("root")
	if err != nil {
		return err
	}
	if !strings.Contains(out, "restarting adbd as root") && !strings.Contains(out, "adbd is already running as root") {
		return errors.New("Unable to gain root access to device")
	}
	return dev.WaitForAvailability(dev.Timeout)
}

// Reboot method reboots associated device. It waits for the device to become
// available again within specified restart timeout and the device to complete
// its boot sequence within specified boot timeout. It returns error in case
// of adb operation failure or timeout has been reached.
func (dev Device) Reboot(restartTimeout int, bootTimeout int) error {
	_, err := dev.Adb("reboot")
	if err != nil {
		return err
	}
	err = dev.WaitForAvailability(restartTimeout)
	if err != nil {
		return err
	}
	return dev.WaitForBootToComplete(bootTimeout)
}

// WaitForBootToComplete method waits for the device to complete its boot sequence
// within specified ammount of timeout. It returns error in case of adb operation failure
// or timeout has been reached.
func (dev Device) WaitForBootToComplete(bootTimeout int) error {
	startTime := time.Now()
	for {
		currentTime := time.Now()
		delta := currentTime.Sub(startTime)
		if delta.Seconds() >= float64(bootTimeout) {
			break
		}
		val, err := dev.GetProperty(PROP_BOOT_STATUS)
		if err != nil {
			continue
		}
		if val == "1" {
			return nil
		}
	}
	return errors.New("Device not completed boot sequence in timeout")
}

func (dev Device) CheckInternetConnection() (bool, error) {
	ret, err := dev.Shell("dumpsys", "telephony.registry", "|", "grep", "mDataConnectionState")
	if err != nil {
		return false, err
	}
	return strings.Contains(ret, "mDataConnectionState=2"), nil
}
<|endoftext|>