<commit_before>package main

import (
	"bytes"
	"strings"
)

func createKey(path string) string {
	var buffer bytes.Buffer
	buffer.WriteString(*uploadpath)
	if *uploadpath == "/" {
		if startWith(path, "/") {
			return path
		}
		buffer.WriteString(path)
		return buffer.String()
	} else {
		if !endWith(*uploadpath, "/") && !startWith(path, "/") {
			buffer.WriteString("/")
		}
		if endWith(*uploadpath, "/") && startWith(path, "/") {
			buffer.WriteString(string(path[1:]))
		} else {
			buffer.WriteString(path)
		}
		return buffer.String()
	}
}

func startWith(original, substring string) bool {
	if len(substring) > len(original) {
		return false
	}
	str := string(original[0:len(substring)])
	return str == substring
}

func endWith(original, substring string) bool {
	if len(substring) > len(original) {
		return false
	}
	str := string(original[len(original)-len(substring) : len(original)])
	return str == substring
}

func getFileName(filepath string) string {
	if *rename != "" {
		return *rename
	}
	index := strings.LastIndex(filepath, "/")
	if index == -1 {
		return filepath
	}
	return filepath[index+1:]
}

func getFolderName(filepath string) string {
	if endWith(filepath, "/") {
		pos := strings.LastIndex(string(filepath[:len(filepath)-1]), "/")
		return string(filepath[pos+1 : len(filepath)-1])
	} else {
		pos := strings.LastIndex(filepath, "/")
		return string(filepath[pos+1:])
	}
}

func getPathInsideFolder(path, folder string) string {
	pos := strings.Index(path, folder)
	var result string
	if pos != -1 {
		result = string(path[pos-1:])
	}
	return result
}
<commit_msg>Fix gofmt issue<commit_after>package main

import (
	"bytes"
	"strings"
)

func createKey(path string) string {
	var buffer bytes.Buffer
	buffer.WriteString(*uploadpath)
	if *uploadpath == "/" {
		if startWith(path, "/") {
			return path
		}
		buffer.WriteString(path)
		return buffer.String()
	} else {
		if !endWith(*uploadpath, "/") && !startWith(path, "/") {
			buffer.WriteString("/")
		}
		if endWith(*uploadpath, "/") && startWith(path, "/") {
			buffer.WriteString(string(path[1:]))
		} else {
			buffer.WriteString(path)
		}
		return buffer.String()
	}
}

func startWith(original, substring string) bool {
	if len(substring) > len(original) {
		return false
	}
	str := string(original[0:len(substring)])
	return str == substring
}

func endWith(original, substring string) bool {
	if len(substring) > len(original) {
		return false
	}
	str := string(original[len(original)-len(substring):])
	return str == substring
}

func getFileName(filepath string) string {
	if *rename != "" {
		return *rename
	}
	index := strings.LastIndex(filepath, "/")
	if index == -1 {
		return filepath
	}
	return filepath[index+1:]
}

func getFolderName(filepath string) string {
	if endWith(filepath, "/") {
		pos := strings.LastIndex(string(filepath[:len(filepath)-1]), "/")
		return string(filepath[pos+1 : len(filepath)-1])
	} else {
		pos := strings.LastIndex(filepath, "/")
		return string(filepath[pos+1:])
	}
}

func getPathInsideFolder(path, folder string) string {
	pos := strings.Index(path, folder)
	var result string
	if pos != -1 {
		result = string(path[pos-1:])
	}
	return result
}
<|endoftext|>


<commit_before>package storage

import (
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/BurntSushi/toml"
	"github.com/containers/storage/pkg/idtools"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*IDMappingOptions, error) {
	options := IDMappingOptions{
		HostUIDMapping: true,
		HostGIDMapping: true,
	}
	if subGIDMap == "" && subUIDMap != "" {
		subGIDMap = subUIDMap
	}
	if subUIDMap == "" && subGIDMap != "" {
		subUIDMap = subGIDMap
	}
	if len(GIDMapSlice) == 0 && len(UIDMapSlice) != 0 {
		GIDMapSlice = UIDMapSlice
	}
	if len(UIDMapSlice) == 0 && len(GIDMapSlice) != 0 {
		UIDMapSlice = GIDMapSlice
	}
	if len(UIDMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 {
		UIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())}
	}
	if len(GIDMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 {
		GIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())}
	}

	if subUIDMap != "" && subGIDMap != "" {
		mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to create NewIDMappings for uidmap=%s gidmap=%s", subUIDMap, subGIDMap)
		}
		options.UIDMap = mappings.UIDs()
		options.GIDMap = mappings.GIDs()
	}
	parsedUIDMap, err := idtools.ParseIDMap(UIDMapSlice, "UID")
	if err != nil {
		return nil, errors.Wrapf(err, "failed to create ParseUIDMap UID=%s", UIDMapSlice)
	}
	parsedGIDMap, err := idtools.ParseIDMap(GIDMapSlice, "GID")
	if err != nil {
		return nil, errors.Wrapf(err, "failed to create ParseGIDMap GID=%s", UIDMapSlice)
	}
	options.UIDMap = append(options.UIDMap, parsedUIDMap...)
	options.GIDMap = append(options.GIDMap, parsedGIDMap...)
	if len(options.UIDMap) > 0 {
		options.HostUIDMapping = false
	}
	if len(options.GIDMap) > 0 {
		options.HostGIDMapping = false
	}
	return &options, nil
}

// GetRootlessRuntimeDir returns the runtime directory when running as non root
func GetRootlessRuntimeDir(rootlessUid int) (string, error) {
	runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
	if runtimeDir == "" {
		tmpDir := fmt.Sprintf("/run/user/%d", rootlessUid)
		st, err := os.Stat(tmpDir)
		if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Getuid() && st.Mode().Perm() == 0700 {
			return tmpDir, nil
		}
	}
	tmpDir := fmt.Sprintf("%s/%d", os.TempDir(), rootlessUid)
	if err := os.MkdirAll(tmpDir, 0700); err != nil {
		logrus.Errorf("failed to create %s: %v", tmpDir, err)
	} else {
		st, err := os.Stat(tmpDir)
		if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Getuid() && st.Mode().Perm() == 0700 {
			return tmpDir, nil
		}
	}
	home, err := homeDir()
	if err != nil {
		return "", errors.Wrapf(err, "neither XDG_RUNTIME_DIR nor HOME was set non-empty")
	}
	resolvedHome, err := filepath.EvalSymlinks(home)
	if err != nil {
		return "", errors.Wrapf(err, "cannot resolve %s", home)
	}
	return filepath.Join(resolvedHome, "rundir"), nil
}

// getRootlessDirInfo returns the parent path of where the storage for containers and
// volumes will be in rootless mode
func getRootlessDirInfo(rootlessUid int) (string, string, error) {
	rootlessRuntime, err := GetRootlessRuntimeDir(rootlessUid)
	if err != nil {
		return "", "", err
	}

	dataDir := os.Getenv("XDG_DATA_HOME")
	if dataDir == "" {
		home, err := homeDir()
		if err != nil {
			return "", "", errors.Wrapf(err, "neither XDG_DATA_HOME nor HOME was set non-empty")
		}
		// runc doesn't like symlinks in the rootfs path, and at least
		// on CoreOS /home is a symlink to /var/home, so resolve any symlink.
		resolvedHome, err := filepath.EvalSymlinks(home)
		if err != nil {
			return "", "", errors.Wrapf(err, "cannot resolve %s", home)
		}
		dataDir = filepath.Join(resolvedHome, ".local", "share")
	}
	return dataDir, rootlessRuntime, nil
}

// getRootlessStorageOpts returns the storage opts for containers running as non root
func getRootlessStorageOpts(rootlessUid int) (StoreOptions, error) {
	var opts StoreOptions

	dataDir, rootlessRuntime, err := getRootlessDirInfo(rootlessUid)
	if err != nil {
		return opts, err
	}
	opts.RunRoot = rootlessRuntime
	opts.GraphRoot = filepath.Join(dataDir, "containers", "storage")
	if path, err := exec.LookPath("fuse-overlayfs"); err == nil {
		opts.GraphDriverName = "overlay"
		opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)}
	} else {
		opts.GraphDriverName = "vfs"
	}
	return opts, nil
}

type tomlOptionsConfig struct {
	MountProgram string `toml:"mount_program"`
}

func getTomlStorage(storeOptions *StoreOptions) *tomlConfig {
	config := new(tomlConfig)

	config.Storage.Driver = storeOptions.GraphDriverName
	config.Storage.RunRoot = storeOptions.RunRoot
	config.Storage.GraphRoot = storeOptions.GraphRoot
	for _, i := range storeOptions.GraphDriverOptions {
		s := strings.Split(i, "=")
		if s[0] == "overlay.mount_program" {
			config.Storage.Options.MountProgram = s[1]
		}
	}

	return config
}

// DefaultStoreOptions returns the default storage ops for containers
func DefaultStoreOptions(rootless bool, rootlessUid int) (StoreOptions, error) {
	var (
		defaultRootlessRunRoot   string
		defaultRootlessGraphRoot string
		err                      error
	)
	storageOpts := defaultStoreOptions
	if rootless {
		storageOpts, err = getRootlessStorageOpts(rootlessUid)
		if err != nil {
			return storageOpts, err
		}
	}

	storageConf, err := DefaultConfigFile(rootless)
	if err != nil {
		return storageOpts, err
	}
	if _, err = os.Stat(storageConf); err == nil {
		defaultRootlessRunRoot = storageOpts.RunRoot
		defaultRootlessGraphRoot = storageOpts.GraphRoot
		storageOpts = StoreOptions{}
		ReloadConfigurationFile(storageConf, &storageOpts)
	}

	if !os.IsNotExist(err) {
		return storageOpts, errors.Wrapf(err, "cannot stat %s", storageConf)
	}

	if rootless {
		if err == nil {
			// If the file did not specify a graphroot or runroot,
			// set sane defaults so we don't try and use root-owned
			// directories
			if storageOpts.RunRoot == "" {
				storageOpts.RunRoot = defaultRootlessRunRoot
			}
			if storageOpts.GraphRoot == "" {
				storageOpts.GraphRoot = defaultRootlessGraphRoot
			}
		} else {
			if err := os.MkdirAll(filepath.Dir(storageConf), 0755); err != nil {
				return storageOpts, errors.Wrapf(err, "cannot make directory %s", filepath.Dir(storageConf))
			}
			file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
			if err != nil {
				return storageOpts, errors.Wrapf(err, "cannot open %s", storageConf)
			}

			tomlConfiguration := getTomlStorage(&storageOpts)
			defer file.Close()
			enc := toml.NewEncoder(file)
			if err := enc.Encode(tomlConfiguration); err != nil {
				os.Remove(storageConf)

				return storageOpts, errors.Wrapf(err, "failed to encode %s", storageConf)
			}
		}
	}
	return storageOpts, nil
}

func homeDir() (string, error) {
	home := os.Getenv("HOME")
	if home == "" {
		usr, err := user.Current()
		if err != nil {
			return "", errors.Wrapf(err, "neither XDG_RUNTIME_DIR nor HOME was set non-empty")
		}
		home = usr.HomeDir
	}
	return home, nil
}
<commit_msg>Fix cross compilation code for other platforms<commit_after>package storage

import (
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/BurntSushi/toml"
	"github.com/containers/storage/pkg/idtools"
	"github.com/containers/storage/pkg/system"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*IDMappingOptions, error) {
	options := IDMappingOptions{
		HostUIDMapping: true,
		HostGIDMapping: true,
	}
	if subGIDMap == "" && subUIDMap != "" {
		subGIDMap = subUIDMap
	}
	if subUIDMap == "" && subGIDMap != "" {
		subUIDMap = subGIDMap
	}
	if len(GIDMapSlice) == 0 && len(UIDMapSlice) != 0 {
		GIDMapSlice = UIDMapSlice
	}
	if len(UIDMapSlice) == 0 && len(GIDMapSlice) != 0 {
		UIDMapSlice = GIDMapSlice
	}
	if len(UIDMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 {
		UIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())}
	}
	if len(GIDMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 {
		GIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())}
	}

	if subUIDMap != "" && subGIDMap != "" {
		mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to create NewIDMappings for uidmap=%s gidmap=%s", subUIDMap, subGIDMap)
		}
		options.UIDMap = mappings.UIDs()
		options.GIDMap = mappings.GIDs()
	}
	parsedUIDMap, err := idtools.ParseIDMap(UIDMapSlice, "UID")
	if err != nil {
		return nil, errors.Wrapf(err, "failed to create ParseUIDMap UID=%s", UIDMapSlice)
	}
	parsedGIDMap, err := idtools.ParseIDMap(GIDMapSlice, "GID")
	if err != nil {
		return nil, errors.Wrapf(err, "failed to create ParseGIDMap GID=%s", UIDMapSlice)
	}
	options.UIDMap = append(options.UIDMap, parsedUIDMap...)
	options.GIDMap = append(options.GIDMap, parsedGIDMap...)
	if len(options.UIDMap) > 0 {
		options.HostUIDMapping = false
	}
	if len(options.GIDMap) > 0 {
		options.HostGIDMapping = false
	}
	return &options, nil
}

