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

package ebiten

import (
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
)

// AppendInputChars appends "printable" runes, read from the keyboard at the time update is called, to runes,
// and returns the extended buffer.
// Giving a slice that already has enough capacity works efficiently.
//
// AppendInputChars represents the environment's locale-dependent translation of keyboard
// input to Unicode characters.
//
// IsKeyPressed is based on a mapping of device (US keyboard) codes to input device keys.
// "Control" and modifier keys should be handled with IsKeyPressed.
//
// AppendInputChars is concurrent-safe.
//
// On Android (ebitenmobile), EbitenView must be focusable to enable to handle keyboard keys.
//
// Keyboards don't work on iOS yet (#1090).
func AppendInputChars(runes []rune) []rune {
	return uiDriver().Input().AppendInputChars(runes)
}

// InputChars return "printable" runes read from the keyboard at the time update is called.
//
// Deprecated: as of v2.2. Use AppendInputChars instead.
func InputChars() []rune {
	return AppendInputChars(nil)
}

// IsKeyPressed returns a boolean indicating whether key is pressed.
//
// If you want to know whether the key started being pressed in the current frame,
// use inpututil.IsKeyJustPressed
//
// Known issue: On Edge browser, some keys don't work well:
//
//   - KeyKPEnter and KeyKPEqual are recognized as KeyEnter and KeyEqual.
//   - KeyPrintScreen is only treated at keyup event.
//
// IsKeyPressed is concurrent-safe.
//
// On Android (ebitenmobile), EbitenView must be focusable to enable to handle keyboard keys.
//
// Keyboards don't work on iOS yet (#1090).
func IsKeyPressed(key Key) bool {
	if !key.isValid() {
		return false
	}

	var keys []driver.Key
	switch key {
	case KeyAlt:
		keys = []driver.Key{driver.KeyAltLeft, driver.KeyAltRight}
	case KeyControl:
		keys = []driver.Key{driver.KeyControlLeft, driver.KeyControlRight}
	case KeyShift:
		keys = []driver.Key{driver.KeyShiftLeft, driver.KeyShiftRight}
	case KeyMeta:
		keys = []driver.Key{driver.KeyMetaLeft, driver.KeyMetaRight}
	default:
		keys = []driver.Key{driver.Key(key)}
	}
	for _, k := range keys {
		if uiDriver().Input().IsKeyPressed(k) {
			return true
		}
	}
	return false
}

// CursorPosition returns a position of a mouse cursor relative to the game screen (window). The cursor position is
// 'logical' position and this considers the scale of the screen.
//
// CursorPosition returns (0, 0) before the main loop on desktops and browsers.
//
// CursorPosition always returns (0, 0) on mobiles.
//
// CursorPosition is concurrent-safe.
func CursorPosition() (x, y int) {
	return uiDriver().Input().CursorPosition()
}

// Wheel returns x and y offsets of the mouse wheel or touchpad scroll.
// It returns 0 if the wheel isn't being rolled.
//
// Wheel is concurrent-safe.
func Wheel() (xoff, yoff float64) {
	return uiDriver().Input().Wheel()
}

// IsMouseButtonPressed returns a boolean indicating whether mouseButton is pressed.
//
// If you want to know whether the mouseButton started being pressed in the current frame,
// use inpututil.IsMouseButtonJustPressed
//
// IsMouseButtonPressed is concurrent-safe.
func IsMouseButtonPressed(mouseButton MouseButton) bool {
	return uiDriver().Input().IsMouseButtonPressed(mouseButton)
}

// GamepadID represents a gamepad's identifier.
type GamepadID = driver.GamepadID

// GamepadSDLID returns a string with the GUID generated in the same way as SDL.
// To detect devices, see also the community project of gamepad devices database: https://github.com/gabomdq/SDL_GameControllerDB
//
// GamepadSDLID always returns an empty string on browsers and mobiles.
//
// GamepadSDLID is concurrent-safe.
func GamepadSDLID(id GamepadID) string {
	return uiDriver().Input().GamepadSDLID(id)
}

// GamepadName returns a string with the name.
// This function may vary in how it returns descriptions for the same device across platforms.
// for example the following drivers/platforms see a Xbox One controller as the following:
//
//   - Windows: "Xbox Controller"
//   - Chrome: "Xbox 360 Controller (XInput STANDARD GAMEPAD)"
//   - Firefox: "xinput"
//
// GamepadName always returns an empty string on iOS.
//
// GamepadName is concurrent-safe.
func GamepadName(id GamepadID) string {
	return uiDriver().Input().GamepadName(id)
}

// AppendGamepadIDs appends available gamepad IDs to gamepadIDs, and returns the extended buffer.
// Giving a slice that already has enough capacity works efficiently.
//
// AppendGamepadIDs is concurrent-safe.
//
// AppendGamepadIDs doesn't append anything on iOS.
func AppendGamepadIDs(gamepadIDs []GamepadID) []GamepadID {
	return uiDriver().Input().AppendGamepadIDs(gamepadIDs)
}

// GamepadIDs returns a slice indicating available gamepad IDs.
//
// Deprecated: as of v2.2. Use AppendGamepadIDs instead.
func GamepadIDs() []GamepadID {
	return AppendGamepadIDs(nil)
}

// GamepadAxisNum returns the number of axes of the gamepad (id).
//
// GamepadAxisNum is concurrent-safe.
//
// GamepadAxisNum always returns 0 on iOS.
func GamepadAxisNum(id GamepadID) int {
	return uiDriver().Input().GamepadAxisNum(id)
}

// GamepadAxisValue returns a float value [-1.0 - 1.0] of the given gamepad (id)'s axis (axis).
//
// GamepadAxisValue is concurrent-safe.
//
// GamepadAxisValue always returns 0 on iOS.
func GamepadAxisValue(id GamepadID, axis int) float64 {
	return uiDriver().Input().GamepadAxisValue(id, axis)
}

// GamepadAxis returns a float value [-1.0 - 1.0] of the given gamepad (id)'s axis (axis).
//
// Deprecated: as of v2.2. Use GamepadAxisValue instead.
func GamepadAxis(id GamepadID, axis int) float64 {
	return GamepadAxisValue(id, axis)
}

// GamepadButtonNum returns the number of the buttons of the given gamepad (id).
//
// GamepadButtonNum is concurrent-safe.
//
// GamepadButtonNum always returns 0 on iOS.
func GamepadButtonNum(id GamepadID) int {
	return uiDriver().Input().GamepadButtonNum(id)
}

// IsGamepadButtonPressed reports whether the given button of the gamepad (id) is pressed or not.
//
// If you want to know whether the given button of gamepad (id) started being pressed in the current frame,
// use inpututil.IsGamepadButtonJustPressed
//
// IsGamepadButtonPressed is concurrent-safe.
//
// The relationships between physical buttons and buttion IDs depend on environments.
// There can be differences even between Chrome and Firefox.
//
// IsGamepadButtonPressed always returns false on iOS.
func IsGamepadButtonPressed(id GamepadID, button GamepadButton) bool {
	return uiDriver().Input().IsGamepadButtonPressed(id, button)
}

// StandardGamepadAxisValue returns a float value [-1.0 - 1.0] of the given gamepad (id)'s standard axis (axis).
//
// StandardGamepadAxisValue returns 0 when the gamepad doesn't have a standard gamepad layout mapping.
//
// StandardGamepadAxisValue is concurrent safe.
func StandardGamepadAxisValue(id GamepadID, axis StandardGamepadAxis) float64 {
	return uiDriver().Input().StandardGamepadAxisValue(id, axis)
}

// IsStandardGamepadButtonPressed reports whether the given gamepad (id)'s standard gamepad button (button) is pressed.
//
// IsStandardGamepadButtonPressed returns false when the gamepad doesn't have a standard gamepad layout mapping.
//
// IsStandardGamepadButtonPressed is concurrent safe.
func IsStandardGamepadButtonPressed(id GamepadID, button StandardGamepadButton) bool {
	return uiDriver().Input().IsStandardGamepadButtonPressed(id, button)
}

// IsStandardGamepadLayoutAvailable reports whether the gamepad (id) has a standard gamepad layout mapping.
//
// IsStandardGamepadLayoutAvailable is concurrent-safe.
func IsStandardGamepadLayoutAvailable(id GamepadID) bool {
	return uiDriver().Input().IsStandardGamepadLayoutAvailable(id)
}

// UpdateStandardGamepadLayoutMappings updates the gamepad layout definitions using a set of definitions in SDL_GameControllerDB format.
//
// Multiple input definitions can be provided separated by newlines.
// In particular, it is valid to pass an entire gamecontrollerdb.txt file.
//
// Note though that Ebiten already includes its own copy of this file,
// so this call should only be necessary to add mappings for hardware not supported yet;
// ideally games using the StandardGamepad functions should allow the user to provide mappings and then call this function if provided.
// When using this facility to support new hardware, please also send a pull request to https://github.com/gabomdq/SDL_GameControllerDB to make your mapping available to everyone else.
//
// UpdateStandardGamepadLayoutMappings reports whether the mappings were applied, and returns an error in case any occurred while parsing the mappings.
//
// On platforms where gamepad mappings are not managed by Ebiten, this always returns false and nil.
//
// UpdateStandardGamepadLayoutMappings must be called on the main thread before ebiten.Run, and is concurrent-safe after ebiten.Run.
//
// Updated mappings take effect immediately even for already connected gamepads.
func UpdateStandardGamepadLayoutMappings(mappings string) (bool, error) {
	return uiDriver().Input().UpdateStandardGamepadLayoutMappings(mappings)
}

// TouchID represents a touch's identifier.
type TouchID = driver.TouchID

// AppendTouchIDs appends the current touch states to touches, and returns the extended buffer.
// Giving a slice that already has enough capacity works efficiently.
//
// If you want to know whether a touch started being pressed in the current frame,
// use inpututil.JustPressedTouchIDs
//
// AppendTouchIDs doesn't append anything when there are no touches.
// AppendTouchIDs always does nothing on desktops.
//
// AppendTouchIDs is concurrent-safe.
func AppendTouchIDs(touches []TouchID) []TouchID {
	return uiDriver().Input().AppendTouchIDs(touches)
}

// TouchIDs returns the current touch states.
//
// Deperecated: as of v2.2. Use AppendTouchIDs instead.
func TouchIDs() []TouchID {
	return AppendTouchIDs(nil)
}

// TouchPosition returns the position for the touch of the specified ID.
//
// If the touch of the specified ID is not present, TouchPosition returns (0, 0).
//
// TouchPosition is cuncurrent-safe.
func TouchPosition(id TouchID) (int, int) {
	return uiDriver().Input().TouchPosition(id)
}
<commit_msg>ebiten: Update comments<commit_after>// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
)

