<commit_before>package lazycache

import (
	"fmt"
	flag "github.com/spf13/pflag"
	"github.com/spf13/viper"
	"strings"
)

func ViperConfiguration() {

	// Configuration
	viper.SetDefault("port", 8080)
	viper.SetDefault("bind", "0.0.0.0")
	viper.SetDefault("imagestore", "")
	viper.SetDefault("imagestore.bucket", "camhd-image-cache")

	viper.SetDefault("quicktimestore", "")
	viper.SetDefault("directorystore", "")

	viper.SetConfigName("lazycache")
	viper.AddConfigPath("/etc/lazycache")
	viper.AddConfigPath(".")
	err := viper.ReadInConfig() // Find and read the config file
	if err != nil {             // Handle errors reading the config file
		switch err.(type) {
		case viper.ConfigFileNotFoundError:
			// ignore
		default:
			panic(fmt.Errorf("Fatal error config file: %s \n", err))
		}
	}

	viper.SetEnvPrefix("lazycache")
	viper.AutomaticEnv()
	// Convert '.' to '_' in configuration variable names
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

	// var (
	// 	bindFlag          = flag.String("bind", "0.0.0.0", "Network interface to bind to (defaults to 0.0.0.0)")
	// 	ImageStoreFlag   = flag.String("image-store", "", "Type of image store (none, google)")
	// 	ImageBucketFlag = flag.String("image-store-bucket", "", "Bucket used for Google image store")
	// )
	flag.Int("port", 80, "Network port to listen on (default: 8080)")
	flag.String("bind", "0.0.0.0", "Network interface to bind to (defaults to 0.0.0.0)")

	flag.String("image-store", "", "Type of image store (none, local, google)")
	flag.String("image-store-bucket", "camhd-image-cache", "Bucket used for Google image store")
	flag.String("image-local-root", "", "Bucket used for Google image store")
	flag.String("image-url-root", "", "Bucket used for Google image store")

	flag.String("quicktime-store", "", "Type of quicktime store (none, redis)")
	flag.String("directory-store", "", "Type of directory store (none, redis)")
	flag.String("redis-host", "localhost:6379", "Host used for redis store")

	viper.BindPFlag("port", flag.Lookup("port"))
	viper.BindPFlag("bind", flag.Lookup("bind"))
	viper.BindPFlag("imagestore", flag.Lookup("image-store"))

	viper.BindPFlag("imagestore.bucket", flag.Lookup("image-store-bucket"))
	viper.BindPFlag("imagestore.localroot", flag.Lookup("image-local-root"))

	viper.BindPFlag("directorystore", flag.Lookup("directory-store"))
	viper.BindPFlag("quicktimestore", flag.Lookup("quicktime-store"))
	viper.BindPFlag("redishost", flag.Lookup("redis-host"))

	flag.Parse()
}

func ConfigureImageStoreFromViper() {
	storeKey := viper.GetString("imagestore")
	DefaultLogger.Log("msg", fmt.Sprintf("Configuring image store with type \"%s\"", storeKey))
	switch strings.ToLower(storeKey) {
	default:
		DefaultLogger.Log("msg", fmt.Sprintf("Unable to determine type of image store from \"%s\"", storeKey))
		DefaultImageStore = NullImageStore{}
	case "", "none":
		DefaultLogger.Log("msg", "No image store configured.")
		DefaultImageStore = NullImageStore{}
	case "local":
		DefaultImageStore = CreateLocalStore(viper.GetString("imagestore.localRoot"),
			viper.GetString("imagestore.bind"))
	case "google":
		DefaultImageStore = CreateGoogleStore(viper.GetString("imagestore.bucket"))
	}
}

func ConfigureQuicktimeStoreFromViper() {
	storeKey := viper.GetString("quicktimestore")
	DefaultLogger.Log("msg", fmt.Sprintf("Configuring quicktime store with type \"%s\"", storeKey))

	switch strings.ToLower(storeKey) {
	default:
		DefaultLogger.Log("msg", fmt.Sprintf("Unable to determine type of image store from \"%s\"", storeKey))
		QTMetadataStore = CreateMapJSONStore()
	case "", "none":
		DefaultLogger.Log("msg", "Using default QuicktimeStore.")
		QTMetadataStore = CreateMapJSONStore()
	case "redis":
		hostname := viper.GetString("redishost")
		DefaultLogger.Log("msg", fmt.Sprintf("Connecting to redis host \"%s\"", hostname))
		redis, err := CreateRedisJSONStore(hostname, "qt")
		if err != nil {
			DefaultLogger.Log("msg", fmt.Sprintf("Failed to configure Redis Quicktime store to host \"%s\"", hostname))
		}

		DefaultLogger.Log("msg", fmt.Sprintf("Logging movie metadata to Redis at %s", hostname))
		QTMetadataStore = redis
	}
}

func ConfigureDirectoryStoreFromViper() {
	storeKey := viper.GetString("directorystore")
	DefaultLogger.Log("msg", fmt.Sprintf("Configuring directory store with type \"%s\"", storeKey))

	switch strings.ToLower(storeKey) {
	default:
		DefaultLogger.Log("msg", fmt.Sprintf("Unable to determine type of directory store from \"%s\"", storeKey))
		DirKeyStore = CreateMapJSONStore()
	case "", "none":
		DefaultLogger.Log("msg", "Using default directory store.")
		DirKeyStore = CreateMapJSONStore()
	case "redis":
		hostname := viper.GetString("redishost")
		DefaultLogger.Log("msg", fmt.Sprintf("Connecting to redis host \"%s\"", hostname))
		redis, err := CreateRedisJSONStore(hostname, "dir")
		if err != nil {
			DefaultLogger.Log("msg", fmt.Sprintf("Failed to configure Redis directory store to host \"%s\"", hostname))
		}

		DefaultLogger.Log("msg", fmt.Sprintf("Logging directory metadata to Redis at %s", hostname))
		DirKeyStore = redis
	}
}

func ConfigureFromViper() {
	DefaultLogger.Log("msg", "In ConfigureFromViper")
	ConfigureImageStoreFromViper()
	ConfigureDirectoryStoreFromViper()
	ConfigureQuicktimeStoreFromViper()
}
<commit_msg>Added redishost as a viper option.<commit_after>package lazycache

import (
	"fmt"
	flag "github.com/spf13/pflag"
	"github.com/spf13/viper"
	"strings"
)

func ViperConfiguration() {

	// Configuration
	viper.SetDefault("port", 8080)
	viper.SetDefault("bind", "0.0.0.0")
	viper.SetDefault("imagestore", "")
	viper.SetDefault("imagestore.bucket", "camhd-image-cache")

	viper.SetDefault("quicktimestore", "")
	viper.SetDefault("directorystore", "")
	viper.SetDefault("redishost", "localhost:6379")

	viper.SetConfigName("lazycache")
	viper.AddConfigPath("/etc/lazycache")
	viper.AddConfigPath(".")
	err := viper.ReadInConfig() // Find and read the config file
	if err != nil {             // Handle errors reading the config file
		switch err.(type) {
		case viper.ConfigFileNotFoundError:
			// ignore
		default:
			panic(fmt.Errorf("Fatal error config file: %s \n", err))
		}
	}

	viper.SetEnvPrefix("lazycache")
	viper.AutomaticEnv()
	// Convert '.' to '_' in configuration variable names
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

	// var (
	// 	bindFlag          = flag.String("bind", "0.0.0.0", "Network interface to bind to (defaults to 0.0.0.0)")
	// 	ImageStoreFlag   = flag.String("image-store", "", "Type of image store (none, google)")
	// 	ImageBucketFlag = flag.String("image-store-bucket", "", "Bucket used for Google image store")
	// )
	flag.Int("port", 80, "Network port to listen on (default: 8080)")
	flag.String("bind", "0.0.0.0", "Network interface to bind to (defaults to 0.0.0.0)")

	flag.String("image-store", "", "Type of image store (none, local, google)")
	flag.String("image-store-bucket", "camhd-image-cache", "Bucket used for Google image store")
	flag.String("image-local-root", "", "Bucket used for Google image store")
	flag.String("image-url-root", "", "Bucket used for Google image store")

	flag.String("quicktime-store", "", "Type of quicktime store (none, redis)")
	flag.String("directory-store", "", "Type of directory store (none, redis)")
	flag.String("redis-host", "localhost:6379", "Host used for redis store")

	viper.BindPFlag("port", flag.Lookup("port"))
	viper.BindPFlag("bind", flag.Lookup("bind"))

	viper.BindPFlag("imagestore", flag.Lookup("image-store"))
	viper.BindPFlag("imagestore.bucket", flag.Lookup("image-store-bucket"))
	viper.BindPFlag("imagestore.localroot", flag.Lookup("image-local-root"))

	viper.BindPFlag("directorystore", flag.Lookup("directory-store"))
	viper.BindPFlag("quicktimestore", flag.Lookup("quicktime-store"))
	viper.BindPFlag("redishost", flag.Lookup("redis-host"))

	flag.Parse()
}

func ConfigureImageStoreFromViper() {
	storeKey := viper.GetString("imagestore")
	DefaultLogger.Log("msg", fmt.Sprintf("Configuring image store with type \"%s\"", storeKey))
	switch strings.ToLower(storeKey) {
	default:
		DefaultLogger.Log("msg", fmt.Sprintf("Unable to determine type of image store from \"%s\"", storeKey))
		DefaultImageStore = NullImageStore{}
	case "", "none":
		DefaultLogger.Log("msg", "No image store configured.")
		DefaultImageStore = NullImageStore{}
	case "local":
		DefaultImageStore = CreateLocalStore(viper.GetString("imagestore.localRoot"),
			viper.GetString("imagestore.bind"))
	case "google":
		DefaultImageStore = CreateGoogleStore(viper.GetString("imagestore.bucket"))
	}
}

func ConfigureQuicktimeStoreFromViper() {
	storeKey := viper.GetString("quicktimestore")
	DefaultLogger.Log("msg", fmt.Sprintf("Configuring quicktime store with type \"%s\"", storeKey))

	switch strings.ToLower(storeKey) {
	default:
		DefaultLogger.Log("msg", fmt.Sprintf("Unable to determine type of image store from \"%s\"", storeKey))
		QTMetadataStore = CreateMapJSONStore()
	case "", "none":
		DefaultLogger.Log("msg", "Using default QuicktimeStore.")
		QTMetadataStore = CreateMapJSONStore()
	case "redis":
		hostname := viper.GetString("redishost")
		DefaultLogger.Log("msg", fmt.Sprintf("Connecting to redis host \"%s\"", hostname))
		redis, err := CreateRedisJSONStore(hostname, "qt")
		if err != nil {
			DefaultLogger.Log("msg", fmt.Sprintf("Failed to configure Redis Quicktime store to host \"%s\"", hostname))
		}

		DefaultLogger.Log("msg", fmt.Sprintf("Logging movie metadata to Redis at %s", hostname))
		QTMetadataStore = redis
	}
}