// GetRootlessRuntimeDir returns the runtime directory when running as non root
func GetRootlessRuntimeDir(rootlessUid int) (string, error) {
	runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
	if runtimeDir == "" {
		tmpDir := fmt.Sprintf("/run/user/%d", rootlessUid)
		st, err := system.Stat(tmpDir)
		if err == nil && int(st.UID()) == os.Getuid() && st.Mode() == 0700 {
			return tmpDir, nil
		}
	}
	tmpDir := fmt.Sprintf("%s/%d", os.TempDir(), rootlessUid)
	if err := os.MkdirAll(tmpDir, 0700); err != nil {
		logrus.Errorf("failed to create %s: %v", tmpDir, err)
	} else {
		return tmpDir, nil
	}
	home, err := homeDir()
	if err != nil {
		return "", errors.Wrapf(err, "neither XDG_RUNTIME_DIR nor HOME was set non-empty")
	}
	resolvedHome, err := filepath.EvalSymlinks(home)
	if err != nil {
		return "", errors.Wrapf(err, "cannot resolve %s", home)
	}
	return filepath.Join(resolvedHome, "rundir"), nil
}

// getRootlessDirInfo returns the parent path of where the storage for containers and
// volumes will be in rootless mode
func getRootlessDirInfo(rootlessUid int) (string, string, error) {
	rootlessRuntime, err := GetRootlessRuntimeDir(rootlessUid)
	if err != nil {
		return "", "", err
	}

	dataDir := os.Getenv("XDG_DATA_HOME")
	if dataDir == "" {
		home, err := homeDir()
		if err != nil {
			return "", "", errors.Wrapf(err, "neither XDG_DATA_HOME nor HOME was set non-empty")
		}
		// runc doesn't like symlinks in the rootfs path, and at least
		// on CoreOS /home is a symlink to /var/home, so resolve any symlink.
		resolvedHome, err := filepath.EvalSymlinks(home)
		if err != nil {
			return "", "", errors.Wrapf(err, "cannot resolve %s", home)
		}
		dataDir = filepath.Join(resolvedHome, ".local", "share")
	}
	return dataDir, rootlessRuntime, nil
}

// getRootlessStorageOpts returns the storage opts for containers running as non root
func getRootlessStorageOpts(rootlessUid int) (StoreOptions, error) {
	var opts StoreOptions

	dataDir, rootlessRuntime, err := getRootlessDirInfo(rootlessUid)
	if err != nil {
		return opts, err
	}
	opts.RunRoot = rootlessRuntime
	opts.GraphRoot = filepath.Join(dataDir, "containers", "storage")
	if path, err := exec.LookPath("fuse-overlayfs"); err == nil {
		opts.GraphDriverName = "overlay"
		opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)}
	} else {
		opts.GraphDriverName = "vfs"
	}
	return opts, nil
}

type tomlOptionsConfig struct {
	MountProgram string `toml:"mount_program"`
}

func getTomlStorage(storeOptions *StoreOptions) *tomlConfig {
	config := new(tomlConfig)

	config.Storage.Driver = storeOptions.GraphDriverName
	config.Storage.RunRoot = storeOptions.RunRoot
	config.Storage.GraphRoot = storeOptions.GraphRoot
	for _, i := range storeOptions.GraphDriverOptions {
		s := strings.Split(i, "=")
		if s[0] == "overlay.mount_program" {
			config.Storage.Options.MountProgram = s[1]
		}
	}

	return config
}

// DefaultStoreOptions returns the default storage ops for containers
func DefaultStoreOptions(rootless bool, rootlessUid int) (StoreOptions, error) {
	var (
		defaultRootlessRunRoot   string
		defaultRootlessGraphRoot string
		err                      error
	)
	storageOpts := defaultStoreOptions
	if rootless {
		storageOpts, err = getRootlessStorageOpts(rootlessUid)
		if err != nil {
			return storageOpts, err
		}
	}

	storageConf, err := DefaultConfigFile(rootless)
	if err != nil {
		return storageOpts, err
	}
	if _, err = os.Stat(storageConf); err == nil {
		defaultRootlessRunRoot = storageOpts.RunRoot
		defaultRootlessGraphRoot = storageOpts.GraphRoot
		storageOpts = StoreOptions{}
		ReloadConfigurationFile(storageConf, &storageOpts)
	}

	if !os.IsNotExist(err) {
		return storageOpts, errors.Wrapf(err, "cannot stat %s", storageConf)
	}

	if rootless {
		if err == nil {
			// If the file did not specify a graphroot or runroot,
			// set sane defaults so we don't try and use root-owned
			// directories
			if storageOpts.RunRoot == "" {
				storageOpts.RunRoot = defaultRootlessRunRoot
			}
			if storageOpts.GraphRoot == "" {
				storageOpts.GraphRoot = defaultRootlessGraphRoot
			}
		} else {
			if err := os.MkdirAll(filepath.Dir(storageConf), 0755); err != nil {
				return storageOpts, errors.Wrapf(err, "cannot make directory %s", filepath.Dir(storageConf))
			}
			file, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
			if err != nil {
				return storageOpts, errors.Wrapf(err, "cannot open %s", storageConf)
			}

			tomlConfiguration := getTomlStorage(&storageOpts)
			defer file.Close()
			enc := toml.NewEncoder(file)
			if err := enc.Encode(tomlConfiguration); err != nil {
				os.Remove(storageConf)

				return storageOpts, errors.Wrapf(err, "failed to encode %s", storageConf)
			}
		}
	}
	return storageOpts, nil
}

func homeDir() (string, error) {
	home := os.Getenv("HOME")
	if home == "" {
		usr, err := user.Current()
		if err != nil {
			return "", errors.Wrapf(err, "neither XDG_RUNTIME_DIR nor HOME was set non-empty")
		}
		home = usr.HomeDir
	}
	return home, nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"

	shellwords "github.com/mattn/go-shellwords"
	"github.com/msoap/raphanus"
	raphanuscommon "github.com/msoap/raphanus/common"
)

// codeBytesLength - length of random code in bytes
const codeBytesLength = 15

// exec shell commands with text to STDIN
func execShell(shellCmd, input string, varsNames []string, userID, chatID int, userName, userDisplayName string, cache *raphanus.DB, cacheTTL int, config *Config) (result []byte) {
	cacheKey := shellCmd + "/" + input
	if cacheTTL > 0 {
		if cacheData, err := cache.GetBytes(cacheKey); err != raphanuscommon.ErrKeyNotExists && err != nil {
			log.Printf("get from cache failed: %s", err)
		} else if err == nil {
			// cache hit
			return cacheData
		}
	}

	shell, params, err := getShellAndParams(shellCmd, config.shell, runtime.GOOS == "windows")
	if err != nil {
		log.Print("parse shell failed: ", err)
		return nil
	}

	ctx := context.Background()
	if config.shTimeout > 0 {
		var cancelFn context.CancelFunc
		ctx, cancelFn = context.WithTimeout(ctx, time.Duration(config.shTimeout)*time.Second)
		defer cancelFn()
	}

	osExecCommand := exec.CommandContext(ctx, shell, params...) // #nosec
	osExecCommand.Stderr = os.Stderr

	// copy variables from parent process
	osExecCommand.Env = append(osExecCommand.Env, os.Environ()...)

	if input != "" {
		if len(varsNames) > 0 {
			// set user input to shell vars
			arguments := regexp.MustCompile(`\s+`).Split(input, len(varsNames))
			for i, arg := range arguments {
				osExecCommand.Env = append(osExecCommand.Env, fmt.Sprintf("%s=%s", varsNames[i], arg))
			}
		} else {
			var stdin io.WriteCloser
			errExec := errChain(func() (err error) {
				stdin, err = osExecCommand.StdinPipe()
				return err
			}, func() error {
				_, err = io.WriteString(stdin, input)
				return err
			}, func() error {
				return stdin.Close()
			})
			if errExec != nil {
				log.Print("get STDIN error: ", err)
			}
		}
	}

	// set S2T_* env vars
	s2tVariables := [...]struct{ name, value string }{
		{"S2T_LOGIN", userName},
		{"S2T_USERID", strconv.Itoa(userID)},
		{"S2T_USERNAME", userDisplayName},
		{"S2T_CHATID", strconv.Itoa(userID)},
	}
	for _, row := range s2tVariables {
		osExecCommand.Env = append(osExecCommand.Env, fmt.Sprintf("%s=%s", row.name, row.value))
	}

	shellOut, err := osExecCommand.Output()
	if err != nil {
		log.Print("exec error: ", err)
		result = []byte(fmt.Sprintf("exec error: %s", err))
	} else {
		result = shellOut
	}

	if cacheTTL > 0 {
		if err := cache.SetBytes(cacheKey, result, cacheTTL); err != nil {
			log.Printf("set to cache failed: %s", err)
		}
	}

	return result
}

// errChain - handle errors on few functions
func errChain(chainFuncs ...func() error) error {
	for _, fn := range chainFuncs {
		if err := fn(); err != nil {
			return err
		}
	}

	return nil
}

// return 2 strings, second="" if string dont contain space
func splitStringHalfBySpace(str string) (one, two string) {
	array := regexp.MustCompile(`\s+`).Split(str, 2)
	one, two = array[0], ""
	if len(array) > 1 {
		two = array[1]
	}

	return one, two
}

// cleanUserName - remove @ from telegram username
func cleanUserName(in string) string {
	return regexp.MustCompile("@").ReplaceAllLiteralString(in, "")
}

// getRandomCode - generate random code for authorize user
func getRandomCode() string {
	buffer := make([]byte, codeBytesLength)
	_, err := rand.Read(buffer)
	if err != nil {
		log.Fatalf("Get code error: %s", err)
	}

	return base64.URLEncoding.EncodeToString(buffer)
}

// parseBotCommand - parse command-line arguments for one bot command
func parseBotCommand(pathRaw, shellCmd string) (path string, command Command, err error) {
	if len(pathRaw) == 0 || pathRaw[0] != '/' {
		return "", command, fmt.Errorf("error: path %s dont starts with /", pathRaw)
	}
	if stringIsEmpty(shellCmd) {
		return "", command, fmt.Errorf("error: shell command cannot be empty")
	}

	_parseAttr := func(varsParts []string) (command Command, err error) {
		for _, oneVar := range varsParts {
			oneVarParts := regexp.MustCompile("=").Split(oneVar, 2)
			if len(oneVarParts) == 1 && oneVarParts[0] == "md" {
				command.isMarkdown = true
			} else if len(oneVarParts) != 2 {
				err = fmt.Errorf("error: parse command modificators: %s", oneVar)
				return
			} else if oneVarParts[0] == "desc" {
				command.description = oneVarParts[1]
				if command.description == "" {
					err = fmt.Errorf("error: command description cannot be empty")
					return
				}
			} else if oneVarParts[0] == "vars" {
				command.vars = regexp.MustCompile(",").Split(oneVarParts[1], -1)
				for _, oneVarName := range command.vars {
					if oneVarName == "" {
						err = fmt.Errorf("error: var name cannot be empty")
						return
					}
				}
			} else {
				err = fmt.Errorf("error: parse command modificators, not found %s", oneVarParts[0])
				return
			}
		}

		return command, nil
	}

	pathParts := regexp.MustCompile(":").Split(pathRaw, -1)
	switch {
	case len(pathParts) == 1:
		// /, /cmd
		path = pathParts[0]
	case pathParts[0] == "/" && regexp.MustCompile("^(plain_text|image)$").MatchString(pathParts[1]):
		// /:plain_text, /:image, /:plain_text:desc=name
		path = "/:" + pathParts[1]
		if pathParts[1] == "image" {
			return "", command, fmt.Errorf("/:image not implemented")
		}
		if len(pathParts) > 2 {
			command, err = _parseAttr(pathParts[2:])
		}
	case len(pathParts) > 1:
		// commands with modificators :desc, :vars
		path = pathParts[0]
		command, err = _parseAttr(pathParts[1:])
	}
	if err != nil {
		return "", command, err
	}

	command.shellCmd = shellCmd

	return path, command, nil
}

// stringIsEmpty - check string is empty
func stringIsEmpty(str string) bool {
	isEmpty, _ := regexp.MatchString(`^\s*$`, str)
	return isEmpty
}

