<commit_before>package ttf

//#cgo windows LDFLAGS: -lSDL2 -lSDL2_ttf
//#cgo linux freebsd darwin pkg-config: sdl2
//#cgo linux freebsd darwin LDFLAGS: -lSDL2_ttf
//#include <stdlib.h>
//#include "sdl_ttf_wrapper.h"
//void Do_TTF_SetError(const char *str) {
//    TTF_SetError(str);
//}
import "C"
import "github.com/veandco/go-sdl2/sdl"
import "unsafe"
import "errors"

//Font Hinting Types
const (
	HINTING_NORMAL = int(C.TTF_HINTING_NORMAL)
	HINTING_LIGHT  = int(C.TTF_HINTING_LIGHT)
	HINTING_MONO   = int(C.TTF_HINTING_MONO)
	HINTING_NONE   = int(C.TTF_HINTING_NONE)
)

//Font Style Types
const (
	STYLE_NORMAL        = 0
	STYLE_BOLD          = 0x01
	STYLE_ITALIC        = 0x02
	STYLE_UNDERLINE     = 0x04
	STYLE_STRIKETHROUGH = 0x08
)

type Font struct {
	f *C.TTF_Font
}

func Init() int {
	return int(C.TTF_Init())
}

func WasInit() bool {
	return int(C.TTF_WasInit()) != 0
}

func Quit() {
	C.TTF_Quit()
}

func GetError() error {
	e := C.TTF_GetError()
	if e == nil {
		return nil
	}
	return errors.New(C.GoString(e))
}

func SetError(err string) {
	_err := C.CString(err)
	defer C.free(unsafe.Pointer(_err))
	C.Do_TTF_SetError(_err)
}

func ByteSwappedUnicode(swap bool) {
	val := 0
	if swap {
		val = 1
	}
	C.TTF_ByteSwappedUNICODE(C.int(val))
}

func OpenFont(file string, size int) (*Font, error) {
	_file := C.CString(file)
	defer C.free(unsafe.Pointer(_file))
	_size := (C.int)(size)
	f := (*C.TTF_Font)(C.TTF_OpenFont(_file, _size))

	if f == nil {
		return nil, GetError()
	}
	return &Font{f}, nil
}

func OpenFontIndex(file string, size int, index int) (*Font, error) {
	_file := C.CString(file)
	defer C.free(unsafe.Pointer(_file))
	_size := (C.int)(size)
	_index := (C.long)(index)
	f := (*C.TTF_Font)(C.TTF_OpenFontIndex(_file, _size, _index))

	if f == nil {
		return nil, GetError()
	}
	return &Font{f}, nil
}

func (f *Font) RenderUTF8_Solid(text string, color sdl.Color) (*sdl.Surface,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	_c := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.A)}
	surface := (*sdl.Surface)(unsafe.Pointer(C.TTF_RenderUTF8_Solid(f.f, _text, _c)))
	if surface == nil {
		return nil, GetError()
	}
	return surface, nil
}
func (f *Font) RenderUTF8_Shaded(text string, fg, bg sdl.Color) (*sdl.Surface,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	_fg := C.SDL_Color{C.Uint8(fg.R), C.Uint8(fg.G), C.Uint8(fg.B), C.Uint8(fg.A)}
	_bg := C.SDL_Color{C.Uint8(bg.R), C.Uint8(bg.G), C.Uint8(bg.B), C.Uint8(bg.A)}
	surface := (*sdl.Surface)(unsafe.Pointer(C.TTF_RenderUTF8_Shaded(f.f, _text, _fg, _bg)))
	if surface == nil {
		return nil, GetError()
	}
	return surface, nil
}
func (f *Font) RenderUTF8_Blended(text string, color sdl.Color) (*sdl.Surface,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	_c := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.A)}
	surface := (*sdl.Surface)(unsafe.Pointer(C.TTF_RenderUTF8_Blended(f.f, _text, _c)))
	if surface == nil {
		return nil, GetError()
	}
	return surface, nil
}

func (f *Font) RenderUTF8_Blended_Wrapped(text string, fg sdl.Color,wrapLength int) (*sdl.Surface,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	_c := C.SDL_Color{C.Uint8(fg.R), C.Uint8(fg.G), C.Uint8(fg.B), C.Uint8(fg.A)}
	surface := (*sdl.Surface)(unsafe.Pointer(C.TTF_RenderUTF8_Blended_Wrapped(f.f, _text, _c, C.Uint32(wrapLength))))
	if surface == nil {
		return nil, GetError()
	}
	return surface, nil
}

func (f *Font) SizeUTF8(text string) (int,int,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	var w C.int
	var h C.int
	result :=  C.TTF_SizeUTF8(f.f,_text,&w,&h)
	if result == 0 {
		return int(w),int(h),nil
	}
	return int(w),int(h),GetError()
}

func (f *Font) Close() {
	C.TTF_CloseFont(f.f)
	f.f = nil
}

func (f *Font) Height() int   { return int(C.TTF_FontHeight(f.f)) }
func (f *Font) Ascent() int   { return int(C.TTF_FontAscent(f.f)) }
func (f *Font) Descent() int  { return int(C.TTF_FontDescent(f.f)) }
func (f *Font) LineSkip() int { return int(C.TTF_FontLineSkip(f.f)) }
func (f *Font) Faces() int    { return int(C.TTF_FontFaces(f.f)) }

func (f *Font) GetStyle() int {
	return int(C.TTF_GetFontStyle(f.f))
}

func (f *Font) SetStyle(style int) {
	C.TTF_SetFontStyle(f.f, C.int(style))
}

func (f *Font) GetHinting() int {
	return int(C.TTF_GetFontHinting(f.f))
}

func (f *Font) SetHinting(hinting int) {
	C.TTF_SetFontHinting(f.f, C.int(hinting))
}

func (f *Font) GetKerning() bool {
	return int(C.TTF_GetFontKerning(f.f)) == 1
}

func (f *Font) SetKerning(allowed bool) {
	val := 0
	if allowed {
		val = 1
	}
	C.TTF_SetFontKerning(f.f, C.int(val))
}

func (f *Font) GetOutline() int {
	return int(C.TTF_GetFontOutline(f.f))
}

func (f *Font) SetOutline(outline int) {
	C.TTF_SetFontOutline(f.f, C.int(outline))
}

func (f *Font) FaceIsFixedWidth() bool {
	return int(C.TTF_FontFaceIsFixedWidth(f.f)) != 0
}

func (f *Font) FaceFamilyName() string {
	_fname := C.TTF_FontFaceFamilyName(f.f)
	fname := C.GoString(_fname)
	return fname
}
<commit_msg>Add OpenFontRW, OpenFontIndexRW functions<commit_after>package ttf

//#cgo windows LDFLAGS: -lSDL2 -lSDL2_ttf
//#cgo linux freebsd darwin pkg-config: sdl2
//#cgo linux freebsd darwin LDFLAGS: -lSDL2_ttf
//#include <stdlib.h>
//#include "sdl_ttf_wrapper.h"
//void Do_TTF_SetError(const char *str) {
//    TTF_SetError(str);
//}
import "C"
import "github.com/veandco/go-sdl2/sdl"
import "unsafe"
import "errors"

//Font Hinting Types
const (
	HINTING_NORMAL = int(C.TTF_HINTING_NORMAL)
	HINTING_LIGHT  = int(C.TTF_HINTING_LIGHT)
	HINTING_MONO   = int(C.TTF_HINTING_MONO)
	HINTING_NONE   = int(C.TTF_HINTING_NONE)
)

//Font Style Types
const (
	STYLE_NORMAL        = 0
	STYLE_BOLD          = 0x01
	STYLE_ITALIC        = 0x02
	STYLE_UNDERLINE     = 0x04
	STYLE_STRIKETHROUGH = 0x08
)

type Font struct {
	f *C.TTF_Font
}

func Init() int {
	return int(C.TTF_Init())
}

func WasInit() bool {
	return int(C.TTF_WasInit()) != 0
}

func Quit() {
	C.TTF_Quit()
}

func GetError() error {
	e := C.TTF_GetError()
	if e == nil {
		return nil
	}
	return errors.New(C.GoString(e))
}

func SetError(err string) {
	_err := C.CString(err)
	defer C.free(unsafe.Pointer(_err))
	C.Do_TTF_SetError(_err)
}

func ByteSwappedUnicode(swap bool) {
	val := 0
	if swap {
		val = 1
	}
	C.TTF_ByteSwappedUNICODE(C.int(val))
}

func OpenFont(file string, size int) (*Font, error) {
	_file := C.CString(file)
	defer C.free(unsafe.Pointer(_file))
	_size := (C.int)(size)
	f := (*C.TTF_Font)(C.TTF_OpenFont(_file, _size))

	if f == nil {
		return nil, GetError()
	}
	return &Font{f}, nil
}

func OpenFontIndex(file string, size int, index int) (*Font, error) {
	_file := C.CString(file)
	defer C.free(unsafe.Pointer(_file))
	_size := (C.int)(size)
	_index := (C.long)(index)
	f := (*C.TTF_Font)(C.TTF_OpenFontIndex(_file, _size, _index))

	if f == nil {
		return nil, GetError()
	}
	return &Font{f}, nil
}

func OpenFontRW(src *sdl.RWops, freesrc, size int) (*Font, error) {
	return OpenFontIndexRW(src, freesrc, size, 0)
}

func OpenFontIndexRW(src *sdl.RWops, freesrc, size, index int) (*Font, error) {
	_src := (*C.SDL_RWops)(unsafe.Pointer(src))
	_freesrc := (C.int)(freesrc)
	_size := (C.int)(size)
	_index := (C.long)(index)
	f := (*C.TTF_Font)(C.TTF_OpenFontIndexRW(_src, _freesrc, _size, _index))

	if f == nil {
		return nil, GetError()
	}
	return &Font{f}, nil
}

func (f *Font) RenderUTF8_Solid(text string, color sdl.Color) (*sdl.Surface,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	_c := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.A)}
	surface := (*sdl.Surface)(unsafe.Pointer(C.TTF_RenderUTF8_Solid(f.f, _text, _c)))
	if surface == nil {
		return nil, GetError()
	}
	return surface, nil
}
func (f *Font) RenderUTF8_Shaded(text string, fg, bg sdl.Color) (*sdl.Surface,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	_fg := C.SDL_Color{C.Uint8(fg.R), C.Uint8(fg.G), C.Uint8(fg.B), C.Uint8(fg.A)}
	_bg := C.SDL_Color{C.Uint8(bg.R), C.Uint8(bg.G), C.Uint8(bg.B), C.Uint8(bg.A)}
	surface := (*sdl.Surface)(unsafe.Pointer(C.TTF_RenderUTF8_Shaded(f.f, _text, _fg, _bg)))
	if surface == nil {
		return nil, GetError()
	}
	return surface, nil
}
func (f *Font) RenderUTF8_Blended(text string, color sdl.Color) (*sdl.Surface,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	_c := C.SDL_Color{C.Uint8(color.R), C.Uint8(color.G), C.Uint8(color.B), C.Uint8(color.A)}
	surface := (*sdl.Surface)(unsafe.Pointer(C.TTF_RenderUTF8_Blended(f.f, _text, _c)))
	if surface == nil {
		return nil, GetError()
	}
	return surface, nil
}