func ConfigureDirectoryStoreFromViper() {
	storeKey := viper.GetString("directorystore")
	DefaultLogger.Log("msg", fmt.Sprintf("Configuring directory store with type \"%s\"", storeKey))

	switch strings.ToLower(storeKey) {
	default:
		DefaultLogger.Log("msg", fmt.Sprintf("Unable to determine type of directory store from \"%s\"", storeKey))
		DirKeyStore = CreateMapJSONStore()
	case "", "none":
		DefaultLogger.Log("msg", "Using default directory store.")
		DirKeyStore = CreateMapJSONStore()
	case "redis":
		hostname := viper.GetString("redishost")
		DefaultLogger.Log("msg", fmt.Sprintf("Connecting to redis host \"%s\"", hostname))
		redis, err := CreateRedisJSONStore(hostname, "dir")
		if err != nil {
			DefaultLogger.Log("msg", fmt.Sprintf("Failed to configure Redis directory store to host \"%s\"", hostname))
		}

		DefaultLogger.Log("msg", fmt.Sprintf("Logging directory metadata to Redis at %s", hostname))
		DirKeyStore = redis
	}
}

func ConfigureFromViper() {
	DefaultLogger.Log("msg", "In ConfigureFromViper")
	ConfigureImageStoreFromViper()
	ConfigureDirectoryStoreFromViper()
	ConfigureQuicktimeStoreFromViper()
}
<|endoftext|>


<commit_before>package config

import (
	"flag"
	"reflect"

	"fmt"
)

var baseOptionSet OptionSet

func init() {
	baseOptionSet = make(OptionSet)

	Add(String("config", "config.json", "The filename of the config file to use", false))
	Add(Bool("config-export", false, "Export the as-run configuration to a file", false))
}

func String(name string, default_value string, description string, exportable bool) *Option {

	opt := Option{
		Name:        name,
		Description: description,

		DefaultValue: reflect.ValueOf(default_value),
		Value:        reflect.ValueOf(default_value),
		Type:         reflect.TypeOf(default_value),

		Exportable: exportable,
		flag:       flag.String(name, default_value, description),
	}

	return &opt
}

func Bool(name string, default_value bool, description string, exportable bool) *Option {

	opt := Option{
		Name:        name,
		Description: description,

		DefaultValue: reflect.ValueOf(default_value),
		Value:        reflect.ValueOf(default_value),
		Type:         reflect.TypeOf(default_value),

		Exportable: exportable,
		flag:       flag.Bool(name, default_value, description),
	}

	return &opt
}

func Int(name string, default_value int64, description string, exportable bool) *Option {

	opt := Option{
		Name:        name,
		Description: description,

		DefaultValue: reflect.ValueOf(default_value),
		Value:        reflect.ValueOf(default_value),
		Type:         reflect.TypeOf(default_value),

		Exportable: exportable,
		flag:       flag.Int64(name, default_value, description),
	}

	return &opt
}

func Float(name string, default_value float64, description string, exportable bool) *Option {

	opt := Option{
		Name:        name,
		Description: description,

		DefaultValue: reflect.ValueOf(default_value),
		Value:        reflect.ValueOf(default_value),
		Type:         reflect.TypeOf(default_value),

		Exportable: exportable,
		flag:       flag.Float64(name, default_value, description),
	}

	return &opt
}

func Add(o *Option) {
	baseOptionSet[o.Name] = o
}

func Build() {
	importFlags(true)
	config_filename := Require("config").String()
	importConfigFile(config_filename)
	flag.Parse()
	importFlags(false)

	if Require("config-export").Bool() {
		exportConfigToFile(config_filename)
	}
}

func Require(key string) *Option {

	s, err := Get(key)

	if err != nil {
		panic(err)
	}

	return s
}

func Get(key string) (*Option, error) {

	s, exists := baseOptionSet.Get(key)

	if !exists {
		return nil, fmt.Errorf("config option with key %s not found", key)
	}

	return s, nil
}

func importFlags(visitall bool) {
	setter := func(f *flag.Flag) {
		if v, exists := baseOptionSet.Get(f.Name); exists {
			var target, val reflect.Value
			target = reflect.ValueOf(v).Elem().FieldByName("Value")

			switch v.flag.(type) {
			case *string:
				val = reflect.ValueOf(*(v.flag.(*string)))
			case *int64:
				val = reflect.ValueOf(*(v.flag.(*int64)))
			case *float64:
				val = reflect.ValueOf(*(v.flag.(*float64)))
			case *bool:
				val = reflect.ValueOf(*(v.flag.(*bool)))
			}

			target.Set(val)
		}
	}

	if visitall {
		flag.VisitAll(setter)
	} else {
		flag.Visit(setter)
	}
}
<commit_msg>changing order of config parsing so we make sure to get the config file override if necessary<commit_after>package config

import (
	"flag"
	"reflect"

	"fmt"
)

var baseOptionSet OptionSet

func init() {
	baseOptionSet = make(OptionSet)

	Add(String("config", "config.json", "The filename of the config file to use", false))
	Add(Bool("config-export", false, "Export the as-run configuration to a file", false))
}

func String(name string, default_value string, description string, exportable bool) *Option {

	opt := Option{
		Name:        name,
		Description: description,

		DefaultValue: reflect.ValueOf(default_value),
		Value:        reflect.ValueOf(default_value),
		Type:         reflect.TypeOf(default_value),

		Exportable: exportable,
		flag:       flag.String(name, default_value, description),
	}

	return &opt
}

func Bool(name string, default_value bool, description string, exportable bool) *Option {

	opt := Option{
		Name:        name,
		Description: description,

		DefaultValue: reflect.ValueOf(default_value),
		Value:        reflect.ValueOf(default_value),
		Type:         reflect.TypeOf(default_value),

		Exportable: exportable,
		flag:       flag.Bool(name, default_value, description),
	}

	return &opt
}

func Int(name string, default_value int64, description string, exportable bool) *Option {

	opt := Option{
		Name:        name,
		Description: description,

		DefaultValue: reflect.ValueOf(default_value),
		Value:        reflect.ValueOf(default_value),
		Type:         reflect.TypeOf(default_value),

		Exportable: exportable,
		flag:       flag.Int64(name, default_value, description),
	}

	return &opt
}

func Float(name string, default_value float64, description string, exportable bool) *Option {

	opt := Option{
		Name:        name,
		Description: description,

		DefaultValue: reflect.ValueOf(default_value),
		Value:        reflect.ValueOf(default_value),
		Type:         reflect.TypeOf(default_value),

		Exportable: exportable,
		flag:       flag.Float64(name, default_value, description),
	}

	return &opt
}

func Add(o *Option) {
	baseOptionSet[o.Name] = o
}

func Build() {
	// parse flags
	flag.Parse()

	// set default values
	importFlags(true)

	// determine location of config file, import it
	config_filename := Require("config").String()
	importConfigFile(config_filename)

	// overwrite with flag
	importFlags(false)

	// export new config to file if necessary
	if Require("config-export").Bool() {
		exportConfigToFile(config_filename)
	}
}

func Require(key string) *Option {

	s, err := Get(key)

	if err != nil {
		panic(err)
	}

	return s
}

func Get(key string) (*Option, error) {

	s, exists := baseOptionSet.Get(key)

	if !exists {
		return nil, fmt.Errorf("config option with key %s not found", key)
	}

	return s, nil
}

func importFlags(visitall bool) {
	setter := func(f *flag.Flag) {
		if v, exists := baseOptionSet.Get(f.Name); exists {
			var target, val reflect.Value
			target = reflect.ValueOf(v).Elem().FieldByName("Value")

			switch v.flag.(type) {
			case *string:
				val = reflect.ValueOf(*(v.flag.(*string)))
			case *int64:
				val = reflect.ValueOf(*(v.flag.(*int64)))
			case *float64:
				val = reflect.ValueOf(*(v.flag.(*float64)))
			case *bool:
				val = reflect.ValueOf(*(v.flag.(*bool)))
			}

			fmt.Println(f.Name, val, visitall, f)

			target.Set(val)
		}
	}

	if visitall {
		flag.VisitAll(setter)
	} else {
		flag.Visit(setter)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/pkg/errors"
	"github.com/spf13/pflag"
	"gopkg.in/ini.v1"
)

// loadCfgFromFile returns config loaded from file if file exists, nil otherwise
func loadCfgFromFile(cfgFilePath string) (*ini.File, error) {
	if _, err := os.Stat(cfgFilePath); os.IsNotExist(err) {
		return nil, nil
	} else if err != nil {
		return nil, errors.WithStack(err)
	}

	f, err := ini.ShadowLoad(cfgFilePath)
	if err != nil {
		return nil, errors.Wrap(err, "while loading config file")
	}

	return f, nil
}

//TODO: this line will be reverted
// findFlagByName searches for a flag in provided flag sets
func findFlagByName(n string, flagSets ...*pflag.FlagSet) *pflag.Flag {
	for _, fs := range flagSets {
		if f := fs.Lookup(n); f != nil {
			return f
		}
	}
	return nil
}

// trySetFlagFromConfig tries to find flagName in flag sets and set its value.
// If flag not found it tries to set passthrough option value.
// If flagName doesn't match passthrough prefixes, it will return an error.
func trySetFlagFromConfig(flagName string, k *ini.Key, flagSets ...*pflag.FlagSet) error {
	f := findFlagByName(flagName, flagSets...)
	if f != nil {
		if f.Changed {
			return nil
		}
		err := f.Value.Set(k.Value())
		if err != nil {
			return errors.Wrapf(err, "invalid value for key %s", flagName)
		}
		return nil
	}

	prefix, targ, err := passthroughPrefixes.Lookup(flagName)
	if err != nil {
		return errors.Wrapf(err, "invalid key %s", flagName)
	}
	if prefix != nil {
		valuePtr := prefix.FieldSelector(passthroughOpts, targ)
		if *valuePtr == nil {
			*valuePtr = k.ValueWithShadows()
		} else {
			*valuePtr = append(*valuePtr, k.ValueWithShadows()...)
		}
		return nil
	}
	return fmt.Errorf("unknown key %s", flagName)
}

// loadFlagValuesFromConfig loads config and assigns its values to flag set (only if flag value wasn't changed before)
func loadFlagValuesFromConfig(cfgFilePath string, fs, persistentFs *pflag.FlagSet) {
	if strings.ToLower(cfgFilePath) == "none" {
		// Skip loading: special value to ignore config
		return
	}

	configFile, err := loadCfgFromFile(cfgFilePath)
	if err != nil || configFile == nil && cfgFilePath != defaultConfigFilePath {
		log.Fatal().Err(err).Msgf("Could not load config file %s", cfgFilePath)
		return
	}
	if configFile == nil {
		return
	}

	for _, currSection := range configFile.Sections() {
		for _, k := range currSection.Keys() {
			flagName := k.Name()
			if currSection.Name() != ini.DefaultSection {
				flagName = currSection.Name() + "." + flagName
			}

			err = trySetFlagFromConfig(flagName, k, fs, persistentFs)
			if err != nil {
				log.Fatal().Err(err).Msg("Invalid config")
				return
			}
		}
	}
}
<commit_msg>Ensure travis script is working<commit_after>//
// DISCLAIMER
//
// Copyright 2017-2022 ArangoDB GmbH, Cologne, Germany
//
// 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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//

package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/pkg/errors"
	"github.com/spf13/pflag"
	"gopkg.in/ini.v1"
)