// AppendInputChars appends "printable" runes, read from the keyboard at the time update is called, to runes,
// and returns the extended buffer.
// Giving a slice that already has enough capacity works efficiently.
//
// AppendInputChars represents the environment's locale-dependent translation of keyboard
// input to Unicode characters.
//
// IsKeyPressed is based on a mapping of device (US keyboard) codes to input device keys.
// "Control" and modifier keys should be handled with IsKeyPressed.
//
// AppendInputChars is concurrent-safe.
//
// On Android (ebitenmobile), EbitenView must be focusable to enable to handle keyboard keys.
//
// Keyboards don't work on iOS yet (#1090).
func AppendInputChars(runes []rune) []rune {
	return uiDriver().Input().AppendInputChars(runes)
}

// InputChars return "printable" runes read from the keyboard at the time update is called.
//
// Deprecated: as of v2.2. Use AppendInputChars instead.
func InputChars() []rune {
	return AppendInputChars(nil)
}

// IsKeyPressed returns a boolean indicating whether key is pressed.
//
// If you want to know whether the key started being pressed in the current frame,
// use inpututil.IsKeyJustPressed
//
// Known issue: On Edge browser, some keys don't work well:
//
//   - KeyKPEnter and KeyKPEqual are recognized as KeyEnter and KeyEqual.
//   - KeyPrintScreen is only treated at keyup event.
//
// IsKeyPressed is concurrent-safe.
//
// On Android (ebitenmobile), EbitenView must be focusable to enable to handle keyboard keys.
//
// Keyboards don't work on iOS yet (#1090).
func IsKeyPressed(key Key) bool {
	if !key.isValid() {
		return false
	}

	var keys []driver.Key
	switch key {
	case KeyAlt:
		keys = []driver.Key{driver.KeyAltLeft, driver.KeyAltRight}
	case KeyControl:
		keys = []driver.Key{driver.KeyControlLeft, driver.KeyControlRight}
	case KeyShift:
		keys = []driver.Key{driver.KeyShiftLeft, driver.KeyShiftRight}
	case KeyMeta:
		keys = []driver.Key{driver.KeyMetaLeft, driver.KeyMetaRight}
	default:
		keys = []driver.Key{driver.Key(key)}
	}
	for _, k := range keys {
		if uiDriver().Input().IsKeyPressed(k) {
			return true
		}
	}
	return false
}

// CursorPosition returns a position of a mouse cursor relative to the game screen (window). The cursor position is
// 'logical' position and this considers the scale of the screen.
//
// CursorPosition returns (0, 0) before the main loop on desktops and browsers.
//
// CursorPosition always returns (0, 0) on mobiles.
//
// CursorPosition is concurrent-safe.
func CursorPosition() (x, y int) {
	return uiDriver().Input().CursorPosition()
}

// Wheel returns x and y offsets of the mouse wheel or touchpad scroll.
// It returns 0 if the wheel isn't being rolled.
//
// Wheel is concurrent-safe.
func Wheel() (xoff, yoff float64) {
	return uiDriver().Input().Wheel()
}

// IsMouseButtonPressed returns a boolean indicating whether mouseButton is pressed.
//
// If you want to know whether the mouseButton started being pressed in the current frame,
// use inpututil.IsMouseButtonJustPressed
//
// IsMouseButtonPressed is concurrent-safe.
func IsMouseButtonPressed(mouseButton MouseButton) bool {
	return uiDriver().Input().IsMouseButtonPressed(mouseButton)
}

// GamepadID represents a gamepad's identifier.
type GamepadID = driver.GamepadID

// GamepadSDLID returns a string with the GUID generated in the same way as SDL.
// To detect devices, see also the community project of gamepad devices database: https://github.com/gabomdq/SDL_GameControllerDB
//
// GamepadSDLID always returns an empty string on browsers and mobiles.
//
// GamepadSDLID is concurrent-safe.
func GamepadSDLID(id GamepadID) string {
	return uiDriver().Input().GamepadSDLID(id)
}

// GamepadName returns a string with the name.
// This function may vary in how it returns descriptions for the same device across platforms.
// for example the following drivers/platforms see a Xbox One controller as the following:
//
//   - Windows: "Xbox Controller"
//   - Chrome: "Xbox 360 Controller (XInput STANDARD GAMEPAD)"
//   - Firefox: "xinput"
//
// GamepadName always returns an empty string on iOS.
//
// GamepadName is concurrent-safe.
func GamepadName(id GamepadID) string {
	return uiDriver().Input().GamepadName(id)
}

// AppendGamepadIDs appends available gamepad IDs to gamepadIDs, and returns the extended buffer.
// Giving a slice that already has enough capacity works efficiently.
//
// AppendGamepadIDs is concurrent-safe.
//
// AppendGamepadIDs doesn't append anything on iOS.
func AppendGamepadIDs(gamepadIDs []GamepadID) []GamepadID {
	return uiDriver().Input().AppendGamepadIDs(gamepadIDs)
}

// GamepadIDs returns a slice indicating available gamepad IDs.
//
// Deprecated: as of v2.2. Use AppendGamepadIDs instead.
func GamepadIDs() []GamepadID {
	return AppendGamepadIDs(nil)
}

// GamepadAxisNum returns the number of axes of the gamepad (id).
//
// GamepadAxisNum is concurrent-safe.
//
// GamepadAxisNum always returns 0 on iOS.
func GamepadAxisNum(id GamepadID) int {
	return uiDriver().Input().GamepadAxisNum(id)
}

// GamepadAxisValue returns a float value [-1.0 - 1.0] of the given gamepad (id)'s axis (axis).
//
// GamepadAxisValue is concurrent-safe.
//
// GamepadAxisValue always returns 0 on iOS.
func GamepadAxisValue(id GamepadID, axis int) float64 {
	return uiDriver().Input().GamepadAxisValue(id, axis)
}

// GamepadAxis returns a float value [-1.0 - 1.0] of the given gamepad (id)'s axis (axis).
//
// Deprecated: as of v2.2. Use GamepadAxisValue instead.
func GamepadAxis(id GamepadID, axis int) float64 {
	return GamepadAxisValue(id, axis)
}

// GamepadButtonNum returns the number of the buttons of the given gamepad (id).
//
// GamepadButtonNum is concurrent-safe.
//
// GamepadButtonNum always returns 0 on iOS.
func GamepadButtonNum(id GamepadID) int {
	return uiDriver().Input().GamepadButtonNum(id)
}

// IsGamepadButtonPressed reports whether the given button of the gamepad (id) is pressed or not.
//
// If you want to know whether the given button of gamepad (id) started being pressed in the current frame,
// use inpututil.IsGamepadButtonJustPressed
//
// IsGamepadButtonPressed is concurrent-safe.
//
// The relationships between physical buttons and buttion IDs depend on environments.
// There can be differences even between Chrome and Firefox.
//
// IsGamepadButtonPressed always returns false on iOS.
func IsGamepadButtonPressed(id GamepadID, button GamepadButton) bool {
	return uiDriver().Input().IsGamepadButtonPressed(id, button)
}

// StandardGamepadAxisValue returns a float value [-1.0 - 1.0] of the given gamepad (id)'s standard axis (axis).
//
// StandardGamepadAxisValue returns 0 when the gamepad doesn't have a standard gamepad layout mapping.
//
// StandardGamepadAxisValue is concurrent safe.
func StandardGamepadAxisValue(id GamepadID, axis StandardGamepadAxis) float64 {
	return uiDriver().Input().StandardGamepadAxisValue(id, axis)
}

// IsStandardGamepadButtonPressed reports whether the given gamepad (id)'s standard gamepad button (button) is pressed.
//
// IsStandardGamepadButtonPressed returns false when the gamepad doesn't have a standard gamepad layout mapping.
//
// IsStandardGamepadButtonPressed is concurrent safe.
func IsStandardGamepadButtonPressed(id GamepadID, button StandardGamepadButton) bool {
	return uiDriver().Input().IsStandardGamepadButtonPressed(id, button)
}

// IsStandardGamepadLayoutAvailable reports whether the gamepad (id) has a standard gamepad layout mapping.
//
// IsStandardGamepadLayoutAvailable is concurrent-safe.
func IsStandardGamepadLayoutAvailable(id GamepadID) bool {
	return uiDriver().Input().IsStandardGamepadLayoutAvailable(id)
}

// UpdateStandardGamepadLayoutMappings parses the specified string mappings in SDL_GameControllerDB format and
// updates the gamepad layout definitions.
//
// UpdateStandardGamepadLayoutMappings reports whether the mappings were applied,
// and returns an error in case any occurred while parsing the mappings.
//
// One or more input definitions can be provided separated by newlines.
// In particular, it is valid to pass an entire gamecontrollerdb.txt file.
// Note though that Ebiten already includes its own copy of this file,
// so this call should only be necessary to add mappings for hardware not supported yet;
// ideally games using the StandardGamepad* functions should allow the user to provide mappings and
// then call this function if provided.
// When using this facility to support new hardware, please also send a pull request to
// https://github.com/gabomdq/SDL_GameControllerDB to make your mapping available to everyone else.
//
// On platforms where gamepad mappings are not managed by Ebiten, this always returns false and nil.
//
// UpdateStandardGamepadLayoutMappings must be called on the main thread before ebiten.Run, and is concurrent-safe after ebiten.Run.
//
// Updated mappings take effect immediately even for already connected gamepads.
func UpdateStandardGamepadLayoutMappings(mappings string) (bool, error) {
	return uiDriver().Input().UpdateStandardGamepadLayoutMappings(mappings)
}

// TouchID represents a touch's identifier.
type TouchID = driver.TouchID

// AppendTouchIDs appends the current touch states to touches, and returns the extended buffer.
// Giving a slice that already has enough capacity works efficiently.
//
// If you want to know whether a touch started being pressed in the current frame,
// use inpututil.JustPressedTouchIDs
//
// AppendTouchIDs doesn't append anything when there are no touches.
// AppendTouchIDs always does nothing on desktops.
//
// AppendTouchIDs is concurrent-safe.
func AppendTouchIDs(touches []TouchID) []TouchID {
	return uiDriver().Input().AppendTouchIDs(touches)
}

// TouchIDs returns the current touch states.
//
// Deperecated: as of v2.2. Use AppendTouchIDs instead.
func TouchIDs() []TouchID {
	return AppendTouchIDs(nil)
}

// TouchPosition returns the position for the touch of the specified ID.
//
// If the touch of the specified ID is not present, TouchPosition returns (0, 0).
//
// TouchPosition is cuncurrent-safe.
func TouchPosition(id TouchID) (int, int) {
	return uiDriver().Input().TouchPosition(id)
}
<|endoftext|>


<commit_before>package annotator

import (
	"errors"
	"time"

	"github.com/hltcoe/goncrete"
	"go.uber.org/zap"
)

// Copier implements goncrete.AnnotateCommunicationService by returning
// a copy of the original communication, or an error if nil.
type Copier struct {
	log *zap.Logger
}

// NewCopier returns an instantiated Copier
func NewCopier(log *zap.Logger) *Copier {
	return &Copier{log}
}

var (
	_ goncrete.AnnotateCommunicationService = (*Copier)(nil)
)

func (p *Copier) Annotate(original *goncrete.Communication) (*goncrete.Communication, error) {
	if original == nil {
		return nil, errors.New("can't copy a nil communication")
	}
	return original, nil
}

