<commit_before>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if f.Relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if key := submodel.FieldByName("Id"); !key.IsValid() || key.Uint() == 0 {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Related(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<commit_msg>Allow set time to empty<commit_after>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.Resource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	return meta.Permission.HasPermission(mode, context.Roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.Resource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.Resource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if f.Relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.Resource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							} else {
								field.Set(reflect.Zero(field.Type()))
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if key := submodel.FieldByName("Id"); !key.IsValid() || key.Uint() == 0 {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Related(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cmd

import (
	"archive/zip"
	"fmt"
	"go/build"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

const (
	javacTargetVer = "1.7"
	minAndroidAPI  = 15
)

const manifestHeader = `Manifest-Version: 1.0
Created-By: 1.0 (Go)

`

func NDKRoot() (string, error) {
	sdkHome := os.Getenv("ANDROID_HOME")
	if sdkHome == "" {
		return "", fmt.Errorf("$ANDROID_HOME does not point to an Android NDK. $ANDROID_HOME is unset.")
	}

	path, err := filepath.Abs(filepath.Join(sdkHome, "ndk-bundle"))
	if err != nil {
		return "", fmt.Errorf("$ANDROID_HOME does not point to an Android NDK. Error cleaning path %v.", err)
	}
	return path, nil
}

type ndkToolchain struct {
	arch       string
	abi        string
	platform   string
	gcc        string
	toolPrefix string
}

func (tc *ndkToolchain) Path(ndkRoot string, toolName string) string {
	// The nm tool is located in the GCC directory structure.
	isUtil := toolName == "nm"
	if runtime.GOOS == "windows" {
		toolName += ".exe"
	}
	path := filepath.Join(ndkRoot, "toolchains")
	if isUtil {
		toolName = tc.toolPrefix + "-" + toolName
		path = filepath.Join(path, tc.gcc)
	} else {
		path = filepath.Join(path, "llvm")
	}
	path = filepath.Join(path, "prebuilt")
	return filepath.Join(path, archNDK(), "bin", toolName)
}

type ndkConfig map[string]ndkToolchain // map: GOOS->androidConfig.

func GetAndroidABI(arch string) string {
	switch arch {
	case "arm":
		return "armeabi-v7a"
	case "arm64":
		return "arm64-v8a"
	case "386":
		return "x86"
	case "amd64":
		return "x86_64"
	}
	return ""
}

func GetAndroidEnv(gomobpath string) (map[string][]string, error) {
	ndkRoot, err := NDKRoot()
	if err != nil {
		return nil, err
	}

	var ndk_ = ndkConfig{
		"arm": {
			arch:       "arm",
			platform:   "android-15",
			gcc:        "arm-linux-androideabi-4.9",
			toolPrefix: "arm-linux-androideabi",
		},
		"arm64": {
			arch:       "arm64",
			platform:   "android-21",
			gcc:        "aarch64-linux-android-4.9",
			toolPrefix: "aarch64-linux-android",
		},

		"386": {
			arch:       "x86",
			platform:   "android-15",
			gcc:        "x86-4.9",
			toolPrefix: "i686-linux-android",
		},
		"amd64": {
			arch:       "x86_64",
			platform:   "android-21",
			gcc:        "x86_64-4.9",
			toolPrefix: "x86_64-linux-android",
		},
	}

	androidENV := make(map[string][]string)
	for arch, toolchain := range ndk_ {
		// Emulate the flags in the clang wrapper scripts generated
		// by make_standalone_toolchain.py
		s := strings.SplitN(toolchain.toolPrefix, "-", 3)
		a, os, env := s[0], s[1], s[2]
		if a == "arm" {
			a = "armv7a"
		}
		target := strings.Join([]string{a, "none", os, env}, "-")
		sysroot := filepath.Join(ndkRoot, "platforms", toolchain.platform, "arch-"+toolchain.arch)
		gcctoolchain := filepath.Join(ndkRoot, "toolchains", toolchain.gcc, "prebuilt", archNDK())
		flags := fmt.Sprintf("-target %s --sysroot %s -gcc-toolchain %s", target, sysroot, gcctoolchain)
		cflags := fmt.Sprintf("%s -I%s/include", flags, gomobpath)
		ldflags := fmt.Sprintf("%s -L%s/usr/lib -L%s/lib/%s", flags, sysroot, gomobpath, arch)
		androidENV[arch] = []string{
			"GOOS=android",
			"GOARCH=" + arch,
			"CC=" + toolchain.Path(ndkRoot, "clang"),
			"CXX=" + toolchain.Path(ndkRoot, "clang++"),
			"CGO_CFLAGS=" + cflags,
			"CGO_CPPFLAGS=" + cflags,
			"CGO_LDFLAGS=" + ldflags,
			"CGO_ENABLED=1",
		}
		if arch == "arm" {
			androidENV[arch] = append(androidENV[arch], "GOARM=7")
		}
	}
	return androidENV, nil
}

func archNDK() string {
	if runtime.GOOS == "windows" && runtime.GOARCH == "386" {
		return "windows"
	} else {
		var arch string
		switch runtime.GOARCH {
		case "386":
			arch = "x86"
		case "amd64":
			arch = "x86_64"
		default:
			panic("unsupported GOARCH: " + runtime.GOARCH)
		}
		return runtime.GOOS + "-" + arch
	}
}

// androidAPIPath returns an android SDK platform directory under ANDROID_HOME.
// If there are multiple platforms that satisfy the minimum version requirement
// androidAPIPath returns the latest one among them.
func AndroidAPIPath() (string, error) {
	sdk := os.Getenv("ANDROID_HOME")
	if sdk == "" {
		return "", fmt.Errorf("ANDROID_HOME environment var is not set")
	}
	sdkDir, err := os.Open(filepath.Join(sdk, "platforms"))
	if err != nil {
		return "", fmt.Errorf("failed to find android SDK platform: %v", err)
	}
	defer sdkDir.Close()
	fis, err := sdkDir.Readdir(-1)
	if err != nil {
		return "", fmt.Errorf("failed to find android SDK platform (min API level: %d): %v", minAndroidAPI, err)
	}

	var apiPath string
	var apiVer int
	for _, fi := range fis {
		name := fi.Name()
		if !fi.IsDir() || !strings.HasPrefix(name, "android-") {
			continue
		}
		n, err := strconv.Atoi(name[len("android-"):])
		if err != nil || n < minAndroidAPI {
			continue
		}
		p := filepath.Join(sdkDir.Name(), name)
		_, err = os.Stat(filepath.Join(p, "android.jar"))
		if err == nil && apiVer < n {
			apiPath = p
			apiVer = n
		}
	}
	if apiVer == 0 {
		return "", fmt.Errorf("failed to find android SDK platform (min API level: %d) in %s",
			minAndroidAPI, sdkDir.Name())
	}
	return apiPath, nil
}

// AAR is the format for the binary distribution of an Android Library Project
// and it is a ZIP archive with extension .aar.
// http://tools.android.com/tech-docs/new-build-system/aar-format
//
// These entries are directly at the root of the archive.
//
//  AndroidManifest.xml (mandatory)
//  classes.jar (mandatory)
//  assets/ (optional)
//  jni/<abi>/libgojni.so
//  R.txt (mandatory)
//  res/ (mandatory)
//  libs/*.jar (optional, not relevant)
//  proguard.txt (optional)
//  lint.jar (optional, not relevant)
//  aidl (optional, not relevant)
//
// javac and jar commands are needed to build classes.jar.
func BuildAAR(flags *Flags, androidDir string, pkgs []*build.Package, androidArchs []string, tmpdir string, aarPath string) (err error) {
	var out io.Writer = ioutil.Discard
	if !flags.BuildN {
		f, err := os.Create(aarPath)
		if err != nil {
			return err
		}
		defer func() {
			if cerr := f.Close(); err == nil {
				err = cerr
			}
		}()
		out = f
	}

	aarw := zip.NewWriter(out)
	aarwcreate := func(name string) (io.Writer, error) {
		if flags.BuildV {
			fmt.Fprintf(os.Stderr, "aar: %s\n", name)
		}
		return aarw.Create(name)
	}
	w, err := aarwcreate("AndroidManifest.xml")
	if err != nil {
		return err
	}
	const manifestFmt = `<manifest xmlns:android="http://schemas.android.com/apk/res/android" package=%q>
<uses-sdk android:minSdkVersion="%d"/></manifest>`
	fmt.Fprintf(w, manifestFmt, "go."+pkgs[0].Name+".gojni", minAndroidAPI)

	w, err = aarwcreate("proguard.txt")
	if err != nil {
		return err
	}
	fmt.Fprintln(w, `-keep class go.** { *; }`)

	w, err = aarwcreate("classes.jar")
	if err != nil {
		return err
	}
	src := filepath.Join(androidDir, "src/main/java")
	if err := BuildJar(flags, w, src, tmpdir); err != nil {
		return err
	}

	files := map[string]string{}
	for _, pkg := range pkgs {
		assetsDir := filepath.Join(pkg.Dir, "assets")
		assetsDirExists := false
		if fi, err := os.Stat(assetsDir); err == nil {
			assetsDirExists = fi.IsDir()
		} else if !os.IsNotExist(err) {
			return err
		}

		if assetsDirExists {
			err := filepath.Walk(
				assetsDir, func(path string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if info.IsDir() {
						return nil
					}
					f, err := os.Open(path)
					if err != nil {
						return err
					}
					defer f.Close()
					name := "assets/" + path[len(assetsDir)+1:]
					if orig, exists := files[name]; exists {
						return fmt.Errorf("package %s asset name conflict: %s already added from package %s",
							pkg.ImportPath, name, orig)
					}
					files[name] = pkg.ImportPath
					w, err := aarwcreate(name)
					if err != nil {
						return nil
					}
					_, err = io.Copy(w, f)
					return err
				})
			if err != nil {
				return err
			}
		}
	}

	for _, arch := range androidArchs {
		lib := GetAndroidABI(arch) + "/libgojni.so"
		w, err = aarwcreate("jni/" + lib)
		if err != nil {
			return err
		}
		if !flags.BuildN {
			r, err := os.Open(filepath.Join(androidDir, "src/main/jniLibs/"+lib))
			if err != nil {
				return err
			}
			defer r.Close()
			if _, err := io.Copy(w, r); err != nil {
				return err
			}
		}
	}

	// TODO(hyangah): do we need to use aapt to create R.txt?
	w, err = aarwcreate("R.txt")
	if err != nil {
		return err
	}

	w, err = aarwcreate("res/")
	if err != nil {
		return err
	}

	return aarw.Close()
}

func BuildJar(flags *Flags, w io.Writer, srcDir string, tmpdir string) error {
	bindClasspath := ""

	var srcFiles []string
	if flags.BuildN {
		srcFiles = []string{"*.java"}
	} else {
		err := filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if filepath.Ext(path) == ".java" {
				srcFiles = append(srcFiles, filepath.Join(".", path[len(srcDir):]))
			}
			return nil
		})
		if err != nil {
			return err
		}
	}

	dst := filepath.Join(tmpdir, "javac-output")
	if !flags.BuildN {
		if err := os.MkdirAll(dst, 0700); err != nil {
			return err
		}
	}

	bClspath, err := bootClasspath()

	if err != nil {
		return err
	}

	args := []string{
		"-d", dst,
		"-source", javacTargetVer,
		"-target", javacTargetVer,
		"-bootclasspath", bClspath,
	}
	if bindClasspath != "" {
		args = append(args, "-classpath", bindClasspath)
	}

	args = append(args, srcFiles...)

	javac := exec.Command("javac", args...)
	javac.Dir = srcDir
	if err := RunCmd(&Flags{}, tmpdir, javac); err != nil {
		return err
	}

	// fmt.Println("javac", args)
	// if buildX {
	// KD: printcmd("jar c -C %s .", dst)
	// }
	if flags.BuildN {
		return nil
	}
	jarw := zip.NewWriter(w)
	jarwcreate := func(name string) (io.Writer, error) {
		if flags.BuildV {
			fmt.Fprintf(os.Stderr, "jar: %s\n", name)
		}
		return jarw.Create(name)
	}
	f, err := jarwcreate("META-INF/MANIFEST.MF")
	if err != nil {
		return err
	}
	fmt.Fprintf(f, manifestHeader)

	err = filepath.Walk(dst, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		out, err := jarwcreate(filepath.ToSlash(path[len(dst)+1:]))
		if err != nil {
			return err
		}
		in, err := os.Open(path)
		if err != nil {
			return err
		}
		defer in.Close()
		_, err = io.Copy(out, in)
		return err
	})
	if err != nil {
		return err
	}
	return jarw.Close()
}

func bootClasspath() (string, error) {
	// bindBootClasspath := "" // KD: command parameter
	// if bindBootClasspath != "" {
	// 	return bindBootClasspath, nil
	// }
	apiPath, err := AndroidAPIPath()
	if err != nil {
		return "", err
	}
	return filepath.Join(apiPath, "android.jar"), nil
}
<commit_msg>Remove use of gomobilepath by GetAndroidEnv. vestigal<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cmd

import (
	"archive/zip"
	"fmt"
	"go/build"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

const (
	javacTargetVer = "1.7"
	minAndroidAPI  = 15
)

const manifestHeader = `Manifest-Version: 1.0
Created-By: 1.0 (Go)

`

func NDKRoot() (string, error) {
	sdkHome := os.Getenv("ANDROID_HOME")
	if sdkHome == "" {
		return "", fmt.Errorf("$ANDROID_HOME does not point to an Android NDK. $ANDROID_HOME is unset.")
	}

	path, err := filepath.Abs(filepath.Join(sdkHome, "ndk-bundle"))
	if err != nil {
		return "", fmt.Errorf("$ANDROID_HOME does not point to an Android NDK. Error cleaning path %v.", err)
	}
	return path, nil
}

type ndkToolchain struct {
	arch       string
	abi        string
	platform   string
	gcc        string
	toolPrefix string
}

func (tc *ndkToolchain) Path(ndkRoot string, toolName string) string {
	// The nm tool is located in the GCC directory structure.
	isUtil := toolName == "nm"
	if runtime.GOOS == "windows" {
		toolName += ".exe"
	}
	path := filepath.Join(ndkRoot, "toolchains")
	if isUtil {
		toolName = tc.toolPrefix + "-" + toolName
		path = filepath.Join(path, tc.gcc)
	} else {
		path = filepath.Join(path, "llvm")
	}
	path = filepath.Join(path, "prebuilt")
	return filepath.Join(path, archNDK(), "bin", toolName)
}

type ndkConfig map[string]ndkToolchain // map: GOOS->androidConfig.

func GetAndroidABI(arch string) string {
	switch arch {
	case "arm":
		return "armeabi-v7a"
	case "arm64":
		return "arm64-v8a"
	case "386":
		return "x86"
	case "amd64":
		return "x86_64"
	}
	return ""
}

func GetAndroidEnv(gomobpath string) (map[string][]string, error) {
	ndkRoot, err := NDKRoot()
	if err != nil {
		return nil, err
	}

	var ndk_ = ndkConfig{
		"arm": {
			arch:       "arm",
			platform:   "android-15",
			gcc:        "arm-linux-androideabi-4.9",
			toolPrefix: "arm-linux-androideabi",
		},
		"arm64": {
			arch:       "arm64",
			platform:   "android-21",
			gcc:        "aarch64-linux-android-4.9",
			toolPrefix: "aarch64-linux-android",
		},

		"386": {
			arch:       "x86",
			platform:   "android-15",
			gcc:        "x86-4.9",
			toolPrefix: "i686-linux-android",
		},
		"amd64": {
			arch:       "x86_64",
			platform:   "android-21",
			gcc:        "x86_64-4.9",
			toolPrefix: "x86_64-linux-android",
		},
	}

	androidENV := make(map[string][]string)
	for arch, toolchain := range ndk_ {
		// Emulate the flags in the clang wrapper scripts generated
		// by make_standalone_toolchain.py
		s := strings.SplitN(toolchain.toolPrefix, "-", 3)
		a, os, env := s[0], s[1], s[2]
		if a == "arm" {
			a = "armv7a"
		}
		target := strings.Join([]string{a, "none", os, env}, "-")
		sysroot := filepath.Join(ndkRoot, "platforms", toolchain.platform, "arch-"+toolchain.arch)
		gcctoolchain := filepath.Join(ndkRoot, "toolchains", toolchain.gcc, "prebuilt", archNDK())
		flags := fmt.Sprintf("-target %s --sysroot %s -gcc-toolchain %s", target, sysroot, gcctoolchain)
		cflags := fmt.Sprintf("%s", flags)
		ldflags := fmt.Sprintf("%s -L%s/usr/lib", flags, sysroot)
		androidENV[arch] = []string{
			"GOOS=android",
			"GOARCH=" + arch,
			"CC=" + toolchain.Path(ndkRoot, "clang"),
			"CXX=" + toolchain.Path(ndkRoot, "clang++"),
			"CGO_CFLAGS=" + cflags,
			"CGO_CPPFLAGS=" + cflags,
			"CGO_LDFLAGS=" + ldflags,
			"CGO_ENABLED=1",
		}
		if arch == "arm" {
			androidENV[arch] = append(androidENV[arch], "GOARM=7")
		}
	}
	return androidENV, nil
}

func archNDK() string {
	if runtime.GOOS == "windows" && runtime.GOARCH == "386" {
		return "windows"
	} else {
		var arch string
		switch runtime.GOARCH {
		case "386":
			arch = "x86"
		case "amd64":
			arch = "x86_64"
		default:
			panic("unsupported GOARCH: " + runtime.GOARCH)
		}
		return runtime.GOOS + "-" + arch
	}
}

// androidAPIPath returns an android SDK platform directory under ANDROID_HOME.
// If there are multiple platforms that satisfy the minimum version requirement
// androidAPIPath returns the latest one among them.
func AndroidAPIPath() (string, error) {
	sdk := os.Getenv("ANDROID_HOME")
	if sdk == "" {
		return "", fmt.Errorf("ANDROID_HOME environment var is not set")
	}
	sdkDir, err := os.Open(filepath.Join(sdk, "platforms"))
	if err != nil {
		return "", fmt.Errorf("failed to find android SDK platform: %v", err)
	}
	defer sdkDir.Close()
	fis, err := sdkDir.Readdir(-1)
	if err != nil {
		return "", fmt.Errorf("failed to find android SDK platform (min API level: %d): %v", minAndroidAPI, err)
	}

	var apiPath string
	var apiVer int
	for _, fi := range fis {
		name := fi.Name()
		if !fi.IsDir() || !strings.HasPrefix(name, "android-") {
			continue
		}
		n, err := strconv.Atoi(name[len("android-"):])
		if err != nil || n < minAndroidAPI {
			continue
		}
		p := filepath.Join(sdkDir.Name(), name)
		_, err = os.Stat(filepath.Join(p, "android.jar"))
		if err == nil && apiVer < n {
			apiPath = p
			apiVer = n
		}
	}
	if apiVer == 0 {
		return "", fmt.Errorf("failed to find android SDK platform (min API level: %d) in %s",
			minAndroidAPI, sdkDir.Name())
	}
	return apiPath, nil
}

// AAR is the format for the binary distribution of an Android Library Project
// and it is a ZIP archive with extension .aar.
// http://tools.android.com/tech-docs/new-build-system/aar-format
//
// These entries are directly at the root of the archive.
//
//  AndroidManifest.xml (mandatory)
//  classes.jar (mandatory)
//  assets/ (optional)
//  jni/<abi>/libgojni.so
//  R.txt (mandatory)
//  res/ (mandatory)
//  libs/*.jar (optional, not relevant)
//  proguard.txt (optional)
//  lint.jar (optional, not relevant)
//  aidl (optional, not relevant)
//
// javac and jar commands are needed to build classes.jar.
func BuildAAR(flags *Flags, androidDir string, pkgs []*build.Package, androidArchs []string, tmpdir string, aarPath string) (err error) {
	var out io.Writer = ioutil.Discard
	if !flags.BuildN {
		f, err := os.Create(aarPath)
		if err != nil {
			return err
		}
		defer func() {
			if cerr := f.Close(); err == nil {
				err = cerr
			}
		}()
		out = f
	}

	aarw := zip.NewWriter(out)
	aarwcreate := func(name string) (io.Writer, error) {
		if flags.BuildV {
			fmt.Fprintf(os.Stderr, "aar: %s\n", name)
		}
		return aarw.Create(name)
	}
	w, err := aarwcreate("AndroidManifest.xml")
	if err != nil {
		return err
	}
	const manifestFmt = `<manifest xmlns:android="http://schemas.android.com/apk/res/android" package=%q>
<uses-sdk android:minSdkVersion="%d"/></manifest>`
	fmt.Fprintf(w, manifestFmt, "go."+pkgs[0].Name+".gojni", minAndroidAPI)

	w, err = aarwcreate("proguard.txt")
	if err != nil {
		return err
	}
	fmt.Fprintln(w, `-keep class go.** { *; }`)

	w, err = aarwcreate("classes.jar")
	if err != nil {
		return err
	}
	src := filepath.Join(androidDir, "src/main/java")
	if err := BuildJar(flags, w, src, tmpdir); err != nil {
		return err
	}

	files := map[string]string{}
	for _, pkg := range pkgs {
		assetsDir := filepath.Join(pkg.Dir, "assets")
		assetsDirExists := false
		if fi, err := os.Stat(assetsDir); err == nil {
			assetsDirExists = fi.IsDir()
		} else if !os.IsNotExist(err) {
			return err
		}

		if assetsDirExists {
			err := filepath.Walk(
				assetsDir, func(path string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if info.IsDir() {
						return nil
					}
					f, err := os.Open(path)
					if err != nil {
						return err
					}
					defer f.Close()
					name := "assets/" + path[len(assetsDir)+1:]
					if orig, exists := files[name]; exists {
						return fmt.Errorf("package %s asset name conflict: %s already added from package %s",
							pkg.ImportPath, name, orig)
					}
					files[name] = pkg.ImportPath
					w, err := aarwcreate(name)
					if err != nil {
						return nil
					}
					_, err = io.Copy(w, f)
					return err
				})
			if err != nil {
				return err
			}
		}
	}

	for _, arch := range androidArchs {
		lib := GetAndroidABI(arch) + "/libgojni.so"
		w, err = aarwcreate("jni/" + lib)
		if err != nil {
			return err
		}
		if !flags.BuildN {
			r, err := os.Open(filepath.Join(androidDir, "src/main/jniLibs/"+lib))
			if err != nil {
				return err
			}
			defer r.Close()
			if _, err := io.Copy(w, r); err != nil {
				return err
			}
		}
	}

	// TODO(hyangah): do we need to use aapt to create R.txt?
	w, err = aarwcreate("R.txt")
	if err != nil {
		return err
	}

	w, err = aarwcreate("res/")
	if err != nil {
		return err
	}

	return aarw.Close()
}

func BuildJar(flags *Flags, w io.Writer, srcDir string, tmpdir string) error {
	bindClasspath := ""

	var srcFiles []string
	if flags.BuildN {
		srcFiles = []string{"*.java"}
	} else {
		err := filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if filepath.Ext(path) == ".java" {
				srcFiles = append(srcFiles, filepath.Join(".", path[len(srcDir):]))
			}
			return nil
		})
		if err != nil {
			return err
		}
	}

	dst := filepath.Join(tmpdir, "javac-output")
	if !flags.BuildN {
		if err := os.MkdirAll(dst, 0700); err != nil {
			return err
		}
	}

	bClspath, err := bootClasspath()

	if err != nil {
		return err
	}

	args := []string{
		"-d", dst,
		"-source", javacTargetVer,
		"-target", javacTargetVer,
		"-bootclasspath", bClspath,
	}
	if bindClasspath != "" {
		args = append(args, "-classpath", bindClasspath)
	}

	args = append(args, srcFiles...)

	javac := exec.Command("javac", args...)
	javac.Dir = srcDir
	if err := RunCmd(&Flags{}, tmpdir, javac); err != nil {
		return err
	}

	// fmt.Println("javac", args)
	// if buildX {
	// KD: printcmd("jar c -C %s .", dst)
	// }
	if flags.BuildN {
		return nil
	}
	jarw := zip.NewWriter(w)
	jarwcreate := func(name string) (io.Writer, error) {
		if flags.BuildV {
			fmt.Fprintf(os.Stderr, "jar: %s\n", name)
		}
		return jarw.Create(name)
	}
	f, err := jarwcreate("META-INF/MANIFEST.MF")
	if err != nil {
		return err
	}
	fmt.Fprintf(f, manifestHeader)

	err = filepath.Walk(dst, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		out, err := jarwcreate(filepath.ToSlash(path[len(dst)+1:]))
		if err != nil {
			return err
		}
		in, err := os.Open(path)
		if err != nil {
			return err
		}
		defer in.Close()
		_, err = io.Copy(out, in)
		return err
	})
	if err != nil {
		return err
	}
	return jarw.Close()
}

func bootClasspath() (string, error) {
	// bindBootClasspath := "" // KD: command parameter
	// if bindBootClasspath != "" {
	// 	return bindBootClasspath, nil
	// }
	apiPath, err := AndroidAPIPath()
	if err != nil {
		return "", err
	}
	return filepath.Join(apiPath, "android.jar"), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/garyburd/redigo/redis"
	"github.com/getbread/redistore"
	"github.com/gorilla/sessions"
	"github.com/mikoim/steam-jp-finder"
	"github.com/unrolled/render"
	"github.com/yohcop/openid-go"
)

const (
	openidURL   = "https://steamcommunity.com/openid"
	sessionName = "louise"
)

type app struct {
	pool           *redis.Pool
	rdr            *render.Render
	session        sessions.Store
	nonceStore     openid.NonceStore
	discoveryCache openid.DiscoveryCache
}

func newApp(pool *redis.Pool, keyPairs ...[]byte) (*app, error) {
	session, err := redistore.NewRediStoreWithPool(pool, keyPairs...)
	session.Options.HttpOnly = true
	if err != nil {
		return nil, err
	}

	return &app{
		pool:           pool,
		rdr:            render.New(),
		session:        session,
		nonceStore:     openid.NewSimpleNonceStore(),
		discoveryCache: openid.NewSimpleDiscoveryCache(),
	}, nil
}

func (s *app) indexHandler(w http.ResponseWriter, r *http.Request) {
	session, _ := s.session.Get(r, sessionName)
	s.rdr.Text(w, http.StatusOK, fmt.Sprintln(session.Values["SteamID"]))
}

func (s *app) loginHandler(w http.ResponseWriter, r *http.Request) {
	if url, err := openid.RedirectURL(openidURL, sjf.RootURI(r)+"/login/callback", sjf.RootURI(r)); err == nil {
		http.Redirect(w, r, url, http.StatusSeeOther)
	} else {
		logrus.Error(err)
	}
}

func (s *app) loginCallbackHandler(w http.ResponseWriter, r *http.Request) {
	id, err := openid.Verify(sjf.URI(r), s.discoveryCache, s.nonceStore)
	if err != nil {
		logrus.Error(err)
		http.Redirect(w, r, sjf.RootURI(r)+"/?error=Login failed", http.StatusSeeOther)
		return
	}

	steamID, err := sjf.SteamID(id)
	if err != nil {
		logrus.Error(err)
		http.Redirect(w, r, sjf.RootURI(r)+"/?error=Invalid Steam ID", http.StatusSeeOther)
		return
	}

	session, _ := s.session.Get(r, sessionName)
	session.Values["SteamID"] = steamID
	session.Save(r, w)

	http.Redirect(w, r, sjf.RootURI(r), http.StatusSeeOther)
}

func (s *app) logoutHandler(w http.ResponseWriter, r *http.Request) {
	session, _ := s.session.Get(r, sessionName)
	session.Options.MaxAge = -1
	session.Save(r, w)

	http.Redirect(w, r, sjf.RootURI(r), http.StatusSeeOther)
}
<commit_msg>add error check in session<commit_after>package main

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/garyburd/redigo/redis"
	"github.com/getbread/redistore"
	"github.com/gorilla/sessions"
	"github.com/mikoim/steam-jp-finder"
	"github.com/unrolled/render"
	"github.com/yohcop/openid-go"
)