// loadCfgFromFile returns config loaded from file if file exists, nil otherwise
func loadCfgFromFile(cfgFilePath string) (*ini.File, error) {
	if _, err := os.Stat(cfgFilePath); os.IsNotExist(err) {
		return nil, nil
	} else if err != nil {
		return nil, errors.WithStack(err)
	}

	f, err := ini.ShadowLoad(cfgFilePath)
	if err != nil {
		return nil, errors.Wrap(err, "while loading config file")
	}

	return f, nil
}

// findFlagByName searches for a flag in provided flag sets
func findFlagByName(n string, flagSets ...*pflag.FlagSet) *pflag.Flag {
	for _, fs := range flagSets {
		if f := fs.Lookup(n); f != nil {
			return f
		}
	}
	return nil
}

// trySetFlagFromConfig tries to find flagName in flag sets and set its value.
// If flag not found it tries to set passthrough option value.
// If flagName doesn't match passthrough prefixes, it will return an error.
func trySetFlagFromConfig(flagName string, k *ini.Key, flagSets ...*pflag.FlagSet) error {
	f := findFlagByName(flagName, flagSets...)
	if f != nil {
		if f.Changed {
			return nil
		}
		err := f.Value.Set(k.Value())
		if err != nil {
			return errors.Wrapf(err, "invalid value for key %s", flagName)
		}
		return nil
	}

	prefix, targ, err := passthroughPrefixes.Lookup(flagName)
	if err != nil {
		return errors.Wrapf(err, "invalid key %s", flagName)
	}
	if prefix != nil {
		valuePtr := prefix.FieldSelector(passthroughOpts, targ)
		if *valuePtr == nil {
			*valuePtr = k.ValueWithShadows()
		} else {
			*valuePtr = append(*valuePtr, k.ValueWithShadows()...)
		}
		return nil
	}
	return fmt.Errorf("unknown key %s", flagName)
}

