<commit_before>// The nodefs package offers a high level API that resembles the
// kernel's idea of what an FS looks like.  File systems can have
// multiple hard-links to one file, for example. It is also suited if
// the data to represent fits in memory: you can construct the
// complete file system tree at mount time
package nodefs

import (
	"time"

	"github.com/hanwen/go-fuse/fuse"
)

// The Node interface implements the user-defined file system
// functionality
type Node interface {
	// Inode and SetInode are basic getter/setters.  They are
	// called by the FileSystemConnector. You get them for free by
	// embedding the result of NewDefaultNode() in your node
	// struct.
	Inode() *Inode
	SetInode(node *Inode)

	// OnMount is called on the root node just after a mount is
	// executed, either when the actual root is mounted, or when a
	// filesystem is mounted in-process. The passed-in
	// FileSystemConnector gives access to Notify methods and
	// Debug settings.
	OnMount(conn *FileSystemConnector)

	// OnUnmount is executed just before a submount is removed,
	// and when the process receives a forget for the FUSE root
	// node.
	OnUnmount()

	// Lookup finds a child node to this node; it is only called
	// for directory Nodes.
	Lookup(out *fuse.Attr, name string, context *fuse.Context) (*Inode, fuse.Status)

	// Deletable() should return true if this inode may be
	// discarded from the children list. This will be called from
	// within the treeLock critical section, so you cannot look at
	// other inodes.
	Deletable() bool

	// OnForget is called when the reference to this inode is
	// dropped from the tree.
	OnForget()

	// Misc.
	Access(mode uint32, context *fuse.Context) (code fuse.Status)
	Readlink(c *fuse.Context) ([]byte, fuse.Status)

	// Namespace operations; these are only called on directory Nodes.

	// Mknod should create the node, add it to the receiver's
	// inode, and return it
	Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (newNode *Inode, code fuse.Status)

	// Mkdir should create the directory Inode, add it to the
	// receiver's Inode, and return it
	Mkdir(name string, mode uint32, context *fuse.Context) (newNode *Inode, code fuse.Status)
	Unlink(name string, context *fuse.Context) (code fuse.Status)
	Rmdir(name string, context *fuse.Context) (code fuse.Status)

	// Symlink should create a child inode to the receiver, and
	// return it.
	Symlink(name string, content string, context *fuse.Context) (*Inode, fuse.Status)
	Rename(oldName string, newParent Node, newName string, context *fuse.Context) (code fuse.Status)

	// Link should return the Inode of the resulting link. In
	// a POSIX conformant file system, this should add 'existing'
	// to the receiver, and return the Inode corresponding to
	// 'existing'.
	Link(name string, existing Node, context *fuse.Context) (newNode *Inode, code fuse.Status)

	// Create should return an open file, and the Inode for that file.
	Create(name string, flags uint32, mode uint32, context *fuse.Context) (file File, child *Inode, code fuse.Status)

	// Open opens a file, and returns a File which is associated
	// with a file handle. It is OK to return (nil, OK) here. In
	// that case, the Node should implement Read or Write
	// directly.
	Open(flags uint32, context *fuse.Context) (file File, code fuse.Status)
	OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Status)

	// XAttrs
	GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status)
	RemoveXAttr(attr string, context *fuse.Context) fuse.Status
	SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status
	ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status)

	// Attributes
	Read(file File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status)
	Write(file File, data []byte, off int64, context *fuse.Context) (written uint32, code fuse.Status)
	GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status)
	Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status)
	Chown(file File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status)
	Truncate(file File, size uint64, context *fuse.Context) (code fuse.Status)
	Utimens(file File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status)
	Fallocate(file File, off uint64, size uint64, mode uint32, context *fuse.Context) (code fuse.Status)

	StatFs() *fuse.StatfsOut
}

// A File object is returned from FileSystem.Open and
// FileSystem.Create.  Include the NewDefaultFile return value into
// the struct to inherit a null implementation.
type File interface {
	// Called upon registering the filehandle in the inode.
	SetInode(*Inode)

	// The String method is for debug printing.
	String() string

	// Wrappers around other File implementations, should return
	// the inner file here.
	InnerFile() File

	Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status)
	Write(data []byte, off int64) (written uint32, code fuse.Status)

	// Flush is called for close() call on a file descriptor. In
	// case of duplicated descriptor, it may be called more than
	// once for a file.
	Flush() fuse.Status

	// This is called to before the file handle is forgotten. This
	// method has no return value, so nothing can synchronizes on
	// the call. Any cleanup that requires specific synchronization or
	// could fail with I/O errors should happen in Flush instead.
	Release()
	Fsync(flags int) (code fuse.Status)

	// The methods below may be called on closed files, due to
	// concurrency.  In that case, you should return EBADF.
	Truncate(size uint64) fuse.Status
	GetAttr(out *fuse.Attr) fuse.Status
	Chown(uid uint32, gid uint32) fuse.Status
	Chmod(perms uint32) fuse.Status
	Utimens(atime *time.Time, mtime *time.Time) fuse.Status
	Allocate(off uint64, size uint64, mode uint32) (code fuse.Status)
}

// Wrap a File return in this to set FUSE flags.  Also used internally
// to store open file data.
type WithFlags struct {
	File

	// For debugging.
	Description string

	// Put FOPEN_* flags here.
	FuseFlags uint32

	// O_RDWR, O_TRUNCATE, etc.
	OpenFlags uint32
}

// Options contains time out options for a node FileSystem.  The
// default copied from libfuse and set in NewMountOptions() is
// (1s,1s,0s).
type Options struct {
	EntryTimeout    time.Duration
	AttrTimeout     time.Duration
	NegativeTimeout time.Duration

	// If set, replace all uids with given UID.
	// NewFileSystemOptions() will set this to the daemon's
	// uid/gid.
	*fuse.Owner

	// If set, use a more portable, but slower inode number
	// generation scheme.  This will make inode numbers (exported
	// back to callers) stay within int32, which is necessary for
	// making stat() succeed in 32-bit programs.
	PortableInodes bool
}
<commit_msg>fuse/nodefs: reorganize read/write in interface ordering.<commit_after>// The nodefs package offers a high level API that resembles the
// kernel's idea of what an FS looks like.  File systems can have
// multiple hard-links to one file, for example. It is also suited if
// the data to represent fits in memory: you can construct the
// complete file system tree at mount time
package nodefs

import (
	"time"

	"github.com/hanwen/go-fuse/fuse"
)

// The Node interface implements the user-defined file system
// functionality
type Node interface {
	// Inode and SetInode are basic getter/setters.  They are
	// called by the FileSystemConnector. You get them for free by
	// embedding the result of NewDefaultNode() in your node
	// struct.
	Inode() *Inode
	SetInode(node *Inode)

	// OnMount is called on the root node just after a mount is
	// executed, either when the actual root is mounted, or when a
	// filesystem is mounted in-process. The passed-in
	// FileSystemConnector gives access to Notify methods and
	// Debug settings.
	OnMount(conn *FileSystemConnector)

	// OnUnmount is executed just before a submount is removed,
	// and when the process receives a forget for the FUSE root
	// node.
	OnUnmount()

	// Lookup finds a child node to this node; it is only called
	// for directory Nodes.
	Lookup(out *fuse.Attr, name string, context *fuse.Context) (*Inode, fuse.Status)

	// Deletable() should return true if this inode may be
	// discarded from the children list. This will be called from
	// within the treeLock critical section, so you cannot look at
	// other inodes.
	Deletable() bool

	// OnForget is called when the reference to this inode is
	// dropped from the tree.
	OnForget()

	// Misc.
	Access(mode uint32, context *fuse.Context) (code fuse.Status)
	Readlink(c *fuse.Context) ([]byte, fuse.Status)

	// Namespace operations; these are only called on directory Nodes.

	// Mknod should create the node, add it to the receiver's
	// inode, and return it
	Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (newNode *Inode, code fuse.Status)

	// Mkdir should create the directory Inode, add it to the
	// receiver's Inode, and return it
	Mkdir(name string, mode uint32, context *fuse.Context) (newNode *Inode, code fuse.Status)
	Unlink(name string, context *fuse.Context) (code fuse.Status)
	Rmdir(name string, context *fuse.Context) (code fuse.Status)

	// Symlink should create a child inode to the receiver, and
	// return it.
	Symlink(name string, content string, context *fuse.Context) (*Inode, fuse.Status)
	Rename(oldName string, newParent Node, newName string, context *fuse.Context) (code fuse.Status)

	// Link should return the Inode of the resulting link. In
	// a POSIX conformant file system, this should add 'existing'
	// to the receiver, and return the Inode corresponding to
	// 'existing'.
	Link(name string, existing Node, context *fuse.Context) (newNode *Inode, code fuse.Status)

	// Create should return an open file, and the Inode for that file.
	Create(name string, flags uint32, mode uint32, context *fuse.Context) (file File, child *Inode, code fuse.Status)

	// Open opens a file, and returns a File which is associated
	// with a file handle. It is OK to return (nil, OK) here. In
	// that case, the Node should implement Read or Write
	// directly.
	Open(flags uint32, context *fuse.Context) (file File, code fuse.Status)
	OpenDir(context *fuse.Context) ([]fuse.DirEntry, fuse.Status)
	Read(file File, dest []byte, off int64, context *fuse.Context) (fuse.ReadResult, fuse.Status)
	Write(file File, data []byte, off int64, context *fuse.Context) (written uint32, code fuse.Status)

	// XAttrs
	GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status)
	RemoveXAttr(attr string, context *fuse.Context) fuse.Status
	SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status
	ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status)

	// Attributes
	GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status)
	Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status)
	Chown(file File, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status)
	Truncate(file File, size uint64, context *fuse.Context) (code fuse.Status)
	Utimens(file File, atime *time.Time, mtime *time.Time, context *fuse.Context) (code fuse.Status)
	Fallocate(file File, off uint64, size uint64, mode uint32, context *fuse.Context) (code fuse.Status)

	StatFs() *fuse.StatfsOut
}

// A File object is returned from FileSystem.Open and
// FileSystem.Create.  Include the NewDefaultFile return value into
// the struct to inherit a null implementation.
type File interface {
	// Called upon registering the filehandle in the inode.
	SetInode(*Inode)

	// The String method is for debug printing.
	String() string

	// Wrappers around other File implementations, should return
	// the inner file here.
	InnerFile() File

	Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status)
	Write(data []byte, off int64) (written uint32, code fuse.Status)

	// Flush is called for close() call on a file descriptor. In
	// case of duplicated descriptor, it may be called more than
	// once for a file.
	Flush() fuse.Status

	// This is called to before the file handle is forgotten. This
	// method has no return value, so nothing can synchronizes on
	// the call. Any cleanup that requires specific synchronization or
	// could fail with I/O errors should happen in Flush instead.
	Release()
	Fsync(flags int) (code fuse.Status)

	// The methods below may be called on closed files, due to
	// concurrency.  In that case, you should return EBADF.
	Truncate(size uint64) fuse.Status
	GetAttr(out *fuse.Attr) fuse.Status
	Chown(uid uint32, gid uint32) fuse.Status
	Chmod(perms uint32) fuse.Status
	Utimens(atime *time.Time, mtime *time.Time) fuse.Status
	Allocate(off uint64, size uint64, mode uint32) (code fuse.Status)
}

// Wrap a File return in this to set FUSE flags.  Also used internally
// to store open file data.
type WithFlags struct {
	File

	// For debugging.
	Description string

	// Put FOPEN_* flags here.
	FuseFlags uint32

	// O_RDWR, O_TRUNCATE, etc.
	OpenFlags uint32
}

