<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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.

// +build linux

// ssh_keydir manages a user's ~/.ssh/authorized_keys.d and can produce a
// ~/.ssh/authorized_keys file from the authorized_keys.d contents.
package ssh_keydir

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"syscall"
)

const (
	AuthorizedKeysFile = "authorized_keys"
	AuthorizedKeysDir  = "authorized_keys.d"
	PreservedKeysName  = "orig_authorized_keys"
	SSHDir             = ".ssh"

	lockFile  = ".authorized_keys.d.lock"       // In "~/".
	stageFile = ".authorized_keys.d.stage_file" // In "~/.ssh".
	stageDir  = ".authorized_keys.d.stage_dir"  // In "~/.ssh".
)

// SSHAuthorizedKeysDir represents an opened user's authorized_keys.d.
type SSHAuthorizedKeysDir struct {
	Path string     // Path to authorized_keys.d directory.
	User *user.User // User of the directory.
	lock *os.File   // Lock file for serializing Open()-Close().
}

// SSHAuthorizedKey represents an opened user's authorized_keys.d/<name> entry.
type SSHAuthorizedKey struct {
	Name     string                // Name given to the key.
	Disabled bool                  // Disabled state of the key.
	Path     string                // Path to the file backing the key.
	origin   *SSHAuthorizedKeysDir // Originating dir for this key.
}

// sshDirPath returns the path to the .ssh dir for the user.
func sshDirPath(u *user.User) string {
	return filepath.Join(u.HomeDir, SSHDir)
}

// authKeysFilePath returns the path to the authorized_keys file for the user.
func authKeysFilePath(u *user.User) string {
	return filepath.Join(sshDirPath(u), AuthorizedKeysFile)
}

// authKeysDirPath returns the path to the authorized_keys.d for the user.
func authKeysDirPath(u *user.User) string {
	return filepath.Join(sshDirPath(u), AuthorizedKeysDir)
}

// lockFilePath returns the path to the lock file for the user.
func lockFilePath(u *user.User) string {
	return filepath.Join(u.HomeDir, lockFile)
}

// stageDirPath returns the path to the staging directory for the user.
func stageDirPath(u *user.User) string {
	return filepath.Join(sshDirPath(u), stageDir)
}

// stageFilePath returns the path to the staging file for the user.
func stageFilePath(u *user.User) string {
	return filepath.Join(sshDirPath(u), stageFile)
}

// withUmask calls function f with the umask modified.
func withUmask(umask int, f func() error) error {
	o := syscall.Umask(umask)
	defer syscall.Umask(o)
	return f()
}

// asUser calls function f as the user/group usr with a umask of 077.
func asUser(usr *user.User, f func() error) error {
	// FIXME(vc): This will probably need to be changed to use CGO for the
	// user/group-switched operations in self-created threads so the Go
	// scheduler can't spuriously call clone() from them.
	// This is an implementation detail though, nothing externally visible.
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	asu, _ := strconv.Atoi(usr.Uid)
	asg, _ := strconv.Atoi(usr.Gid)

	eu := os.Geteuid()
	eg := os.Getegid()

	if eg != asg {
		if err := syscall.Setregid(-1, asg); err != nil {
			return err
		}
		defer syscall.Setregid(-1, eg)
	}

	if eu != asu {
		if err := syscall.Setreuid(-1, asu); err != nil {
			return err
		}
		defer syscall.Setreuid(-1, eu)
	}

	return withUmask(077, f)
}

// opendir opens the authorized keys directory.
func opendir(dir string) (*SSHAuthorizedKeysDir, error) {
	fi, err := os.Stat(dir)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("%q is not a directory", dir)
	}
	return &SSHAuthorizedKeysDir{Path: dir}, nil
}

// acquireLock locks the lock file for the given user's authorized_keys.d.
// A lock file is created if it doesn't already exist.
// The locking is currently a simple coarse-grained mutex held for the
// Open()-Close() duration, implemented using a lock file in the user's ~/.
func acquireLock(u *user.User) (*os.File, error) {
	f, err := os.OpenFile(lockFilePath(u), os.O_CREATE|os.O_RDONLY, 0600)
	if err != nil {
		return nil, err
	}
	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
		f.Close()
		return nil, err
	}
	return f, nil
}

// createAuthorizedKeysDir creates an authorized keys directory for the user.
// If the user has an authorized_keys file, it is migrated.
func createAuthorizedKeysDir(u *user.User) (*SSHAuthorizedKeysDir, error) {
	var akd *SSHAuthorizedKeysDir
	err := asUser(u, func() error {
		td := stageDirPath(u)
		if err := os.MkdirAll(td, 0700); err != nil {
			return err
		}
		defer os.RemoveAll(td)

		d, err := opendir(td)
		if err != nil {
			return err
		}
		d.User = u

		akfb, err := ioutil.ReadFile(authKeysFilePath(u))
		if err != nil && !os.IsNotExist(err) {
			return err
		} else if err == nil {
			err = d.Add(PreservedKeysName, akfb, false, false)
			if err != nil {
				return err
			}
		}
		if err = d.rename(authKeysDirPath(u)); err != nil {
			return err
		}
		akd = d
		return nil
	})
	return akd, err
}

// Open opens the authorized keys directory for the supplied user.
// If create is false, Open will fail if no directory exists yet.
// If create is true, Open will create the directory if it doesn't exist,
// preserving the authorized_keys file in the process.
// After a successful open, Close should be called when finished to unlock
// the directory.
func Open(usr *user.User, create bool) (*SSHAuthorizedKeysDir, error) {
	l, err := acquireLock(usr)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			l.Close()
		}
	}()

	akd, err := opendir(authKeysDirPath(usr))
	if err != nil && (!create || !os.IsNotExist(err)) {
		return nil, err
	} else if os.IsNotExist(err) {
		akd, err = createAuthorizedKeysDir(usr)
		if err != nil {
			return nil, err
		}
	}

	akd.lock = l
	akd.User = usr
	return akd, nil
}

// Close closes the authorized keys directory.
func (akd *SSHAuthorizedKeysDir) Close() error {
	return akd.lock.Close()
}

// rename renames the authorized_keys dir to the supplied path.
func (akd *SSHAuthorizedKeysDir) rename(to string) error {
	err := os.Rename(akd.Path, to)
	if err != nil {
		return err
	}
	akd.Path = to
	return nil
}

// path returns the path to the named key.
func (akd *SSHAuthorizedKeysDir) path(n string) string {
	return filepath.Join(akd.Path, n)
}

// WalkKeys iterates across all keys in akd, calling f for each key.
// Iterating stops on error, and the error is propagated out.
func (akd *SSHAuthorizedKeysDir) WalkKeys(f func(*SSHAuthorizedKey) error) error {
	d, err := os.Open(akd.Path)
	if err != nil {
		return err
	}

	names, err := d.Readdirnames(0)
	if err != nil {
		return err
	}

	sort.Strings(names)
	for _, n := range names {
		ak, err := akd.Open(n)
		if err != nil {
			return err
		}
		if err := f(ak); err != nil {
			return err
		}
	}

	return nil
}

// Open opens the key at name.
func (akd *SSHAuthorizedKeysDir) Open(name string) (*SSHAuthorizedKey, error) {
	p := akd.path(name)
	fi, err := os.Stat(p)
	if err != nil {
		return nil, err
	}
	ak := &SSHAuthorizedKey{
		Name:     name,
		Disabled: (fi.Size() == 0),
		Path:     p,
		origin:   akd,
	}
	return ak, nil
}

// Remove removes the key at name.
func (akd *SSHAuthorizedKeysDir) Remove(name string) error {
	ak, err := akd.Open(name)
	if err != nil {
		return err
	}
	return ak.Remove()
}

// Disable disables the key at name.
func (akd *SSHAuthorizedKeysDir) Disable(name string) error {
	ak, err := akd.Open(name)
	if err != nil {
		return err
	}
	return ak.Disable()
}

// Add adds the supplied key at name.
// replace enables replacing keys already existing at name.
// force enables adding keys to a disabled name, enabling it in the process.
func (akd *SSHAuthorizedKeysDir) Add(name string, keys []byte, replace, force bool) error {
	// TODO FIXME(vc): name can contain anything
	// either add escaping, or place restrictions on its contents.
	p := akd.path(name)
	fi, err := os.Stat(p)
	if err == nil {
		if fi.Size() > 0 && !replace {
			return fmt.Errorf("key %q already exists", name)
		} else if fi.Size() == 0 && !force {
			return fmt.Errorf("key %q disabled", name)
		}
	} else if !os.IsNotExist(err) {
		return err
	}
	ak := &SSHAuthorizedKey{Path: p, origin: akd}
	return ak.Replace(keys)
}

// Sync synchronizes the user's ~/.ssh/authorized_keys file with the
// current authorized_keys.d directory state.
func (akd *SSHAuthorizedKeysDir) Sync() error {
	return asUser(akd.User, akd.doSync)
}

// Sync synchronizes the user's ~/.ssh/authorized_keys file with the
// current authorized_keys.d directory state.
func (akd *SSHAuthorizedKeysDir) doSync() error {
	sp := stageFilePath(akd.User)
	sf, err := os.OpenFile(sp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			sf.Close()
			os.Remove(sp)
		}
	}()

	if err := akd.WalkKeys(func(k *SSHAuthorizedKey) error {
		if !k.Disabled {
			kb, err := ioutil.ReadFile(k.Path)
			if err != nil {
				return err
			}
			if _, err := sf.Write(kb); err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return err
	}

	if err := sf.Close(); err != nil {
		return err
	}

	err = os.Rename(sp, authKeysFilePath(akd.User))
	if err != nil {
		return err
	}

	return nil
}

// Remove removes the opened key.
func (ak *SSHAuthorizedKey) Remove() error {
	return os.Remove(ak.Path)
}

// Disable disables the opened key.
func (ak *SSHAuthorizedKey) Disable() error {
	return os.Truncate(ak.Path, 0)
}

// Replace replaces the opened key with the supplied data.
func (ak *SSHAuthorizedKey) Replace(keys []byte) error {
	return asUser(ak.origin.User, func() error {
		sp := stageFilePath(ak.origin.User)
		sf, err := os.OpenFile(sp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
		if err != nil {
			return err
		}
		defer os.Remove(sp)
		if _, err = sf.Write(keys); err != nil {
			return err
		}
		if err := sf.Close(); err != nil {
			return err
		}
		return os.Rename(sp, ak.Path)
	})
}
<commit_msg>authorized_keys_d: fixup the package name, was ssh_keydir<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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.

// +build linux

// authorized_keys_d manages a user's ~/.ssh/authorized_keys.d and can produce
// a ~/.ssh/authorized_keys file from the authorized_keys.d contents.
package authorized_keys_d

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"syscall"
)

const (
	AuthorizedKeysFile = "authorized_keys"
	AuthorizedKeysDir  = "authorized_keys.d"
	PreservedKeysName  = "orig_authorized_keys"
	SSHDir             = ".ssh"

	lockFile  = ".authorized_keys.d.lock"       // In "~/".
	stageFile = ".authorized_keys.d.stage_file" // In "~/.ssh".
	stageDir  = ".authorized_keys.d.stage_dir"  // In "~/.ssh".
)

// SSHAuthorizedKeysDir represents an opened user's authorized_keys.d.
type SSHAuthorizedKeysDir struct {
	Path string     // Path to authorized_keys.d directory.
	User *user.User // User of the directory.
	lock *os.File   // Lock file for serializing Open()-Close().
}

// SSHAuthorizedKey represents an opened user's authorized_keys.d/<name> entry.
type SSHAuthorizedKey struct {
	Name     string                // Name given to the key.
	Disabled bool                  // Disabled state of the key.
	Path     string                // Path to the file backing the key.
	origin   *SSHAuthorizedKeysDir // Originating dir for this key.
}

// sshDirPath returns the path to the .ssh dir for the user.
func sshDirPath(u *user.User) string {
	return filepath.Join(u.HomeDir, SSHDir)
}

// authKeysFilePath returns the path to the authorized_keys file for the user.
func authKeysFilePath(u *user.User) string {
	return filepath.Join(sshDirPath(u), AuthorizedKeysFile)
}

// authKeysDirPath returns the path to the authorized_keys.d for the user.
func authKeysDirPath(u *user.User) string {
	return filepath.Join(sshDirPath(u), AuthorizedKeysDir)
}

// lockFilePath returns the path to the lock file for the user.
func lockFilePath(u *user.User) string {
	return filepath.Join(u.HomeDir, lockFile)
}

// stageDirPath returns the path to the staging directory for the user.
func stageDirPath(u *user.User) string {
	return filepath.Join(sshDirPath(u), stageDir)
}

// stageFilePath returns the path to the staging file for the user.
func stageFilePath(u *user.User) string {
	return filepath.Join(sshDirPath(u), stageFile)
}

// withUmask calls function f with the umask modified.
func withUmask(umask int, f func() error) error {
	o := syscall.Umask(umask)
	defer syscall.Umask(o)
	return f()
}

// asUser calls function f as the user/group usr with a umask of 077.
func asUser(usr *user.User, f func() error) error {
	// FIXME(vc): This will probably need to be changed to use CGO for the
	// user/group-switched operations in self-created threads so the Go
	// scheduler can't spuriously call clone() from them.
	// This is an implementation detail though, nothing externally visible.
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	asu, _ := strconv.Atoi(usr.Uid)
	asg, _ := strconv.Atoi(usr.Gid)

	eu := os.Geteuid()
	eg := os.Getegid()

	if eg != asg {
		if err := syscall.Setregid(-1, asg); err != nil {
			return err
		}
		defer syscall.Setregid(-1, eg)
	}

	if eu != asu {
		if err := syscall.Setreuid(-1, asu); err != nil {
			return err
		}
		defer syscall.Setreuid(-1, eu)
	}

	return withUmask(077, f)
}

// opendir opens the authorized keys directory.
func opendir(dir string) (*SSHAuthorizedKeysDir, error) {
	fi, err := os.Stat(dir)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("%q is not a directory", dir)
	}
	return &SSHAuthorizedKeysDir{Path: dir}, nil
}