// loadFlagValuesFromConfig loads config and assigns its values to flag set (only if flag value wasn't changed before)
func loadFlagValuesFromConfig(cfgFilePath string, fs, persistentFs *pflag.FlagSet) {
	if strings.ToLower(cfgFilePath) == "none" {
		// Skip loading: special value to ignore config
		return
	}

	configFile, err := loadCfgFromFile(cfgFilePath)
	if err != nil || configFile == nil && cfgFilePath != defaultConfigFilePath {
		log.Fatal().Err(err).Msgf("Could not load config file %s", cfgFilePath)
		return
	}
	if configFile == nil {
		return
	}

	for _, currSection := range configFile.Sections() {
		for _, k := range currSection.Keys() {
			flagName := k.Name()
			if currSection.Name() != ini.DefaultSection {
				flagName = currSection.Name() + "." + flagName
			}

			err = trySetFlagFromConfig(flagName, k, fs, persistentFs)
			if err != nil {
				log.Fatal().Err(err).Msg("Invalid config")
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package i18n

import (
	"fmt"
	"log"
	"path/filepath"
	"strings"

	"github.com/gobuffalo/buffalo"
	"github.com/gobuffalo/packr"
	"github.com/nicksnyder/go-i18n/i18n"
	"github.com/nicksnyder/go-i18n/i18n/language"
	"github.com/nicksnyder/go-i18n/i18n/translation"
	"github.com/pkg/errors"
)

// LanguageFinder can be implemented for custom finding of search
// languages. This can be useful if you want to load a user's language
// from something like a database. See Middleware() for more information
// on how the default implementation searches for languages.
type LanguageFinder func(*Translator, buffalo.Context) []string

// Translator for handling all your i18n needs.
type Translator struct {
	// Box - where are the files?
	Box packr.Box
	// DefaultLanguage - default is passed as a parameter on New.
	DefaultLanguage string
	// CookieName - name of the cookie to find the desired language.
	// default is "lang"
	CookieName string
	// SessionName - name of the session to find the desired language.
	// default is "lang"
	SessionName string
	// HelperName - name of the view helper. default is "t"
	HelperName     string
	LanguageFinder LanguageFinder
}

// Load translations from the t.Box.
func (t *Translator) Load() error {
	return t.Box.Walk(func(path string, f packr.File) error {
		b, err := t.Box.MustBytes(path)
		if err != nil {
			log.Fatal(err)
			return errors.WithStack(err)
		}

		base := filepath.Base(path)
		dir := filepath.Dir(path)

		// Add a prefix to the loaded string, to avoid collision with an ISO lang code
		return i18n.ParseTranslationFileBytes(fmt.Sprintf("%sbuff%s", dir, base), b)
	})
}

// AddTranslation directly, without using a file. This is useful if you wish to load translations
// from a database, instead of disk.
func (t *Translator) AddTranslation(lang *language.Language, translations ...translation.Translation) {
	i18n.AddTranslation(lang, translations...)
}

// New Translator. Requires a packr.Box that points to the location
// of the translation files, as well as a default language. This will
// also call t.Load() and load the translations from disk.
func New(box packr.Box, language string) (*Translator, error) {
	t := &Translator{
		Box:             box,
		DefaultLanguage: language,
		CookieName:      "lang",
		SessionName:     "lang",
		HelperName:      "t",
		LanguageFinder:  defaultLanguageFinder,
	}
	return t, t.Load()
}

// Middleware for loading the translations for the language(s)
// selected. By default languages are loaded in the following order:
//
// Cookie - "lang"
// Session - "lang"
// Header - "Accept-Language"
// Default - "en-US"
//
// These values can be changed on the Translator itself. In development
// model the translation files will be reloaded on each request.
func (t *Translator) Middleware() buffalo.MiddlewareFunc {
	return func(next buffalo.Handler) buffalo.Handler {
		return func(c buffalo.Context) error {

			// in development reload the translations
			if c.Value("env").(string) == "development" {
				err := t.Load()
				if err != nil {
					return err
				}
			}

			// set languages in context, if not set yet
			if langs := c.Value("languages"); langs == nil {
				c.Set("languages", t.LanguageFinder(t, c))
			}

			// set translator
			if T := c.Value("T"); T == nil {
				langs := c.Value("languages").([]string)
				T, err := i18n.Tfunc(langs[0], langs[1:]...)
				if err != nil {
					return err
				}
				c.Set("T", T)
			}

			// set up the helper function for the views:
			c.Set(t.HelperName, func(s string, i ...interface{}) string {
				return t.Translate(c, s, i...)
			})
			return next(c)
		}
	}
}

// Translate returns the translation of the string identified by translationID.
//
// See https://github.com/nicksnyder/go-i18n
//
// If there is no translation for translationID, then the translationID itself is returned.
// This makes it easy to identify missing translations in your app.
//
// If translationID is a non-plural form, then the first variadic argument may be a map[string]interface{}
// or struct that contains template data.
//
// If translationID is a plural form, the function accepts two parameter signatures
// 1. T(count int, data struct{})
// The first variadic argument must be an integer type
// (int, int8, int16, int32, int64) or a float formatted as a string (e.g. "123.45").
// The second variadic argument may be a map[string]interface{} or struct{} that contains template data.
// 2. T(data struct{})
// data must be a struct{} or map[string]interface{} that contains a Count field and the template data,
// Count field must be an integer type (int, int8, int16, int32, int64)
// or a float formatted as a string (e.g. "123.45").
func (t *Translator) Translate(c buffalo.Context, translationID string, args ...interface{}) string {
	T := c.Value("T").(i18n.TranslateFunc)
	return T(translationID, args...)
}

// AvailableLanguages gets the list of languages provided by the app
func (t *Translator) AvailableLanguages() []string {
	return i18n.LanguageTags()
}

func defaultLanguageFinder(t *Translator, c buffalo.Context) []string {
	langs := []string{}

	r := c.Request()

	// try to get the language from a cookie:
	if cookie, err := r.Cookie(t.CookieName); err == nil {
		if cookie.Value != "" {
			langs = append(langs, cookie.Value)
		}
	}

	// try to get the language from the session
	if s := c.Session().Get(t.SessionName); s != nil {
		langs = append(langs, s.(string))
	}

	// try to get the language from a header:
	acceptLang := r.Header.Get("Accept-Language")
	if acceptLang != "" {
		langs = append(langs, parseAcceptLanguage(acceptLang)...)
	}

	// finally set the default app language as fallback
	langs = append(langs, t.DefaultLanguage)
	return langs
}

// Inspired from https://siongui.github.io/2015/02/22/go-parse-accept-language/
// Parse an Accept-Language string to get usable lang values for i18n system
func parseAcceptLanguage(acptLang string) []string {
	var lqs []string

	langQStrs := strings.Split(acptLang, ",")
	for _, langQStr := range langQStrs {
		trimedLangQStr := strings.Trim(langQStr, " ")

		langQ := strings.Split(trimedLangQStr, ";")
		lq := langQ[0]
		lqs = append(lqs, lq)
	}
	return lqs
}
<commit_msg>fixed wonky test around AvailableLanguages<commit_after>package i18n

import (
	"fmt"
	"log"
	"path/filepath"
	"sort"
	"strings"

	"github.com/gobuffalo/buffalo"
	"github.com/gobuffalo/packr"
	"github.com/nicksnyder/go-i18n/i18n"
	"github.com/nicksnyder/go-i18n/i18n/language"
	"github.com/nicksnyder/go-i18n/i18n/translation"
	"github.com/pkg/errors"
)

// LanguageFinder can be implemented for custom finding of search
// languages. This can be useful if you want to load a user's language
// from something like a database. See Middleware() for more information
// on how the default implementation searches for languages.
type LanguageFinder func(*Translator, buffalo.Context) []string

// Translator for handling all your i18n needs.
type Translator struct {
	// Box - where are the files?
	Box packr.Box
	// DefaultLanguage - default is passed as a parameter on New.
	DefaultLanguage string
	// CookieName - name of the cookie to find the desired language.
	// default is "lang"
	CookieName string
	// SessionName - name of the session to find the desired language.
	// default is "lang"
	SessionName string
	// HelperName - name of the view helper. default is "t"
	HelperName     string
	LanguageFinder LanguageFinder
}

// Load translations from the t.Box.
func (t *Translator) Load() error {
	return t.Box.Walk(func(path string, f packr.File) error {
		b, err := t.Box.MustBytes(path)
		if err != nil {
			log.Fatal(err)
			return errors.WithStack(err)
		}

		base := filepath.Base(path)
		dir := filepath.Dir(path)

		// Add a prefix to the loaded string, to avoid collision with an ISO lang code
		return i18n.ParseTranslationFileBytes(fmt.Sprintf("%sbuff%s", dir, base), b)
	})
}

// AddTranslation directly, without using a file. This is useful if you wish to load translations
// from a database, instead of disk.
func (t *Translator) AddTranslation(lang *language.Language, translations ...translation.Translation) {
	i18n.AddTranslation(lang, translations...)
}

// New Translator. Requires a packr.Box that points to the location
// of the translation files, as well as a default language. This will
// also call t.Load() and load the translations from disk.
func New(box packr.Box, language string) (*Translator, error) {
	t := &Translator{
		Box:             box,
		DefaultLanguage: language,
		CookieName:      "lang",
		SessionName:     "lang",
		HelperName:      "t",
		LanguageFinder:  defaultLanguageFinder,
	}
	return t, t.Load()
}

// Middleware for loading the translations for the language(s)
// selected. By default languages are loaded in the following order:
//
// Cookie - "lang"
// Session - "lang"
// Header - "Accept-Language"
// Default - "en-US"
//
// These values can be changed on the Translator itself. In development
// model the translation files will be reloaded on each request.
func (t *Translator) Middleware() buffalo.MiddlewareFunc {
	return func(next buffalo.Handler) buffalo.Handler {
		return func(c buffalo.Context) error {

			// in development reload the translations
			if c.Value("env").(string) == "development" {
				err := t.Load()
				if err != nil {
					return err
				}
			}

			// set languages in context, if not set yet
			if langs := c.Value("languages"); langs == nil {
				c.Set("languages", t.LanguageFinder(t, c))
			}

			// set translator
			if T := c.Value("T"); T == nil {
				langs := c.Value("languages").([]string)
				T, err := i18n.Tfunc(langs[0], langs[1:]...)
				if err != nil {
					return err
				}
				c.Set("T", T)
			}

			// set up the helper function for the views:
			c.Set(t.HelperName, func(s string, i ...interface{}) string {
				return t.Translate(c, s, i...)
			})
			return next(c)
		}
	}
}

// Translate returns the translation of the string identified by translationID.
//
// See https://github.com/nicksnyder/go-i18n
//
// If there is no translation for translationID, then the translationID itself is returned.
// This makes it easy to identify missing translations in your app.
//
// If translationID is a non-plural form, then the first variadic argument may be a map[string]interface{}
// or struct that contains template data.
//
// If translationID is a plural form, the function accepts two parameter signatures
// 1. T(count int, data struct{})
// The first variadic argument must be an integer type
// (int, int8, int16, int32, int64) or a float formatted as a string (e.g. "123.45").
// The second variadic argument may be a map[string]interface{} or struct{} that contains template data.
// 2. T(data struct{})
// data must be a struct{} or map[string]interface{} that contains a Count field and the template data,
// Count field must be an integer type (int, int8, int16, int32, int64)
// or a float formatted as a string (e.g. "123.45").
func (t *Translator) Translate(c buffalo.Context, translationID string, args ...interface{}) string {
	T := c.Value("T").(i18n.TranslateFunc)
	return T(translationID, args...)
}

// AvailableLanguages gets the list of languages provided by the app
func (t *Translator) AvailableLanguages() []string {
	lt := i18n.LanguageTags()
	sort.Strings(lt)
	return lt
}

func defaultLanguageFinder(t *Translator, c buffalo.Context) []string {
	langs := []string{}

	r := c.Request()

	// try to get the language from a cookie:
	if cookie, err := r.Cookie(t.CookieName); err == nil {
		if cookie.Value != "" {
			langs = append(langs, cookie.Value)
		}
	}

	// try to get the language from the session
	if s := c.Session().Get(t.SessionName); s != nil {
		langs = append(langs, s.(string))
	}

	// try to get the language from a header:
	acceptLang := r.Header.Get("Accept-Language")
	if acceptLang != "" {
		langs = append(langs, parseAcceptLanguage(acceptLang)...)
	}

	// finally set the default app language as fallback
	langs = append(langs, t.DefaultLanguage)
	return langs
}

// Inspired from https://siongui.github.io/2015/02/22/go-parse-accept-language/
// Parse an Accept-Language string to get usable lang values for i18n system
func parseAcceptLanguage(acptLang string) []string {
	var lqs []string

	langQStrs := strings.Split(acptLang, ",")
	for _, langQStr := range langQStrs {
		trimedLangQStr := strings.Trim(langQStr, " ")

		langQ := strings.Split(trimedLangQStr, ";")
		lq := langQ[0]
		lqs = append(lqs, lq)
	}
	return lqs
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"path/filepath"
	"strings"
	"sync"
	"text/template"

	"github.com/99designs/smartling"
	"github.com/codegangsta/cli"
)

var ProjectCommand = cli.Command{
	Name:  "project",
	Usage: "manage local project files",
	Before: func(c *cli.Context) error {
		err := cmdBefore(c)
		if err != nil {
			return nil
		}

		return loadProjectErr
	},
	After: func(c *cli.Context) error {
		cleanupTempFiles()
		return nil
	},
	Subcommands: []cli.Command{
		projectFilesCommand,
		projectStatusCommand,
		projectPullCommand,
		projectPushCommand,
	},
}

func fetchRemoteFileList() stringSlice {
	files := stringSlice{}
	listFiles, err := client.List(smartling.ListRequest{})
	panicIfErr(err)

	for _, fs := range listFiles {
		files = append(files, fs.FileUri)
	}

	return files
}

func fetchLocales() []string {
	ll := []string{}
	locales, err := client.Locales()
	panicIfErr(err)
	for _, l := range locales {
		ll = append(ll, l.Locale)
	}

	return ll
}

var projectFilesCommand = cli.Command{
	Name:        "files",
	Usage:       "lists the local files",
	Description: "files",
	Action: func(c *cli.Context) {
		if len(c.Args()) != 0 {
			log.Println("Wrong number of arguments")
			log.Fatalln("Usage: files")
		}

		for _, projectFilepath := range ProjectConfig.Files() {
			fmt.Println(projectFilepath)
		}
	},
}

var projectStatusCommand = cli.Command{
	Name:        "status",
	Usage:       "show the status of the project's remote files",
	Description: "status [<prefix>]",
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "awaiting-auth",
			Usage: "Output the number of strings Awaiting Authorization",
		},
	},
	Action: func(c *cli.Context) {
		if len(c.Args()) > 1 {
			log.Println("Wrong number of arguments")
			log.Fatalln("Usage: status [<prefix>]")
		}

		prefix := prefixOrGitPrefix(c.Args().Get(0))
		locales := fetchLocales()
		statuses := GetProjectStatus(prefix, locales)

		if c.Bool("awaiting-auth") {
			fmt.Println(statuses.AwaitingAuthorizationCount())
		} else {
			fmt.Print("\n")
			PrintProjectStatusTable(statuses, locales)
			fmt.Print("\n")
			fmt.Printf("Awaiting Authorization: %4d\n", statuses.AwaitingAuthorizationCount())
			fmt.Printf("Total:                  %4d\n", statuses.TotalStringsCount())
		}

	},
}

var projectPullCommand = cli.Command{
	Name:  "pull",
	Usage: "translate local project files using Smartling as a translation memory",

	Action: func(c *cli.Context) {
		if len(c.Args()) != 0 {
			log.Println("Wrong number of arguments")
			log.Fatalln("Usage: project pull")
		}

		locales, err := client.Locales()
		panicIfErr(err)

		var wg sync.WaitGroup
		for _, projectFilepath := range ProjectConfig.Files() {
			for _, l := range locales {
				wg.Add(1)
				go func(locale, projectFilepath string) {
					defer wg.Done()

					hit, b, err, _ := translateViaCache(
						locale,
						localRelativeFilePath(projectFilepath),
						filetypeForProjectFile(projectFilepath),
						ProjectConfig.ParserConfig,
					)
					panicIfErr(err)

					fp := localPullFilePath(projectFilepath, locale)
					cached := ""
					if hit {
						cached = "(using cache)"
					}
					fmt.Println(fp, cached)
					err = ioutil.WriteFile(fp, b, 0644)
					panicIfErr(err)
				}(l.Locale, projectFilepath)
			}
		}
		wg.Wait()
	},
}

func cleanPrefix(s string) string {
	s = filepath.Clean("/" + s)
	if s == "/" {
		return ""
	}
	return s
}

func prefixOrGitPrefix(prefix string) string {
	if prefix == "" {
		prefix = pushPrefix()
	}

	if prefix == "/branch/master" {
		prefix = "/"
	}

	prefix = cleanPrefix(prefix)

	if prefix != "" {
		log.Println("Using prefix", prefix)
	}
	return prefix
}

type RemoteFileStatus struct {
	RemoteFilePath string
	Statuses       map[string]*smartling.FileStatus
}

func (r *RemoteFileStatus) NotCompletedStringCount() int {
	c := 0
	for _, fs := range r.Statuses {
		c += fs.NotCompletedStringCount()
	}
	return c
}

func fetchStatusForLocales(remoteFilePath string, locales []string) RemoteFileStatus {
	ss := RemoteFileStatus{
		RemoteFilePath: remoteFilePath,
		Statuses:       map[string]*smartling.FileStatus{},
	}

	var wg sync.WaitGroup
	for _, locale := range locales {
		wg.Add(1)
		go func(f, l string) {
			defer wg.Done()

			s, err := client.Status(f, l)
			panicIfErr(err)
			ss.Statuses[l] = &s

		}(remoteFilePath, locale)
	}
	wg.Wait()

	return ss
}

var projectPushCommand = cli.Command{
	Name:  "push",
	Usage: "upload local project files that contain untranslated strings",
	Description: `push [<prefix>]
Outputs the uploaded files for the given prefix
`,
	Action: func(c *cli.Context) {
		if len(c.Args()) > 1 {
			log.Println("Wrong number of arguments")
			log.Fatalln("Usage: push [<prefix>]")
		}

		prefix := prefixOrGitPrefix(c.Args().Get(0))
		locales := fetchLocales()

		var wg sync.WaitGroup
		for _, projectFilepath := range ProjectConfig.Files() {
			wg.Add(1)
			go func(prefix, projectFilepath string) {
				defer wg.Done()

				remoteFile := filepath.Clean(prefix + "/" + projectFilepath)

				_, err := client.Upload(projectFilepath, &smartling.UploadRequest{
					FileUri:      remoteFile,
					FileType:     filetypeForProjectFile(projectFilepath),
					ParserConfig: ProjectConfig.ParserConfig,
				})
				panicIfErr(err)

				remoteFileStatuses := fetchStatusForLocales(remoteFile, locales)

				// when using a prefix, we don't want to see files with
				// completely translated content
				if prefix != "" && remoteFileStatuses.NotCompletedStringCount() == 0 {
					err := client.Delete(remoteFile)
					panicIfErr(err)
				} else {
					fmt.Println(remoteFile)
				}
			}(prefix, projectFilepath)
		}
		wg.Wait()
	},
}

func filetypeForProjectFile(projectFilepath string) smartling.FileType {
	ft := smartling.FileTypeByExtension(filepath.Ext(projectFilepath))
	if ft == "" {
		ft = ProjectConfig.FileType
	}
	if ft == "" {
		log.Panicln("Can't determine file type for " + projectFilepath)
	}

	return ft
}

type FilenameParts struct {
	Path           string
	Base           string
	Dir            string
	Ext            string
	PathWithoutExt string
	Locale         string
}

func localRelativeFilePath(remotepath string) string {
	fp, err := filepath.Rel(".", filepath.Join(ProjectConfig.path, remotepath))
	panicIfErr(err)
	return fp
}

func localPullFilePath(p, locale string) string {
	parts := FilenameParts{
		Path:   p,
		Dir:    filepath.Dir(p),
		Base:   filepath.Base(p),
		Ext:    filepath.Ext(p),
		Locale: locale,
	}

	dt := defaultPullDestination
	if dt != "" {
		dt = ProjectConfig.PullFilePath
	}

	out := bytes.NewBufferString("")
	tmpl := template.New("name")
	tmpl.Funcs(template.FuncMap{
		"TrimSuffix": strings.TrimSuffix,
	})
	_, err := tmpl.Parse(dt)
	panicIfErr(err)

	err = tmpl.Execute(out, parts)
	panicIfErr(err)

	return localRelativeFilePath(out.String())
}
<commit_msg>Add Truncate template function<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"path/filepath"
	"strings"
	"sync"
	"text/template"

	"github.com/99designs/smartling"
	"github.com/codegangsta/cli"
)

var ProjectCommand = cli.Command{
	Name:  "project",
	Usage: "manage local project files",
	Before: func(c *cli.Context) error {
		err := cmdBefore(c)
		if err != nil {
			return nil
		}

		return loadProjectErr
	},
	After: func(c *cli.Context) error {
		cleanupTempFiles()
		return nil
	},
	Subcommands: []cli.Command{
		projectFilesCommand,
		projectStatusCommand,
		projectPullCommand,
		projectPushCommand,
	},
}

func fetchRemoteFileList() stringSlice {
	files := stringSlice{}
	listFiles, err := client.List(smartling.ListRequest{})
	panicIfErr(err)

	for _, fs := range listFiles {
		files = append(files, fs.FileUri)
	}

	return files
}

func fetchLocales() []string {
	ll := []string{}
	locales, err := client.Locales()
	panicIfErr(err)
	for _, l := range locales {
		ll = append(ll, l.Locale)
	}

	return ll
}

var projectFilesCommand = cli.Command{
	Name:        "files",
	Usage:       "lists the local files",
	Description: "files",
	Action: func(c *cli.Context) {
		if len(c.Args()) != 0 {
			log.Println("Wrong number of arguments")
			log.Fatalln("Usage: files")
		}

		for _, projectFilepath := range ProjectConfig.Files() {
			fmt.Println(projectFilepath)
		}
	},
}

var projectStatusCommand = cli.Command{
	Name:        "status",
	Usage:       "show the status of the project's remote files",
	Description: "status [<prefix>]",
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "awaiting-auth",
			Usage: "Output the number of strings Awaiting Authorization",
		},
	},
	Action: func(c *cli.Context) {
		if len(c.Args()) > 1 {
			log.Println("Wrong number of arguments")
			log.Fatalln("Usage: status [<prefix>]")
		}

		prefix := prefixOrGitPrefix(c.Args().Get(0))
		locales := fetchLocales()
		statuses := GetProjectStatus(prefix, locales)

		if c.Bool("awaiting-auth") {
			fmt.Println(statuses.AwaitingAuthorizationCount())
		} else {
			fmt.Print("\n")
			PrintProjectStatusTable(statuses, locales)
			fmt.Print("\n")
			fmt.Printf("Awaiting Authorization: %4d\n", statuses.AwaitingAuthorizationCount())
			fmt.Printf("Total:                  %4d\n", statuses.TotalStringsCount())
		}

	},
}