func (f *Font) RenderUTF8_Blended_Wrapped(text string, fg sdl.Color,wrapLength int) (*sdl.Surface,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	_c := C.SDL_Color{C.Uint8(fg.R), C.Uint8(fg.G), C.Uint8(fg.B), C.Uint8(fg.A)}
	surface := (*sdl.Surface)(unsafe.Pointer(C.TTF_RenderUTF8_Blended_Wrapped(f.f, _text, _c, C.Uint32(wrapLength))))
	if surface == nil {
		return nil, GetError()
	}
	return surface, nil
}

func (f *Font) SizeUTF8(text string) (int,int,error) {
	_text := C.CString(text)
	defer C.free(unsafe.Pointer(_text))
	var w C.int
	var h C.int
	result :=  C.TTF_SizeUTF8(f.f,_text,&w,&h)
	if result == 0 {
		return int(w),int(h),nil
	}
	return int(w),int(h),GetError()
}

func (f *Font) Close() {
	C.TTF_CloseFont(f.f)
	f.f = nil
}

func (f *Font) Height() int   { return int(C.TTF_FontHeight(f.f)) }
func (f *Font) Ascent() int   { return int(C.TTF_FontAscent(f.f)) }
func (f *Font) Descent() int  { return int(C.TTF_FontDescent(f.f)) }
func (f *Font) LineSkip() int { return int(C.TTF_FontLineSkip(f.f)) }
func (f *Font) Faces() int    { return int(C.TTF_FontFaces(f.f)) }

func (f *Font) GetStyle() int {
	return int(C.TTF_GetFontStyle(f.f))
}

func (f *Font) SetStyle(style int) {
	C.TTF_SetFontStyle(f.f, C.int(style))
}

func (f *Font) GetHinting() int {
	return int(C.TTF_GetFontHinting(f.f))
}

func (f *Font) SetHinting(hinting int) {
	C.TTF_SetFontHinting(f.f, C.int(hinting))
}

func (f *Font) GetKerning() bool {
	return int(C.TTF_GetFontKerning(f.f)) == 1
}

func (f *Font) SetKerning(allowed bool) {
	val := 0
	if allowed {
		val = 1
	}
	C.TTF_SetFontKerning(f.f, C.int(val))
}

func (f *Font) GetOutline() int {
	return int(C.TTF_GetFontOutline(f.f))
}

func (f *Font) SetOutline(outline int) {
	C.TTF_SetFontOutline(f.f, C.int(outline))
}

func (f *Font) FaceIsFixedWidth() bool {
	return int(C.TTF_FontFaceIsFixedWidth(f.f)) != 0
}

func (f *Font) FaceFamilyName() string {
	_fname := C.TTF_FontFaceFamilyName(f.f)
	fname := C.GoString(_fname)
	return fname
}
<|endoftext|>


<commit_before>package kcp

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/des"
	"crypto/sha1"

	"golang.org/x/crypto/blowfish"
	"golang.org/x/crypto/cast5"
	"golang.org/x/crypto/pbkdf2"
	"golang.org/x/crypto/salsa20"
	"golang.org/x/crypto/tea"
	"golang.org/x/crypto/twofish"
	"golang.org/x/crypto/xtea"
)

var (
	initialVector = []byte{167, 115, 79, 156, 18, 172, 27, 1, 164, 21, 242, 193, 252, 120, 230, 107}
	saltxor       = `sH3CIVoF#rWLtJo6`
)

// BlockCrypt defines encryption/decryption methods for a given byte slice.
// Notes on implementing: the data to be encrypted contains a builtin
// nonce at the first 16 bytes
type BlockCrypt interface {
	// Encrypt encrypts the whole block in src into dst.
	// Dst and src may point at the same memory.
	Encrypt(dst, src []byte)

	// Decrypt decrypts the whole block in src into dst.
	// Dst and src may point at the same memory.
	Decrypt(dst, src []byte)
}

type salsa20BlockCrypt struct {
	key [32]byte
}

// NewSalsa20BlockCrypt https://en.wikipedia.org/wiki/Salsa20
func NewSalsa20BlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(salsa20BlockCrypt)
	copy(c.key[:], key)
	return c, nil
}

func (c *salsa20BlockCrypt) Encrypt(dst, src []byte) {
	salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
	copy(dst[:8], src[:8])
}
func (c *salsa20BlockCrypt) Decrypt(dst, src []byte) {
	salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
	copy(dst[:8], src[:8])
}

type twofishBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewTwofishBlockCrypt https://en.wikipedia.org/wiki/Twofish
func NewTwofishBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(twofishBlockCrypt)
	block, err := twofish.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, twofish.BlockSize)
	c.decbuf = make([]byte, 2*twofish.BlockSize)
	return c, nil
}

func (c *twofishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *twofishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type tripleDESBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewTripleDESBlockCrypt https://en.wikipedia.org/wiki/Triple_DES
func NewTripleDESBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(tripleDESBlockCrypt)
	block, err := des.NewTripleDESCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, des.BlockSize)
	c.decbuf = make([]byte, 2*des.BlockSize)
	return c, nil
}

func (c *tripleDESBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *tripleDESBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type cast5BlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewCast5BlockCrypt https://en.wikipedia.org/wiki/CAST-128
func NewCast5BlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(cast5BlockCrypt)
	block, err := cast5.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, cast5.BlockSize)
	c.decbuf = make([]byte, 2*cast5.BlockSize)
	return c, nil
}

func (c *cast5BlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *cast5BlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type blowfishBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewBlowfishBlockCrypt https://en.wikipedia.org/wiki/Blowfish_(cipher)
func NewBlowfishBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(blowfishBlockCrypt)
	block, err := blowfish.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, blowfish.BlockSize)
	c.decbuf = make([]byte, 2*blowfish.BlockSize)
	return c, nil
}

func (c *blowfishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *blowfishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type aesBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewAESBlockCrypt https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
func NewAESBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(aesBlockCrypt)
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, aes.BlockSize)
	c.decbuf = make([]byte, 2*aes.BlockSize)
	return c, nil
}

func (c *aesBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *aesBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type teaBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewTEABlockCrypt https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
func NewTEABlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(teaBlockCrypt)
	block, err := tea.NewCipherWithRounds(key, 16)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, tea.BlockSize)
	c.decbuf = make([]byte, 2*tea.BlockSize)
	return c, nil
}

func (c *teaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *teaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type xteaBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewXTEABlockCrypt https://en.wikipedia.org/wiki/XTEA
func NewXTEABlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(xteaBlockCrypt)
	block, err := xtea.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, xtea.BlockSize)
	c.decbuf = make([]byte, 2*xtea.BlockSize)
	return c, nil
}

func (c *xteaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *xteaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type simpleXORBlockCrypt struct {
	xortbl []byte
}

// NewSimpleXORBlockCrypt simple xor with key expanding
func NewSimpleXORBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(simpleXORBlockCrypt)
	c.xortbl = pbkdf2.Key(key, []byte(saltxor), 32, mtuLimit, sha1.New)
	return c, nil
}

func (c *simpleXORBlockCrypt) Encrypt(dst, src []byte) { xorBytes(dst, src, c.xortbl) }
func (c *simpleXORBlockCrypt) Decrypt(dst, src []byte) { xorBytes(dst, src, c.xortbl) }

type noneBlockCrypt struct{}

// NewNoneBlockCrypt does nothing but copying
func NewNoneBlockCrypt(key []byte) (BlockCrypt, error) {
	return new(noneBlockCrypt), nil
}

func (c *noneBlockCrypt) Encrypt(dst, src []byte) { copy(dst, src) }
func (c *noneBlockCrypt) Decrypt(dst, src []byte) { copy(dst, src) }

// packet encryption with local CFB mode
func encrypt(block cipher.Block, dst, src, buf []byte) {
	blocksize := block.BlockSize()
	tbl := buf[:blocksize]
	block.Encrypt(tbl, initialVector)
	n := len(src) / blocksize
	base := 0
	for i := 0; i < n; i++ {
		xorWords(dst[base:], src[base:], tbl)
		block.Encrypt(tbl, dst[base:])
		base += blocksize
	}
	xorBytes(dst[base:], src[base:], tbl)
}

func decrypt(block cipher.Block, dst, src, buf []byte) {
	blocksize := block.BlockSize()
	tbl := buf[:blocksize]
	next := buf[blocksize:]
	block.Encrypt(tbl, initialVector)
	n := len(src) / blocksize
	base := 0
	for i := 0; i < n; i++ {
		block.Encrypt(next, src[base:])
		xorWords(dst[base:], src[base:], tbl)
		tbl, next = next, tbl
		base += blocksize
	}
	xorBytes(dst[base:], src[base:], tbl)
}
<commit_msg>use avx2 & sse2 optimized xor, estimated 3 times faster<commit_after>package kcp

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/des"
	"crypto/sha1"

	"github.com/templexxx/xor"

	"golang.org/x/crypto/blowfish"
	"golang.org/x/crypto/cast5"
	"golang.org/x/crypto/pbkdf2"
	"golang.org/x/crypto/salsa20"
	"golang.org/x/crypto/tea"
	"golang.org/x/crypto/twofish"
	"golang.org/x/crypto/xtea"
)

var (
	initialVector = []byte{167, 115, 79, 156, 18, 172, 27, 1, 164, 21, 242, 193, 252, 120, 230, 107}
	saltxor       = `sH3CIVoF#rWLtJo6`
)

// BlockCrypt defines encryption/decryption methods for a given byte slice.
// Notes on implementing: the data to be encrypted contains a builtin
// nonce at the first 16 bytes
type BlockCrypt interface {
	// Encrypt encrypts the whole block in src into dst.
	// Dst and src may point at the same memory.
	Encrypt(dst, src []byte)

	// Decrypt decrypts the whole block in src into dst.
	// Dst and src may point at the same memory.
	Decrypt(dst, src []byte)
}

type salsa20BlockCrypt struct {
	key [32]byte
}

// NewSalsa20BlockCrypt https://en.wikipedia.org/wiki/Salsa20
func NewSalsa20BlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(salsa20BlockCrypt)
	copy(c.key[:], key)
	return c, nil
}

func (c *salsa20BlockCrypt) Encrypt(dst, src []byte) {
	salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
	copy(dst[:8], src[:8])
}
func (c *salsa20BlockCrypt) Decrypt(dst, src []byte) {
	salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
	copy(dst[:8], src[:8])
}

type twofishBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewTwofishBlockCrypt https://en.wikipedia.org/wiki/Twofish
func NewTwofishBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(twofishBlockCrypt)
	block, err := twofish.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, twofish.BlockSize)
	c.decbuf = make([]byte, 2*twofish.BlockSize)
	return c, nil
}

func (c *twofishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *twofishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type tripleDESBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewTripleDESBlockCrypt https://en.wikipedia.org/wiki/Triple_DES
func NewTripleDESBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(tripleDESBlockCrypt)
	block, err := des.NewTripleDESCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, des.BlockSize)
	c.decbuf = make([]byte, 2*des.BlockSize)
	return c, nil
}