// split string by chunks less maxSize size (whole rows)
func splitStringLinesBySize(input string, maxSize int) []string {
	result := []string{}
	parts := regexp.MustCompile("\n").Split(input, -1)
	chunks := []string{parts[0]}
	chunkSize := len(parts[0])

	for _, part := range parts[1:] {
		// current + "\n" + next > maxSize
		if chunkSize+1+len(part) > maxSize {
			result = append(result, strings.Join(chunks, "\n"))
			chunks = []string{part}
			chunkSize = len(part)
		} else {
			chunks = append(chunks, part)
			chunkSize += 1 + len(part)
		}
	}
	if len(chunks) > 0 {
		result = append(result, strings.Join(chunks, "\n"))
	}

	return result
}

// create dir if it is not exists
func createDirIfNeed(dir string) {
	if _, err := os.Stat(dir); err != nil {
		err = os.MkdirAll(dir, 0700)
		if err != nil {
			log.Fatal("create dir error:", dir)
		}
	}
}

// get home dir
func getOsUserHomeDir() string {
	homeDir := os.Getenv("HOME")
	if runtime.GOOS == "windows" {
		homeDir = os.Getenv("APPDATA")
	}
	return homeDir
}

// read default or user db file name
func getDBFilePath(usersDBFile string, needCreateDir bool) (fileName string) {
	if usersDBFile == "" {
		dirName := getOsUserHomeDir() + string(os.PathSeparator) + ".config"
		if needCreateDir {
			createDirIfNeed(dirName)
		}
		fileName = dirName + string(os.PathSeparator) + DBFileName
	} else {
		fileName = usersDBFile
	}

	return fileName
}

// ------------------------------------------------------------------
// getShellAndParams - get default shell and command
func getShellAndParams(cmd string, customShell string, isWindows bool) (shell string, params []string, err error) {
	shell, params = "sh", []string{"-c", cmd}
	if isWindows {
		shell, params = "cmd", []string{"/C", cmd}
	}

	// custom shell
	switch {
	case customShell != "sh" && customShell != "":
		shell = customShell
	case customShell == "":
		cmdLine, err := shellwords.Parse(cmd)
		if err != nil {
			return shell, params, fmt.Errorf("Parse '%s' failed: %s", cmd, err)
		}

		shell, params = cmdLine[0], cmdLine[1:]
	}

	return shell, params, nil
}
<commit_msg>Fixed goland warnings<commit_after>package main

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"

	shellwords "github.com/mattn/go-shellwords"
	"github.com/msoap/raphanus"
	raphanuscommon "github.com/msoap/raphanus/common"
)

// codeBytesLength - length of random code in bytes
const codeBytesLength = 15

// exec shell commands with text to STDIN
func execShell(shellCmd, input string, varsNames []string, userID, chatID int, userName, userDisplayName string, cache *raphanus.DB, cacheTTL int, config *Config) (result []byte) {
	cacheKey := shellCmd + "/" + input
	if cacheTTL > 0 {
		if cacheData, err := cache.GetBytes(cacheKey); err != raphanuscommon.ErrKeyNotExists && err != nil {
			log.Printf("get from cache failed: %s", err)
		} else if err == nil {
			// cache hit
			return cacheData
		}
	}

	shell, params, err := getShellAndParams(shellCmd, config.shell, runtime.GOOS == "windows")
	if err != nil {
		log.Print("parse shell failed: ", err)
		return nil
	}

	ctx := context.Background()
	if config.shTimeout > 0 {
		var cancelFn context.CancelFunc
		ctx, cancelFn = context.WithTimeout(ctx, time.Duration(config.shTimeout)*time.Second)
		defer cancelFn()
	}

	osExecCommand := exec.CommandContext(ctx, shell, params...) // #nosec
	osExecCommand.Stderr = os.Stderr

	// copy variables from parent process
	osExecCommand.Env = append(osExecCommand.Env, os.Environ()...)

	if input != "" {
		if len(varsNames) > 0 {
			// set user input to shell vars
			arguments := regexp.MustCompile(`\s+`).Split(input, len(varsNames))
			for i, arg := range arguments {
				osExecCommand.Env = append(osExecCommand.Env, fmt.Sprintf("%s=%s", varsNames[i], arg))
			}
		} else {
			var stdin io.WriteCloser
			errExec := errChain(func() (err error) {
				stdin, err = osExecCommand.StdinPipe()
				return err
			}, func() error {
				_, err = io.WriteString(stdin, input)
				return err
			}, func() error {
				return stdin.Close()
			})
			if errExec != nil {
				log.Print("get STDIN error: ", err)
			}
		}
	}

	// set S2T_* env vars
	s2tVariables := [...]struct{ name, value string }{
		{"S2T_LOGIN", userName},
		{"S2T_USERID", strconv.Itoa(userID)},
		{"S2T_USERNAME", userDisplayName},
		{"S2T_CHATID", strconv.Itoa(userID)},
	}
	for _, row := range s2tVariables {
		osExecCommand.Env = append(osExecCommand.Env, fmt.Sprintf("%s=%s", row.name, row.value))
	}

	shellOut, err := osExecCommand.Output()
	if err != nil {
		log.Print("exec error: ", err)
		result = []byte(fmt.Sprintf("exec error: %s", err))
	} else {
		result = shellOut
	}

	if cacheTTL > 0 {
		if err := cache.SetBytes(cacheKey, result, cacheTTL); err != nil {
			log.Printf("set to cache failed: %s", err)
		}
	}

	return result
}

// errChain - handle errors on few functions
func errChain(chainFuncs ...func() error) error {
	for _, fn := range chainFuncs {
		if err := fn(); err != nil {
			return err
		}
	}

	return nil
}

// return 2 strings, second="" if string dont contain space
func splitStringHalfBySpace(str string) (one, two string) {
	array := regexp.MustCompile(`\s+`).Split(str, 2)
	one, two = array[0], ""
	if len(array) > 1 {
		two = array[1]
	}

	return one, two
}

// cleanUserName - remove @ from telegram username
func cleanUserName(in string) string {
	return regexp.MustCompile("@").ReplaceAllLiteralString(in, "")
}

// getRandomCode - generate random code for authorize user
func getRandomCode() string {
	buffer := make([]byte, codeBytesLength)
	_, err := rand.Read(buffer)
	if err != nil {
		log.Fatalf("Get code error: %s", err)
	}

	return base64.URLEncoding.EncodeToString(buffer)
}

// parseBotCommand - parse command-line arguments for one bot command
func parseBotCommand(pathRaw, shellCmd string) (path string, command Command, err error) {
	if len(pathRaw) == 0 || pathRaw[0] != '/' {
		return "", command, fmt.Errorf("error: path %s dont starts with /", pathRaw)
	}
	if stringIsEmpty(shellCmd) {
		return "", command, fmt.Errorf("error: shell command cannot be empty")
	}

	parseAttrFn := func(varsParts []string) (command Command, err error) {
		for _, oneVar := range varsParts {
			oneVarParts := regexp.MustCompile("=").Split(oneVar, 2)
			if len(oneVarParts) == 1 && oneVarParts[0] == "md" {
				command.isMarkdown = true
			} else if len(oneVarParts) != 2 {
				err = fmt.Errorf("error: parse command modificators: %s", oneVar)
				return
			} else if oneVarParts[0] == "desc" {
				command.description = oneVarParts[1]
				if command.description == "" {
					err = fmt.Errorf("error: command description cannot be empty")
					return
				}
			} else if oneVarParts[0] == "vars" {
				command.vars = regexp.MustCompile(",").Split(oneVarParts[1], -1)
				for _, oneVarName := range command.vars {
					if oneVarName == "" {
						err = fmt.Errorf("error: var name cannot be empty")
						return
					}
				}
			} else {
				err = fmt.Errorf("error: parse command modificators, not found %s", oneVarParts[0])
				return
			}
		}

		return command, nil
	}

	pathParts := regexp.MustCompile(":").Split(pathRaw, -1)
	switch {
	case len(pathParts) == 1:
		// /, /cmd
		path = pathParts[0]
	case pathParts[0] == "/" && regexp.MustCompile("^(plain_text|image)$").MatchString(pathParts[1]):
		// /:plain_text, /:image, /:plain_text:desc=name
		path = "/:" + pathParts[1]
		if pathParts[1] == "image" {
			return "", command, fmt.Errorf("/:image not implemented")
		}
		if len(pathParts) > 2 {
			command, err = parseAttrFn(pathParts[2:])
		}
	case len(pathParts) > 1:
		// commands with modificators :desc, :vars
		path = pathParts[0]
		command, err = parseAttrFn(pathParts[1:])
	}
	if err != nil {
		return "", command, err
	}

	command.shellCmd = shellCmd

	return path, command, nil
}

// stringIsEmpty - check string is empty
func stringIsEmpty(str string) bool {
	isEmpty, _ := regexp.MatchString(`^\s*$`, str)
	return isEmpty
}

// split string by chunks less maxSize size (whole rows)
func splitStringLinesBySize(input string, maxSize int) []string {
	result := []string{}
	parts := regexp.MustCompile("\n").Split(input, -1)
	chunks := []string{parts[0]}
	chunkSize := len(parts[0])

	for _, part := range parts[1:] {
		// current + "\n" + next > maxSize
		if chunkSize+1+len(part) > maxSize {
			result = append(result, strings.Join(chunks, "\n"))
			chunks = []string{part}
			chunkSize = len(part)
		} else {
			chunks = append(chunks, part)
			chunkSize += 1 + len(part)
		}
	}
	if len(chunks) > 0 {
		result = append(result, strings.Join(chunks, "\n"))
	}

	return result
}

// create dir if it is not exists
func createDirIfNeed(dir string) {
	if _, err := os.Stat(dir); err != nil {
		err = os.MkdirAll(dir, 0700)
		if err != nil {
			log.Fatal("create dir error:", dir)
		}
	}
}

// get home dir
func getOsUserHomeDir() string {
	homeDir := os.Getenv("HOME")
	if runtime.GOOS == "windows" {
		homeDir = os.Getenv("APPDATA")
	}
	return homeDir
}

// read default or user db file name
func getDBFilePath(usersDBFile string, needCreateDir bool) (fileName string) {
	if usersDBFile == "" {
		dirName := getOsUserHomeDir() + string(os.PathSeparator) + ".config"
		if needCreateDir {
			createDirIfNeed(dirName)
		}
		fileName = dirName + string(os.PathSeparator) + DBFileName
	} else {
		fileName = usersDBFile
	}

	return fileName
}

// ------------------------------------------------------------------
// getShellAndParams - get default shell and command
func getShellAndParams(cmd string, customShell string, isWindows bool) (shell string, params []string, err error) {
	shell, params = "sh", []string{"-c", cmd}
	if isWindows {
		shell, params = "cmd", []string{"/C", cmd}
	}

	// custom shell
	switch {
	case customShell != "sh" && customShell != "":
		shell = customShell
	case customShell == "":
		cmdLine, err := shellwords.Parse(cmd)
		if err != nil {
			return shell, params, fmt.Errorf("failed parse %q: %s", cmd, err)
		}

		shell, params = cmdLine[0], cmdLine[1:]
	}

	return shell, params, nil
}
<|endoftext|>


<commit_before>// Copyright 2015-2017 HenryLee. All Rights Reserved.
//
// 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 tp

import (
	"crypto/tls"
	"os"
	"sync"
	"time"

	"github.com/henrylee2cn/goutil"
	"github.com/henrylee2cn/goutil/pool"
	"github.com/henrylee2cn/teleport/socket"
)

// GetPacket gets a *Packet form packet stack.
// Note:
//  newBodyFunc is only for reading form connection;
//  settings are only for writing to connection.
//  func GetPacket(settings ...socket.PacketSetting) *socket.Packet
var GetPacket = socket.GetPacket

// PutPacket puts a *socket.Packet to packet stack.
//  func PutPacket(p *socket.Packet)
var PutPacket = socket.PutPacket

var (
	_maxGoroutinesAmount      = (1024 * 1024 * 8) / 8 // max memory 8GB (8KB/goroutine)
	_maxGoroutineIdleDuration time.Duration
	_gopool                   = pool.NewGoPool(_maxGoroutinesAmount, _maxGoroutineIdleDuration)
)

// SetGopool set or reset go pool config.
// Note: Make sure to call it before calling NewPeer() and Go()
func SetGopool(maxGoroutinesAmount int, maxGoroutineIdleDuration time.Duration) {
	_maxGoroutinesAmount, _maxGoroutineIdleDuration := maxGoroutinesAmount, maxGoroutineIdleDuration
	if _gopool != nil {
		_gopool.Stop()
	}
	_gopool = pool.NewGoPool(_maxGoroutinesAmount, _maxGoroutineIdleDuration)
}