var projectPullCommand = cli.Command{
	Name:  "pull",
	Usage: "translate local project files using Smartling as a translation memory",

	Action: func(c *cli.Context) {
		if len(c.Args()) != 0 {
			log.Println("Wrong number of arguments")
			log.Fatalln("Usage: project pull")
		}

		locales, err := client.Locales()
		panicIfErr(err)

		var wg sync.WaitGroup
		for _, projectFilepath := range ProjectConfig.Files() {
			for _, l := range locales {
				wg.Add(1)
				go func(locale, projectFilepath string) {
					defer wg.Done()

					hit, b, err, _ := translateViaCache(
						locale,
						localRelativeFilePath(projectFilepath),
						filetypeForProjectFile(projectFilepath),
						ProjectConfig.ParserConfig,
					)
					panicIfErr(err)

					fp := localPullFilePath(projectFilepath, locale)
					cached := ""
					if hit {
						cached = "(using cache)"
					}
					fmt.Println(fp, cached)
					err = ioutil.WriteFile(fp, b, 0644)
					panicIfErr(err)
				}(l.Locale, projectFilepath)
			}
		}
		wg.Wait()
	},
}

func cleanPrefix(s string) string {
	s = filepath.Clean("/" + s)
	if s == "/" {
		return ""
	}
	return s
}

func prefixOrGitPrefix(prefix string) string {
	if prefix == "" {
		prefix = pushPrefix()
	}

	if prefix == "/branch/master" {
		prefix = "/"
	}

	prefix = cleanPrefix(prefix)

	if prefix != "" {
		log.Println("Using prefix", prefix)
	}
	return prefix
}

type RemoteFileStatus struct {
	RemoteFilePath string
	Statuses       map[string]*smartling.FileStatus
}

func (r *RemoteFileStatus) NotCompletedStringCount() int {
	c := 0
	for _, fs := range r.Statuses {
		c += fs.NotCompletedStringCount()
	}
	return c
}

func fetchStatusForLocales(remoteFilePath string, locales []string) RemoteFileStatus {
	ss := RemoteFileStatus{
		RemoteFilePath: remoteFilePath,
		Statuses:       map[string]*smartling.FileStatus{},
	}

	var wg sync.WaitGroup
	for _, locale := range locales {
		wg.Add(1)
		go func(f, l string) {
			defer wg.Done()

			s, err := client.Status(f, l)
			panicIfErr(err)
			ss.Statuses[l] = &s

		}(remoteFilePath, locale)
	}
	wg.Wait()

	return ss
}

var projectPushCommand = cli.Command{
	Name:  "push",
	Usage: "upload local project files that contain untranslated strings",
	Description: `push [<prefix>]
Outputs the uploaded files for the given prefix
`,
	Action: func(c *cli.Context) {
		if len(c.Args()) > 1 {
			log.Println("Wrong number of arguments")
			log.Fatalln("Usage: push [<prefix>]")
		}

		prefix := prefixOrGitPrefix(c.Args().Get(0))
		locales := fetchLocales()

		var wg sync.WaitGroup
		for _, projectFilepath := range ProjectConfig.Files() {
			wg.Add(1)
			go func(prefix, projectFilepath string) {
				defer wg.Done()

				remoteFile := filepath.Clean(prefix + "/" + projectFilepath)

				_, err := client.Upload(projectFilepath, &smartling.UploadRequest{
					FileUri:      remoteFile,
					FileType:     filetypeForProjectFile(projectFilepath),
					ParserConfig: ProjectConfig.ParserConfig,
				})
				panicIfErr(err)

				remoteFileStatuses := fetchStatusForLocales(remoteFile, locales)

				// when using a prefix, we don't want to see files with
				// completely translated content
				if prefix != "" && remoteFileStatuses.NotCompletedStringCount() == 0 {
					err := client.Delete(remoteFile)
					panicIfErr(err)
				} else {
					fmt.Println(remoteFile)
				}
			}(prefix, projectFilepath)
		}
		wg.Wait()
	},
}

func filetypeForProjectFile(projectFilepath string) smartling.FileType {
	ft := smartling.FileTypeByExtension(filepath.Ext(projectFilepath))
	if ft == "" {
		ft = ProjectConfig.FileType
	}
	if ft == "" {
		log.Panicln("Can't determine file type for " + projectFilepath)
	}

	return ft
}

type FilenameParts struct {
	Path           string
	Base           string
	Dir            string
	Ext            string
	PathWithoutExt string
	Locale         string
}

func localRelativeFilePath(remotepath string) string {
	fp, err := filepath.Rel(".", filepath.Join(ProjectConfig.path, remotepath))
	panicIfErr(err)
	return fp
}

func localPullFilePath(p, locale string) string {
	parts := FilenameParts{
		Path:   p,
		Dir:    filepath.Dir(p),
		Base:   filepath.Base(p),
		Ext:    filepath.Ext(p),
		Locale: locale,
	}

	dt := defaultPullDestination
	if dt != "" {
		dt = ProjectConfig.PullFilePath
	}

	out := bytes.NewBufferString("")
	tmpl := template.New("name")
	tmpl.Funcs(template.FuncMap{
		"TrimSuffix": strings.TrimSuffix,
		"Truncate": func(s string, n int) string {
			return s[:n]
		},
	})
	_, err := tmpl.Parse(dt)
	panicIfErr(err)

	err = tmpl.Execute(out, parts)
	panicIfErr(err)

	return localRelativeFilePath(out.String())
}
<|endoftext|>


<commit_before>package tui

import (
	"image"

	"github.com/gdamore/tcell"
)

var _ UI = &tcellUI{}

type tcellUI struct {
	painter *Painter
	root    Widget

	keybindings []*keybinding

	quit chan struct{}

	screen tcell.Screen

	kbFocus *kbFocusController

	eventQueue chan event
}