// Options contains time out options for a node FileSystem.  The
// default copied from libfuse and set in NewMountOptions() is
// (1s,1s,0s).
type Options struct {
	EntryTimeout    time.Duration
	AttrTimeout     time.Duration
	NegativeTimeout time.Duration

	// If set, replace all uids with given UID.
	// NewFileSystemOptions() will set this to the daemon's
	// uid/gid.
	*fuse.Owner

	// If set, use a more portable, but slower inode number
	// generation scheme.  This will make inode numbers (exported
	// back to callers) stay within int32, which is necessary for
	// making stat() succeed in 32-bit programs.
	PortableInodes bool
}
<|endoftext|>


<commit_before>package resource

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

	"github.com/pkg/errors"
	"github.com/satori/go.uuid"

	"github.com/diegobernardes/flare"
)

type pagination struct {
	base *flare.Pagination
}

func (p *pagination) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Limit  int `json:"limit"`
		Offset int `json:"offset"`
		Total  int `json:"total"`
	}{
		Limit:  p.base.Limit,
		Total:  p.base.Total,
		Offset: p.base.Offset,
	})
}

type resource struct {
	base *flare.Resource
}

func (r *resource) MarshalJSON() ([]byte, error) {
	change := map[string]string{
		"kind":  r.base.Change.Kind,
		"field": r.base.Change.Field,
	}

	if r.base.Change.DateFormat != "" {
		change["dateFormat"] = r.base.Change.DateFormat
	}

	return json.Marshal(&struct {
		Id        string            `json:"id"`
		Domains   []string          `json:"domains"`
		Path      string            `json:"path"`
		Change    map[string]string `json:"change"`
		CreatedAt string            `json:"createdAt"`
		UpdatedAt string            `json:"updatedAt"`
	}{
		Id:        r.base.Id,
		Domains:   r.base.Domains,
		Path:      r.base.Path,
		Change:    change,
		CreatedAt: r.base.CreatedAt.Format(time.RFC3339),
		UpdatedAt: r.base.UpdatedAt.Format(time.RFC3339),
	})
}

type resourceCreate struct {
	Path    string   `json:"path"`
	Domains []string `json:"domains"`
	Change  struct {
		Kind       string `json:"kind"`
		Field      string `json:"field"`
		DateFormat string `json:"dateFormat"`
	} `json:"change"`
}

func (r *resourceCreate) cleanup() {
	trim := func(value string) string { return strings.TrimSpace(value) }
	r.Path = trim(r.Path)
	r.Change.Kind = trim(r.Change.Kind)
	r.Change.Field = trim(r.Change.Field)
	r.Change.DateFormat = trim(r.Change.DateFormat)

	for i, value := range r.Domains {
		r.Domains[i] = trim(value)
	}
}

func (r *resourceCreate) valid() error {
	if len(r.Domains) == 0 {
		return errors.New("missing domains")
	}

	if r.Path == "" {
		return errors.New("missing path")
	}

	if r.Change.Field == "" {
		return errors.New("missing change")
	}

	switch r.Change.Kind {
	case flare.ResourceChangeInteger, flare.ResourceChangeString:
	case flare.ResourceChangeDate:
		if r.Change.DateFormat == "" {
			return errors.New("missing change.dateFormat")
		}
	default:
		return errors.New("invalid change.kind")
	}

	return nil
}

func (r *resourceCreate) toFlareResource() *flare.Resource {
	return &flare.Resource{
		Id:      uuid.NewV4().String(),
		Domains: r.Domains,
		Path:    r.Path,
		Change: flare.ResourceChange{
			Kind:       r.Change.Kind,
			Field:      r.Change.Field,
			DateFormat: r.Change.DateFormat,
		},
	}
}

func transformResource(r *flare.Resource) *resource { return &resource{r} }

func transformPagination(p *flare.Pagination) *pagination { return &pagination{base: p} }

func transformResources(r []flare.Resource) []resource {
	result := make([]resource, len(r))
	for i := 0; i < len(r); i++ {
		result[i] = resource{&r[i]}
	}
	return result
}

type response struct {
	Pagination *pagination
	Error      *responseError
	Resources  []resource
	Resource   *resource
}

func (r *response) MarshalJSON() ([]byte, error) {
	var result interface{}

	if r.Error != nil {
		result = map[string]*responseError{"error": r.Error}
	} else if r.Resource != nil {
		result = r.Resource
	} else {
		result = map[string]interface{}{
			"pagination": r.Pagination,
			"resources":  r.Resources,
		}
	}

	return json.Marshal(result)
}

type responseError struct {
	Status int    `json:"status"`
	Title  string `json:"title"`
	Detail string `json:"detail,omitempty"`
}
<commit_msg>resource: added track mark<commit_after>package resource

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

	"github.com/pkg/errors"
	"github.com/satori/go.uuid"

	"github.com/diegobernardes/flare"
)

type pagination struct {
	base *flare.Pagination
}

func (p *pagination) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Limit  int `json:"limit"`
		Offset int `json:"offset"`
		Total  int `json:"total"`
	}{
		Limit:  p.base.Limit,
		Total:  p.base.Total,
		Offset: p.base.Offset,
	})
}

type resource struct {
	base *flare.Resource
}

func (r *resource) MarshalJSON() ([]byte, error) {
	change := map[string]string{
		"kind":  r.base.Change.Kind,
		"field": r.base.Change.Field,
	}

	if r.base.Change.DateFormat != "" {
		change["dateFormat"] = r.base.Change.DateFormat
	}

	return json.Marshal(&struct {
		Id        string            `json:"id"`
		Domains   []string          `json:"domains"`
		Path      string            `json:"path"`
		Change    map[string]string `json:"change"`
		CreatedAt string            `json:"createdAt"`
		UpdatedAt string            `json:"updatedAt"`
	}{
		Id:        r.base.Id,
		Domains:   r.base.Domains,
		Path:      r.base.Path,
		Change:    change,
		CreatedAt: r.base.CreatedAt.Format(time.RFC3339),
		UpdatedAt: r.base.UpdatedAt.Format(time.RFC3339),
	})
}

type resourceCreate struct {
	Path    string   `json:"path"`
	Domains []string `json:"domains"`
	Change  struct {
		Kind       string `json:"kind"`
		Field      string `json:"field"`
		DateFormat string `json:"dateFormat"`
	} `json:"change"`
}

func (r *resourceCreate) cleanup() {
	trim := func(value string) string { return strings.TrimSpace(value) }
	r.Path = trim(r.Path)
	r.Change.Kind = trim(r.Change.Kind)
	r.Change.Field = trim(r.Change.Field)
	r.Change.DateFormat = trim(r.Change.DateFormat)

	for i, value := range r.Domains {
		r.Domains[i] = trim(value)
	}
}

func (r *resourceCreate) valid() error {
	if len(r.Domains) == 0 {
		return errors.New("missing domains")
	}

	if r.Path == "" {
		return errors.New("missing path")
	}

	trackCount := strings.Count(r.Path, "{track}")
	if trackCount == 0 {
		return errors.New("missing track mark on path")
	} else if trackCount > 1 {
		return errors.Errorf("path can have only one track, current has %d", trackCount)
	}

	if r.Change.Field == "" {
		return errors.New("missing change")
	}

	switch r.Change.Kind {
	case flare.ResourceChangeInteger, flare.ResourceChangeString:
	case flare.ResourceChangeDate:
		if r.Change.DateFormat == "" {
			return errors.New("missing change.dateFormat")
		}
	default:
		return errors.New("invalid change.kind")
	}

	return nil
}

func (r *resourceCreate) toFlareResource() *flare.Resource {
	return &flare.Resource{
		Id:      uuid.NewV4().String(),
		Domains: r.Domains,
		Path:    r.Path,
		Change: flare.ResourceChange{
			Kind:       r.Change.Kind,
			Field:      r.Change.Field,
			DateFormat: r.Change.DateFormat,
		},
	}
}

func transformResource(r *flare.Resource) *resource { return &resource{r} }

func transformPagination(p *flare.Pagination) *pagination { return &pagination{base: p} }

func transformResources(r []flare.Resource) []resource {
	result := make([]resource, len(r))
	for i := 0; i < len(r); i++ {
		result[i] = resource{&r[i]}
	}
	return result
}

type response struct {
	Pagination *pagination
	Error      *responseError
	Resources  []resource
	Resource   *resource
}

func (r *response) MarshalJSON() ([]byte, error) {
	var result interface{}

	if r.Error != nil {
		result = map[string]*responseError{"error": r.Error}
	} else if r.Resource != nil {
		result = r.Resource
	} else {
		result = map[string]interface{}{
			"pagination": r.Pagination,
			"resources":  r.Resources,
		}
	}

	return json.Marshal(result)
}

type responseError struct {
	Status int    `json:"status"`
	Title  string `json:"title"`
	Detail string `json:"detail,omitempty"`
}
<|endoftext|>


<commit_before>package response

import (
	"encoding/xml"
	"errors"
	"net/http"
	"reflect"

	"github.com/codegangsta/inject"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/encoder"
)

type wrappedResponseWriter struct {
	http.ResponseWriter

	statusCode int
}

func newWrappedResponseWriter(w http.ResponseWriter) *wrappedResponseWriter {
	wr := &wrappedResponseWriter{ResponseWriter: w}
	return wr
}

func (wr *wrappedResponseWriter) WriteHeader(code int) {
	wr.ResponseWriter.WriteHeader(code)
	wr.statusCode = code
}

type errorResponse struct {
	XMLName xml.Name `json:"-" xml:"error"`
	Error   int      `json:"error" xml:"code"`
	Message string   `json:"message" xml:"message"`
}

func NewEncoder() martini.Handler {
	return func(c martini.Context, w http.ResponseWriter) {
		wrappedWriter := newWrappedResponseWriter(w)
		c.MapTo(wrappedWriter, (*http.ResponseWriter)(nil))
		c.MapTo(encoder.JsonEncoder{}, (*encoder.Encoder)(nil))

		var rtnHandler martini.ReturnHandler
		rtnHandler = func(ctx martini.Context, vals []reflect.Value) {
			rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil)))
			res := rv.Interface().(http.ResponseWriter)
			var responseVal reflect.Value
			if len(vals) > 1 && vals[0].Kind() == reflect.Int {
				res.WriteHeader(int(vals[0].Int()))
				responseVal = vals[1]
			} else if len(vals) > 0 {
				responseVal = vals[0]
			}
			if isNil(responseVal) {
				wrappedRes := res.(*wrappedResponseWriter)
				code := wrappedRes.statusCode
				if code == 0 {
					panic(errors.New("No return code set for error"))
				}
				responseVal = reflect.ValueOf(errorResponse{Error: code, Message: http.StatusText(code)})
			}
			if canDeref(responseVal) {
				responseVal = responseVal.Elem()
			}
			if isByteSlice(responseVal) {
				res.Write(responseVal.Bytes())
			} else if isStruct(responseVal) || isStructSlice(responseVal) {
				encv := ctx.Get(inject.InterfaceOf((*encoder.Encoder)(nil)))
				enc := encv.Interface().(encoder.Encoder)
				res.Header().Set("Content-Type", "application/json; charset=utf-8")
				res.Write(encoder.Must(enc.Encode(responseVal.Interface())))
			} else {
				res.Write([]byte(responseVal.String()))
			}
		}
		c.Map(rtnHandler)
	}
}

func isByteSlice(val reflect.Value) bool {
	return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8
}

func isStruct(val reflect.Value) bool {
	return val.Kind() == reflect.Struct
}

func isStructSlice(val reflect.Value) bool {
	return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Struct
}

func isNil(val reflect.Value) bool {
	return val.Kind() == reflect.Invalid
}