// Go similar to go func, but return false if insufficient resources.
func Go(fn func()) bool {
	if err := _gopool.Go(fn); err != nil {
		Warnf("%s", err.Error())
		return false
	}
	return true
}

// AnywayGo similar to go func, but concurrent resources are limited.
func AnywayGo(fn func()) {
TRYGO:
	if !Go(fn) {
		time.Sleep(time.Second)
		goto TRYGO
	}
}

// NewFakePullCmd creates a fake PullCmd.
func NewFakePullCmd(p Peer, uri string, args, reply interface{}, rerr *Rerror) PullCmd {
	output := socket.NewPacket(
		socket.WithPtype(TypePull),
		socket.WithUri(uri),
		socket.WithBody(args),
	)
	return &pullCmd{
		sess:   newSession(p.(*peer), nil, nil),
		output: output,
		reply:  reply,
		public: goutil.RwMap(),
		rerr:   rerr,
	}
}

func newTLSConfig(certFile, keyFile string) (*tls.Config, error) {
	var tlsConfig *tls.Config
	if len(certFile) > 0 && len(keyFile) > 0 {
		cert, err := tls.LoadX509KeyPair(certFile, keyFile)
		if err != nil {
			return nil, err
		}
		tlsConfig = &tls.Config{
			Certificates: []tls.Certificate{cert},
			// NextProtos: []string{"http/1.1", "h2"},
			PreferServerCipherSuites: true,
		}
	}
	return tlsConfig, nil
}

var printPidOnce sync.Once

func doPrintPid() {
	printPidOnce.Do(func() {
		Printf("The current process PID: %d", os.Getpid())
	})
}
<commit_msg>fix NewFakePullCmd function unsafe bug<commit_after>// Copyright 2015-2017 HenryLee. All Rights Reserved.
//
// 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 tp

import (
	"crypto/tls"
	"os"
	"sync"
	"time"

	"github.com/henrylee2cn/goutil"
	"github.com/henrylee2cn/goutil/pool"
	"github.com/henrylee2cn/teleport/socket"
)

// GetPacket gets a *Packet form packet stack.
// Note:
//  newBodyFunc is only for reading form connection;
//  settings are only for writing to connection.
//  func GetPacket(settings ...socket.PacketSetting) *socket.Packet
var GetPacket = socket.GetPacket

// PutPacket puts a *socket.Packet to packet stack.
//  func PutPacket(p *socket.Packet)
var PutPacket = socket.PutPacket

var (
	_maxGoroutinesAmount      = (1024 * 1024 * 8) / 8 // max memory 8GB (8KB/goroutine)
	_maxGoroutineIdleDuration time.Duration
	_gopool                   = pool.NewGoPool(_maxGoroutinesAmount, _maxGoroutineIdleDuration)
)

// SetGopool set or reset go pool config.
// Note: Make sure to call it before calling NewPeer() and Go()
func SetGopool(maxGoroutinesAmount int, maxGoroutineIdleDuration time.Duration) {
	_maxGoroutinesAmount, _maxGoroutineIdleDuration := maxGoroutinesAmount, maxGoroutineIdleDuration
	if _gopool != nil {
		_gopool.Stop()
	}
	_gopool = pool.NewGoPool(_maxGoroutinesAmount, _maxGoroutineIdleDuration)
}

// Go similar to go func, but return false if insufficient resources.
func Go(fn func()) bool {
	if err := _gopool.Go(fn); err != nil {
		Warnf("%s", err.Error())
		return false
	}
	return true
}

// AnywayGo similar to go func, but concurrent resources are limited.
func AnywayGo(fn func()) {
TRYGO:
	if !Go(fn) {
		time.Sleep(time.Second)
		goto TRYGO
	}
}

// NewFakePullCmd creates a fake PullCmd.
func NewFakePullCmd(p Peer, uri string, args, reply interface{}, rerr *Rerror) PullCmd {
	output := socket.NewPacket(
		socket.WithPtype(TypePull),
		socket.WithUri(uri),
		socket.WithBody(args),
	)
	cmd := &pullCmd{
		output: output,
		reply:  reply,
		public: goutil.RwMap(),
		rerr:   rerr,
	}

	if peerObj, ok := p.(*peer); ok {
		cmd.sess = newSession(peerObj, nil, nil)
	}
	return cmd
}

func newTLSConfig(certFile, keyFile string) (*tls.Config, error) {
	var tlsConfig *tls.Config
	if len(certFile) > 0 && len(keyFile) > 0 {
		cert, err := tls.LoadX509KeyPair(certFile, keyFile)
		if err != nil {
			return nil, err
		}
		tlsConfig = &tls.Config{
			Certificates: []tls.Certificate{cert},
			// NextProtos: []string{"http/1.1", "h2"},
			PreferServerCipherSuites: true,
		}
	}
	return tlsConfig, nil
}

var printPidOnce sync.Once

func doPrintPid() {
	printPidOnce.Do(func() {
		Printf("The current process PID: %d", os.Getpid())
	})
}
<|endoftext|>


<commit_before>package utron

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
)

var baseApp *App

func init() {
	baseApp = NewApp()
	if err := baseApp.Init(); err != nil {
		// TODO log this?
	}
}

// App is the main utron application.
type App struct {
	router     *Router
	cfg        *Config
	view       View
	log        Logger
	model      *Model
	configPath string
	isInit     bool
}

// NewApp creates a new bare-bone utron application. To use the MVC components, you should call
// the Init method before serving requests.
func NewApp() *App {
	app := &App{}
	app.Set(logThis)
	r := NewRouter(app)
	app.Set(r)
	app.Set(NewModel())
	return app
}

// NewMVC creates a new MVC utron app. If cfg is passed, it should be a directory to look for
// the configuration files. The App returned is initialized.
func NewMVC(cfg ...string) (*App, error) {
	app := NewApp()
	if len(cfg) > 0 {
		app.SetConfigPath(cfg[0])
	}
	if err := app.Init(); err != nil {
		return nil, err
	}
	return app, nil
}

// Init initializes the MVC App.
func (a *App) Init() error {
	if a.configPath == "" {
		a.SetConfigPath("config")
	}
	return a.init()
}

// SetConfigPath sets the directory path to search for the config files.
func (a *App) SetConfigPath(dir string) {
	a.configPath = dir
}

// init initializes values to the app components.
func (a *App) init() error {
	appConfig, err := loadConfig(a.configPath)
	if err != nil {
		return err
	}

	views, err := NewSimpleView(appConfig.ViewsDir)
	if err != nil {
		return err
	}
	if a.model != nil && !a.model.IsOpen() {
		oerr := a.model.OpenWithConfig(appConfig)
		if oerr != nil {
			return oerr
		}
	} else {
		model, err := NewModelWithConfig(appConfig)
		if err != nil {
			return err
		}
		a.Set(model)
	}
	a.router.loadRoutes(a.configPath) // Load a routes file if available.
	a.Set(appConfig)
	a.Set(views)
	a.isInit = true

	// In case the StaticDir is specified in the Config file, register
	// a handler serving contents of that directory under the PathPrefix /static/.
	if appConfig.StaticDir != "" {
		static, err := getAbsolutePath(appConfig.StaticDir)
		if err != nil {
			logThis.Errors(err)
		}
		if static != "" {
			a.router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(static))))
		}

	}
	return nil
}

// getAbsolutePath returns the absolute path to dir. If the dir is relative, then we add
// the current working directory. Checks are made to ensure the directory exist.
// In case of any error, an empty string is returned.
func getAbsolutePath(dir string) (string, error) {
	info, err := os.Stat(dir)
	if err != nil {
		return "", err
	}
	if !info.IsDir() {
		return "", fmt.Errorf("untron: %s is not a directory", dir)
	}

	if filepath.IsAbs(dir) { // If dir is already absolute, return it.
		return dir, nil
	}
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	absDir := filepath.Join(wd, dir)
	_, err = os.Stat(absDir)
	if err != nil {
		return "", err
	}
	return absDir, nil
}

// loadConfig loads the configuration file. If cfg is provided, then it is used as the directory
// for searching the configuration files. It defaults to the directory named config in the current
// working directory.
func loadConfig(cfg ...string) (*Config, error) {
	cfgDir := "config"
	if len(cfg) > 0 {
		cfgDir = cfg[0]
	}

	// Load configurations.
	cfgFile, err := findConfigFile(cfgDir, "app")
	if err != nil {
		return nil, err
	}
	return NewConfig(cfgFile)
}

// findConfigFile finds the configuration file name in the directory dir.
func findConfigFile(dir string, name string) (file string, err error) {
	extensions := []string{".json", ".toml", ".yml"}

	for _, ext := range extensions {
		file = filepath.Join(dir, name)
		if info, serr := os.Stat(file); serr == nil && !info.IsDir() {
			return
		}
		file = file + ext
		if info, serr := os.Stat(file); serr == nil && !info.IsDir() {
			return
		}
	}
	return "", fmt.Errorf("utron: can't find configuration file %s in %s", name, dir)
}

// AddController registers a controller, and middlewares if any is provided.
func (a *App) AddController(ctrl Controller, middlewares ...interface{}) {
	a.router.Add(ctrl, middlewares...)
}

// Set is for assigning a value to *App components. The following can be set:
//	Logger by passing Logger
//	View by passing View
//	Router by passing *Router
//	Config by passing *Config
//	Model by passing *Model
func (a *App) Set(value interface{}) {
	switch value.(type) {
	case Logger:
		a.log = value.(Logger)
	case *Router:
		a.router = value.(*Router)
	case View:
		a.view = value.(View)
	case *Config:
		a.cfg = value.(*Config)
	case *Model:
		a.model = value.(*Model)
	}
}

// ServeHTTP serves http requests. It can be used with other http.Handler implementations.
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.router.ServeHTTP(w, r)
}

// SetConfigPath sets the path to look for the configuration files in the
// global utron App.
func SetConfigPath(path string) {
	baseApp.SetConfigPath(path)
}

// RegisterModels registers models in the global utron App.
func RegisterModels(models ...interface{}) {
	baseApp.model.Register(models...)
}

// RegisterController registers a controller in the global utron App.
func RegisterController(ctrl Controller, middlewares ...interface{}) {
	baseApp.router.Add(ctrl, middlewares...)
}

// ServeHTTP serves request using global utron App.
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !baseApp.isInit {
		if err := baseApp.Init(); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	baseApp.ServeHTTP(w, r)
}

// Migrate runs migrations on the global utron app.
func Migrate() {
	baseApp.model.AutoMigrateAll()
}

// Run runs a http server, serving the global utron App.
//
// By using this, you should make sure you followed the MVC pattern.
func Run() {
	if baseApp.cfg.Automigrate {
		Migrate()
	}
	port := baseApp.cfg.Port
	logThis.Info("starting server at ", baseApp.cfg.BaseURL)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), baseApp))
}
<commit_msg>Log error on failed initialization<commit_after>package utron

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
)

var baseApp *App

func init() {
	baseApp = NewApp()
	if err := baseApp.Init(); err != nil {
		// TODO log this?
		log.Println(err)
	}
}

// App is the main utron application.
type App struct {
	router     *Router
	cfg        *Config
	view       View
	log        Logger
	model      *Model
	configPath string
	isInit     bool
}

// NewApp creates a new bare-bone utron application. To use the MVC components, you should call
// the Init method before serving requests.
func NewApp() *App {
	app := &App{}
	app.Set(logThis)
	r := NewRouter(app)
	app.Set(r)
	app.Set(NewModel())
	return app
}

// NewMVC creates a new MVC utron app. If cfg is passed, it should be a directory to look for
// the configuration files. The App returned is initialized.
func NewMVC(cfg ...string) (*App, error) {
	app := NewApp()
	if len(cfg) > 0 {
		app.SetConfigPath(cfg[0])
	}
	if err := app.Init(); err != nil {
		return nil, err
	}
	return app, nil
}

// Init initializes the MVC App.
func (a *App) Init() error {
	if a.configPath == "" {
		a.SetConfigPath("config")
	}
	return a.init()
}

// SetConfigPath sets the directory path to search for the config files.
func (a *App) SetConfigPath(dir string) {
	a.configPath = dir
}