// acquireLock locks the lock file for the given user's authorized_keys.d.
// A lock file is created if it doesn't already exist.
// The locking is currently a simple coarse-grained mutex held for the
// Open()-Close() duration, implemented using a lock file in the user's ~/.
func acquireLock(u *user.User) (*os.File, error) {
	f, err := os.OpenFile(lockFilePath(u), os.O_CREATE|os.O_RDONLY, 0600)
	if err != nil {
		return nil, err
	}
	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
		f.Close()
		return nil, err
	}
	return f, nil
}

// createAuthorizedKeysDir creates an authorized keys directory for the user.
// If the user has an authorized_keys file, it is migrated.
func createAuthorizedKeysDir(u *user.User) (*SSHAuthorizedKeysDir, error) {
	var akd *SSHAuthorizedKeysDir
	err := asUser(u, func() error {
		td := stageDirPath(u)
		if err := os.MkdirAll(td, 0700); err != nil {
			return err
		}
		defer os.RemoveAll(td)

		d, err := opendir(td)
		if err != nil {
			return err
		}
		d.User = u

		akfb, err := ioutil.ReadFile(authKeysFilePath(u))
		if err != nil && !os.IsNotExist(err) {
			return err
		} else if err == nil {
			err = d.Add(PreservedKeysName, akfb, false, false)
			if err != nil {
				return err
			}
		}
		if err = d.rename(authKeysDirPath(u)); err != nil {
			return err
		}
		akd = d
		return nil
	})
	return akd, err
}

// Open opens the authorized keys directory for the supplied user.
// If create is false, Open will fail if no directory exists yet.
// If create is true, Open will create the directory if it doesn't exist,
// preserving the authorized_keys file in the process.
// After a successful open, Close should be called when finished to unlock
// the directory.
func Open(usr *user.User, create bool) (*SSHAuthorizedKeysDir, error) {
	l, err := acquireLock(usr)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			l.Close()
		}
	}()

	akd, err := opendir(authKeysDirPath(usr))
	if err != nil && (!create || !os.IsNotExist(err)) {
		return nil, err
	} else if os.IsNotExist(err) {
		akd, err = createAuthorizedKeysDir(usr)
		if err != nil {
			return nil, err
		}
	}

	akd.lock = l
	akd.User = usr
	return akd, nil
}

// Close closes the authorized keys directory.
func (akd *SSHAuthorizedKeysDir) Close() error {
	return akd.lock.Close()
}

// rename renames the authorized_keys dir to the supplied path.
func (akd *SSHAuthorizedKeysDir) rename(to string) error {
	err := os.Rename(akd.Path, to)
	if err != nil {
		return err
	}
	akd.Path = to
	return nil
}

// path returns the path to the named key.
func (akd *SSHAuthorizedKeysDir) path(n string) string {
	return filepath.Join(akd.Path, n)
}

// WalkKeys iterates across all keys in akd, calling f for each key.
// Iterating stops on error, and the error is propagated out.
func (akd *SSHAuthorizedKeysDir) WalkKeys(f func(*SSHAuthorizedKey) error) error {
	d, err := os.Open(akd.Path)
	if err != nil {
		return err
	}

	names, err := d.Readdirnames(0)
	if err != nil {
		return err
	}

	sort.Strings(names)
	for _, n := range names {
		ak, err := akd.Open(n)
		if err != nil {
			return err
		}
		if err := f(ak); err != nil {
			return err
		}
	}

	return nil
}

// Open opens the key at name.
func (akd *SSHAuthorizedKeysDir) Open(name string) (*SSHAuthorizedKey, error) {
	p := akd.path(name)
	fi, err := os.Stat(p)
	if err != nil {
		return nil, err
	}
	ak := &SSHAuthorizedKey{
		Name:     name,
		Disabled: (fi.Size() == 0),
		Path:     p,
		origin:   akd,
	}
	return ak, nil
}

// Remove removes the key at name.
func (akd *SSHAuthorizedKeysDir) Remove(name string) error {
	ak, err := akd.Open(name)
	if err != nil {
		return err
	}
	return ak.Remove()
}

// Disable disables the key at name.
func (akd *SSHAuthorizedKeysDir) Disable(name string) error {
	ak, err := akd.Open(name)
	if err != nil {
		return err
	}
	return ak.Disable()
}

// Add adds the supplied key at name.
// replace enables replacing keys already existing at name.
// force enables adding keys to a disabled name, enabling it in the process.
func (akd *SSHAuthorizedKeysDir) Add(name string, keys []byte, replace, force bool) error {
	// TODO FIXME(vc): name can contain anything
	// either add escaping, or place restrictions on its contents.
	p := akd.path(name)
	fi, err := os.Stat(p)
	if err == nil {
		if fi.Size() > 0 && !replace {
			return fmt.Errorf("key %q already exists", name)
		} else if fi.Size() == 0 && !force {
			return fmt.Errorf("key %q disabled", name)
		}
	} else if !os.IsNotExist(err) {
		return err
	}
	ak := &SSHAuthorizedKey{Path: p, origin: akd}
	return ak.Replace(keys)
}

// Sync synchronizes the user's ~/.ssh/authorized_keys file with the
// current authorized_keys.d directory state.
func (akd *SSHAuthorizedKeysDir) Sync() error {
	return asUser(akd.User, akd.doSync)
}