func (c *Copier) GetMetadata() (*goncrete.AnnotationMetadata, error) {
	amd := goncrete.NewAnnotationMetadata()
	amd.Tool = "copier"
	amd.Timestamp = time.Now().Unix()
	amd.KBest = 0
	return amd, nil
}

func (c *Copier) GetDocumentation() (string, error) {
	return "Copies the original communication and returns", nil
}

func (p *Copier) Shutdown() (err error) {
	return nil
}
<commit_msg>logging in annotator<commit_after>package annotator

import (
	"errors"
	"time"

	"github.com/hltcoe/goncrete"
	"go.uber.org/zap"
)

// Copier implements goncrete.AnnotateCommunicationService by returning
// a copy of the original communication, or an error if nil.
type Copier struct {
	log *zap.Logger
}

// NewCopier returns an instantiated Copier
func NewCopier(log *zap.Logger) *Copier {
	return &Copier{log}
}

var (
	_ goncrete.AnnotateCommunicationService = (*Copier)(nil)
)

func (c *Copier) Annotate(original *goncrete.Communication) (*goncrete.Communication, error) {
	c.log.Debug("called", zap.String("method", "Annotate"))
	if original == nil {
		return nil, errors.New("can't copy a nil communication")
	}
	c.log.Debug("communication", zap.String("id", original.ID))
	return original, nil
}

func (c *Copier) GetMetadata() (*goncrete.AnnotationMetadata, error) {
	c.log.Debug("called", zap.String("method", "GetMetadata"))
	amd := goncrete.NewAnnotationMetadata()
	amd.Tool = "copier"
	amd.Timestamp = time.Now().Unix()
	amd.KBest = 0
	return amd, nil
}

func (c *Copier) GetDocumentation() (string, error) {
	c.log.Debug("called", zap.String("method", "GetDocumentation"))
	return "Copies the original communication and returns", nil
}

func (c *Copier) Shutdown() (err error) {
	c.log.Debug("called", zap.String("method", "Shutdown"))
	return nil
}
<|endoftext|>


<commit_before>package apps

import (
	"encoding/json"
	"io"
	"net/url"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/prefixer"
)

// KonnManifest contains all the informations associated with an installed
// konnector.
type KonnManifest struct {
	DocRev string `json:"_rev,omitempty"`

	Name       string `json:"name"`
	NamePrefix string `json:"name_prefix,omitempty"`
	Editor     string `json:"editor"`
	Icon       string `json:"icon"`

	Aggregator  *json.RawMessage `json:"aggregator,omitempty"`

	Type        string           `json:"type,omitempty"`
	License     string           `json:"license,omitempty"`
	Language    string           `json:"language,omitempty"`
	VendorLink  string           `json:"vendor_link"`
	Locales     *json.RawMessage `json:"locales,omitempty"`
	Langs       *json.RawMessage `json:"langs,omitempty"`
	Platforms   *json.RawMessage `json:"platforms,omitempty"`
	Categories  *json.RawMessage `json:"categories,omitempty"`
	Developer   *json.RawMessage `json:"developer,omitempty"`
	Screenshots *json.RawMessage `json:"screenshots,omitempty"`
	Tags        *json.RawMessage `json:"tags,omitempty"`

	Frequency    string           `json:"frequency,omitempty"`
	DataTypes    *json.RawMessage `json:"data_types,omitempty"`
	Doctypes     *json.RawMessage `json:"doctypes,omitempty"`
	Fields       *json.RawMessage `json:"fields,omitempty"`
	Folders      *json.RawMessage `json:"folders,omitempty"`
	Messages     *json.RawMessage `json:"messages,omitempty"`
	OAuth        *json.RawMessage `json:"oauth,omitempty"`
	TimeInterval *json.RawMessage `json:"time_interval,omitempty"`

	Parameters    *json.RawMessage `json:"parameters,omitempty"`
	Notifications Notifications    `json:"notifications"`

	// OnDeleteAccount can be used to specify a file path which will be executed
	// when an account associated with the konnector is deleted.
	OnDeleteAccount string `json:"on_delete_account,omitempty"`

	DocSlug          string          `json:"slug"`
	DocState         State           `json:"state"`
	DocSource        string          `json:"source"`
	DocVersion       string          `json:"version"`
	DocPermissions   permissions.Set `json:"permissions"`
	AvailableVersion string          `json:"available_version,omitempty"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`

	Err string `json:"error,omitempty"`
	err error
}

// ID is part of the Manifest interface
func (m *KonnManifest) ID() string { return m.DocType() + "/" + m.DocSlug }

// Rev is part of the Manifest interface
func (m *KonnManifest) Rev() string { return m.DocRev }

// DocType is part of the Manifest interface
func (m *KonnManifest) DocType() string { return consts.Konnectors }

// Clone is part of the Manifest interface
func (m *KonnManifest) Clone() couchdb.Doc {
	cloned := *m

	cloned.DocPermissions = make(permissions.Set, len(m.DocPermissions))
	copy(cloned.DocPermissions, m.DocPermissions)

	cloned.Aggregator = cloneRawMessage(m.Aggregator)

	cloned.Locales = cloneRawMessage(m.Locales)
	cloned.Langs = cloneRawMessage(m.Langs)
	cloned.Platforms = cloneRawMessage(m.Platforms)
	cloned.Categories = cloneRawMessage(m.Categories)
	cloned.Developer = cloneRawMessage(m.Developer)
	cloned.Screenshots = cloneRawMessage(m.Screenshots)
	cloned.Tags = cloneRawMessage(m.Tags)
	cloned.Parameters = cloneRawMessage(m.Parameters)

	cloned.DataTypes = cloneRawMessage(m.DataTypes)
	cloned.Doctypes = cloneRawMessage(m.Doctypes)
	cloned.Fields = cloneRawMessage(m.Fields)
	cloned.Folders = cloneRawMessage(m.Folders)
	cloned.Messages = cloneRawMessage(m.Messages)
	cloned.OAuth = cloneRawMessage(m.OAuth)
	cloned.TimeInterval = cloneRawMessage(m.TimeInterval)

	cloned.Notifications = make(Notifications, len(m.Notifications))
	for k, v := range m.Notifications {
		props := (&v).Clone()
		cloned.Notifications[k] = *props
	}
	return &cloned
}

// SetID is part of the Manifest interface
func (m *KonnManifest) SetID(id string) {}

// SetRev is part of the Manifest interface
func (m *KonnManifest) SetRev(rev string) { m.DocRev = rev }

// SetSource is part of the Manifest interface
func (m *KonnManifest) SetSource(src *url.URL) { m.DocSource = src.String() }

// Source is part of the Manifest interface
func (m *KonnManifest) Source() string { return m.DocSource }

// Version is part of the Manifest interface
func (m *KonnManifest) Version() string { return m.DocVersion }

// Slug is part of the Manifest interface
func (m *KonnManifest) Slug() string { return m.DocSlug }

// State is part of the Manifest interface
func (m *KonnManifest) State() State { return m.DocState }

// LastUpdate is part of the Manifest interface
func (m *KonnManifest) LastUpdate() time.Time { return m.UpdatedAt }

// SetState is part of the Manifest interface
func (m *KonnManifest) SetState(state State) { m.DocState = state }

// SetVersion is part of the Manifest interface
func (m *KonnManifest) SetVersion(version string) { m.DocVersion = version }

// SetAvailableVersion is part of the Manifest interface
func (m *KonnManifest) SetAvailableVersion(version string) { m.AvailableVersion = version }

// AppType is part of the Manifest interface
func (m *KonnManifest) AppType() AppType { return Konnector }

// Permissions is part of the Manifest interface
func (m *KonnManifest) Permissions() permissions.Set {
	return m.DocPermissions
}

// SetError is part of the Manifest interface
func (m *KonnManifest) SetError(err error) {
	m.SetState(Errored)
	m.Err = err.Error()
	m.err = err
}

// Error is part of the Manifest interface
func (m *KonnManifest) Error() error { return m.err }

// Match is part of the Manifest interface
func (m *KonnManifest) Match(field, value string) bool {
	switch field {
	case "slug":
		return m.DocSlug == value
	case "state":
		return m.DocState == State(value)
	}
	return false
}

// ReadManifest is part of the Manifest interface
func (m *KonnManifest) ReadManifest(r io.Reader, slug, sourceURL string) (Manifest, error) {
	var newManifest KonnManifest
	if err := json.NewDecoder(r).Decode(&newManifest); err != nil {
		return nil, ErrBadManifest
	}

	newManifest.SetID(m.ID())
	newManifest.SetRev(m.Rev())
	newManifest.SetState(m.State())
	newManifest.CreatedAt = m.CreatedAt
	newManifest.DocSlug = slug
	newManifest.DocSource = sourceURL
	if newManifest.Parameters == nil {
		newManifest.Parameters = m.Parameters
	}

	return &newManifest, nil
}

// Create is part of the Manifest interface
func (m *KonnManifest) Create(db prefixer.Prefixer) error {
	m.CreatedAt = time.Now()
	m.UpdatedAt = time.Now()
	if err := couchdb.CreateNamedDocWithDB(db, m); err != nil {
		return err
	}
	_, err := permissions.CreateKonnectorSet(db, m.Slug(), m.Permissions())
	return err
}

// Update is part of the Manifest interface
func (m *KonnManifest) Update(db prefixer.Prefixer) error {
	m.UpdatedAt = time.Now()
	err := couchdb.UpdateDoc(db, m)
	if err != nil {
		return err
	}
	_, err = permissions.UpdateKonnectorSet(db, m.Slug(), m.Permissions())
	return err
}

// Delete is part of the Manifest interface
func (m *KonnManifest) Delete(db prefixer.Prefixer) error {
	err := permissions.DestroyKonnector(db, m.Slug())
	if err != nil && !couchdb.IsNotFoundError(err) {
		return err
	}
	return couchdb.DeleteDoc(db, m)
}

// GetKonnectorBySlug fetch the manifest of a konnector from the database given
// a slug.
func GetKonnectorBySlug(db prefixer.Prefixer, slug string) (*KonnManifest, error) {
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}
	man := &KonnManifest{}
	err := couchdb.GetDoc(db, consts.Konnectors, consts.Konnectors+"/"+slug, man)
	if couchdb.IsNotFoundError(err) {
		return nil, ErrNotFound
	}
	if err != nil {
		return nil, err
	}
	return man, nil
}

// GetWebappBySlugAndUpdate fetch the KonnManifest and perform an update of
// the application if necessary and if the application was installed from the
// registry.
func GetKonnectorBySlugAndUpdate(db prefixer.Prefixer, slug string, copier Copier, registries []*url.URL) (*KonnManifest, error) {
	man, err := GetKonnectorBySlug(db, slug)
	if err != nil {
		return nil, err
	}
	return DoLazyUpdate(db, man, man.AvailableVersion, copier, registries).(*KonnManifest), nil
}