func canDeref(val reflect.Value) bool {
	return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr
}
<commit_msg>Added options for HTML Json escape, and Json indenting<commit_after>package response

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"net/http"
	"reflect"

	"github.com/codegangsta/inject"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/encoder"
)

type wrappedResponseWriter struct {
	http.ResponseWriter

	statusCode int
}

func newWrappedResponseWriter(w http.ResponseWriter) *wrappedResponseWriter {
	wr := &wrappedResponseWriter{ResponseWriter: w}
	return wr
}

func (wr *wrappedResponseWriter) WriteHeader(code int) {
	wr.ResponseWriter.WriteHeader(code)
	wr.statusCode = code
}

type errorResponse struct {
	XMLName xml.Name `json:"-" xml:"error"`
	Error   int      `json:"error" xml:"code"`
	Message string   `json:"message" xml:"message"`
}

type Options struct {
	Html   bool
	Indent bool
}

func NewEncoder(opts ...Options) martini.Handler {
	return func(c martini.Context, w http.ResponseWriter) {
		wrappedWriter := newWrappedResponseWriter(w)
		c.MapTo(wrappedWriter, (*http.ResponseWriter)(nil))
		c.MapTo(encoder.JsonEncoder{}, (*encoder.Encoder)(nil))

		var rtnHandler martini.ReturnHandler
		rtnHandler = func(ctx martini.Context, vals []reflect.Value) {
			rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil)))
			res := rv.Interface().(http.ResponseWriter)
			var responseVal reflect.Value
			if len(vals) > 1 && vals[0].Kind() == reflect.Int {
				res.WriteHeader(int(vals[0].Int()))
				responseVal = vals[1]
			} else if len(vals) > 0 {
				responseVal = vals[0]
			}
			if isNil(responseVal) {
				wrappedRes := res.(*wrappedResponseWriter)
				code := wrappedRes.statusCode
				if code == 0 {
					panic(errors.New("No return code set for error"))
				}
				responseVal = reflect.ValueOf(errorResponse{Error: code, Message: http.StatusText(code)})
			}
			if canDeref(responseVal) {
				responseVal = responseVal.Elem()
			}
			if isByteSlice(responseVal) {
				res.Write(responseVal.Bytes())
			} else if isStruct(responseVal) || isStructSlice(responseVal) {
				encv := ctx.Get(inject.InterfaceOf((*encoder.Encoder)(nil)))
				enc := encv.Interface().(encoder.Encoder)
				res.Header().Set("Content-Type", "application/json; charset=utf-8")
				buf := bytes.NewBuffer(encoder.Must(enc.Encode(responseVal.Interface())))
				if len(opts) > 0 {
					if opts[0].Html {
						val := buf.Bytes()
						buf.Reset()
						json.HTMLEscape(buf, val)
					}
					if opts[0].Indent {
						val := buf.Bytes()
						buf.Reset()
						json.Indent(buf, val, "", "\t")
					}
				}
				res.Write(buf.Bytes())
			} else {
				res.Write([]byte(responseVal.String()))
			}
		}
		c.Map(rtnHandler)
	}
}

func isByteSlice(val reflect.Value) bool {
	return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8
}

func isStruct(val reflect.Value) bool {
	return val.Kind() == reflect.Struct
}

func isStructSlice(val reflect.Value) bool {
	return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Struct
}

func isNil(val reflect.Value) bool {
	return val.Kind() == reflect.Invalid
}

func canDeref(val reflect.Value) bool {
	return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr
}
<|endoftext|>


<commit_before>package ast

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io"
	"regexp"
	"strconv"
	"strings"
)

// AnnotationDeclaration defines a annotation type which holds detail about a giving annotation.
type AnnotationDeclaration struct {
	Name      string                 `json:"name"`
	Template  string                 `json:"template"`
	Arguments []string               `json:"arguments"`
	Params    map[string]string      `json:"params"`
	Attrs     map[string]interface{} `json:"attrs"`
	Defer     bool                   `json:"defer"`
}

// HasArg returns true/false if the giving AnnotationDeclaration has a giving key in its Arguments.
func (ad AnnotationDeclaration) HasArg(name string) bool {
	for _, item := range ad.Arguments {
		if item == name {
			return true
		}
	}

	return false
}

// Param returns the associated  param value with giving key ("name").
func (ad AnnotationDeclaration) Param(name string) string {
	return ad.Params[name]
}

// Attrs returns the associated  param value with giving key ("name").
func (ad AnnotationDeclaration) Attr(name string) interface{} {
	return ad.Attrs[name]
}

// ReadAnnotationsFromCommentry returns a slice of all annotation passed from the provided list.
func ReadAnnotationsFromCommentry(r io.Reader) []AnnotationDeclaration {
	var annotations []AnnotationDeclaration

	reader := bufio.NewReader(r)

	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			break
		}

		trimmedline := string(cleanWord([]byte(line)))
		if trimmedline == "" {
			continue
		}

		// Do we have a annotation here?
		if !strings.HasPrefix(trimmedline, "@") {
			continue
		}

		params := make(map[string]string, 0)

		if !strings.Contains(trimmedline, "(") {
			annotations = append(annotations, AnnotationDeclaration{Name: trimmedline, Params: params, Attrs: make(map[string]interface{})})
			continue
		}

		argIndex := strings.IndexRune(trimmedline, '(')
		argName := trimmedline[:argIndex]
		argContents := trimmedline[argIndex:]

		// Do we have a template associated with this annotation, if not, split the
		// commas and let those be our arguments.
		if !strings.HasSuffix(argContents, "{") {

			argContents = strings.TrimPrefix(strings.TrimSuffix(argContents, ")"), "(")

			var parts []string

			for _, part := range strings.Split(argContents, ",") {
				trimmed := strings.TrimSpace(part)
				if trimmed == "" {
					continue
				}

				parts = append(parts, trimmed)

				// If we are dealing with key value pairs then split, trimspace and set
				// in params. We only expect 2 values, any more and we wont consider the rest.
				if kvPieces := strings.Split(trimmed, "=>"); len(kvPieces) > 1 {
					val := strings.TrimSpace(kvPieces[1])
					params[strings.TrimSpace(kvPieces[0])] = val
				}
			}

			var deferred bool

			// Find out if we are to be deferred
			defered, ok := params["defer"]
			if !ok {
				defered, ok = params["Defer"]
			}

			deferred, _ = strconv.ParseBool(defered)

			annotations = append(annotations, AnnotationDeclaration{
				Arguments: parts,
				Name:      argName,
				Params:    params,
				Defer:     deferred,
				Attrs:     make(map[string]interface{}),
			})

			continue
		}

		templateIndex := strings.IndexRune(argContents, '{')
		templateArgs := argContents[:templateIndex]
		templateArgs = strings.TrimPrefix(strings.TrimSuffix(templateArgs, ")"), "(")

		var parts []string

		for _, part := range strings.Split(templateArgs, ",") {
			trimmed := strings.TrimSpace(part)
			if trimmed == "" {
				continue
			}

			parts = append(parts, trimmed)

			// If we are dealing with key value pairs then split, trimspace and set
			// in params. We only expect 2 values, any more and we wont consider the rest.
			if kvPieces := strings.Split(trimmed, "=>"); len(kvPieces) > 1 {
				val := strings.TrimSpace(kvPieces[1])

				params[strings.TrimSpace(kvPieces[0])] = val
			}
		}

		template := strings.TrimSpace(readTemplate(reader))

		var asJSON bool
		for _, item := range parts {
			if item == "asJSON" {
				asJSON = true
				break
			}
		}

		if _, ok := params["asJSON"]; ok {
			asJSON = true
		}

		var attrs map[string]interface{}

		if asJSON {
			if err := json.Unmarshal([]byte(template), &attrs); err == nil {
				template = ""
			}
		} else {
			attrs = make(map[string]interface{})
		}

		annotations = append(annotations, AnnotationDeclaration{
			Arguments: parts,
			Name:      argName,
			Template:  template,
			Params:    params,
			Attrs:     attrs,
		})

	}

	return annotations
}

var ending = []byte("})")
var newline = []byte("\n")
var empty = []byte("")
var singleComment = []byte("//")
var multiComment = []byte("/*")
var multiCommentItem = []byte("*")
var commentry = regexp.MustCompile(`\s*?([\/\/*|\*|\/]+)`)

func readTemplate(reader *bufio.Reader) string {
	var bu bytes.Buffer

	var seenEnd bool

	for {
		// Do we have another pending prefix, if so, we are at the ending, so return.
		if seenEnd {
			data, _ := reader.Peek(100)
			dataVal := commentry.ReplaceAllString(string(data), "")
			dataVal = string(cleanWord([]byte(dataVal)))

			// fmt.Printf("Peek2: %+q -> %+q\n", dataVal, data)

			if strings.HasPrefix(string(dataVal), "@") {
				return bu.String()
			}

			// If it's all space, then return.
			// if strings.TrimSpace(string(dataVal)) == "" {
			return bu.String()
			// }
		}

		twoWord, err := reader.ReadString('\n')
		if err != nil {
			bu.WriteString(twoWord)
			return bu.String()
		}

		twoWorded := cleanWord([]byte(twoWord))
		// fmt.Printf("Ending: %+q -> %t\n", twoWorded, bytes.HasPrefix(twoWorded, ending))

		if bytes.HasPrefix(twoWorded, ending) {
			seenEnd = true
			continue
		}

		bu.WriteString(twoWord)
	}

}

func cleanWord(word []byte) []byte {
	word = bytes.TrimSpace(word)
	word = bytes.TrimPrefix(word, singleComment)
	word = bytes.TrimPrefix(word, multiComment)
	word = bytes.TrimPrefix(word, multiCommentItem)
	return bytes.TrimSpace(word)
}
<commit_msg>Fix nil Attrs map<commit_after>package ast

import (
	"bufio"
	"bytes"
	"encoding/json"
	"io"
	"regexp"
	"strconv"
	"strings"
)

// AnnotationDeclaration defines a annotation type which holds detail about a giving annotation.
type AnnotationDeclaration struct {
	Name      string                 `json:"name"`
	Template  string                 `json:"template"`
	Arguments []string               `json:"arguments"`
	Params    map[string]string      `json:"params"`
	Attrs     map[string]interface{} `json:"attrs"`
	Defer     bool                   `json:"defer"`
}

// HasArg returns true/false if the giving AnnotationDeclaration has a giving key in its Arguments.
func (ad AnnotationDeclaration) HasArg(name string) bool {
	for _, item := range ad.Arguments {
		if item == name {
			return true
		}
	}

	return false
}

// Param returns the associated  param value with giving key ("name").
func (ad AnnotationDeclaration) Param(name string) string {
	return ad.Params[name]
}

// Attrs returns the associated  param value with giving key ("name").
func (ad AnnotationDeclaration) Attr(name string) interface{} {
	return ad.Attrs[name]
}