// Sync synchronizes the user's ~/.ssh/authorized_keys file with the
// current authorized_keys.d directory state.
func (akd *SSHAuthorizedKeysDir) doSync() error {
	sp := stageFilePath(akd.User)
	sf, err := os.OpenFile(sp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			sf.Close()
			os.Remove(sp)
		}
	}()

	if err := akd.WalkKeys(func(k *SSHAuthorizedKey) error {
		if !k.Disabled {
			kb, err := ioutil.ReadFile(k.Path)
			if err != nil {
				return err
			}
			if _, err := sf.Write(kb); err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return err
	}

	if err := sf.Close(); err != nil {
		return err
	}

	err = os.Rename(sp, authKeysFilePath(akd.User))
	if err != nil {
		return err
	}

	return nil
}

// Remove removes the opened key.
func (ak *SSHAuthorizedKey) Remove() error {
	return os.Remove(ak.Path)
}

// Disable disables the opened key.
func (ak *SSHAuthorizedKey) Disable() error {
	return os.Truncate(ak.Path, 0)
}

// Replace replaces the opened key with the supplied data.
func (ak *SSHAuthorizedKey) Replace(keys []byte) error {
	return asUser(ak.origin.User, func() error {
		sp := stageFilePath(ak.origin.User)
		sf, err := os.OpenFile(sp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
		if err != nil {
			return err
		}
		defer os.Remove(sp)
		if _, err = sf.Write(keys); err != nil {
			return err
		}
		if err := sf.Close(); err != nil {
			return err
		}
		return os.Rename(sp, ak.Path)
	})
}
<|endoftext|>


<commit_before>package gopenid

import (
	"bytes"
	"errors"
	"fmt"
	"net/url"
	"sort"
	"strings"
	"sync"
)

const (
	NsOpenID10 NamespaceURI = "http://openid.net/signon/1.0"
	NsOpenID11 NamespaceURI = "http://openid.net/signon/1.1"
	NsOpenID20 NamespaceURI = "http://specs.openid.net/auth/2.0"

	NsIdentifierSelect NamespaceURI = "http://specs.openid.net/auth/2.0/identifier_select"
)

var (
	ErrMalformedMessage   = errors.New("malformed Message")
	ErrUnsupportedVersion = errors.New("unsupported version")
	ErrKeyContainsColon   = errors.New("key contains colon")
	ErrKeyContainsNewLine = errors.New("key contains new line")
	ErrValueNotFound      = errors.New("value not found")

	ProtocolFields = []string{
		"assoc_handle",
		"assoc_type",
		"claimed_id",
		"contact",
		"delegate",
		"dh_consumer_public",
		"dh_gen",
		"dh_modulus",
		"error",
		"identity",
		"invalidate_handle",
		"mode",
		"ns",
		"op_endpoint",
		"openid",
		"realm",
		"reference",
		"response_nonce",
		"return_to",
		"server",
		"session_type",
		"sig",
		"signed",
		"trust_root",
	}
)

type NamespaceURI string

func (ns NamespaceURI) String() string {
	return string(ns)
}

type MessageKey struct {
	namespace NamespaceURI
	key       string
}

func NewMessageKey(ns NamespaceURI, key string) MessageKey {
	return MessageKey{
		namespace: ns,
		key:       key,
	}
}

func (k *MessageKey) GetNamespace() NamespaceURI {
	return k.namespace
}

func (k *MessageKey) GetKey() string {
	return k.key
}

type MessageValue string

func (v MessageValue) String() string {
	return string(v)
}

func (v MessageValue) Bytes() []byte {
	return []byte(v)
}

type Message struct {
	namespace     NamespaceURI
	nsuri2nsalias map[NamespaceURI]string
	nsalias2nsuri map[string]NamespaceURI
	args          map[MessageKey]MessageValue
	sync.Mutex
}

func NewMessage(ns NamespaceURI) Message {
	return Message{
		namespace:     ns,
		nsuri2nsalias: make(map[NamespaceURI]string),
		nsalias2nsuri: make(map[string]NamespaceURI),
		args:          make(map[MessageKey]MessageValue),
	}
}

func (m *Message) GetOpenIDNamespace() NamespaceURI {
	return m.namespace
}

func (m *Message) GetNamespaceURI(alias string) (NamespaceURI, bool) {
	if alias == "openid" {
		return m.GetOpenIDNamespace(), true
	} else {
		nsuri, ok := m.nsalias2nsuri[alias]
		return nsuri, ok
	}
}

func (m *Message) GetNamespaceAlias(uri NamespaceURI) (string, bool) {
	if uri == m.GetOpenIDNamespace() {
		return "", true
	} else {
		nsalias, ok := m.nsuri2nsalias[uri]
		return nsalias, ok
	}
}

func (m *Message) SetNamespaceAlias(alias string, uri NamespaceURI) {
	m.nsuri2nsalias[uri] = alias
	m.nsalias2nsuri[alias] = uri
}

func (m *Message) GetArg(k MessageKey) (MessageValue, bool) {
	v, ok := m.args[k]
	return v, ok
}

func (m *Message) AddArg(k MessageKey, v MessageValue) {
	m.args[k] = v
}

func (m *Message) GetArgs(nsuri NamespaceURI) map[MessageKey]MessageValue {
	ret := make(map[MessageKey]MessageValue)

	for k, v := range m.args {
		if k.GetNamespace() == nsuri {
			ret[k] = v
		}
	}
	return ret
}

func (m *Message) ToQuery() url.Values {
	query := url.Values{
		"openid.ns": []string{m.namespace.String()},
	}

	for nsalias, nsuri := range m.nsalias2nsuri {
		query[fmt.Sprintf("openid.ns.%s", nsalias)] = []string{nsuri.String()}
	}

	for key, value := range m.args {
		var queryKey string
		if alias, _ := m.GetNamespaceAlias(key.GetNamespace()); alias == "" {
			queryKey = fmt.Sprintf("openid.%s", key.GetKey())
		} else {
			queryKey = fmt.Sprintf("openid.%s.%s", alias, key.GetKey())
		}
		query[queryKey] = []string{value.String()}
	}

	return query
}
func (m *Message) Keys() []string {
	ret := make([]string, 0, len(m.args))

	for key := range m.args {
		parts := make([]string, 0, 3)
		parts = append(parts, "openid")

		if nsalias, ok := m.GetNamespaceAlias(key.GetNamespace()); !ok {
			continue
		} else if nsalias != "" {
			parts = append(parts, nsalias)
		}

		parts = append(parts, key.GetKey())

		ret = append(
			ret,
			strings.Join(parts, "."),
		)
	}

	fmt.Println(ret)

	return ret

}

func (m *Message) ToKeyValue(order []string) (b []byte, err error) {
	validator := func(str string, isKey bool) error {
		if isKey && strings.Index(str, ":") > -1 {
			return ErrKeyContainsColon
		}

		if strings.Index(str, "\n") > -1 {
			return ErrKeyContainsNewLine
		}

		return nil
	}

	lines := make([][]byte, 0, len(order))

	for _, key := range order {
		if !strings.HasPrefix(key, "openid.") {
			err = ErrValueNotFound
			return
		}
		var (
			parts = strings.SplitN(key[7:], ".", 2)

			value string
		)
		if len(parts) > 1 {
			if parts[0] == "ns" {
				v, ok := m.nsalias2nsuri[parts[1]]
				if !ok {
					err = ErrValueNotFound
					return
				}
				value = v.String()
			} else {
				v, ok := m.args[NewMessageKey(m.nsalias2nsuri[parts[0]], parts[1])]
				if !ok {
					err = ErrValueNotFound
					return
				}
				value = v.String()
			}
		} else if parts[0] == "ns" {
			value = m.namespace.String()
		} else {
			v, ok := m.args[NewMessageKey(m.namespace, parts[0])]
			if !ok {
				err = ErrValueNotFound
				return
			}
			value = v.String()
		}

		if err = validator(key, true); err != nil {
			return
		} else if err = validator(value, false); err != nil {
			return
		}

		lines = append(
			lines,
			[]byte(fmt.Sprintf("%s:%s", key, value)),
		)
	}

	b = bytes.Join(lines, []byte{'\n'})
	return
}

func (m *Message) Copy() Message {
	m.Lock()
	defer m.Unlock()

	msg := NewMessage(m.namespace)

	for k, v := range m.nsuri2nsalias {
		msg.nsuri2nsalias[k] = v
	}

	for k, v := range m.nsalias2nsuri {
		msg.nsalias2nsuri[k] = v
	}

	for k, v := range m.args {
		msg.args[k] = v
	}

	return msg
}

func MessageFromQuery(req url.Values) (msg Message, err error) {
	var (
		ns    NamespaceURI
		nsmap = make(map[string]NamespaceURI)
		args  = make(map[string]map[string]string)
	)

	for key, values := range req {
		if len(values) > 1 {
			// Messages MUST NOT contain multiple parameters with the same name
			err = ErrMalformedMessage
			return
		} else if !strings.HasPrefix(key, "openid.") {
			continue
		}

		var (
			parts = strings.SplitN(key[7:], ".", 2)
			value = values[0]

			nsalias string
			key     string
		)

		if len(parts) == 1 {
			key = parts[0]
		} else {
			nsalias = parts[0]
			key = parts[1]
		}

		if nsalias == "" && key == "ns" {
			ns = NamespaceURI(value)
		} else if nsalias == "ns" {
			if strings.Index(key, ".") >= 0 {
				// A namespace alias MUST NOT contain a period
				err = ErrMalformedMessage
				return
			} else if idx := sort.SearchStrings(ProtocolFields, key); ProtocolFields[idx] == key {
				// The namespace alias is not allowed
				err = ErrMalformedMessage
				return
			}
			nsmap[key] = NamespaceURI(value)
		} else {
			if _, ok := args[nsalias]; !ok {
				args[nsalias] = make(map[string]string)
			}
			args[nsalias][key] = value
		}
	}

	switch ns {
	case NsOpenID10:
	case NsOpenID11:
	case NsOpenID20:
	case "":
	default:
		err = ErrUnsupportedVersion
		return
	}

	if ns == "" {
		// OpenID Authentication 1.1 Compatibility mode
		ns = NsOpenID11
	}

	msg = NewMessage(ns)
	for nsalias, kv := range args {
		nsuri, isKnownAlias := nsmap[nsalias]
		if isKnownAlias {
			msg.SetNamespaceAlias(nsalias, nsuri)
		} else {
			nsuri = ns
		}

		for key, value := range kv {
			if !isKnownAlias && nsalias != "" {
				key = fmt.Sprintf("%s.%s", nsalias, key)
			}

			msg.AddArg(
				NewMessageKey(nsuri, key),
				MessageValue(value),
			)
		}
	}

	return
}
<commit_msg>remove lines for debug<commit_after>package gopenid

import (
	"bytes"
	"errors"
	"fmt"
	"net/url"
	"sort"
	"strings"
	"sync"
)

const (
	NsOpenID10 NamespaceURI = "http://openid.net/signon/1.0"
	NsOpenID11 NamespaceURI = "http://openid.net/signon/1.1"
	NsOpenID20 NamespaceURI = "http://specs.openid.net/auth/2.0"

	NsIdentifierSelect NamespaceURI = "http://specs.openid.net/auth/2.0/identifier_select"
)

var (
	ErrMalformedMessage   = errors.New("malformed Message")
	ErrUnsupportedVersion = errors.New("unsupported version")
	ErrKeyContainsColon   = errors.New("key contains colon")
	ErrKeyContainsNewLine = errors.New("key contains new line")
	ErrValueNotFound      = errors.New("value not found")

	ProtocolFields = []string{
		"assoc_handle",
		"assoc_type",
		"claimed_id",
		"contact",
		"delegate",
		"dh_consumer_public",
		"dh_gen",
		"dh_modulus",
		"error",
		"identity",
		"invalidate_handle",
		"mode",
		"ns",
		"op_endpoint",
		"openid",
		"realm",
		"reference",
		"response_nonce",
		"return_to",
		"server",
		"session_type",
		"sig",
		"signed",
		"trust_root",
	}
)

type NamespaceURI string

func (ns NamespaceURI) String() string {
	return string(ns)
}

type MessageKey struct {
	namespace NamespaceURI
	key       string
}

func NewMessageKey(ns NamespaceURI, key string) MessageKey {
	return MessageKey{
		namespace: ns,
		key:       key,
	}
}

func (k *MessageKey) GetNamespace() NamespaceURI {
	return k.namespace
}

func (k *MessageKey) GetKey() string {
	return k.key
}

type MessageValue string

func (v MessageValue) String() string {
	return string(v)
}

func (v MessageValue) Bytes() []byte {
	return []byte(v)
}

type Message struct {
	namespace     NamespaceURI
	nsuri2nsalias map[NamespaceURI]string
	nsalias2nsuri map[string]NamespaceURI
	args          map[MessageKey]MessageValue
	sync.Mutex
}

func NewMessage(ns NamespaceURI) Message {
	return Message{
		namespace:     ns,
		nsuri2nsalias: make(map[NamespaceURI]string),
		nsalias2nsuri: make(map[string]NamespaceURI),
		args:          make(map[MessageKey]MessageValue),
	}
}

func (m *Message) GetOpenIDNamespace() NamespaceURI {
	return m.namespace
}

func (m *Message) GetNamespaceURI(alias string) (NamespaceURI, bool) {
	if alias == "openid" {
		return m.GetOpenIDNamespace(), true
	} else {
		nsuri, ok := m.nsalias2nsuri[alias]
		return nsuri, ok
	}
}

func (m *Message) GetNamespaceAlias(uri NamespaceURI) (string, bool) {
	if uri == m.GetOpenIDNamespace() {
		return "", true
	} else {
		nsalias, ok := m.nsuri2nsalias[uri]
		return nsalias, ok
	}
}

func (m *Message) SetNamespaceAlias(alias string, uri NamespaceURI) {
	m.nsuri2nsalias[uri] = alias
	m.nsalias2nsuri[alias] = uri
}

func (m *Message) GetArg(k MessageKey) (MessageValue, bool) {
	v, ok := m.args[k]
	return v, ok
}

func (m *Message) AddArg(k MessageKey, v MessageValue) {
	m.args[k] = v
}

func (m *Message) GetArgs(nsuri NamespaceURI) map[MessageKey]MessageValue {
	ret := make(map[MessageKey]MessageValue)

	for k, v := range m.args {
		if k.GetNamespace() == nsuri {
			ret[k] = v
		}
	}
	return ret
}

func (m *Message) ToQuery() url.Values {
	query := url.Values{
		"openid.ns": []string{m.namespace.String()},
	}

	for nsalias, nsuri := range m.nsalias2nsuri {
		query[fmt.Sprintf("openid.ns.%s", nsalias)] = []string{nsuri.String()}
	}

	for key, value := range m.args {
		var queryKey string
		if alias, _ := m.GetNamespaceAlias(key.GetNamespace()); alias == "" {
			queryKey = fmt.Sprintf("openid.%s", key.GetKey())
		} else {
			queryKey = fmt.Sprintf("openid.%s.%s", alias, key.GetKey())
		}
		query[queryKey] = []string{value.String()}
	}

	return query
}
func (m *Message) Keys() []string {
	ret := make([]string, 0, len(m.args))

	for key := range m.args {
		parts := make([]string, 0, 3)
		parts = append(parts, "openid")

		if nsalias, ok := m.GetNamespaceAlias(key.GetNamespace()); !ok {
			continue
		} else if nsalias != "" {
			parts = append(parts, nsalias)
		}

		parts = append(parts, key.GetKey())

		ret = append(
			ret,
			strings.Join(parts, "."),
		)
	}

	return ret

}

func (m *Message) ToKeyValue(order []string) (b []byte, err error) {
	validator := func(str string, isKey bool) error {
		if isKey && strings.Index(str, ":") > -1 {
			return ErrKeyContainsColon
		}

		if strings.Index(str, "\n") > -1 {
			return ErrKeyContainsNewLine
		}

		return nil
	}

	lines := make([][]byte, 0, len(order))

	for _, key := range order {
		if !strings.HasPrefix(key, "openid.") {
			err = ErrValueNotFound
			return
		}
		var (
			parts = strings.SplitN(key[7:], ".", 2)

			value string
		)
		if len(parts) > 1 {
			if parts[0] == "ns" {
				v, ok := m.nsalias2nsuri[parts[1]]
				if !ok {
					err = ErrValueNotFound
					return
				}
				value = v.String()
			} else {
				v, ok := m.args[NewMessageKey(m.nsalias2nsuri[parts[0]], parts[1])]
				if !ok {
					err = ErrValueNotFound
					return
				}
				value = v.String()
			}
		} else if parts[0] == "ns" {
			value = m.namespace.String()
		} else {
			v, ok := m.args[NewMessageKey(m.namespace, parts[0])]
			if !ok {
				err = ErrValueNotFound
				return
			}
			value = v.String()
		}

		if err = validator(key, true); err != nil {
			return
		} else if err = validator(value, false); err != nil {
			return
		}

		lines = append(
			lines,
			[]byte(fmt.Sprintf("%s:%s", key, value)),
		)
	}

	b = bytes.Join(lines, []byte{'\n'})
	return
}

func (m *Message) Copy() Message {
	m.Lock()
	defer m.Unlock()

	msg := NewMessage(m.namespace)

	for k, v := range m.nsuri2nsalias {
		msg.nsuri2nsalias[k] = v
	}

	for k, v := range m.nsalias2nsuri {
		msg.nsalias2nsuri[k] = v
	}

	for k, v := range m.args {
		msg.args[k] = v
	}

	return msg
}

func MessageFromQuery(req url.Values) (msg Message, err error) {
	var (
		ns    NamespaceURI
		nsmap = make(map[string]NamespaceURI)
		args  = make(map[string]map[string]string)
	)

	for key, values := range req {
		if len(values) > 1 {
			// Messages MUST NOT contain multiple parameters with the same name
			err = ErrMalformedMessage
			return
		} else if !strings.HasPrefix(key, "openid.") {
			continue
		}

		var (
			parts = strings.SplitN(key[7:], ".", 2)
			value = values[0]

			nsalias string
			key     string
		)

		if len(parts) == 1 {
			key = parts[0]
		} else {
			nsalias = parts[0]
			key = parts[1]
		}

		if nsalias == "" && key == "ns" {
			ns = NamespaceURI(value)
		} else if nsalias == "ns" {
			if strings.Index(key, ".") >= 0 {
				// A namespace alias MUST NOT contain a period
				err = ErrMalformedMessage
				return
			} else if idx := sort.SearchStrings(ProtocolFields, key); ProtocolFields[idx] == key {
				// The namespace alias is not allowed
				err = ErrMalformedMessage
				return
			}
			nsmap[key] = NamespaceURI(value)
		} else {
			if _, ok := args[nsalias]; !ok {
				args[nsalias] = make(map[string]string)
			}
			args[nsalias][key] = value
		}
	}

	switch ns {
	case NsOpenID10:
	case NsOpenID11:
	case NsOpenID20:
	case "":
	default:
		err = ErrUnsupportedVersion
		return
	}

	if ns == "" {
		// OpenID Authentication 1.1 Compatibility mode
		ns = NsOpenID11
	}

	msg = NewMessage(ns)
	for nsalias, kv := range args {
		nsuri, isKnownAlias := nsmap[nsalias]
		if isKnownAlias {
			msg.SetNamespaceAlias(nsalias, nsuri)
		} else {
			nsuri = ns
		}

		for key, value := range kv {
			if !isKnownAlias && nsalias != "" {
				key = fmt.Sprintf("%s.%s", nsalias, key)
			}

			msg.AddArg(
				NewMessageKey(nsuri, key),
				MessageValue(value),
			)
		}
	}

	return
}
<|endoftext|>


<commit_before>package incus

import (
	"encoding/json"
	"errors"
	"log"
	"strings"
	"time"

	"github.com/alexjlockwood/gcm"
	apns "github.com/anachronistic/apns"
	"github.com/spf13/viper"
)

type CommandMsg struct {
	Command map[string]string      `json:"command"`
	Message map[string]interface{} `json:"message,omitempty"`
}

type Message struct {
	Event string                 `json:"event"`
	Data  map[string]interface{} `json:"data"`
	Time  int64                  `json:"time"`
}

func (this *CommandMsg) FromSocket(sock *Socket) {
	command, ok := this.Command["command"]
	if !ok {
		return
	}

	if DEBUG {
		log.Printf("Handling socket message of type %s\n", command)
	}

	sock.Server.Stats.LogCommand("websocket", strings.ToLower(command))

	switch strings.ToLower(command) {
	case "message":
		if !CLIENT_BROAD {
			return
		}

		if sock.Server.Store.StorageType == "redis" {
			this.forwardToRedis(sock.Server)
			return
		}

		this.sendMessage(sock.Server)

	case "setpage":
		page, ok := this.Command["page"]
		if !ok || page == "" {
			return
		}

		if sock.Page != "" {
			sock.Server.Store.UnsetPage(sock) //remove old page if it exists
		}

		sock.Page = page
		sock.Server.Store.SetPage(sock) // set new page
	case "setpresence":
		active, ok := this.Message["presence"]

		if !ok {
			if DEBUG {
				log.Printf("Ignoring presence command with no boolean presence")
			}

			return
		}

		switch activeT := active.(type) {
		case bool:
			if activeT {
				sock.Server.Store.redis.MarkActive(sock.UID, sock.SID, time.Now().Unix())
			} else {
				sock.Server.Store.redis.MarkInactive(sock.UID, sock.SID)
			}
		default:
			if DEBUG {
				log.Printf("Ignoring presence command with invalid (non-boolean) type")
			}
		}
	}
}

func (this *CommandMsg) FromRedis(server *Server) {
	command, ok := this.Command["command"]
	if !ok {
		return
	}

	server.Stats.LogCommand("redis", strings.ToLower(command))

	if DEBUG {
		log.Printf("Handling redis message of type %s\n", command)
	}

	switch strings.ToLower(command) {

	case "message":
		this.sendMessage(server)

	case "pushios":
		if viper.GetBool("apns_enabled") {
			this.pushiOS(server)
		}

	case "pushandroid":
		if viper.GetBool("gcm_enabled") {
			this.pushAndroid(server)
		}

	case "push":
		if strings.ToLower(this.Command["push_type"]) == "ios" {
			this.pushiOS(server)
		}

		if strings.ToLower(this.Command["push_type"]) == "android" {
			this.pushAndroid(server)
		}
	case "pushormessage":

		active, err := server.Store.redis.QueryIsUserActive(this.Command["user"], time.Now().Unix())

		if err == nil {
			if active {
				websocketMessage := &CommandMsg{
					Command: this.Command,
					Message: this.Message["websocket"].(map[string]interface{}),
				}

				websocketMessage.sendMessage(server)
			} else {
				iosMessage, ok := this.Message["ios"]

				if ok {
					iosCommand := &CommandMsg{
						Command: this.Command,
						Message: iosMessage.(map[string]interface{}),
					}
					iosCommand.pushiOS(server)
				}

				androidMessage, ok := this.Message["android"]
				if ok {
					androidCommand := &CommandMsg{
						Command: this.Command,
						Message: androidMessage.(map[string]interface{}),
					}
					androidCommand.pushAndroid(server)
				}

			}
		} else {
			log.Printf("Error fetching whether %s was active: %s", this.Command["user"], err.Error())
		}
	}
}

func (this *CommandMsg) formatMessage() (*Message, error) {
	event, e_ok := this.Message["event"].(string)
	data, b_ok := this.Message["data"].(map[string]interface{})

	if !b_ok || !e_ok {
		return nil, errors.New("Could not format message")
	}

	msg := &Message{event, data, time.Now().UTC().Unix()}

	return msg, nil
}

func (this *CommandMsg) sendMessage(server *Server) {
	user, userok := this.Command["user"]
	page, pageok := this.Command["page"]

	if userok {
		this.messageUser(user, page, server)
	} else if pageok {
		this.messagePage(page, server)
	} else {
		this.messageAll(server)
	}
}

func (this *CommandMsg) pushiOS(server *Server) {
	deviceToken, deviceTokenOkay := this.Command["device_token"]
	build, buildOkay := this.Command["build"]

	if !deviceTokenOkay {
		log.Println("Device token not provided!")
		return
	}

	if !buildOkay {
		log.Println("Build type not provided!")
		return
	}

	msg, err := this.formatMessage()
	if err != nil {
		log.Println("Could not format message")
		return
	}

	payload := apns.NewPayload()
	payload.Sound = viper.GetString("ios_push_sound")

	payload.Alert = msg.Data["message_text"]
	badgeAmt, hasBadge := msg.Data["badge_count"]
	if hasBadge {
		payload.Badge = int(badgeAmt.(float64))
	}

	pn := apns.NewPushNotification()
	pn.DeviceToken = deviceToken
	pn.AddPayload(payload)
	pn.Set("payload", msg)

	client := server.GetAPNSClient(build)
	resp := client.Send(pn)
	alert, _ := pn.PayloadString()
	server.Stats.LogAPNSPush()

	if resp.Error != nil {
		server.Stats.LogAPNSError()
		log.Printf("Alert (iOS): %s\n", alert)
		log.Printf("Error (iOS): %s\n", resp.Error)
	}
}

func (this *CommandMsg) pushAndroid(server *Server) {
	registration_ids, registration_ids_ok := this.Command["registration_ids"]

	if !registration_ids_ok {
		log.Println("Registration ID(s) not provided!")
		return
	}

	msg, err := this.formatMessage()
	if err != nil {
		log.Println("Could not format message")
		return
	}

	data := map[string]interface{}{"event": msg.Event, "data": msg.Data, "time": msg.Time}

	regIDs := strings.Split(registration_ids, ",")
	gcmMessage := gcm.NewMessage(data, regIDs...)

	sender := server.GetGCMClient()

	server.Stats.LogGCMPush()
	gcmResponse, gcmErr := sender.Send(gcmMessage, 2)
	if gcmErr != nil {
		server.Stats.LogGCMError()
		log.Printf("Error (Android): %s\n", gcmErr)
		return
	}

	if gcmResponse.Failure > 0 {
		server.Stats.LogGCMFailure()
		if !viper.GetBool("redis_enabled") {
			log.Println("Could not push to android_error_queue since redis is not enabled")
			return
		}

		failurePayload := map[string]interface{}{"registration_ids": regIDs, "results": gcmResponse.Results}

		msg_str, _ := json.Marshal(failurePayload)
		server.Store.redis.Push(viper.GetString("android_error_queue"), string(msg_str))
	}
}

func (this *CommandMsg) messageUser(UID string, page string, server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		if DEBUG {
			log.Printf("Error formatting message: %s", err.Error())
		}
		return
	}

	user, err := server.Store.Client(UID)
	if err != nil {
		if DEBUG {
			log.Printf("Skipping UID %s because %s", UID, err.Error())
		}
		return
	}

	server.Stats.LogUserMessage()

	for _, sock := range user {
		if page != "" && page != sock.Page {
			if DEBUG {
				log.Printf("Skipping given page %s != %s", page, sock.Page)
			}

			continue
		}

		if !sock.isClosed() {
			sock.buff <- msg
		} else {
			if DEBUG {
				log.Printf("Skipping because closed")
			}
		}
	}
}

func (this *CommandMsg) messageAll(server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	server.Stats.LogBroadcastMessage()
	clients := server.Store.Clients()

	for _, user := range clients {
		for _, sock := range user {
			if !sock.isClosed() {
				sock.buff <- msg
			}
		}
	}

	return
}

func (this *CommandMsg) messagePage(page string, server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	server.Stats.LogPageMessage()

	pageMap := server.Store.getPage(page)
	if pageMap == nil {
		return
	}

	for _, sock := range pageMap {
		if !sock.isClosed() {
			sock.buff <- msg
		}
	}

	return
}

func (this *CommandMsg) forwardToRedis(server *Server) {
	msg_str, _ := json.Marshal(this)
	server.Store.redis.Publish(viper.GetString("redis_message_channel"), string(msg_str)) //pass the message into redis to send message across cluster
}
<commit_msg>fix: Fix extracting push data from wrong place<commit_after>package incus

import (
	"encoding/json"
	"errors"
	"log"
	"strings"
	"time"

	"github.com/alexjlockwood/gcm"
	apns "github.com/anachronistic/apns"
	"github.com/spf13/viper"
)

type CommandMsg struct {
	Command map[string]string      `json:"command"`
	Message map[string]interface{} `json:"message,omitempty"`
}

type Message struct {
	Event string                 `json:"event"`
	Data  map[string]interface{} `json:"data"`
	Time  int64                  `json:"time"`
}

func (this *CommandMsg) FromSocket(sock *Socket) {
	command, ok := this.Command["command"]
	if !ok {
		return
	}

	if DEBUG {
		log.Printf("Handling socket message of type %s\n", command)
	}

	sock.Server.Stats.LogCommand("websocket", strings.ToLower(command))

	switch strings.ToLower(command) {
	case "message":
		if !CLIENT_BROAD {
			return
		}

		if sock.Server.Store.StorageType == "redis" {
			this.forwardToRedis(sock.Server)
			return
		}

		this.sendMessage(sock.Server)

	case "setpage":
		page, ok := this.Command["page"]
		if !ok || page == "" {
			return
		}

		if sock.Page != "" {
			sock.Server.Store.UnsetPage(sock) //remove old page if it exists
		}

		sock.Page = page
		sock.Server.Store.SetPage(sock) // set new page
	case "setpresence":
		active, ok := this.Message["presence"]

		if !ok {
			if DEBUG {
				log.Printf("Ignoring presence command with no boolean presence")
			}

			return
		}

		switch activeT := active.(type) {
		case bool:
			if activeT {
				sock.Server.Store.redis.MarkActive(sock.UID, sock.SID, time.Now().Unix())
			} else {
				sock.Server.Store.redis.MarkInactive(sock.UID, sock.SID)
			}
		default:
			if DEBUG {
				log.Printf("Ignoring presence command with invalid (non-boolean) type")
			}
		}
	}
}

func (this *CommandMsg) FromRedis(server *Server) {
	command, ok := this.Command["command"]
	if !ok {
		return
	}

	server.Stats.LogCommand("redis", strings.ToLower(command))

	if DEBUG {
		log.Printf("Handling redis message of type %s\n", command)
	}

	switch strings.ToLower(command) {

	case "message":
		this.sendMessage(server)

	case "pushios":
		if viper.GetBool("apns_enabled") {
			this.pushiOS(server)
		}

	case "pushandroid":
		if viper.GetBool("gcm_enabled") {
			this.pushAndroid(server)
		}

	case "push":
		if strings.ToLower(this.Command["push_type"]) == "ios" {
			this.pushiOS(server)
		}

		if strings.ToLower(this.Command["push_type"]) == "android" {
			this.pushAndroid(server)
		}
	case "pushormessage":

		active, err := server.Store.redis.QueryIsUserActive(this.Command["user"], time.Now().Unix())

		if err == nil {
			if active {
				websocketMessage := &CommandMsg{
					Command: this.Command,
					Message: this.Message["websocket"].(map[string]interface{}),
				}

				websocketMessage.sendMessage(server)
			} else {

				pushData, ok := this.Message["push"].(map[string]interface{})

				if !ok {
					return
				}

				iosMessage, ok := pushData["ios"]

				if ok {
					iosCommand := &CommandMsg{
						Command: this.Command,
						Message: iosMessage.(map[string]interface{}),
					}
					iosCommand.pushiOS(server)
				}

				androidMessage, ok := pushData["android"]
				if ok {
					androidCommand := &CommandMsg{
						Command: this.Command,
						Message: androidMessage.(map[string]interface{}),
					}
					androidCommand.pushAndroid(server)
				}

			}
		} else {
			log.Printf("Error fetching whether %s was active: %s", this.Command["user"], err.Error())
		}
	}
}

func (this *CommandMsg) formatMessage() (*Message, error) {
	event, e_ok := this.Message["event"].(string)
	data, b_ok := this.Message["data"].(map[string]interface{})

	if !b_ok || !e_ok {
		return nil, errors.New("Could not format message")
	}

	msg := &Message{event, data, time.Now().UTC().Unix()}

	return msg, nil
}

func (this *CommandMsg) sendMessage(server *Server) {
	user, userok := this.Command["user"]
	page, pageok := this.Command["page"]

	if userok {
		this.messageUser(user, page, server)
	} else if pageok {
		this.messagePage(page, server)
	} else {
		this.messageAll(server)
	}
}

func (this *CommandMsg) pushiOS(server *Server) {
	deviceToken, deviceTokenOkay := this.Command["device_token"]
	build, buildOkay := this.Command["build"]

	if !deviceTokenOkay {
		log.Println("Device token not provided!")
		return
	}

	if !buildOkay {
		log.Println("Build type not provided!")
		return
	}

	msg, err := this.formatMessage()
	if err != nil {
		log.Println("Could not format message")
		return
	}

	payload := apns.NewPayload()
	payload.Sound = viper.GetString("ios_push_sound")

	payload.Alert = msg.Data["message_text"]
	badgeAmt, hasBadge := msg.Data["badge_count"]
	if hasBadge {
		payload.Badge = int(badgeAmt.(float64))
	}

	pn := apns.NewPushNotification()
	pn.DeviceToken = deviceToken
	pn.AddPayload(payload)
	pn.Set("payload", msg)

	client := server.GetAPNSClient(build)
	resp := client.Send(pn)
	alert, _ := pn.PayloadString()
	server.Stats.LogAPNSPush()

	if resp.Error != nil {
		server.Stats.LogAPNSError()
		log.Printf("Alert (iOS): %s\n", alert)
		log.Printf("Error (iOS): %s\n", resp.Error)
	}
}

func (this *CommandMsg) pushAndroid(server *Server) {
	registration_ids, registration_ids_ok := this.Command["registration_ids"]

	if !registration_ids_ok {
		log.Println("Registration ID(s) not provided!")
		return
	}

	msg, err := this.formatMessage()
	if err != nil {
		log.Println("Could not format message")
		return
	}

	data := map[string]interface{}{"event": msg.Event, "data": msg.Data, "time": msg.Time}

	regIDs := strings.Split(registration_ids, ",")
	gcmMessage := gcm.NewMessage(data, regIDs...)

	sender := server.GetGCMClient()

	server.Stats.LogGCMPush()
	gcmResponse, gcmErr := sender.Send(gcmMessage, 2)
	if gcmErr != nil {
		server.Stats.LogGCMError()
		log.Printf("Error (Android): %s\n", gcmErr)
		return
	}

	if gcmResponse.Failure > 0 {
		server.Stats.LogGCMFailure()
		if !viper.GetBool("redis_enabled") {
			log.Println("Could not push to android_error_queue since redis is not enabled")
			return
		}

		failurePayload := map[string]interface{}{"registration_ids": regIDs, "results": gcmResponse.Results}

		msg_str, _ := json.Marshal(failurePayload)
		server.Store.redis.Push(viper.GetString("android_error_queue"), string(msg_str))
	}
}

func (this *CommandMsg) messageUser(UID string, page string, server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		if DEBUG {
			log.Printf("Error formatting message: %s", err.Error())
		}
		return
	}

	user, err := server.Store.Client(UID)
	if err != nil {
		if DEBUG {
			log.Printf("Skipping UID %s because %s", UID, err.Error())
		}
		return
	}

	server.Stats.LogUserMessage()

	for _, sock := range user {
		if page != "" && page != sock.Page {
			if DEBUG {
				log.Printf("Skipping given page %s != %s", page, sock.Page)
			}

			continue
		}

		if !sock.isClosed() {
			sock.buff <- msg
		} else {
			if DEBUG {
				log.Printf("Skipping because closed")
			}
		}
	}
}