// ListKonnectors returns the list of installed konnectors applications.
//
// TODO: pagination
func ListKonnectors(db prefixer.Prefixer) ([]Manifest, error) {
	var docs []*KonnManifest
	req := &couchdb.AllDocsRequest{Limit: 100}
	err := couchdb.GetAllDocs(db, consts.Konnectors, req, &docs)
	if err != nil {
		return nil, err
	}
	mans := make([]Manifest, len(docs))
	for i, m := range docs {
		mans[i] = m
	}
	return mans, nil
}

var _ Manifest = &KonnManifest{}
<commit_msg>Fix lint<commit_after>package apps

import (
	"encoding/json"
	"io"
	"net/url"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/prefixer"
)

// KonnManifest contains all the informations associated with an installed
// konnector.
type KonnManifest struct {
	DocRev string `json:"_rev,omitempty"`

	Name       string `json:"name"`
	NamePrefix string `json:"name_prefix,omitempty"`
	Editor     string `json:"editor"`
	Icon       string `json:"icon"`

	Type        string           `json:"type,omitempty"`
	License     string           `json:"license,omitempty"`
	Language    string           `json:"language,omitempty"`
	VendorLink  string           `json:"vendor_link"`
	Locales     *json.RawMessage `json:"locales,omitempty"`
	Langs       *json.RawMessage `json:"langs,omitempty"`
	Platforms   *json.RawMessage `json:"platforms,omitempty"`
	Categories  *json.RawMessage `json:"categories,omitempty"`
	Developer   *json.RawMessage `json:"developer,omitempty"`
	Screenshots *json.RawMessage `json:"screenshots,omitempty"`
	Tags        *json.RawMessage `json:"tags,omitempty"`

	Frequency    string           `json:"frequency,omitempty"`
	DataTypes    *json.RawMessage `json:"data_types,omitempty"`
	Doctypes     *json.RawMessage `json:"doctypes,omitempty"`
	Fields       *json.RawMessage `json:"fields,omitempty"`
	Folders      *json.RawMessage `json:"folders,omitempty"`
	Messages     *json.RawMessage `json:"messages,omitempty"`
	OAuth        *json.RawMessage `json:"oauth,omitempty"`
	TimeInterval *json.RawMessage `json:"time_interval,omitempty"`

	Aggregator *json.RawMessage `json:"aggregator,omitempty"`

	Parameters    *json.RawMessage `json:"parameters,omitempty"`
	Notifications Notifications    `json:"notifications"`

	// OnDeleteAccount can be used to specify a file path which will be executed
	// when an account associated with the konnector is deleted.
	OnDeleteAccount string `json:"on_delete_account,omitempty"`

	DocSlug          string          `json:"slug"`
	DocState         State           `json:"state"`
	DocSource        string          `json:"source"`
	DocVersion       string          `json:"version"`
	DocPermissions   permissions.Set `json:"permissions"`
	AvailableVersion string          `json:"available_version,omitempty"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`

	Err string `json:"error,omitempty"`
	err error
}

// ID is part of the Manifest interface
func (m *KonnManifest) ID() string { return m.DocType() + "/" + m.DocSlug }

// Rev is part of the Manifest interface
func (m *KonnManifest) Rev() string { return m.DocRev }

// DocType is part of the Manifest interface
func (m *KonnManifest) DocType() string { return consts.Konnectors }

// Clone is part of the Manifest interface
func (m *KonnManifest) Clone() couchdb.Doc {
	cloned := *m

	cloned.DocPermissions = make(permissions.Set, len(m.DocPermissions))
	copy(cloned.DocPermissions, m.DocPermissions)

	cloned.Locales = cloneRawMessage(m.Locales)
	cloned.Langs = cloneRawMessage(m.Langs)
	cloned.Platforms = cloneRawMessage(m.Platforms)
	cloned.Categories = cloneRawMessage(m.Categories)
	cloned.Developer = cloneRawMessage(m.Developer)
	cloned.Screenshots = cloneRawMessage(m.Screenshots)
	cloned.Tags = cloneRawMessage(m.Tags)
	cloned.Parameters = cloneRawMessage(m.Parameters)

	cloned.DataTypes = cloneRawMessage(m.DataTypes)
	cloned.Doctypes = cloneRawMessage(m.Doctypes)
	cloned.Fields = cloneRawMessage(m.Fields)
	cloned.Folders = cloneRawMessage(m.Folders)
	cloned.Messages = cloneRawMessage(m.Messages)
	cloned.OAuth = cloneRawMessage(m.OAuth)
	cloned.TimeInterval = cloneRawMessage(m.TimeInterval)

	cloned.Aggregator = cloneRawMessage(m.Aggregator)

	cloned.Notifications = make(Notifications, len(m.Notifications))
	for k, v := range m.Notifications {
		props := (&v).Clone()
		cloned.Notifications[k] = *props
	}
	return &cloned
}

// SetID is part of the Manifest interface
func (m *KonnManifest) SetID(id string) {}

// SetRev is part of the Manifest interface
func (m *KonnManifest) SetRev(rev string) { m.DocRev = rev }

// SetSource is part of the Manifest interface
func (m *KonnManifest) SetSource(src *url.URL) { m.DocSource = src.String() }

// Source is part of the Manifest interface
func (m *KonnManifest) Source() string { return m.DocSource }

// Version is part of the Manifest interface
func (m *KonnManifest) Version() string { return m.DocVersion }

// Slug is part of the Manifest interface
func (m *KonnManifest) Slug() string { return m.DocSlug }

// State is part of the Manifest interface
func (m *KonnManifest) State() State { return m.DocState }

// LastUpdate is part of the Manifest interface
func (m *KonnManifest) LastUpdate() time.Time { return m.UpdatedAt }

// SetState is part of the Manifest interface
func (m *KonnManifest) SetState(state State) { m.DocState = state }

// SetVersion is part of the Manifest interface
func (m *KonnManifest) SetVersion(version string) { m.DocVersion = version }

// SetAvailableVersion is part of the Manifest interface
func (m *KonnManifest) SetAvailableVersion(version string) { m.AvailableVersion = version }

// AppType is part of the Manifest interface
func (m *KonnManifest) AppType() AppType { return Konnector }

// Permissions is part of the Manifest interface
func (m *KonnManifest) Permissions() permissions.Set {
	return m.DocPermissions
}

// SetError is part of the Manifest interface
func (m *KonnManifest) SetError(err error) {
	m.SetState(Errored)
	m.Err = err.Error()
	m.err = err
}

// Error is part of the Manifest interface
func (m *KonnManifest) Error() error { return m.err }

// Match is part of the Manifest interface
func (m *KonnManifest) Match(field, value string) bool {
	switch field {
	case "slug":
		return m.DocSlug == value
	case "state":
		return m.DocState == State(value)
	}
	return false
}

// ReadManifest is part of the Manifest interface
func (m *KonnManifest) ReadManifest(r io.Reader, slug, sourceURL string) (Manifest, error) {
	var newManifest KonnManifest
	if err := json.NewDecoder(r).Decode(&newManifest); err != nil {
		return nil, ErrBadManifest
	}

	newManifest.SetID(m.ID())
	newManifest.SetRev(m.Rev())
	newManifest.SetState(m.State())
	newManifest.CreatedAt = m.CreatedAt
	newManifest.DocSlug = slug
	newManifest.DocSource = sourceURL
	if newManifest.Parameters == nil {
		newManifest.Parameters = m.Parameters
	}

	return &newManifest, nil
}

// Create is part of the Manifest interface
func (m *KonnManifest) Create(db prefixer.Prefixer) error {
	m.CreatedAt = time.Now()
	m.UpdatedAt = time.Now()
	if err := couchdb.CreateNamedDocWithDB(db, m); err != nil {
		return err
	}
	_, err := permissions.CreateKonnectorSet(db, m.Slug(), m.Permissions())
	return err
}

// Update is part of the Manifest interface
func (m *KonnManifest) Update(db prefixer.Prefixer) error {
	m.UpdatedAt = time.Now()
	err := couchdb.UpdateDoc(db, m)
	if err != nil {
		return err
	}
	_, err = permissions.UpdateKonnectorSet(db, m.Slug(), m.Permissions())
	return err
}

// Delete is part of the Manifest interface
func (m *KonnManifest) Delete(db prefixer.Prefixer) error {
	err := permissions.DestroyKonnector(db, m.Slug())
	if err != nil && !couchdb.IsNotFoundError(err) {
		return err
	}
	return couchdb.DeleteDoc(db, m)
}

// GetKonnectorBySlug fetch the manifest of a konnector from the database given
// a slug.
func GetKonnectorBySlug(db prefixer.Prefixer, slug string) (*KonnManifest, error) {
	if slug == "" || !slugReg.MatchString(slug) {
		return nil, ErrInvalidSlugName
	}
	man := &KonnManifest{}
	err := couchdb.GetDoc(db, consts.Konnectors, consts.Konnectors+"/"+slug, man)
	if couchdb.IsNotFoundError(err) {
		return nil, ErrNotFound
	}
	if err != nil {
		return nil, err
	}
	return man, nil
}

// GetWebappBySlugAndUpdate fetch the KonnManifest and perform an update of
// the application if necessary and if the application was installed from the
// registry.
func GetKonnectorBySlugAndUpdate(db prefixer.Prefixer, slug string, copier Copier, registries []*url.URL) (*KonnManifest, error) {
	man, err := GetKonnectorBySlug(db, slug)
	if err != nil {
		return nil, err
	}
	return DoLazyUpdate(db, man, man.AvailableVersion, copier, registries).(*KonnManifest), nil
}

// ListKonnectors returns the list of installed konnectors applications.
//
// TODO: pagination
func ListKonnectors(db prefixer.Prefixer) ([]Manifest, error) {
	var docs []*KonnManifest
	req := &couchdb.AllDocsRequest{Limit: 100}
	err := couchdb.GetAllDocs(db, consts.Konnectors, req, &docs)
	if err != nil {
		return nil, err
	}
	mans := make([]Manifest, len(docs))
	for i, m := range docs {
		mans[i] = m
	}
	return mans, nil
}

var _ Manifest = &KonnManifest{}
<|endoftext|>


<commit_before>// +build appengine

package value

import (
	"errors"
	"fmt"
	"html/template"
	"net/http"
	"time"

	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"appengine/user"
)

func init() {
	http.HandleFunc("/_ah/value/admin", adminHandler)
	http.HandleFunc("/_ah/value/update", updateHandler)
}

var adminTmpl = template.Must(template.New("admin").Parse(`

Admin

{{range $key, $val := .}}
  <form action="/_ah/value/update" method="POST">

    <input type="hidden" name="delete_key" value="{{$key}}"></input>
    {{$key}}
    {{$val}}
    <input type="submit" value="Delete"></input>

  </form>
{{else}}

no values currently configured

{{end}}
<form action="/_ah/value/update" method="POST">

  <input type="text" name="key"></input>
  <input type="text" name="val"></input>
  <input type="submit" value="Add"></input>

</form>

`))

func adminHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	if !user.IsAdmin(c) {
		if user.Current(c) == nil {
			loginURL, _ := user.LoginURL(c, "/_ah/value/admin")
			fmt.Fprintf(w, "Log in", loginURL)
		} else {
			http.Error(w, "Forbidden", http.StatusForbidden)
		}
		return
	}
	if r.Method != "GET" {
		return
	}
	v := map[string]string{}
	q := datastore.NewQuery(Kind)
	for t := q.Run(c); ; {
		var e e
		k, err := t.Next(&e)
		if err == datastore.Done {
			break
		} else if err != nil {
			c.Errorf("%v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		v[k.StringID()] = e.Value
	}
	if err := adminTmpl.Execute(w, v); err != nil {
		c.Warningf("error executing template: %v", err)
	}
}

func updateHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Redirect(w, r, "/_ah/value/admin", http.StatusSeeOther)
		return
	}
	c := appengine.NewContext(r)
	if !user.IsAdmin(c) {
		http.Error(w, "Forbidden", http.StatusForbidden)
		return
	}

	deleteKey := r.FormValue("delete_key")
	if deleteKey != "" {
		if err := memcache.Delete(c, deleteKey); err != nil && err != memcache.ErrCacheMiss {
			c.Errorf("error deleting %q from memcache: %v", deleteKey, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		k := datastore.NewKey(c, Kind, deleteKey, 0, nil)
		if err := datastore.Delete(c, k); err != nil {
			c.Errorf("error deleting %q from datastore: %v", deleteKey, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	} else {
		key := r.FormValue("key")
		val := r.FormValue("val")
		if err := set(c, key, val); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	// TODO: remove this hack.
	time.Sleep(time.Millisecond * 500)
	http.Redirect(w, r, "/_ah/value/admin", http.StatusSeeOther)
}

func set(c appengine.Context, key string, val string) error {
	if _, err := memcache.Get(c, key); err != memcache.ErrCacheMiss {
		return errors.New("key found in memcache")
	}
	return datastore.RunInTransaction(c, func(tc appengine.Context) error {
		// Fail if the value is already stored.
		k := datastore.NewKey(c, Kind, key, 0, nil)
		if err := datastore.Get(tc, k, nil); err == nil {
			return errors.New("key found in datastore")
		} else if err == datastore.ErrNoSuchEntity {
			// Okay, good. Keep going.
		} else if err != nil {
			return err
		}

		// Put the value in the datastore.
		_, err := datastore.Put(tc, k, &e{Value: val})
		return err
	}, nil)
}
<commit_msg>admin UI for issuing a Get/GetMulti<commit_after>// +build appengine

package value

import (
	"errors"
	"fmt"
	"html/template"
	"net/http"
	"strings"
	"time"

	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"appengine/user"
)

func init() {
	http.HandleFunc("/_ah/value/admin", adminHandler)
	http.HandleFunc("/_ah/value/update", updateHandler)
}

var adminTmpl = template.Must(template.New("admin").Parse(`

Admin

{{range $key, $val := .}}
  <form action="/_ah/value/update" method="POST">

    <input type="hidden" name="delete_key" value="{{$key}}"></input>
    {{$key}}
    {{$val}}
    <input type="submit" value="Delete"></input>

  </form>
{{else}}

no values currently configured

{{end}}
<form action="/_ah/value/update" method="POST">

  <input type="text" name="key"></input>
  <input type="text" name="val"></input>
  <input type="submit" value="Add"></input>

</form>

`))

func adminHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	if !user.IsAdmin(c) {
		if user.Current(c) == nil {
			loginURL, _ := user.LoginURL(c, "/_ah/value/admin")
			fmt.Fprintf(w, "Log in", loginURL)
		} else {
			http.Error(w, "Forbidden", http.StatusForbidden)
		}
		return
	}
	if r.Method != "GET" {
		return
	}

	val := r.FormValue("val")
	if val != "" {
		keys := strings.Split(val, ",")
		m := GetMulti(c, keys...)
		for _, v := range keys {
			fmt.Fprintf(w, "%s: %q\n", v, m[v])
		}
		return
	}

	v := map[string]string{}
	q := datastore.NewQuery(Kind)
	for t := q.Run(c); ; {
		var e e
		k, err := t.Next(&e)
		if err == datastore.Done {
			break
		} else if err != nil {
			c.Errorf("%v", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		v[k.StringID()] = e.Value
	}
	if err := adminTmpl.Execute(w, v); err != nil {
		c.Warningf("error executing template: %v", err)
	}
}

func updateHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Redirect(w, r, "/_ah/value/admin", http.StatusSeeOther)
		return
	}
	c := appengine.NewContext(r)
	if !user.IsAdmin(c) {
		http.Error(w, "Forbidden", http.StatusForbidden)
		return
	}

	deleteKey := r.FormValue("delete_key")
	if deleteKey != "" {
		if err := memcache.Delete(c, deleteKey); err != nil && err != memcache.ErrCacheMiss {
			c.Errorf("error deleting %q from memcache: %v", deleteKey, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		k := datastore.NewKey(c, Kind, deleteKey, 0, nil)
		if err := datastore.Delete(c, k); err != nil {
			c.Errorf("error deleting %q from datastore: %v", deleteKey, err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	} else {
		key := r.FormValue("key")
		val := r.FormValue("val")
		if err := set(c, key, val); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	// TODO: remove this hack.
	time.Sleep(time.Millisecond * 500)
	http.Redirect(w, r, "/_ah/value/admin", http.StatusSeeOther)
}

func set(c appengine.Context, key string, val string) error {
	if _, err := memcache.Get(c, key); err != memcache.ErrCacheMiss {
		return errors.New("key found in memcache")
	}
	return datastore.RunInTransaction(c, func(tc appengine.Context) error {
		// Fail if the value is already stored.
		k := datastore.NewKey(c, Kind, key, 0, nil)
		if err := datastore.Get(tc, k, nil); err == nil {
			return errors.New("key found in datastore")
		} else if err == datastore.ErrNoSuchEntity {
			// Okay, good. Keep going.
		} else if err != nil {
			return err
		}

		// Put the value in the datastore.
		_, err := datastore.Put(tc, k, &e{Value: val})
		return err
	}, nil)
}
<|endoftext|>


<commit_before>//
// Code used in populating JSON objects to generating Keybase-style
// signatures.
//
package libkb

import (
	"fmt"
	"github.com/keybase/go-jsonw"
	"time"
)

func ClientId() *jsonw.Wrapper {
	ret := jsonw.NewDictionary()
	ret.SetKey("version", jsonw.NewString(CLIENT_VERSION))
	ret.SetKey("name", jsonw.NewString(GO_CLIENT_ID))
	return ret
}

func (u *User) ToTrackingStatementKey(errp *error) *jsonw.Wrapper {
	ret := jsonw.NewDictionary()
	if key, err := u.GetActiveKey(); err != nil {
		*errp = fmt.Errorf("User %s doesn't have an active key: %s",
			u.name, err.Error())
	} else if fp, err := u.GetActivePgpFingerprint(); err != nil {
		*errp = fmt.Errorf("User %s doesn't have an active fingerprint: %s",
			u.name, err.Error())
	} else {
		ret.SetKey("kid", jsonw.NewString(key.GetKid().ToString()))
		ret.SetKey("key_fingerprint", jsonw.NewString(fp.ToString()))
	}
	return ret
}

func (u *User) ToTrackingStatementBasics(errp *error) *jsonw.Wrapper {
	ret := jsonw.NewDictionary()
	ret.SetKey("username", jsonw.NewString(u.name))
	if last_id_change, err := u.basics.AtKey("last_id_change").GetInt(); err == nil {
		ret.SetKey("last_id_change", jsonw.NewInt(last_id_change))
	}
	if id_version, err := u.basics.AtKey("id_version").GetInt(); err == nil {
		ret.SetKey("id_version", jsonw.NewInt(id_version))
	}
	return ret
}

func (u *User) ToTrackingStatementSeqTail() *jsonw.Wrapper {
	return u.sigs.AtKey("last")
}

func (u *User) ToTrackingStatement(w *jsonw.Wrapper) (err error) {

	track := jsonw.NewDictionary()
	track.SetKey("key", u.ToTrackingStatementKey(&err))
	track.SetKey("seq_tail", u.ToTrackingStatementSeqTail())
	track.SetKey("basics", u.ToTrackingStatementBasics(&err))
	track.SetKey("id", jsonw.NewString(string(u.id)))
	track.SetKey("remote_key_proofs", u.IdTable.ToTrackingStatement())

	if err != nil {
		return
	}

	w.SetKey("type", jsonw.NewString("track"))
	w.SetKey("version", jsonw.NewInt(TRACK_STATEMENT_V1))
	w.SetKey("track", track)
	return
}

func (u *User) ToKeyStanza() (*jsonw.Wrapper, error) {
	ret := jsonw.NewDictionary()
	ret.SetKey("uid", jsonw.NewString(string(u.id)))
	ret.SetKey("username", jsonw.NewString(u.name))
	ret.SetKey("host", jsonw.NewString(CANONICAL_HOST))
	if fp, err := u.GetActivePgpFingerprint(); err != nil {
		return nil, err
	} else {
		ret.SetKey("fingerprint", jsonw.NewString(fp.ToString()))
		ret.SetKey("key_id", jsonw.NewString(fp.ToKeyId()))
	}
	return ret, nil
}

func (s *SocialProofChainLink) ToTrackingStatement() (*jsonw.Wrapper, error) {
	ret := s.BaseToTrackingStatement()
	err := remoteProofToTrackingStatement(s, ret)
	if err != nil {
		ret = nil
	}
	return ret, err
}

func (idt *IdentityTable) ToTrackingStatement() *jsonw.Wrapper {
	v := idt.activeProofs
	tmp := make([]*jsonw.Wrapper, 0, len(v))
	for _, proof := range v {
		if d, err := proof.ToTrackingStatement(); err != nil {
			G.Log.Warning("Problem with a proof: %s", err.Error())
		} else if d != nil {
			tmp = append(tmp, d)
		}
	}
	ret := jsonw.NewArray(len(tmp))
	for i, d := range tmp {
		ret.SetIndex(i, d)
	}
	return ret
}

func (g *GenericChainLink) BaseToTrackingStatement() *jsonw.Wrapper {
	ret := jsonw.NewDictionary()
	ret.SetKey("curr", jsonw.NewString(g.id.ToString()))
	ret.SetKey("sig_id", jsonw.NewString(g.GetSigId().ToString(true)))

	rkp := jsonw.NewDictionary()
	ret.SetKey("remote_key_proof", rkp)
	rkp.SetKey("state", jsonw.NewInt(g.GetProofState()))

	prev := g.GetPrev()
	var prev_val *jsonw.Wrapper
	if prev == nil {
		prev_val = jsonw.NewNil()
	} else {
		prev_val = jsonw.NewString(prev.ToString())
	}

	ret.SetKey("prev", prev_val)
	ret.SetKey("ctime", jsonw.NewInt64(g.unpacked.ctime))
	ret.SetKey("etime", jsonw.NewInt64(g.unpacked.etime))
	return ret
}

func remoteProofToTrackingStatement(s RemoteProofChainLink, base *jsonw.Wrapper) error {
	typ_s := s.TableKey()
	if i, found := REMOTE_SERVICE_TYPES[typ_s]; !found {
		return fmt.Errorf("No service type found for '%s' in proof %d",
			typ_s, s.GetSeqno())
	} else {
		base.AtKey("remote_key_proof").SetKey("proof_type", jsonw.NewInt(i))
	}
	base.AtKey("remote_key_proof").SetKey("check_data_json", s.CheckDataJson())
	base.SetKey("sig_type", jsonw.NewInt(SIG_TYPE_REMOTE_PROOF))
	return nil
}

func (u *User) ProofMetadata() (ret *jsonw.Wrapper, err error) {

	var seqno int
	var prev_s string
	var key, prev *jsonw.Wrapper

	last := u.sigs.AtKey("last")
	last.AtKey("seqno").GetIntVoid(&seqno, &err)
	last.AtKey("payload_hash").GetStringVoid(&prev_s, &err)
	if err != nil {
		seqno = 1
		prev = jsonw.NewNil()
		err = nil
	} else {
		seqno++
		prev = jsonw.NewString(prev_s)
	}

	ret = jsonw.NewDictionary()
	ret.SetKey("tag", jsonw.NewString("signature"))
	ret.SetKey("ctime", jsonw.NewInt64(time.Now().Unix()))
	ret.SetKey("expire_in", jsonw.NewInt(SIG_EXPIRE_IN))
	ret.SetKey("seqno", jsonw.NewInt(seqno))
	ret.SetKey("prev", prev)

	body := jsonw.NewDictionary()
	key, err = u.ToKeyStanza()
	if err != nil {
		ret = nil
		return
	}
	body.SetKey("key", key)
	ret.SetKey("body", body)

	return
}

func (u1 *User) TrackingProofFor(u2 *User) (ret *jsonw.Wrapper, err error) {
	ret, err = u1.ProofMetadata()
	if err == nil {
		err = u1.ToTrackingStatement(ret.AtKey("body"))
	}
	return
}
<commit_msg>bugfixes<commit_after>//
// Code used in populating JSON objects to generating Keybase-style
// signatures.
//
package libkb

import (
	"fmt"
	"github.com/keybase/go-jsonw"
	"time"
)

func ClientId() *jsonw.Wrapper {
	ret := jsonw.NewDictionary()
	ret.SetKey("version", jsonw.NewString(CLIENT_VERSION))
	ret.SetKey("name", jsonw.NewString(GO_CLIENT_ID))
	return ret
}

func (u *User) ToTrackingStatementKey(errp *error) *jsonw.Wrapper {
	ret := jsonw.NewDictionary()
	if key, err := u.GetActiveKey(); err != nil {
		*errp = fmt.Errorf("User %s doesn't have an active key: %s",
			u.name, err.Error())
	} else if fp, err := u.GetActivePgpFingerprint(); err != nil {
		*errp = fmt.Errorf("User %s doesn't have an active fingerprint: %s",
			u.name, err.Error())
	} else {
		ret.SetKey("kid", jsonw.NewString(key.GetKid().ToString()))
		ret.SetKey("key_fingerprint", jsonw.NewString(fp.ToString()))
	}
	return ret
}

func (u *User) ToTrackingStatementBasics(errp *error) *jsonw.Wrapper {
	ret := jsonw.NewDictionary()
	ret.SetKey("username", jsonw.NewString(u.name))
	if last_id_change, err := u.basics.AtKey("last_id_change").GetInt(); err == nil {
		ret.SetKey("last_id_change", jsonw.NewInt(last_id_change))
	}
	if id_version, err := u.basics.AtKey("id_version").GetInt(); err == nil {
		ret.SetKey("id_version", jsonw.NewInt(id_version))
	}
	return ret
}

func (u *User) ToTrackingStatementSeqTail() *jsonw.Wrapper {
	return u.sigs.AtKey("last")
}

func (u *User) ToTrackingStatement(w *jsonw.Wrapper) (err error) {

	track := jsonw.NewDictionary()
	track.SetKey("key", u.ToTrackingStatementKey(&err))
	track.SetKey("seq_tail", u.ToTrackingStatementSeqTail())
	track.SetKey("basics", u.ToTrackingStatementBasics(&err))
	track.SetKey("id", jsonw.NewString(string(u.id)))
	track.SetKey("remote_key_proofs", u.IdTable.ToTrackingStatement())

	if err != nil {
		return
	}

	w.SetKey("type", jsonw.NewString("track"))
	w.SetKey("version", jsonw.NewInt(TRACK_STATEMENT_V1))
	w.SetKey("track", track)
	return
}

func (u *User) ToKeyStanza() (*jsonw.Wrapper, error) {
	ret := jsonw.NewDictionary()
	ret.SetKey("uid", jsonw.NewString(string(u.id)))
	ret.SetKey("username", jsonw.NewString(u.name))
	ret.SetKey("host", jsonw.NewString(CANONICAL_HOST))
	if fp, err := u.GetActivePgpFingerprint(); err != nil {
		return nil, err
	} else {
		ret.SetKey("fingerprint", jsonw.NewString(fp.ToString()))
		ret.SetKey("key_id", jsonw.NewString(fp.ToKeyId()))
	}
	return ret, nil
}

func (s *SocialProofChainLink) ToTrackingStatement() (*jsonw.Wrapper, error) {
	ret := s.BaseToTrackingStatement()
	err := remoteProofToTrackingStatement(s, ret)
	if err != nil {
		ret = nil
	}
	return ret, err
}

func (idt *IdentityTable) ToTrackingStatement() *jsonw.Wrapper {
	v := idt.activeProofs
	tmp := make([]*jsonw.Wrapper, 0, len(v))
	for _, proof := range v {
		if d, err := proof.ToTrackingStatement(); err != nil {
			G.Log.Warning("Problem with a proof: %s", err.Error())
		} else if d != nil {
			tmp = append(tmp, d)
		}
	}
	ret := jsonw.NewArray(len(tmp))
	for i, d := range tmp {
		ret.SetIndex(i, d)
	}
	return ret
}

func (g *GenericChainLink) BaseToTrackingStatement() *jsonw.Wrapper {
	ret := jsonw.NewDictionary()
	ret.SetKey("curr", jsonw.NewString(g.id.ToString()))
	ret.SetKey("sig_id", jsonw.NewString(g.GetSigId().ToString(true)))

	rkp := jsonw.NewDictionary()
	ret.SetKey("remote_key_proof", rkp)
	rkp.SetKey("state", jsonw.NewInt(g.GetProofState()))

	prev := g.GetPrev()
	var prev_val *jsonw.Wrapper
	if prev == nil {
		prev_val = jsonw.NewNil()
	} else {
		prev_val = jsonw.NewString(prev.ToString())
	}

	ret.SetKey("prev", prev_val)
	ret.SetKey("ctime", jsonw.NewInt64(g.unpacked.ctime))
	ret.SetKey("etime", jsonw.NewInt64(g.unpacked.etime))
	return ret
}

func remoteProofToTrackingStatement(s RemoteProofChainLink, base *jsonw.Wrapper) error {
	typ_s := s.TableKey()
	if i, found := REMOTE_SERVICE_TYPES[typ_s]; !found {
		return fmt.Errorf("No service type found for '%s' in proof %d",
			typ_s, s.GetSeqno())
	} else {
		base.AtKey("remote_key_proof").SetKey("proof_type", jsonw.NewInt(i))
	}
	base.AtKey("remote_key_proof").SetKey("check_data_json", s.CheckDataJson())
	base.SetKey("sig_type", jsonw.NewInt(SIG_TYPE_REMOTE_PROOF))
	return nil
}

func (u *User) ProofMetadata() (ret *jsonw.Wrapper, err error) {

	var seqno int
	var prev_s string
	var key, prev *jsonw.Wrapper

	last := u.sigs.AtKey("last")
	last.AtKey("seqno").GetIntVoid(&seqno, &err)
	last.AtKey("payload_hash").GetStringVoid(&prev_s, &err)
	if err != nil {
		seqno = 1
		prev = jsonw.NewNil()
		err = nil
	} else {
		seqno++
		prev = jsonw.NewString(prev_s)
	}

	ret = jsonw.NewDictionary()
	ret.SetKey("tag", jsonw.NewString("signature"))
	ret.SetKey("ctime", jsonw.NewInt64(time.Now().Unix()))
	ret.SetKey("expire_in", jsonw.NewInt(SIG_EXPIRE_IN))
	ret.SetKey("seqno", jsonw.NewInt(seqno))
	ret.SetKey("prev", prev)

	body := jsonw.NewDictionary()
	key, err = u.ToKeyStanza()
	if err != nil {
		ret = nil
		return
	}
	body.SetKey("key", key)
	ret.SetKey("body", body)

	return
}

func (u1 *User) TrackingProofFor(u2 *User) (ret *jsonw.Wrapper, err error) {
	ret, err = u1.ProofMetadata()
	if err == nil {
		err = u2.ToTrackingStatement(ret.AtKey("body"))
	}
	return
}
<|endoftext|>


<commit_before>package DAO

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"io"
)

// encrypt method is aes256
const (
	TOKEN_LENGTH = 32
)

var (
	KEY = []byte("XY0nG86PSRJqMGz957Yza1D34393MPII") // 32 bytes
)

func PerformLogin(uname string, pwd string) (string, bool) {
	// this method is not finished
	userDao := NewUserDAO()
	user := userDao.GetUserByName(uname)
	hashpwd, err := decrypt(user.Pwd, KEY)
	if err != nil {
		return "", false
	}
	if pwd == hashpwd {
		token, err := TokenGenerator()
		if err != nil {
			return token, false
		} else {
			return token, true
		}
	} else {
		return "wrong password", false
	}
}

func PerformLogout(token string) {
	// todo this is a sub
}

func TokenGenerator() (string, error) {
	// change the length of the generated random string here
	size := TOKEN_LENGTH

	rb := make([]byte, size)
	_, err := rand.Read(rb)
	if err != nil {
		return "error", errors.New("cannot generate token for user")
	}
	rs := base64.URLEncoding.EncodeToString(rb)
	return rs, nil
}

func ValidateToken(token string) bool {
	return true
}

func encrypt(str string, key []byte) (string, error) {
	text := []byte(str)
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	b := base64.StdEncoding.EncodeToString(text)
	ciphertext := make([]byte, aes.BlockSize+len(b))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return "", err
	}
	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
	return string(ciphertext[:]), nil
}

func decrypt(str string, key []byte) (string, error) {
	text := []byte(str)
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	if len(text) < aes.BlockSize {
		return "", errors.New("ciphertext too short")
	}
	iv := text[:aes.BlockSize]
	text = text[aes.BlockSize:]
	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(text, text)
	data, err := base64.StdEncoding.DecodeString(string(text))
	if err != nil {
		return "", err
	}
	return string(data[:]), nil
}
<commit_msg>init commit of session management<commit_after>package DAO

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"io"
	"net/http"
	"net/url"
	"sync"
)

// encrypt method is aes256
const (
	TOKEN_LENGTH = 32
)

var (
	KEY            = []byte("XY0nG86PSRJqMGz957Yza1D34393MPII") // 32 bytes
	provides       = make(map[string]Provider)
	globalSessions *Manager
)

type Manager struct {
	cookieName  string
	lock        sync.Mutex
	provider    Provider
	maxlifetime int64
}

func NewManager(providerName, cookieName string, maklifetime int64) (*Manager, error) {
	return nil, nil
}

type Provider interface {
	SessionInit(sid string) (Session, error)
	SessionRead(sid string) (Session, error)
	SessionDestroy(sid string) error
	SessionGC(maxLifeTime int64)
}

type Session interface {
	Set(key, value interface{}) error //set session value
	Get(key interface{}) interface{}  //get session value
	Delete(key interface{}) error     //delete session value
	SessionID() string                //back current sessionID
}

func Register(name string, provider Provider) {
	if provider == nil {
		panic("session: Register provide is nil")
	}
	if _, dup := provides[name]; dup {
		panic("session: Register called twice for provider" + name)
	}
	provides[name] = provider
}

func (manager *Manager) sessionId() (string, error) {
	// change the length of the generated random string here
	size := TOKEN_LENGTH

	rb := make([]byte, size)
	_, err := rand.Read(rb)
	if err != nil {
		return "error", errors.New("cannot generate token for user")
	}
	rs := base64.URLEncoding.EncodeToString(rb)
	return rs, nil
}

func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session Session) {
	manager.lock.Lock()
	defer manager.lock.Unlock()
	cookie, err := r.Cookie(manager.cookieName)
	if err != nil || cookie.Value != "" {
		sid := manager.sessionId()
		session, _ = manager.provider.SessionInit(sid)
		cookie := http.Cookie{Name: manager.cookieName, value: url.QueryEscape(sid), Path: "/", HttpOnly: true, MaxAge: int(manager.maxlifetime)}
		http.SetCookie(w, &cookie)
	} else {
		sid, _ := url.QueryEscape(cookie.Value)
		session, _ = manager.provider.SessionRead(sid)
	}

	return
}

func login(w http.ResponseWriter, r *http.Request) {
	session := globalSessions.SessionStart(w, r)
	r.ParseForm()
	session.Set("username", r.Form["username"])
	http.Redirect(w, r, "/", 302)
}

func PerformLogin(uname string, pwd string) (string, bool) {
	// this method is not finished
	userDao := NewUserDAO()
	manager, _ := NewManager("providerName", "cookieName", 0)
	user := userDao.GetUserByName(uname)
	hashpwd, err := decrypt(user.Pwd, KEY)
	if err != nil {
		return "", false
	}
	if pwd == hashpwd {
		token, err := manager.sessionId()
		if err != nil {
			return token, false
		} else {
			return token, true
		}
	} else {
		return "wrong password", false
	}
}

func PerformLogout(token string) {
	// todo this is a sub
}

func ValidateToken(token string) bool {
	return true
}

func encrypt(str string, key []byte) (string, error) {
	text := []byte(str)
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	b := base64.StdEncoding.EncodeToString(text)
	ciphertext := make([]byte, aes.BlockSize+len(b))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return "", err
	}
	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
	return string(ciphertext[:]), nil
}

func decrypt(str string, key []byte) (string, error) {
	text := []byte(str)
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	if len(text) < aes.BlockSize {
		return "", errors.New("ciphertext too short")
	}
	iv := text[:aes.BlockSize]
	text = text[aes.BlockSize:]
	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(text, text)
	data, err := base64.StdEncoding.DecodeString(string(text))
	if err != nil {
		return "", err
	}
	return string(data[:]), nil
}

func init() {
	globalSessions, _ = NewManager("memory", "gosessionid", 3600)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/hawx/riviera-admin/actions"
	"github.com/hawx/riviera-admin/views"
	"github.com/hawx/alexandria/web/persona"

	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
)

const HELP = `Usage: riviera-admin [options]

  An admin panel for riviera

    --port <num>       # Port to bind to (default: 8081)
    --socket <path>    # Serve using a unix socket instead
    --riviera <url>    # Url to riviera (default: http://localhost:8080/)

    --audience <host>  # Domain site is running under (default: localhost)
    --user <email>     # User who can access the admin panel
    --secret <str>     # String to use as cookie secret

    --help             # Display help message
`

var (
	port     = flag.String("port", "8081", "")
	socket   = flag.String("socket", "", "")
	riviera  = flag.String("riviera", "http://localhost:8080/", "")

	audience = flag.String("audience", "localhost", "")
	user     = flag.String("user", "", "")
	secret   = flag.String("secret", "some-secret", "")

	help     = flag.Bool("help", false, "")
)

var Login = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Type", "text/html")
	views.Login.Execute(w, struct{}{})
})

var List = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get(*riviera + "-/list")
	if err != nil {
		log.Print("list", err)
		w.WriteHeader(500)
		return
	}

	var list []string
	json.NewDecoder(resp.Body).Decode(&list)

	w.Header().Add("Content-Type", "text/html")
	views.Index.Execute(w, list)
})