const (
	openidURL   = "https://steamcommunity.com/openid"
	sessionName = "louise"
)

type app struct {
	pool           *redis.Pool
	rdr            *render.Render
	session        sessions.Store
	nonceStore     openid.NonceStore
	discoveryCache openid.DiscoveryCache
}

func newApp(pool *redis.Pool, keyPairs ...[]byte) (*app, error) {
	session, err := redistore.NewRediStoreWithPool(pool, keyPairs...)
	session.Options.HttpOnly = true
	if err != nil {
		return nil, err
	}

	return &app{
		pool:           pool,
		rdr:            render.New(),
		session:        session,
		nonceStore:     openid.NewSimpleNonceStore(),
		discoveryCache: openid.NewSimpleDiscoveryCache(),
	}, nil
}

func (s *app) indexHandler(w http.ResponseWriter, r *http.Request) {
	session, err := s.session.Get(r, sessionName)
	if err != nil {
		logrus.Error(err)
		return
	}

	text := ""
	steamID := session.Values["SteamID"]
	if steamID != nil {
		text = steamID.(string)
	}

	s.rdr.Text(w, http.StatusOK, fmt.Sprintln(text))
}

func (s *app) loginHandler(w http.ResponseWriter, r *http.Request) {
	if url, err := openid.RedirectURL(openidURL, sjf.RootURI(r)+"/login/callback", sjf.RootURI(r)); err == nil {
		http.Redirect(w, r, url, http.StatusSeeOther)
	} else {
		logrus.Error(err)
	}
}