func (c *tripleDESBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *tripleDESBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type cast5BlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewCast5BlockCrypt https://en.wikipedia.org/wiki/CAST-128
func NewCast5BlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(cast5BlockCrypt)
	block, err := cast5.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, cast5.BlockSize)
	c.decbuf = make([]byte, 2*cast5.BlockSize)
	return c, nil
}

func (c *cast5BlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *cast5BlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type blowfishBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewBlowfishBlockCrypt https://en.wikipedia.org/wiki/Blowfish_(cipher)
func NewBlowfishBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(blowfishBlockCrypt)
	block, err := blowfish.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, blowfish.BlockSize)
	c.decbuf = make([]byte, 2*blowfish.BlockSize)
	return c, nil
}

func (c *blowfishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *blowfishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type aesBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewAESBlockCrypt https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
func NewAESBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(aesBlockCrypt)
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, aes.BlockSize)
	c.decbuf = make([]byte, 2*aes.BlockSize)
	return c, nil
}

func (c *aesBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *aesBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type teaBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewTEABlockCrypt https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
func NewTEABlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(teaBlockCrypt)
	block, err := tea.NewCipherWithRounds(key, 16)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, tea.BlockSize)
	c.decbuf = make([]byte, 2*tea.BlockSize)
	return c, nil
}

func (c *teaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *teaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type xteaBlockCrypt struct {
	encbuf []byte
	decbuf []byte
	block  cipher.Block
}

// NewXTEABlockCrypt https://en.wikipedia.org/wiki/XTEA
func NewXTEABlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(xteaBlockCrypt)
	block, err := xtea.NewCipher(key)
	if err != nil {
		return nil, err
	}
	c.block = block
	c.encbuf = make([]byte, xtea.BlockSize)
	c.decbuf = make([]byte, 2*xtea.BlockSize)
	return c, nil
}

func (c *xteaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf) }
func (c *xteaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf) }

type simpleXORBlockCrypt struct {
	xortbl []byte
}

// NewSimpleXORBlockCrypt simple xor with key expanding
func NewSimpleXORBlockCrypt(key []byte) (BlockCrypt, error) {
	c := new(simpleXORBlockCrypt)
	c.xortbl = pbkdf2.Key(key, []byte(saltxor), 32, mtuLimit, sha1.New)
	return c, nil
}

func (c *simpleXORBlockCrypt) Encrypt(dst, src []byte) { xor.Bytes(dst, src, c.xortbl) }
func (c *simpleXORBlockCrypt) Decrypt(dst, src []byte) { xor.Bytes(dst, src, c.xortbl) }

type noneBlockCrypt struct{}

// NewNoneBlockCrypt does nothing but copying
func NewNoneBlockCrypt(key []byte) (BlockCrypt, error) {
	return new(noneBlockCrypt), nil
}

func (c *noneBlockCrypt) Encrypt(dst, src []byte) { copy(dst, src) }
func (c *noneBlockCrypt) Decrypt(dst, src []byte) { copy(dst, src) }

// packet encryption with local CFB mode
func encrypt(block cipher.Block, dst, src, buf []byte) {
	blocksize := block.BlockSize()
	tbl := buf[:blocksize]
	block.Encrypt(tbl, initialVector)
	n := len(src) / blocksize
	base := 0
	for i := 0; i < n; i++ {
		xor.BytesSrc1(dst[base:], src[base:], tbl)
		block.Encrypt(tbl, dst[base:])
		base += blocksize
	}
	xor.BytesSrc0(dst[base:], src[base:], tbl)
}

func decrypt(block cipher.Block, dst, src, buf []byte) {
	blocksize := block.BlockSize()
	tbl := buf[:blocksize]
	next := buf[blocksize:]
	block.Encrypt(tbl, initialVector)
	n := len(src) / blocksize
	base := 0
	for i := 0; i < n; i++ {
		block.Encrypt(next, src[base:])
		xor.BytesSrc1(dst[base:], src[base:], tbl)
		tbl, next = next, tbl
		base += blocksize
	}
	xor.BytesSrc0(dst[base:], src[base:], tbl)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Ceph-CSI 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 cephfs

import (
	"context"
	"fmt"
	"path"
	"strings"

	"github.com/ceph/ceph-csi/internal/util"

	fsAdmin "github.com/ceph/go-ceph/cephfs/admin"
)

var (
	// clusterAdditionalInfo contains information regarding if resize is
	// supported in the particular cluster and subvolumegroup is
	// created or not.
	// Subvolumegroup creation and volume resize decisions are
	// taken through this additional cluster information.
	clusterAdditionalInfo = make(map[string]*localClusterState)
)

const (
	cephEntityClientPrefix = "client."

	// modeAllRWX can be used for setting permissions to Read-Write-eXecute
	// for User, Group and Other.
	modeAllRWX = 0777
)

// Subvolume holds subvolume information. This includes only the needed members
// from fsAdmin.SubVolumeInfo.
type Subvolume struct {
	BytesQuota int64
	Path       string
	Features   []string
}

func getVolumeRootPathCephDeprecated(volID volumeID) string {
	return path.Join("/", "csi-volumes", string(volID))
}

func (vo *volumeOptions) getVolumeRootPathCeph(ctx context.Context, volID volumeID) (string, error) {
	fsa, err := vo.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin err %s", err)
		return "", err
	}
	svPath, err := fsa.SubVolumePath(vo.FsName, vo.SubvolumeGroup, string(volID))
	if err != nil {
		util.ErrorLog(ctx, "failed to get the rootpath for the vol %s: %s", string(volID), err)
		if strings.Contains(err.Error(), volumeNotFound) {
			return "", util.JoinErrors(ErrVolumeNotFound, err)
		}
		return "", err
	}
	return svPath, nil
}

func (vo *volumeOptions) getSubVolumeInfo(ctx context.Context, volID volumeID) (*Subvolume, error) {
	fsa, err := vo.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin, can not fetch metadata pool for %s:", vo.FsName, err)
		return nil, err
	}

	info, err := fsa.SubVolumeInfo(vo.FsName, vo.SubvolumeGroup, string(volID))
	if err != nil {
		util.ErrorLog(ctx, "failed to get subvolume info for the vol %s: %s", string(volID), err)
		if strings.HasPrefix(err.Error(), volumeNotFound) {
			return nil, ErrVolumeNotFound
		}
		// In case the error is other than invalid command return error to the caller.
		if !strings.Contains(err.Error(), invalidCommand) {
			return nil, ErrInvalidCommand
		}

		return nil, err
	}

	subvol := Subvolume{
		// only set BytesQuota when it is of type ByteCount
		Path:     info.Path,
		Features: make([]string, len(info.Features)),
	}
	bc, ok := info.BytesQuota.(fsAdmin.ByteCount)
	if !ok {
		// we ignore info.BytesQuota == Infinite and just continue
		// without returning quota information
		if info.BytesQuota != fsAdmin.Infinite {
			return nil, fmt.Errorf("subvolume %s has unsupported quota: %v", string(volID), info.BytesQuota)
		}
	} else {
		subvol.BytesQuota = int64(bc)
	}
	for i, feature := range info.Features {
		subvol.Features[i] = string(feature)
	}

	return &subvol, nil
}

type localClusterState struct {
	// set true if cluster supports resize functionality.
	resizeSupported bool
	// set true once a subvolumegroup is created
	// for corresponding cluster.
	subVolumeGroupCreated bool
}

func createVolume(ctx context.Context, volOptions *volumeOptions, volID volumeID, bytesQuota int64) error {
	// verify if corresponding ClusterID key is present in the map,
	// and if not, initialize with default values(false).
	if _, keyPresent := clusterAdditionalInfo[volOptions.ClusterID]; !keyPresent {
		clusterAdditionalInfo[volOptions.ClusterID] = &localClusterState{}
	}

	ca, err := volOptions.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin, can not create subvolume %s: %s", string(volID), err)
		return err
	}

	// create subvolumegroup if not already created for the cluster.
	if !clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated {
		opts := fsAdmin.SubVolumeGroupOptions{}
		err = ca.CreateSubVolumeGroup(volOptions.FsName, volOptions.SubvolumeGroup, &opts)
		if err != nil {
			util.ErrorLog(ctx, "failed to create subvolume group %s, for the vol %s: %s", volOptions.SubvolumeGroup, string(volID), err)
			return err
		}
		util.DebugLog(ctx, "cephfs: created subvolume group %s", volOptions.SubvolumeGroup)
		clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated = true
	}

	opts := fsAdmin.SubVolumeOptions{
		Size: fsAdmin.ByteCount(bytesQuota),
		Mode: modeAllRWX,
	}
	if volOptions.Pool != "" {
		opts.PoolLayout = volOptions.Pool
	}

	// FIXME: check if the right credentials are used ("-n", cephEntityClientPrefix + cr.ID)
	err = ca.CreateSubVolume(volOptions.FsName, volOptions.SubvolumeGroup, string(volID), &opts)
	if err != nil {
		util.ErrorLog(ctx, "failed to create subvolume %s in fs %s: %s", string(volID), volOptions.FsName, err)
		return err
	}

	return nil
}

// resizeVolume will try to use ceph fs subvolume resize command to resize the
// subvolume. If the command is not available as a fallback it will use
// CreateVolume to resize the subvolume.
func (vo *volumeOptions) resizeVolume(ctx context.Context, volID volumeID, bytesQuota int64) error {
	// keyPresent checks whether corresponding clusterID key is present in clusterAdditionalInfo
	var keyPresent bool
	// verify if corresponding ClusterID key is present in the map,
	// and if not, initialize with default values(false).
	if _, keyPresent = clusterAdditionalInfo[vo.ClusterID]; !keyPresent {
		clusterAdditionalInfo[vo.ClusterID] = &localClusterState{}
	}
	// resize subvolume when either it's supported, or when corresponding
	// clusterID key was not present.
	if clusterAdditionalInfo[vo.ClusterID].resizeSupported || !keyPresent {
		fsa, err := vo.conn.GetFSAdmin()
		if err != nil {
			util.ErrorLog(ctx, "could not get FSAdmin, can not resize volume %s:", vo.FsName, err)
			return err
		}

		_, err = fsa.ResizeSubVolume(vo.FsName, vo.SubvolumeGroup, string(volID), fsAdmin.ByteCount(bytesQuota), true)
		if err == nil {
			clusterAdditionalInfo[vo.ClusterID].resizeSupported = true
			return nil
		}
		// In case the error is other than invalid command return error to the caller.
		if !strings.Contains(err.Error(), invalidCommand) {
			util.ErrorLog(ctx, "failed to resize subvolume %s in fs %s: %s", string(volID), vo.FsName, err)
			return err
		}
	}
	clusterAdditionalInfo[vo.ClusterID].resizeSupported = false
	return createVolume(ctx, vo, volID, bytesQuota)
}