func (this *CommandMsg) messageAll(server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	server.Stats.LogBroadcastMessage()
	clients := server.Store.Clients()

	for _, user := range clients {
		for _, sock := range user {
			if !sock.isClosed() {
				sock.buff <- msg
			}
		}
	}

	return
}

func (this *CommandMsg) messagePage(page string, server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	server.Stats.LogPageMessage()

	pageMap := server.Store.getPage(page)
	if pageMap == nil {
		return
	}

	for _, sock := range pageMap {
		if !sock.isClosed() {
			sock.buff <- msg
		}
	}

	return
}

func (this *CommandMsg) forwardToRedis(server *Server) {
	msg_str, _ := json.Marshal(this)
	server.Store.redis.Publish(viper.GetString("redis_message_channel"), string(msg_str)) //pass the message into redis to send message across cluster
}
<|endoftext|>


<commit_before><commit_msg>providers/service - make linting happy<commit_after><|endoftext|>


<commit_before>package status

import (
	"bytes"
	"fmt"
	"net/http"
	"runtime"
	"strings"
)

type Code int

var (
	Code_OK                  Code = 0
	Code_INVALID_ARGUMENT    Code = 1
	Code_INTERNAL_ERROR      Code = 2
	Code_UNKNOWN             Code = 3
	Code_NOT_FOUND           Code = 4
	Code_ALREADY_EXISTS      Code = 5
	Code_PERMISSION_DENIED   Code = 6
	Code_UNAUTHENTICATED     Code = 7
	Code_RESOURCE_EXHAUSTED  Code = 8
	Code_FAILED_PRECONDITION Code = 9
	Code_ABORTED             Code = 10
	Code_UNIMPLEMENTED       Code = 11
)