func newTcellUI(root Widget) (*tcellUI, error) {
	screen, err := tcell.NewScreen()
	if err != nil {
		return nil, err
	}

	s := &tcellSurface{
		screen: screen,
	}
	p := NewPainter(s, DefaultTheme)

	return &tcellUI{
		painter:     p,
		root:        root,
		keybindings: make([]*keybinding, 0),
		quit:        make(chan struct{}, 1),
		screen:      screen,
		kbFocus:     &kbFocusController{chain: DefaultFocusChain},
		eventQueue:  make(chan event),
	}, nil
}

func (ui *tcellUI) SetWidget(w Widget) {
	ui.root = w
}

func (ui *tcellUI) SetTheme(t *Theme) {
	ui.painter.theme = t
}

func (ui *tcellUI) SetFocusChain(chain FocusChain) {
	ui.kbFocus.chain = chain
}

func (ui *tcellUI) SetKeybinding(seq string, fn func()) {
	ui.keybindings = append(ui.keybindings, &keybinding{
		sequence: seq,
		handler:  fn,
	})
}

// ClearKeybindings reinitialises ui.keybindings so as to revert to a
// clear/original state
func (ui *tcellUI) ClearKeybindings() {
	ui.keybindings = make([]*keybinding, 0)
}

func (ui *tcellUI) Run() error {
	if err := ui.screen.Init(); err != nil {
		return err
	}

	if w := ui.kbFocus.chain.FocusDefault(); w != nil {
		w.SetFocused(true)
		ui.kbFocus.focusedWidget = w
	}

	ui.screen.SetStyle(tcell.StyleDefault)
	ui.screen.EnableMouse()
	ui.screen.Clear()

	go func() {
		for {
			switch ev := ui.screen.PollEvent().(type) {
			case *tcell.EventKey:
				ui.handleKeyEvent(ev)
			case *tcell.EventMouse:
				ui.handleMouseEvent(ev)
			case *tcell.EventResize:
				ui.handleResizeEvent(ev)
			}
		}
	}()

	for {
		select {
		case <-ui.quit:
			return nil
		case ev := <-ui.eventQueue:
			ui.handleEvent(ev)
		}
	}
}

func (ui *tcellUI) handleEvent(ev event) {
	switch e := ev.(type) {
	case KeyEvent:
		logger.Printf("Received key event: %s", e.Name())

		for _, b := range ui.keybindings {
			if b.match(e) {
				b.handler()
			}
		}
		ui.kbFocus.OnKeyEvent(e)
		ui.root.OnKeyEvent(e)
		ui.painter.Repaint(ui.root)
	case callbackEvent:
		// Gets stuck in a print loop when the logger is a widget.
		//logger.Printf("Received callback event")
		e.cbFn()
		ui.painter.Repaint(ui.root)
	case paintEvent:
		logger.Printf("Received paint event")
		ui.painter.Repaint(ui.root)
	}
}

func (ui *tcellUI) handleKeyEvent(tev *tcell.EventKey) {
	ui.eventQueue <- KeyEvent{
		Key:       Key(tev.Key()),
		Rune:      tev.Rune(),
		Modifiers: ModMask(tev.Modifiers()),
	}
}

func (ui *tcellUI) handleMouseEvent(ev *tcell.EventMouse) {
	x, y := ev.Position()
	ui.eventQueue <- MouseEvent{Pos: image.Pt(x, y)}
}

func (ui *tcellUI) handleResizeEvent(ev *tcell.EventResize) {
	ui.eventQueue <- paintEvent{}
}

// Quit signals to the UI to start shutting down.
func (ui *tcellUI) Quit() {
	logger.Printf("Quitting")
	ui.screen.Fini()
	ui.quit <- struct{}{}
}

// Schedule an update of the UI, running the given
// function in the UI goroutine.
//
// Use this to update the UI in response to external events,
// like a timer tick.
// This method should be used any time you call methods
// to change UI objects after the first call to `UI.Run()`.
//
// Changes invoked outside of either this callback or the
// other event handler callbacks may appear to work, but
// is likely a race condition.  (Run your program with
// `go run -race` or `go install -race` to detect this!)
//
// Calling Update from within an event handler, or from within an Update call,
// is an error, and will deadlock.
func (ui *tcellUI) Update(fn func()) {
	blk := make(chan struct{})
	ui.eventQueue <- callbackEvent{func() {
		fn()
		close(blk)
	}}
	<-blk
}

var _ Surface = &tcellSurface{}

type tcellSurface struct {
	screen tcell.Screen
}

func (s *tcellSurface) SetCell(x, y int, ch rune, style Style) {
	st := tcell.StyleDefault.Normal().
		Foreground(convertColor(style.Fg, false)).
		Background(convertColor(style.Bg, false)).
		Reverse(style.Reverse == DecorationOn).
		Bold(style.Bold == DecorationOn).
		Underline(style.Underline == DecorationOn)

	s.screen.SetContent(x, y, ch, nil, st)
}

func (s *tcellSurface) SetCursor(x, y int) {
	s.screen.ShowCursor(x, y)
}

func (s *tcellSurface) HideCursor() {
	s.screen.HideCursor()
}

func (s *tcellSurface) Begin() {
	s.screen.Clear()
}

func (s *tcellSurface) End() {
	s.screen.Show()
}

func (s *tcellSurface) Size() image.Point {
	w, h := s.screen.Size()
	return image.Point{w, h}
}

func convertColor(col Color, fg bool) tcell.Color {
	switch col {
	case ColorDefault:
		if fg {
			return tcell.ColorWhite
		}
		return tcell.ColorDefault
	case ColorBlack:
		return tcell.ColorBlack
	case ColorWhite:
		return tcell.ColorWhite
	case ColorRed:
		return tcell.ColorRed
	case ColorGreen:
		return tcell.ColorGreen
	case ColorBlue:
		return tcell.ColorBlue
	case ColorCyan:
		return tcell.ColorDarkCyan
	case ColorMagenta:
		return tcell.ColorDarkMagenta
	case ColorYellow:
		return tcell.ColorYellow
	default:
		if col > 0 {
			return tcell.Color(col)
		}
		return tcell.ColorDefault
	}
}
<commit_msg>Update focusedWidget field when SetFocusChain() called<commit_after>package tui

import (
	"image"

	"github.com/gdamore/tcell"
)

var _ UI = &tcellUI{}

type tcellUI struct {
	painter *Painter
	root    Widget

	keybindings []*keybinding

	quit chan struct{}

	screen tcell.Screen

	kbFocus *kbFocusController

	eventQueue chan event
}

func newTcellUI(root Widget) (*tcellUI, error) {
	screen, err := tcell.NewScreen()
	if err != nil {
		return nil, err
	}

	s := &tcellSurface{
		screen: screen,
	}
	p := NewPainter(s, DefaultTheme)

	return &tcellUI{
		painter:     p,
		root:        root,
		keybindings: make([]*keybinding, 0),
		quit:        make(chan struct{}, 1),
		screen:      screen,
		kbFocus:     &kbFocusController{chain: DefaultFocusChain},
		eventQueue:  make(chan event),
	}, nil
}

func (ui *tcellUI) SetWidget(w Widget) {
	ui.root = w
}

func (ui *tcellUI) SetTheme(t *Theme) {
	ui.painter.theme = t
}

func (ui *tcellUI) SetFocusChain(chain FocusChain) {
	if ui.kbFocus.focusedWidget != nil {
		ui.kbFocus.focusedWidget.SetFocused(false)
	}

	ui.kbFocus.chain = chain
	ui.kbFocus.focusedWidget = chain.FocusDefault()

	if ui.kbFocus.focusedWidget != nil {
		ui.kbFocus.focusedWidget.SetFocused(true)
	}
}

func (ui *tcellUI) SetKeybinding(seq string, fn func()) {
	ui.keybindings = append(ui.keybindings, &keybinding{
		sequence: seq,
		handler:  fn,
	})
}

// ClearKeybindings reinitialises ui.keybindings so as to revert to a
// clear/original state
func (ui *tcellUI) ClearKeybindings() {
	ui.keybindings = make([]*keybinding, 0)
}

func (ui *tcellUI) Run() error {
	if err := ui.screen.Init(); err != nil {
		return err
	}

	if w := ui.kbFocus.chain.FocusDefault(); w != nil {
		w.SetFocused(true)
		ui.kbFocus.focusedWidget = w
	}

	ui.screen.SetStyle(tcell.StyleDefault)
	ui.screen.EnableMouse()
	ui.screen.Clear()

	go func() {
		for {
			switch ev := ui.screen.PollEvent().(type) {
			case *tcell.EventKey:
				ui.handleKeyEvent(ev)
			case *tcell.EventMouse:
				ui.handleMouseEvent(ev)
			case *tcell.EventResize:
				ui.handleResizeEvent(ev)
			}
		}
	}()

	for {
		select {
		case <-ui.quit:
			return nil
		case ev := <-ui.eventQueue:
			ui.handleEvent(ev)
		}
	}
}

func (ui *tcellUI) handleEvent(ev event) {
	switch e := ev.(type) {
	case KeyEvent:
		logger.Printf("Received key event: %s", e.Name())

		for _, b := range ui.keybindings {
			if b.match(e) {
				b.handler()
			}
		}
		ui.kbFocus.OnKeyEvent(e)
		ui.root.OnKeyEvent(e)
		ui.painter.Repaint(ui.root)
	case callbackEvent:
		// Gets stuck in a print loop when the logger is a widget.
		//logger.Printf("Received callback event")
		e.cbFn()
		ui.painter.Repaint(ui.root)
	case paintEvent:
		logger.Printf("Received paint event")
		ui.painter.Repaint(ui.root)
	}
}

func (ui *tcellUI) handleKeyEvent(tev *tcell.EventKey) {
	ui.eventQueue <- KeyEvent{
		Key:       Key(tev.Key()),
		Rune:      tev.Rune(),
		Modifiers: ModMask(tev.Modifiers()),
	}
}

func (ui *tcellUI) handleMouseEvent(ev *tcell.EventMouse) {
	x, y := ev.Position()
	ui.eventQueue <- MouseEvent{Pos: image.Pt(x, y)}
}

func (ui *tcellUI) handleResizeEvent(ev *tcell.EventResize) {
	ui.eventQueue <- paintEvent{}
}

// Quit signals to the UI to start shutting down.
func (ui *tcellUI) Quit() {
	logger.Printf("Quitting")
	ui.screen.Fini()
	ui.quit <- struct{}{}
}

// Schedule an update of the UI, running the given
// function in the UI goroutine.
//
// Use this to update the UI in response to external events,
// like a timer tick.
// This method should be used any time you call methods
// to change UI objects after the first call to `UI.Run()`.
//
// Changes invoked outside of either this callback or the
// other event handler callbacks may appear to work, but
// is likely a race condition.  (Run your program with
// `go run -race` or `go install -race` to detect this!)
//
// Calling Update from within an event handler, or from within an Update call,
// is an error, and will deadlock.
func (ui *tcellUI) Update(fn func()) {
	blk := make(chan struct{})
	ui.eventQueue <- callbackEvent{func() {
		fn()
		close(blk)
	}}
	<-blk
}