// ReadAnnotationsFromCommentry returns a slice of all annotation passed from the provided list.
func ReadAnnotationsFromCommentry(r io.Reader) []AnnotationDeclaration {
	var annotations []AnnotationDeclaration

	reader := bufio.NewReader(r)

	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			break
		}

		trimmedline := string(cleanWord([]byte(line)))
		if trimmedline == "" {
			continue
		}

		// Do we have a annotation here?
		if !strings.HasPrefix(trimmedline, "@") {
			continue
		}

		params := make(map[string]string, 0)

		if !strings.Contains(trimmedline, "(") {
			annotations = append(annotations, AnnotationDeclaration{Name: trimmedline, Params: params, Attrs: make(map[string]interface{})})
			continue
		}

		argIndex := strings.IndexRune(trimmedline, '(')
		argName := trimmedline[:argIndex]
		argContents := trimmedline[argIndex:]

		// Do we have a template associated with this annotation, if not, split the
		// commas and let those be our arguments.
		if !strings.HasSuffix(argContents, "{") {

			argContents = strings.TrimPrefix(strings.TrimSuffix(argContents, ")"), "(")

			var parts []string

			for _, part := range strings.Split(argContents, ",") {
				trimmed := strings.TrimSpace(part)
				if trimmed == "" {
					continue
				}

				parts = append(parts, trimmed)

				// If we are dealing with key value pairs then split, trimspace and set
				// in params. We only expect 2 values, any more and we wont consider the rest.
				if kvPieces := strings.Split(trimmed, "=>"); len(kvPieces) > 1 {
					val := strings.TrimSpace(kvPieces[1])
					params[strings.TrimSpace(kvPieces[0])] = val
				}
			}

			var deferred bool

			// Find out if we are to be deferred
			defered, ok := params["defer"]
			if !ok {
				defered, ok = params["Defer"]
			}

			deferred, _ = strconv.ParseBool(defered)

			annotations = append(annotations, AnnotationDeclaration{
				Arguments: parts,
				Name:      argName,
				Params:    params,
				Defer:     deferred,
				Attrs:     make(map[string]interface{}),
			})

			continue
		}

		templateIndex := strings.IndexRune(argContents, '{')
		templateArgs := argContents[:templateIndex]
		templateArgs = strings.TrimPrefix(strings.TrimSuffix(templateArgs, ")"), "(")

		var parts []string

		for _, part := range strings.Split(templateArgs, ",") {
			trimmed := strings.TrimSpace(part)
			if trimmed == "" {
				continue
			}

			parts = append(parts, trimmed)

			// If we are dealing with key value pairs then split, trimspace and set
			// in params. We only expect 2 values, any more and we wont consider the rest.
			if kvPieces := strings.Split(trimmed, "=>"); len(kvPieces) > 1 {
				val := strings.TrimSpace(kvPieces[1])
				params[strings.TrimSpace(kvPieces[0])] = val
			}
		}

		template := strings.TrimSpace(readTemplate(reader))

		var asJSON bool
		for _, item := range parts {
			if item == "asJSON" {
				asJSON = true
				break
			}
		}

		if _, ok := params["asJSON"]; ok {
			asJSON = true
		}

		var attrs map[string]interface{}

		if asJSON {
			if err := json.Unmarshal([]byte(template), &attrs); err == nil {
				template = ""
			}
		} else {
			attrs = make(map[string]interface{})
		}

		annotations = append(annotations, AnnotationDeclaration{
			Arguments: parts,
			Name:      argName,
			Template:  template,
			Params:    params,
			Attrs:     attrs,
		})

	}

	return annotations
}

var ending = []byte("})")
var newline = []byte("\n")
var empty = []byte("")
var singleComment = []byte("//")
var multiComment = []byte("/*")
var multiCommentItem = []byte("*")
var commentry = regexp.MustCompile(`\s*?([\/\/*|\*|\/]+)`)

func readTemplate(reader *bufio.Reader) string {
	var bu bytes.Buffer

	var seenEnd bool

	for {
		// Do we have another pending prefix, if so, we are at the ending, so return.
		if seenEnd {
			data, _ := reader.Peek(100)
			dataVal := commentry.ReplaceAllString(string(data), "")
			dataVal = string(cleanWord([]byte(dataVal)))

			// fmt.Printf("Peek2: %+q -> %+q\n", dataVal, data)

			if strings.HasPrefix(string(dataVal), "@") {
				return bu.String()
			}

			// If it's all space, then return.
			// if strings.TrimSpace(string(dataVal)) == "" {
			return bu.String()
			// }
		}

		twoWord, err := reader.ReadString('\n')
		if err != nil {
			bu.WriteString(twoWord)
			return bu.String()
		}

		twoWorded := cleanWord([]byte(twoWord))
		// fmt.Printf("Ending: %+q -> %t\n", twoWorded, bytes.HasPrefix(twoWorded, ending))

		if bytes.HasPrefix(twoWorded, ending) {
			seenEnd = true
			continue
		}

		bu.WriteString(twoWord)
	}

}

func cleanWord(word []byte) []byte {
	word = bytes.TrimSpace(word)
	word = bytes.TrimPrefix(word, singleComment)
	word = bytes.TrimPrefix(word, multiComment)
	word = bytes.TrimPrefix(word, multiCommentItem)
	return bytes.TrimSpace(word)
}
<|endoftext|>


<commit_before>package remote

import (
	"bytes"
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/hashicorp/terraform/terraform"
)

const (
	// LocalDirectory is the directory created in the working
	// dir to hold the remote state file.
	LocalDirectory = ".terraform"

	// HiddenStateFile is the name of the state file in the
	// LocalDirectory
	HiddenStateFile = "terraform.tfstate"

	// BackupHiddenStateFile is the path we backup the state
	// file to before modifications are made
	BackupHiddenStateFile = "terraform.tfstate.backup"
)

// StateChangeResult is used to communicate to a caller
// what actions have been taken when updating a state file
type StateChangeResult int

const (
	// StateChangeNoop indicates nothing has happened,
	// but that does not indicate an error. Everything is
	// just up to date. (Push/Pull)
	StateChangeNoop StateChangeResult = iota

	// StateChangeInit indicates that there is no local or
	// remote state, and that the state was initialized
	StateChangeInit

	// StateChangeUpdateLocal indicates the local state
	// was updated. (Pull)
	StateChangeUpdateLocal

	// StateChangeUpdateRemote indicates the remote state
	// was updated. (Push)
	StateChangeUpdateRemote

	// StateChangeLocalNewer means the pull was a no-op
	// because the local state is newer than that of the
	// server. This means a Push should take place. (Pull)
	StateChangeLocalNewer

	// StateChangeRemoteNewer means the push was a no-op
	// because the remote state is newer than that of the
	// local state. This means a Pull should take place.
	// (Push)
	StateChangeRemoteNewer

	// StateChangeConflict means that the push or pull
	// was a no-op because there is a conflict. This means
	// there are multiple state definitions at the same
	// serial number with different contents. This requires
	// an operator to intervene and resolve the conflict.
	// Shame on the user for doing concurrent apply.
	// (Push/Pull)
	StateChangeConflict
)

func (sc StateChangeResult) String() string {
	switch sc {
	case StateChangeNoop:
		return "Local and remote state in sync"
	case StateChangeInit:
		return "Local state initialized"
	case StateChangeUpdateLocal:
		return "Local state updated"
	case StateChangeUpdateRemote:
		return "Remote state updated"
	case StateChangeLocalNewer:
		return "Local state is newer than remote state, push required"
	case StateChangeRemoteNewer:
		return "Remote state is newer than local state, pull required"
	case StateChangeConflict:
		return "Local and remote state conflict, manual resolution required"
	default:
		return fmt.Sprintf("Unknown state change type: %d", sc)
	}
}

// SuccessfulPull is used to clasify the StateChangeResult for
// a pull operation. This is different by operation, but can be used
// to determine a proper exit code.
func (sc StateChangeResult) SuccessfulPull() bool {
	switch sc {
	case StateChangeNoop:
		return true
	case StateChangeInit:
		return true
	case StateChangeUpdateLocal:
		return true
	case StateChangeLocalNewer:
		return false
	case StateChangeConflict:
		return false
	default:
		return false
	}
}

// SuccessfulPush is used to clasify the StateChangeResult for
// a push operation. This is different by operation, but can be used
// to determine a proper exit code
func (sc StateChangeResult) SuccessfulPush() bool {
	switch sc {
	case StateChangeNoop:
		return true
	case StateChangeUpdateRemote:
		return true
	case StateChangeRemoteNewer:
		return false
	case StateChangeConflict:
		return false
	default:
		return false
	}
}

// EnsureDirectory is used to make sure the local storage
// directory exists
func EnsureDirectory() error {
	cwd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Failed to get current directory: %v", err)
	}
	path := filepath.Join(cwd, LocalDirectory)
	if err := os.Mkdir(path, 0770); err != nil {
		if os.IsExist(err) {
			return nil
		}
		return fmt.Errorf("Failed to make directory '%s': %v", path, err)
	}
	return nil
}

// HiddenStatePath is used to return the path to the hidden state file,
// should there be one.
// TODO: Rename to LocalStatePath
func HiddenStatePath() (string, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return "", fmt.Errorf("Failed to get current directory: %v", err)
	}
	path := filepath.Join(cwd, LocalDirectory, HiddenStateFile)
	return path, nil
}

// HaveLocalState is used to check if we have a local state file
func HaveLocalState() (bool, error) {
	path, err := HiddenStatePath()
	if err != nil {
		return false, err
	}
	return ExistsFile(path)
}

// ExistsFile is used to check if a given file exists
func ExistsFile(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

// ValidConfig does a purely logical validation of the remote config
func ValidConfig(conf *terraform.RemoteState) error {
	// Default the type to Atlas
	if conf.Type == "" {
		conf.Type = "atlas"
	}
	_, err := NewClientByState(conf)
	if err != nil {
		return err
	}
	return nil
}

// ReadLocalState is used to read and parse the local state file
func ReadLocalState() (*terraform.State, []byte, error) {
	path, err := HiddenStatePath()
	if err != nil {
		return nil, nil, err
	}

	// Open the existing file
	raw, err := ioutil.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil, nil
		}
		return nil, nil, fmt.Errorf("Failed to open state file '%s': %s", path, err)
	}

	// Decode the state
	state, err := terraform.ReadState(bytes.NewReader(raw))
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to read state file '%s': %v", path, err)
	}
	return state, raw, nil
}

// RefreshState is used to read the remote state given
// the configuration for the remote endpoint, and update
// the local state if necessary.
func RefreshState(conf *terraform.RemoteState) (StateChangeResult, error) {
	if conf == nil {
		return StateChangeNoop, fmt.Errorf("Missing remote server configuration")
	}

	// Read the state from the server
	client, err := NewClientByState(conf)
	if err != nil {
		return StateChangeNoop,
			fmt.Errorf("Failed to create remote client: %v", err)
	}
	payload, err := client.GetState()
	if err != nil {
		return StateChangeNoop,
			fmt.Errorf("Failed to read remote state: %v", err)
	}

	// Parse the remote state
	var remoteState *terraform.State
	if payload != nil {
		remoteState, err = terraform.ReadState(bytes.NewReader(payload.State))
		if err != nil {
			return StateChangeNoop,
				fmt.Errorf("Failed to parse remote state: %v", err)
		}

		// Ensure we understand the remote version!
		if remoteState.Version > terraform.StateVersion {
			return StateChangeNoop, fmt.Errorf(
				`Remote state is version %d, this version of Terraform only understands up to %d`, remoteState.Version, terraform.StateVersion)
		}
	}

	// Decode the state
	localState, raw, err := ReadLocalState()
	if err != nil {
		return StateChangeNoop, err
	}

	// We need to handle the matrix of cases in reconciling
	// the local and remote state. Primarily the concern is
	// around the Serial number which should grow monotonically.
	// Additionally, we use the MD5 to detect a conflict for
	// a given Serial.
	switch {
	case remoteState == nil && localState == nil:
		// Initialize a blank state
		out, _ := blankState(conf)
		if err := Persist(bytes.NewReader(out)); err != nil {
			return StateChangeNoop,
				fmt.Errorf("Failed to persist state: %v", err)
		}
		return StateChangeInit, nil

	case remoteState == nil && localState != nil:
		// User should probably do a push, nothing to do
		return StateChangeLocalNewer, nil

	case remoteState != nil && localState == nil:
		goto PERSIST

	case remoteState.Serial < localState.Serial:
		// User should probably do a push, nothing to do
		return StateChangeLocalNewer, nil

	case remoteState.Serial > localState.Serial:
		goto PERSIST

	case remoteState.Serial == localState.Serial:
		// Check for a hash collision on the local/remote state
		localMD5 := md5.Sum(raw)
		if bytes.Equal(localMD5[:md5.Size], payload.MD5) {
			// Hash collision, everything is up-to-date
			return StateChangeNoop, nil
		} else {
			// This is very bad. This means we have 2 state files
			// with the same Serial but a different hash. Most probably
			// explaination is two parallel apply operations. This
			// requires a manual reconciliation.
			return StateChangeConflict, nil
		}
	default:
		// We should not reach this point
		panic("Unhandled remote update case")
	}

PERSIST:
	// Update the local state from the remote state
	if err := Persist(bytes.NewReader(payload.State)); err != nil {
		return StateChangeNoop,
			fmt.Errorf("Failed to persist state: %v", err)
	}
	return StateChangeUpdateLocal, nil
}