var Subscribe = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue("url")

	err := actions.Subscribe(*riviera, url)
	if err != nil {
		log.Println("subscribe:", err)
		w.WriteHeader(500)
		return
	}

	if r.FormValue("redirect") == "origin" {
		http.Redirect(w, r, url, 301)
		return
	}

	http.Redirect(w, r, "/", 301)
})

var Unsubscribe = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	err := actions.Unsubscribe(*riviera, r.FormValue("url"))
	if err != nil {
		log.Println(err)
		return
	}

	http.Redirect(w, r, "/", 301)
})

func main() {
	flag.Parse()

	if *help {
		fmt.Println(HELP)
		return
	}

	store := persona.NewStore(*secret)
	persona := persona.New(store, *audience, []string{*user})

	r := mux.NewRouter()

	r.Methods("GET").Path("/").Handler(persona.Switch(List, Login))
	r.Methods("GET").Path("/subscribe").Handler(persona.Protect(Subscribe))
	r.Methods("GET").Path("/unsubscribe").Handler(persona.Protect(Unsubscribe))
	r.Methods("POST").Path("/sign-in").Handler(persona.SignIn)
	r.Methods("GET").Path("/sign-out").Handler(persona.SignOut)

	http.Handle("/", r)

	if *socket == "" {
		log.Println("listening on port :" + *port)
		log.Fatal(http.ListenAndServe(":"+*port, context.ClearHandler(http.DefaultServeMux)))

	} else {
		l, err := net.Listen("unix", *socket)
		if err != nil {
			log.Fatal(err)
		}

		defer l.Close()

		go func() {
			log.Println("listening on", *socket)
			log.Fatal(http.Serve(l, context.ClearHandler(http.DefaultServeMux)))
		}()

		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt, os.Kill)

		s := <-c
		log.Printf("caught %s: shutting down", s)
	}
}
<commit_msg>Move interrupt handling out of if<commit_after>package main