// init initializes values to the app components.
func (a *App) init() error {
	appConfig, err := loadConfig(a.configPath)
	if err != nil {
		return err
	}

	views, err := NewSimpleView(appConfig.ViewsDir)
	if err != nil {
		return err
	}
	if a.model != nil && !a.model.IsOpen() {
		oerr := a.model.OpenWithConfig(appConfig)
		if oerr != nil {
			return oerr
		}
	} else {
		model, err := NewModelWithConfig(appConfig)
		if err != nil {
			return err
		}
		a.Set(model)
	}
	a.router.loadRoutes(a.configPath) // Load a routes file if available.
	a.Set(appConfig)
	a.Set(views)
	a.isInit = true

	// In case the StaticDir is specified in the Config file, register
	// a handler serving contents of that directory under the PathPrefix /static/.
	if appConfig.StaticDir != "" {
		static, err := getAbsolutePath(appConfig.StaticDir)
		if err != nil {
			logThis.Errors(err)
		}
		if static != "" {
			a.router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(static))))
		}

	}
	return nil
}

// getAbsolutePath returns the absolute path to dir. If the dir is relative, then we add
// the current working directory. Checks are made to ensure the directory exist.
// In case of any error, an empty string is returned.
func getAbsolutePath(dir string) (string, error) {
	info, err := os.Stat(dir)
	if err != nil {
		return "", err
	}
	if !info.IsDir() {
		return "", fmt.Errorf("untron: %s is not a directory", dir)
	}

	if filepath.IsAbs(dir) { // If dir is already absolute, return it.
		return dir, nil
	}
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	absDir := filepath.Join(wd, dir)
	_, err = os.Stat(absDir)
	if err != nil {
		return "", err
	}
	return absDir, nil
}

// loadConfig loads the configuration file. If cfg is provided, then it is used as the directory
// for searching the configuration files. It defaults to the directory named config in the current
// working directory.
func loadConfig(cfg ...string) (*Config, error) {
	cfgDir := "config"
	if len(cfg) > 0 {
		cfgDir = cfg[0]
	}

	// Load configurations.
	cfgFile, err := findConfigFile(cfgDir, "app")
	if err != nil {
		return nil, err
	}
	return NewConfig(cfgFile)
}

// findConfigFile finds the configuration file name in the directory dir.
func findConfigFile(dir string, name string) (file string, err error) {
	extensions := []string{".json", ".toml", ".yml"}

	for _, ext := range extensions {
		file = filepath.Join(dir, name)
		if info, serr := os.Stat(file); serr == nil && !info.IsDir() {
			return
		}
		file = file + ext
		if info, serr := os.Stat(file); serr == nil && !info.IsDir() {
			return
		}
	}
	return "", fmt.Errorf("utron: can't find configuration file %s in %s", name, dir)
}

// AddController registers a controller, and middlewares if any is provided.
func (a *App) AddController(ctrl Controller, middlewares ...interface{}) {
	a.router.Add(ctrl, middlewares...)
}

// Set is for assigning a value to *App components. The following can be set:
//	Logger by passing Logger
//	View by passing View
//	Router by passing *Router
//	Config by passing *Config
//	Model by passing *Model
func (a *App) Set(value interface{}) {
	switch value.(type) {
	case Logger:
		a.log = value.(Logger)
	case *Router:
		a.router = value.(*Router)
	case View:
		a.view = value.(View)
	case *Config:
		a.cfg = value.(*Config)
	case *Model:
		a.model = value.(*Model)
	}
}

// ServeHTTP serves http requests. It can be used with other http.Handler implementations.
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.router.ServeHTTP(w, r)
}

// SetConfigPath sets the path to look for the configuration files in the
// global utron App.
func SetConfigPath(path string) {
	baseApp.SetConfigPath(path)
}

// RegisterModels registers models in the global utron App.
func RegisterModels(models ...interface{}) {
	baseApp.model.Register(models...)
}

// RegisterController registers a controller in the global utron App.
func RegisterController(ctrl Controller, middlewares ...interface{}) {
	baseApp.router.Add(ctrl, middlewares...)
}

// ServeHTTP serves request using global utron App.
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !baseApp.isInit {
		if err := baseApp.Init(); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	baseApp.ServeHTTP(w, r)
}

// Migrate runs migrations on the global utron app.
func Migrate() {
	baseApp.model.AutoMigrateAll()
}

// Run runs a http server, serving the global utron App.
//
// By using this, you should make sure you followed the MVC pattern.
func Run() {
	if baseApp.cfg.Automigrate {
		Migrate()
	}
	port := baseApp.cfg.Port
	logThis.Info("starting server at ", baseApp.cfg.BaseURL)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), baseApp))
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package atomic

import "sync/atomic"

// Value shadows the type of the same name from sync/atomic
// https://godoc.org/sync/atomic#Value
type Value struct {
	atomic.Value

	_ nocmp // disallow non-atomic comparison
}
<commit_msg>Value: place nocmp zero-sized field first (#109)<commit_after>// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package atomic

import "sync/atomic"

// Value shadows the type of the same name from sync/atomic
// https://godoc.org/sync/atomic#Value
type Value struct {
	_ nocmp // disallow non-atomic comparison

	atomic.Value
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/howeyc/fsnotify"
	"github.com/jessevdk/go-flags"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

const usage = `
Usage:
  watch paths... [options]

Example:
  watch src --on-change 'make build'

Options:
      --on-change <arg>  Run command on any change
  -h, --halt             Exits on error (Default: false)
  -i, --interval <arg>   Run command once within this interval (Default: 1s)
  -n, --no-recurse       Skip subfolders (Default: false)
  -q, --quiet            Suppress standard output (Default: false)

Intervals can be milliseconds(ms), seconds(s), minutes(m), or hours(h).
The format is the integer followed by the abbreviation.
`

var (
	last     time.Time
	interval time.Duration
	paths    []string
	err      error
)

var opts struct {
	Help        bool   `short:"h" long:"help"      description:"Show this help message" default:false`
	Halt        bool   `short:"h" long:"halt"      description:"Exits on error (Default: false)" default:false`
	Quiet       bool   `short:"q" long:"quiet"     description:"Suppress standard output (Default: false)" default:false`
	Interval    string `short:"i" long:"interval"  description:"Run command once within this interval (Default: 1s)" default:"1s"`
	NoRecursive bool   `short:"n" long:"no-recursive" description:"Skip subfolders (Default: false)" default:false`
	OnChange    string `long:"on-change"           description:"Run command on change."`
}

func init() {
	args, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	paths, err = ResolvePaths(args[1:])

	if len(paths) <= 0 {
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(2) // 2 for --help exit code
	}

	interval, err = time.ParseDuration(opts.Interval)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	last = time.Now().Add(-interval)
}

func main() {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	done := make(chan bool)

	// clean-up watcher on interrupt (^C)
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)
	go func() {
		<-interrupt
		if !opts.Quiet {
			fmt.Fprintln(os.Stdout, "Interrupted. Cleaning up before exiting...")
		}
		watcher.Close()
		os.Exit(0)
	}()

	// process watcher events
	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				if !opts.Quiet {
					fmt.Fprintln(os.Stdout, ev)
				}
				if time.Since(last).Nanoseconds() > interval.Nanoseconds() {
					last = time.Now()
					err = ExecCommand()
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						if opts.Halt {
							os.Exit(1)
						}
					}
				}
			case err := <-watcher.Error:
				fmt.Fprintln(os.Stderr, err)
				if opts.Halt {
					os.Exit(1)
				}
			}
		}
	}()

	// add paths to be watched
	for _, p := range paths {
		err = watcher.Watch(p)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}

	// wait and watch
	<-done
}

func ExecCommand() error {
	if opts.OnChange == "" {
		return nil
	} else {
		args := strings.Split(opts.OnChange, " ")
		cmd := exec.Command(args[0], args[1:]...)

		if !opts.Quiet {
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
		}
		cmd.Stdin = os.Stdin

		return cmd.Run()
	}
}

// Resolve path arguments by walking directories and adding subfolders.
func ResolvePaths(args []string) ([]string, error) {
	var stat os.FileInfo
	resolved := make([]string, 0)

	var once sync.Once
	var recurse error = nil

	walker := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if recurse == nil && opts.NoRecursive && info.IsDir() {
			once.Do(func() {
				recurse = filepath.SkipDir
			})
		}

		resolved = append(resolved, path)

		return recurse
	}

	for _, path := range args {
		if path == "" {
			continue
		}

		stat, err = os.Stat(path)
		if err != nil {
			return nil, err
		}

		if !stat.IsDir() {
			resolved = append(resolved, path)
			continue
		}

		err = filepath.Walk(path, walker)
	}

	return resolved, nil
}
<commit_msg>remove pointless sync.Once<commit_after>package main

import (
	"fmt"
	"github.com/howeyc/fsnotify"
	"github.com/jessevdk/go-flags"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strings"
	"time"
)

const usage = `
Usage:
  watch paths... [options]

Example:
  watch src --on-change 'make build'

Options:
      --on-change <arg>  Run command on any change
  -h, --halt             Exits on error (Default: false)
  -i, --interval <arg>   Run command once within this interval (Default: 1s)
  -n, --no-recurse       Skip subfolders (Default: false)
  -q, --quiet            Suppress standard output (Default: false)

Intervals can be milliseconds(ms), seconds(s), minutes(m), or hours(h).
The format is the integer followed by the abbreviation.
`

var (
	last     time.Time
	interval time.Duration
	paths    []string
	err      error
)

var opts struct {
	Help        bool   `short:"h" long:"help"      description:"Show this help message" default:false`
	Halt        bool   `short:"h" long:"halt"      description:"Exits on error (Default: false)" default:false`
	Quiet       bool   `short:"q" long:"quiet"     description:"Suppress standard output (Default: false)" default:false`
	Interval    string `short:"i" long:"interval"  description:"Run command once within this interval (Default: 1s)" default:"1s"`
	NoRecursive bool   `short:"n" long:"no-recursive" description:"Skip subfolders (Default: false)" default:false`
	OnChange    string `long:"on-change"           description:"Run command on change."`
}

func init() {
	args, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	paths, err = ResolvePaths(args[1:])

	if len(paths) <= 0 {
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(2) // 2 for --help exit code
	}

	interval, err = time.ParseDuration(opts.Interval)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	last = time.Now().Add(-interval)
}

func main() {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	done := make(chan bool)

	// clean-up watcher on interrupt (^C)
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)
	go func() {
		<-interrupt
		if !opts.Quiet {
			fmt.Fprintln(os.Stdout, "Interrupted. Cleaning up before exiting...")
		}
		watcher.Close()
		os.Exit(0)
	}()

	// process watcher events
	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				if !opts.Quiet {
					fmt.Fprintln(os.Stdout, ev)
				}
				if time.Since(last).Nanoseconds() > interval.Nanoseconds() {
					last = time.Now()
					err = ExecCommand()
					if err != nil {
						fmt.Fprintln(os.Stderr, err)
						if opts.Halt {
							os.Exit(1)
						}
					}
				}
			case err := <-watcher.Error:
				fmt.Fprintln(os.Stderr, err)
				if opts.Halt {
					os.Exit(1)
				}
			}
		}
	}()

	// add paths to be watched
	for _, p := range paths {
		err = watcher.Watch(p)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}

	// wait and watch
	<-done
}

func ExecCommand() error {
	if opts.OnChange == "" {
		return nil
	} else {
		args := strings.Split(opts.OnChange, " ")
		cmd := exec.Command(args[0], args[1:]...)

		if !opts.Quiet {
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
		}
		cmd.Stdin = os.Stdin

		return cmd.Run()
	}
}

// Resolve path arguments by walking directories and adding subfolders.
func ResolvePaths(args []string) ([]string, error) {
	var stat os.FileInfo
	resolved := make([]string, 0)

	var recurse error = nil

	if opts.NoRecursive {
		recurse = filepath.SkipDir
	}

	walker := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		resolved = append(resolved, path)

		return recurse
	}

	for _, path := range args {
		if path == "" {
			continue
		}

		stat, err = os.Stat(path)
		if err != nil {
			return nil, err
		}

		if !stat.IsDir() {
			resolved = append(resolved, path)
			continue
		}

		err = filepath.Walk(path, walker)
	}

	return resolved, nil
}
<|endoftext|>


<commit_before>package imap

import (
	"bytes"
	"fmt"
	"io"
	"strconv"
	"time"
	"unicode"
)

type flusher interface {
	Flush() error
}

type (
	// A raw string.
	RawString string
)

type WriterTo interface {
	WriteTo(w *Writer) error
}

func formatNumber(num uint32) string {
	return strconv.FormatUint(uint64(num), 10)
}

// Convert a string list to a field list.
func FormatStringList(list []string) (fields []interface{}) {
	fields = make([]interface{}, len(list))
	for i, v := range list {
		fields[i] = v
	}
	return
}

// Check if a string is 8-bit clean.
func isAscii(s string) bool {
	for _, c := range s {
		if c > unicode.MaxASCII || unicode.IsControl(c) {
			return false
		}
	}
	return true
}