func (s *app) loginCallbackHandler(w http.ResponseWriter, r *http.Request) {
	id, err := openid.Verify(sjf.URI(r), s.discoveryCache, s.nonceStore)
	if err != nil {
		logrus.Error(err)
		http.Redirect(w, r, sjf.RootURI(r)+"/?error=Login failed", http.StatusSeeOther)
		return
	}

	steamID, err := sjf.SteamID(id)
	if err != nil {
		logrus.Error(err)
		http.Redirect(w, r, sjf.RootURI(r)+"/?error=Invalid Steam ID", http.StatusSeeOther)
		return
	}

	session, _ := s.session.Get(r, sessionName)
	if err != nil {
		logrus.Error(err)
		return
	}

	session.Values["SteamID"] = steamID
	session.Save(r, w)

	http.Redirect(w, r, sjf.RootURI(r), http.StatusSeeOther)
}

func (s *app) logoutHandler(w http.ResponseWriter, r *http.Request) {
	session, _ := s.session.Get(r, sessionName)
	session.Options.MaxAge = -1
	session.Save(r, w)

	http.Redirect(w, r, sjf.RootURI(r), http.StatusSeeOther)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

var (
	Version   = "N/A"
	BuildTime = "N/A"
)

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Version information",
	Long:  `Various version details`,
	RunE: func(cmd *cobra.Command, args []string) error {
		printVersion()
		return nil
	},
}