import (
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/hawx/riviera-admin/actions"
	"github.com/hawx/riviera-admin/views"
	"github.com/hawx/alexandria/web/persona"

	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
)

const HELP = `Usage: riviera-admin [options]

  An admin panel for riviera

    --port <num>       # Port to bind to (default: 8081)
    --socket <path>    # Serve using a unix socket instead
    --riviera <url>    # Url to riviera (default: http://localhost:8080/)

    --audience <host>  # Domain site is running under (default: localhost)
    --user <email>     # User who can access the admin panel
    --secret <str>     # String to use as cookie secret

    --help             # Display help message
`

var (
	port     = flag.String("port", "8081", "")
	socket   = flag.String("socket", "", "")
	riviera  = flag.String("riviera", "http://localhost:8080/", "")

	audience = flag.String("audience", "localhost", "")
	user     = flag.String("user", "", "")
	secret   = flag.String("secret", "some-secret", "")

	help     = flag.Bool("help", false, "")
)

var Login = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Type", "text/html")
	views.Login.Execute(w, struct{}{})
})

var List = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	resp, err := http.Get(*riviera + "-/list")
	if err != nil {
		log.Print("list", err)
		w.WriteHeader(500)
		return
	}

	var list []string
	json.NewDecoder(resp.Body).Decode(&list)

	w.Header().Add("Content-Type", "text/html")
	views.Index.Execute(w, list)
})

var Subscribe = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	url := r.FormValue("url")

	err := actions.Subscribe(*riviera, url)
	if err != nil {
		log.Println("subscribe:", err)
		w.WriteHeader(500)
		return
	}

	if r.FormValue("redirect") == "origin" {
		http.Redirect(w, r, url, 301)
		return
	}

	http.Redirect(w, r, "/", 301)
})

var Unsubscribe = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	err := actions.Unsubscribe(*riviera, r.FormValue("url"))
	if err != nil {
		log.Println(err)
		return
	}

	http.Redirect(w, r, "/", 301)
})

func main() {
	flag.Parse()

	if *help {
		fmt.Println(HELP)
		return
	}

	store := persona.NewStore(*secret)
	persona := persona.New(store, *audience, []string{*user})

	r := mux.NewRouter()

	r.Methods("GET").Path("/").Handler(persona.Switch(List, Login))
	r.Methods("GET").Path("/subscribe").Handler(persona.Protect(Subscribe))
	r.Methods("GET").Path("/unsubscribe").Handler(persona.Protect(Unsubscribe))
	r.Methods("POST").Path("/sign-in").Handler(persona.SignIn)
	r.Methods("GET").Path("/sign-out").Handler(persona.SignOut)

	http.Handle("/", r)

	if *socket == "" {
		go func() {
			log.Println("listening on port :" + *port)
			log.Fatal(http.ListenAndServe(":"+*port, context.ClearHandler(http.DefaultServeMux)))
		}()

	} else {
		l, err := net.Listen("unix", *socket)
		if err != nil {
			log.Fatal(err)
		}

		defer l.Close()

		go func() {
			log.Println("listening on", *socket)
			log.Fatal(http.Serve(l, context.ClearHandler(http.DefaultServeMux)))
		}()
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)

	s := <-c
	log.Printf("caught %s: shutting down", s)
}
<|endoftext|>


<commit_before>// Package logfile manages log files.
package logfile

import (
	"context"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	logging "github.com/op/go-logging"
	"github.com/pkg/errors"
	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/kopia/kopia/cli"
	"github.com/kopia/kopia/internal/ospath"
	repologging "github.com/kopia/kopia/repo/logging"
)

var fileLogFormat = logging.MustStringFormatter(
	`%{time:2006-01-02 15:04:05.000} %{level:.1s} [%{shortfile}] %{message}`)

var consoleLogFormat = logging.MustStringFormatter(
	`%{color}%{time:15:04:05.000} [%{module}] %{message}%{color:reset}`)