// An IMAP writer.
type Writer struct {
	io.Writer

	AllowAsyncLiterals bool

	continues <-chan bool
}

// Helper function to write a string to w.
func (w *Writer) writeString(s string) error {
	_, err := io.WriteString(w.Writer, s)
	return err
}

func (w *Writer) writeCrlf() error {
	if err := w.writeString(crlf); err != nil {
		return err
	}

	return w.Flush()
}

func (w *Writer) writeNumber(num uint32) error {
	return w.writeString(formatNumber(num))
}

func (w *Writer) writeQuoted(s string) error {
	return w.writeString(strconv.Quote(s))
}

func (w *Writer) writeQuotedOrLiteral(s string) error {
	if !isAscii(s) {
		// IMAP doesn't allow 8-bit data outside literals
		return w.writeLiteral(bytes.NewBufferString(s))
	}

	return w.writeQuoted(s)
}

func (w *Writer) writeDateTime(t time.Time, layout string) error {
	if t.IsZero() {
		return w.writeString(nilAtom)
	}
	return w.writeQuoted(t.Format(layout))
}

func (w *Writer) writeFields(fields []interface{}) error {
	for i, field := range fields {
		if i > 0 { // Write separator
			if err := w.writeString(string(sp)); err != nil {
				return err
			}
		}

		if err := w.writeField(field); err != nil {
			return err
		}
	}

	return nil
}

func (w *Writer) writeList(fields []interface{}) error {
	if err := w.writeString(string(listStart)); err != nil {
		return err
	}

	if err := w.writeFields(fields); err != nil {
		return err
	}

	return w.writeString(string(listEnd))
}

func (w *Writer) writeLiteral(l Literal) error {
	if l == nil {
		return w.writeString(nilAtom)
	}

	unsyncLiteral := w.AllowAsyncLiterals && l.Len() <= 4096

	header := string(literalStart) + strconv.Itoa(l.Len())
	if unsyncLiteral {
		header += string('+')
	}
	header += string(literalEnd) + crlf
	if err := w.writeString(header); err != nil {
		return err
	}

	// If a channel is available, wait for a continuation request before sending data
	if !unsyncLiteral && w.continues != nil {
		// Make sure to flush the writer, otherwise we may never receive a continuation request
		if err := w.Flush(); err != nil {
			return err
		}

		if !<-w.continues {
			return fmt.Errorf("imap: cannot send literal: no continuation request received")
		}
	}

	// In case of bufio.Buffer, it will be 0 after io.Copy.
	literalLen := int64(l.Len())

	n, err := io.CopyN(w, l, literalLen)
	if err != nil {
		return err
	}
	if n != literalLen {
		return fmt.Errorf("imap: size of Literal is not equal to Len() (%d != %d)", n, l.Len())
	}

	return nil
}

func (w *Writer) writeField(field interface{}) error {
	if field == nil {
		return w.writeString(nilAtom)
	}

	switch field := field.(type) {
	case RawString:
		return w.writeString(string(field))
	case string:
		return w.writeQuotedOrLiteral(field)
	case int:
		return w.writeNumber(uint32(field))
	case uint32:
		return w.writeNumber(field)
	case Literal:
		return w.writeLiteral(field)
	case []interface{}:
		return w.writeList(field)
	case envelopeDateTime:
		return w.writeDateTime(time.Time(field), envelopeDateTimeLayout)
	case searchDate:
		return w.writeDateTime(time.Time(field), searchDateLayout)
	case Date:
		return w.writeDateTime(time.Time(field), DateLayout)
	case DateTime:
		return w.writeDateTime(time.Time(field), DateTimeLayout)
	case time.Time:
		return w.writeDateTime(field, DateTimeLayout)
	case *SeqSet:
		return w.writeString(field.String())
	case *BodySectionName:
		// Can contain spaces - that's why we don't just pass it as a string
		return w.writeString(string(field.FetchItem()))
	}

	return fmt.Errorf("imap: cannot format field: %v", field)
}

func (w *Writer) writeRespCode(code StatusRespCode, args []interface{}) error {
	if err := w.writeString(string(respCodeStart)); err != nil {
		return err
	}

	fields := []interface{}{RawString(code)}
	fields = append(fields, args...)

	if err := w.writeFields(fields); err != nil {
		return err
	}

	return w.writeString(string(respCodeEnd))
}

func (w *Writer) writeLine(fields ...interface{}) error {
	if err := w.writeFields(fields); err != nil {
		return err
	}

	return w.writeCrlf()
}

func (w *Writer) Flush() error {
	if f, ok := w.Writer.(flusher); ok {
		return f.Flush()
	}
	return nil
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{Writer: w}
}

func NewClientWriter(w io.Writer, continues <-chan bool) *Writer {
	return &Writer{Writer: w, continues: continues}
}
<commit_msg>Provide a useful error message if written literal is smaller than expected<commit_after>package imap

import (
	"bytes"
	"fmt"
	"io"
	"strconv"
	"time"
	"unicode"
)

type flusher interface {
	Flush() error
}

type (
	// A raw string.
	RawString string
)

type WriterTo interface {
	WriteTo(w *Writer) error
}

func formatNumber(num uint32) string {
	return strconv.FormatUint(uint64(num), 10)
}

// Convert a string list to a field list.
func FormatStringList(list []string) (fields []interface{}) {
	fields = make([]interface{}, len(list))
	for i, v := range list {
		fields[i] = v
	}
	return
}

// Check if a string is 8-bit clean.
func isAscii(s string) bool {
	for _, c := range s {
		if c > unicode.MaxASCII || unicode.IsControl(c) {
			return false
		}
	}
	return true
}

// An IMAP writer.
type Writer struct {
	io.Writer

	AllowAsyncLiterals bool

	continues <-chan bool
}

// Helper function to write a string to w.
func (w *Writer) writeString(s string) error {
	_, err := io.WriteString(w.Writer, s)
	return err
}

func (w *Writer) writeCrlf() error {
	if err := w.writeString(crlf); err != nil {
		return err
	}

	return w.Flush()
}

func (w *Writer) writeNumber(num uint32) error {
	return w.writeString(formatNumber(num))
}

func (w *Writer) writeQuoted(s string) error {
	return w.writeString(strconv.Quote(s))
}

func (w *Writer) writeQuotedOrLiteral(s string) error {
	if !isAscii(s) {
		// IMAP doesn't allow 8-bit data outside literals
		return w.writeLiteral(bytes.NewBufferString(s))
	}

	return w.writeQuoted(s)
}

func (w *Writer) writeDateTime(t time.Time, layout string) error {
	if t.IsZero() {
		return w.writeString(nilAtom)
	}
	return w.writeQuoted(t.Format(layout))
}

func (w *Writer) writeFields(fields []interface{}) error {
	for i, field := range fields {
		if i > 0 { // Write separator
			if err := w.writeString(string(sp)); err != nil {
				return err
			}
		}

		if err := w.writeField(field); err != nil {
			return err
		}
	}

	return nil
}

func (w *Writer) writeList(fields []interface{}) error {
	if err := w.writeString(string(listStart)); err != nil {
		return err
	}

	if err := w.writeFields(fields); err != nil {
		return err
	}

	return w.writeString(string(listEnd))
}

func (w *Writer) writeLiteral(l Literal) error {
	if l == nil {
		return w.writeString(nilAtom)
	}

	unsyncLiteral := w.AllowAsyncLiterals && l.Len() <= 4096

	header := string(literalStart) + strconv.Itoa(l.Len())
	if unsyncLiteral {
		header += string('+')
	}
	header += string(literalEnd) + crlf
	if err := w.writeString(header); err != nil {
		return err
	}

	// If a channel is available, wait for a continuation request before sending data
	if !unsyncLiteral && w.continues != nil {
		// Make sure to flush the writer, otherwise we may never receive a continuation request
		if err := w.Flush(); err != nil {
			return err
		}

		if !<-w.continues {
			return fmt.Errorf("imap: cannot send literal: no continuation request received")
		}
	}

	// In case of bufio.Buffer, it will be 0 after io.Copy.
	literalLen := int64(l.Len())

	n, err := io.CopyN(w, l, literalLen)
	if err != nil {
		if err == io.EOF && n != literalLen {
			return fmt.Errorf("imap: size of Literal is not equal to Len() (%d != %d)", n, l.Len())
		}
		return err
	}
	if n != literalLen {
		return fmt.Errorf("imap: size of Literal is not equal to Len() (%d != %d)", n, l.Len())
	}

	return nil
}

func (w *Writer) writeField(field interface{}) error {
	if field == nil {
		return w.writeString(nilAtom)
	}

	switch field := field.(type) {
	case RawString:
		return w.writeString(string(field))
	case string:
		return w.writeQuotedOrLiteral(field)
	case int:
		return w.writeNumber(uint32(field))
	case uint32:
		return w.writeNumber(field)
	case Literal:
		return w.writeLiteral(field)
	case []interface{}:
		return w.writeList(field)
	case envelopeDateTime:
		return w.writeDateTime(time.Time(field), envelopeDateTimeLayout)
	case searchDate:
		return w.writeDateTime(time.Time(field), searchDateLayout)
	case Date:
		return w.writeDateTime(time.Time(field), DateLayout)
	case DateTime:
		return w.writeDateTime(time.Time(field), DateTimeLayout)
	case time.Time:
		return w.writeDateTime(field, DateTimeLayout)
	case *SeqSet:
		return w.writeString(field.String())
	case *BodySectionName:
		// Can contain spaces - that's why we don't just pass it as a string
		return w.writeString(string(field.FetchItem()))
	}

	return fmt.Errorf("imap: cannot format field: %v", field)
}

func (w *Writer) writeRespCode(code StatusRespCode, args []interface{}) error {
	if err := w.writeString(string(respCodeStart)); err != nil {
		return err
	}

	fields := []interface{}{RawString(code)}
	fields = append(fields, args...)

	if err := w.writeFields(fields); err != nil {
		return err
	}

	return w.writeString(string(respCodeEnd))
}

func (w *Writer) writeLine(fields ...interface{}) error {
	if err := w.writeFields(fields); err != nil {
		return err
	}

	return w.writeCrlf()
}

func (w *Writer) Flush() error {
	if f, ok := w.Writer.(flusher); ok {
		return f.Flush()
	}
	return nil
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{Writer: w}
}

func NewClientWriter(w io.Writer, continues <-chan bool) *Writer {
	return &Writer{Writer: w, continues: continues}
}
<|endoftext|>


<commit_before>package narcissus

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

// Write writes a structure pointer to the Augeas tree
func (n *Narcissus) Write(val interface{}) error {
	ref, err := structRef(val)
	if err != nil {
		return fmt.Errorf("invalid interface: %v", err)
	}

	path, err := getPath(ref)
	if err != nil {
		return fmt.Errorf("undefined path: %v", err)
	}

	return n.writeStruct(ref, path)
}

func (n *Narcissus) writeStruct(ref reflect.Value, path string) error {
	refType := ref.Type()
	for i := 0; i < refType.NumField(); i++ {
		if refType.Field(i).Name == "augeasPath" {
			// Ignore the special `augeasPath` field
			continue
		}
		err := n.writeField(ref.Field(i), refType.Field(i), path)
		if err != nil {
			return fmt.Errorf("failed to write field %s: %v", refType.Field(i).Name, err)
		}
	}

	return nil
}

func (n *Narcissus) writeField(field reflect.Value, fieldType reflect.StructField, path string) error {
	fieldPath := fmt.Sprintf("%s/%s", path, fieldType.Tag.Get("path"))
	if field.Kind() == reflect.Slice {
		return n.writeSliceField(field, fieldType, path, fieldPath)
	} else if field.Kind() == reflect.Map {
		return n.writeMapField(field, fieldType, path, fieldPath)
	}
	return n.writeSimpleField(field, fieldPath, fieldType.Tag)
}

func (n *Narcissus) writeSimpleField(field reflect.Value, fieldPath string, tag reflect.StructTag) error {
	aug := n.Augeas
	// There might be a better way to convert, but that does it
	value := fmt.Sprintf("%v", field.Interface())

	if tag.Get("value-from") == "label" {
		return nil
	}

	err := aug.Set(fieldPath, value)
	return err
}

func (n *Narcissus) writeSliceField(field reflect.Value, fieldType reflect.StructField, path, fieldPath string) error {
	for i := 0; i < field.Len(); i++ {
		value := field.Index(i)
		var p string
		if fieldType.Tag.Get("type") == "seq" {
			p = fmt.Sprintf("%s/%v", path, i+1)
		} else {
			p = fmt.Sprintf("%s[%v]", fieldPath, i+1)
		}
		if value.Kind() == reflect.Struct {
			err := n.writeStruct(value, p)
			if err != nil {
				return fmt.Errorf("failed to write slice struct value: %v", err)
			}
		} else {
			err := n.writeSimpleField(value, p, fieldType.Tag)
			if err != nil {
				return fmt.Errorf("failed to write slice value: %v", err)
			}
		}
	}

	return nil
}