// PushState is used to read the local state and
// update the remote state if necessary. The state push
// can be 'forced' to override any conflict detection
// on the server-side.
func PushState(conf *terraform.RemoteState, force bool) (StateChangeResult, error) {
	// Read the local state
	_, raw, err := ReadLocalState()
	if err != nil {
		return StateChangeNoop, err
	}

	// Check if there is no local state
	if raw == nil {
		return StateChangeNoop, fmt.Errorf("No local state to push")
	}

	// Push the state to the server
	client, err := NewClientByState(conf)
	if err != nil {
		return StateChangeNoop,
			fmt.Errorf("Failed to create remote client: %v", err)
	}
	err = client.PutState(raw, force)

	// Handle the various edge cases
	switch err {
	case nil:
		return StateChangeUpdateRemote, nil
	case ErrServerNewer:
		return StateChangeRemoteNewer, nil
	case ErrConflict:
		return StateChangeConflict, nil
	default:
		return StateChangeNoop, err
	}
}

// DeleteState is used to delete the remote state given
// the configuration for the remote endpoint.
func DeleteState(conf *terraform.RemoteState) error {
	if conf == nil {
		return fmt.Errorf("Missing remote server configuration")
	}

	// Setup the client
	client, err := NewClientByState(conf)
	if err != nil {
		return fmt.Errorf("Failed to create remote client: %v", err)
	}

	// Destroy the state
	err = client.DeleteState()
	if err != nil {
		return fmt.Errorf("Failed to delete remote state: %v", err)
	}
	return nil
}

// blankState is used to return a serialized form of a blank state
// with only the remote info.
func blankState(conf *terraform.RemoteState) ([]byte, error) {
	blank := terraform.NewState()
	blank.Remote = conf
	buf := bytes.NewBuffer(nil)
	err := terraform.WriteState(blank, buf)
	return buf.Bytes(), err
}

// PersistState is used to persist out the given terraform state
// in our local state cache location.
func PersistState(s *terraform.State) error {
	buf := bytes.NewBuffer(nil)
	if err := terraform.WriteState(s, buf); err != nil {
		return fmt.Errorf("Failed to encode state: %v", err)
	}
	if err := Persist(buf); err != nil {
		return err
	}
	return nil
}

// Persist is used to write out the state given by a reader (likely
// being streamed from a remote server) to the local storage.
func Persist(r io.Reader) error {
	cwd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Failed to get current directory: %v", err)
	}
	statePath := filepath.Join(cwd, LocalDirectory, HiddenStateFile)
	backupPath := filepath.Join(cwd, LocalDirectory, BackupHiddenStateFile)

	// Backup the old file if it exists
	if err := CopyFile(statePath, backupPath); err != nil {
		return fmt.Errorf("Failed to backup state file '%s' to '%s': %v", statePath, backupPath, err)
	}

	// Open the state path
	fh, err := os.Create(statePath)
	if err != nil {
		return fmt.Errorf("Failed to open state file '%s': %v", statePath, err)
	}

	// Copy the new state
	_, err = io.Copy(fh, r)
	fh.Close()
	if err != nil {
		os.Remove(statePath)
		return fmt.Errorf("Failed to persist state file: %v", err)
	}
	return nil
}

// CopyFile is used to copy from a source file if it exists to a destination.
// This is used to create a backup of the state file.
func CopyFile(src, dst string) error {
	srcFH, err := os.Open(src)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	defer srcFH.Close()

	dstFH, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer dstFH.Close()

	_, err = io.Copy(dstFH, srcFH)
	return err
}
<commit_msg>remote: just limiting the public API<commit_after>package remote

import (
	"bytes"
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/hashicorp/terraform/terraform"
)

const (
	// LocalDirectory is the directory created in the working
	// dir to hold the remote state file.
	LocalDirectory = ".terraform"

	// HiddenStateFile is the name of the state file in the
	// LocalDirectory
	HiddenStateFile = "terraform.tfstate"

	// BackupHiddenStateFile is the path we backup the state
	// file to before modifications are made
	BackupHiddenStateFile = "terraform.tfstate.backup"
)

// StateChangeResult is used to communicate to a caller
// what actions have been taken when updating a state file
type StateChangeResult int

const (
	// StateChangeNoop indicates nothing has happened,
	// but that does not indicate an error. Everything is
	// just up to date. (Push/Pull)
	StateChangeNoop StateChangeResult = iota

	// StateChangeInit indicates that there is no local or
	// remote state, and that the state was initialized
	StateChangeInit

	// StateChangeUpdateLocal indicates the local state
	// was updated. (Pull)
	StateChangeUpdateLocal

	// StateChangeUpdateRemote indicates the remote state
	// was updated. (Push)
	StateChangeUpdateRemote

	// StateChangeLocalNewer means the pull was a no-op
	// because the local state is newer than that of the
	// server. This means a Push should take place. (Pull)
	StateChangeLocalNewer

	// StateChangeRemoteNewer means the push was a no-op
	// because the remote state is newer than that of the
	// local state. This means a Pull should take place.
	// (Push)
	StateChangeRemoteNewer

	// StateChangeConflict means that the push or pull
	// was a no-op because there is a conflict. This means
	// there are multiple state definitions at the same
	// serial number with different contents. This requires
	// an operator to intervene and resolve the conflict.
	// Shame on the user for doing concurrent apply.
	// (Push/Pull)
	StateChangeConflict
)

func (sc StateChangeResult) String() string {
	switch sc {
	case StateChangeNoop:
		return "Local and remote state in sync"
	case StateChangeInit:
		return "Local state initialized"
	case StateChangeUpdateLocal:
		return "Local state updated"
	case StateChangeUpdateRemote:
		return "Remote state updated"
	case StateChangeLocalNewer:
		return "Local state is newer than remote state, push required"
	case StateChangeRemoteNewer:
		return "Remote state is newer than local state, pull required"
	case StateChangeConflict:
		return "Local and remote state conflict, manual resolution required"
	default:
		return fmt.Sprintf("Unknown state change type: %d", sc)
	}
}

// SuccessfulPull is used to clasify the StateChangeResult for
// a pull operation. This is different by operation, but can be used
// to determine a proper exit code.
func (sc StateChangeResult) SuccessfulPull() bool {
	switch sc {
	case StateChangeNoop:
		return true
	case StateChangeInit:
		return true
	case StateChangeUpdateLocal:
		return true
	case StateChangeLocalNewer:
		return false
	case StateChangeConflict:
		return false
	default:
		return false
	}
}

// SuccessfulPush is used to clasify the StateChangeResult for
// a push operation. This is different by operation, but can be used
// to determine a proper exit code
func (sc StateChangeResult) SuccessfulPush() bool {
	switch sc {
	case StateChangeNoop:
		return true
	case StateChangeUpdateRemote:
		return true
	case StateChangeRemoteNewer:
		return false
	case StateChangeConflict:
		return false
	default:
		return false
	}
}

// EnsureDirectory is used to make sure the local storage
// directory exists
func EnsureDirectory() error {
	cwd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Failed to get current directory: %v", err)
	}
	path := filepath.Join(cwd, LocalDirectory)
	if err := os.Mkdir(path, 0770); err != nil {
		if os.IsExist(err) {
			return nil
		}
		return fmt.Errorf("Failed to make directory '%s': %v", path, err)
	}
	return nil
}

// HiddenStatePath is used to return the path to the hidden state file,
// should there be one.
// TODO: Rename to LocalStatePath
func HiddenStatePath() (string, error) {
	cwd, err := os.Getwd()
	if err != nil {
		return "", fmt.Errorf("Failed to get current directory: %v", err)
	}
	path := filepath.Join(cwd, LocalDirectory, HiddenStateFile)
	return path, nil
}

// HaveLocalState is used to check if we have a local state file
func HaveLocalState() (bool, error) {
	path, err := HiddenStatePath()
	if err != nil {
		return false, err
	}

	return existsFile(path)
}

// ValidConfig does a purely logical validation of the remote config
func ValidConfig(conf *terraform.RemoteState) error {
	// Default the type to Atlas
	if conf.Type == "" {
		conf.Type = "atlas"
	}
	_, err := NewClientByState(conf)
	if err != nil {
		return err
	}
	return nil
}

// ReadLocalState is used to read and parse the local state file
func ReadLocalState() (*terraform.State, []byte, error) {
	path, err := HiddenStatePath()
	if err != nil {
		return nil, nil, err
	}

	// Open the existing file
	raw, err := ioutil.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil, nil
		}
		return nil, nil, fmt.Errorf("Failed to open state file '%s': %s", path, err)
	}

	// Decode the state
	state, err := terraform.ReadState(bytes.NewReader(raw))
	if err != nil {
		return nil, nil, fmt.Errorf("Failed to read state file '%s': %v", path, err)
	}
	return state, raw, nil
}

// RefreshState is used to read the remote state given
// the configuration for the remote endpoint, and update
// the local state if necessary.
func RefreshState(conf *terraform.RemoteState) (StateChangeResult, error) {
	if conf == nil {
		return StateChangeNoop, fmt.Errorf("Missing remote server configuration")
	}

	// Read the state from the server
	client, err := NewClientByState(conf)
	if err != nil {
		return StateChangeNoop,
			fmt.Errorf("Failed to create remote client: %v", err)
	}
	payload, err := client.GetState()
	if err != nil {
		return StateChangeNoop,
			fmt.Errorf("Failed to read remote state: %v", err)
	}

	// Parse the remote state
	var remoteState *terraform.State
	if payload != nil {
		remoteState, err = terraform.ReadState(bytes.NewReader(payload.State))
		if err != nil {
			return StateChangeNoop,
				fmt.Errorf("Failed to parse remote state: %v", err)
		}

		// Ensure we understand the remote version!
		if remoteState.Version > terraform.StateVersion {
			return StateChangeNoop, fmt.Errorf(
				`Remote state is version %d, this version of Terraform only understands up to %d`, remoteState.Version, terraform.StateVersion)
		}
	}

	// Decode the state
	localState, raw, err := ReadLocalState()
	if err != nil {
		return StateChangeNoop, err
	}

	// We need to handle the matrix of cases in reconciling
	// the local and remote state. Primarily the concern is
	// around the Serial number which should grow monotonically.
	// Additionally, we use the MD5 to detect a conflict for
	// a given Serial.
	switch {
	case remoteState == nil && localState == nil:
		// Initialize a blank state
		out, _ := blankState(conf)
		if err := Persist(bytes.NewReader(out)); err != nil {
			return StateChangeNoop,
				fmt.Errorf("Failed to persist state: %v", err)
		}
		return StateChangeInit, nil

	case remoteState == nil && localState != nil:
		// User should probably do a push, nothing to do
		return StateChangeLocalNewer, nil

	case remoteState != nil && localState == nil:
		goto PERSIST

	case remoteState.Serial < localState.Serial:
		// User should probably do a push, nothing to do
		return StateChangeLocalNewer, nil

	case remoteState.Serial > localState.Serial:
		goto PERSIST

	case remoteState.Serial == localState.Serial:
		// Check for a hash collision on the local/remote state
		localMD5 := md5.Sum(raw)
		if bytes.Equal(localMD5[:md5.Size], payload.MD5) {
			// Hash collision, everything is up-to-date
			return StateChangeNoop, nil
		} else {
			// This is very bad. This means we have 2 state files
			// with the same Serial but a different hash. Most probably
			// explaination is two parallel apply operations. This
			// requires a manual reconciliation.
			return StateChangeConflict, nil
		}
	default:
		// We should not reach this point
		panic("Unhandled remote update case")
	}

PERSIST:
	// Update the local state from the remote state
	if err := Persist(bytes.NewReader(payload.State)); err != nil {
		return StateChangeNoop,
			fmt.Errorf("Failed to persist state: %v", err)
	}
	return StateChangeUpdateLocal, nil
}