func printVersion() {
	fmt.Fprintf(os.Stdout, "Version %s built on %s\n", Version, BuildTime)
}
<commit_msg>Added comments for exported variables<commit_after>package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

// Version and BuildTime are filled in during build by the Makefile
var (
	Version   = "N/A"
	BuildTime = "N/A"
)

var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Version information",
	Long:  `Various version details`,
	RunE: func(cmd *cobra.Command, args []string) error {
		printVersion()
		return nil
	},
}

func printVersion() {
	fmt.Fprintf(os.Stdout, "Version %s built on %s\n", Version, BuildTime)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
)

func hwaf_make_cmd_version() *commander.Command {
	cmd := &commander.Command{
		Run:       hwaf_run_cmd_version,
		UsageLine: "version",
		Short:     "print version and exit",
		Long: `
print version and exit.

ex:
 $ hwaf version
 hwaf-20130725
`,
		Flag: *flag.NewFlagSet("hwaf-version", flag.ExitOnError),
	}
	return cmd
}

func hwaf_run_cmd_version(cmd *commander.Command, args []string) {
	fmt.Printf("hwaf-20130725\n")
}

// EOF
<commit_msg>version: 20130729<commit_after>package main

import (
	"fmt"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
)

func hwaf_make_cmd_version() *commander.Command {
	cmd := &commander.Command{
		Run:       hwaf_run_cmd_version,
		UsageLine: "version",
		Short:     "print version and exit",
		Long: `
print version and exit.

ex:
 $ hwaf version
 hwaf-20130729
`,
		Flag: *flag.NewFlagSet("hwaf-version", flag.ExitOnError),
	}
	return cmd
}

func hwaf_run_cmd_version(cmd *commander.Command, args []string) {
	fmt.Printf("hwaf-20130729\n")
}

// EOF
<|endoftext|>


<commit_before>package api

import (
	"errors"
	"fmt"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/aisk/logp"
	"github.com/aisk/wizard"
	"github.com/cloudfoundry-attic/jibber_jabber"
	"github.com/juju/persistent-cookiejar"
	"github.com/leancloud/lean-cli/api/regions"
	"github.com/leancloud/lean-cli/apps"
	"github.com/leancloud/lean-cli/utils"
	"github.com/leancloud/lean-cli/version"
	"github.com/levigross/grequests"
)

var dashboardBaseUrls = map[regions.Region]string{
	regions.CN:  "https://leancloud.cn",
	regions.US:  "https://us.leancloud.cn",
	regions.TAB: "https://tab.leancloud.cn",
}

var (
	// Get2FACode is the function to get the user's two-factor-authentication code.
	// You can override it with your custom function.
	Get2FACode = func() (int, error) {
		result := new(string)
		wizard.Ask([]wizard.Question{
			{
				Content: "Please input 2-factor auth code",
				Input: &wizard.Input{
					Result: result,
					Hidden: false,
				},
			},
		})
		code, err := strconv.Atoi(*result)
		if err != nil {
			return 0, errors.New("2-factor auth code should be numerical")
		}
		return code, nil
	}
)

type Client struct {
	CookieJar *cookiejar.Jar
	Region    regions.Region
	AppID     string
}

func NewClientByRegion(region regions.Region) *Client {
	return &Client{
		CookieJar: newCookieJar(),
		Region:    region,
	}
}

func NewClientByApp(appID string) *Client {
	return &Client{
		CookieJar: newCookieJar(),
		AppID:     appID,
	}
}

func (client *Client) GetBaseURL() string {
	envBaseURL := os.Getenv("LEANCLOUD_DASHBOARD")

	if envBaseURL != "" {
		return envBaseURL
	}

	region := client.Region

	if client.AppID != "" {
		var err error
		region, err = apps.GetAppRegion(client.AppID)

		if err != nil {
			panic(err) // This error should be catch at top level
		}
	}

	if url, ok := dashboardBaseUrls[region]; ok {
		return url
	} else {
		panic("invalid region")
	}
}

func (client *Client) options() (*grequests.RequestOptions, error) {
	u, err := url.Parse(client.GetBaseURL())
	if err != nil {
		panic(err)
	}
	cookies := client.CookieJar.Cookies(u)
	xsrf := ""
	for _, cookie := range cookies {
		if cookie.Name == "XSRF-TOKEN" {
			xsrf = cookie.Value
			break
		}
	}

	return &grequests.RequestOptions{
		Headers: map[string]string{
			"X-XSRF-TOKEN":    xsrf,
			"Accept-Language": getSystemLanguage(),
		},
		CookieJar:    client.CookieJar,
		UseCookieJar: true,
		UserAgent:    "LeanCloud-CLI/" + version.Version,
	}, nil
}

func doRequest(client *Client, method string, path string, params map[string]interface{}, options *grequests.RequestOptions) (*grequests.Response, error) {
	var err error
	if options == nil {
		if options, err = client.options(); err != nil {
			return nil, err
		}
	}
	if params != nil {
		options.JSON = params
	}
	var fn func(string, *grequests.RequestOptions) (*grequests.Response, error)
	switch method {
	case "GET":
		fn = grequests.Get
	case "POST":
		fn = grequests.Post
	case "PUT":
		fn = grequests.Put
	case "DELETE":
		fn = grequests.Delete
	case "PATCH":
		fn = grequests.Patch
	default:
		panic("invalid method: " + method)
	}
	resp, err := fn(client.GetBaseURL()+path, options)
	if err != nil {
		return nil, err
	}

	resp, err = client.checkAndDo2FA(resp)
	if err != nil {
		return nil, err
	}

	if !resp.Ok {
		if strings.HasPrefix(strings.TrimSpace(resp.Header.Get("Content-Type")), "application/json") {
			return nil, NewErrorFromResponse(resp)
		}
		return nil, fmt.Errorf("HTTP Error: %d, %s %s", resp.StatusCode, method, path)
	}

	if err = client.CookieJar.Save(); err != nil {
		return nil, err
	}

	return resp, nil
}

// check if the requests need two-factor-authentication and then do it.
func (client *Client) checkAndDo2FA(resp *grequests.Response) (*grequests.Response, error) {
	if resp.StatusCode != 401 {
		// don't need 2FA
		return resp, nil
	}
	var result struct {
		Token string `json:"token"`
	}
	err := resp.JSON(&result)
	if err != nil {
		return nil, err
	}
	token := result.Token
	if token == "" {
		return resp, nil
	}
	code, err := Get2FACode()
	if err != nil {
		return nil, err
	}

	jar, err := cookiejar.New(&cookiejar.Options{
		Filename: filepath.Join(utils.ConfigDir(), "leancloud", "cookies"),
	})
	if err != nil {
		return nil, err
	}

	resp, err = grequests.Post(client.GetBaseURL()+"/1.1/do2fa", &grequests.RequestOptions{
		JSON: map[string]interface{}{
			"token": token,
			"code":  code,
		},
		CookieJar: jar,
	})
	if err != nil {
		return nil, err
	}
	if !resp.Ok {
		if strings.HasPrefix(strings.TrimSpace(resp.Header.Get("Content-Type")), "application/json") {
			return nil, NewErrorFromResponse(resp)
		}
		return nil, fmt.Errorf("HTTP Error: %d, %s %s", resp.StatusCode, "POST", "/do2fa")
	}

	if err := jar.Save(); err != nil {
		return nil, err
	}

	return resp, nil
}

func (client *Client) get(path string, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "GET", path, nil, options)
}

func (client *Client) post(path string, params map[string]interface{}, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "POST", path, params, options)
}

func (client *Client) patch(path string, params map[string]interface{}, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "PATCH", path, params, options)
}

func (client *Client) put(path string, params map[string]interface{}, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "PUT", path, params, options)
}

func (client *Client) delete(path string, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "DELETE", path, nil, options)
}

func newCookieJar() *cookiejar.Jar {
	jarFileDir := filepath.Join(utils.ConfigDir(), "leancloud")

	os.MkdirAll(jarFileDir, 0775)

	jar, err := cookiejar.New(&cookiejar.Options{
		Filename: filepath.Join(jarFileDir, "cookies"),
	})
	if err != nil {
		panic(err)
	}
	return jar
}

func getSystemLanguage() string {
	language, err := jibber_jabber.DetectLanguage()

	if err != nil {
		logp.Error(err)
	}

	return language
}
<commit_msg>:twisted_rightwards_arrows: Merge pull request #421 from weakish/us-dashboard<commit_after>package api

import (
	"errors"
	"fmt"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/aisk/logp"
	"github.com/aisk/wizard"
	"github.com/cloudfoundry-attic/jibber_jabber"
	"github.com/juju/persistent-cookiejar"
	"github.com/leancloud/lean-cli/api/regions"
	"github.com/leancloud/lean-cli/apps"
	"github.com/leancloud/lean-cli/utils"
	"github.com/leancloud/lean-cli/version"
	"github.com/levigross/grequests"
)