var logLevels = []string{"debug", "info", "warning", "error"}
var (
	logFile        = cli.App().Flag("log-file", "Log file name.").String()
	logDir         = cli.App().Flag("log-dir", "Directory where log files should be written.").Envar("KOPIA_LOG_DIR").Default(ospath.LogsDir()).String()
	logDirMaxFiles = cli.App().Flag("log-dir-max-files", "Maximum number of log files to retain").Envar("KOPIA_LOG_DIR_MAX_FILES").Default("1000").Hidden().Int()
	logDirMaxAge   = cli.App().Flag("log-dir-max-age", "Maximum age of log files to retain").Envar("KOPIA_LOG_DIR_MAX_AGE").Hidden().Duration()
	logLevel       = cli.App().Flag("log-level", "Console log level").Default("info").Enum(logLevels...)
	fileLogLevel   = cli.App().Flag("file-log-level", "File log level").Default("info").Enum(logLevels...)
)

var log = repologging.GetContextLoggerFunc("kopia")

const (
	logFileNamePrefix = "kopia-"
	logFileNameSuffix = ".log"
)

// Initialize is invoked as part of command execution to create log file just before it's needed.
func Initialize(ctx *kingpin.ParseContext) error {
	var logBackends []logging.Backend

	var logFileName, symlinkName string

	if lfn := *logFile; lfn != "" {
		var err error

		logFileName, err = filepath.Abs(lfn)
		if err != nil {
			return err
		}
	}

	var shouldSweepLogs bool

	if logFileName == "" && *logDir != "" {
		logBaseName := fmt.Sprintf("%v%v-%v%v", logFileNamePrefix, time.Now().Format("20060102-150405"), os.Getpid(), logFileNameSuffix)
		logFileName = filepath.Join(*logDir, logBaseName)
		symlinkName = "kopia.latest.log"

		if *logDirMaxAge > 0 || *logDirMaxFiles > 0 {
			shouldSweepLogs = true
		}
	}

	if logFileName != "" {
		logFileDir := filepath.Dir(logFileName)
		logFileBaseName := filepath.Base(logFileName)

		if err := os.MkdirAll(logFileDir, 0o700); err != nil {
			fmt.Fprintln(os.Stderr, "Unable to create logs directory:", err)
		}

		logBackends = append(
			logBackends,
			levelFilter(
				*fileLogLevel,
				logging.NewBackendFormatter(
					&onDemandBackend{
						logDir:          logFileDir,
						logFileBaseName: logFileBaseName,
						symlinkName:     symlinkName,
					}, fileLogFormat)))
	}

	logBackends = append(logBackends,
		levelFilter(
			*logLevel,
			logging.NewBackendFormatter(
				logging.NewLogBackend(os.Stderr, "", 0),
				consoleLogFormat)))

	logging.SetBackend(logBackends...)

	if shouldSweepLogs {
		go sweepLogDir(context.TODO(), *logDir, *logDirMaxFiles, *logDirMaxAge)
	}

	return nil
}

func sweepLogDir(ctx context.Context, dirname string, maxCount int, maxAge time.Duration) {
	var timeCutoff time.Time
	if maxAge > 0 {
		timeCutoff = time.Now().Add(-maxAge)
	}

	if maxCount == 0 {
		maxCount = math.MaxInt32
	}

	entries, err := ioutil.ReadDir(dirname)
	if err != nil {
		log(ctx).Warningf("unable to read log directory: %v", err)
		return
	}

	sort.Slice(entries, func(i, j int) bool {
		return entries[i].ModTime().After(entries[j].ModTime())
	})

	cnt := 0

	for _, e := range entries {
		if !strings.HasPrefix(e.Name(), logFileNamePrefix) {
			continue
		}

		if !strings.HasSuffix(e.Name(), logFileNameSuffix) {
			continue
		}

		cnt++

		if cnt > maxCount || e.ModTime().Before(timeCutoff) {
			if err = os.Remove(filepath.Join(dirname, e.Name())); err != nil {
				log(ctx).Warningf("unable to remove log file: %v", err)
			}
		}
	}
}

func levelFilter(level string, writer logging.Backend) logging.Backend {
	l := logging.AddModuleLevel(writer)

	switch level {
	case "debug":
		l.SetLevel(logging.DEBUG, "")
	case "info":
		l.SetLevel(logging.INFO, "")
	case "warning":
		l.SetLevel(logging.WARNING, "")
	case "error":
		l.SetLevel(logging.ERROR, "")
	default:
		l.SetLevel(logging.CRITICAL, "")
	}

	return l
}

type onDemandBackend struct {
	logDir          string
	logFileBaseName string
	symlinkName     string

	backend logging.Backend
	once    sync.Once
}

func (w *onDemandBackend) Log(level logging.Level, depth int, rec *logging.Record) error {
	w.once.Do(func() {
		lf := filepath.Join(w.logDir, w.logFileBaseName)
		f, err := os.Create(lf)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unable to open log file: %v\n", err)
			return
		}

		w.backend = logging.NewLogBackend(f, "", 0)

		if w.symlinkName != "" {
			symlink := filepath.Join(w.logDir, w.symlinkName)
			_ = os.Remove(symlink)                     // best-effort remove
			_ = os.Symlink(w.logFileBaseName, symlink) // best-effort symlink
		}
	})

	if w.backend == nil {
		return errors.New("no backend")
	}

	return w.backend.Log(level, depth+1, rec)
}
<commit_msg>logging: changed default file log level to debug<commit_after>// Package logfile manages log files.
package logfile

import (
	"context"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"

	logging "github.com/op/go-logging"
	"github.com/pkg/errors"
	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/kopia/kopia/cli"
	"github.com/kopia/kopia/internal/ospath"
	repologging "github.com/kopia/kopia/repo/logging"
)

var fileLogFormat = logging.MustStringFormatter(
	`%{time:2006-01-02 15:04:05.000} %{level:.1s} [%{shortfile}] %{message}`)

var consoleLogFormat = logging.MustStringFormatter(
	`%{color}%{time:15:04:05.000} [%{module}] %{message}%{color:reset}`)

var logLevels = []string{"debug", "info", "warning", "error"}
var (
	logFile        = cli.App().Flag("log-file", "Log file name.").String()
	logDir         = cli.App().Flag("log-dir", "Directory where log files should be written.").Envar("KOPIA_LOG_DIR").Default(ospath.LogsDir()).String()
	logDirMaxFiles = cli.App().Flag("log-dir-max-files", "Maximum number of log files to retain").Envar("KOPIA_LOG_DIR_MAX_FILES").Default("1000").Hidden().Int()
	logDirMaxAge   = cli.App().Flag("log-dir-max-age", "Maximum age of log files to retain").Envar("KOPIA_LOG_DIR_MAX_AGE").Hidden().Duration()
	logLevel       = cli.App().Flag("log-level", "Console log level").Default("info").Enum(logLevels...)
	fileLogLevel   = cli.App().Flag("file-log-level", "File log level").Default("debug").Enum(logLevels...)
)

var log = repologging.GetContextLoggerFunc("kopia")

const (
	logFileNamePrefix = "kopia-"
	logFileNameSuffix = ".log"
)

// Initialize is invoked as part of command execution to create log file just before it's needed.
func Initialize(ctx *kingpin.ParseContext) error {
	var logBackends []logging.Backend

	var logFileName, symlinkName string

	if lfn := *logFile; lfn != "" {
		var err error

		logFileName, err = filepath.Abs(lfn)
		if err != nil {
			return err
		}
	}

	var shouldSweepLogs bool

	if logFileName == "" && *logDir != "" {
		logBaseName := fmt.Sprintf("%v%v-%v%v", logFileNamePrefix, time.Now().Format("20060102-150405"), os.Getpid(), logFileNameSuffix)
		logFileName = filepath.Join(*logDir, logBaseName)
		symlinkName = "kopia.latest.log"

		if *logDirMaxAge > 0 || *logDirMaxFiles > 0 {
			shouldSweepLogs = true
		}
	}

	if logFileName != "" {
		logFileDir := filepath.Dir(logFileName)
		logFileBaseName := filepath.Base(logFileName)

		if err := os.MkdirAll(logFileDir, 0o700); err != nil {
			fmt.Fprintln(os.Stderr, "Unable to create logs directory:", err)
		}

		logBackends = append(
			logBackends,
			levelFilter(
				*fileLogLevel,
				logging.NewBackendFormatter(
					&onDemandBackend{
						logDir:          logFileDir,
						logFileBaseName: logFileBaseName,
						symlinkName:     symlinkName,
					}, fileLogFormat)))
	}

	logBackends = append(logBackends,
		levelFilter(
			*logLevel,
			logging.NewBackendFormatter(
				logging.NewLogBackend(os.Stderr, "", 0),
				consoleLogFormat)))

	logging.SetBackend(logBackends...)

	if shouldSweepLogs {
		go sweepLogDir(context.TODO(), *logDir, *logDirMaxFiles, *logDirMaxAge)
	}

	return nil
}

func sweepLogDir(ctx context.Context, dirname string, maxCount int, maxAge time.Duration) {
	var timeCutoff time.Time
	if maxAge > 0 {
		timeCutoff = time.Now().Add(-maxAge)
	}

	if maxCount == 0 {
		maxCount = math.MaxInt32
	}

	entries, err := ioutil.ReadDir(dirname)
	if err != nil {
		log(ctx).Warningf("unable to read log directory: %v", err)
		return
	}

	sort.Slice(entries, func(i, j int) bool {
		return entries[i].ModTime().After(entries[j].ModTime())
	})

	cnt := 0

	for _, e := range entries {
		if !strings.HasPrefix(e.Name(), logFileNamePrefix) {
			continue
		}

		if !strings.HasSuffix(e.Name(), logFileNameSuffix) {
			continue
		}

		cnt++

		if cnt > maxCount || e.ModTime().Before(timeCutoff) {
			if err = os.Remove(filepath.Join(dirname, e.Name())); err != nil {
				log(ctx).Warningf("unable to remove log file: %v", err)
			}
		}
	}
}

func levelFilter(level string, writer logging.Backend) logging.Backend {
	l := logging.AddModuleLevel(writer)

	switch level {
	case "debug":
		l.SetLevel(logging.DEBUG, "")
	case "info":
		l.SetLevel(logging.INFO, "")
	case "warning":
		l.SetLevel(logging.WARNING, "")
	case "error":
		l.SetLevel(logging.ERROR, "")
	default:
		l.SetLevel(logging.CRITICAL, "")
	}

	return l
}

type onDemandBackend struct {
	logDir          string
	logFileBaseName string
	symlinkName     string

	backend logging.Backend
	once    sync.Once
}

func (w *onDemandBackend) Log(level logging.Level, depth int, rec *logging.Record) error {
	w.once.Do(func() {
		lf := filepath.Join(w.logDir, w.logFileBaseName)
		f, err := os.Create(lf)
		if err != nil {
			fmt.Fprintf(os.Stderr, "unable to open log file: %v\n", err)
			return
		}

		w.backend = logging.NewLogBackend(f, "", 0)

		if w.symlinkName != "" {
			symlink := filepath.Join(w.logDir, w.symlinkName)
			_ = os.Remove(symlink)                     // best-effort remove
			_ = os.Symlink(w.logFileBaseName, symlink) // best-effort symlink
		}
	})

	if w.backend == nil {
		return errors.New("no backend")
	}

	return w.backend.Log(level, depth+1, rec)
}
<|endoftext|>