func (n *Narcissus) writeMapField(field reflect.Value, fieldType reflect.StructField, path, fieldPath string) error {
	for _, k := range field.MapKeys() {
		value := field.MapIndex(k)
		var p string
		if strings.HasSuffix(fieldPath, "/*") {
			// TrimSuffix? ouch!
			p = fmt.Sprintf("%s/%s", strings.TrimSuffix(fieldPath, "/*"), k)
		} else {
			p = fmt.Sprintf("%s[.='%s']", fieldPath, k)
		}
		if value.Kind() == reflect.Struct {
			err := n.writeSimpleField(k, p, fieldType.Tag)
			if err != nil {
				return fmt.Errorf("failed to write map key: %v", err)
			}
			err = n.writeStruct(value, p)
			if err != nil {
				return fmt.Errorf("failed to write map struct value: %v", err)
			}
		} else {
			err := n.writeSimpleField(value, p, fieldType.Tag)
			if err != nil {
				return fmt.Errorf("failed to write map value: %v", err)
			}
		}
	}

	return nil
}
<commit_msg>Honor key=label in writeMapField()<commit_after>package narcissus

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

// Write writes a structure pointer to the Augeas tree
func (n *Narcissus) Write(val interface{}) error {
	ref, err := structRef(val)
	if err != nil {
		return fmt.Errorf("invalid interface: %v", err)
	}

	path, err := getPath(ref)
	if err != nil {
		return fmt.Errorf("undefined path: %v", err)
	}

	return n.writeStruct(ref, path)
}

func (n *Narcissus) writeStruct(ref reflect.Value, path string) error {
	refType := ref.Type()
	for i := 0; i < refType.NumField(); i++ {
		if refType.Field(i).Name == "augeasPath" {
			// Ignore the special `augeasPath` field
			continue
		}
		err := n.writeField(ref.Field(i), refType.Field(i), path)
		if err != nil {
			return fmt.Errorf("failed to write field %s: %v", refType.Field(i).Name, err)
		}
	}

	return nil
}

func (n *Narcissus) writeField(field reflect.Value, fieldType reflect.StructField, path string) error {
	fieldPath := fmt.Sprintf("%s/%s", path, fieldType.Tag.Get("path"))
	if field.Kind() == reflect.Slice {
		return n.writeSliceField(field, fieldType, path, fieldPath)
	} else if field.Kind() == reflect.Map {
		return n.writeMapField(field, fieldType, path, fieldPath)
	}
	return n.writeSimpleField(field, fieldPath, fieldType.Tag)
}

func (n *Narcissus) writeSimpleField(field reflect.Value, fieldPath string, tag reflect.StructTag) error {
	aug := n.Augeas
	// There might be a better way to convert, but that does it
	value := fmt.Sprintf("%v", field.Interface())

	if tag.Get("value-from") == "label" {
		return nil
	}

	err := aug.Set(fieldPath, value)
	return err
}

func (n *Narcissus) writeSliceField(field reflect.Value, fieldType reflect.StructField, path, fieldPath string) error {
	for i := 0; i < field.Len(); i++ {
		value := field.Index(i)
		var p string
		if fieldType.Tag.Get("type") == "seq" {
			p = fmt.Sprintf("%s/%v", path, i+1)
		} else {
			p = fmt.Sprintf("%s[%v]", fieldPath, i+1)
		}
		if value.Kind() == reflect.Struct {
			err := n.writeStruct(value, p)
			if err != nil {
				return fmt.Errorf("failed to write slice struct value: %v", err)
			}
		} else {
			err := n.writeSimpleField(value, p, fieldType.Tag)
			if err != nil {
				return fmt.Errorf("failed to write slice value: %v", err)
			}
		}
	}

	return nil
}

func (n *Narcissus) writeMapField(field reflect.Value, fieldType reflect.StructField, path, fieldPath string) (err error) {
	for _, k := range field.MapKeys() {
		value := field.MapIndex(k)
		var p string
		if strings.HasSuffix(fieldPath, "/*") {
			// TrimSuffix? ouch!
			p = fmt.Sprintf("%s/%s", strings.TrimSuffix(fieldPath, "/*"), k)
		} else {
			p = fmt.Sprintf("%s[.='%s']", fieldPath, k)
		}
		if value.Kind() == reflect.Struct {
			if fieldType.Tag.Get("key") != "label" {
				err = n.writeSimpleField(k, p, fieldType.Tag)
				if err != nil {
					return fmt.Errorf("failed to write map key: %v", err)
				}
			}
			err = n.writeStruct(value, p)
			if err != nil {
				return fmt.Errorf("failed to write map struct value: %v", err)
			}
		} else {
			err := n.writeSimpleField(value, p, fieldType.Tag)
			if err != nil {
				return fmt.Errorf("failed to write map value: %v", err)
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package discordgo

import (
	"encoding/json"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type wsWriter struct {
	session *Session

	conn           net.Conn
	closer         chan interface{}
	incoming       chan interface{}
	sendCloseQueue chan ws.StatusCode

	writer *wsutil.Writer
}

func (w *wsWriter) Run() {
	w.writer = wsutil.NewWriter(w.conn, ws.StateClientSide, ws.OpText)

	for {
		select {
		case <-w.closer:
			select {
			// Ensure we send the close frame
			case code := <-w.sendCloseQueue:
				w.sendClose(code)
			default:
			}
			return
		case msg := <-w.incoming:
			var err error
			switch t := msg.(type) {
			case []byte:
				err = w.writeRaw(t)
			default:
				err = w.writeJson(t)
			}

			if err != nil {
				w.session.log(LogError, "Error writing to gateway: %s", err.Error())
				return
			}
		case code := <-w.sendCloseQueue:
			w.sendClose(code)
		}
	}
}

type requestGuildMembersData struct {
	GuildID string `json:"guild_id"`
	Query   string `json:"query"`
	Limit   int    `json:"limit"`
}

type requestGuildMembersOp struct {
	Op   int                     `json:"op"`
	Data requestGuildMembersData `json:"d"`
}

func (w *wsWriter) writeJson(data interface{}) error {
	serialized, err := json.Marshal(data)
	if err != nil {
		return err
	}

	return w.writeRaw(serialized)
}

func (w *wsWriter) writeRaw(data []byte) error {
	_, err := w.writer.WriteThrough(data)
	if err != nil {
		return err
	}

	return w.writer.Flush()
}

func (w *wsWriter) sendClose(code ws.StatusCode) error {

	d, err := ws.CompileFrame(ws.NewCloseFrame(code, ""))
	if err != nil {
		return err
	}

	_, err = w.conn.Write(d)
	return err
}

func (w *wsWriter) Queue(data interface{}) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.incoming <- data:
	}
}

func (w *wsWriter) QueueClose(code ws.StatusCode) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.sendCloseQueue <- code:
	}
}

// // onVoiceServerUpdate handles the Voice Server Update data websocket event.
// //
// // This is also fired if the Guild's voice region changes while connected
// // to a voice channel.  In that case, need to re-establish connection to
// // the new region endpoint.
// func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {

// 	s.log(LogInformational, "called")

// 	s.RLock()
// 	voice, exists := s.VoiceConnections[st.GuildID]
// 	s.RUnlock()

// 	// If no VoiceConnection exists, just skip this
// 	if !exists {
// 		return
// 	}

// 	// If currently connected to voice ws/udp, then disconnect.
// 	// Has no effect if not connected.
// 	voice.Close()

// 	// Store values for later use
// 	voice.Lock()
// 	voice.token = st.Token
// 	voice.endpoint = st.Endpoint
// 	voice.GuildID = st.GuildID
// 	voice.Unlock()

// 	// Open a connection to the voice server
// 	err := voice.open()
// 	if err != nil {
// 		s.log(LogError, "onVoiceServerUpdate voice.open, %s", err)
// >>>>>>> develop
// 	}
// }

type wsHeartBeater struct {
	sync.Mutex

	writer      *wsWriter
	sequence    *int64
	receivedAck bool
	missedAcks  int
	stop        chan interface{}

	// Called when we received no Ack from last heartbeat
	onNoAck func()
}

func (wh *wsHeartBeater) ReceivedAck() {
	wh.Lock()
	wh.receivedAck = true
	wh.missedAcks = 0
	wh.Unlock()
}

func (wh *wsHeartBeater) UpdateSequence(seq int64) {
	atomic.StoreInt64(wh.sequence, seq)
}

func (wh *wsHeartBeater) Run(interval time.Duration) {
	ticker := time.NewTicker(interval)

	for {
		select {
		case <-ticker.C:

			wh.Lock()
			hasReceivedAck := wh.receivedAck
			wh.receivedAck = false
			wh.missedAcks++
			missed := wh.missedAcks
			wh.Unlock()

			if !hasReceivedAck && wh.onNoAck != nil && missed > 4 {
				wh.onNoAck()
			}

			wh.SendBeat()
		case <-wh.stop:
			return
		}
	}
}

func (wh *wsHeartBeater) SendBeat() {
	seq := atomic.LoadInt64(wh.sequence)

	wh.writer.Queue(&outgoingEvent{
		Operation: GatewayOPHeartbeat,
		Data:      seq,
	})
}
<commit_msg>Remove unused stuff<commit_after>package discordgo

import (
	"encoding/json"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type wsWriter struct {
	session *Session

	conn           net.Conn
	closer         chan interface{}
	incoming       chan interface{}
	sendCloseQueue chan ws.StatusCode

	writer *wsutil.Writer
}

func (w *wsWriter) Run() {
	w.writer = wsutil.NewWriter(w.conn, ws.StateClientSide, ws.OpText)

	for {
		select {
		case <-w.closer:
			select {
			// Ensure we send the close frame
			case code := <-w.sendCloseQueue:
				w.sendClose(code)
			default:
			}
			return
		case msg := <-w.incoming:
			var err error
			switch t := msg.(type) {
			case []byte:
				err = w.writeRaw(t)
			default:
				err = w.writeJson(t)
			}

			if err != nil {
				w.session.log(LogError, "Error writing to gateway: %s", err.Error())
				return
			}
		case code := <-w.sendCloseQueue:
			w.sendClose(code)
		}
	}
}

type requestGuildMembersData struct {
	GuildID string `json:"guild_id"`
	Query   string `json:"query"`
	Limit   int    `json:"limit"`
}

type requestGuildMembersOp struct {
	Op   int                     `json:"op"`
	Data requestGuildMembersData `json:"d"`
}

func (w *wsWriter) writeJson(data interface{}) error {
	serialized, err := json.Marshal(data)
	if err != nil {
		return err
	}

	return w.writeRaw(serialized)
}

func (w *wsWriter) writeRaw(data []byte) error {
	_, err := w.writer.WriteThrough(data)
	if err != nil {
		return err
	}

	return w.writer.Flush()
}

func (w *wsWriter) sendClose(code ws.StatusCode) error {

	d, err := ws.CompileFrame(ws.NewCloseFrame(code, ""))
	if err != nil {
		return err
	}

	_, err = w.conn.Write(d)
	return err
}

func (w *wsWriter) Queue(data interface{}) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.incoming <- data:
	}
}

func (w *wsWriter) QueueClose(code ws.StatusCode) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.sendCloseQueue <- code:
	}
}

type wsHeartBeater struct {
	sync.Mutex

	writer      *wsWriter
	sequence    *int64
	receivedAck bool
	missedAcks  int
	stop        chan interface{}

	// Called when we received no Ack from last heartbeat
	onNoAck func()
}

func (wh *wsHeartBeater) ReceivedAck() {
	wh.Lock()
	wh.receivedAck = true
	wh.missedAcks = 0
	wh.Unlock()
}

func (wh *wsHeartBeater) UpdateSequence(seq int64) {
	atomic.StoreInt64(wh.sequence, seq)
}

func (wh *wsHeartBeater) Run(interval time.Duration) {
	ticker := time.NewTicker(interval)

	for {
		select {
		case <-ticker.C:

			wh.Lock()
			hasReceivedAck := wh.receivedAck
			wh.receivedAck = false
			wh.missedAcks++
			missed := wh.missedAcks
			wh.Unlock()

			if !hasReceivedAck && wh.onNoAck != nil && missed > 4 {
				wh.onNoAck()
			}

			wh.SendBeat()
		case <-wh.stop:
			return
		}
	}
}