var dashboardBaseUrls = map[regions.Region]string{
	regions.CN:  "https://leancloud.cn",
	regions.US:  "https://console.leancloud.app",
	regions.TAB: "https://tab.leancloud.cn",
}

var (
	// Get2FACode is the function to get the user's two-factor-authentication code.
	// You can override it with your custom function.
	Get2FACode = func() (int, error) {
		result := new(string)
		wizard.Ask([]wizard.Question{
			{
				Content: "Please input 2-factor auth code",
				Input: &wizard.Input{
					Result: result,
					Hidden: false,
				},
			},
		})
		code, err := strconv.Atoi(*result)
		if err != nil {
			return 0, errors.New("2-factor auth code should be numerical")
		}
		return code, nil
	}
)

type Client struct {
	CookieJar *cookiejar.Jar
	Region    regions.Region
	AppID     string
}

func NewClientByRegion(region regions.Region) *Client {
	return &Client{
		CookieJar: newCookieJar(),
		Region:    region,
	}
}

func NewClientByApp(appID string) *Client {
	return &Client{
		CookieJar: newCookieJar(),
		AppID:     appID,
	}
}

func (client *Client) GetBaseURL() string {
	envBaseURL := os.Getenv("LEANCLOUD_DASHBOARD")

	if envBaseURL != "" {
		return envBaseURL
	}

	region := client.Region

	if client.AppID != "" {
		var err error
		region, err = apps.GetAppRegion(client.AppID)

		if err != nil {
			panic(err) // This error should be catch at top level
		}
	}

	if url, ok := dashboardBaseUrls[region]; ok {
		return url
	} else {
		panic("invalid region")
	}
}

func (client *Client) options() (*grequests.RequestOptions, error) {
	u, err := url.Parse(client.GetBaseURL())
	if err != nil {
		panic(err)
	}
	cookies := client.CookieJar.Cookies(u)
	xsrf := ""
	for _, cookie := range cookies {
		if cookie.Name == "XSRF-TOKEN" {
			xsrf = cookie.Value
			break
		}
	}

	return &grequests.RequestOptions{
		Headers: map[string]string{
			"X-XSRF-TOKEN":    xsrf,
			"Accept-Language": getSystemLanguage(),
		},
		CookieJar:    client.CookieJar,
		UseCookieJar: true,
		UserAgent:    "LeanCloud-CLI/" + version.Version,
	}, nil
}

func doRequest(client *Client, method string, path string, params map[string]interface{}, options *grequests.RequestOptions) (*grequests.Response, error) {
	var err error
	if options == nil {
		if options, err = client.options(); err != nil {
			return nil, err
		}
	}
	if params != nil {
		options.JSON = params
	}
	var fn func(string, *grequests.RequestOptions) (*grequests.Response, error)
	switch method {
	case "GET":
		fn = grequests.Get
	case "POST":
		fn = grequests.Post
	case "PUT":
		fn = grequests.Put
	case "DELETE":
		fn = grequests.Delete
	case "PATCH":
		fn = grequests.Patch
	default:
		panic("invalid method: " + method)
	}
	resp, err := fn(client.GetBaseURL()+path, options)
	if err != nil {
		return nil, err
	}

	resp, err = client.checkAndDo2FA(resp)
	if err != nil {
		return nil, err
	}

	if !resp.Ok {
		if strings.HasPrefix(strings.TrimSpace(resp.Header.Get("Content-Type")), "application/json") {
			return nil, NewErrorFromResponse(resp)
		}
		return nil, fmt.Errorf("HTTP Error: %d, %s %s", resp.StatusCode, method, path)
	}

	if err = client.CookieJar.Save(); err != nil {
		return nil, err
	}

	return resp, nil
}

// check if the requests need two-factor-authentication and then do it.
func (client *Client) checkAndDo2FA(resp *grequests.Response) (*grequests.Response, error) {
	if resp.StatusCode != 401 {
		// don't need 2FA
		return resp, nil
	}
	var result struct {
		Token string `json:"token"`
	}
	err := resp.JSON(&result)
	if err != nil {
		return nil, err
	}
	token := result.Token
	if token == "" {
		return resp, nil
	}
	code, err := Get2FACode()
	if err != nil {
		return nil, err
	}

	jar, err := cookiejar.New(&cookiejar.Options{
		Filename: filepath.Join(utils.ConfigDir(), "leancloud", "cookies"),
	})
	if err != nil {
		return nil, err
	}

	resp, err = grequests.Post(client.GetBaseURL()+"/1.1/do2fa", &grequests.RequestOptions{
		JSON: map[string]interface{}{
			"token": token,
			"code":  code,
		},
		CookieJar: jar,
	})
	if err != nil {
		return nil, err
	}
	if !resp.Ok {
		if strings.HasPrefix(strings.TrimSpace(resp.Header.Get("Content-Type")), "application/json") {
			return nil, NewErrorFromResponse(resp)
		}
		return nil, fmt.Errorf("HTTP Error: %d, %s %s", resp.StatusCode, "POST", "/do2fa")
	}

	if err := jar.Save(); err != nil {
		return nil, err
	}

	return resp, nil
}

func (client *Client) get(path string, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "GET", path, nil, options)
}

func (client *Client) post(path string, params map[string]interface{}, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "POST", path, params, options)
}

func (client *Client) patch(path string, params map[string]interface{}, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "PATCH", path, params, options)
}

func (client *Client) put(path string, params map[string]interface{}, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "PUT", path, params, options)
}

func (client *Client) delete(path string, options *grequests.RequestOptions) (*grequests.Response, error) {
	return doRequest(client, "DELETE", path, nil, options)
}

func newCookieJar() *cookiejar.Jar {
	jarFileDir := filepath.Join(utils.ConfigDir(), "leancloud")

	os.MkdirAll(jarFileDir, 0775)

	jar, err := cookiejar.New(&cookiejar.Options{
		Filename: filepath.Join(jarFileDir, "cookies"),
	})
	if err != nil {
		panic(err)
	}
	return jar
}