var (
	_codeHttpMapping = map[Code]int{
		Code_OK:                  http.StatusOK,
		Code_INVALID_ARGUMENT:    http.StatusBadRequest,
		Code_INTERNAL_ERROR:      http.StatusInternalServerError,
		Code_UNKNOWN:             http.StatusInternalServerError,
		Code_NOT_FOUND:           http.StatusNotFound,
		Code_ALREADY_EXISTS:      http.StatusConflict,
		Code_PERMISSION_DENIED:   http.StatusForbidden,
		Code_UNAUTHENTICATED:     http.StatusUnauthorized,
		Code_RESOURCE_EXHAUSTED:  429,
		Code_FAILED_PRECONDITION: http.StatusPreconditionFailed,
		Code_ABORTED:             http.StatusConflict,
		Code_UNIMPLEMENTED:       http.StatusNotImplemented,
	}
)

var (
	_codeNameMapping = map[Code]string{
		Code_OK:                  "OK",
		Code_INVALID_ARGUMENT:    "INVALID_ARGUMENT",
		Code_INTERNAL_ERROR:      "INTERNAL_ERROR",
		Code_UNKNOWN:             "UNKNOWN",
		Code_NOT_FOUND:           "NOT_FOUND",
		Code_ALREADY_EXISTS:      "ALREADY_EXISTS",
		Code_PERMISSION_DENIED:   "PERMISSION_DENIED",
		Code_UNAUTHENTICATED:     "UNAUTHENTICATED",
		Code_RESOURCE_EXHAUSTED:  "RESOURCE_EXHAUSTED",
		Code_FAILED_PRECONDITION: "FAILED_PRECONDITION",
		Code_ABORTED:             "ABORTED",
		Code_UNIMPLEMENTED:       "UNIMPLEMENTED",
	}
)

func (c Code) String() string {
	if mapping, present := _codeNameMapping[c]; present {
		return mapping
	}
	return fmt.Sprintf("Unknown Code %d", c)
}

func (c Code) HttpStatus() int {
	if mapping, present := _codeHttpMapping[c]; present {
		return mapping
	}
	return http.StatusInternalServerError
}

var _ error = &Status{}

type Status struct {
	Code       Code
	Message    string
	Cause      error
	StackTrace string
}