var _ Surface = &tcellSurface{}

type tcellSurface struct {
	screen tcell.Screen
}

func (s *tcellSurface) SetCell(x, y int, ch rune, style Style) {
	st := tcell.StyleDefault.Normal().
		Foreground(convertColor(style.Fg, false)).
		Background(convertColor(style.Bg, false)).
		Reverse(style.Reverse == DecorationOn).
		Bold(style.Bold == DecorationOn).
		Underline(style.Underline == DecorationOn)

	s.screen.SetContent(x, y, ch, nil, st)
}

func (s *tcellSurface) SetCursor(x, y int) {
	s.screen.ShowCursor(x, y)
}

func (s *tcellSurface) HideCursor() {
	s.screen.HideCursor()
}

func (s *tcellSurface) Begin() {
	s.screen.Clear()
}

func (s *tcellSurface) End() {
	s.screen.Show()
}

func (s *tcellSurface) Size() image.Point {
	w, h := s.screen.Size()
	return image.Point{w, h}
}

func convertColor(col Color, fg bool) tcell.Color {
	switch col {
	case ColorDefault:
		if fg {
			return tcell.ColorWhite
		}
		return tcell.ColorDefault
	case ColorBlack:
		return tcell.ColorBlack
	case ColorWhite:
		return tcell.ColorWhite
	case ColorRed:
		return tcell.ColorRed
	case ColorGreen:
		return tcell.ColorGreen
	case ColorBlue:
		return tcell.ColorBlue
	case ColorCyan:
		return tcell.ColorDarkCyan
	case ColorMagenta:
		return tcell.ColorDarkMagenta
	case ColorYellow:
		return tcell.ColorYellow
	default:
		if col > 0 {
			return tcell.Color(col)
		}
		return tcell.ColorDefault
	}
}
<|endoftext|>


<commit_before>package genericsite

// Dismisses various attempts at accessing this site

import (
	"github.com/xyproto/web"
)

// Honeypot?
func ServeForFun() {
	// These appeared in the log
	bogus := []string{"/signup", "/wp-login.php", "/join.php", "/register.php", "/profile.php", "/user/register/", "/tools/quicklogin.one", "/sign_up.html", "/profile.php", "/ucp.php", "/account/register.php", "/join_form.php", "/tiki-register.php", "/YaBB.cgi/", "/YaBB.pl/", "/member/register", "/signup.php", "/blogs/load/recent", "/member/join.php"}
	for _, location := range bogus {
		web.Get(location, Hello)
	}

	bogusParam := []string{"/index.php", "/viewtopic.php"}
	for _, location := range bogusParam {
		web.Get(location, ParamExample)
	}
}
<commit_msg>Removed a file that wasn't needed<commit_after><|endoftext|>