func getSystemLanguage() string {
	language, err := jibber_jabber.DetectLanguage()

	if err != nil {
		logp.Error(err)
	}

	return language
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"compress/gzip"
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func importFile(filename string) {
	// Print out status
	fmt.Print("[OK!] Attempting to parse ")
	fmt.Println(filename)

	// Try to open the file
	file, err := os.Open(filename)
	if err != nil {
		fmt.Println("[ERR] Sorry! I Couldn't access the specified file.")
		fmt.Println("      Double check the permissions and file path.")
		return
	}
	defer file.Close()
	fmt.Println("[OK!] File opened")

	// Check file extension
	var gzipped bool = false
	if strings.HasSuffix(filename, ".txt") {
		gzipped = false
	} else if strings.HasSuffix(filename, ".txt.gz") {
		gzipped = true
	} else {
		fmt.Println("[ERR] My deepest apologies! The file doesn't meet the requirements.")
		fmt.Println("      BitCannon currently accepts .txt and gzipped .txt files only.")
		return
	}
	fmt.Println("[OK!] Extension is valid")

	var scanner *bufio.Scanner
	if gzipped {
		reader, err := gzip.NewReader(file)
		if err != nil {
			fmt.Println("[ERR] My bad! I tried to start uncompressing your archive.")
			fmt.Println("      Try checking the file, or send me the file so I can check it out.")
			return
		}
		defer reader.Close()
		scanner = bufio.NewScanner(reader)
		fmt.Println("[OK!] GZip detected, unzipping enabled")
	} else {
		scanner = bufio.NewScanner(file)
	}
	fmt.Println("[OK!] Reading initialized")

	imported := 0
	skipped := 0
	// Now we scan ୧༼ಠ益ಠ༽୨
	for scanner.Scan() {
		status, _ := importLine(scanner.Text())
		if status {
			imported++
		} else {
			skipped++
		}
	}
	fmt.Println("[OK!] Reading completed")
	fmt.Println("      " + strconv.Itoa(imported) + " torrents imported")
	fmt.Println("      " + strconv.Itoa(skipped) + " torrents skipped")
}

func importLine(line string) (bool, error) {
	if strings.Count(line, "|") != 4 {
		return false, errors.New("Something's up with this torrent. Expected 5 values separated by |.")
	}
	data := strings.Split(line, "|")
	return torrentDB.Insert(data[0], data[1], data[2], data[3], data[4])
}
<commit_msg>Cleanup / refactor / prep for streaming updates.<commit_after>package main

import (
	"bufio"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

func importFile(filename string) {
	// Print out status
	fmt.Print("[OK!] Attempting to parse ")
	fmt.Println(filename)

	// Try to open the file
	file, err := os.Open(filename)
	if err != nil {
		fmt.Println("[ERR] Sorry! I Couldn't access the specified file.")
		fmt.Println("      Double check the permissions and file path.")
		return
	}
	defer file.Close()
	fmt.Println("[OK!] File opened")

	// Check file extension
	var gzipped bool = false
	if strings.HasSuffix(filename, ".txt") {
		gzipped = false
	} else if strings.HasSuffix(filename, ".txt.gz") {
		gzipped = true
	} else {
		fmt.Println("[ERR] My deepest apologies! The file doesn't meet the requirements.")
		fmt.Println("      BitCannon currently accepts .txt and gzipped .txt files only.")
		return
	}
	fmt.Println("[OK!] Extension is valid")

	if gzipped {
		reader, err := gzip.NewReader(file)
		if err != nil {
			fmt.Println("[ERR] My bad! I tried to start uncompressing your archive.")
			fmt.Println("      Try checking the file, or send me the file so I can check it out.")
			return
		}
		defer reader.Close()
		fmt.Println("[OK!] GZip detected, unzipping enabled")
		importReader(reader)
	} else {
		importReader(file)
	}
}

func importReader(reader io.Reader) {
	scanner := bufio.NewScanner(reader)
	fmt.Println("[OK!] Reading initialized")
	imported := 0
	skipped := 0
	// Now we scan ୧༼ಠ益ಠ༽୨
	for scanner.Scan() {
		status, _ := importLine(scanner.Text())
		if status {
			imported++
		} else {
			skipped++
		}
	}
	fmt.Println("[OK!] Reading completed")
	fmt.Println("      " + strconv.Itoa(imported) + " torrents imported")
	fmt.Println("      " + strconv.Itoa(skipped) + " torrents skipped")
}

func importLine(line string) (bool, error) {
	if strings.Count(line, "|") != 4 {
		return false, errors.New("Something's up with this torrent. Expected 5 values separated by |.")
	}
	data := strings.Split(line, "|")
	return torrentDB.Insert(data[0], data[1], data[2], data[3], data[4])
}
<|endoftext|>


<commit_before>package api

import (
	"github.com/martinp/atbapi/atb"
	"strconv"
)

type BusStops struct {
	Stops []BusStop `json:"stops"`
}

type BusStop struct {
	StopId      int     `json:"stopId"`
	NodeId      int     `json:"nodeId"`
	Description string  `json:"description"`
	Longitude   float64 `json:"longitude"`
	Latitude    float64 `json:"latitude"`
	MobileCode  string  `json:"mobileCode"`
	MobileName  string  `json:"mobileName"`
}

type Departures struct {
	TowardsCentrum bool        `json:"isGoingTowardsCentrum"`
	Departures     []Departure `json:"departures"`
}

type Departure struct {
	LineId                  string `json:"line"`
	RegisteredDepartureTime string `json:"registeredDepartureTime"`
	ScheduledDepartureTime  string `json:"scheduledDepartureTime"`
	Destination             string `json:"destination"`
	IsRealtimeData          bool   `json:"isRealtimeData"`
}

func convertBusStop(s atb.BusStop) (BusStop, error) {
	nodeId, err := strconv.Atoi(s.NodeId)
	if err != nil {
		return BusStop{}, err
	}
	longitude, err := strconv.ParseFloat(s.Longitude, 64)
	if err != nil {
		return BusStop{}, err
	}
	latitude := float64(s.Latitude)
	return BusStop{
		StopId:      s.StopId,
		NodeId:      nodeId,
		Description: s.Description,
		Longitude:   longitude,
		Latitude:    latitude,
		MobileCode:  s.MobileCode,
		MobileName:  s.MobileName,
	}, nil
}

func convertBusStops(s atb.BusStops) (BusStops, error) {
	stops := make([]BusStop, 0, len(s.Stops))
	for _, stop := range s.Stops {
		converted, err := convertBusStop(stop)
		if err != nil {
			return BusStops{}, err
		}
		stops = append(stops, converted)
	}
	return BusStops{Stops: stops}, nil
}

func isRealtime(s string) bool {
	return strings.EqualFold(s, "prev")
}

func convertForecast(f atb.Forecast) (Departure, error) {
	return Departure{
		LineId:                  f.LineId,
		Destination:             f.Destination,
		RegisteredDepartureTime: f.RegisteredDepartureTime,
		ScheduledDepartureTime:  f.ScheduledDepartureTime,
		IsRealtimeData:          isRealtime(f.StationForecast),
	}, nil
}

func convertForecasts(f atb.Forecasts) (Departures, error) {
	towardsCentrum := false
	if len(f.Nodes) > 0 {
		nodeId, err := strconv.Atoi(f.Nodes[0].NodeId)
		if err != nil {
			return Departures{}, err
		}
		towardsCentrum = (nodeId/1000)%2 == 1
	}
	departures := make([]Departure, 0, len(f.Forecasts))
	for _, forecast := range f.Forecasts {
		departure, err := convertForecast(forecast)
		if err != nil {
			return Departures{}, err
		}
		departures = append(departures, departure)
	}
	return Departures{
		TowardsCentrum: towardsCentrum,
		Departures:     departures,
	}, nil
}
<commit_msg>Convert time format<commit_after>package api

import (
	"github.com/martinp/atbapi/atb"
	"strconv"
	"time"
)

type BusStops struct {
	Stops []BusStop `json:"stops"`
}

type BusStop struct {
	StopId      int     `json:"stopId"`
	NodeId      int     `json:"nodeId"`
	Description string  `json:"description"`
	Longitude   float64 `json:"longitude"`
	Latitude    float64 `json:"latitude"`
	MobileCode  string  `json:"mobileCode"`
	MobileName  string  `json:"mobileName"`
}

type Departures struct {
	TowardsCentrum bool        `json:"isGoingTowardsCentrum"`
	Departures     []Departure `json:"departures"`
}

type Departure struct {
	LineId                  string `json:"line"`
	RegisteredDepartureTime string `json:"registeredDepartureTime"`
	ScheduledDepartureTime  string `json:"scheduledDepartureTime"`
	Destination             string `json:"destination"`
	IsRealtimeData          bool   `json:"isRealtimeData"`
}

func convertBusStop(s atb.BusStop) (BusStop, error) {
	nodeId, err := strconv.Atoi(s.NodeId)
	if err != nil {
		return BusStop{}, err
	}
	longitude, err := strconv.ParseFloat(s.Longitude, 64)
	if err != nil {
		return BusStop{}, err
	}
	latitude := float64(s.Latitude)
	return BusStop{
		StopId:      s.StopId,
		NodeId:      nodeId,
		Description: s.Description,
		Longitude:   longitude,
		Latitude:    latitude,
		MobileCode:  s.MobileCode,
		MobileName:  s.MobileName,
	}, nil
}

func convertBusStops(s atb.BusStops) (BusStops, error) {
	stops := make([]BusStop, 0, len(s.Stops))
	for _, stop := range s.Stops {
		converted, err := convertBusStop(stop)
		if err != nil {
			return BusStops{}, err
		}
		stops = append(stops, converted)
	}
	return BusStops{Stops: stops}, nil
}

func convertTime(src string) (string, error) {
	t, err := time.Parse("02.01.2006 15:04", src)
	if err != nil {
		return "", err
	}
	return t.Format("2006-01-02T15:04:05.000"), nil
}

func isRealtime(s string) bool {
	return strings.EqualFold(s, "prev")
}

func convertForecast(f atb.Forecast) (Departure, error) {
	registeredDeparture, err := convertTime(f.RegisteredDepartureTime)
	if err != nil {
		return Departure{}, err
	}
	scheduledDeparture, err := convertTime(f.ScheduledDepartureTime)
	if err != nil {
		return Departure{}, err
	}
	return Departure{
		LineId:                  f.LineId,
		Destination:             f.Destination,
		RegisteredDepartureTime: registeredDeparture,
		ScheduledDepartureTime:  scheduledDeparture,
		IsRealtimeData:          isRealtime(f.StationForecast),
	}, nil
}

func convertForecasts(f atb.Forecasts) (Departures, error) {
	towardsCentrum := false
	if len(f.Nodes) > 0 {
		nodeId, err := strconv.Atoi(f.Nodes[0].NodeId)
		if err != nil {
			return Departures{}, err
		}
		towardsCentrum = (nodeId/1000)%2 == 1
	}
	departures := make([]Departure, 0, len(f.Forecasts))
	for _, forecast := range f.Forecasts {
		departure, err := convertForecast(forecast)
		if err != nil {
			return Departures{}, err
		}
		departures = append(departures, departure)
	}
	return Departures{
		TowardsCentrum: towardsCentrum,
		Departures:     departures,
	}, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 David Lu
// See License.txt

package api

import (
	l4g "github.com/alecthomas/log4go"
	"github.com/davidlu1997/gogogo/model"
	"net/http"
	"strings"
)

func InitPlayer() {
	BaseRoutes.Players.Handle("/create", ApiHandler(createPlayer)).Methods("POST")

	BaseRoutes.NeedPlayer.Handle("/update", ApiPlayerRequired(updatePlayer)).Methods("POST")
	BaseRoutes.NeedPlayer.Handle("/newpassword", ApiPlayerRequired(updatePassword)).Methods("POST")
	BaseRoutes.NeedPlayer.Handle("/newusername", ApiPlayerRequired(updateUsername)).Methods("POST")
	BaseRoutes.NeedPlayer.Handle("/newemail", ApiPlayerRequired(updateEmail)).Methods("POST")
	BaseRoutes.NeedPlayer.Handle("/get", ApiPlayerRequired(getPlayer)).Methods("GET")
	BaseRoutes.NeedPlayer.Handle("/games", ApiPlayerRequired(getPlayerGames)).Methods("GET")
	BaseRoutes.NeedPlayer.Handle("/find", ApiPlayerRequired(findPlayer)).Methods("POST")
}

func createPlayer(s *Session, w http.ResponseWriter, r *http.Request) {
	player := model.PlayerFromJson(r.Body)

	if player == nil {
		s.SetInvalidParam("createPlayer", "player")
		return
	}

	data := r.URL.Query().Get("d")
	props := model.MapFromJson(strings.NewReader(data))
	player.Email = props["email"]

	registeredPlayer, err := CreatePlayer(player)
	if err != nil {
		s.Err = err
		return
	}

	w.Write([]byte(registeredPlayer.ToJson()))
}

func updatePlayer(s *Session, w http.ResponseWriter, r *http.Request) {
	player := model.PlayerFromJson(r.Body)

	if player == nil {
		s.SetInvalidParam("updatePlayer", "player")
		return
	}

	data := r.URL.Query().Get("d")
	props := model.MapFromJson(strings.NewReader(data))
	player.Email = props["email"]

	updatedPlayer, err := CreatePlayer(player)
	if err != nil {
		s.Err = err
		return
	}

	w.Write([]byte(updatedPlayer.ToJson()))
}

func updatePassword(s *Session, w http.ResponseWriter, r *http.Request) {

}

func updateUsername(s *Session, w http.ResponseWriter, r *http.Request) {

}

func updateEmail(s *Session, w http.ResponseWriter, r *http.Request) {

}

func getPlayer(s *Session, w http.ResponseWriter, r *http.Request) {

}

func getPlayerGames(s *Session, w http.ResponseWriter, r *http.Request) {

}

func findPlayer(s *Session, w http.ResponseWriter, r *http.Request) {

}

func CreatePlayer(player *model.Player) (*model.Player, *model.Error) {
	if result := <-Srv.Store.Player().Save(player); result.Err != nil {
		l4g.Error("Create player save error: %s", result.Err)
		return nil, result.Err
	} else {
		registeredPlayer := result.Data.(*model.Player)

		return registeredPlayer, nil
	}
}

func UpdatePlayer(player *model.Player) (*model.Player, *model.Error) {
	if result := <-Srv.Store.Player().Update(player); result.Err != nil {
		l4g.Error("Player update error: %s", result.Err)
		return nil, result.Err
	} else {
		updatedPlayer := result.Data.(*model.Player)

		return updatedPlayer, nil
	}
}
<commit_msg>added player api<commit_after>// Copyright (c) 2016 David Lu
// See License.txt

package api

import (
	"net/http"
	"strings"

	"github.com/davidlu1997/gogogo/model"
)

func InitPlayer() {
	BaseRoutes.Players.Handle("/create", ApiHandler(createPlayer)).Methods("POST")

	BaseRoutes.NeedPlayer.Handle("/update", ApiPlayerRequired(updatePlayer)).Methods("POST")
	BaseRoutes.NeedPlayer.Handle("/get", ApiPlayerRequired(getPlayer)).Methods("GET")
	BaseRoutes.NeedPlayer.Handle("/games", ApiPlayerRequired(getPlayerGames)).Methods("GET")
	BaseRoutes.NeedPlayer.Handle("/find", ApiPlayerRequired(findPlayer)).Methods("POST")
}

func createPlayer(s *Session, w http.ResponseWriter, r *http.Request) {
	player := model.PlayerFromJson(r.Body)

	if player == nil {
		s.SetInvalidParam("createPlayer", "player")
		return
	}

	data := r.URL.Query().Get("d")
	props := model.MapFromJson(strings.NewReader(data))
	player.Email = props["email"]

	registeredPlayer, err := CreatePlayer(player)
	if err != nil {
		s.Err = err
		return
	}

	w.Write([]byte(registeredPlayer.ToJson()))
}

func updatePlayer(s *Session, w http.ResponseWriter, r *http.Request) {
	player := model.PlayerFromJson(r.Body)

	if player == nil {
		s.SetInvalidParam("updatePlayer", "player")
		return
	}

	data := r.URL.Query().Get("d")
	props := model.MapFromJson(strings.NewReader(data))
	player.Email = props["email"]

	updatedPlayer, err := CreatePlayer(player)
	if err != nil {
		s.Err = err
		return
	}

	w.Write([]byte(updatedPlayer.ToJson()))
}

func getPlayer(s *Session, w http.ResponseWriter, r *http.Request) {

}

func getPlayerGames(s *Session, w http.ResponseWriter, r *http.Request) {

}

func findPlayer(s *Session, w http.ResponseWriter, r *http.Request) {

}

func CreatePlayer(player *model.Player) (*model.Player, *model.Error) {
	if result := <-Srv.Store.Player().Save(player); result.Err != nil {
		return nil, result.Err
	} else {
		registeredPlayer := result.Data.(*model.Player)

		return registeredPlayer, nil
	}
}

func UpdatePlayer(player *model.Player) (*model.Player, *model.Error) {
	if result := <-Srv.Store.Player().Update(player); result.Err != nil {
		return nil, result.Err
	} else {
		updatedPlayer := result.Data.(*model.Player)

		return updatedPlayer, nil
	}
}

func GetPlayer(id string) (*model.Player, *model.Error) {

}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2015 PPCD developers.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wire

import (
	"io"

	"github.com/ppcsuite/ppcd/chaincfg/chainhash"
)

// MsgCheckPoint implements the Message interface and represents a bitcoin reject
// message.
//
// This message was not added until protocol version RejectVersion.
type MsgCheckPoint struct {
	// Cmd is the command for the message which was rejected such as
	// as CmdBlock or CmdTx.  This can be obtained from the Command function
	// of a Message.
	Cmd string

	// Hash identifies a specific block or transaction that was rejected
	// and therefore only applies the MsgBlock and MsgTx messages.
	Hash chainhash.Hash
}

// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgCheckPoint) BtcDecode(r io.Reader, pver uint32) error {

	// Command that was rejected.
	cmd, err := readVarString(r, pver)
	if err != nil {
		return err
	}
	msg.Cmd = cmd

	// CmdBlock and CmdTx messages have an additional hash field that
	// identifies the specific block or transaction.
	err = readElement(r, &msg.Hash)
	if err != nil {
		return err
	}

	return nil
}

// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgCheckPoint) BtcEncode(w io.Writer, pver uint32) error {

	// Command that was rejected.
	err := writeVarString(w, pver, msg.Cmd)
	if err != nil {
		return err
	}

	// CmdBlock and CmdTx messages have an additional hash field that
	// identifies the specific block or transaction.
	err = writeElement(w, &msg.Hash)
	if err != nil {
		return err
	}

	return nil
}

// Command returns the protocol command string for the message.  This is part
// of the Message interface implementation.
func (msg *MsgCheckPoint) Command() string {
	return CmdCheckPoint
}

// MaxPayloadLength returns the maximum length the payload can be for the
// receiver.  This is part of the Message interface implementation.
func (msg *MsgCheckPoint) MaxPayloadLength(pver uint32) uint32 {
	plen := MaxMessagePayload
	return uint32(plen)
}

// NewMsgCheckPoint returns a new bitcoin reject message that conforms to the
// Message interface.  See MsgCheckPoint for details.
func NewMsgCheckPoint(command string) *MsgCheckPoint {
	return &MsgCheckPoint{
		Cmd: command,
	}
}
<commit_msg>more minor fixes<commit_after>// Copyright (c) 2014-2015 PPCD developers.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wire

import (
	"io"

	"github.com/ppcsuite/ppcd/chaincfg/chainhash"
)

// MsgCheckPoint implements the Message interface and represents a bitcoin reject
// message.
//
// This message was not added until protocol version RejectVersion.
type MsgCheckPoint struct {
	// Cmd is the command for the message which was rejected such as
	// as CmdBlock or CmdTx.  This can be obtained from the Command function
	// of a Message.
	Cmd string

	// Hash identifies a specific block or transaction that was rejected
	// and therefore only applies the MsgBlock and MsgTx messages.
	Hash chainhash.Hash
}

// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgCheckPoint) BtcDecode(r io.Reader, pver uint32) error {

	// Command that was rejected.
	cmd, err := ReadVarString(r, pver)
	if err != nil {
		return err
	}
	msg.Cmd = cmd

	// CmdBlock and CmdTx messages have an additional hash field that
	// identifies the specific block or transaction.
	err = readElement(r, &msg.Hash)
	if err != nil {
		return err
	}

	return nil
}

// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgCheckPoint) BtcEncode(w io.Writer, pver uint32) error {

	// Command that was rejected.
	err := WriteVarString(w, pver, msg.Cmd)
	if err != nil {
		return err
	}

	// CmdBlock and CmdTx messages have an additional hash field that
	// identifies the specific block or transaction.
	err = writeElement(w, &msg.Hash)
	if err != nil {
		return err
	}

	return nil
}

// Command returns the protocol command string for the message.  This is part
// of the Message interface implementation.
func (msg *MsgCheckPoint) Command() string {
	return CmdCheckPoint
}

// MaxPayloadLength returns the maximum length the payload can be for the
// receiver.  This is part of the Message interface implementation.
func (msg *MsgCheckPoint) MaxPayloadLength(pver uint32) uint32 {
	plen := MaxMessagePayload
	return uint32(plen)
}

// NewMsgCheckPoint returns a new bitcoin reject message that conforms to the
// Message interface.  See MsgCheckPoint for details.
func NewMsgCheckPoint(command string) *MsgCheckPoint {
	return &MsgCheckPoint{
		Cmd: command,
	}
}
<|endoftext|>


<commit_before>package api

func (api *InternalAPI) RegisterRoutes() {
	api.router.GET("/", AuthHandler(""), GetIndex)
	api.router.PATCH("/config", AuthHandler("c:config"), PatchConfiguration)

	// Register routes for v1 of the API. This API should be fully backwards compatable with
	// the existing Nodejs Daemon API.
	v1 := api.router.Group("/v1")
	{
		v1BaseRoutes := v1.Group("/server")
		{
			v1BaseRoutes.GET("/", AuthHandler("c:list"), ListServers)
			v1BaseRoutes.POST("/", AuthHandler("c:create"), StoreServer)
		}

		v1ServerRoutes := v1.Group("/server/:server")
		{
			v1ServerRoutes.GET("/", AuthHandler("s:get"), ViewServer)
			v1ServerRoutes.GET("/log", AuthHandler("s:console"), GetLogForServer)

			v1ServerRoutes.POST("/reinstall", AuthHandler("s:install-server"), ReinstallServer)
			v1ServerRoutes.POST("/rebuild", AuthHandler("g:server:rebuild"), RebuildServer)
			v1ServerRoutes.POST("/password", AuthHandler(""), SetServerPassword)
			v1ServerRoutes.POST("/power", AuthHandler("s:power"), PowerServer)
			v1ServerRoutes.POST("/command", AuthHandler("s:command"), SendCommandToServer)
			v1ServerRoutes.POST("/suspend", AuthHandler(""), SuspendServer)
			v1ServerRoutes.POST("/unsuspend", AuthHandler(""), UnsuspendServer)

			v1ServerRoutes.PATCH("/", AuthHandler("s:config"), UpdateServer)
			v1ServerRoutes.DELETE("/", AuthHandler("g:server:delete"), DeleteServer)
		}

		v1ServerFileRoutes := v1.Group("/server/:server/files")
		{
			v1ServerFileRoutes.GET("/file/:file", AuthHandler("s:files:read"), ReadFileContents)
			v1ServerFileRoutes.GET("/stat/:file", AuthHandler("s:files:get"), StatFile)
			v1ServerFileRoutes.GET("/dir/:directory", AuthHandler("s:files:get"), ListDirectory)
			v1ServerFileRoutes.GET("/download/:token", DownloadFile)

			v1ServerFileRoutes.POST("/dir/:directory", AuthHandler("s:files:create"), StoreDirectory)
			v1ServerFileRoutes.POST("/file/:file", AuthHandler("s:files:post"), WriteFileContents)
			v1ServerFileRoutes.POST("/copy/:file", AuthHandler("s:files:copy"), CopyFile)
			v1ServerFileRoutes.POST("/move/:file", AuthHandler("s:files:move"), MoveFile)
			v1ServerFileRoutes.POST("/rename/:file", AuthHandler("s:files:move"), MoveFile)
			v1ServerFileRoutes.POST("/compress/:file", AuthHandler("s:files:compress"), CompressFile)
			v1ServerFileRoutes.POST("/decompress/:file", AuthHandler("s:files:decompress"), DecompressFile)

			v1ServerFileRoutes.DELETE("/file/:file", AuthHandler("s:files:delete"), DeleteFile)
		}
	}
}
<commit_msg>Forgotten routes into v1 namespace<commit_after>package api