// PushState is used to read the local state and
// update the remote state if necessary. The state push
// can be 'forced' to override any conflict detection
// on the server-side.
func PushState(conf *terraform.RemoteState, force bool) (StateChangeResult, error) {
	// Read the local state
	_, raw, err := ReadLocalState()
	if err != nil {
		return StateChangeNoop, err
	}

	// Check if there is no local state
	if raw == nil {
		return StateChangeNoop, fmt.Errorf("No local state to push")
	}

	// Push the state to the server
	client, err := NewClientByState(conf)
	if err != nil {
		return StateChangeNoop,
			fmt.Errorf("Failed to create remote client: %v", err)
	}
	err = client.PutState(raw, force)

	// Handle the various edge cases
	switch err {
	case nil:
		return StateChangeUpdateRemote, nil
	case ErrServerNewer:
		return StateChangeRemoteNewer, nil
	case ErrConflict:
		return StateChangeConflict, nil
	default:
		return StateChangeNoop, err
	}
}

// DeleteState is used to delete the remote state given
// the configuration for the remote endpoint.
func DeleteState(conf *terraform.RemoteState) error {
	if conf == nil {
		return fmt.Errorf("Missing remote server configuration")
	}

	// Setup the client
	client, err := NewClientByState(conf)
	if err != nil {
		return fmt.Errorf("Failed to create remote client: %v", err)
	}

	// Destroy the state
	err = client.DeleteState()
	if err != nil {
		return fmt.Errorf("Failed to delete remote state: %v", err)
	}
	return nil
}

// blankState is used to return a serialized form of a blank state
// with only the remote info.
func blankState(conf *terraform.RemoteState) ([]byte, error) {
	blank := terraform.NewState()
	blank.Remote = conf
	buf := bytes.NewBuffer(nil)
	err := terraform.WriteState(blank, buf)
	return buf.Bytes(), err
}

// PersistState is used to persist out the given terraform state
// in our local state cache location.
func PersistState(s *terraform.State) error {
	buf := bytes.NewBuffer(nil)
	if err := terraform.WriteState(s, buf); err != nil {
		return fmt.Errorf("Failed to encode state: %v", err)
	}
	if err := Persist(buf); err != nil {
		return err
	}
	return nil
}

// Persist is used to write out the state given by a reader (likely
// being streamed from a remote server) to the local storage.
func Persist(r io.Reader) error {
	cwd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("Failed to get current directory: %v", err)
	}
	statePath := filepath.Join(cwd, LocalDirectory, HiddenStateFile)
	backupPath := filepath.Join(cwd, LocalDirectory, BackupHiddenStateFile)

	// Backup the old file if it exists
	if err := copyFile(statePath, backupPath); err != nil {
		return fmt.Errorf("Failed to backup state file '%s' to '%s': %v", statePath, backupPath, err)
	}

	// Open the state path
	fh, err := os.Create(statePath)
	if err != nil {
		return fmt.Errorf("Failed to open state file '%s': %v", statePath, err)
	}

	// Copy the new state
	_, err = io.Copy(fh, r)
	fh.Close()
	if err != nil {
		os.Remove(statePath)
		return fmt.Errorf("Failed to persist state file: %v", err)
	}
	return nil
}

// copyFile is used to copy from a source file if it exists to a destination.
// This is used to create a backup of the state file.
func copyFile(src, dst string) error {
	srcFH, err := os.Open(src)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	defer srcFH.Close()

	dstFH, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer dstFH.Close()

	_, err = io.Copy(dstFH, srcFH)
	return err
}

// existsFile is used to check if a given file exists
func existsFile(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
<|endoftext|>


<commit_before>package gateway

import (
	"bytes"
	"fmt"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	//	"github.com/davecgh/go-spew/spew"
	"crypto/sha1"
	"github.com/hashicorp/golang-lru"
	"github.com/peterhellberg/emojilib"
	"net/http"
	"regexp"
	"strings"
	"time"
)

type Gateway struct {
	*config.Config
	Router         *Router
	MyConfig       *config.Gateway
	Bridges        map[string]*bridge.Bridge
	Channels       map[string]*config.ChannelInfo
	ChannelOptions map[string]config.ChannelOptions
	Message        chan config.Message
	Name           string
	Messages       *lru.Cache
}

type BrMsgID struct {
	br        *bridge.Bridge
	ID        string
	ChannelID string
}

func New(cfg config.Gateway, r *Router) *Gateway {
	gw := &Gateway{Channels: make(map[string]*config.ChannelInfo), Message: r.Message,
		Router: r, Bridges: make(map[string]*bridge.Bridge), Config: r.Config}
	cache, _ := lru.New(5000)
	gw.Messages = cache
	gw.AddConfig(&cfg)
	return gw
}

func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
	br := gw.Router.getBridge(cfg.Account)
	if br == nil {
		br = bridge.New(gw.Config, cfg, gw.Message)
	}
	gw.mapChannelsToBridge(br)
	gw.Bridges[cfg.Account] = br
	return nil
}

func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
	gw.Name = cfg.Name
	gw.MyConfig = cfg
	gw.mapChannels()
	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
		err := gw.AddBridge(&br)
		if err != nil {
			return err
		}
	}
	return nil
}

func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) {
	for ID, channel := range gw.Channels {
		if br.Account == channel.Account {
			br.Channels[ID] = *channel
		}
	}
}

func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
	br.Disconnect()
	time.Sleep(time.Second * 5)
RECONNECT:
	log.Infof("Reconnecting %s", br.Account)
	err := br.Connect()
	if err != nil {
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
		time.Sleep(time.Second * 60)
		goto RECONNECT
	}
	br.Joined = make(map[string]bool)
	br.JoinChannels()
}

func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) {
	for _, br := range cfg {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		if _, ok := gw.Channels[ID]; !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account,
				SameChannel: make(map[string]bool)}
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		} else {
			// if we already have a key and it's not our current direction it means we have a bidirectional inout
			if gw.Channels[ID].Direction != direction {
				gw.Channels[ID].Direction = "inout"
			}
		}
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}
}

func (gw *Gateway) mapChannels() error {
	gw.mapChannelConfig(gw.MyConfig.In, "in")
	gw.mapChannelConfig(gw.MyConfig.Out, "out")
	gw.mapChannelConfig(gw.MyConfig.InOut, "inout")
	return nil
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
	var channels []config.ChannelInfo
	// if source channel is in only, do nothing
	for _, channel := range gw.Channels {
		// lookup the channel from the message
		if channel.ID == getChannelID(*msg) {
			// we only have destinations if the original message is from an "in" (sending) channel
			if !strings.Contains(channel.Direction, "in") {
				return channels
			}
			continue
		}
	}
	for _, channel := range gw.Channels {
		if _, ok := gw.Channels[getChannelID(*msg)]; !ok {
			continue
		}

		// do samechannelgateway logic
		if channel.SameChannel[msg.Gateway] {
			if msg.Channel == channel.Name && msg.Account != dest.Account {
				channels = append(channels, *channel)
			}
			continue
		}
		if strings.Contains(channel.Direction, "out") && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
			channels = append(channels, *channel)
		}
	}
	return channels
}

func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrMsgID {
	var brMsgIDs []*BrMsgID

	// TODO refactor
	// only slack now, check will have to be done in the different bridges.
	// we need to check if we can't use fallback or text in other bridges
	if msg.Extra != nil {
		if dest.Protocol != "discord" &&
			dest.Protocol != "slack" &&
			dest.Protocol != "mattermost" &&
			dest.Protocol != "telegram" &&
			dest.Protocol != "matrix" {
			if msg.Text == "" {
				return brMsgIDs
			}
		}
	}
	// only relay join/part when configged
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
		return brMsgIDs
	}
	// broadcast to every out channel (irc QUIT)
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
		log.Debug("empty channel")
		return brMsgIDs
	}
	originchannel := msg.Channel
	origmsg := msg
	channels := gw.getDestChannel(&msg, *dest)
	for _, channel := range channels {
		// do not send to ourself
		if channel.ID == getChannelID(origmsg) {
			continue
		}
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
		msg.Channel = channel.Name
		msg.Avatar = gw.modifyAvatar(origmsg, dest)
		msg.Username = gw.modifyUsername(origmsg, dest)
		msg.ID = ""
		if res, ok := gw.Messages.Get(origmsg.ID); ok {
			IDs := res.([]*BrMsgID)
			for _, id := range IDs {
				// check protocol, bridge name and channelname
				// for people that reuse the same bridge multiple times. see #342
				if dest.Protocol == id.br.Protocol && dest.Name == id.br.Name && channel.ID == id.ChannelID {
					msg.ID = id.ID
				}
			}
		}
		// for api we need originchannel as channel
		if dest.Protocol == "api" {
			msg.Channel = originchannel
		}
		mID, err := dest.Send(msg)
		if err != nil {
			fmt.Println(err)
		}
		// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
		if mID != "" {
			brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID, channel.ID})
		}
	}
	return brMsgIDs
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	// if we don't have the bridge, ignore it
	if _, ok := gw.Bridges[msg.Account]; !ok {
		return true
	}
	if msg.Text == "" {
		// we have an attachment or actual bytes
		if msg.Extra != nil && (msg.Extra["attachments"] != nil || len(msg.Extra["file"]) > 0) {
			return false
		}
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
		return true
	}
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
		if msg.Username == entry {
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
			return true
		}
	}
	// TODO do not compile regexps everytime
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreMessages) {
		if entry != "" {
			re, err := regexp.Compile(entry)
			if err != nil {
				log.Errorf("incorrect regexp %s for %s", entry, msg.Account)
				continue
			}
			if re.MatchString(msg.Text) {
				log.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
				return true
			}
		}
	}
	return false
}

func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) string {
	br := gw.Bridges[msg.Account]
	msg.Protocol = br.Protocol
	if gw.Config.General.StripNick || dest.Config.StripNick {
		re := regexp.MustCompile("[^a-zA-Z0-9]+")
		msg.Username = re.ReplaceAllString(msg.Username, "")
	}
	nick := dest.Config.RemoteNickFormat
	if nick == "" {
		nick = gw.Config.General.RemoteNickFormat
	}

	// loop to replace nicks
	for _, outer := range br.Config.ReplaceNicks {
		search := outer[0]
		replace := outer[1]
		// TODO move compile to bridge init somewhere
		re, err := regexp.Compile(search)
		if err != nil {
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
			break
		}
		msg.Username = re.ReplaceAllString(msg.Username, replace)
	}

	if len(msg.Username) > 0 {
		// fix utf-8 issue #193
		i := 0
		for index := range msg.Username {
			if i == 1 {
				i = index
				break
			}
			i++
		}
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
	}
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
	return nick
}