func (wh *wsHeartBeater) SendBeat() {
	seq := atomic.LoadInt64(wh.sequence)

	wh.writer.Queue(&outgoingEvent{
		Operation: GatewayOPHeartbeat,
		Data:      seq,
	})
}
<|endoftext|>


<commit_before>package discordgo

import (
	"encoding/json"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type wsWriter struct {
	session *Session

	conn           net.Conn
	closer         chan interface{}
	incoming       chan interface{}
	sendCloseQueue chan ws.StatusCode

	writer *wsutil.Writer
}

func (w *wsWriter) Run() {
	w.writer = wsutil.NewWriter(w.conn, ws.StateClientSide, ws.OpText)

	for {
		select {
		case <-w.closer:
			select {
			// Ensure we send the close frame
			case code := <-w.sendCloseQueue:
				w.sendClose(code)
			default:
			}
			return
		case msg := <-w.incoming:
			var err error
			switch t := msg.(type) {
			case []byte:
				err = w.writeRaw(t)
			default:
				err = w.writeJson(t)
			}

			if err != nil {
				w.session.log(LogError, "Error writing to gateway: %s", err.Error())
				return
			}
		case code := <-w.sendCloseQueue:
			w.sendClose(code)
		}
	}
}

type requestGuildMembersData struct {
	GuildID string `json:"guild_id"`
	Query   string `json:"query"`
	Limit   int    `json:"limit"`
}

type requestGuildMembersOp struct {
	Op   int                     `json:"op"`
	Data requestGuildMembersData `json:"d"`
}

func (w *wsWriter) writeJson(data interface{}) error {
	serialized, err := json.Marshal(data)
	if err != nil {
		return err
	}

	return w.writeRaw(serialized)
}

func (w *wsWriter) writeRaw(data []byte) error {
	_, err := w.writer.WriteThrough(data)
	if err != nil {
		return err
	}

	return w.writer.Flush()
}

func (w *wsWriter) sendClose(code ws.StatusCode) error {

	d, err := ws.CompileFrame(ws.NewCloseFrame(code, ""))
	if err != nil {
		return err
	}

	_, err = w.conn.Write(d)
	return err
}

func (w *wsWriter) Queue(data interface{}) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.incoming <- data:
	}
}

func (w *wsWriter) QueueClose(code ws.StatusCode) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.sendCloseQueue <- code:
	}
}

// // onVoiceServerUpdate handles the Voice Server Update data websocket event.
// //
// // This is also fired if the Guild's voice region changes while connected
// // to a voice channel.  In that case, need to re-establish connection to
// // the new region endpoint.
// func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {

// 	s.log(LogInformational, "called")

// 	s.RLock()
// 	voice, exists := s.VoiceConnections[st.GuildID]
// 	s.RUnlock()

// 	// If no VoiceConnection exists, just skip this
// 	if !exists {
// 		return
// 	}

// 	// If currently connected to voice ws/udp, then disconnect.
// 	// Has no effect if not connected.
// 	voice.Close()

// 	// Store values for later use
// 	voice.Lock()
// 	voice.token = st.Token
// 	voice.endpoint = st.Endpoint
// 	voice.GuildID = st.GuildID
// 	voice.Unlock()

// 	// Open a connection to the voice server
// 	err := voice.open()
// 	if err != nil {
// 		s.log(LogError, "onVoiceServerUpdate voice.open, %s", err)
// >>>>>>> develop
// 	}
// }

type wsHeartBeater struct {
	sync.Mutex

	writer      *wsWriter
	sequence    *int64
	receivedAck bool
	stop        chan interface{}

	// Called when we received no Ack from last heartbeat
	onNoAck func()
}

func (wh *wsHeartBeater) ReceivedAck() {
	wh.Lock()
	wh.receivedAck = true
	wh.Unlock()
}

func (wh *wsHeartBeater) UpdateSequence(seq int64) {
	atomic.StoreInt64(wh.sequence, seq)
}

func (wh *wsHeartBeater) Run(interval time.Duration) {
	ticker := time.NewTicker(interval)

	for {
		select {
		case <-ticker.C:

			wh.Lock()
			hasReceivedAck := wh.receivedAck
			wh.receivedAck = false
			wh.Unlock()
			if !hasReceivedAck && wh.onNoAck != nil {
				wh.onNoAck()
			}

			wh.SendBeat()
		case <-wh.stop:
			return
		}
	}
}

func (wh *wsHeartBeater) SendBeat() {
	seq := atomic.LoadInt64(wh.sequence)

	wh.writer.Queue(&outgoingEvent{
		Operation: GatewayOPHeartbeat,
		Data:      seq,
	})
}
<commit_msg>Trigger reconnect after 5 missed acks<commit_after>package discordgo

import (
	"encoding/json"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type wsWriter struct {
	session *Session

	conn           net.Conn
	closer         chan interface{}
	incoming       chan interface{}
	sendCloseQueue chan ws.StatusCode

	writer *wsutil.Writer
}

func (w *wsWriter) Run() {
	w.writer = wsutil.NewWriter(w.conn, ws.StateClientSide, ws.OpText)

	for {
		select {
		case <-w.closer:
			select {
			// Ensure we send the close frame
			case code := <-w.sendCloseQueue:
				w.sendClose(code)
			default:
			}
			return
		case msg := <-w.incoming:
			var err error
			switch t := msg.(type) {
			case []byte:
				err = w.writeRaw(t)
			default:
				err = w.writeJson(t)
			}

			if err != nil {
				w.session.log(LogError, "Error writing to gateway: %s", err.Error())
				return
			}
		case code := <-w.sendCloseQueue:
			w.sendClose(code)
		}
	}
}

type requestGuildMembersData struct {
	GuildID string `json:"guild_id"`
	Query   string `json:"query"`
	Limit   int    `json:"limit"`
}

type requestGuildMembersOp struct {
	Op   int                     `json:"op"`
	Data requestGuildMembersData `json:"d"`
}

func (w *wsWriter) writeJson(data interface{}) error {
	serialized, err := json.Marshal(data)
	if err != nil {
		return err
	}

	return w.writeRaw(serialized)
}

func (w *wsWriter) writeRaw(data []byte) error {
	_, err := w.writer.WriteThrough(data)
	if err != nil {
		return err
	}

	return w.writer.Flush()
}

func (w *wsWriter) sendClose(code ws.StatusCode) error {

	d, err := ws.CompileFrame(ws.NewCloseFrame(code, ""))
	if err != nil {
		return err
	}

	_, err = w.conn.Write(d)
	return err
}

func (w *wsWriter) Queue(data interface{}) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.incoming <- data:
	}
}

func (w *wsWriter) QueueClose(code ws.StatusCode) {
	select {
	case <-time.After(time.Second * 10):
	case <-w.closer:
	case w.sendCloseQueue <- code:
	}
}

// // onVoiceServerUpdate handles the Voice Server Update data websocket event.
// //
// // This is also fired if the Guild's voice region changes while connected
// // to a voice channel.  In that case, need to re-establish connection to
// // the new region endpoint.
// func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {

// 	s.log(LogInformational, "called")

// 	s.RLock()
// 	voice, exists := s.VoiceConnections[st.GuildID]
// 	s.RUnlock()

// 	// If no VoiceConnection exists, just skip this
// 	if !exists {
// 		return
// 	}

// 	// If currently connected to voice ws/udp, then disconnect.
// 	// Has no effect if not connected.
// 	voice.Close()

// 	// Store values for later use
// 	voice.Lock()
// 	voice.token = st.Token
// 	voice.endpoint = st.Endpoint
// 	voice.GuildID = st.GuildID
// 	voice.Unlock()

// 	// Open a connection to the voice server
// 	err := voice.open()
// 	if err != nil {
// 		s.log(LogError, "onVoiceServerUpdate voice.open, %s", err)
// >>>>>>> develop
// 	}
// }

type wsHeartBeater struct {
	sync.Mutex

	writer      *wsWriter
	sequence    *int64
	receivedAck bool
	missedAcks  int
	stop        chan interface{}

	// Called when we received no Ack from last heartbeat
	onNoAck func()
}

func (wh *wsHeartBeater) ReceivedAck() {
	wh.Lock()
	wh.receivedAck = true
	wh.missedAcks = 0
	wh.Unlock()
}

func (wh *wsHeartBeater) UpdateSequence(seq int64) {
	atomic.StoreInt64(wh.sequence, seq)
}

func (wh *wsHeartBeater) Run(interval time.Duration) {
	ticker := time.NewTicker(interval)

	for {
		select {
		case <-ticker.C:

			wh.Lock()
			hasReceivedAck := wh.receivedAck
			wh.receivedAck = false
			wh.missedAcks++
			missed := wh.missedAcks
			wh.Unlock()

			if !hasReceivedAck && wh.onNoAck != nil && missed > 4 {
				wh.onNoAck()
			}

			wh.SendBeat()
		case <-wh.stop:
			return
		}
	}
}

func (wh *wsHeartBeater) SendBeat() {
	seq := atomic.LoadInt64(wh.sequence)

	wh.writer.Queue(&outgoingEvent{
		Operation: GatewayOPHeartbeat,
		Data:      seq,
	})
}
<|endoftext|>


<commit_before>package peco

func (q *Query) Set(s string) {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	q.query = []rune(s)
}

func (q *Query) Reset() {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	q.query = []rune(nil)
}

func (q *Query) RestoreSavedQuery() {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	q.query = q.savedQuery
	q.query = []rune(nil)
}

func (q *Query) SaveQuery() {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	q.savedQuery = q.query
	q.savedQuery = []rune(nil)
}

func (q *Query) DeleteRange(start, end int) {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	if start == -1 {
		return
	}

	l := len(q.query)
	if end > l {
		end = l
	}

	if start > end {
		return
	}

	// everything up to "start" is left in tact
	// everything between start <-> end is deleted
	// everything up to "start" is left in tact
	// everything between start <-> end is deleted
	copy(q.query[start:], q.query[end:])
	q.query = q.query[:l-(end-start)]
}

func (q *Query) String() string {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	return string(q.query)
}

func (q *Query) Len() int {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	return len(q.query)
}

func (q *Query) Append(r rune) {
	q.mutex.Lock()
	defer q.mutex.Unlock()

	q.query = append(q.query, r)
}

func (q *Query) Runes() []rune {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	return q.query
}

func (q *Query) RuneAt(where int) rune {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	return q.query[where]
}

func (q *Query) InsertAt(ch rune, where int) {
	q.mutex.Lock()
	defer q.mutex.Unlock()

	if where == len(q.query) {
		q.query = append(q.query, ch)
		return
	}

	sq := q.query
	buf := make([]rune, len(sq)+1)
	copy(buf, sq[:where])
	buf[where] = ch
	copy(buf[where+1:], sq[where:])
	q.query = buf
}
<commit_msg>Change `Runes()` to return a copy of the underlying runes<commit_after>package peco

func (q *Query) Set(s string) {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	q.query = []rune(s)
}

func (q *Query) Reset() {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	q.query = []rune(nil)
}

func (q *Query) RestoreSavedQuery() {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	q.query = q.savedQuery
	q.query = []rune(nil)
}

func (q *Query) SaveQuery() {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	q.savedQuery = q.query
	q.savedQuery = []rune(nil)
}

func (q *Query) DeleteRange(start, end int) {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	if start == -1 {
		return
	}

	l := len(q.query)
	if end > l {
		end = l
	}

	if start > end {
		return
	}

	// everything up to "start" is left in tact
	// everything between start <-> end is deleted
	// everything up to "start" is left in tact
	// everything between start <-> end is deleted
	copy(q.query[start:], q.query[end:])
	q.query = q.query[:l-(end-start)]
}

func (q *Query) String() string {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	return string(q.query)
}

func (q *Query) Len() int {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	return len(q.query)
}

func (q *Query) Append(r rune) {
	q.mutex.Lock()
	defer q.mutex.Unlock()

	q.query = append(q.query, r)
}

// Runes returns a copy of the underlying query as an array of runes.
func (q *Query) Runes() []rune {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	ret := make([]rune, len(q.query))
	copy(ret, q.query)

	// Because this is a copy, the user of this function does not need
	// to know about locking and stuff
	return ret
}

func (q *Query) RuneAt(where int) rune {
	q.mutex.Lock()
	defer q.mutex.Unlock()
	return q.query[where]
}

func (q *Query) InsertAt(ch rune, where int) {
	q.mutex.Lock()
	defer q.mutex.Unlock()

	if where == len(q.query) {
		q.query = append(q.query, ch)
		return
	}

	sq := q.query
	buf := make([]rune, len(sq)+1)
	copy(buf, sq[:where])
	buf[where] = ch
	copy(buf[where+1:], sq[where:])
	q.query = buf
}
<|endoftext|>