func (api *InternalAPI) RegisterRoutes() {
	// Register routes for v1 of the API. This API should be fully backwards compatable with
	// the existing Nodejs Daemon API.
	v1 := api.router.Group("/v1")
	{
		v1.GET("/", AuthHandler(""), GetIndex)
		v1.PATCH("/config", AuthHandler("c:config"), PatchConfiguration)

		v1BaseRoutes := v1.Group("/server")
		{
			v1BaseRoutes.GET("/", AuthHandler("c:list"), ListServers)
			v1BaseRoutes.POST("/", AuthHandler("c:create"), StoreServer)
		}

		v1ServerRoutes := v1.Group("/server/:server")
		{
			v1ServerRoutes.GET("/", AuthHandler("s:get"), ViewServer)
			v1ServerRoutes.GET("/log", AuthHandler("s:console"), GetLogForServer)

			v1ServerRoutes.POST("/reinstall", AuthHandler("s:install-server"), ReinstallServer)
			v1ServerRoutes.POST("/rebuild", AuthHandler("g:server:rebuild"), RebuildServer)
			v1ServerRoutes.POST("/password", AuthHandler(""), SetServerPassword)
			v1ServerRoutes.POST("/power", AuthHandler("s:power"), PowerServer)
			v1ServerRoutes.POST("/command", AuthHandler("s:command"), SendCommandToServer)
			v1ServerRoutes.POST("/suspend", AuthHandler(""), SuspendServer)
			v1ServerRoutes.POST("/unsuspend", AuthHandler(""), UnsuspendServer)

			v1ServerRoutes.PATCH("/", AuthHandler("s:config"), UpdateServer)
			v1ServerRoutes.DELETE("/", AuthHandler("g:server:delete"), DeleteServer)
		}

		v1ServerFileRoutes := v1.Group("/server/:server/files")
		{
			v1ServerFileRoutes.GET("/file/:file", AuthHandler("s:files:read"), ReadFileContents)
			v1ServerFileRoutes.GET("/stat/:file", AuthHandler("s:files:get"), StatFile)
			v1ServerFileRoutes.GET("/dir/:directory", AuthHandler("s:files:get"), ListDirectory)
			v1ServerFileRoutes.GET("/download/:token", DownloadFile)

			v1ServerFileRoutes.POST("/dir/:directory", AuthHandler("s:files:create"), StoreDirectory)
			v1ServerFileRoutes.POST("/file/:file", AuthHandler("s:files:post"), WriteFileContents)
			v1ServerFileRoutes.POST("/copy/:file", AuthHandler("s:files:copy"), CopyFile)
			v1ServerFileRoutes.POST("/move/:file", AuthHandler("s:files:move"), MoveFile)
			v1ServerFileRoutes.POST("/rename/:file", AuthHandler("s:files:move"), MoveFile)
			v1ServerFileRoutes.POST("/compress/:file", AuthHandler("s:files:compress"), CompressFile)
			v1ServerFileRoutes.POST("/decompress/:file", AuthHandler("s:files:decompress"), DecompressFile)

			v1ServerFileRoutes.DELETE("/file/:file", AuthHandler("s:files:delete"), DeleteFile)
		}
	}
}
<|endoftext|>


<commit_before>package thuder

import (
	"path/filepath"
	"io"
	"os"
)

var (
	ErrBadPath = errors.New("the file path is not of required formate")
	ErrNeedDir = errors.New("a directory is required for this operation")
)

//Node is a node to be modified in the file system, such as files, folders, and
//deletes
type Node struct {
	fc   *FileContext //allow sharing for node with same context
	info os.FileInfo  //basic data read from the file system
}

//fileContext contains additional node information
type FileContext struct {
	from string      //source directory
	perm os.FileMode //save as mode perm
	isDelet bool //if true, this file should be removed in a push
}

//NewFileContext Creat a new root node, the fullname must be an absolute path.
func NewRootNode(fullname string) (*Node, error){
	if !filepath.IsAbs(fullname){
		return nil, ErrBadPath
	}
	dir, file := filepath.Split(fullname)
	fc := &FileContext{
		from: dir
		perm: os.FileMode(0755)
	}
	info, err := os.Stat(fullname)
	if err != nil{
		return nil, err
	}
	return &Node{
		fc: fc
		info:info
	}, nil
}

//Open calls os.Open on the file refrenced by this node
func (n *Node) Open() (*os.File, error){
	return os.Open(n.FullName())
}

func (n *Node) FullName() string{
	return filepath.Join(n.fc.from, n.info.Name())
}

//NewFileContext Creat a new child file context to be used by files with the same dir and perm
func NewFileContext(fi os.FileInfo, parent *Node) *FileContext{

}

//Collection is a document tree that collects meta data of changes in a directory
//to be made
type Collection struct {
	nodes map[string]Node
}

//Add adds all nodes by filename to the collection, existing node with the same
//name are overwitten.
func (c *Collection) Add(parent Node) error {
	if !parent.info.IsDir(){
		return ErrNeedDir
	}
	f, err := parent.Open()
	if err != nil{
		return err
	}
	defer f.Close()
	list, err := f.Readdir(-1)
	if err != nil{
		return err
	}
	for _, fi := range list{

	}
	return nil
}

//DirReader can list os.FileInfo, as implemented by os.File
type DirReader interface {
	Readdir(n int) ([]FileInfo, error)
}

type PullJob struct {
	Source string //source path
	Target string //target path
}

func (p *PullJob) Do() error {

	fi, err := os.Stat(dir)
	if err != nil {
		return err
	}

	os.Open(name string)

	c := Collection{}
	c.Collect()

	return nil
}

func ChildNodes(dir string, fi os.FileInfo, parent *Node) ([]Node, error) {
	fi, err := os.Stat(filepath.Join(dir, fi.Name()))
	if err != nil{
		return nil, err
	}
	retu &Node{
	fc   *FileContext //allow sharing for node with same context
	info fi
	}
}
<commit_msg>work in progess, can compile<commit_after>package thuder

import (
	"errors"
	"os"
	"path/filepath"
)

var (
	ErrBadPath = errors.New("the file path is not of required formate")
	ErrNeedDir = errors.New("a directory is required for this operation")
)

//Node is a node to be modified in the file system, such as files, folders, and
//deletes
type Node struct {
	fc   *FileContext //allow sharing for node with same context
	info os.FileInfo  //basic data read from the file system
}

//fileContext contains additional node information
type FileContext struct {
	from    string      //source directory
	perm    os.FileMode //save as mode perm
	isDelet bool        //if true, this file should be removed in a push
}

//NewFileContext Creat a new root node, the fullname must be an absolute path.
func NewRootNode(fullname string) (*Node, error) {
	if !filepath.IsAbs(fullname) {
		return nil, ErrBadPath
	}
	dir, _ := filepath.Split(fullname)
	fc := &FileContext{
		from: dir,
		perm: os.FileMode(0755),
	}
	info, err := os.Stat(fullname)
	if err != nil {
		return nil, err
	}
	return &Node{
		fc:   fc,
		info: info,
	}, nil
}

//Open calls os.Open on the file refrenced by this node
func (n *Node) Open() (*os.File, error) {
	return os.Open(n.FullName())
}

func (n *Node) FullName() string {
	return filepath.Join(n.fc.from, n.info.Name())
}

//NewFileContext Creat a new child file context to be used by files with the same dir and perm
func NewFileContext(parent *Node) *FileContext {
	return &FileContext{
		from: parent.FullName(),
		perm: parent.fc.perm,
	}
}

//Collection is a document tree that collects meta data of changes in a directory
//to be made
type Collection struct {
	nodes map[string]Node
}

func NewCollection() *Collection {
	return &Collection{
		nodes: make(map[string]Node),
	}
}

//Add adds all nodes by filename to the collection, existing node with the same
//name are overwitten.
func (c *Collection) Add(parent *Node) error {
	if !parent.info.IsDir() {
		return ErrNeedDir
	}
	f, err := parent.Open()
	if err != nil {
		return err
	}
	defer f.Close()
	list, err := f.Readdir(-1)
	if err != nil {
		return err
	}
	fc := NewFileContext(parent)
	for _, fi := range list {
		c.nodes[fi.Name()] = Node{
			fc:   fc,
			info: fi,
		}
	}
	return nil
}

//DirReader can list os.FileInfo, as implemented by os.File
type DirReader interface {
	Readdir(n int) ([]os.FileInfo, error)
}

type PullJob struct {
	Source string //source path
	Target string //target path
}

/*
func (p *PullJob) Do() error {

	fi, err := os.Stat(dir)
	if err != nil {
		return err
	}

	os.Open(name string)

	c := Collection{}
	c.Collect()

	return nil
}

func ChildNodes(dir string, fi os.FileInfo, parent *Node) ([]Node, error) {
	fi, err := os.Stat(filepath.Join(dir, fi.Name()))
	if err != nil{
		return nil, err
	}
	retu &Node{
	fc   *FileContext //allow sharing for node with same context
	info fi
	}
}
*/
<|endoftext|>