func InvalidArgument(e error, v ...interface{}) *Status {
	return &Status{
		Code:       Code_INVALID_ARGUMENT,
		Message:    fmt.Sprint(v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func InvalidArgumentf(e error, format string, v ...interface{}) *Status {
	return &Status{
		Code:       Code_INVALID_ARGUMENT,
		Message:    fmt.Sprintf(format, v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func InternalError(e error, v ...interface{}) *Status {
	return &Status{
		Code:       Code_INTERNAL_ERROR,
		Message:    fmt.Sprint(v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func InternalErrorf(e error, format string, v ...interface{}) *Status {
	return &Status{
		Code:       Code_INTERNAL_ERROR,
		Message:    fmt.Sprintf(format, v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func NotFound(e error, v ...interface{}) *Status {
	return &Status{
		Code:       Code_NOT_FOUND,
		Message:    fmt.Sprint(v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func NotFoundf(e error, format string, v ...interface{}) *Status {
	return &Status{
		Code:       Code_NOT_FOUND,
		Message:    fmt.Sprintf(format, v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func AlreadyExists(e error, v ...interface{}) *Status {
	return &Status{
		Code:       Code_ALREADY_EXISTS,
		Message:    fmt.Sprint(v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func AlreadyExistsf(e error, format string, v ...interface{}) *Status {
	return &Status{
		Code:       Code_ALREADY_EXISTS,
		Message:    fmt.Sprintf(format, v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func getStackTrace() string {
	s := make([]byte, 4096)
	size := runtime.Stack(s, false)
	stack := string(s[:size])

	// Always trim the constructor method
	return strings.SplitN(stack, "\n", 6)[6-1]
}

func (s *Status) Error() string {
	var b bytes.Buffer
	var err error = s
	var first bool = true
	for err != nil {
		if first {
			first = false
			b.WriteString("Status ")
		} else {
			b.WriteString("Caused by ")
		}
		switch e := err.(type) {
		case *Status:
			if e.Message != "" {
				b.WriteString(e.Code.String() + ": " + e.Message)
			} else {
				b.WriteString(e.Code.String())
			}
			if e.StackTrace != "" {
				b.WriteString("\n\t")
				b.WriteString(strings.Join(strings.Split(e.StackTrace, "\n"), "\n\t"))
			}
			err = e.Cause
		default:
			b.WriteString(err.Error())
		}
	}
	return b.String()
}
<commit_msg>fix oom for status printing<commit_after>package status

import (
	"bytes"
	"fmt"
	"net/http"
	"runtime"
	"strings"
)

type Code int

var (
	Code_OK                  Code = 0
	Code_INVALID_ARGUMENT    Code = 1
	Code_INTERNAL_ERROR      Code = 2
	Code_UNKNOWN             Code = 3
	Code_NOT_FOUND           Code = 4
	Code_ALREADY_EXISTS      Code = 5
	Code_PERMISSION_DENIED   Code = 6
	Code_UNAUTHENTICATED     Code = 7
	Code_RESOURCE_EXHAUSTED  Code = 8
	Code_FAILED_PRECONDITION Code = 9
	Code_ABORTED             Code = 10
	Code_UNIMPLEMENTED       Code = 11
)

var (
	_codeHttpMapping = map[Code]int{
		Code_OK:                  http.StatusOK,
		Code_INVALID_ARGUMENT:    http.StatusBadRequest,
		Code_INTERNAL_ERROR:      http.StatusInternalServerError,
		Code_UNKNOWN:             http.StatusInternalServerError,
		Code_NOT_FOUND:           http.StatusNotFound,
		Code_ALREADY_EXISTS:      http.StatusConflict,
		Code_PERMISSION_DENIED:   http.StatusForbidden,
		Code_UNAUTHENTICATED:     http.StatusUnauthorized,
		Code_RESOURCE_EXHAUSTED:  429,
		Code_FAILED_PRECONDITION: http.StatusPreconditionFailed,
		Code_ABORTED:             http.StatusConflict,
		Code_UNIMPLEMENTED:       http.StatusNotImplemented,
	}
)

var (
	_codeNameMapping = map[Code]string{
		Code_OK:                  "OK",
		Code_INVALID_ARGUMENT:    "INVALID_ARGUMENT",
		Code_INTERNAL_ERROR:      "INTERNAL_ERROR",
		Code_UNKNOWN:             "UNKNOWN",
		Code_NOT_FOUND:           "NOT_FOUND",
		Code_ALREADY_EXISTS:      "ALREADY_EXISTS",
		Code_PERMISSION_DENIED:   "PERMISSION_DENIED",
		Code_UNAUTHENTICATED:     "UNAUTHENTICATED",
		Code_RESOURCE_EXHAUSTED:  "RESOURCE_EXHAUSTED",
		Code_FAILED_PRECONDITION: "FAILED_PRECONDITION",
		Code_ABORTED:             "ABORTED",
		Code_UNIMPLEMENTED:       "UNIMPLEMENTED",
	}
)

func (c Code) String() string {
	if mapping, present := _codeNameMapping[c]; present {
		return mapping
	}
	return fmt.Sprintf("Unknown Code %d", c)
}

func (c Code) HttpStatus() int {
	if mapping, present := _codeHttpMapping[c]; present {
		return mapping
	}
	return http.StatusInternalServerError
}

var _ error = &Status{}

type Status struct {
	Code       Code
	Message    string
	Cause      error
	StackTrace string
}

func InvalidArgument(e error, v ...interface{}) *Status {
	return &Status{
		Code:       Code_INVALID_ARGUMENT,
		Message:    fmt.Sprint(v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func InvalidArgumentf(e error, format string, v ...interface{}) *Status {
	return &Status{
		Code:       Code_INVALID_ARGUMENT,
		Message:    fmt.Sprintf(format, v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func InternalError(e error, v ...interface{}) *Status {
	return &Status{
		Code:       Code_INTERNAL_ERROR,
		Message:    fmt.Sprint(v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func InternalErrorf(e error, format string, v ...interface{}) *Status {
	return &Status{
		Code:       Code_INTERNAL_ERROR,
		Message:    fmt.Sprintf(format, v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func NotFound(e error, v ...interface{}) *Status {
	return &Status{
		Code:       Code_NOT_FOUND,
		Message:    fmt.Sprint(v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func NotFoundf(e error, format string, v ...interface{}) *Status {
	return &Status{
		Code:       Code_NOT_FOUND,
		Message:    fmt.Sprintf(format, v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func AlreadyExists(e error, v ...interface{}) *Status {
	return &Status{
		Code:       Code_ALREADY_EXISTS,
		Message:    fmt.Sprint(v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func AlreadyExistsf(e error, format string, v ...interface{}) *Status {
	return &Status{
		Code:       Code_ALREADY_EXISTS,
		Message:    fmt.Sprintf(format, v...),
		Cause:      e,
		StackTrace: getStackTrace(),
	}
}

func getStackTrace() string {
	s := make([]byte, 4096)
	size := runtime.Stack(s, false)
	stack := string(s[:size])

	// Always trim the constructor method
	return strings.SplitN(stack, "\n", 6)[6-1]
}

func (s *Status) Error() string {
	var b bytes.Buffer
	var err error = s
	var first bool = true
	for err != nil {
		if first {
			first = false
			b.WriteString("Status ")
		} else {
			b.WriteString("Caused by ")
		}
		switch e := err.(type) {
		case *Status:
			if e.Message != "" {
				b.WriteString(e.Code.String() + ": " + e.Message)
			} else {
				b.WriteString(e.Code.String())
			}
			if e.StackTrace != "" {
				b.WriteString("\n\t")
				b.WriteString(strings.Join(strings.Split(e.StackTrace, "\n"), "\n\t"))
			}
			err = e.Cause
		default:
			b.WriteString(err.Error())
			err = nil
		}
	}
	return b.String()
}
<|endoftext|>


<commit_before>package status

import (
	"fmt"

	"github.com/shurcooL/go/gists/gist7480523"
)

// PorcelainPresenter is a simple porcelain presenter of GoPackage to humans.
//
// It currently is, and must remain read-only and safe for concurrent execution.
var PorcelainPresenter gist7480523.GoPackageStringer = PlumbingPresenterV2

// Force not to verify that each package has been checked out from the source control repository implied by its import path. This can be useful if the source is a local fork of the original.
var FFlag bool

// This format should remain stable across versions and regardless of user configuration.
//
// It currently is, and must remain read-only and safe for concurrent execution.
var PlumbingPresenterV2 gist7480523.GoPackageStringer = func(goPackage *gist7480523.GoPackage) string {
	out := ""

	if repo := goPackage.Dir.Repo; repo != nil {
		repoImportPath := gist7480523.GetRepoImportPath(repo.Vcs.RootPath(), goPackage.Bpkg.SrcRoot)

		if repo.VcsLocal.LocalBranch != repo.Vcs.GetDefaultBranch() {
			out += "b"
		} else {
			out += " "
		}
		if repo.VcsLocal.Status != "" {
			out += "*"
		} else {
			out += " "
		}
		if !FFlag && (repo.RepoRoot == nil || repo.RepoRoot.Repo != repo.VcsLocal.Remote) {
			out += "#"
		} else if repo.VcsLocal.LocalRev != repo.VcsRemote.RemoteRev {
			if repo.VcsRemote.RemoteRev != "" {
				if !repo.VcsRemote.IsContained {
					out += "+"
				} else {
					out += "-"
				}
			} else {
				out += "!"
			}
		} else {
			out += " "
		}
		if repo.VcsLocal.Stash != "" {
			out += "$"
		} else {
			out += " "
		}

		out += " " + repoImportPath + "/..."
	} else {
		out += "????"

		out += " " + goPackage.Bpkg.ImportPath
	}

	return out
}

// PlumbingPresenter gives the output in an easy-to-parse format for scripts.
// This format should remain stable across versions and regardless of user configuration.
//
// It currently is, and must remain read-only and safe for concurrent execution.
var PlumbingPresenter gist7480523.GoPackageStringer = func(goPackage *gist7480523.GoPackage) string {
	out := ""

	if repo := goPackage.Dir.Repo; repo != nil {
		out += "@"
		if repo.VcsLocal.LocalBranch != repo.Vcs.GetDefaultBranch() {
			out += "b"
		} else {
			out += " "
		}
		if repo.VcsLocal.Status != "" {
			out += "*"
		} else {
			out += " "
		}
		if repo.VcsLocal.LocalRev != repo.VcsRemote.RemoteRev {
			out += "+"
		} else {
			out += " "
		}
	} else {
		out += "    "
	}
	if goPackage.Bpkg.IsCommand() {
		out += "/"
	} else {
		out += " "
	}

	out += " " + goPackage.Bpkg.ImportPath

	return out
}

// DebugPresenter gives debug output.
//
// It currently is, and must remain read-only and safe for concurrent execution.
var DebugPresenter gist7480523.GoPackageStringer = func(goPackage *gist7480523.GoPackage) string {
	out := goPackage.Bpkg.ImportPath

	out += fmt.Sprintf("\tgoPackage.Dir.Repo=%p", goPackage.Dir.Repo)
	out += fmt.Sprintf("\tgoPackage.Bpkg.SrcRoot=%q", goPackage.Bpkg.SrcRoot)

	if repo := goPackage.Dir.Repo; repo != nil {
		out += fmt.Sprintf("\tRootPath=%q", repo.Vcs.RootPath())
		out += fmt.Sprintf("\tLocalBranch=%q", repo.VcsLocal.LocalBranch)
		out += fmt.Sprintf("\tDefaultBranch=%q", repo.Vcs.GetDefaultBranch())
		out += fmt.Sprintf("\tStatus=%q", repo.VcsLocal.Status)
		out += fmt.Sprintf("\tStash=%q", repo.VcsLocal.Stash)
		out += fmt.Sprintf("\tRemote=%q", repo.VcsLocal.Remote)
		out += fmt.Sprintf("\tLocalRev=%q", repo.VcsLocal.LocalRev)
		out += fmt.Sprintf("\tRemoteRev=%q", repo.VcsRemote.RemoteRev)
		out += fmt.Sprintf("\tIsContained=%v", repo.VcsRemote.IsContained)
	}

	return out
}
<commit_msg>Include RepoRoot.Repo info in debug presenter.<commit_after>package status

import (
	"fmt"

	"github.com/shurcooL/go/gists/gist7480523"
)

// PorcelainPresenter is a simple porcelain presenter of GoPackage to humans.
//
// It currently is, and must remain read-only and safe for concurrent execution.
var PorcelainPresenter gist7480523.GoPackageStringer = PlumbingPresenterV2

// Force not to verify that each package has been checked out from the source control repository implied by its import path. This can be useful if the source is a local fork of the original.
var FFlag bool

// This format should remain stable across versions and regardless of user configuration.
//
// It currently is, and must remain read-only and safe for concurrent execution.
var PlumbingPresenterV2 gist7480523.GoPackageStringer = func(goPackage *gist7480523.GoPackage) string {
	out := ""

	if repo := goPackage.Dir.Repo; repo != nil {
		repoImportPath := gist7480523.GetRepoImportPath(repo.Vcs.RootPath(), goPackage.Bpkg.SrcRoot)

		if repo.VcsLocal.LocalBranch != repo.Vcs.GetDefaultBranch() {
			out += "b"
		} else {
			out += " "
		}
		if repo.VcsLocal.Status != "" {
			out += "*"
		} else {
			out += " "
		}
		if !FFlag && (repo.RepoRoot == nil || repo.RepoRoot.Repo != repo.VcsLocal.Remote) {
			out += "#"
		} else if repo.VcsLocal.LocalRev != repo.VcsRemote.RemoteRev {
			if repo.VcsRemote.RemoteRev != "" {
				if !repo.VcsRemote.IsContained {
					out += "+"
				} else {
					out += "-"
				}
			} else {
				out += "!"
			}
		} else {
			out += " "
		}
		if repo.VcsLocal.Stash != "" {
			out += "$"
		} else {
			out += " "
		}

		out += " " + repoImportPath + "/..."
	} else {
		out += "????"

		out += " " + goPackage.Bpkg.ImportPath
	}

	return out
}

// PlumbingPresenter gives the output in an easy-to-parse format for scripts.
// This format should remain stable across versions and regardless of user configuration.
//
// It currently is, and must remain read-only and safe for concurrent execution.
var PlumbingPresenter gist7480523.GoPackageStringer = func(goPackage *gist7480523.GoPackage) string {
	out := ""

	if repo := goPackage.Dir.Repo; repo != nil {
		out += "@"
		if repo.VcsLocal.LocalBranch != repo.Vcs.GetDefaultBranch() {
			out += "b"
		} else {
			out += " "
		}
		if repo.VcsLocal.Status != "" {
			out += "*"
		} else {
			out += " "
		}
		if repo.VcsLocal.LocalRev != repo.VcsRemote.RemoteRev {
			out += "+"
		} else {
			out += " "
		}
	} else {
		out += "    "
	}
	if goPackage.Bpkg.IsCommand() {
		out += "/"
	} else {
		out += " "
	}

	out += " " + goPackage.Bpkg.ImportPath

	return out
}

// DebugPresenter gives debug output.
//
// It currently is, and must remain read-only and safe for concurrent execution.
var DebugPresenter gist7480523.GoPackageStringer = func(goPackage *gist7480523.GoPackage) string {
	out := goPackage.Bpkg.ImportPath

	out += fmt.Sprintf("\tgoPackage.Dir.Repo=%p", goPackage.Dir.Repo)
	out += fmt.Sprintf("\tgoPackage.Bpkg.SrcRoot=%q", goPackage.Bpkg.SrcRoot)

	if repo := goPackage.Dir.Repo; repo != nil {
		out += fmt.Sprintf("\tRootPath=%q", repo.Vcs.RootPath())
		out += fmt.Sprintf("\tLocalBranch=%q", repo.VcsLocal.LocalBranch)
		out += fmt.Sprintf("\tDefaultBranch=%q", repo.Vcs.GetDefaultBranch())
		out += fmt.Sprintf("\tStatus=%q", repo.VcsLocal.Status)
		out += fmt.Sprintf("\tStash=%q", repo.VcsLocal.Stash)
		if repo.RepoRoot == nil {
			out += fmt.Sprintf("\tRepoRoot=<nil>")
		} else {
			out += fmt.Sprintf("\tRepoRoot.Repo=%q", repo.RepoRoot.Repo)
		}
		out += fmt.Sprintf("\tRemote=%q", repo.VcsLocal.Remote)
		out += fmt.Sprintf("\tLocalRev=%q", repo.VcsLocal.LocalRev)
		out += fmt.Sprintf("\tRemoteRev=%q", repo.VcsRemote.RemoteRev)
		out += fmt.Sprintf("\tIsContained=%v", repo.VcsRemote.IsContained)
	}

	return out
}
<|endoftext|>


<commit_before>// Copyright 2016-2020 The Libsacloud Authors
//
// 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 fake

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"reflect"
	"sort"
	"strings"
	"sync"

	"github.com/fatih/structs"

	"github.com/fsnotify/fsnotify"

	"github.com/sacloud/libsacloud/v2/sacloud"
	"github.com/sacloud/libsacloud/v2/sacloud/types"
)

const defaultJSONFilePath = "libsacloud-fake-store.json"

// JSONFileStore .
type JSONFileStore struct {
	Path       string
	Ctx        context.Context
	NoInitData bool

	mu    sync.Mutex
	cache JSONFileStoreData
}

// JSONFileStoreData .
type JSONFileStoreData map[string]map[string]interface{}

// MarshalJSON .
func (d JSONFileStoreData) MarshalJSON() ([]byte, error) {
	var transformed []map[string]interface{}
	for cacheKey, resources := range d {
		resourceKey, zone := d.parseKey(cacheKey)
		for id, value := range resources {
			var mapValue map[string]interface{}
			if d.isArrayOrSlice(value) {
				mapValue = map[string]interface{}{
					"Values": value,
				}
			} else {
				mapValue = structs.Map(value)
			}

			mapValue["ID"] = id
			mapValue["ZoneName"] = zone
			mapValue["ResourceType"] = resourceKey

			transformed = append(transformed, mapValue)
		}
	}

	sort.Slice(transformed, func(i, j int) bool {
		rt1 := transformed[i]["ResourceType"].(string)
		rt2 := transformed[j]["ResourceType"].(string)
		if rt1 == rt2 {
			id1 := types.StringID(transformed[i]["ID"].(string))
			id2 := types.StringID(transformed[j]["ID"].(string))
			return id1 < id2
		}
		return rt1 < rt2
	})

	return json.MarshalIndent(transformed, "", "\t")
}

// UnmarshalJSON .
func (d *JSONFileStoreData) UnmarshalJSON(data []byte) error {
	var transformed []map[string]interface{}
	if err := json.Unmarshal(data, &transformed); err != nil {
		return err
	}

	dest := JSONFileStoreData{}
	for _, mapValue := range transformed {
		rawID, ok := mapValue["ID"]
		if !ok {
			return fmt.Errorf("invalid JSON: 'ID' field is missing: %v", mapValue)
		}
		id := rawID.(string)

		rawZone, ok := mapValue["ZoneName"]
		if !ok {
			return fmt.Errorf("invalid JSON: 'ZoneName' field is missing: %v", mapValue)
		}
		zone := rawZone.(string)

		rawRt, ok := mapValue["ResourceType"]
		if !ok {
			return fmt.Errorf("invalid JSON: 'ResourceType' field is missing: %v", mapValue)
		}
		rt := rawRt.(string)

		var resources map[string]interface{}
		r, ok := dest[d.key(rt, zone)]
		if ok {
			resources = r
		} else {
			resources = map[string]interface{}{}
		}
		if v, ok := mapValue["Values"]; ok {
			resources[id] = v
		} else {
			resources[id] = mapValue
		}

		dest[d.key(rt, zone)] = resources
	}

	*d = dest
	return nil
}

func (d *JSONFileStoreData) isArrayOrSlice(v interface{}) bool {
	rt := reflect.TypeOf(v)
	switch rt.Kind() {
	case reflect.Slice, reflect.Array:
		return true
	case reflect.Ptr:
		return d.isArrayOrSlice(reflect.ValueOf(v).Elem().Interface())
	}
	return false
}

func (d *JSONFileStoreData) key(resourceKey, zone string) string {
	return fmt.Sprintf("%s/%s", resourceKey, zone)
}

func (d *JSONFileStoreData) parseKey(k string) (string, string) {
	ss := strings.Split(k, "/")
	if len(ss) == 2 {
		return ss[0], ss[1]
	}
	return "", ""
}

// NewJSONFileStore .
func NewJSONFileStore(path string) *JSONFileStore {
	return &JSONFileStore{
		Path:  path,
		cache: make(map[string]map[string]interface{}),
	}
}

// Init .
func (s *JSONFileStore) Init() error {
	if s.Ctx == nil {
		s.Ctx = context.Background()
	}
	if s.Path == "" {
		s.Path = defaultJSONFilePath
	}
	if stat, err := os.Stat(s.Path); err == nil {
		if stat.IsDir() {
			return fmt.Errorf("path %q is directory", s.Path)
		}
	} else {
		if _, err := os.Create(s.Path); err != nil {
			return err
		}
	}

	if err := s.load(); err != nil {
		return err
	}
	s.startWatcher()
	return nil
}

func (s *JSONFileStore) startWatcher() {
	ctx := s.Ctx
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}
	log.Printf("file watch start: %q", s.Path)

	go func() {
		defer watcher.Close()
		for {
			select {
			case event, ok := <-watcher.Events:
				if !ok {
					return
				}
				switch {
				case event.Op&fsnotify.Write == fsnotify.Write,
					event.Op&fsnotify.Create == fsnotify.Create,
					event.Op&fsnotify.Rename == fsnotify.Rename:

					if err := s.load(); err != nil {
						log.Printf("reloading %q is failed: %s\n", s.Path, err)
					}

					if event.Op&fsnotify.Rename == fsnotify.Rename {
						if err := watcher.Add(s.Path); err != nil {
							panic(err)
						}
					}
					log.Printf("reloaded: %q\n", s.Path)
				}
			case err, ok := <-watcher.Errors:
				if !ok {
					return
				}
				panic(err)
			case <-ctx.Done():
				return
			}
		}
	}()
	if err := watcher.Add(s.Path); err != nil {
		panic(err)
	}
}

// NeedInitData .
func (s *JSONFileStore) NeedInitData() bool {
	if s.NoInitData {
		return false
	}
	return len(s.cache) < 2
}

// Put .
func (s *JSONFileStore) Put(resourceKey, zone string, id types.ID, value interface{}) {
	s.mu.Lock()
	defer s.mu.Unlock()

	values := s.values(resourceKey, zone)
	if values == nil {
		values = map[string]interface{}{}
	}
	values[id.String()] = value
	s.cache[s.key(resourceKey, zone)] = values

	s.store() // nolint
}

// Get .
func (s *JSONFileStore) Get(resourceKey, zone string, id types.ID) interface{} {
	s.mu.Lock()
	defer s.mu.Unlock()

	values := s.values(resourceKey, zone)
	if values == nil {
		return nil
	}
	return values[id.String()]
}

// List .
func (s *JSONFileStore) List(resourceKey, zone string) []interface{} {
	s.mu.Lock()
	defer s.mu.Unlock()

	values := s.values(resourceKey, zone)
	var ret []interface{}
	for _, v := range values {
		ret = append(ret, v)
	}
	return ret
}

// Delete .
func (s *JSONFileStore) Delete(resourceKey, zone string, id types.ID) {
	s.mu.Lock()
	defer s.mu.Unlock()

	values := s.values(resourceKey, zone)
	if values != nil {
		delete(values, id.String())
	}
	s.store() // nolint
}

var jsonResourceTypeMap = map[string]func() interface{}{
	ResourceArchive:         func() interface{} { return &sacloud.Archive{} },
	ResourceAuthStatus:      func() interface{} { return &sacloud.AuthStatus{} },
	ResourceAutoBackup:      func() interface{} { return &sacloud.AutoBackup{} },
	ResourceBill:            func() interface{} { return &sacloud.Bill{} },
	ResourceBridge:          func() interface{} { return &sacloud.Bridge{} },
	ResourceCDROM:           func() interface{} { return &sacloud.CDROM{} },
	ResourceCoupon:          func() interface{} { return &sacloud.Coupon{} },
	ResourceDatabase:        func() interface{} { return &sacloud.Database{} },
	ResourceDisk:            func() interface{} { return &sacloud.Disk{} },
	ResourceDiskPlan:        func() interface{} { return &sacloud.DiskPlan{} },
	ResourceDNS:             func() interface{} { return &sacloud.DNS{} },
	ResourceGSLB:            func() interface{} { return &sacloud.GSLB{} },
	ResourceIcon:            func() interface{} { return &sacloud.Icon{} },
	ResourceInterface:       func() interface{} { return &sacloud.Interface{} },
	ResourceInternet:        func() interface{} { return &sacloud.Internet{} },
	ResourceInternetPlan:    func() interface{} { return &sacloud.InternetPlan{} },
	ResourceIPAddress:       func() interface{} { return &sacloud.IPAddress{} },
	ResourceIPv6Net:         func() interface{} { return &sacloud.IPv6Net{} },
	ResourceIPv6Addr:        func() interface{} { return &sacloud.IPv6Addr{} },
	ResourceLicense:         func() interface{} { return &sacloud.License{} },
	ResourceLicenseInfo:     func() interface{} { return &sacloud.LicenseInfo{} },
	ResourceLoadBalancer:    func() interface{} { return &sacloud.LoadBalancer{} },
	ResourceMobileGateway:   func() interface{} { return &sacloud.MobileGateway{} },
	ResourceNFS:             func() interface{} { return &sacloud.NFS{} },
	ResourceNote:            func() interface{} { return &sacloud.Note{} },
	ResourcePacketFilter:    func() interface{} { return &sacloud.PacketFilter{} },
	ResourcePrivateHost:     func() interface{} { return &sacloud.PrivateHost{} },
	ResourcePrivateHostPlan: func() interface{} { return &sacloud.PrivateHostPlan{} },
	ResourceProxyLB:         func() interface{} { return &sacloud.ProxyLB{} },
	ResourceRegion:          func() interface{} { return &sacloud.Region{} },
	ResourceServer:          func() interface{} { return &sacloud.Server{} },
	ResourceServerPlan:      func() interface{} { return &sacloud.ServerPlan{} },
	ResourceServiceClass:    func() interface{} { return &sacloud.ServiceClass{} },
	ResourceSIM:             func() interface{} { return &sacloud.SIM{} },
	ResourceSimpleMonitor:   func() interface{} { return &sacloud.SimpleMonitor{} },
	ResourceSSHKey:          func() interface{} { return &sacloud.SSHKey{} },
	ResourceSwitch:          func() interface{} { return &sacloud.Switch{} },
	ResourceVPCRouter:       func() interface{} { return &sacloud.VPCRouter{} },
	ResourceWebAccel:        func() interface{} { return &sacloud.WebAccel{} },
	ResourceZone:            func() interface{} { return &sacloud.Zone{} },

	valuePoolResourceKey:         func() interface{} { return &valuePool{} },
	"BillDetails":                func() interface{} { return &[]*sacloud.BillDetail{} },
	"MobileGatewayDNS":           func() interface{} { return &sacloud.MobileGatewayDNSSetting{} },
	"MobileGatewaySIMRoutes":     func() interface{} { return &[]*sacloud.MobileGatewaySIMRoute{} },
	"MobileGatewaySIMs":          func() interface{} { return &[]*sacloud.MobileGatewaySIMInfo{} },
	"MobileGatewayTrafficConfig": func() interface{} { return &sacloud.MobileGatewayTrafficControl{} },
	"ProxyLBStatus":              func() interface{} { return &sacloud.ProxyLBHealth{} },
	"SIMNetworkOperator":         func() interface{} { return &[]*sacloud.SIMNetworkOperatorConfig{} },
}

func (s *JSONFileStore) unmarshalResource(resourceKey string, data []byte) (interface{}, error) {
	f, ok := jsonResourceTypeMap[resourceKey]
	if !ok {
		panic(fmt.Errorf("type %q is not registered", resourceKey))
	}
	v := f()
	if err := json.Unmarshal(data, v); err != nil {
		return nil, err
	}
	return v, nil
}

func (s *JSONFileStore) store() error {
	data, err := json.MarshalIndent(s.cache, "", "\t")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(s.Path, data, 0600)
}

func (s *JSONFileStore) load() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	data, err := ioutil.ReadFile(s.Path)
	if err != nil {
		return err
	}
	if len(data) == 0 {
		return nil
	}

	var cache = JSONFileStoreData{}
	if err := json.Unmarshal(data, &cache); err != nil {
		return err
	}

	var loaded = make(map[string]map[string]interface{})
	for cacheKey, values := range cache {
		resourceKey, _ := s.parseKey(cacheKey)

		var dest = make(map[string]interface{})
		for id, v := range values {
			data, err := json.Marshal(v)
			if err != nil {
				return err
			}
			cv, err := s.unmarshalResource(resourceKey, data)
			if err != nil {
				return err
			}
			dest[id] = cv
		}
		loaded[cacheKey] = dest
	}
	s.cache = loaded
	return nil
}

func (s *JSONFileStore) key(resourceKey, zone string) string {
	return fmt.Sprintf("%s/%s", resourceKey, zone)
}

func (s *JSONFileStore) parseKey(k string) (string, string) {
	ss := strings.Split(k, "/")
	if len(ss) == 2 {
		return ss[0], ss[1]
	}
	return "", ""
}

func (s *JSONFileStore) values(resourceKey, zone string) map[string]interface{} {
	return s.cache[s.key(resourceKey, zone)]
}
<commit_msg>Add ResourceTypeMap to fake json store<commit_after>// Copyright 2016-2020 The Libsacloud Authors
//
// 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 fake

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"reflect"
	"sort"
	"strings"
	"sync"

	"github.com/fatih/structs"

	"github.com/fsnotify/fsnotify"

	"github.com/sacloud/libsacloud/v2/sacloud"
	"github.com/sacloud/libsacloud/v2/sacloud/types"
)

const defaultJSONFilePath = "libsacloud-fake-store.json"

// JSONFileStore .
type JSONFileStore struct {
	Path       string
	Ctx        context.Context
	NoInitData bool

	mu    sync.Mutex
	cache JSONFileStoreData
}

// JSONFileStoreData .
type JSONFileStoreData map[string]map[string]interface{}

// MarshalJSON .
func (d JSONFileStoreData) MarshalJSON() ([]byte, error) {
	var transformed []map[string]interface{}
	for cacheKey, resources := range d {
		resourceKey, zone := d.parseKey(cacheKey)
		for id, value := range resources {
			var mapValue map[string]interface{}
			if d.isArrayOrSlice(value) {
				mapValue = map[string]interface{}{
					"Values": value,
				}
			} else {
				mapValue = structs.Map(value)
			}

			mapValue["ID"] = id
			mapValue["ZoneName"] = zone
			mapValue["ResourceType"] = resourceKey

			transformed = append(transformed, mapValue)
		}
	}

	sort.Slice(transformed, func(i, j int) bool {
		rt1 := transformed[i]["ResourceType"].(string)
		rt2 := transformed[j]["ResourceType"].(string)
		if rt1 == rt2 {
			id1 := types.StringID(transformed[i]["ID"].(string))
			id2 := types.StringID(transformed[j]["ID"].(string))
			return id1 < id2
		}
		return rt1 < rt2
	})

	return json.MarshalIndent(transformed, "", "\t")
}

// UnmarshalJSON .
func (d *JSONFileStoreData) UnmarshalJSON(data []byte) error {
	var transformed []map[string]interface{}
	if err := json.Unmarshal(data, &transformed); err != nil {
		return err
	}

	dest := JSONFileStoreData{}
	for _, mapValue := range transformed {
		rawID, ok := mapValue["ID"]
		if !ok {
			return fmt.Errorf("invalid JSON: 'ID' field is missing: %v", mapValue)
		}
		id := rawID.(string)

		rawZone, ok := mapValue["ZoneName"]
		if !ok {
			return fmt.Errorf("invalid JSON: 'ZoneName' field is missing: %v", mapValue)
		}
		zone := rawZone.(string)

		rawRt, ok := mapValue["ResourceType"]
		if !ok {
			return fmt.Errorf("invalid JSON: 'ResourceType' field is missing: %v", mapValue)
		}
		rt := rawRt.(string)

		var resources map[string]interface{}
		r, ok := dest[d.key(rt, zone)]
		if ok {
			resources = r
		} else {
			resources = map[string]interface{}{}
		}
		if v, ok := mapValue["Values"]; ok {
			resources[id] = v
		} else {
			resources[id] = mapValue
		}

		dest[d.key(rt, zone)] = resources
	}

	*d = dest
	return nil
}

func (d *JSONFileStoreData) isArrayOrSlice(v interface{}) bool {
	rt := reflect.TypeOf(v)
	switch rt.Kind() {
	case reflect.Slice, reflect.Array:
		return true
	case reflect.Ptr:
		return d.isArrayOrSlice(reflect.ValueOf(v).Elem().Interface())
	}
	return false
}

func (d *JSONFileStoreData) key(resourceKey, zone string) string {
	return fmt.Sprintf("%s/%s", resourceKey, zone)
}

func (d *JSONFileStoreData) parseKey(k string) (string, string) {
	ss := strings.Split(k, "/")
	if len(ss) == 2 {
		return ss[0], ss[1]
	}
	return "", ""
}

// NewJSONFileStore .
func NewJSONFileStore(path string) *JSONFileStore {
	return &JSONFileStore{
		Path:  path,
		cache: make(map[string]map[string]interface{}),
	}
}

// Init .
func (s *JSONFileStore) Init() error {
	if s.Ctx == nil {
		s.Ctx = context.Background()
	}
	if s.Path == "" {
		s.Path = defaultJSONFilePath
	}
	if stat, err := os.Stat(s.Path); err == nil {
		if stat.IsDir() {
			return fmt.Errorf("path %q is directory", s.Path)
		}
	} else {
		if _, err := os.Create(s.Path); err != nil {
			return err
		}
	}

	if err := s.load(); err != nil {
		return err
	}
	s.startWatcher()
	return nil
}

func (s *JSONFileStore) startWatcher() {
	ctx := s.Ctx
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}
	log.Printf("file watch start: %q", s.Path)

	go func() {
		defer watcher.Close()
		for {
			select {
			case event, ok := <-watcher.Events:
				if !ok {
					return
				}
				switch {
				case event.Op&fsnotify.Write == fsnotify.Write,
					event.Op&fsnotify.Create == fsnotify.Create,
					event.Op&fsnotify.Rename == fsnotify.Rename:

					if err := s.load(); err != nil {
						log.Printf("reloading %q is failed: %s\n", s.Path, err)
					}

					if event.Op&fsnotify.Rename == fsnotify.Rename {
						if err := watcher.Add(s.Path); err != nil {
							panic(err)
						}
					}
					log.Printf("reloaded: %q\n", s.Path)
				}
			case err, ok := <-watcher.Errors:
				if !ok {
					return
				}
				panic(err)
			case <-ctx.Done():
				return
			}
		}
	}()
	if err := watcher.Add(s.Path); err != nil {
		panic(err)
	}
}

// NeedInitData .
func (s *JSONFileStore) NeedInitData() bool {
	if s.NoInitData {
		return false
	}
	return len(s.cache) < 2
}

// Put .
func (s *JSONFileStore) Put(resourceKey, zone string, id types.ID, value interface{}) {
	s.mu.Lock()
	defer s.mu.Unlock()

	values := s.values(resourceKey, zone)
	if values == nil {
		values = map[string]interface{}{}
	}
	values[id.String()] = value
	s.cache[s.key(resourceKey, zone)] = values

	s.store() // nolint
}

// Get .
func (s *JSONFileStore) Get(resourceKey, zone string, id types.ID) interface{} {
	s.mu.Lock()
	defer s.mu.Unlock()

	values := s.values(resourceKey, zone)
	if values == nil {
		return nil
	}
	return values[id.String()]
}

// List .
func (s *JSONFileStore) List(resourceKey, zone string) []interface{} {
	s.mu.Lock()
	defer s.mu.Unlock()

	values := s.values(resourceKey, zone)
	var ret []interface{}
	for _, v := range values {
		ret = append(ret, v)
	}
	return ret
}

// Delete .
func (s *JSONFileStore) Delete(resourceKey, zone string, id types.ID) {
	s.mu.Lock()
	defer s.mu.Unlock()

	values := s.values(resourceKey, zone)
	if values != nil {
		delete(values, id.String())
	}
	s.store() // nolint
}

var jsonResourceTypeMap = map[string]func() interface{}{
	ResourceArchive:           func() interface{} { return &sacloud.Archive{} },
	ResourceAuthStatus:        func() interface{} { return &sacloud.AuthStatus{} },
	ResourceAutoBackup:        func() interface{} { return &sacloud.AutoBackup{} },
	ResourceBill:              func() interface{} { return &sacloud.Bill{} },
	ResourceBridge:            func() interface{} { return &sacloud.Bridge{} },
	ResourceCDROM:             func() interface{} { return &sacloud.CDROM{} },
	ResourceContainerRegistry: func() interface{} { return &sacloud.ContainerRegistry{} },
	ResourceCoupon:            func() interface{} { return &sacloud.Coupon{} },
	ResourceDatabase:          func() interface{} { return &sacloud.Database{} },
	ResourceDisk:              func() interface{} { return &sacloud.Disk{} },
	ResourceDiskPlan:          func() interface{} { return &sacloud.DiskPlan{} },
	ResourceDNS:               func() interface{} { return &sacloud.DNS{} },
	ResourceGSLB:              func() interface{} { return &sacloud.GSLB{} },
	ResourceIcon:              func() interface{} { return &sacloud.Icon{} },
	ResourceInterface:         func() interface{} { return &sacloud.Interface{} },
	ResourceInternet:          func() interface{} { return &sacloud.Internet{} },
	ResourceInternetPlan:      func() interface{} { return &sacloud.InternetPlan{} },
	ResourceIPAddress:         func() interface{} { return &sacloud.IPAddress{} },
	ResourceIPv6Net:           func() interface{} { return &sacloud.IPv6Net{} },
	ResourceIPv6Addr:          func() interface{} { return &sacloud.IPv6Addr{} },
	ResourceLicense:           func() interface{} { return &sacloud.License{} },
	ResourceLicenseInfo:       func() interface{} { return &sacloud.LicenseInfo{} },
	ResourceLoadBalancer:      func() interface{} { return &sacloud.LoadBalancer{} },
	ResourceMobileGateway:     func() interface{} { return &sacloud.MobileGateway{} },
	ResourceNFS:               func() interface{} { return &sacloud.NFS{} },
	ResourceNote:              func() interface{} { return &sacloud.Note{} },
	ResourcePacketFilter:      func() interface{} { return &sacloud.PacketFilter{} },
	ResourcePrivateHost:       func() interface{} { return &sacloud.PrivateHost{} },
	ResourcePrivateHostPlan:   func() interface{} { return &sacloud.PrivateHostPlan{} },
	ResourceProxyLB:           func() interface{} { return &sacloud.ProxyLB{} },
	ResourceRegion:            func() interface{} { return &sacloud.Region{} },
	ResourceServer:            func() interface{} { return &sacloud.Server{} },
	ResourceServerPlan:        func() interface{} { return &sacloud.ServerPlan{} },
	ResourceServiceClass:      func() interface{} { return &sacloud.ServiceClass{} },
	ResourceSIM:               func() interface{} { return &sacloud.SIM{} },
	ResourceSimpleMonitor:     func() interface{} { return &sacloud.SimpleMonitor{} },
	ResourceSubnet:            func() interface{} { return &sacloud.Subnet{} },
	ResourceSSHKey:            func() interface{} { return &sacloud.SSHKey{} },
	ResourceSwitch:            func() interface{} { return &sacloud.Switch{} },
	ResourceVPCRouter:         func() interface{} { return &sacloud.VPCRouter{} },
	ResourceWebAccel:          func() interface{} { return &sacloud.WebAccel{} },
	ResourceZone:              func() interface{} { return &sacloud.Zone{} },

	valuePoolResourceKey:         func() interface{} { return &valuePool{} },
	"BillDetails":                func() interface{} { return &[]*sacloud.BillDetail{} },
	"MobileGatewayDNS":           func() interface{} { return &sacloud.MobileGatewayDNSSetting{} },
	"MobileGatewaySIMRoutes":     func() interface{} { return &[]*sacloud.MobileGatewaySIMRoute{} },
	"MobileGatewaySIMs":          func() interface{} { return &[]*sacloud.MobileGatewaySIMInfo{} },
	"MobileGatewayTrafficConfig": func() interface{} { return &sacloud.MobileGatewayTrafficControl{} },
	"ProxyLBStatus":              func() interface{} { return &sacloud.ProxyLBHealth{} },
	"SIMNetworkOperator":         func() interface{} { return &[]*sacloud.SIMNetworkOperatorConfig{} },
}

func (s *JSONFileStore) unmarshalResource(resourceKey string, data []byte) (interface{}, error) {
	f, ok := jsonResourceTypeMap[resourceKey]
	if !ok {
		panic(fmt.Errorf("type %q is not registered", resourceKey))
	}
	v := f()
	if err := json.Unmarshal(data, v); err != nil {
		return nil, err
	}
	return v, nil
}

func (s *JSONFileStore) store() error {
	data, err := json.MarshalIndent(s.cache, "", "\t")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(s.Path, data, 0600)
}

func (s *JSONFileStore) load() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	data, err := ioutil.ReadFile(s.Path)
	if err != nil {
		return err
	}
	if len(data) == 0 {
		return nil
	}

	var cache = JSONFileStoreData{}
	if err := json.Unmarshal(data, &cache); err != nil {
		return err
	}

	var loaded = make(map[string]map[string]interface{})
	for cacheKey, values := range cache {
		resourceKey, _ := s.parseKey(cacheKey)

		var dest = make(map[string]interface{})
		for id, v := range values {
			data, err := json.Marshal(v)
			if err != nil {
				return err
			}
			cv, err := s.unmarshalResource(resourceKey, data)
			if err != nil {
				return err
			}
			dest[id] = cv
		}
		loaded[cacheKey] = dest
	}
	s.cache = loaded
	return nil
}

func (s *JSONFileStore) key(resourceKey, zone string) string {
	return fmt.Sprintf("%s/%s", resourceKey, zone)
}

func (s *JSONFileStore) parseKey(k string) (string, string) {
	ss := strings.Split(k, "/")
	if len(ss) == 2 {
		return ss[0], ss[1]
	}
	return "", ""
}

func (s *JSONFileStore) values(resourceKey, zone string) map[string]interface{} {
	return s.cache[s.key(resourceKey, zone)]
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"fmt"
	"warcluster/db_manager"
	"warcluster/entities"
)

// The three constants bellow are used by calculateCanvasSize to determine the size of the area for wich information will be sent to the user.
const BEST_PING = 150
const WORST_PING = 1500
const STEPS = 10

// calculateCanvasSize is used to determine how big of an area(information about an area)
// do we need to send to the user to eleminate traces of lag.
func calculateCanvasSize(position []int, resolution []int, lag int) ([]int, []int) {
	step := int(WORST_PING - BEST_PING/STEPS)
	multiply := 1.1 + float32((lag-BEST_PING)/step)*0.1
	end_resolution := []int{
		int(float32(resolution[0]) * multiply),
		int(float32(resolution[1]) * multiply),
	}

	top_left := []int{
		position[0] - int((end_resolution[0]-resolution[0])/2),
		position[1] - int((end_resolution[1]-resolution[1])/2),
	}

	bottom_right := []int{
		position[0] + resolution[0] + int((end_resolution[0]-resolution[0])/2),
		position[1] + resolution[1] + int((end_resolution[1]-resolution[1])/2),
	}
	return top_left, bottom_right
}

// scopeOfView is not finished yet but the purpose of the function is to call calculateCanvasSize
// and give the player the information contained in the given borders.
//
//  TODO: Make some proper JSON Unmarshaling out here
func scopeOfView(request *Request) error {
	var line string
	entity_list := db_manager.GetEntities("*")
	for _, entity := range entity_list {
		switch t := entity.(type) {
		case *entities.Mission, *entities.Planet, *entities.Player, *entities.Sun:
			if key, json, err := t.Serialize(); err == nil {
				line += fmt.Sprintf("\"%v\": %s, ", key, json)
			}
		}
	}
	line = fmt.Sprintf("{\"Command\": \"scope_of_view_result\", \"Planets\": {%v}}", line[:len(line)-2])
	request.Client.Session.Send([]byte(fmt.Sprintf("%v", line)))
	return nil
}

// This function makes all the checks needed for creation of a new mission.
func parseAction(request *Request) error {
	var err error = nil

	defer func() error {
		if panicked := recover(); panicked != nil {
			err = errors.New("Invalid action!")
		}
		return nil
	}()

	source, err := db_manager.GetEntity(request.StartPlanet)
	if err != nil {
		return errors.New("Start planet does not exist")
	}

	target, err := db_manager.GetEntity(request.EndPlanet)
	if err != nil {
		return errors.New("End planet does not exist")
	}

	if source.(*entities.Planet).Owner != request.Client.Player.String() {
		err = errors.New("This is not your home!")
	}

	mission := request.Client.Player.StartMission(source.(*entities.Planet), target.(*entities.Planet), request.Fleet)
	if _, serialized_mission, err := mission.Serialize(); err == nil {
		go StartMissionary(mission)
		db_manager.SetEntity(mission)
		sessions.Broadcast([]byte(fmt.Sprintf("{ \"Command\": \"send_mission\", \"mission\": %s}", serialized_mission)))
		if source_key, source_json, source_err := source.Serialize(); source_err == nil {
			sessions.Broadcast([]byte(fmt.Sprintf("{\"Command\": \"state_change\", \"Planets\": {\"%s\": %s}}", source_key, source_json)))
			db_manager.SetEntity(source)
		}
		return nil
	}

	return err
}
<commit_msg>Add mission type and capitalize mission key in send_mission<commit_after>package server

import (
	"errors"
	"fmt"
	"warcluster/db_manager"
	"warcluster/entities"
)

// The three constants bellow are used by calculateCanvasSize to determine the size of the area for wich information will be sent to the user.
const BEST_PING = 150
const WORST_PING = 1500
const STEPS = 10

// calculateCanvasSize is used to determine how big of an area(information about an area)
// do we need to send to the user to eleminate traces of lag.
func calculateCanvasSize(position []int, resolution []int, lag int) ([]int, []int) {
	step := int(WORST_PING - BEST_PING/STEPS)
	multiply := 1.1 + float32((lag-BEST_PING)/step)*0.1
	end_resolution := []int{
		int(float32(resolution[0]) * multiply),
		int(float32(resolution[1]) * multiply),
	}

	top_left := []int{
		position[0] - int((end_resolution[0]-resolution[0])/2),
		position[1] - int((end_resolution[1]-resolution[1])/2),
	}

	bottom_right := []int{
		position[0] + resolution[0] + int((end_resolution[0]-resolution[0])/2),
		position[1] + resolution[1] + int((end_resolution[1]-resolution[1])/2),
	}
	return top_left, bottom_right
}

// scopeOfView is not finished yet but the purpose of the function is to call calculateCanvasSize
// and give the player the information contained in the given borders.
//
//  TODO: Make some proper JSON Unmarshaling out here
func scopeOfView(request *Request) error {
	var line string
	entity_list := db_manager.GetEntities("*")
	for _, entity := range entity_list {
		switch t := entity.(type) {
		case *entities.Mission, *entities.Planet, *entities.Player, *entities.Sun:
			if key, json, err := t.Serialize(); err == nil {
				line += fmt.Sprintf("\"%v\": %s, ", key, json)
			}
		}
	}
	line = fmt.Sprintf("{\"Command\": \"scope_of_view_result\", \"Planets\": {%v}}", line[:len(line)-2])
	request.Client.Session.Send([]byte(fmt.Sprintf("%v", line)))
	return nil
}

// This function makes all the checks needed for creation of a new mission.
func parseAction(request *Request) error {
	var err error = nil

	defer func() error {
		if panicked := recover(); panicked != nil {
			err = errors.New("Invalid action!")
		}
		return nil
	}()

	source, err := db_manager.GetEntity(request.StartPlanet)
	if err != nil {
		return errors.New("Start planet does not exist")
	}

	target, err := db_manager.GetEntity(request.EndPlanet)
	if err != nil {
		return errors.New("End planet does not exist")
	}

	if source.(*entities.Planet).Owner != request.Client.Player.String() {
		err = errors.New("This is not your home!")
	}

	mission := request.Client.Player.StartMission(source.(*entities.Planet), target.(*entities.Planet), request.Fleet)
	if _, serialized_mission, err := mission.Serialize(); err == nil {
		go StartMissionary(mission)
		db_manager.SetEntity(mission)
		sessions.Broadcast([]byte(fmt.Sprintf("{ \"Command\": \"send_mission\", \"Type\": \"attack\", \"Mission\": %s}", serialized_mission)))
		if source_key, source_json, source_err := source.Serialize(); source_err == nil {
			sessions.Broadcast([]byte(fmt.Sprintf("{\"Command\": \"state_change\", \"Planets\": {\"%s\": %s}}", source_key, source_json)))
			db_manager.SetEntity(source)
		}
		return nil
	}

	return err
}
<|endoftext|>