func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string {
	iconurl := gw.Config.General.IconURL
	if iconurl == "" {
		iconurl = dest.Config.IconURL
	}
	iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
	if msg.Avatar == "" {
		msg.Avatar = iconurl
	}
	return msg.Avatar
}

func (gw *Gateway) modifyMessage(msg *config.Message) {
	// replace :emoji: to unicode
	msg.Text = emojilib.Replace(msg.Text)
	br := gw.Bridges[msg.Account]
	// loop to replace messages
	for _, outer := range br.Config.ReplaceMessages {
		search := outer[0]
		replace := outer[1]
		// TODO move compile to bridge init somewhere
		re, err := regexp.Compile(search)
		if err != nil {
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
			break
		}
		msg.Text = re.ReplaceAllString(msg.Text, replace)
	}
	msg.Gateway = gw.Name
}

func (gw *Gateway) handleFiles(msg *config.Message) {
	if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
		return
	}
	if len(msg.Extra["file"]) > 0 {
		client := &http.Client{
			Timeout: time.Second * 5,
		}
		for i, f := range msg.Extra["file"] {
			fi := f.(config.FileInfo)
			sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data))
			reader := bytes.NewReader(*fi.Data)
			url := gw.Config.General.MediaServerUpload + "/" + sha1sum + "/" + fi.Name
			durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
			extra := msg.Extra["file"][i].(config.FileInfo)
			extra.URL = durl
			msg.Extra["file"][i] = extra
			req, _ := http.NewRequest("PUT", url, reader)
			req.Header.Set("Content-Type", "binary/octet-stream")
			_, err := client.Do(req)
			if err != nil {
				log.Errorf("mediaserver upload failed: %#v", err)
			}
			log.Debugf("mediaserver download URL = %s", durl)
		}
	}
}

func getChannelID(msg config.Message) string {
	return msg.Channel + msg.Account
}

func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
	return msg.Gateway == gw.Name
}

func isApi(account string) bool {
	return strings.HasPrefix(account, "api.")
}
<commit_msg>Obey the Gateway value from the json (api). Closes #344<commit_after>package gateway

import (
	"bytes"
	"fmt"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	//	"github.com/davecgh/go-spew/spew"
	"crypto/sha1"
	"github.com/hashicorp/golang-lru"
	"github.com/peterhellberg/emojilib"
	"net/http"
	"regexp"
	"strings"
	"time"
)

type Gateway struct {
	*config.Config
	Router         *Router
	MyConfig       *config.Gateway
	Bridges        map[string]*bridge.Bridge
	Channels       map[string]*config.ChannelInfo
	ChannelOptions map[string]config.ChannelOptions
	Message        chan config.Message
	Name           string
	Messages       *lru.Cache
}

type BrMsgID struct {
	br        *bridge.Bridge
	ID        string
	ChannelID string
}

func New(cfg config.Gateway, r *Router) *Gateway {
	gw := &Gateway{Channels: make(map[string]*config.ChannelInfo), Message: r.Message,
		Router: r, Bridges: make(map[string]*bridge.Bridge), Config: r.Config}
	cache, _ := lru.New(5000)
	gw.Messages = cache
	gw.AddConfig(&cfg)
	return gw
}

func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
	br := gw.Router.getBridge(cfg.Account)
	if br == nil {
		br = bridge.New(gw.Config, cfg, gw.Message)
	}
	gw.mapChannelsToBridge(br)
	gw.Bridges[cfg.Account] = br
	return nil
}

func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
	gw.Name = cfg.Name
	gw.MyConfig = cfg
	gw.mapChannels()
	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
		err := gw.AddBridge(&br)
		if err != nil {
			return err
		}
	}
	return nil
}

func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) {
	for ID, channel := range gw.Channels {
		if br.Account == channel.Account {
			br.Channels[ID] = *channel
		}
	}
}

func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
	br.Disconnect()
	time.Sleep(time.Second * 5)
RECONNECT:
	log.Infof("Reconnecting %s", br.Account)
	err := br.Connect()
	if err != nil {
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
		time.Sleep(time.Second * 60)
		goto RECONNECT
	}
	br.Joined = make(map[string]bool)
	br.JoinChannels()
}

func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) {
	for _, br := range cfg {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		if _, ok := gw.Channels[ID]; !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account,
				SameChannel: make(map[string]bool)}
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		} else {
			// if we already have a key and it's not our current direction it means we have a bidirectional inout
			if gw.Channels[ID].Direction != direction {
				gw.Channels[ID].Direction = "inout"
			}
		}
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}
}

func (gw *Gateway) mapChannels() error {
	gw.mapChannelConfig(gw.MyConfig.In, "in")
	gw.mapChannelConfig(gw.MyConfig.Out, "out")
	gw.mapChannelConfig(gw.MyConfig.InOut, "inout")
	return nil
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
	var channels []config.ChannelInfo

	// for messages received from the api check that the gateway is the specified one
	if msg.Protocol == "api" && gw.Name != msg.Gateway {
		return channels
	}

	// if source channel is in only, do nothing
	for _, channel := range gw.Channels {
		// lookup the channel from the message
		if channel.ID == getChannelID(*msg) {
			// we only have destinations if the original message is from an "in" (sending) channel
			if !strings.Contains(channel.Direction, "in") {
				return channels
			}
			continue
		}
	}
	for _, channel := range gw.Channels {
		if _, ok := gw.Channels[getChannelID(*msg)]; !ok {
			continue
		}

		// do samechannelgateway logic
		if channel.SameChannel[msg.Gateway] {
			if msg.Channel == channel.Name && msg.Account != dest.Account {
				channels = append(channels, *channel)
			}
			continue
		}
		if strings.Contains(channel.Direction, "out") && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
			channels = append(channels, *channel)
		}
	}
	return channels
}

func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrMsgID {
	var brMsgIDs []*BrMsgID

	// TODO refactor
	// only slack now, check will have to be done in the different bridges.
	// we need to check if we can't use fallback or text in other bridges
	if msg.Extra != nil {
		if dest.Protocol != "discord" &&
			dest.Protocol != "slack" &&
			dest.Protocol != "mattermost" &&
			dest.Protocol != "telegram" &&
			dest.Protocol != "matrix" {
			if msg.Text == "" {
				return brMsgIDs
			}
		}
	}
	// only relay join/part when configged
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
		return brMsgIDs
	}
	// broadcast to every out channel (irc QUIT)
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
		log.Debug("empty channel")
		return brMsgIDs
	}
	originchannel := msg.Channel
	origmsg := msg
	channels := gw.getDestChannel(&msg, *dest)
	for _, channel := range channels {
		// do not send to ourself
		if channel.ID == getChannelID(origmsg) {
			continue
		}
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
		msg.Channel = channel.Name
		msg.Avatar = gw.modifyAvatar(origmsg, dest)
		msg.Username = gw.modifyUsername(origmsg, dest)
		msg.ID = ""
		if res, ok := gw.Messages.Get(origmsg.ID); ok {
			IDs := res.([]*BrMsgID)
			for _, id := range IDs {
				// check protocol, bridge name and channelname
				// for people that reuse the same bridge multiple times. see #342
				if dest.Protocol == id.br.Protocol && dest.Name == id.br.Name && channel.ID == id.ChannelID {
					msg.ID = id.ID
				}
			}
		}
		// for api we need originchannel as channel
		if dest.Protocol == "api" {
			msg.Channel = originchannel
		}
		mID, err := dest.Send(msg)
		if err != nil {
			fmt.Println(err)
		}
		// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
		if mID != "" {
			brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID, channel.ID})
		}
	}
	return brMsgIDs
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	// if we don't have the bridge, ignore it
	if _, ok := gw.Bridges[msg.Account]; !ok {
		return true
	}
	if msg.Text == "" {
		// we have an attachment or actual bytes
		if msg.Extra != nil && (msg.Extra["attachments"] != nil || len(msg.Extra["file"]) > 0) {
			return false
		}
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
		return true
	}
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
		if msg.Username == entry {
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
			return true
		}
	}
	// TODO do not compile regexps everytime
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreMessages) {
		if entry != "" {
			re, err := regexp.Compile(entry)
			if err != nil {
				log.Errorf("incorrect regexp %s for %s", entry, msg.Account)
				continue
			}
			if re.MatchString(msg.Text) {
				log.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
				return true
			}
		}
	}
	return false
}

func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) string {
	br := gw.Bridges[msg.Account]
	msg.Protocol = br.Protocol
	if gw.Config.General.StripNick || dest.Config.StripNick {
		re := regexp.MustCompile("[^a-zA-Z0-9]+")
		msg.Username = re.ReplaceAllString(msg.Username, "")
	}
	nick := dest.Config.RemoteNickFormat
	if nick == "" {
		nick = gw.Config.General.RemoteNickFormat
	}

	// loop to replace nicks
	for _, outer := range br.Config.ReplaceNicks {
		search := outer[0]
		replace := outer[1]
		// TODO move compile to bridge init somewhere
		re, err := regexp.Compile(search)
		if err != nil {
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
			break
		}
		msg.Username = re.ReplaceAllString(msg.Username, replace)
	}

	if len(msg.Username) > 0 {
		// fix utf-8 issue #193
		i := 0
		for index := range msg.Username {
			if i == 1 {
				i = index
				break
			}
			i++
		}
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
	}
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
	return nick
}

func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string {
	iconurl := gw.Config.General.IconURL
	if iconurl == "" {
		iconurl = dest.Config.IconURL
	}
	iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
	if msg.Avatar == "" {
		msg.Avatar = iconurl
	}
	return msg.Avatar
}

func (gw *Gateway) modifyMessage(msg *config.Message) {
	// replace :emoji: to unicode
	msg.Text = emojilib.Replace(msg.Text)
	br := gw.Bridges[msg.Account]
	// loop to replace messages
	for _, outer := range br.Config.ReplaceMessages {
		search := outer[0]
		replace := outer[1]
		// TODO move compile to bridge init somewhere
		re, err := regexp.Compile(search)
		if err != nil {
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
			break
		}
		msg.Text = re.ReplaceAllString(msg.Text, replace)
	}

	// messages from api have Gateway specified, don't overwrite
	if msg.Protocol != "api" {
		msg.Gateway = gw.Name
	}
}

func (gw *Gateway) handleFiles(msg *config.Message) {
	if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
		return
	}
	if len(msg.Extra["file"]) > 0 {
		client := &http.Client{
			Timeout: time.Second * 5,
		}
		for i, f := range msg.Extra["file"] {
			fi := f.(config.FileInfo)
			sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data))
			reader := bytes.NewReader(*fi.Data)
			url := gw.Config.General.MediaServerUpload + "/" + sha1sum + "/" + fi.Name
			durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
			extra := msg.Extra["file"][i].(config.FileInfo)
			extra.URL = durl
			msg.Extra["file"][i] = extra
			req, _ := http.NewRequest("PUT", url, reader)
			req.Header.Set("Content-Type", "binary/octet-stream")
			_, err := client.Do(req)
			if err != nil {
				log.Errorf("mediaserver upload failed: %#v", err)
			}
			log.Debugf("mediaserver download URL = %s", durl)
		}
	}
}

func getChannelID(msg config.Message) string {
	return msg.Channel + msg.Account
}

func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
	return msg.Gateway == gw.Name
}

func isApi(account string) bool {
	return strings.HasPrefix(account, "api.")
}
<|endoftext|>


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

package time

import (
	"syscall"
	"sync"
	"os"
)

// BUG(brainman): The Windows implementation assumes that
// this year's rules for daylight savings time apply to all previous
// and future years as well.