func purgeVolume(ctx context.Context, volID volumeID, cr *util.Credentials, volOptions *volumeOptions, force bool) error {
	arg := []string{
		"fs",
		"subvolume",
		"rm",
		volOptions.FsName,
		string(volID),
		"--group_name",
		volOptions.SubvolumeGroup,
		"-m", volOptions.Monitors,
		"-c", util.CephConfigPath,
		"-n", cephEntityClientPrefix + cr.ID,
		"--keyfile=" + cr.KeyFile,
	}
	if force {
		arg = append(arg, "--force")
	}
	if checkSubvolumeHasFeature("snapshot-retention", volOptions.Features) {
		arg = append(arg, "--retain-snapshots")
	}

	err := execCommandErr(ctx, "ceph", arg...)
	if err != nil {
		util.ErrorLog(ctx, "failed to purge subvolume %s in fs %s: %s", string(volID), volOptions.FsName, err)
		if strings.Contains(err.Error(), volumeNotEmpty) {
			return util.JoinErrors(ErrVolumeHasSnapshots, err)
		}
		if strings.Contains(err.Error(), volumeNotFound) {
			return util.JoinErrors(ErrVolumeNotFound, err)
		}
		return err
	}

	return nil
}

// checkSubvolumeHasFeature verifies if the referred subvolume has
// the required feature.
func checkSubvolumeHasFeature(feature string, subVolFeatures []string) bool {
	// The subvolume "features" are based on the internal version of the subvolume.
	// Verify if subvolume supports the required feature.
	for _, subvolFeature := range subVolFeatures {
		if subvolFeature == feature {
			return true
		}
	}
	return false
}
<commit_msg>cephfs: use rados.ErrNotFound in getVolumeRootPathCeph<commit_after>/*
Copyright 2018 The Ceph-CSI 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 cephfs

import (
	"context"
	"errors"
	"fmt"
	"path"
	"strings"

	"github.com/ceph/ceph-csi/internal/util"

	fsAdmin "github.com/ceph/go-ceph/cephfs/admin"
	"github.com/ceph/go-ceph/rados"
)

var (
	// clusterAdditionalInfo contains information regarding if resize is
	// supported in the particular cluster and subvolumegroup is
	// created or not.
	// Subvolumegroup creation and volume resize decisions are
	// taken through this additional cluster information.
	clusterAdditionalInfo = make(map[string]*localClusterState)
)

const (
	cephEntityClientPrefix = "client."

	// modeAllRWX can be used for setting permissions to Read-Write-eXecute
	// for User, Group and Other.
	modeAllRWX = 0777
)

// Subvolume holds subvolume information. This includes only the needed members
// from fsAdmin.SubVolumeInfo.
type Subvolume struct {
	BytesQuota int64
	Path       string
	Features   []string
}

func getVolumeRootPathCephDeprecated(volID volumeID) string {
	return path.Join("/", "csi-volumes", string(volID))
}

func (vo *volumeOptions) getVolumeRootPathCeph(ctx context.Context, volID volumeID) (string, error) {
	fsa, err := vo.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin err %s", err)
		return "", err
	}
	svPath, err := fsa.SubVolumePath(vo.FsName, vo.SubvolumeGroup, string(volID))
	if err != nil {
		util.ErrorLog(ctx, "failed to get the rootpath for the vol %s: %s", string(volID), err)
		if errors.Is(err, rados.ErrNotFound) {
			return "", util.JoinErrors(ErrVolumeNotFound, err)
		}
		return "", err
	}
	return svPath, nil
}

func (vo *volumeOptions) getSubVolumeInfo(ctx context.Context, volID volumeID) (*Subvolume, error) {
	fsa, err := vo.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin, can not fetch metadata pool for %s:", vo.FsName, err)
		return nil, err
	}

	info, err := fsa.SubVolumeInfo(vo.FsName, vo.SubvolumeGroup, string(volID))
	if err != nil {
		util.ErrorLog(ctx, "failed to get subvolume info for the vol %s: %s", string(volID), err)
		if strings.HasPrefix(err.Error(), volumeNotFound) {
			return nil, ErrVolumeNotFound
		}
		// In case the error is other than invalid command return error to the caller.
		if !strings.Contains(err.Error(), invalidCommand) {
			return nil, ErrInvalidCommand
		}

		return nil, err
	}

	subvol := Subvolume{
		// only set BytesQuota when it is of type ByteCount
		Path:     info.Path,
		Features: make([]string, len(info.Features)),
	}
	bc, ok := info.BytesQuota.(fsAdmin.ByteCount)
	if !ok {
		// we ignore info.BytesQuota == Infinite and just continue
		// without returning quota information
		if info.BytesQuota != fsAdmin.Infinite {
			return nil, fmt.Errorf("subvolume %s has unsupported quota: %v", string(volID), info.BytesQuota)
		}
	} else {
		subvol.BytesQuota = int64(bc)
	}
	for i, feature := range info.Features {
		subvol.Features[i] = string(feature)
	}

	return &subvol, nil
}

type localClusterState struct {
	// set true if cluster supports resize functionality.
	resizeSupported bool
	// set true once a subvolumegroup is created
	// for corresponding cluster.
	subVolumeGroupCreated bool
}

func createVolume(ctx context.Context, volOptions *volumeOptions, volID volumeID, bytesQuota int64) error {
	// verify if corresponding ClusterID key is present in the map,
	// and if not, initialize with default values(false).
	if _, keyPresent := clusterAdditionalInfo[volOptions.ClusterID]; !keyPresent {
		clusterAdditionalInfo[volOptions.ClusterID] = &localClusterState{}
	}

	ca, err := volOptions.conn.GetFSAdmin()
	if err != nil {
		util.ErrorLog(ctx, "could not get FSAdmin, can not create subvolume %s: %s", string(volID), err)
		return err
	}

	// create subvolumegroup if not already created for the cluster.
	if !clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated {
		opts := fsAdmin.SubVolumeGroupOptions{}
		err = ca.CreateSubVolumeGroup(volOptions.FsName, volOptions.SubvolumeGroup, &opts)
		if err != nil {
			util.ErrorLog(ctx, "failed to create subvolume group %s, for the vol %s: %s", volOptions.SubvolumeGroup, string(volID), err)
			return err
		}
		util.DebugLog(ctx, "cephfs: created subvolume group %s", volOptions.SubvolumeGroup)
		clusterAdditionalInfo[volOptions.ClusterID].subVolumeGroupCreated = true
	}

	opts := fsAdmin.SubVolumeOptions{
		Size: fsAdmin.ByteCount(bytesQuota),
		Mode: modeAllRWX,
	}
	if volOptions.Pool != "" {
		opts.PoolLayout = volOptions.Pool
	}

	// FIXME: check if the right credentials are used ("-n", cephEntityClientPrefix + cr.ID)
	err = ca.CreateSubVolume(volOptions.FsName, volOptions.SubvolumeGroup, string(volID), &opts)
	if err != nil {
		util.ErrorLog(ctx, "failed to create subvolume %s in fs %s: %s", string(volID), volOptions.FsName, err)
		return err
	}

	return nil
}

// resizeVolume will try to use ceph fs subvolume resize command to resize the
// subvolume. If the command is not available as a fallback it will use
// CreateVolume to resize the subvolume.
func (vo *volumeOptions) resizeVolume(ctx context.Context, volID volumeID, bytesQuota int64) error {
	// keyPresent checks whether corresponding clusterID key is present in clusterAdditionalInfo
	var keyPresent bool
	// verify if corresponding ClusterID key is present in the map,
	// and if not, initialize with default values(false).
	if _, keyPresent = clusterAdditionalInfo[vo.ClusterID]; !keyPresent {
		clusterAdditionalInfo[vo.ClusterID] = &localClusterState{}
	}
	// resize subvolume when either it's supported, or when corresponding
	// clusterID key was not present.
	if clusterAdditionalInfo[vo.ClusterID].resizeSupported || !keyPresent {
		fsa, err := vo.conn.GetFSAdmin()
		if err != nil {
			util.ErrorLog(ctx, "could not get FSAdmin, can not resize volume %s:", vo.FsName, err)
			return err
		}

		_, err = fsa.ResizeSubVolume(vo.FsName, vo.SubvolumeGroup, string(volID), fsAdmin.ByteCount(bytesQuota), true)
		if err == nil {
			clusterAdditionalInfo[vo.ClusterID].resizeSupported = true
			return nil
		}
		// In case the error is other than invalid command return error to the caller.
		if !strings.Contains(err.Error(), invalidCommand) {
			util.ErrorLog(ctx, "failed to resize subvolume %s in fs %s: %s", string(volID), vo.FsName, err)
			return err
		}
	}
	clusterAdditionalInfo[vo.ClusterID].resizeSupported = false
	return createVolume(ctx, vo, volID, bytesQuota)
}

func purgeVolume(ctx context.Context, volID volumeID, cr *util.Credentials, volOptions *volumeOptions, force bool) error {
	arg := []string{
		"fs",
		"subvolume",
		"rm",
		volOptions.FsName,
		string(volID),
		"--group_name",
		volOptions.SubvolumeGroup,
		"-m", volOptions.Monitors,
		"-c", util.CephConfigPath,
		"-n", cephEntityClientPrefix + cr.ID,
		"--keyfile=" + cr.KeyFile,
	}
	if force {
		arg = append(arg, "--force")
	}
	if checkSubvolumeHasFeature("snapshot-retention", volOptions.Features) {
		arg = append(arg, "--retain-snapshots")
	}

	err := execCommandErr(ctx, "ceph", arg...)
	if err != nil {
		util.ErrorLog(ctx, "failed to purge subvolume %s in fs %s: %s", string(volID), volOptions.FsName, err)
		if strings.Contains(err.Error(), volumeNotEmpty) {
			return util.JoinErrors(ErrVolumeHasSnapshots, err)
		}
		if strings.Contains(err.Error(), volumeNotFound) {
			return util.JoinErrors(ErrVolumeNotFound, err)
		}
		return err
	}

	return nil
}

// checkSubvolumeHasFeature verifies if the referred subvolume has
// the required feature.
func checkSubvolumeHasFeature(feature string, subVolFeatures []string) bool {
	// The subvolume "features" are based on the internal version of the subvolume.
	// Verify if subvolume supports the required feature.
	for _, subvolFeature := range subVolFeatures {
		if subvolFeature == feature {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package sentry

import (
	"errors"
	"fmt"
	"github.com/getsentry/raven-go"
	"github.com/gin-gonic/gin"
	"net/http"
	"runtime/debug"
)

func Recovery(client *raven.Client, onlyCrashes bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			flags := map[string]string{
				"endpoint": c.Request.RequestURI,
			}
			if rval := recover(); rval != nil {
				debug.PrintStack()
				rvalStr := fmt.Sprint(rval)
				packet := raven.NewPacket(rvalStr, raven.NewException(errors.New(rvalStr), raven.NewStacktrace(2, 3, nil)))
				client.Capture(packet, flags)
				c.Writer.WriteHeader(http.StatusInternalServerError)
			}
			if !onlyCrashes {
				for _, item := range c.Errors {
					packet := raven.NewPacket(item.Err, &raven.Message{item.Err, []interface{}{item.Meta}})
					client.Capture(packet, flags)
				}
			}
		}()
		c.Next()
	}
}
<commit_msg>Fixes sentry middleware<commit_after>package sentry

import (
	"errors"
	"fmt"
	"net/http"
	"runtime/debug"

	"github.com/getsentry/raven-go"
	"github.com/gin-gonic/gin"
)

func Recovery(client *raven.Client, onlyCrashes bool) gin.HandlerFunc {

	return func(c *gin.Context) {
		defer func() {
			flags := map[string]string{
				"endpoint": c.Request.RequestURI,
			}
			if rval := recover(); rval != nil {
				debug.PrintStack()
				rvalStr := fmt.Sprint(rval)
				packet := raven.NewPacket(rvalStr, raven.NewException(errors.New(rvalStr), raven.NewStacktrace(2, 3, nil)))
				client.Capture(packet, map[string]string{
					"endpoint": c.Request.RequestURI,
				})
				c.AbortWithStatus(http.StatusInternalServerError)
			}
			if !onlyCrashes {
				for _, item := range c.Errors {
					packet := raven.NewPacket(item.Error.Error(), &raven.Message{
						Message: item.Error.Error(),
						Params:  []interface{}{item.Meta},
					})
					client.Capture(packet, flags)
				}
			}
		}()

		c.Next()
	}
}
<|endoftext|>


<commit_before>package util

import "hash/crc64"

var table = crc64.MakeTable(crc64.ECMA)

// Hash returns a 64-bit hash of p.
func Hash64(p []byte) uint64 {
	return crc64.Checksum(p, table)
}
<commit_msg>remove unused fn<commit_after><|endoftext|>


<commit_before>package common

import (
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"log"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"
)

const KeyLeftShift uint32 = 0xFFE1

type bootCommandTemplateData struct {
	HTTPIP   string
	HTTPPort uint
	Name     string
}

// This step "types" the boot command into the VM via prltype, built on the
// Parallels Virtualization SDK - C API.
//
// Uses:
//   driver Driver
//   http_port int
//   ui     packer.Ui
//   vmName string
//
// Produces:
//   <nothing>
type StepTypeBootCommand struct {
	BootCommand    []string
	HostInterfaces []string
	VMName         string
	Tpl            *packer.ConfigTemplate
}

func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
	httpPort := state.Get("http_port").(uint)
	ui := state.Get("ui").(packer.Ui)
	driver := state.Get("driver").(Driver)

	hostIp := "0.0.0.0"

	if len(s.HostInterfaces) > 0 {
		// Determine the host IP
		ipFinder := &IfconfigIPFinder{Devices: s.HostInterfaces}

		ip, err := ipFinder.HostIP()
		if err != nil {
			err := fmt.Errorf("Error detecting host IP: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
		hostIp = ip
	}

	ui.Say(fmt.Sprintf("Host IP for the Parallels machine: %s", hostIp))

	tplData := &bootCommandTemplateData{
		hostIp,
		httpPort,
		s.VMName,
	}

	ui.Say("Typing the boot command...")
	for _, command := range s.BootCommand {
		command, err := s.Tpl.Process(command, tplData)
		if err != nil {
			err := fmt.Errorf("Error preparing boot command: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		codes := []string{}
		for _, code := range scancodes(command) {
			if code == "wait" {
				if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
					err := fmt.Errorf("Error sending boot command: %s", err)
					state.Put("error", err)
					ui.Error(err.Error())
					return multistep.ActionHalt
				}
				codes = []string{}
				time.Sleep(1 * time.Second)
				continue
			}

			if code == "wait5" {
				if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
					err := fmt.Errorf("Error sending boot command: %s", err)
					state.Put("error", err)
					ui.Error(err.Error())
					return multistep.ActionHalt
				}
				codes = []string{}
				time.Sleep(5 * time.Second)
				continue
			}

			if code == "wait10" {
				if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
					err := fmt.Errorf("Error sending boot command: %s", err)
					state.Put("error", err)
					ui.Error(err.Error())
					return multistep.ActionHalt
				}
				codes = []string{}
				time.Sleep(10 * time.Second)
				continue
			}

			// Since typing is sometimes so slow, we check for an interrupt
			// in between each character.
			if _, ok := state.GetOk(multistep.StateCancelled); ok {
				return multistep.ActionHalt
			}
			codes = append(codes, code)
		}
		log.Printf("Sending scancodes: %#v", codes)
		if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
			err := fmt.Errorf("Error sending boot command: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}

	return multistep.ActionContinue
}

func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}

func scancodes(message string) []string {
	// Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
	//
	// Scancodes represent raw keyboard output and are fed to the VM by the
	// Parallels Virtualization SDK - C API, PrlDevKeyboard_SendKeyEvent
	//
	// Scancodes are recorded here in pairs. The first entry represents
	// the key press and the second entry represents the key release and is
	// derived from the first by the addition of 0x80.
	special := make(map[string][]string)
	special["<bs>"] = []string{"0e", "8e"}
	special["<del>"] = []string{"53", "d3"}
	special["<enter>"] = []string{"1c", "9c"}
	special["<esc>"] = []string{"01", "81"}
	special["<f1>"] = []string{"3b", "bb"}
	special["<f2>"] = []string{"3c", "bc"}
	special["<f3>"] = []string{"3d", "bd"}
	special["<f4>"] = []string{"3e", "be"}
	special["<f5>"] = []string{"3f", "bf"}
	special["<f6>"] = []string{"40", "c0"}
	special["<f7>"] = []string{"41", "c1"}
	special["<f8>"] = []string{"42", "c2"}
	special["<f9>"] = []string{"43", "c3"}
	special["<f10>"] = []string{"44", "c4"}
	special["<return>"] = []string{"1c", "9c"}
	special["<tab>"] = []string{"0f", "8f"}

	shiftedChars := "!@#$%^&*()_+{}:\"~|<>?"

	scancodeIndex := make(map[string]uint)
	scancodeIndex["1234567890-="] = 0x02
	scancodeIndex["!@#$%^&*()_+"] = 0x02
	scancodeIndex["qwertyuiop[]"] = 0x10
	scancodeIndex["QWERTYUIOP{}"] = 0x10
	scancodeIndex["asdfghjkl;'`"] = 0x1e
	scancodeIndex[`ASDFGHJKL:"~`] = 0x1e
	scancodeIndex["\\zxcvbnm,./"] = 0x2b
	scancodeIndex["|ZXCVBNM<>?"] = 0x2b
	scancodeIndex[" "] = 0x39

	scancodeMap := make(map[rune]uint)
	for chars, start := range scancodeIndex {
		var i uint = 0
		for len(chars) > 0 {
			r, size := utf8.DecodeRuneInString(chars)
			chars = chars[size:]
			scancodeMap[r] = start + i
			i += 1
		}
	}

	result := make([]string, 0, len(message)*2)
	for len(message) > 0 {
		var scancode []string

		if strings.HasPrefix(message, "<wait>") {
			log.Printf("Special code <wait> found, will sleep 1 second at this point.")
			scancode = []string{"wait"}
			message = message[len("<wait>"):]
		}

		if strings.HasPrefix(message, "<wait5>") {
			log.Printf("Special code <wait5> found, will sleep 5 seconds at this point.")
			scancode = []string{"wait5"}
			message = message[len("<wait5>"):]
		}

		if strings.HasPrefix(message, "<wait10>") {
			log.Printf("Special code <wait10> found, will sleep 10 seconds at this point.")
			scancode = []string{"wait10"}
			message = message[len("<wait10>"):]
		}

		if scancode == nil {
			for specialCode, specialValue := range special {
				if strings.HasPrefix(message, specialCode) {
					log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue)
					scancode = specialValue
					message = message[len(specialCode):]
					break
				}
			}
		}

		if scancode == nil {
			r, size := utf8.DecodeRuneInString(message)
			message = message[size:]
			scancodeInt := scancodeMap[r]
			keyShift := unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)

			scancode = make([]string, 0, 4)
			if keyShift {
				scancode = append(scancode, "2a")
			}

			scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt))
			scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt+0x80))

			if keyShift {
				scancode = append(scancode, "aa")
			}
		}

		result = append(result, scancode...)
	}

	return result
}
<commit_msg>builder/parallels: Added some navigation keys. Fixes [GH-1442]<commit_after>package common

import (
	"fmt"
	"log"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"

	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
)

const KeyLeftShift uint32 = 0xFFE1

type bootCommandTemplateData struct {
	HTTPIP   string
	HTTPPort uint
	Name     string
}

// This step "types" the boot command into the VM via prltype, built on the
// Parallels Virtualization SDK - C API.
//
// Uses:
//   driver Driver
//   http_port int
//   ui     packer.Ui
//   vmName string
//
// Produces:
//   <nothing>
type StepTypeBootCommand struct {
	BootCommand    []string
	HostInterfaces []string
	VMName         string
	Tpl            *packer.ConfigTemplate
}

func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
	httpPort := state.Get("http_port").(uint)
	ui := state.Get("ui").(packer.Ui)
	driver := state.Get("driver").(Driver)

	hostIp := "0.0.0.0"

	if len(s.HostInterfaces) > 0 {
		// Determine the host IP
		ipFinder := &IfconfigIPFinder{Devices: s.HostInterfaces}

		ip, err := ipFinder.HostIP()
		if err != nil {
			err := fmt.Errorf("Error detecting host IP: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
		hostIp = ip
	}

	ui.Say(fmt.Sprintf("Host IP for the Parallels machine: %s", hostIp))

	tplData := &bootCommandTemplateData{
		hostIp,
		httpPort,
		s.VMName,
	}

	ui.Say("Typing the boot command...")
	for _, command := range s.BootCommand {
		command, err := s.Tpl.Process(command, tplData)
		if err != nil {
			err := fmt.Errorf("Error preparing boot command: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}

		codes := []string{}
		for _, code := range scancodes(command) {
			if code == "wait" {
				if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
					err := fmt.Errorf("Error sending boot command: %s", err)
					state.Put("error", err)
					ui.Error(err.Error())
					return multistep.ActionHalt
				}
				codes = []string{}
				time.Sleep(1 * time.Second)
				continue
			}

			if code == "wait5" {
				if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
					err := fmt.Errorf("Error sending boot command: %s", err)
					state.Put("error", err)
					ui.Error(err.Error())
					return multistep.ActionHalt
				}
				codes = []string{}
				time.Sleep(5 * time.Second)
				continue
			}

			if code == "wait10" {
				if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
					err := fmt.Errorf("Error sending boot command: %s", err)
					state.Put("error", err)
					ui.Error(err.Error())
					return multistep.ActionHalt
				}
				codes = []string{}
				time.Sleep(10 * time.Second)
				continue
			}

			// Since typing is sometimes so slow, we check for an interrupt
			// in between each character.
			if _, ok := state.GetOk(multistep.StateCancelled); ok {
				return multistep.ActionHalt
			}
			codes = append(codes, code)
		}
		log.Printf("Sending scancodes: %#v", codes)
		if err := driver.SendKeyScanCodes(s.VMName, codes...); err != nil {
			err := fmt.Errorf("Error sending boot command: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt
		}
	}

	return multistep.ActionContinue
}

func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}

func scancodes(message string) []string {
	// Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
	//
	// Scancodes represent raw keyboard output and are fed to the VM by the
	// Parallels Virtualization SDK - C API, PrlDevKeyboard_SendKeyEvent
	//
	// Scancodes are recorded here in pairs. The first entry represents
	// the key press and the second entry represents the key release and is
	// derived from the first by the addition of 0x80.
	special := make(map[string][]string)
	special["<bs>"] = []string{"0e", "8e"}
	special["<del>"] = []string{"53", "d3"}
	special["<enter>"] = []string{"1c", "9c"}
	special["<esc>"] = []string{"01", "81"}
	special["<f1>"] = []string{"3b", "bb"}
	special["<f2>"] = []string{"3c", "bc"}
	special["<f3>"] = []string{"3d", "bd"}
	special["<f4>"] = []string{"3e", "be"}
	special["<f5>"] = []string{"3f", "bf"}
	special["<f6>"] = []string{"40", "c0"}
	special["<f7>"] = []string{"41", "c1"}
	special["<f8>"] = []string{"42", "c2"}
	special["<f9>"] = []string{"43", "c3"}
	special["<f10>"] = []string{"44", "c4"}
	special["<return>"] = []string{"1c", "9c"}
	special["<tab>"] = []string{"0f", "8f"}

	special["<up>"] = []string{"48", "c8"}
	special["<down>"] = []string{"50", "d0"}
	special["<left>"] = []string{"4b", "cb"}
	special["<right>"] = []string{"4d", "cd"}
	special["<spacebar>"] = []string{"39", "b9"}
	special["<insert>"] = []string{"52", "d2"}
	special["<home>"] = []string{"47", "c7"}
	special["<end>"] = []string{"4f", "cf"}
	special["<pageUp>"] = []string{"49", "c9"}
	special["<pageDown>"] = []string{"51", "d1"}

	shiftedChars := "!@#$%^&*()_+{}:\"~|<>?"

	scancodeIndex := make(map[string]uint)
	scancodeIndex["1234567890-="] = 0x02
	scancodeIndex["!@#$%^&*()_+"] = 0x02
	scancodeIndex["qwertyuiop[]"] = 0x10
	scancodeIndex["QWERTYUIOP{}"] = 0x10
	scancodeIndex["asdfghjkl;'`"] = 0x1e
	scancodeIndex[`ASDFGHJKL:"~`] = 0x1e
	scancodeIndex["\\zxcvbnm,./"] = 0x2b
	scancodeIndex["|ZXCVBNM<>?"] = 0x2b
	scancodeIndex[" "] = 0x39

	scancodeMap := make(map[rune]uint)
	for chars, start := range scancodeIndex {
		var i uint = 0
		for len(chars) > 0 {
			r, size := utf8.DecodeRuneInString(chars)
			chars = chars[size:]
			scancodeMap[r] = start + i
			i += 1
		}
	}

	result := make([]string, 0, len(message)*2)
	for len(message) > 0 {
		var scancode []string

		if strings.HasPrefix(message, "<wait>") {
			log.Printf("Special code <wait> found, will sleep 1 second at this point.")
			scancode = []string{"wait"}
			message = message[len("<wait>"):]
		}

		if strings.HasPrefix(message, "<wait5>") {
			log.Printf("Special code <wait5> found, will sleep 5 seconds at this point.")
			scancode = []string{"wait5"}
			message = message[len("<wait5>"):]
		}

		if strings.HasPrefix(message, "<wait10>") {
			log.Printf("Special code <wait10> found, will sleep 10 seconds at this point.")
			scancode = []string{"wait10"}
			message = message[len("<wait10>"):]
		}

		if scancode == nil {
			for specialCode, specialValue := range special {
				if strings.HasPrefix(message, specialCode) {
					log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue)
					scancode = specialValue
					message = message[len(specialCode):]
					break
				}
			}
		}

		if scancode == nil {
			r, size := utf8.DecodeRuneInString(message)
			message = message[size:]
			scancodeInt := scancodeMap[r]
			keyShift := unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)

			scancode = make([]string, 0, 4)
			if keyShift {
				scancode = append(scancode, "2a")
			}

			scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt))
			scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt+0x80))

			if keyShift {
				scancode = append(scancode, "aa")
			}
		}

		result = append(result, scancode...)
	}

	return result
}
<|endoftext|>


<commit_before>package gochimp3

import (
	"errors"
	"fmt"
)

const (
	members_path       = "/lists/%s/members"
	single_member_path = members_path + "/%s"

	member_activity_path = single_member_path + "/activity"
	member_goals_path    = single_member_path + "/goals"

	member_notes_path       = single_member_path + "/notes"
	single_member_note_path = member_notes_path + "/%s"

	member_tags_path       = single_member_path + "/tags"
	single_member_tag_path = member_tags_path + "/%s"
)

type ListOfMembers struct {
	baseList

	ListID  string   `json:"list_id"`
	Members []Member `json:"members"`
}

type MemberResponse struct {
	EmailAddress    string                 `json:"email_address"`
	EmailType       string                 `json:"email_type,omitempty"`
	Status          string                 `json:"status"`
	StatusIfNew     string                 `json:"status_if_new,omitempty"`
	MergeFields     map[string]interface{} `json:"merge_fields,omitempty"`
	Interests       map[string]bool        `json:"interests,omitempty"`
	Language        string                 `json:"language"`
	VIP             bool                   `json:"vip"`
	Location        *MemberLocation        `json:"location,omitempty"`
	IPOpt           string                 `json:"ip_opt,omitempty"`
	IPSignup        string                 `json:"ip_signup,omitempty"`
	Tags            []MemberTag            `json:"tags,omitempty"`
	TimestampSignup string                 `json:"timestamp_signup,omitempty"`
	TimestampOpt    string                 `json:"timestamp_opt,omitempty"`
}

type MemberRequest struct {
	EmailAddress         string                 `json:"email_address"`
	EmailType            string                 `json:"email_type,omitempty"`
	Status               string                 `json:"status"`
	StatusIfNew          string                 `json:"status_if_new,omitempty"`
	MergeFields          map[string]interface{} `json:"merge_fields,omitempty"`
	Interests            map[string]bool        `json:"interests,omitempty"`
	Language             string                 `json:"language"`
	VIP                  bool                   `json:"vip"`
	Location             *MemberLocation        `json:"location,omitempty"`
	MarketingPermissions *MarketingPermissions  `json:"marketing_permissions,omitempty"`
	IPOpt                string                 `json:"ip_opt,omitempty"`
	IPSignup             string                 `json:"ip_signup,omitempty"`
	Tags                 []string               `json:"tags,omitempty"`
	TimestampSignup      string                 `json:"timestamp_signup,omitempty"`
	TimestampOpt         string                 `json:"timestamp_opt,omitempty"`
}

type Member struct {
	MemberResponse

	ID            string          `json:"id"`
	ListID        string          `json:"list_id"`
	UniqueEmailID string          `json:"unique_email_id"`
	EmailType     string          `json:"email_type"`
	Stats         MemberStats     `json:"stats"`
	MemberRating  int             `json:"member_rating"`
	LastChanged   string          `json:"last_changed"`
	EmailClient   string          `json:"email_client"`
	LastNote      MemberNoteShort `json:"last_note"`

	api *API
}

func (mem *Member) CanMakeRequest() error {
	if mem.ListID == "" {
		return errors.New("No ListID provided")
	}

	if mem.ID == "" {
		return errors.New("No ID provided")
	}

	return nil
}

type MemberStats struct {
	AvgOpenRate  float64 `json:"avg_open_rate"`
	AvgClickRate float64 `json:"avg_click_rate"`
}

type MemberLocation struct {
	Latitude    float64 `json:"latitude"`
	Longitude   float64 `json:"longitude"`
	GMTOffset   int     `json:"gmtoff"`
	DSTOffset   int     `json:"dstoff"`
	CountryCode string  `json:"country_code"`
	Timezone    string  `json:"timezone"`
}

type MarketingPermissions []MarketingPermission

type MarketingPermission struct {
	MarketingPermissionID string `json:"marketing_permission_id"`
	Text                  string `json:"text"`
	Enabled               bool   `json:"enabled"`
}

type MemberNoteShort struct {
	ID        int    `json:"note_id"`
	CreatedAt string `json:"created_at"`
	CreatedBy string `json:"created_by"`
	Note      string `json:"note"`
}

type MemberTag struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func (list *ListResponse) GetMembers(params *InterestCategoriesQueryParams) (*ListOfMembers, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(members_path, list.ID)
	response := new(ListOfMembers)

	err := list.api.Request("GET", endpoint, params, nil, response)
	if err != nil {
		return nil, err
	}

	for _, m := range response.Members {
		m.api = list.api
	}

	return response, nil
}

func (list *ListResponse) GetMember(id string, params *BasicQueryParams) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("GET", endpoint, params, nil, response)
}

func (list *ListResponse) CreateMember(body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(members_path, list.ID)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("POST", endpoint, nil, body, response)
}

func (list *ListResponse) UpdateMember(id string, body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("PATCH", endpoint, nil, body, response)
}

func (list *ListResponse) AddOrUpdateMember(id string, body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("PUT", endpoint, nil, body, response)
}

func (list *ListResponse) DeleteMember(id string) (bool, error) {
	if err := list.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	return list.api.RequestOk("DELETE", endpoint)
}

// ------------------------------------------------------------------------------------------------
// Activity
// ------------------------------------------------------------------------------------------------

type ListOfMemberActivity struct {
	baseList

	EmailID  string     `json:"email_id"`
	ListID   string     `json:"list_id"`
	Activity []Activity `json:"activity"`
}

type MemberActivity struct {
	Action         string `json:"action"`
	Timestamp      string `json:"timestamp"`
	URL            string `json:"url"`
	Type           string `json:"type"`
	CampaignID     string `json:"campaign_id"`
	Title          string `json:"title"`
	ParentCampaign string `json:"parent_campaign"`
}

func (mem *Member) GetActivity(params *BasicQueryParams) (*ListOfMemberActivity, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_activity_path, mem.ListID, mem.ID)
	response := new(ListOfMemberActivity)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

// ------------------------------------------------------------------------------------------------
// Goals
// ------------------------------------------------------------------------------------------------

type ListOfMemberGoals struct {
	baseList

	EmailID string       `json:"email_id"`
	ListID  string       `json:"list_id"`
	Goals   []MemberGoal `json:"goals"`
}

type MemberGoal struct {
	ID            int    `json:"goal_id"`
	Event         string `json:"event"`
	LastVisitedAt string `json:"last_visited_at"`
	Data          string `json:"data"`
}

func (mem *Member) GetGoals(params *BasicQueryParams) (*ListOfMemberGoals, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_goals_path, mem.ListID, mem.ID)
	response := new(ListOfMemberGoals)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

// ------------------------------------------------------------------------------------------------
// NOTES
// ------------------------------------------------------------------------------------------------

type ListOfMemberNotes struct {
	baseList

	EmailID string           `json:"email_id"`
	ListID  string           `json:"list_id"`
	Notes   []MemberNoteLong `json:"notes"`
}

type MemberNoteLong struct {
	ID        int    `json:"id"`
	CreatedAt string `json:"created_at"`
	CreatedBy string `json:"created_by"`
	UpdatedAt string `json:"updated_at"`
	Note      string `json:"note"`
	ListID    string `json:"list_id"`
	EmailID   string `json:"email_id"`

	withLinks
}

func (mem *Member) GetNotes(params *ExtendedQueryParams) (*ListOfMemberNotes, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_notes_path, mem.ListID, mem.ID)
	response := new(ListOfMemberNotes)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) CreateNote(msg string) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_notes_path, mem.ListID, mem.ID)
	response := new(MemberNoteLong)

	body := struct{ Note string }{
		Note: msg,
	}

	return response, mem.api.Request("POST", endpoint, nil, &body, response)
}

func (mem *Member) UpdateNote(id, msg string) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	response := new(MemberNoteLong)

	body := struct{ Note string }{
		Note: msg,
	}

	return response, mem.api.Request("PATCH", endpoint, nil, &body, response)
}

func (mem *Member) GetNote(id string, params *BasicQueryParams) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	response := new(MemberNoteLong)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) DeleteNote(id string) (bool, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	return mem.api.RequestOk("DELETE", endpoint)
}

// ------------------------------------------------------------------------------------------------
// TAGS
// ------------------------------------------------------------------------------------------------

type ListOfMemberTags struct {
	baseList

	Tags []MemberTagLong `json:"tags"`
}

type UpdateMemberTag struct {
	Name   string `json:"name"`
	Status string `json:"status"`
}

type MemberTagLong struct {
	ID        int     `json:"id"`
	Name      string  `json:"name"`
	DataAdded *string `json:"date_added,omitempty"`
	Status    string  `json:"status,omitempty"`

	withLinks
}

func (mem *Member) GetTags(params *ExtendedQueryParams) (*ListOfMemberTags, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_tags_path, mem.ListID, mem.ID)
	response := new(ListOfMemberTags)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) UpdateTags(tags []UpdateMemberTag) (*ListOfMemberTags, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_tags_path, mem.ListID, mem.ID)
	response := new(ListOfMemberTags)

	body := struct {
		Tags []UpdateMemberTag `json:"tags,omitempty"`
	}{
		Tags: tags,
	}

	return response, mem.api.Request("POST", endpoint, nil, &body, response)
}
<commit_msg>Add DeleteMemberPermanent<commit_after>package gochimp3

import (
	"errors"
	"fmt"
)

const (
	members_path       = "/lists/%s/members"
	single_member_path = members_path + "/%s"

	member_activity_path = single_member_path + "/activity"
	member_goals_path    = single_member_path + "/goals"

	member_notes_path       = single_member_path + "/notes"
	single_member_note_path = member_notes_path + "/%s"

	member_tags_path       = single_member_path + "/tags"
	single_member_tag_path = member_tags_path + "/%s"

	delete_permanent_path = single_member_path + "/acttions/delete-permanent"
)

type ListOfMembers struct {
	baseList

	ListID  string   `json:"list_id"`
	Members []Member `json:"members"`
}

type MemberResponse struct {
	EmailAddress    string                 `json:"email_address"`
	EmailType       string                 `json:"email_type,omitempty"`
	Status          string                 `json:"status"`
	StatusIfNew     string                 `json:"status_if_new,omitempty"`
	MergeFields     map[string]interface{} `json:"merge_fields,omitempty"`
	Interests       map[string]bool        `json:"interests,omitempty"`
	Language        string                 `json:"language"`
	VIP             bool                   `json:"vip"`
	Location        *MemberLocation        `json:"location,omitempty"`
	IPOpt           string                 `json:"ip_opt,omitempty"`
	IPSignup        string                 `json:"ip_signup,omitempty"`
	Tags            []MemberTag            `json:"tags,omitempty"`
	TimestampSignup string                 `json:"timestamp_signup,omitempty"`
	TimestampOpt    string                 `json:"timestamp_opt,omitempty"`
}

type MemberRequest struct {
	EmailAddress         string                 `json:"email_address"`
	EmailType            string                 `json:"email_type,omitempty"`
	Status               string                 `json:"status"`
	StatusIfNew          string                 `json:"status_if_new,omitempty"`
	MergeFields          map[string]interface{} `json:"merge_fields,omitempty"`
	Interests            map[string]bool        `json:"interests,omitempty"`
	Language             string                 `json:"language"`
	VIP                  bool                   `json:"vip"`
	Location             *MemberLocation        `json:"location,omitempty"`
	MarketingPermissions *MarketingPermissions  `json:"marketing_permissions,omitempty"`
	IPOpt                string                 `json:"ip_opt,omitempty"`
	IPSignup             string                 `json:"ip_signup,omitempty"`
	Tags                 []string               `json:"tags,omitempty"`
	TimestampSignup      string                 `json:"timestamp_signup,omitempty"`
	TimestampOpt         string                 `json:"timestamp_opt,omitempty"`
}

type Member struct {
	MemberResponse

	ID            string          `json:"id"`
	ListID        string          `json:"list_id"`
	UniqueEmailID string          `json:"unique_email_id"`
	EmailType     string          `json:"email_type"`
	Stats         MemberStats     `json:"stats"`
	MemberRating  int             `json:"member_rating"`
	LastChanged   string          `json:"last_changed"`
	EmailClient   string          `json:"email_client"`
	LastNote      MemberNoteShort `json:"last_note"`

	api *API
}

func (mem *Member) CanMakeRequest() error {
	if mem.ListID == "" {
		return errors.New("No ListID provided")
	}

	if mem.ID == "" {
		return errors.New("No ID provided")
	}

	return nil
}

type MemberStats struct {
	AvgOpenRate  float64 `json:"avg_open_rate"`
	AvgClickRate float64 `json:"avg_click_rate"`
}

type MemberLocation struct {
	Latitude    float64 `json:"latitude"`
	Longitude   float64 `json:"longitude"`
	GMTOffset   int     `json:"gmtoff"`
	DSTOffset   int     `json:"dstoff"`
	CountryCode string  `json:"country_code"`
	Timezone    string  `json:"timezone"`
}

type MarketingPermissions []MarketingPermission

type MarketingPermission struct {
	MarketingPermissionID string `json:"marketing_permission_id"`
	Text                  string `json:"text"`
	Enabled               bool   `json:"enabled"`
}

type MemberNoteShort struct {
	ID        int    `json:"note_id"`
	CreatedAt string `json:"created_at"`
	CreatedBy string `json:"created_by"`
	Note      string `json:"note"`
}

type MemberTag struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func (list *ListResponse) GetMembers(params *InterestCategoriesQueryParams) (*ListOfMembers, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(members_path, list.ID)
	response := new(ListOfMembers)

	err := list.api.Request("GET", endpoint, params, nil, response)
	if err != nil {
		return nil, err
	}

	for _, m := range response.Members {
		m.api = list.api
	}

	return response, nil
}

func (list *ListResponse) GetMember(id string, params *BasicQueryParams) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("GET", endpoint, params, nil, response)
}

func (list *ListResponse) CreateMember(body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(members_path, list.ID)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("POST", endpoint, nil, body, response)
}

func (list *ListResponse) UpdateMember(id string, body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("PATCH", endpoint, nil, body, response)
}

func (list *ListResponse) AddOrUpdateMember(id string, body *MemberRequest) (*Member, error) {
	if err := list.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	response := new(Member)
	response.api = list.api

	return response, list.api.Request("PUT", endpoint, nil, body, response)
}

func (list *ListResponse) DeleteMember(id string) (bool, error) {
	if err := list.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_member_path, list.ID, id)
	return list.api.RequestOk("DELETE", endpoint)
}

func (list *ListResponse) DeleteMemberPermanent(id string) (bool, error) {
	if err := list.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(delete_permanent_path, list.ID, id)
	return list.api.RequestOk("POST", endpoint)
}

// ------------------------------------------------------------------------------------------------
// Activity
// ------------------------------------------------------------------------------------------------

type ListOfMemberActivity struct {
	baseList

	EmailID  string     `json:"email_id"`
	ListID   string     `json:"list_id"`
	Activity []Activity `json:"activity"`
}

type MemberActivity struct {
	Action         string `json:"action"`
	Timestamp      string `json:"timestamp"`
	URL            string `json:"url"`
	Type           string `json:"type"`
	CampaignID     string `json:"campaign_id"`
	Title          string `json:"title"`
	ParentCampaign string `json:"parent_campaign"`
}

func (mem *Member) GetActivity(params *BasicQueryParams) (*ListOfMemberActivity, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_activity_path, mem.ListID, mem.ID)
	response := new(ListOfMemberActivity)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

// ------------------------------------------------------------------------------------------------
// Goals
// ------------------------------------------------------------------------------------------------

type ListOfMemberGoals struct {
	baseList

	EmailID string       `json:"email_id"`
	ListID  string       `json:"list_id"`
	Goals   []MemberGoal `json:"goals"`
}

type MemberGoal struct {
	ID            int    `json:"goal_id"`
	Event         string `json:"event"`
	LastVisitedAt string `json:"last_visited_at"`
	Data          string `json:"data"`
}

func (mem *Member) GetGoals(params *BasicQueryParams) (*ListOfMemberGoals, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_goals_path, mem.ListID, mem.ID)
	response := new(ListOfMemberGoals)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

// ------------------------------------------------------------------------------------------------
// NOTES
// ------------------------------------------------------------------------------------------------

type ListOfMemberNotes struct {
	baseList

	EmailID string           `json:"email_id"`
	ListID  string           `json:"list_id"`
	Notes   []MemberNoteLong `json:"notes"`
}

type MemberNoteLong struct {
	ID        int    `json:"id"`
	CreatedAt string `json:"created_at"`
	CreatedBy string `json:"created_by"`
	UpdatedAt string `json:"updated_at"`
	Note      string `json:"note"`
	ListID    string `json:"list_id"`
	EmailID   string `json:"email_id"`

	withLinks
}

func (mem *Member) GetNotes(params *ExtendedQueryParams) (*ListOfMemberNotes, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_notes_path, mem.ListID, mem.ID)
	response := new(ListOfMemberNotes)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) CreateNote(msg string) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_notes_path, mem.ListID, mem.ID)
	response := new(MemberNoteLong)

	body := struct{ Note string }{
		Note: msg,
	}

	return response, mem.api.Request("POST", endpoint, nil, &body, response)
}

func (mem *Member) UpdateNote(id, msg string) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	response := new(MemberNoteLong)

	body := struct{ Note string }{
		Note: msg,
	}

	return response, mem.api.Request("PATCH", endpoint, nil, &body, response)
}

func (mem *Member) GetNote(id string, params *BasicQueryParams) (*MemberNoteLong, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	response := new(MemberNoteLong)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) DeleteNote(id string) (bool, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return false, err
	}

	endpoint := fmt.Sprintf(single_member_note_path, mem.ListID, mem.ID, id)
	return mem.api.RequestOk("DELETE", endpoint)
}

// ------------------------------------------------------------------------------------------------
// TAGS
// ------------------------------------------------------------------------------------------------

type ListOfMemberTags struct {
	baseList

	Tags []MemberTagLong `json:"tags"`
}

type UpdateMemberTag struct {
	Name   string `json:"name"`
	Status string `json:"status"`
}

type MemberTagLong struct {
	ID        int     `json:"id"`
	Name      string  `json:"name"`
	DataAdded *string `json:"date_added,omitempty"`
	Status    string  `json:"status,omitempty"`

	withLinks
}

func (mem *Member) GetTags(params *ExtendedQueryParams) (*ListOfMemberTags, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_tags_path, mem.ListID, mem.ID)
	response := new(ListOfMemberTags)

	return response, mem.api.Request("GET", endpoint, params, nil, response)
}

func (mem *Member) UpdateTags(tags []UpdateMemberTag) (*ListOfMemberTags, error) {
	if err := mem.CanMakeRequest(); err != nil {
		return nil, err
	}

	endpoint := fmt.Sprintf(member_tags_path, mem.ListID, mem.ID)
	response := new(ListOfMemberTags)

	body := struct {
		Tags []UpdateMemberTag `json:"tags,omitempty"`
	}{
		Tags: tags,
	}

	return response, mem.api.Request("POST", endpoint, nil, &body, response)
}
<|endoftext|>


<commit_before>package db

import "time"

// Proposal represents the db schema of a proposal
type Proposal struct {
	ID          int64
	UserID      int64
	Title       string
	Description string
	Recipient   string
	Value       uint64
	Ends        time.Time
	Votes       uint64
	Moderated   bool
}

// LoadProposalByID loads a proposal by ID from the database
func (context *PollyContext) LoadProposalByID(id int64) (Proposal, error) {
	proposal := Proposal{}
	if id < 1 {
		return proposal, ErrInvalidID
	}

	err := context.QueryRow("SELECT id, userid, title, description, recipient, value, ends, votes, moderated FROM proposals WHERE id = $1", id).Scan(&proposal.ID, &proposal.UserID, &proposal.Title, &proposal.Description, &proposal.Recipient, &proposal.Value, &proposal.Ends, &proposal.Votes, &proposal.Moderated)
	return proposal, err
}

// GetProposalByID returns a proposal by ID from the cache
func (context *PollyContext) GetProposalByID(id int64) (Proposal, error) {
	proposal := Proposal{}
	proposalCache, err := proposalsCache.Value(id, context)
	if err != nil {
		return proposal, err
	}

	proposal = *proposalCache.Data().(*Proposal)
	return proposal, nil
}

// LoadAllProposals loads all proposals from the database
func (context *PollyContext) LoadAllProposals() ([]Proposal, error) {
	proposals := []Proposal{}

	rows, err := context.Query("SELECT id, userid, title, description, recipient, value, ends, votes, moderated FROM proposals ORDER BY ends ASC")
	if err != nil {
		return proposals, err
	}

	defer rows.Close()
	for rows.Next() {
		proposal := Proposal{}
		err = rows.Scan(&proposal.ID, &proposal.UserID, &proposal.Title, &proposal.Description, &proposal.Recipient, &proposal.Value, &proposal.Ends, &proposal.Votes, &proposal.Moderated)
		if err != nil {
			return proposals, err
		}

		proposals = append(proposals, proposal)
	}

	return proposals, err
}

// Update a proposal in the database
func (proposal *Proposal) Update(context *PollyContext) error {
	_, err := context.Exec("UPDATE proposals SET title = $1, description = $2, moderated = $3 WHERE id = $4", proposal.Title, proposal.Description, proposal.Moderated, proposal.ID)
	if err != nil {
		panic(err)
	}

	proposalsCache.Delete(proposal.ID)
	return err
}

// Save a proposal to the database
func (proposal *Proposal) Save(context *PollyContext) error {
	if proposal.Value < uint64(context.Config.App.Proposals.SmallGrantValueThreshold) {
		minEndDate := time.Now().AddDate(0, 0, int(context.Config.App.Proposals.SmallGrantVoteMinDays))
		if proposal.Ends.Before(minEndDate) {
			proposal.Ends = minEndDate
		}
	}

	err := context.QueryRow("INSERT INTO proposals (userid, title, description, recipient, value, ends) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", proposal.UserID, proposal.Title, proposal.Description, proposal.Recipient, proposal.Value, proposal.Ends).Scan(&proposal.ID)
	proposalsCache.Delete(proposal.ID)
	return err
}

// Ended returns true if a proposal either ended or got rejected by votes
func (proposal *Proposal) Ended(context *PollyContext) bool {
	return proposal.Ends.Before(time.Now()) ||
		(proposal.Value < uint64(context.Config.App.Proposals.SmallGrantValueThreshold) &&
			proposal.Votes >= uint64(context.Config.App.Proposals.SmallGrantVoteThreshold))
}

// Vote marks a vote for a proposal
func (proposal *Proposal) Vote(context *PollyContext, user User) (Vote, error) {
	vote := Vote{
		UserID:     user.ID,
		ProposalID: proposal.ID,
		Vote:       true,
	}
	err := vote.Save(context)
	if err != nil {
		return Vote{}, err
	}

	err = context.QueryRow("UPDATE proposals SET votes=votes+1 RETURNING votes").Scan(&proposal.Votes)
	proposalsCache.Delete(proposal.ID)
	return vote, err
}
<commit_msg>Fixed updating votes counter for proposals<commit_after>package db

import "time"

// Proposal represents the db schema of a proposal
type Proposal struct {
	ID          int64
	UserID      int64
	Title       string
	Description string
	Recipient   string
	Value       uint64
	Ends        time.Time
	Votes       uint64
	Moderated   bool
}

// LoadProposalByID loads a proposal by ID from the database
func (context *PollyContext) LoadProposalByID(id int64) (Proposal, error) {
	proposal := Proposal{}
	if id < 1 {
		return proposal, ErrInvalidID
	}

	err := context.QueryRow("SELECT id, userid, title, description, recipient, value, ends, votes, moderated FROM proposals WHERE id = $1", id).Scan(&proposal.ID, &proposal.UserID, &proposal.Title, &proposal.Description, &proposal.Recipient, &proposal.Value, &proposal.Ends, &proposal.Votes, &proposal.Moderated)
	return proposal, err
}

// GetProposalByID returns a proposal by ID from the cache
func (context *PollyContext) GetProposalByID(id int64) (Proposal, error) {
	proposal := Proposal{}
	proposalCache, err := proposalsCache.Value(id, context)
	if err != nil {
		return proposal, err
	}

	proposal = *proposalCache.Data().(*Proposal)
	return proposal, nil
}

// LoadAllProposals loads all proposals from the database
func (context *PollyContext) LoadAllProposals() ([]Proposal, error) {
	proposals := []Proposal{}

	rows, err := context.Query("SELECT id, userid, title, description, recipient, value, ends, votes, moderated FROM proposals ORDER BY ends ASC")
	if err != nil {
		return proposals, err
	}

	defer rows.Close()
	for rows.Next() {
		proposal := Proposal{}
		err = rows.Scan(&proposal.ID, &proposal.UserID, &proposal.Title, &proposal.Description, &proposal.Recipient, &proposal.Value, &proposal.Ends, &proposal.Votes, &proposal.Moderated)
		if err != nil {
			return proposals, err
		}

		proposals = append(proposals, proposal)
	}

	return proposals, err
}

// Update a proposal in the database
func (proposal *Proposal) Update(context *PollyContext) error {
	_, err := context.Exec("UPDATE proposals SET title = $1, description = $2, moderated = $3 WHERE id = $4", proposal.Title, proposal.Description, proposal.Moderated, proposal.ID)
	if err != nil {
		panic(err)
	}

	proposalsCache.Delete(proposal.ID)
	return err
}

// Save a proposal to the database
func (proposal *Proposal) Save(context *PollyContext) error {
	if proposal.Value < uint64(context.Config.App.Proposals.SmallGrantValueThreshold) {
		minEndDate := time.Now().AddDate(0, 0, int(context.Config.App.Proposals.SmallGrantVoteMinDays))
		if proposal.Ends.Before(minEndDate) {
			proposal.Ends = minEndDate
		}
	}

	err := context.QueryRow("INSERT INTO proposals (userid, title, description, recipient, value, ends) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", proposal.UserID, proposal.Title, proposal.Description, proposal.Recipient, proposal.Value, proposal.Ends).Scan(&proposal.ID)
	proposalsCache.Delete(proposal.ID)
	return err
}

// Ended returns true if a proposal either ended or got rejected by votes
func (proposal *Proposal) Ended(context *PollyContext) bool {
	return proposal.Ends.Before(time.Now()) ||
		(proposal.Value < uint64(context.Config.App.Proposals.SmallGrantValueThreshold) &&
			proposal.Votes >= uint64(context.Config.App.Proposals.SmallGrantVoteThreshold))
}

// Vote marks a vote for a proposal
func (proposal *Proposal) Vote(context *PollyContext, user User) (Vote, error) {
	vote := Vote{
		UserID:     user.ID,
		ProposalID: proposal.ID,
		Vote:       true,
	}
	err := vote.Save(context)
	if err != nil {
		return Vote{}, err
	}

	err = context.QueryRow("UPDATE proposals SET votes=votes+1 WHERE id = $1 RETURNING votes", proposal.ID).Scan(&proposal.Votes)
	proposalsCache.Delete(proposal.ID)
	return vote, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/rmera/gochem"
	"github.com/rmera/scu"
	"os"
)

//improper.go Takes an XYZ file and the zero-based indexes of 4 atoms (a,b,c and d) It calculates the improper dihedral between
//a plane defined by the vectors ab and ac and the vector ad. The atoms b and c must be given in an order such that the
//cross produc ab x ac points in the same general direction as the vector ad.

func main() {
	mol, err := chem.XYZRead(os.Args[1])
	if err != nil {
		panic(err.Error())
	}
	ndx,_:=scu.IndexStringParse(os.Args[2])
	fmt.Println(ndx)
	coord := mol.Coords[0]
	piv:=coord.VecView(ndx[0])
	v1:=coord.VecView(ndx[1])
	v2:=coord.VecView(ndx[2])
	v1.Sub(v1,piv)
	v2.Sub(v2,piv)
	plane:=chem.ZeroVecs(1)
	plane.Cross(v1,v2)
	v3:=coord.VecView(ndx[3])
	v3.Sub(v3,piv)
	angle:=chem.Angle(v3,plane)
	fmt.Println(90-angle*chem.Rad2Deg, 90+angle*chem.Rad2Deg)
}
<commit_msg>improved the documentation of improper<commit_after>package main

import (
	"fmt"
	"github.com/rmera/gochem"
	"github.com/rmera/scu"
	"os"
)

//improper.go Takes an XYZ file and the zero-based indexes of 4 atoms (a,b,c and d) It calculates the improper
//dihedral between a plane defined by the vectors ab and ac and the vector ad. The atoms b and c must be given
//in an order such that the cross produc ab x ac points in the same general direction as the vector ad. The
//indexes a,b,c and d have to be given after the XYZ file name as a quoted string separated by spaces.

//example:

// ./improper file.xyz "0 1 2 3"

func main() {
	mol, err := chem.XYZRead(os.Args[1])
	if err != nil {
		panic(err.Error())
	}
	ndx,_:=scu.IndexStringParse(os.Args[2])
	fmt.Println(ndx)
	coord := mol.Coords[0]
	piv:=coord.VecView(ndx[0])
	v1:=coord.VecView(ndx[1])
	v2:=coord.VecView(ndx[2])
	v1.Sub(v1,piv)
	v2.Sub(v2,piv)
	plane:=chem.ZeroVecs(1)
	plane.Cross(v1,v2)
	v3:=coord.VecView(ndx[3])
	v3.Sub(v3,piv)
	angle:=chem.Angle(v3,plane)
	fmt.Println(90-angle*chem.Rad2Deg, 90+angle*chem.Rad2Deg)
}
<|endoftext|>