<commit_before>/*
 Copyright 2016 Padduck, LLC

 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 server

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/itsjamie/gin-cors"
	"github.com/pufferpanel/pufferd/httphandlers"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/utils"
	"github.com/pkg/errors"
	"strings"
	"strconv"
)

var wsupgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func RegisterRoutes(e *gin.Engine) {
	l := e.Group("/server")
	{
		e.Handle("CONNECT", "/:id/console", func(c *gin.Context) {
			c.Header("Access-Control-Allow-Origin", "*")
			c.Header("Access-Control-Allow-Credentials", "false")
		})
		l.Use(httphandlers.OAuth2Handler)
		l.PUT("/:id", CreateServer)
		l.DELETE("/:id", DeleteServer)
		l.POST("/:id", EditServer)
		l.GET("/:id/start", StartServer)
		l.GET("/:id/stop", StopServer)
		l.POST("/:id/install", InstallServer)
		l.GET("/:id/file/*filename", GetFile)
		l.PUT("/:id/file/*filename", PutFile)
		l.DELETE("/:id/file/*filename", DeleteFile)
		l.POST("/:id/console", PostConsole)
		l.GET("/:id/stats", GetStats)
		l.POST("/:id/reload", ReloadServer)
		l.GET("/:id/console", cors.Middleware(cors.Config{
			Origins:     "*",
			Credentials: true,
		}), GetConsole)
		l.GET("/:id/logs", GetLogs)
	}
	e.GET("/network", httphandlers.OAuth2Handler, NetworkServer)
}

func StartServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.start", true)

	if !valid {
		c.Status(404)
		return
	}

	existing.Start()
}

func StopServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.stop", true)
	wait := c.Param("wait")
	if wait == "" || (wait != "true" && wait != "false") {
		wait = "true"
	}

	if !valid {
		return
	}

	err := existing.Stop()
	if err != nil {
		c.Error(err)
	}

	if wait == "true" {
		err = existing.GetEnvironment().WaitForMainProcess()
		if err != nil {
			c.Error(err)
		}
	}
}

func CreateServer(c *gin.Context) {
	serverId := c.Param("id")
	handleInitialCallServer(c, "server.create", false)

	existing := programs.GetFromCache(serverId)

	if existing != nil {
		c.AbortWithStatus(409)
		return
	}

	data := make(map[string]interface{}, 0)
	err := json.NewDecoder(c.Request.Body).Decode(&data)

	if err != nil {
		logging.Error("Error decoding JSON body", err)
		c.AbortWithError(400, err)
		return
	}

	serverType := data["type"].(string)

	if !programs.Create(serverId, serverType, data) {
		c.AbortWithStatus(500)
	}
}

func DeleteServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.delete", true)

	if !valid {
		return
	}

	programs.Delete(existing.Id())
	c.Status(204)
}

func InstallServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.install", true)

	if !valid {
		return
	}

	c.Status(200)
	go func() {
		existing.Install()
	}()
}

func EditServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.edit", true)

	if !valid {
		return
	}

	data := make(map[string]interface{}, 0)
	json.NewDecoder(c.Request.Body).Decode(&data)

	c.Status(200)
	existing.Edit(data)
}

func GetFile(c *gin.Context) {

	valid, server := handleInitialCallServer(c, "server.file.get", true)

	if !valid {
		return
	}

	targetPath := c.Param("filename")

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		return
	}

	info, err := os.Stat(targetFile)

	if os.IsNotExist(err) {
		c.Status(404)
		return
	}

	if info.IsDir() {
		type FileDesc struct {
			Name      string    `json:"name"`
			Modified  int64     `json:"modifyTime"`
			Size      int64     `json:"size,omitempty"`
			File      bool      `json:"isFile"`
			Extension string    `json:"extension,omitempty"`
		}

		files, _ := ioutil.ReadDir(targetFile)
		fileNames := make([]interface{}, 0)
		if targetPath != "" && targetPath != "." && targetPath != "/" {
			newFile := &FileDesc{
				Name:      "..",
				File:      false,
			}
			fileNames = append(fileNames, newFile)
		}
		for _, file := range files {
			newFile := &FileDesc{
				Name:      file.Name(),
				File:      !file.IsDir(),
			}

			if newFile.File {
				newFile.Size = file.Size()
				newFile.Modified = file.ModTime().Unix()
				newFile.Extension = filepath.Ext(file.Name())
			}

			fileNames = append(fileNames, newFile)
		}
		c.JSON(200, fileNames)
	} else {
		_, err := os.Open(targetFile)
		if err != nil {
			if err == os.ErrNotExist {
				c.AbortWithStatus(404)
			} else {
				c.AbortWithStatus(500)
			}
		}
		c.File(targetFile)
	}
}

func PutFile(c *gin.Context) {
	valid, server := handleInitialCallServer(c, "server.file.put", true)

	if !valid {
		return
	}

	targetPath := c.Param("filename")

	if targetPath == "" {
		c.Status(404)
		return
	}

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		return
	}

	file, err := os.Create(targetFile)

	if err != nil {
		logging.Error("Error writing file", err)
		return
	}

	_, err = io.Copy(file, c.Request.Body)

	if err != nil {
		logging.Error("Error writing file", err)
	}
}

func DeleteFile (c *gin.Context) {
	valid, server := handleInitialCallServer(c, "server.file.delete", true)

	if !valid {
		return
	}

	targetPath := c.Param("filename")

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		return
	}

	err := os.Remove(targetFile)
	if err != nil {
		c.Status(500)
		logging.Error("Failed to delete file", err)
	} else {
		c.Status(204)
	}
}

func PostConsole(c *gin.Context) {
	valid, program := handleInitialCallServer(c, "server.console.send", true)
	if !valid {
		return
	}
	d, _ := ioutil.ReadAll(c.Request.Body)
	cmd := string(d)
	err := program.Execute(cmd)
	if err != nil {
		c.Error(err)
	} else {
		c.Status(200)
	}
}

func GetConsole(c *gin.Context) {
	valid, program := handleInitialCallServer(c, "server.console", true)
	if !valid {
		return
	}
	conn, err := wsupgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		logging.Error("Error creating websocket", err)
		c.AbortWithError(500, err)
		return
	}
	console, _ := program.GetEnvironment().GetConsole()
	for _, v := range console {
		conn.WriteMessage(websocket.TextMessage, []byte(v))
	}
	program.GetEnvironment().AddListener(conn)
}

func GetStats(c *gin.Context) {
	valid, server := handleInitialCallServer(c, "server.stats", true)

	if !valid {
		return
	}

	results, err := server.GetEnvironment().GetStats()
	if err != nil {
		result := make(map[string]interface{})
		result["error"] = err.Error()
		c.JSON(200, result)
	} else {
		c.JSON(200, results)
	}
}

func ReloadServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.reload", true)

	if !valid {
		c.Status(404)
		return
	}

	programs.Reload(existing.Id())
}

func NetworkServer(c *gin.Context) {

	scopes, _ := c.Get("scopes")
	valid := false
	for _, v := range scopes.([]string) {
		if v == "server.network"{
			valid = true
		}
	}
	if !valid {
		c.AbortWithStatus(401)
		return
	}

	servers := c.DefaultQuery("ids", "")
	if servers == "" {
		c.AbortWithError(400, errors.New("Server ids required"))
		return
	}
	serverIds := strings.Split(servers, ",")
	result := make(map[string]string)
	for _, v := range serverIds {
		program, _ := programs.Get(v)
		if program == nil {
			continue
		}
		result[program.Id()] = program.GetNetwork()
	}
	c.JSON(200, result)
}

func GetLogs (c *gin.Context) {
	valid, program := handleInitialCallServer(c, "server.console", true)
	if !valid {
		return
	}

	time := c.DefaultQuery("time", "0")

	castedTime, ok := strconv.ParseInt(time, 10, 64)

	if ok != nil {
		c.AbortWithError(400, errors.New("Time provided is not a valid UNIX time"))
		return
	}

	console, epoch := program.GetEnvironment().GetConsoleFrom(castedTime)
	msg := ""
	for _, k := range console {
		msg += k + "\n"
	}
	result := make(map[string]interface{})
	result["epoch"] = epoch
	result["logs"] = msg;
	c.JSON(200, result)
}

func handleInitialCallServer(c *gin.Context, perm string, requireServer bool) (valid bool, program programs.Program) {
	valid = false

	serverId := c.Param("id")
	canAccessId, _ := c.Get("server_id")

	accessId := canAccessId.(string)

	if accessId != serverId && accessId != "*" {
		c.AbortWithStatus(401)
		return
	}

	if accessId == "*" {
		program, _ = programs.Get(serverId)
	} else {
		program, _ = programs.Get(accessId)
	}

	if requireServer && program == nil {
		c.AbortWithStatus(404)
		valid = false
		return
	}

	scopes, _ := c.Get("scopes")

	for _, v := range scopes.([]string) {
		if v == perm {
			valid = true
		}
	}

	valid = true

	return
}
<commit_msg>Use more appropriate HTTP codes<commit_after>/*
 Copyright 2016 Padduck, LLC

 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 server

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/itsjamie/gin-cors"
	"github.com/pufferpanel/pufferd/httphandlers"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/utils"
	"github.com/pkg/errors"
	"strings"
	"strconv"
)

var wsupgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func RegisterRoutes(e *gin.Engine) {
	l := e.Group("/server")
	{
		e.Handle("CONNECT", "/:id/console", func(c *gin.Context) {
			c.Header("Access-Control-Allow-Origin", "*")
			c.Header("Access-Control-Allow-Credentials", "false")
		})
		l.Use(httphandlers.OAuth2Handler)
		l.PUT("/:id", CreateServer)
		l.DELETE("/:id", DeleteServer)
		l.POST("/:id", EditServer)
		l.GET("/:id/start", StartServer)
		l.GET("/:id/stop", StopServer)
		l.POST("/:id/install", InstallServer)
		l.GET("/:id/file/*filename", GetFile)
		l.PUT("/:id/file/*filename", PutFile)
		l.DELETE("/:id/file/*filename", DeleteFile)
		l.POST("/:id/console", PostConsole)
		l.GET("/:id/stats", GetStats)
		l.POST("/:id/reload", ReloadServer)
		l.GET("/:id/console", cors.Middleware(cors.Config{
			Origins:     "*",
			Credentials: true,
		}), GetConsole)
		l.GET("/:id/logs", GetLogs)
	}
	e.GET("/network", httphandlers.OAuth2Handler, NetworkServer)
}

func StartServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.start", true)

	if !valid {
		c.Status(404)
		return
	}

	existing.Start()
}

func StopServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.stop", true)
	wait := c.Param("wait")
	if wait == "" || (wait != "true" && wait != "false") {
		wait = "true"
	}

	if !valid {
		c.Status(401)
		return
	}

	err := existing.Stop()
	if err != nil {
		c.Error(err)
	}

	if wait == "true" {
		err = existing.GetEnvironment().WaitForMainProcess()
		if err != nil {
			c.AbortWithError(500, err)
		}
	}
	c.Status(204)
}

func CreateServer(c *gin.Context) {
	serverId := c.Param("id")
	handleInitialCallServer(c, "server.create", false)

	existing := programs.GetFromCache(serverId)

	if existing != nil {
		c.AbortWithStatus(409)
		return
	}

	data := make(map[string]interface{}, 0)
	err := json.NewDecoder(c.Request.Body).Decode(&data)

	if err != nil {
		logging.Error("Error decoding JSON body", err)
		c.AbortWithError(400, err)
		return
	}

	serverType := data["type"].(string)

	if !programs.Create(serverId, serverType, data) {
		c.AbortWithStatus(500)
	}
}

func DeleteServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.delete", true)

	if !valid {
		return
	}

	programs.Delete(existing.Id())
	c.Status(204)
}

func InstallServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.install", true)

	if !valid {
		return
	}

	c.Status(204)
	go func() {
		existing.Install()
	}()
}

func EditServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.edit", true)

	if !valid {
		return
	}

	data := make(map[string]interface{}, 0)
	json.NewDecoder(c.Request.Body).Decode(&data)

	existing.Edit(data)
	c.Status(204)
}

func GetFile(c *gin.Context) {

	valid, server := handleInitialCallServer(c, "server.file.get", true)

	if !valid {
		return
	}

	targetPath := c.Param("filename")

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		return
	}

	info, err := os.Stat(targetFile)

	if os.IsNotExist(err) {
		c.Status(404)
		return
	}

	if info.IsDir() {
		type FileDesc struct {
			Name      string    `json:"name"`
			Modified  int64     `json:"modifyTime"`
			Size      int64     `json:"size,omitempty"`
			File      bool      `json:"isFile"`
			Extension string    `json:"extension,omitempty"`
		}

		files, _ := ioutil.ReadDir(targetFile)
		fileNames := make([]interface{}, 0)
		if targetPath != "" && targetPath != "." && targetPath != "/" {
			newFile := &FileDesc{
				Name:      "..",
				File:      false,
			}
			fileNames = append(fileNames, newFile)
		}
		for _, file := range files {
			newFile := &FileDesc{
				Name:      file.Name(),
				File:      !file.IsDir(),
			}

			if newFile.File {
				newFile.Size = file.Size()
				newFile.Modified = file.ModTime().Unix()
				newFile.Extension = filepath.Ext(file.Name())
			}

			fileNames = append(fileNames, newFile)
		}
		c.JSON(200, fileNames)
	} else {
		_, err := os.Open(targetFile)
		if err != nil {
			if err == os.ErrNotExist {
				c.AbortWithStatus(404)
			} else {
				c.AbortWithStatus(500)
			}
		}
		c.File(targetFile)
	}
}

func PutFile(c *gin.Context) {
	valid, server := handleInitialCallServer(c, "server.file.put", true)

	if !valid {
		return
	}

	targetPath := c.Param("filename")

	if targetPath == "" {
		c.Status(404)
		return
	}

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		c.Status(401)
		return
	}

	file, err := os.Create(targetFile)

	if err != nil {
		c.AbortWithError(500, err)
		logging.Error("Error writing file", err)
		return
	}

	c.Request.ParseMultipartForm(32 << 20)
	sourceFile, _, err := c.Request.FormFile("file")
	_, err = io.Copy(file, sourceFile)

	if err != nil {
		c.AbortWithError(500, err)
		logging.Error("Error writing file", err)
	} else {
		c.Status(204)
	}
}

func DeleteFile (c *gin.Context) {
	valid, server := handleInitialCallServer(c, "server.file.delete", true)

	if !valid {
		return
	}

	targetPath := c.Param("filename")

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		return
	}

	err := os.Remove(targetFile)
	if err != nil {
		c.Status(500)
		logging.Error("Failed to delete file", err)
	} else {
		c.Status(204)
	}
}

func PostConsole(c *gin.Context) {
	valid, program := handleInitialCallServer(c, "server.console.send", true)
	if !valid {
		return
	}
	d, _ := ioutil.ReadAll(c.Request.Body)
	cmd := string(d)
	err := program.Execute(cmd)
	if err != nil {
		c.AbortWithError(500, err)
	} else {
		c.Status(200)
	}
}

func GetConsole(c *gin.Context) {
	valid, program := handleInitialCallServer(c, "server.console", true)
	if !valid {
		return
	}
	conn, err := wsupgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		logging.Error("Error creating websocket", err)
		c.AbortWithError(500, err)
		return
	}
	console, _ := program.GetEnvironment().GetConsole()
	for _, v := range console {
		conn.WriteMessage(websocket.TextMessage, []byte(v))
	}
	program.GetEnvironment().AddListener(conn)
}

func GetStats(c *gin.Context) {
	valid, server := handleInitialCallServer(c, "server.stats", true)

	if !valid {
		return
	}

	results, err := server.GetEnvironment().GetStats()
	if err != nil {
		result := make(map[string]interface{})
		result["error"] = err.Error()
		c.JSON(200, result)
	} else {
		c.JSON(200, results)
	}
}

func ReloadServer(c *gin.Context) {
	valid, existing := handleInitialCallServer(c, "server.reload", true)

	if !valid {
		c.Status(404)
		return
	}

	err := programs.Reload(existing.Id())
	if err != nil {
		c.AbortWithError(500, err)
		return
	}
	c.Status(204)
}

func NetworkServer(c *gin.Context) {

	scopes, _ := c.Get("scopes")
	valid := false
	for _, v := range scopes.([]string) {
		if v == "server.network"{
			valid = true
		}
	}
	if !valid {
		c.AbortWithStatus(401)
		return
	}

	servers := c.DefaultQuery("ids", "")
	if servers == "" {
		c.AbortWithError(400, errors.New("Server ids required"))
		return
	}
	serverIds := strings.Split(servers, ",")
	result := make(map[string]string)
	for _, v := range serverIds {
		program, _ := programs.Get(v)
		if program == nil {
			continue
		}
		result[program.Id()] = program.GetNetwork()
	}
	c.JSON(200, result)
}

func GetLogs (c *gin.Context) {
	valid, program := handleInitialCallServer(c, "server.console", true)
	if !valid {
		return
	}

	time := c.DefaultQuery("time", "0")

	castedTime, ok := strconv.ParseInt(time, 10, 64)

	if ok != nil {
		c.AbortWithError(400, errors.New("Time provided is not a valid UNIX time"))
		return
	}

	console, epoch := program.GetEnvironment().GetConsoleFrom(castedTime)
	msg := ""
	for _, k := range console {
		msg += k + "\n"
	}
	result := make(map[string]interface{})
	result["epoch"] = epoch
	result["logs"] = msg;
	c.JSON(200, result)
}

func handleInitialCallServer(c *gin.Context, perm string, requireServer bool) (valid bool, program programs.Program) {
	valid = false

	serverId := c.Param("id")
	canAccessId, _ := c.Get("server_id")

	accessId := canAccessId.(string)

	if accessId != serverId && accessId != "*" {
		c.AbortWithStatus(401)
		return
	}

	if accessId == "*" {
		program, _ = programs.Get(serverId)
	} else {
		program, _ = programs.Get(accessId)
	}

	if requireServer && program == nil {
		c.AbortWithStatus(404)
		valid = false
		return
	}

	scopes, _ := c.Get("scopes")

	for _, v := range scopes.([]string) {
		if v == perm {
			valid = true
		}
	}

	valid = true

	return
}
<|endoftext|>