// TODO(brainman): use GetDynamicTimeZoneInformation, whenever possible (Vista and up),
// to improve on situation described in the bug above.

type zone struct {
	name                  string
	offset                int
	year                  int64
	month, day, dayofweek int
	hour, minute, second  int
	abssec                int64
	prev                  *zone
}

// Populate zone struct with Windows supplied information. Returns true, if data is valid.
func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uint16) (dateisgood bool) {
	z.name = syscall.UTF16ToString(name)
	z.offset = int(bias)
	z.year = int64(d.Year)
	z.month = int(d.Month)
	z.day = int(d.Day)
	z.dayofweek = int(d.DayOfWeek)
	z.hour = int(d.Hour)
	z.minute = int(d.Minute)
	z.second = int(d.Second)
	dateisgood = d.Month != 0
	if dateisgood {
		z.offset += int(biasdelta)
	}
	z.offset = -z.offset * 60
	return
}

// Pre-calculate cutoff time in seconds since the Unix epoch, if data is supplied in "absolute" format.
func (z *zone) preCalculateAbsSec() {
	if z.year != 0 {
		z.abssec = (&Time{z.year, int(z.month), int(z.day), int(z.hour), int(z.minute), int(z.second), 0, 0, ""}).Seconds()
		// Time given is in "local" time. Adjust it for "utc".
		z.abssec -= int64(z.prev.offset)
	}
}

// Convert zone cutoff time to sec in number of seconds since the Unix epoch, given particular year.
func (z *zone) cutoffSeconds(year int64) int64 {
	// Windows specifies daylight savings information in "day in month" format:
	// z.month is month number (1-12)
	// z.dayofweek is appropriate weekday (Sunday=0 to Saturday=6)
	// z.day is week within the month (1 to 5, where 5 is last week of the month)
	// z.hour, z.minute and z.second are absolute time
	t := &Time{year, int(z.month), 1, int(z.hour), int(z.minute), int(z.second), 0, 0, ""}
	t = SecondsToUTC(t.Seconds())
	i := int(z.dayofweek) - t.Weekday
	if i < 0 {
		i += 7
	}
	t.Day += i
	if week := int(z.day) - 1; week < 4 {
		t.Day += week * 7
	} else {
		// "Last" instance of the day.
		t.Day += 4 * 7
		if t.Day > months(year)[t.Month] {
			t.Day -= 7
		}
	}
	// Result is in "local" time. Adjust it for "utc".
	return t.Seconds() - int64(z.prev.offset)
}

// Is t before the cutoff for switching to z?
func (z *zone) isBeforeCutoff(t *Time) bool {
	var coff int64
	if z.year == 0 {
		// "day in month" format used
		coff = z.cutoffSeconds(t.Year)
	} else {
		// "absolute" format used
		coff = z.abssec
	}
	return t.Seconds() < coff
}

type zoneinfo struct {
	disabled         bool // daylight saving time is not used locally
	offsetIfDisabled int
	januaryIsStd     bool // is january 1 standard time?
	std, dst         zone
}

// Pick zone (std or dst) t time belongs to.
func (zi *zoneinfo) pickZone(t *Time) *zone {
	z := &zi.std
	if tz.januaryIsStd {
		if !zi.dst.isBeforeCutoff(t) && zi.std.isBeforeCutoff(t) {
			// after switch to daylight time and before the switch back to standard
			z = &zi.dst
		}
	} else {
		if zi.std.isBeforeCutoff(t) || !zi.dst.isBeforeCutoff(t) {
			// before switch to standard time or after the switch back to daylight
			z = &zi.dst
		}
	}
	return z
}

var tz zoneinfo
var initError os.Error
var onceSetupZone sync.Once

func setupZone() {
	var i syscall.Timezoneinformation
	if _, e := syscall.GetTimeZoneInformation(&i); e != 0 {
		initError = os.NewSyscallError("GetTimeZoneInformation", e)
		return
	}
	if !tz.std.populate(i.Bias, i.StandardBias, &i.StandardDate, i.StandardName[0:]) {
		tz.disabled = true
		tz.offsetIfDisabled = tz.std.offset
		return
	}
	tz.std.prev = &tz.dst
	tz.dst.populate(i.Bias, i.DaylightBias, &i.DaylightDate, i.DaylightName[0:])
	tz.dst.prev = &tz.std
	tz.std.preCalculateAbsSec()
	tz.dst.preCalculateAbsSec()
	// Is january 1 standard time this year?
	t := UTC()
	tz.januaryIsStd = tz.dst.cutoffSeconds(t.Year) < tz.std.cutoffSeconds(t.Year)
}

// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location.
func lookupTimezone(sec int64) (zone string, offset int) {
	onceSetupZone.Do(setupZone)
	if initError != nil {
		return "", 0
	}
	if tz.disabled {
		return "", tz.offsetIfDisabled
	}
	t := SecondsToUTC(sec)
	z := &tz.std
	if tz.std.year == 0 {
		// "day in month" format used
		z = tz.pickZone(t)
	} else {
		// "absolute" format used
		if tz.std.year == t.Year {
			// we have rule for the year in question
			z = tz.pickZone(t)
		} else {
			// we do not have any information for that year,
			// will assume standard offset all year around
		}
	}
	return z.name, z.offset
}

// lookupByName returns the time offset for the
// time zone with the given abbreviation. It only considers
// time zones that apply to the current system.
func lookupByName(name string) (off int, found bool) {
	onceSetupZone.Do(setupZone)
	if initError != nil {
		return 0, false
	}
	if tz.disabled {
		return tz.offsetIfDisabled, false
	}
	switch name {
	case tz.std.name:
		return tz.std.offset, true
	case tz.dst.name:
		return tz.dst.offset, true
	}
	return 0, false
}
<commit_msg>time: fix windows build<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time

import (
	"syscall"
	"sync"
	"os"
)

// BUG(brainman): The Windows implementation assumes that
// this year's rules for daylight savings time apply to all previous
// and future years as well.

// TODO(brainman): use GetDynamicTimeZoneInformation, whenever possible (Vista and up),
// to improve on situation described in the bug above.

type zone struct {
	name                  string
	offset                int
	year                  int64
	month, day, dayofweek int
	hour, minute, second  int
	abssec                int64
	prev                  *zone
}

// Populate zone struct with Windows supplied information. Returns true, if data is valid.
func (z *zone) populate(bias, biasdelta int32, d *syscall.Systemtime, name []uint16) (dateisgood bool) {
	z.name = syscall.UTF16ToString(name)
	z.offset = int(bias)
	z.year = int64(d.Year)
	z.month = int(d.Month)
	z.day = int(d.Day)
	z.dayofweek = int(d.DayOfWeek)
	z.hour = int(d.Hour)
	z.minute = int(d.Minute)
	z.second = int(d.Second)
	dateisgood = d.Month != 0
	if dateisgood {
		z.offset += int(biasdelta)
	}
	z.offset = -z.offset * 60
	return
}

// Pre-calculate cutoff time in seconds since the Unix epoch, if data is supplied in "absolute" format.
func (z *zone) preCalculateAbsSec() {
	if z.year != 0 {
		z.abssec = (&Time{z.year, int(z.month), int(z.day), int(z.hour), int(z.minute), int(z.second), 0, 0, 0, ""}).Seconds()
		// Time given is in "local" time. Adjust it for "utc".
		z.abssec -= int64(z.prev.offset)
	}
}

// Convert zone cutoff time to sec in number of seconds since the Unix epoch, given particular year.
func (z *zone) cutoffSeconds(year int64) int64 {
	// Windows specifies daylight savings information in "day in month" format:
	// z.month is month number (1-12)
	// z.dayofweek is appropriate weekday (Sunday=0 to Saturday=6)
	// z.day is week within the month (1 to 5, where 5 is last week of the month)
	// z.hour, z.minute and z.second are absolute time
	t := &Time{year, int(z.month), 1, int(z.hour), int(z.minute), int(z.second), 0, 0, 0, ""}
	t = SecondsToUTC(t.Seconds())
	i := int(z.dayofweek) - t.Weekday
	if i < 0 {
		i += 7
	}
	t.Day += i
	if week := int(z.day) - 1; week < 4 {
		t.Day += week * 7
	} else {
		// "Last" instance of the day.
		t.Day += 4 * 7
		if t.Day > months(year)[t.Month] {
			t.Day -= 7
		}
	}
	// Result is in "local" time. Adjust it for "utc".
	return t.Seconds() - int64(z.prev.offset)
}

// Is t before the cutoff for switching to z?
func (z *zone) isBeforeCutoff(t *Time) bool {
	var coff int64
	if z.year == 0 {
		// "day in month" format used
		coff = z.cutoffSeconds(t.Year)
	} else {
		// "absolute" format used
		coff = z.abssec
	}
	return t.Seconds() < coff
}

type zoneinfo struct {
	disabled         bool // daylight saving time is not used locally
	offsetIfDisabled int
	januaryIsStd     bool // is january 1 standard time?
	std, dst         zone
}

// Pick zone (std or dst) t time belongs to.
func (zi *zoneinfo) pickZone(t *Time) *zone {
	z := &zi.std
	if tz.januaryIsStd {
		if !zi.dst.isBeforeCutoff(t) && zi.std.isBeforeCutoff(t) {
			// after switch to daylight time and before the switch back to standard
			z = &zi.dst
		}
	} else {
		if zi.std.isBeforeCutoff(t) || !zi.dst.isBeforeCutoff(t) {
			// before switch to standard time or after the switch back to daylight
			z = &zi.dst
		}
	}
	return z
}

var tz zoneinfo
var initError os.Error
var onceSetupZone sync.Once

func setupZone() {
	var i syscall.Timezoneinformation
	if _, e := syscall.GetTimeZoneInformation(&i); e != 0 {
		initError = os.NewSyscallError("GetTimeZoneInformation", e)
		return
	}
	if !tz.std.populate(i.Bias, i.StandardBias, &i.StandardDate, i.StandardName[0:]) {
		tz.disabled = true
		tz.offsetIfDisabled = tz.std.offset
		return
	}
	tz.std.prev = &tz.dst
	tz.dst.populate(i.Bias, i.DaylightBias, &i.DaylightDate, i.DaylightName[0:])
	tz.dst.prev = &tz.std
	tz.std.preCalculateAbsSec()
	tz.dst.preCalculateAbsSec()
	// Is january 1 standard time this year?
	t := UTC()
	tz.januaryIsStd = tz.dst.cutoffSeconds(t.Year) < tz.std.cutoffSeconds(t.Year)
}

// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location.
func lookupTimezone(sec int64) (zone string, offset int) {
	onceSetupZone.Do(setupZone)
	if initError != nil {
		return "", 0
	}
	if tz.disabled {
		return "", tz.offsetIfDisabled
	}
	t := SecondsToUTC(sec)
	z := &tz.std
	if tz.std.year == 0 {
		// "day in month" format used
		z = tz.pickZone(t)
	} else {
		// "absolute" format used
		if tz.std.year == t.Year {
			// we have rule for the year in question
			z = tz.pickZone(t)
		} else {
			// we do not have any information for that year,
			// will assume standard offset all year around
		}
	}
	return z.name, z.offset
}

// lookupByName returns the time offset for the
// time zone with the given abbreviation. It only considers
// time zones that apply to the current system.
func lookupByName(name string) (off int, found bool) {
	onceSetupZone.Do(setupZone)
	if initError != nil {
		return 0, false
	}
	if tz.disabled {
		return tz.offsetIfDisabled, false
	}
	switch name {
	case tz.std.name:
		return tz.std.offset, true
	case tz.dst.name:
		return tz.dst.offset, true
	}
	return 0, false
}
<|endoftext|>