<commit_before>package filesystem

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/docker/distribution/context"
	storagedriver "github.com/docker/distribution/registry/storage/driver"
	"github.com/docker/distribution/registry/storage/driver/base"
	"github.com/docker/distribution/registry/storage/driver/factory"
)

const driverName = "filesystem"
const defaultRootDirectory = "/tmp/registry/storage"

func init() {
	factory.Register(driverName, &filesystemDriverFactory{})
}

// filesystemDriverFactory implements the factory.StorageDriverFactory interface
type filesystemDriverFactory struct{}

func (factory *filesystemDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
	return FromParameters(parameters), nil
}

type driver struct {
	rootDirectory string
}

type baseEmbed struct {
	base.Base
}

// Driver is a storagedriver.StorageDriver implementation backed by a local
// filesystem. All provided paths will be subpaths of the RootDirectory.
type Driver struct {
	baseEmbed
}

// FromParameters constructs a new Driver with a given parameters map
// Optional Parameters:
// - rootdirectory
func FromParameters(parameters map[string]interface{}) *Driver {
	var rootDirectory = defaultRootDirectory
	if parameters != nil {
		rootDir, ok := parameters["rootdirectory"]
		if ok {
			rootDirectory = fmt.Sprint(rootDir)
		}
	}
	return New(rootDirectory)
}

// New constructs a new Driver with a given rootDirectory
func New(rootDirectory string) *Driver {
	return &Driver{
		baseEmbed: baseEmbed{
			Base: base.Base{
				StorageDriver: &driver{
					rootDirectory: rootDirectory,
				},
			},
		},
	}
}

// Implement the storagedriver.StorageDriver interface

func (d *driver) Name() string {
	return driverName
}

// GetContent retrieves the content stored at "path" as a []byte.
func (d *driver) GetContent(ctx context.Context, path string) ([]byte, error) {
	rc, err := d.ReadStream(ctx, path, 0)
	if err != nil {
		return nil, err
	}
	defer rc.Close()

	p, err := ioutil.ReadAll(rc)
	if err != nil {
		return nil, err
	}

	return p, nil
}

// PutContent stores the []byte content at a location designated by "path".
func (d *driver) PutContent(ctx context.Context, subPath string, contents []byte) error {
	if _, err := d.WriteStream(ctx, subPath, 0, bytes.NewReader(contents)); err != nil {
		return err
	}

	return os.Truncate(d.fullPath(subPath), int64(len(contents)))
}

// ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
// given byte offset.
func (d *driver) ReadStream(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
	file, err := os.OpenFile(d.fullPath(path), os.O_RDONLY, 0644)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, storagedriver.PathNotFoundError{Path: path}
		}

		return nil, err
	}

	seekPos, err := file.Seek(int64(offset), os.SEEK_SET)
	if err != nil {
		file.Close()
		return nil, err
	} else if seekPos < int64(offset) {
		file.Close()
		return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
	}

	return file, nil
}

// WriteStream stores the contents of the provided io.Reader at a location
// designated by the given path.
func (d *driver) WriteStream(ctx context.Context, subPath string, offset int64, reader io.Reader) (nn int64, err error) {
	// TODO(stevvooe): This needs to be a requirement.
	// if !path.IsAbs(subPath) {
	// 	return fmt.Errorf("absolute path required: %q", subPath)
	// }

	fullPath := d.fullPath(subPath)
	parentDir := path.Dir(fullPath)
	if err := os.MkdirAll(parentDir, 0755); err != nil {
		return 0, err
	}

	fp, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		// TODO(stevvooe): A few missing conditions in storage driver:
		//	1. What if the path is already a directory?
		//  2. Should number 1 be exposed explicitly in storagedriver?
		//	2. Can this path not exist, even if we create above?
		return 0, err
	}
	defer fp.Close()

	nn, err = fp.Seek(offset, os.SEEK_SET)
	if err != nil {
		return 0, err
	}

	if nn != offset {
		return 0, fmt.Errorf("bad seek to %v, expected %v in fp=%v", offset, nn, fp)
	}

	return io.Copy(fp, reader)
}

// Stat retrieves the FileInfo for the given path, including the current size
// in bytes and the creation time.
func (d *driver) Stat(ctx context.Context, subPath string) (storagedriver.FileInfo, error) {
	fullPath := d.fullPath(subPath)

	fi, err := os.Stat(fullPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, storagedriver.PathNotFoundError{Path: subPath}
		}

		return nil, err
	}

	return fileInfo{
		path:     subPath,
		FileInfo: fi,
	}, nil
}

// List returns a list of the objects that are direct descendants of the given
// path.
func (d *driver) List(ctx context.Context, subPath string) ([]string, error) {
	if subPath[len(subPath)-1] != '/' {
		subPath += "/"
	}
	fullPath := d.fullPath(subPath)

	dir, err := os.Open(fullPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, storagedriver.PathNotFoundError{Path: subPath}
		}
		return nil, err
	}

	defer dir.Close()

	fileNames, err := dir.Readdirnames(0)
	if err != nil {
		return nil, err
	}

	keys := make([]string, 0, len(fileNames))
	for _, fileName := range fileNames {
		keys = append(keys, path.Join(subPath, fileName))
	}

	return keys, nil
}

// Move moves an object stored at sourcePath to destPath, removing the original
// object.
func (d *driver) Move(ctx context.Context, sourcePath string, destPath string) error {
	source := d.fullPath(sourcePath)
	dest := d.fullPath(destPath)

	if _, err := os.Stat(source); os.IsNotExist(err) {
		return storagedriver.PathNotFoundError{Path: sourcePath}
	}

	if err := os.MkdirAll(path.Dir(dest), 0755); err != nil {
		return err
	}

	err := os.Rename(source, dest)
	return err
}

// Delete recursively deletes all objects stored at "path" and its subpaths.
func (d *driver) Delete(ctx context.Context, subPath string) error {
	fullPath := d.fullPath(subPath)

	_, err := os.Stat(fullPath)
	if err != nil && !os.IsNotExist(err) {
		return err
	} else if err != nil {
		return storagedriver.PathNotFoundError{Path: subPath}
	}

	err = os.RemoveAll(fullPath)
	return err
}

// URLFor returns a URL which may be used to retrieve the content stored at the given path.
// May return an UnsupportedMethodErr in certain StorageDriver implementations.
func (d *driver) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) {
	return "", storagedriver.ErrUnsupportedMethod
}

// fullPath returns the absolute path of a key within the Driver's storage.
func (d *driver) fullPath(subPath string) string {
	return path.Join(d.rootDirectory, subPath)
}

type fileInfo struct {
	os.FileInfo
	path string
}

var _ storagedriver.FileInfo = fileInfo{}

// Path provides the full path of the target of this file info.
func (fi fileInfo) Path() string {
	return fi.path
}

// Size returns current length in bytes of the file. The return value can
// be used to write to the end of the file at path. The value is
// meaningless if IsDir returns true.
func (fi fileInfo) Size() int64 {
	if fi.IsDir() {
		return 0
	}

	return fi.FileInfo.Size()
}

// ModTime returns the modification time for the file. For backends that
// don't have a modification time, the creation time should be returned.
func (fi fileInfo) ModTime() time.Time {
	return fi.FileInfo.ModTime()
}

// IsDir returns true if the path is a directory.
func (fi fileInfo) IsDir() bool {
	return fi.FileInfo.IsDir()
}
<commit_msg>Saner default data location<commit_after>package filesystem

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/docker/distribution/context"
	storagedriver "github.com/docker/distribution/registry/storage/driver"
	"github.com/docker/distribution/registry/storage/driver/base"
	"github.com/docker/distribution/registry/storage/driver/factory"
)

const driverName = "filesystem"
const defaultRootDirectory = "/var/lib/registry"

func init() {
	factory.Register(driverName, &filesystemDriverFactory{})
}

// filesystemDriverFactory implements the factory.StorageDriverFactory interface
type filesystemDriverFactory struct{}

func (factory *filesystemDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
	return FromParameters(parameters), nil
}

type driver struct {
	rootDirectory string
}

type baseEmbed struct {
	base.Base
}

// Driver is a storagedriver.StorageDriver implementation backed by a local
// filesystem. All provided paths will be subpaths of the RootDirectory.
type Driver struct {
	baseEmbed
}

// FromParameters constructs a new Driver with a given parameters map
// Optional Parameters:
// - rootdirectory
func FromParameters(parameters map[string]interface{}) *Driver {
	var rootDirectory = defaultRootDirectory
	if parameters != nil {
		rootDir, ok := parameters["rootdirectory"]
		if ok {
			rootDirectory = fmt.Sprint(rootDir)
		}
	}
	return New(rootDirectory)
}

// New constructs a new Driver with a given rootDirectory
func New(rootDirectory string) *Driver {
	return &Driver{
		baseEmbed: baseEmbed{
			Base: base.Base{
				StorageDriver: &driver{
					rootDirectory: rootDirectory,
				},
			},
		},
	}
}

// Implement the storagedriver.StorageDriver interface

func (d *driver) Name() string {
	return driverName
}

// GetContent retrieves the content stored at "path" as a []byte.
func (d *driver) GetContent(ctx context.Context, path string) ([]byte, error) {
	rc, err := d.ReadStream(ctx, path, 0)
	if err != nil {
		return nil, err
	}
	defer rc.Close()

	p, err := ioutil.ReadAll(rc)
	if err != nil {
		return nil, err
	}

	return p, nil
}

// PutContent stores the []byte content at a location designated by "path".
func (d *driver) PutContent(ctx context.Context, subPath string, contents []byte) error {
	if _, err := d.WriteStream(ctx, subPath, 0, bytes.NewReader(contents)); err != nil {
		return err
	}

	return os.Truncate(d.fullPath(subPath), int64(len(contents)))
}

// ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
// given byte offset.
func (d *driver) ReadStream(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
	file, err := os.OpenFile(d.fullPath(path), os.O_RDONLY, 0644)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, storagedriver.PathNotFoundError{Path: path}
		}

		return nil, err
	}

	seekPos, err := file.Seek(int64(offset), os.SEEK_SET)
	if err != nil {
		file.Close()
		return nil, err
	} else if seekPos < int64(offset) {
		file.Close()
		return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
	}

	return file, nil
}

// WriteStream stores the contents of the provided io.Reader at a location
// designated by the given path.
func (d *driver) WriteStream(ctx context.Context, subPath string, offset int64, reader io.Reader) (nn int64, err error) {
	// TODO(stevvooe): This needs to be a requirement.
	// if !path.IsAbs(subPath) {
	// 	return fmt.Errorf("absolute path required: %q", subPath)
	// }

	fullPath := d.fullPath(subPath)
	parentDir := path.Dir(fullPath)
	if err := os.MkdirAll(parentDir, 0755); err != nil {
		return 0, err
	}

	fp, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		// TODO(stevvooe): A few missing conditions in storage driver:
		//	1. What if the path is already a directory?
		//  2. Should number 1 be exposed explicitly in storagedriver?
		//	2. Can this path not exist, even if we create above?
		return 0, err
	}
	defer fp.Close()

	nn, err = fp.Seek(offset, os.SEEK_SET)
	if err != nil {
		return 0, err
	}

	if nn != offset {
		return 0, fmt.Errorf("bad seek to %v, expected %v in fp=%v", offset, nn, fp)
	}

	return io.Copy(fp, reader)
}

// Stat retrieves the FileInfo for the given path, including the current size
// in bytes and the creation time.
func (d *driver) Stat(ctx context.Context, subPath string) (storagedriver.FileInfo, error) {
	fullPath := d.fullPath(subPath)

	fi, err := os.Stat(fullPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, storagedriver.PathNotFoundError{Path: subPath}
		}

		return nil, err
	}

	return fileInfo{
		path:     subPath,
		FileInfo: fi,
	}, nil
}

// List returns a list of the objects that are direct descendants of the given
// path.
func (d *driver) List(ctx context.Context, subPath string) ([]string, error) {
	if subPath[len(subPath)-1] != '/' {
		subPath += "/"
	}
	fullPath := d.fullPath(subPath)

	dir, err := os.Open(fullPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, storagedriver.PathNotFoundError{Path: subPath}
		}
		return nil, err
	}

	defer dir.Close()

	fileNames, err := dir.Readdirnames(0)
	if err != nil {
		return nil, err
	}

	keys := make([]string, 0, len(fileNames))
	for _, fileName := range fileNames {
		keys = append(keys, path.Join(subPath, fileName))
	}

	return keys, nil
}

// Move moves an object stored at sourcePath to destPath, removing the original
// object.
func (d *driver) Move(ctx context.Context, sourcePath string, destPath string) error {
	source := d.fullPath(sourcePath)
	dest := d.fullPath(destPath)

	if _, err := os.Stat(source); os.IsNotExist(err) {
		return storagedriver.PathNotFoundError{Path: sourcePath}
	}

	if err := os.MkdirAll(path.Dir(dest), 0755); err != nil {
		return err
	}

	err := os.Rename(source, dest)
	return err
}

// Delete recursively deletes all objects stored at "path" and its subpaths.
func (d *driver) Delete(ctx context.Context, subPath string) error {
	fullPath := d.fullPath(subPath)

	_, err := os.Stat(fullPath)
	if err != nil && !os.IsNotExist(err) {
		return err
	} else if err != nil {
		return storagedriver.PathNotFoundError{Path: subPath}
	}

	err = os.RemoveAll(fullPath)
	return err
}

// URLFor returns a URL which may be used to retrieve the content stored at the given path.
// May return an UnsupportedMethodErr in certain StorageDriver implementations.
func (d *driver) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) {
	return "", storagedriver.ErrUnsupportedMethod
}

// fullPath returns the absolute path of a key within the Driver's storage.
func (d *driver) fullPath(subPath string) string {
	return path.Join(d.rootDirectory, subPath)
}

type fileInfo struct {
	os.FileInfo
	path string
}

var _ storagedriver.FileInfo = fileInfo{}

// Path provides the full path of the target of this file info.
func (fi fileInfo) Path() string {
	return fi.path
}

// Size returns current length in bytes of the file. The return value can
// be used to write to the end of the file at path. The value is
// meaningless if IsDir returns true.
func (fi fileInfo) Size() int64 {
	if fi.IsDir() {
		return 0
	}

	return fi.FileInfo.Size()
}

// ModTime returns the modification time for the file. For backends that
// don't have a modification time, the creation time should be returned.
func (fi fileInfo) ModTime() time.Time {
	return fi.FileInfo.ModTime()
}

// IsDir returns true if the path is a directory.
func (fi fileInfo) IsDir() bool {
	return fi.FileInfo.IsDir()
}
<|endoftext|>


<commit_before>package main // import "github.com/mvanholsteijn/paas-monitor"

import (
    "os"
    "flag"
    "fmt"
    "strings"
    "net/http"
    "encoding/json"
    "log"
)

var port string

func environmentHandler(w http.ResponseWriter, r *http.Request) {
    var variables map[string]string
    variables = make(map[string]string)

    for _, e := range os.Environ() {
        pair := strings.Split(e, "=")
	variables[pair[0]] = pair[1]
    }

    js, err := json.Marshal(variables)
    if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(js)
}

func requestHandler(w http.ResponseWriter, r *http.Request) {

    var variables map[string]string

    variables = make(map[string]string)
    variables["method"] = r.Method
    variables["url"] = r.URL.String()
    variables["proto"] = r.Proto

    js, err := json.Marshal(variables)
    if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(js)
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    fmt.Fprintf(w, "ok")
}

func stopHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    fmt.Fprintf(w, "stopped on request")
    os.Exit(1)
}

func headerHandler(w http.ResponseWriter, r *http.Request) {

    hdr := r.Header
    hdr["Host"] = []string { r.Host }
    hdr["Content-Type"] = []string { r.Header.Get("Content-Type") }
    hdr["User-Agent"] = []string { r.UserAgent() }

    js, err := json.Marshal(hdr)
    if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(js)
}
var (
    count = 0
)

func statusHandler(w http.ResponseWriter, r *http.Request) {
    var variables map[string]string

    hostName, _ := os.Hostname()
    release := os.Getenv("RELEASE")
    message := os.Getenv("MESSAGE")
    if message == "" {
       message = "Hello World"
    }

    count = count + 1

    variables = make(map[string]string)
    variables["key"] = fmt.Sprintf("%s:%s", hostName, port)
    variables["release"] = release
    variables["servercount"] = fmt.Sprintf("%d", count)
    variables["message"] = fmt.Sprintf("%s from release %s; server call count is %d", message, release, count)

    js, err := json.Marshal(variables)
    if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
    }
    w.Header().Set("Content-Type", "application/json")
    w.Header().Set("Connection", "close")
    w.Write(js)
}

func main() {
    var dir string

    dir = os.Getenv("APPDIR")
    if dir == "" {
	dir = "."
    }
    fs := http.FileServer(http.Dir( dir + "/public"))

    http.Handle("/", fs)
    http.HandleFunc("/environment", environmentHandler)
    http.HandleFunc("/status", statusHandler)
    http.HandleFunc("/header", headerHandler)
    http.HandleFunc("/health", healthHandler)
    http.HandleFunc("/request", requestHandler)
    http.HandleFunc("/stop", stopHandler)

    portEnvName := flag.String("port-env-name", "", "the environment variable name overriding the listen port")
    portSpecified := flag.String("port", "", "the port to listen, override the environment name")
    flag.Parse()

    if *portSpecified != "" && *portEnvName != "" {
	log.Fatalf("specify either -port or -port-env-name, but not both.\n")
    }

    if *portSpecified != "" {
       port = *portSpecified
    } else {
	if *portEnvName != "" && os.Getenv(*portEnvName) != "" {
	    port = os.Getenv(*portEnvName)
	} else {
	    if *portEnvName != "" {
		    log.Fatalf("environment variable '%s' is not set with a port override\n", *portEnvName)
	    }
	    port = os.Getenv("PORT")
            if port == "" {
		port = "1337"
            }
	}
    }

    log.Printf("listening on port %s\n", port)
    err := http.ListenAndServe(":" + port, nil)
    log.Printf("%s", err)
}
<commit_msg>report an error on exit of the server<commit_after>package main // import "github.com/mvanholsteijn/paas-monitor"

import (
    "os"
    "flag"
    "fmt"
    "strings"
    "net/http"
    "encoding/json"
    "log"
)

var port string

func environmentHandler(w http.ResponseWriter, r *http.Request) {
    var variables map[string]string
    variables = make(map[string]string)

    for _, e := range os.Environ() {
        pair := strings.Split(e, "=")
	variables[pair[0]] = pair[1]
    }

    js, err := json.Marshal(variables)
    if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(js)
}

func requestHandler(w http.ResponseWriter, r *http.Request) {

    var variables map[string]string

    variables = make(map[string]string)
    variables["method"] = r.Method
    variables["url"] = r.URL.String()
    variables["proto"] = r.Proto

    js, err := json.Marshal(variables)
    if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(js)
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    fmt.Fprintf(w, "ok")
}

func stopHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    fmt.Fprintf(w, "stopped on request")
    os.Exit(1)
}

func headerHandler(w http.ResponseWriter, r *http.Request) {

    hdr := r.Header
    hdr["Host"] = []string { r.Host }
    hdr["Content-Type"] = []string { r.Header.Get("Content-Type") }
    hdr["User-Agent"] = []string { r.UserAgent() }

    js, err := json.Marshal(hdr)
    if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(js)
}
var (
    count = 0
)

func statusHandler(w http.ResponseWriter, r *http.Request) {
    var variables map[string]string

    hostName, _ := os.Hostname()
    release := os.Getenv("RELEASE")
    message := os.Getenv("MESSAGE")
    if message == "" {
       message = "Hello World"
    }

    count = count + 1

    variables = make(map[string]string)
    variables["key"] = fmt.Sprintf("%s:%s", hostName, port)
    variables["release"] = release
    variables["servercount"] = fmt.Sprintf("%d", count)
    variables["message"] = fmt.Sprintf("%s from release %s; server call count is %d", message, release, count)

    js, err := json.Marshal(variables)
    if err != nil {
	http.Error(w, err.Error(), http.StatusInternalServerError)
	return
    }
    w.Header().Set("Content-Type", "application/json")
    w.Header().Set("Connection", "close")
    w.Write(js)
}

func main() {
    var dir string

    dir = os.Getenv("APPDIR")
    if dir == "" {
	dir = "."
    }
    fs := http.FileServer(http.Dir( dir + "/public"))

    http.Handle("/", fs)
    http.HandleFunc("/environment", environmentHandler)
    http.HandleFunc("/status", statusHandler)
    http.HandleFunc("/header", headerHandler)
    http.HandleFunc("/health", healthHandler)
    http.HandleFunc("/request", requestHandler)
    http.HandleFunc("/stop", stopHandler)

    portEnvName := flag.String("port-env-name", "", "the environment variable name overriding the listen port")
    portSpecified := flag.String("port", "", "the port to listen, override the environment name")
    flag.Parse()

    if *portSpecified != "" && *portEnvName != "" {
	log.Fatalf("specify either -port or -port-env-name, but not both.\n")
    }

    if *portSpecified != "" {
       port = *portSpecified
    } else {
	if *portEnvName != "" && os.Getenv(*portEnvName) != "" {
	    port = os.Getenv(*portEnvName)
	} else {
	    if *portEnvName != "" {
		    log.Fatalf("environment variable '%s' is not set with a port override\n", *portEnvName)
	    }
	    port = os.Getenv("PORT")
            if port == "" {
		port = "1337"
            }
	}
    }

    log.Printf("listening on port %s\n", port)
    err := http.ListenAndServe(":" + port, nil)
    log.Fatal("%s", err)
}
<|endoftext|>


<commit_before>package prompt

import (
	"github.com/Ak-Army/git-prompt/color"
	"strings"
	"sync"
)

type GitStatus interface {
	Prompt(color color.Color) string
}

func GetCurrentStatus() *gitStatus {
	gS := &gitStatus{}
	gS.waitGroup = &sync.WaitGroup{}
	err := gS.getStatus()
	if err != nil {
		return gS
	}
	gS.waitGroup.Add(2)
	go gS.getStashCount()
	go gS.getTag()
	gS.waitGroup.Wait()
	return gS
}

// Status ...
type gitStatus struct {
	GitStatus
	waitGroup *sync.WaitGroup
	branch    string
	tag       string
	detached  bool
	hasremote bool
	ahead     int
	behind    int
	staged    int
	conflicts int
	changed   int
	untracked int
	stashs    int
}

// Format ...
func (gS *gitStatus) Prompt(color color.Color) string {
	if gS.branch == "" {
		return ""
	}
	ret := make([]string, 2)
	ret[0] = color.Yellow("(")

	// branch
	if gS.detached {
		ret[0] += color.Red("%s", gS.branch)
	} else {
		ret[0] += color.Cyan("%s", gS.branch)
	}

	if gS.tag != "" {
		ret[0] += color.Cyan(" %s", gS.tag)
	}

	if gS.hasremote {
		if gS.ahead > 0 {
			ret[1] += color.Green("↑%d", gS.ahead)
		}
		if gS.behind > 0 {
			ret[1] += color.Red("↓%d", gS.behind)
		}
	}

	if gS.staged > 0 {
		ret[1] += color.Blue("+%d", gS.staged)
	}

	if gS.changed > 0 {
		ret[1] += color.Blue("△%d", gS.changed)
	}

	if gS.untracked > 0 {
		ret[1] += color.Blue("?%d", gS.untracked)
	}

	if gS.conflicts > 0 {
		ret[1] += color.Red("!%d", gS.conflicts)
	}

	if gS.stashs > 0 {
		ret[1] += color.Green("|s%d", gS.stashs)
	}

	r := strings.TrimSpace(strings.Join(ret, " "))
	r += color.Yellow(")")

	return r
}

func (gS *gitStatus) getStatus() error {
	lines, err := getLines("git", "status", "--porcelain", "--branch")
	if err != nil {
		return err
	}

	gS.waitGroup.Add(2)
	go gS.parseBranchLine(lines[0])
	go gS.collectChanges(lines[1:])
	return nil
}

func (gS *gitStatus) getStashCount() {
	defer gS.waitGroup.Done()

	stdout, stderr, err := communicate("git", "log", "--format=\"%%gd: %%gs\"", "-g", "--first-parent", "-m", "refs/stash", "--")
	if err != nil {
		return
	} else if strings.Contains(stderr, "fatal") {
		return
	}

	gS.stashs = len(strings.Split(stdout, "\n")) - 1
}

func (gS *gitStatus) parseBranchLine(line string) {
	defer gS.waitGroup.Done()

	if strings.Contains(line, "no branch") {
		gS.detached = true
		gS.branch = "no branch"
		hash, _, _ := communicate("git", "rev-parse", "--short", "HEAD")
		gS.branch += ":" + strings.TrimSpace(hash[0:len(hash)-1])
	} else if strings.Contains(line, "...") {
		gS.hasremote = true

		splitted := strings.Split(line, " ")
		gS.branch = strings.Split(splitted[1], "...")[0]

		if len(splitted) >= 3 {
			joined := strings.Join(splitted[2:], " ")

			gS.ahead = parsePattern(`ahead (\d+)`, joined)
			gS.behind = parsePattern(`behind (\d+)`, joined)
		}
	} else {
		gS.branch = strings.TrimSpace(strings.Split(line, " ")[1])
	}
}

func (gS *gitStatus) collectChanges(lines []string) {
	defer gS.waitGroup.Done()

	for _, line := range lines {
		if len(line) == 0 {
			continue
		}

		idxStatus := line[0]
		wtStatus := line[1]

		// " M  hoge.txt" , "AM ahoo.png" , ...
		if wtStatus != ' ' && wtStatus != '?' {
			gS.changed++
		}

		// "MT hoge.cpp" , "A  fuga.txt" , ...
		if idxStatus != ' ' && idxStatus != '?' {
			gS.staged++
		}

		// "?? hoge.txt", ...
		if idxStatus == '?' && wtStatus == '?' {
			gS.untracked++
		}

		// "UU hogehoge.txt" ...
		if idxStatus == 'U' && wtStatus == 'U' {
			gS.conflicts++
		}
	}
}

func (gS *gitStatus) getTag() {
	defer gS.waitGroup.Done()
	tag, _, _ := communicate("git", "describe", "--tags", "--exact")
	if tag != "" {
		gS.tag = strings.TrimSpace(tag[0 : len(tag)-1])
	}
}
<commit_msg>fetch if its an old repo<commit_after>package prompt

import (
	"github.com/Ak-Army/git-prompt/color"
	"strings"
	"sync"
	"time"
	"strconv"
)

type GitStatus interface {
	Prompt(color color.Color) string
}

func GetCurrentStatus() *gitStatus {
	gS := &gitStatus{}
	gS.waitGroup = &sync.WaitGroup{}
	err := gS.getStatus()
	if err != nil {
		return gS
	}
	gS.waitGroup.Add(2)
	go gS.getStashCount()
	go gS.getTag()
	gS.waitGroup.Wait()
	return gS
}

// Status ...
type gitStatus struct {
	GitStatus
	waitGroup *sync.WaitGroup
	branch    string
	tag       string
	detached  bool
	hasremote bool
	ahead     int
	behind    int
	staged    int
	conflicts int
	changed   int
	untracked int
	stashs    int
}

// Format ...
func (gS *gitStatus) Prompt(color color.Color) string {
	if gS.branch == "" {
		return ""
	}
	ret := make([]string, 2)
	ret[0] = color.Yellow("(")

	// branch
	if gS.detached {
		ret[0] += color.Red("%s", gS.branch)
	} else {
		ret[0] += color.Cyan("%s", gS.branch)
	}

	if gS.tag != "" {
		ret[0] += color.Cyan(" %s", gS.tag)
	}

	if gS.hasremote {
		if gS.ahead > 0 {
			ret[1] += color.Green("↑%d", gS.ahead)
		}
		if gS.behind > 0 {
			ret[1] += color.Red("↓%d", gS.behind)
		}
	}

	if gS.staged > 0 {
		ret[1] += color.Blue("+%d", gS.staged)
	}

	if gS.changed > 0 {
		ret[1] += color.Blue("△%d", gS.changed)
	}

	if gS.untracked > 0 {
		ret[1] += color.Blue("?%d", gS.untracked)
	}

	if gS.conflicts > 0 {
		ret[1] += color.Red("!%d", gS.conflicts)
	}

	if gS.stashs > 0 {
		ret[1] += color.Green("|s%d", gS.stashs)
	}

	r := strings.TrimSpace(strings.Join(ret, " "))
	r += color.Yellow(")")

	return r
}

func (gS *gitStatus) getStatus() error {
	gitDir, _, err := communicate("git", "rev-parse", "--show-toplevel")
	if err != nil {
		return err
	}
	lastFetch, _, err := communicate("stat", "-c", `%Y`, strings.TrimSpace(gitDir)+`/.git/FETCH_HEAD`)
	if err == nil {
		i, err := strconv.ParseInt(strings.TrimSpace(lastFetch), 10, 64)
		if err == nil && time.Since(time.Unix(i, 0)).Hours() > 1 {
			communicate("git", "fetch")
		}
	}
	lines, err := getLines("git", "status", "--porcelain", "--branch")
	if err != nil {
		return err
	}

	gS.waitGroup.Add(2)
	go gS.parseBranchLine(lines[0])
	go gS.collectChanges(lines[1:])
	return nil
}

func (gS *gitStatus) getStashCount() {
	defer gS.waitGroup.Done()

	stdout, stderr, err := communicate("git", "log", "--format=\"%%gd: %%gs\"", "-g", "--first-parent", "-m", "refs/stash", "--")
	if err != nil {
		return
	} else if strings.Contains(stderr, "fatal") {
		return
	}

	gS.stashs = len(strings.Split(stdout, "\n")) - 1
}

func (gS *gitStatus) parseBranchLine(line string) {
	defer gS.waitGroup.Done()

	if strings.Contains(line, "no branch") {
		gS.detached = true
		gS.branch = "no branch"
		hash, _, _ := communicate("git", "rev-parse", "--short", "HEAD")
		gS.branch += ":" + strings.TrimSpace(hash[0:len(hash)-1])
	} else if strings.Contains(line, "...") {
		gS.hasremote = true

		splitted := strings.Split(line, " ")
		gS.branch = strings.Split(splitted[1], "...")[0]

		if len(splitted) >= 3 {
			joined := strings.Join(splitted[2:], " ")

			gS.ahead = parsePattern(`ahead (\d+)`, joined)
			gS.behind = parsePattern(`behind (\d+)`, joined)
		}
	} else {
		gS.branch = strings.TrimSpace(strings.Split(line, " ")[1])
	}
}

func (gS *gitStatus) collectChanges(lines []string) {
	defer gS.waitGroup.Done()

	for _, line := range lines {
		if len(line) == 0 {
			continue
		}

		idxStatus := line[0]
		wtStatus := line[1]

		// " M  hoge.txt" , "AM ahoo.png" , ...
		if wtStatus != ' ' && wtStatus != '?' {
			gS.changed++
		}

		// "MT hoge.cpp" , "A  fuga.txt" , ...
		if idxStatus != ' ' && idxStatus != '?' {
			gS.staged++
		}

		// "?? hoge.txt", ...
		if idxStatus == '?' && wtStatus == '?' {
			gS.untracked++
		}

		// "UU hogehoge.txt" ...
		if idxStatus == 'U' && wtStatus == 'U' {
			gS.conflicts++
		}
	}
}

func (gS *gitStatus) getTag() {
	defer gS.waitGroup.Done()
	tag, _, _ := communicate("git", "describe", "--tags", "--exact")
	if tag != "" {
		gS.tag = strings.TrimSpace(tag[0 : len(tag)-1])
	}
}
<|endoftext|>


<commit_before>package prompt

import (
	"github.com/AlecAivazis/survey/v2"
	"log"
)

type Prompt interface {
	Confirm(string) bool
}

func NewPrompt() Prompt {
	return prompt{}
}

type prompt struct{}

func NewPromptContext(interactive bool, prompt Prompt) PromptContext{
	return PromptContext{
		Interactive: interactive,
		Prompt: prompt,
	}
}

type PromptContext struct {
	Interactive bool
	Prompt Prompt
}

func (p prompt) Confirm(message string) bool {
	if message == "" {
		message = "Confirm?"
	}

	confirmPrompt := &survey.Confirm{
		Default: false,
		Message: message,
	}

	confirmation := false
	err := survey.AskOne(confirmPrompt, &confirmation)
	if err != nil {
		log.Printf("error occured when getting input from user: %s", err)
		return false
	}

	return confirmation
}
<commit_msg>[Ninan] Add go:generate comment to generate mock of prompt<commit_after>package prompt

//go:generate mockgen -destination ../internal/mock/prompt/survey.go -package mock -source survey.go

import (
	"github.com/AlecAivazis/survey/v2"
	"log"
)

type Prompt interface {
	Confirm(string) bool
}

func NewPrompt() Prompt {
	return prompt{}
}

type prompt struct{}

func NewPromptContext(interactive bool, prompt Prompt) PromptContext{
	return PromptContext{
		Interactive: interactive,
		Prompt: prompt,
	}
}

type PromptContext struct {
	Interactive bool
	Prompt Prompt
}

func (p prompt) Confirm(message string) bool {
	if message == "" {
		message = "Confirm?"
	}

	confirmPrompt := &survey.Confirm{
		Default: false,
		Message: message,
	}

	confirmation := false
	err := survey.AskOne(confirmPrompt, &confirmation)
	if err != nil {
		log.Printf("error occured when getting input from user: %s", err)
		return false
	}

	return confirmation
}
<|endoftext|>


<commit_before>// Package ico describes Windows ICO file format.
package ico

// http://msdn.microsoft.com/en-us/library/ms997538.aspx

type ICONDIR struct {
	Reserved uint16 // must be 0
	Type     uint16 // Resource Type (1 for icons)
	Count    uint16 // How many images?
}

type ICONDIRENTRY struct {
	Width       byte   // Width, in pixels, of the image
	Height      byte   // Height, in pixels, of the image
	ColorCount  byte   // Number of colors in image (0 if >=8bpp)
	Reserved    byte   // Reserved (must be 0)
	Planes      uint16 // Color Planes
	BitCount    uint16 // Bits per pixel
	BytesInRes  uint32 // How many bytes in this resource?
	ImageOffset uint32 // Where in the file is this image? [from beginning of file]
}

type BITMAPINFOHEADER struct {
	Size          uint32
	Width         int32
	Height        int32 // NOTE: "represents the combined height of the XOR and AND masks. Remember to divide this number by two before using it to perform calculations for either of the XOR or AND masks."
	Planes        uint16
	BitCount      uint16
	Compression   uint32 // for ico = 0
	SizeImage     uint32
	XPelsPerMeter int32  // for ico = 0
	YPelsPerMeter int32  // for ico = 0
	ClrUsed       uint32 // for ico = 0
	ClrImportant  uint32 // for ico = 0
}

type RGBQUAD struct {
	Blue     byte
	Green    byte
	Red      byte
	Reserved byte // must be 0
}

func skip(r io.Reader, n int64) error {
	_, err := io.CopyN(ioutil.Discard, r, n)
	return err
}

type icoOffset struct {
	n      int
	offset uint32
}

type icoOffsets []icoOffset

func (o icoOffsets) Len() int           { return len(o) }
func (o icoOffsets) Less(i, j int) bool { return o[i].offset < o[j].offset }
func (o icoOffsets) Swap(i, j int) {
	tmp := o[i]
	o[i] = o[j]
	o[j] = tmp
}

type rawico struct {
	icoinfo ICONDIRENTRY
	bmpinfo BITMAPINFOHEADER
	idx     int
	data    []byte
}

// NOTE: won't succeed on files with overlapping offsets
func DecodeAll(r io.Reader) ([]*ICO, error) {
	var hdr ICONDIR
	err := binary.Read(r, binary.LittleEndian, &hdr)
	if err != nil {
		return nil, err
	}
	if hdr.Reserved != 0 || hdr.Type != 1 {
		return nil, fmt.Errorf("bad magic number")
	}

	entries := make([]ICONDIRENTRY, hdr.Count)
	offsets := make([]icoOffset, hdr.Count)
	for i := 0; i < len(entries); i++ {
		err = binary.Read(r, binary.LittleEndian, &entries[i])
		if err != nil {
			return nil, err
		}
		offsets[i] = icoOffset{i, entries[i].ImageOffset}
	}

	sort.Sort(icoOffsets(offsets))

	datas := make([][]byte, hdr.Count)
	offset := binary.Size(&hdr) + len(entries)*binary.Size(ICONDIRENTRY{})
	for i := 0; i < len(offsets); i++ {
		err = skip(r, offsets[i].offset-offset)
		if err != nil {
			return nil, err
		}
		offset = offsets[i].offset

		datas[i] = make([]byte, entries[offsets[i].n].BytesInRes)
		_, err = io.ReadFull(r, datas[i])
		if err != nil {
			return nil, err
		}
	}
}
<commit_msg>ico: more work on reading sections and headers<commit_after>// Package ico describes Windows ICO file format.
package ico

// http://msdn.microsoft.com/en-us/library/ms997538.aspx

import (
	"encoding/binary"
	"fmt"
	"io"
	"io/ioutil"
	"sort"
)

type ICONDIR struct {
	Reserved uint16 // must be 0
	Type     uint16 // Resource Type (1 for icons)
	Count    uint16 // How many images?
}

type ICONDIRENTRY struct {
	Width       byte   // Width, in pixels, of the image
	Height      byte   // Height, in pixels, of the image
	ColorCount  byte   // Number of colors in image (0 if >=8bpp)
	Reserved    byte   // Reserved (must be 0)
	Planes      uint16 // Color Planes
	BitCount    uint16 // Bits per pixel
	BytesInRes  uint32 // How many bytes in this resource?
	ImageOffset uint32 // Where in the file is this image? [from beginning of file]
}

type BITMAPINFOHEADER struct {
	Size          uint32
	Width         int32
	Height        int32 // NOTE: "represents the combined height of the XOR and AND masks. Remember to divide this number by two before using it to perform calculations for either of the XOR or AND masks."
	Planes        uint16
	BitCount      uint16
	Compression   uint32 // for ico = 0
	SizeImage     uint32
	XPelsPerMeter int32  // for ico = 0
	YPelsPerMeter int32  // for ico = 0
	ClrUsed       uint32 // for ico = 0
	ClrImportant  uint32 // for ico = 0
}

type RGBQUAD struct {
	Blue     byte
	Green    byte
	Red      byte
	Reserved byte // must be 0
}

func skip(r io.Reader, n int64) error {
	_, err := io.CopyN(ioutil.Discard, r, n)
	return err
}

type icoOffset struct {
	n      int
	offset uint32
}

type rawico struct {
	icoinfo ICONDIRENTRY
	bmpinfo *BITMAPINFOHEADER
	idx     int
	data    []byte
}

type byOffsets []rawico

func (o byOffsets) Len() int           { return len(o) }
func (o byOffsets) Less(i, j int) bool { return o[i].icoinfo.ImageOffset < o[j].icoinfo.ImageOffset }
func (o byOffsets) Swap(i, j int) {
	tmp := o[i]
	o[i] = o[j]
	o[j] = tmp
}

type ICO struct{}

// NOTE: won't succeed on files with overlapping offsets
func DecodeAll(r io.Reader) ([]*ICO, error) {
	var hdr ICONDIR
	err := binary.Read(r, binary.LittleEndian, &hdr)
	if err != nil {
		return nil, err
	}
	if hdr.Reserved != 0 || hdr.Type != 1 {
		return nil, fmt.Errorf("bad magic number")
	}

	raws := make([]rawico, hdr.Count)
	for i := 0; i < len(raws); i++ {
		err = binary.Read(r, binary.LittleEndian, &raws[i].icoinfo)
		if err != nil {
			return nil, err
		}
		raws[i].idx = i
	}

	sort.Sort(byOffsets(raws))

	offset := uint32(binary.Size(&hdr) + len(raws)*binary.Size(ICONDIRENTRY{}))
	for i := 0; i < len(raws); i++ {
		err = skip(r, int64(raws[i].icoinfo.ImageOffset-offset))
		if err != nil {
			return nil, err
		}
		offset = raws[i].icoinfo.ImageOffset

		raws[i].bmpinfo = &BITMAPINFOHEADER{}
		err = binary.Read(r, binary.LittleEndian, raws[i].bmpinfo)
		if err != nil {
			return nil, err
		}

		raws[i].data = make([]byte, raws[i].icoinfo.BytesInRes-uint32(binary.Size(BITMAPINFOHEADER{})))
		_, err = io.ReadFull(r, raws[i].data)
		if err != nil {
			return nil, err
		}
	}

	return nil, nil
}
<|endoftext|>


<commit_before>package protect

import (
	"log"
	"net/http"
	"time"

	"math"
)

// Queue - struct to hold a number of sessions
type Queue struct {
	limit   chan struct{}
	queued  chan struct{}
	pending chan struct{}
	used    chan struct{}
}

// Protect - handler to control limit of sessions
func (q *Queue) Protect(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Println("[NEW_REQUEST]")
		cn := w.(http.CloseNotifier)
		s := time.Now()
		go func() {
			q.queued <- struct{}{}
		}()
		select {
		case <-cn.CloseNotify():
			<-q.queued
			log.Printf("connection from %s closed by client after %s waiting in queue\n", r.RemoteAddr, time.Since(s))
			return
		case q.limit <- struct{}{}:
			q.pending <- struct{}{}
		}
		<-q.queued
		log.Println("[NEW_REQUEST_ACCEPTED]")
		next.ServeHTTP(w, r)
	}
}

// Used - get created sessions
func (q *Queue) Used() int {
	return len(q.used)
}

// Pending - get pending sessions
func (q *Queue) Pending() int {
	return len(q.pending)
}

// Queued - get queued sessions
func (q *Queue) Queued() int {
	return len(q.queued)
}

// Drop - session is not created
func (q *Queue) Drop() {
	<-q.limit
	<-q.pending
}

// Create - session is created
func (q *Queue) Create() {
	q.used <- <-q.pending
}

// Release - session is closed
func (q *Queue) Release() {
	<-q.limit
	<-q.used
}

// New - create and initialize queue
func New(size int) *Queue {
	return &Queue{
		make(chan struct{}, size),
		make(chan struct{}, math.MaxUint32),
		make(chan struct{}, math.MaxUint32),
		make(chan struct{}, math.MaxUint32),
	}
}
<commit_msg>Fixed build for 386 arch<commit_after>package protect

import (
	"log"
	"net/http"
	"time"

	"math"
)

// Queue - struct to hold a number of sessions
type Queue struct {
	limit   chan struct{}
	queued  chan struct{}
	pending chan struct{}
	used    chan struct{}
}

// Protect - handler to control limit of sessions
func (q *Queue) Protect(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Println("[NEW_REQUEST]")
		cn := w.(http.CloseNotifier)
		s := time.Now()
		go func() {
			q.queued <- struct{}{}
		}()
		select {
		case <-cn.CloseNotify():
			<-q.queued
			log.Printf("connection from %s closed by client after %s waiting in queue\n", r.RemoteAddr, time.Since(s))
			return
		case q.limit <- struct{}{}:
			q.pending <- struct{}{}
		}
		<-q.queued
		log.Println("[NEW_REQUEST_ACCEPTED]")
		next.ServeHTTP(w, r)
	}
}

// Used - get created sessions
func (q *Queue) Used() int {
	return len(q.used)
}

// Pending - get pending sessions
func (q *Queue) Pending() int {
	return len(q.pending)
}

// Queued - get queued sessions
func (q *Queue) Queued() int {
	return len(q.queued)
}

// Drop - session is not created
func (q *Queue) Drop() {
	<-q.limit
	<-q.pending
}

// Create - session is created
func (q *Queue) Create() {
	q.used <- <-q.pending
}

// Release - session is closed
func (q *Queue) Release() {
	<-q.limit
	<-q.used
}

// New - create and initialize queue
func New(size int) *Queue {
	return &Queue{
		make(chan struct{}, size),
		make(chan struct{}, math.MaxInt32),
		make(chan struct{}, math.MaxInt32),
		make(chan struct{}, math.MaxInt32),
	}
}
<|endoftext|>


<commit_before>package protocol

import (
	"crypto/ecdsa"
	"errors"
	"math/rand"

	"github.com/status-im/status-go/eth-node/crypto"
	"github.com/status-im/status-go/eth-node/types"
	"github.com/status-im/status-go/protocol/common"
	"github.com/status-im/status-go/protocol/communities"
	"github.com/status-im/status-go/protocol/protobuf"
	v1protocol "github.com/status-im/status-go/protocol/v1"
)

var chatColors = []string{
	"#fa6565", // red
	"#887af9", // blue
	"#FE8F59", // orange
	"#7cda00", // green
	"#51d0f0", // light-blue
	"#d37ef4", // purple
}

type ChatType int

const (
	ChatTypeOneToOne ChatType = iota + 1
	ChatTypePublic
	ChatTypePrivateGroupChat
	ChatTypeProfile
	ChatTypeTimeline
	ChatTypeCommunityChat
)

const pkStringLength = 68

type Chat struct {
	// ID is the id of the chat, for public chats it is the name e.g. status, for one-to-one
	// is the hex encoded public key and for group chats is a random uuid appended with
	// the hex encoded pk of the creator of the chat
	ID    string `json:"id"`
	Name  string `json:"name"`
	Color string `json:"color"`
	// Active indicates whether the chat has been soft deleted
	Active bool `json:"active"`

	ChatType ChatType `json:"chatType"`

	// Timestamp indicates the last time this chat has received/sent a message
	Timestamp int64 `json:"timestamp"`
	// LastClockValue indicates the last clock value to be used when sending messages
	LastClockValue uint64 `json:"lastClockValue"`
	// DeletedAtClockValue indicates the clock value at time of deletion, messages
	// with lower clock value of this should be discarded
	DeletedAtClockValue uint64 `json:"deletedAtClockValue"`

	// Denormalized fields
	UnviewedMessagesCount uint            `json:"unviewedMessagesCount"`
	LastMessage           *common.Message `json:"lastMessage"`

	// Group chat fields
	// Members are the members who have been invited to the group chat
	Members []ChatMember `json:"members"`
	// MembershipUpdates is all the membership events in the chat
	MembershipUpdates []v1protocol.MembershipUpdateEvent `json:"membershipUpdateEvents"`

	// Generated username name of the chat for one-to-ones
	Alias string `json:"alias,omitempty"`
	// Identicon generated from public key
	Identicon string `json:"identicon"`

	// Muted is used to check whether we want to receive
	// push notifications for this chat
	Muted bool `json:"muted,omitempty"`

	// Public key of administrator who created invitation link
	InvitationAdmin string `json:"invitationAdmin,omitempty"`

	// Public key of user profile
	Profile string `json:"profile,omitempty"`

	// CommunityID is the id of the community it belongs to
	CommunityID string `json:"communityId,omitempty"`
}

func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) {
	// For one to one chatID is an encoded public key
	if c.ChatType != ChatTypeOneToOne {
		return nil, nil
	}
	return common.HexToPubkey(c.ID)
}

func (c *Chat) Public() bool {
	return c.ChatType == ChatTypePublic
}

func (c *Chat) ProfileUpdates() bool {
	return c.ChatType == ChatTypeProfile
}

func (c *Chat) Timeline() bool {
	return c.ChatType == ChatTypeTimeline
}

func (c *Chat) OneToOne() bool {
	return c.ChatType == ChatTypeOneToOne
}

func (c *Chat) CommunityChatID() string {
	if c.ChatType != ChatTypeCommunityChat {
		return c.ID
	}

	// Strips out the local prefix of the community-id
	return c.ID[pkStringLength:]
}

func (c *Chat) Validate() error {
	if c.ID == "" {
		return errors.New("chatID can't be blank")
	}

	if c.OneToOne() {
		_, err := c.PublicKey()
		return err
	}
	return nil
}

func (c *Chat) MembersAsPublicKeys() ([]*ecdsa.PublicKey, error) {
	publicKeys := make([]string, len(c.Members))
	for idx, item := range c.Members {
		publicKeys[idx] = item.ID
	}
	return stringSliceToPublicKeys(publicKeys)
}

func (c *Chat) JoinedMembersAsPublicKeys() ([]*ecdsa.PublicKey, error) {
	var publicKeys []string
	for _, member := range c.Members {
		if member.Joined {
			publicKeys = append(publicKeys, member.ID)
		}
	}
	return stringSliceToPublicKeys(publicKeys)
}

func (c *Chat) HasMember(memberID string) bool {
	for _, member := range c.Members {
		if memberID == member.ID {
			return true
		}
	}

	return false
}

func (c *Chat) updateChatFromGroupMembershipChanges(myID string, g *v1protocol.Group) {

	// Check if we were already in the chat
	hadJoined := c.HasMember(myID)

	// ID
	c.ID = g.ChatID()

	// Name
	c.Name = g.Name()

	// Members
	members := g.Members()
	admins := g.Admins()
	joined := g.Joined()
	chatMembers := make([]ChatMember, 0, len(members))
	for _, m := range members {
		// Check if we joined thanks to these changes, if so, make chat active
		if m == myID && !hadJoined {
			c.Active = true
		}

		chatMember := ChatMember{
			ID: m,
		}
		chatMember.Admin = stringSliceContains(admins, m)
		chatMember.Joined = stringSliceContains(joined, m)
		chatMembers = append(chatMembers, chatMember)
	}
	c.Members = chatMembers

	// MembershipUpdates
	c.MembershipUpdates = g.Events()
}

// NextClockAndTimestamp returns the next clock value
// and the current timestamp
func (c *Chat) NextClockAndTimestamp(timesource common.TimeSource) (uint64, uint64) {
	clock := c.LastClockValue
	timestamp := timesource.GetCurrentTime()
	if clock == 0 || clock < timestamp {
		clock = timestamp
	} else {
		clock = clock + 1
	}
	return clock, timestamp
}

func (c *Chat) UpdateFromMessage(message *common.Message, timesource common.TimeSource) error {
	c.Timestamp = int64(timesource.GetCurrentTime())

	// If the clock of the last message is lower, we set the message
	if c.LastMessage == nil || c.LastMessage.Clock <= message.Clock {
		c.LastMessage = message
	}
	// If the clock is higher we set the clock
	if c.LastClockValue < message.Clock {
		c.LastClockValue = message.Clock
	}
	return nil
}

// ChatMembershipUpdate represent an event on membership of the chat
type ChatMembershipUpdate struct {
	// Unique identifier for the event
	ID string `json:"id"`
	// Type indicates the kind of event
	Type protobuf.MembershipUpdateEvent_EventType `json:"type"`
	// Name represents the name in the event of changing name events
	Name string `json:"name,omitempty"`
	// Clock value of the event
	ClockValue uint64 `json:"clockValue"`
	// Signature of the event
	Signature string `json:"signature"`
	// Hex encoded public key of the creator of the event
	From string `json:"from"`
	// Target of the event for single-target events
	Member string `json:"member,omitempty"`
	// Target of the event for multi-target events
	Members []string `json:"members,omitempty"`
}

// ChatMember represents a member who participates in a group chat
type ChatMember struct {
	// ID is the hex encoded public key of the member
	ID string `json:"id"`
	// Admin indicates if the member is an admin of the group chat
	Admin bool `json:"admin"`
	// Joined indicates if the member has joined the group chat
	Joined bool `json:"joined"`
}

func (c ChatMember) PublicKey() (*ecdsa.PublicKey, error) {
	return common.HexToPubkey(c.ID)
}

func oneToOneChatID(publicKey *ecdsa.PublicKey) string {
	return types.EncodeHex(crypto.FromECDSAPub(publicKey))
}

func OneToOneFromPublicKey(pk *ecdsa.PublicKey, timesource common.TimeSource) *Chat {
	chatID := types.EncodeHex(crypto.FromECDSAPub(pk))
	newChat := CreateOneToOneChat(chatID[:8], pk, timesource)

	return newChat
}

func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey, timesource common.TimeSource) *Chat {
	return &Chat{
		ID:        oneToOneChatID(publicKey),
		Name:      name,
		Timestamp: int64(timesource.GetCurrentTime()),
		Active:    true,
		ChatType:  ChatTypeOneToOne,
	}
}

func CreateCommunityChat(orgID, chatID string, orgChat *protobuf.CommunityChat, timesource common.TimeSource) *Chat {
	color := orgChat.Identity.Color
	if color == "" {
		color = chatColors[rand.Intn(len(chatColors))] // nolint: gosec
	}

	return &Chat{
		CommunityID: orgID,
		Name:        orgChat.Identity.DisplayName,
		Active:      true,
		Color:       color,
		ID:          orgID + chatID,
		Timestamp:   int64(timesource.GetCurrentTime()),
		ChatType:    ChatTypeCommunityChat,
	}
}

func CreateCommunityChats(org *communities.Community, timesource common.TimeSource) []*Chat {
	var chats []*Chat
	orgID := org.IDString()

	for chatID, chat := range org.Chats() {
		chats = append(chats, CreateCommunityChat(orgID, chatID, chat, timesource))
	}
	return chats
}

func CreatePublicChat(name string, timesource common.TimeSource) *Chat {
	return &Chat{
		ID:        name,
		Name:      name,
		Active:    true,
		Timestamp: int64(timesource.GetCurrentTime()),
		Color:     chatColors[rand.Intn(len(chatColors))], // nolint: gosec
		ChatType:  ChatTypePublic,
	}
}

func buildProfileChatID(publicKeyString string) string {
	return "@" + publicKeyString
}

func CreateProfileChat(id string, profile string, timesource common.TimeSource) *Chat {
	return &Chat{
		ID:        id,
		Name:      id,
		Active:    true,
		Timestamp: int64(timesource.GetCurrentTime()),
		Color:     chatColors[rand.Intn(len(chatColors))], // nolint: gosec
		ChatType:  ChatTypeProfile,
		Profile:   profile,
	}
}

func CreateGroupChat(timesource common.TimeSource) Chat {
	return Chat{
		Active:    true,
		Color:     chatColors[rand.Intn(len(chatColors))], // nolint: gosec
		Timestamp: int64(timesource.GetCurrentTime()),
		ChatType:  ChatTypePrivateGroupChat,
	}
}

func stringSliceToPublicKeys(slice []string) ([]*ecdsa.PublicKey, error) {
	result := make([]*ecdsa.PublicKey, len(slice))
	for idx, item := range slice {
		var err error
		result[idx], err = common.HexToPubkey(item)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

func stringSliceContains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}
<commit_msg>fix bug from status-react<commit_after>package protocol

import (
	"crypto/ecdsa"
	"errors"
	"math/rand"

	"github.com/status-im/status-go/eth-node/crypto"
	"github.com/status-im/status-go/eth-node/types"
	"github.com/status-im/status-go/protocol/common"
	"github.com/status-im/status-go/protocol/communities"
	"github.com/status-im/status-go/protocol/protobuf"
	v1protocol "github.com/status-im/status-go/protocol/v1"
)

var chatColors = []string{
	"#fa6565", // red
	"#887af9", // blue
	"#FE8F59", // orange
	"#7cda00", // green
	"#51d0f0", // light-blue
	"#d37ef4", // purple
}

type ChatType int

const (
	ChatTypeOneToOne ChatType = iota + 1
	ChatTypePublic
	ChatTypePrivateGroupChat
	ChatTypeProfile
	ChatTypeTimeline
	ChatTypeCommunityChat
)

const pkStringLength = 68

type Chat struct {
	// ID is the id of the chat, for public chats it is the name e.g. status, for one-to-one
	// is the hex encoded public key and for group chats is a random uuid appended with
	// the hex encoded pk of the creator of the chat
	ID    string `json:"id"`
	Name  string `json:"name"`
	Color string `json:"color"`
	// Active indicates whether the chat has been soft deleted
	Active bool `json:"active"`

	ChatType ChatType `json:"chatType"`

	// Timestamp indicates the last time this chat has received/sent a message
	Timestamp int64 `json:"timestamp"`
	// LastClockValue indicates the last clock value to be used when sending messages
	LastClockValue uint64 `json:"lastClockValue"`
	// DeletedAtClockValue indicates the clock value at time of deletion, messages
	// with lower clock value of this should be discarded
	DeletedAtClockValue uint64 `json:"deletedAtClockValue"`

	// Denormalized fields
	UnviewedMessagesCount uint            `json:"unviewedMessagesCount"`
	LastMessage           *common.Message `json:"lastMessage"`

	// Group chat fields
	// Members are the members who have been invited to the group chat
	Members []ChatMember `json:"members"`
	// MembershipUpdates is all the membership events in the chat
	MembershipUpdates []v1protocol.MembershipUpdateEvent `json:"membershipUpdateEvents"`

	// Generated username name of the chat for one-to-ones
	Alias string `json:"alias,omitempty"`
	// Identicon generated from public key
	Identicon string `json:"identicon"`

	// Muted is used to check whether we want to receive
	// push notifications for this chat
	Muted bool `json:"muted,omitempty"`

	// Public key of administrator who created invitation link
	InvitationAdmin string `json:"invitationAdmin,omitempty"`

	// Public key of user profile
	Profile string `json:"profile,omitempty"`

	// CommunityID is the id of the community it belongs to
	CommunityID string `json:"communityId,omitempty"`
}

func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) {
	// For one to one chatID is an encoded public key
	if c.ChatType != ChatTypeOneToOne {
		return nil, nil
	}
	return common.HexToPubkey(c.ID)
}

func (c *Chat) Public() bool {
	return c.ChatType == ChatTypePublic
}

func (c *Chat) ProfileUpdates() bool {
	return c.ChatType == ChatTypeProfile || len(c.Profile) > 0
}

func (c *Chat) Timeline() bool {
	return c.ChatType == ChatTypeTimeline
}

func (c *Chat) OneToOne() bool {
	return c.ChatType == ChatTypeOneToOne
}

func (c *Chat) CommunityChatID() string {
	if c.ChatType != ChatTypeCommunityChat {
		return c.ID
	}

	// Strips out the local prefix of the community-id
	return c.ID[pkStringLength:]
}

func (c *Chat) Validate() error {
	if c.ID == "" {
		return errors.New("chatID can't be blank")
	}

	if c.OneToOne() {
		_, err := c.PublicKey()
		return err
	}
	return nil
}

func (c *Chat) MembersAsPublicKeys() ([]*ecdsa.PublicKey, error) {
	publicKeys := make([]string, len(c.Members))
	for idx, item := range c.Members {
		publicKeys[idx] = item.ID
	}
	return stringSliceToPublicKeys(publicKeys)
}

func (c *Chat) JoinedMembersAsPublicKeys() ([]*ecdsa.PublicKey, error) {
	var publicKeys []string
	for _, member := range c.Members {
		if member.Joined {
			publicKeys = append(publicKeys, member.ID)
		}
	}
	return stringSliceToPublicKeys(publicKeys)
}

func (c *Chat) HasMember(memberID string) bool {
	for _, member := range c.Members {
		if memberID == member.ID {
			return true
		}
	}

	return false
}

func (c *Chat) updateChatFromGroupMembershipChanges(myID string, g *v1protocol.Group) {

	// Check if we were already in the chat
	hadJoined := c.HasMember(myID)

	// ID
	c.ID = g.ChatID()

	// Name
	c.Name = g.Name()

	// Members
	members := g.Members()
	admins := g.Admins()
	joined := g.Joined()
	chatMembers := make([]ChatMember, 0, len(members))
	for _, m := range members {
		// Check if we joined thanks to these changes, if so, make chat active
		if m == myID && !hadJoined {
			c.Active = true
		}

		chatMember := ChatMember{
			ID: m,
		}
		chatMember.Admin = stringSliceContains(admins, m)
		chatMember.Joined = stringSliceContains(joined, m)
		chatMembers = append(chatMembers, chatMember)
	}
	c.Members = chatMembers

	// MembershipUpdates
	c.MembershipUpdates = g.Events()
}

// NextClockAndTimestamp returns the next clock value
// and the current timestamp
func (c *Chat) NextClockAndTimestamp(timesource common.TimeSource) (uint64, uint64) {
	clock := c.LastClockValue
	timestamp := timesource.GetCurrentTime()
	if clock == 0 || clock < timestamp {
		clock = timestamp
	} else {
		clock = clock + 1
	}
	return clock, timestamp
}

func (c *Chat) UpdateFromMessage(message *common.Message, timesource common.TimeSource) error {
	c.Timestamp = int64(timesource.GetCurrentTime())

	// If the clock of the last message is lower, we set the message
	if c.LastMessage == nil || c.LastMessage.Clock <= message.Clock {
		c.LastMessage = message
	}
	// If the clock is higher we set the clock
	if c.LastClockValue < message.Clock {
		c.LastClockValue = message.Clock
	}
	return nil
}

// ChatMembershipUpdate represent an event on membership of the chat
type ChatMembershipUpdate struct {
	// Unique identifier for the event
	ID string `json:"id"`
	// Type indicates the kind of event
	Type protobuf.MembershipUpdateEvent_EventType `json:"type"`
	// Name represents the name in the event of changing name events
	Name string `json:"name,omitempty"`
	// Clock value of the event
	ClockValue uint64 `json:"clockValue"`
	// Signature of the event
	Signature string `json:"signature"`
	// Hex encoded public key of the creator of the event
	From string `json:"from"`
	// Target of the event for single-target events
	Member string `json:"member,omitempty"`
	// Target of the event for multi-target events
	Members []string `json:"members,omitempty"`
}

// ChatMember represents a member who participates in a group chat
type ChatMember struct {
	// ID is the hex encoded public key of the member
	ID string `json:"id"`
	// Admin indicates if the member is an admin of the group chat
	Admin bool `json:"admin"`
	// Joined indicates if the member has joined the group chat
	Joined bool `json:"joined"`
}

func (c ChatMember) PublicKey() (*ecdsa.PublicKey, error) {
	return common.HexToPubkey(c.ID)
}

func oneToOneChatID(publicKey *ecdsa.PublicKey) string {
	return types.EncodeHex(crypto.FromECDSAPub(publicKey))
}

func OneToOneFromPublicKey(pk *ecdsa.PublicKey, timesource common.TimeSource) *Chat {
	chatID := types.EncodeHex(crypto.FromECDSAPub(pk))
	newChat := CreateOneToOneChat(chatID[:8], pk, timesource)

	return newChat
}

func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey, timesource common.TimeSource) *Chat {
	return &Chat{
		ID:        oneToOneChatID(publicKey),
		Name:      name,
		Timestamp: int64(timesource.GetCurrentTime()),
		Active:    true,
		ChatType:  ChatTypeOneToOne,
	}
}

func CreateCommunityChat(orgID, chatID string, orgChat *protobuf.CommunityChat, timesource common.TimeSource) *Chat {
	color := orgChat.Identity.Color
	if color == "" {
		color = chatColors[rand.Intn(len(chatColors))] // nolint: gosec
	}

	return &Chat{
		CommunityID: orgID,
		Name:        orgChat.Identity.DisplayName,
		Active:      true,
		Color:       color,
		ID:          orgID + chatID,
		Timestamp:   int64(timesource.GetCurrentTime()),
		ChatType:    ChatTypeCommunityChat,
	}
}

func CreateCommunityChats(org *communities.Community, timesource common.TimeSource) []*Chat {
	var chats []*Chat
	orgID := org.IDString()

	for chatID, chat := range org.Chats() {
		chats = append(chats, CreateCommunityChat(orgID, chatID, chat, timesource))
	}
	return chats
}

func CreatePublicChat(name string, timesource common.TimeSource) *Chat {
	return &Chat{
		ID:        name,
		Name:      name,
		Active:    true,
		Timestamp: int64(timesource.GetCurrentTime()),
		Color:     chatColors[rand.Intn(len(chatColors))], // nolint: gosec
		ChatType:  ChatTypePublic,
	}
}

func buildProfileChatID(publicKeyString string) string {
	return "@" + publicKeyString
}

func CreateProfileChat(id string, profile string, timesource common.TimeSource) *Chat {
	return &Chat{
		ID:        id,
		Name:      id,
		Active:    true,
		Timestamp: int64(timesource.GetCurrentTime()),
		Color:     chatColors[rand.Intn(len(chatColors))], // nolint: gosec
		ChatType:  ChatTypeProfile,
		Profile:   profile,
	}
}

func CreateGroupChat(timesource common.TimeSource) Chat {
	return Chat{
		Active:    true,
		Color:     chatColors[rand.Intn(len(chatColors))], // nolint: gosec
		Timestamp: int64(timesource.GetCurrentTime()),
		ChatType:  ChatTypePrivateGroupChat,
	}
}

func stringSliceToPublicKeys(slice []string) ([]*ecdsa.PublicKey, error) {
	result := make([]*ecdsa.PublicKey, len(slice))
	for idx, item := range slice {
		var err error
		result[idx], err = common.HexToPubkey(item)
		if err != nil {
			return nil, err
		}
	}
	return result, nil
}

func stringSliceContains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}
<|endoftext|>


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

package syslog

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	piazza "github.com/venicegeo/pz-gocommon/gocommon"
)

const (
	SyslogdNetwork = ""
	SyslogdRaddr   = ""
)

func GetRequiredEnvVars() (string, string, error) {
	var loggerIndex, loggerType string
	if loggerIndex = os.Getenv("LOGGER_INDEX"); loggerIndex == "" {
		return "", "", errors.New("Elasticsearch index name not set")
	}
	if loggerType = os.Getenv("LOGGER_TYPE"); loggerType == "" {
		return "", "", errors.New("Elasticsearch type name not set")
	}
	return loggerIndex, loggerType, nil
}

func GetRequiredESIWriters(esi elasticsearch.IIndex, loggerType string) (logWriter *ElasticWriter, err error) {
	var indexExisted, typeExisted bool
	logWriter = &ElasticWriter{Esi: esi}
	if err = logWriter.SetType(loggerType); err != nil {
		return nil, err
	}
	if indexExisted, err = logWriter.CreateIndex(); err != nil {
		return nil, err
	}
	if !indexExisted {
		//fmt.Println("Created index:", esi.IndexName())
	}
	if typeExisted, err = logWriter.CreateType(LogMapping); err != nil {
		return nil, err
	}
	if !typeExisted {
		//fmt.Println("Created type:", loggerType)
	}
	return logWriter, err
}

//---------------------------------------------------------------------

// Writer is an interface for writing a Message to some sort of output.
type Writer interface {
	Write(*Message, bool) error
	writeWork(*Message) error
	Close() error
}

// Reader is an interface for reading Messages from some sort of input.
// count is the number of messages to read: 1 means the latest message,
// 2 means the two latest messages, etc. The newest message is at the end
// of the array.
type Reader interface {
	Read(count int) ([]Message, error)
	GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error)
}

type WriterReader interface {
	Reader
	Writer
}

//---------------------------------------------------------------------

type writeWork func(*Message) error

func aSyncLogic(write writeWork, mssg *Message, async bool) (err error) {
	if async {
		go func() {
			if err = write(mssg); err != nil {
				log.Printf("Unable to log message [%s] : %s\n", mssg.String(), err.Error())
			}
		}()
		return nil
	}
	if err = write(mssg); err != nil {
		log.Printf("Unable to log message [%s] : %s\n", mssg.String(), err.Error())
	}
	return err
}

//---------------------------------------------------------------------

// FileWriter implements the Writer interface, writing to a given file
type FileWriter struct {
	FileName string
	file     *os.File
}

// Write writes the message to the supplied file.
func (w *FileWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*FileWriter)(nil)
	return w.writeWork(mssg)
}

func (w *FileWriter) writeWork(mssg *Message) (err error) {
	if w == nil || w.FileName == "" {
		return fmt.Errorf("writer not set not set")
	}

	if w.file == nil {
		w.file, err = os.OpenFile(w.FileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
		if err != nil {
			return err
		}
	}

	s := mssg.String()
	s += "\n"

	_, err = io.WriteString(w.file, s)
	return err
}

// Close closes the file. The creator of the FileWriter must call this.
func (w *FileWriter) Close() error {
	return w.file.Close()
}

//---------------------------------------------------------------------

//StdoutWriter writes messages to STDOUT
type StdoutWriter struct {
}

//Writes message to STDOUT
func (w *StdoutWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*StdoutWriter)(nil)
	return w.writeWork(mssg)
}

func (w *StdoutWriter) writeWork(mssg *Message) error {
	fmt.Println(mssg.String())
	return nil
}

//Nothing to close for this writer
func (w *StdoutWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// MessageWriter implements Reader and Writer, using an array of Messages
// as the backing store
type LocalReaderWriter struct {
	messages []Message
}

// Write writes the message to the backing array
func (w *LocalReaderWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*LocalReaderWriter)(nil)
	return w.writeWork(mssg)
}

func (w *LocalReaderWriter) writeWork(mssg *Message) error {
	if w.messages == nil {
		w.messages = make([]Message, 0)
	}
	w.messages = append(w.messages, *mssg)
	return nil
}

// Read reads messages from the backing array. Will only return as many as are
// available; asking for too many is not an error.
func (w *LocalReaderWriter) Read(count int) ([]Message, error) {
	var _ Reader = (*LocalReaderWriter)(nil)

	if count < 0 {
		return nil, fmt.Errorf("invalid count: %d", count)
	}

	if w.messages == nil || count == 0 {
		return make([]Message, 0), nil
	}

	if count > len(w.messages) {
		count = len(w.messages)
	}

	n := len(w.messages)
	a := w.messages[n-count : n]

	return a, nil
}

func (w *LocalReaderWriter) GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error) {
	return nil, 0, fmt.Errorf("LocalReaderWriter.GetMessages() not supported")
}

func (w *LocalReaderWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// HttpWriter implements Writer, by talking to the actual pz-logger service
type HttpWriter struct {
	url string
	h   piazza.Http
}

func NewHttpWriter(url string, apiKey string) (*HttpWriter, error) {
	w := &HttpWriter{url: url}
	w.h = piazza.Http{
		BaseUrl: url,
		ApiKey:  apiKey,
		//FlightCheck: &piazza.SimpleFlightCheck{},
	}

	return w, nil
}

func (w *HttpWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*HttpWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *HttpWriter) writeWork(mssg *Message) error {
	if jresp := w.h.PzPost("/syslog", mssg); jresp.IsError() {
		return jresp.ToError()
	}
	return nil
}

func (w *HttpWriter) Close() error {
	return nil
}

func (w *HttpWriter) Read(count int) ([]Message, error) {
	var _ Reader = (*HttpWriter)(nil)

	format := &piazza.JsonPagination{
		Page:    0,
		PerPage: count,
		SortBy:  "timeStamp",
		Order:   "desc",
	}
	params := &piazza.HttpQueryParams{}

	mssgs, _, err := w.GetMessages(format, params)
	return mssgs, err

}

// GetMessages is only implemented for HttpWriter as it is most likely the only
// Writer to be used for reading back data.
func (w *HttpWriter) GetMessages(
	format *piazza.JsonPagination,
	params *piazza.HttpQueryParams) ([]Message, int, error) {
	var _ Reader = (*HttpWriter)(nil)

	formatString := format.String()
	paramString := params.String()

	var ext string
	if formatString != "" && paramString != "" {
		ext = "?" + formatString + "&" + paramString
	} else if formatString == "" && paramString != "" {
		ext = "?" + paramString
	} else if formatString != "" && paramString == "" {
		ext = "?" + formatString
	} else if formatString == "" && paramString == "" {
		ext = ""
	} else {
		return nil, 0, errors.New("Internal error: failed to parse query params")
	}

	endpoint := "/syslog" + ext
	jresp := w.h.PzGet(endpoint)
	if jresp.IsError() {
		return nil, 0, jresp.ToError()
	}
	var mssgs []Message
	err := jresp.ExtractData(&mssgs)
	if err != nil {
		return nil, 0, err
	}

	return mssgs, jresp.Pagination.Count, nil
}

//---------------------------------------------------------------------

// SyslogdWriter implements a Writer that writes to the syslogd system service.
// This will almost certainly not work on Windows, but that is okay because Piazza
// does not support Windows.
type SyslogdWriter struct {
	writer *DaemonWriter
}

func (w *SyslogdWriter) initWriter() error {
	if w.writer != nil {
		return nil
	}

	tw, err := Dial(SyslogdNetwork, SyslogdRaddr)
	if err != nil {
		return err
	}

	w.writer = tw

	return nil
}

// Write writes the message to the OS's syslogd system.
func (w *SyslogdWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*SyslogdWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *SyslogdWriter) writeWork(mssg *Message) error {
	if err := w.initWriter(); err != nil {
		return err
	}
	s := mssg.String()
	return w.writer.Write(s)
}

// Close closes the underlying network connection.
func (w *SyslogdWriter) Close() error {
	if w.writer == nil {
		return nil
	}
	return w.writer.Close()
}

//---------------------------------------------------------------------

// ElasticWriter implements the Writer, writing to elasticsearch
type ElasticWriter struct {
	Esi elasticsearch.IIndex
	typ string
	id  string
}

func NewElasticWriter(esi elasticsearch.IIndex, typ string) *ElasticWriter {
	ew := &ElasticWriter{
		Esi: esi,
		typ: typ,
	}
	return ew
}

// Write writes the message to the elasticsearch index, type, id
func (w *ElasticWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*ElasticWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *ElasticWriter) writeWork(mssg *Message) error {
	if w == nil || w.Esi == nil || w.typ == "" {
		return fmt.Errorf("writer not set not set")
	}

	_, err := w.Esi.PostData(w.typ, w.id, mssg)
	return err
}

// SetType sets the type to write to
func (w *ElasticWriter) SetType(typ string) error {
	if w == nil {
		return fmt.Errorf("writer not set not set")
	}
	w.typ = typ
	return nil
}

func (w *ElasticWriter) CreateIndex() (bool, error) {
	if w == nil || w.Esi == nil {
		return false, fmt.Errorf("writer not set not set")
	}
	exists, err := w.Esi.IndexExists()
	if err != nil {
		return exists, err
	}
	if !exists {
		return exists, w.Esi.Create("")
	}
	return exists, nil
}

func (w *ElasticWriter) CreateType(mapping string) (bool, error) {
	if w == nil || w.Esi == nil || w.typ == "" {
		return false, fmt.Errorf("writer not set not set")
	}
	exists, err := w.Esi.TypeExists(w.typ)
	if err != nil {
		return exists, err
	}
	if !exists {
		return exists, w.Esi.SetMapping(w.typ, piazza.JsonString(mapping))
	}
	return exists, nil
}

// SetID sets the id to write to
func (w *ElasticWriter) SetID(id string) error {
	if w == nil {
		return fmt.Errorf("writer not set not set")
	}
	w.id = id
	return nil
}

// Close does nothing but satisfy an interface.
func (w *ElasticWriter) Close() error {
	return nil
}

func (w *ElasticWriter) GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error) {
	return nil, 0, fmt.Errorf("ElasticWriter.GetMessages not supported")
}
func (w *ElasticWriter) Read(count int) ([]Message, error) {
	return nil, fmt.Errorf("ElasticWriter.Read not supported")
}

//-------------------------------

// NilWriter doesn't do anything
type NilWriter struct {
}

func (*NilWriter) Write(*Message, bool) error {
	var _ Writer = (*NilWriter)(nil)
	return nil
}

func (*NilWriter) writeWork(*Message) error {
	return nil
}

func (*NilWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// MultiWriter will write to N different Writers at once. When doing a
// Write or a Close, we call the function on all of them. If any have failed,
// we return the error from the first one that failed.
type MultiWriter struct {
	writers []Writer
}

func NewMultiWriter(ws []Writer) *MultiWriter {
	mw := &MultiWriter{}
	mw.writers = make([]Writer, len(ws))
	copy(mw.writers, ws)
	return mw
}

func (mw *MultiWriter) Write(m *Message, async bool) (err error) {
	var _ Writer = (*HttpWriter)(nil)
	for _, w := range mw.writers {
		e := w.Write(m, async)
		if e != nil && err != nil {
			err = e
		}
	}
	return err
}

func (mw *MultiWriter) writeWork(m *Message) (err error) {
	return nil
}

func (mw *MultiWriter) Close() error {
	var err error

	for _, w := range mw.writers {
		e := w.Close()
		if e != nil && err != nil {
			err = e
		}
	}

	return err
}
<commit_msg>When creating types for esi writers, the writer will return error if the type exists with a different schema<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package syslog

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	piazza "github.com/venicegeo/pz-gocommon/gocommon"
)

const (
	SyslogdNetwork = ""
	SyslogdRaddr   = ""
)

func GetRequiredEnvVars() (string, string, error) {
	var loggerIndex, loggerType string
	if loggerIndex = os.Getenv("LOGGER_INDEX"); loggerIndex == "" {
		return "", "", errors.New("Elasticsearch index name not set")
	}
	if loggerType = os.Getenv("LOGGER_TYPE"); loggerType == "" {
		return "", "", errors.New("Elasticsearch type name not set")
	}
	return loggerIndex, loggerType, nil
}

func GetRequiredESIWriters(esi elasticsearch.IIndex, loggerType string) (logWriter *ElasticWriter, err error) {
	var indexExisted, typeExisted bool
	logWriter = &ElasticWriter{Esi: esi}
	if err = logWriter.SetType(loggerType); err != nil {
		return nil, err
	}
	if indexExisted, err = logWriter.CreateIndex(); err != nil {
		return nil, err
	}
	if !indexExisted {
		//fmt.Println("Created index:", esi.IndexName())
	}
	if typeExisted, err = logWriter.CreateType(LogMapping); err != nil {
		return nil, err
	}
	if !typeExisted {
		//fmt.Println("Created type:", loggerType)
	}
	return logWriter, err
}

//---------------------------------------------------------------------

// Writer is an interface for writing a Message to some sort of output.
type Writer interface {
	Write(*Message, bool) error
	writeWork(*Message) error
	Close() error
}

// Reader is an interface for reading Messages from some sort of input.
// count is the number of messages to read: 1 means the latest message,
// 2 means the two latest messages, etc. The newest message is at the end
// of the array.
type Reader interface {
	Read(count int) ([]Message, error)
	GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error)
}

type WriterReader interface {
	Reader
	Writer
}

//---------------------------------------------------------------------

type writeWork func(*Message) error

func aSyncLogic(write writeWork, mssg *Message, async bool) (err error) {
	if async {
		go func() {
			if err = write(mssg); err != nil {
				log.Printf("Unable to log message [%s] : %s\n", mssg.String(), err.Error())
			}
		}()
		return nil
	}
	if err = write(mssg); err != nil {
		log.Printf("Unable to log message [%s] : %s\n", mssg.String(), err.Error())
	}
	return err
}

//---------------------------------------------------------------------

// FileWriter implements the Writer interface, writing to a given file
type FileWriter struct {
	FileName string
	file     *os.File
}

// Write writes the message to the supplied file.
func (w *FileWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*FileWriter)(nil)
	return w.writeWork(mssg)
}

func (w *FileWriter) writeWork(mssg *Message) (err error) {
	if w == nil || w.FileName == "" {
		return fmt.Errorf("writer not set not set")
	}

	if w.file == nil {
		w.file, err = os.OpenFile(w.FileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
		if err != nil {
			return err
		}
	}

	s := mssg.String()
	s += "\n"

	_, err = io.WriteString(w.file, s)
	return err
}

// Close closes the file. The creator of the FileWriter must call this.
func (w *FileWriter) Close() error {
	return w.file.Close()
}

//---------------------------------------------------------------------

//StdoutWriter writes messages to STDOUT
type StdoutWriter struct {
}

//Writes message to STDOUT
func (w *StdoutWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*StdoutWriter)(nil)
	return w.writeWork(mssg)
}

func (w *StdoutWriter) writeWork(mssg *Message) error {
	fmt.Println(mssg.String())
	return nil
}

//Nothing to close for this writer
func (w *StdoutWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// MessageWriter implements Reader and Writer, using an array of Messages
// as the backing store
type LocalReaderWriter struct {
	messages []Message
}

// Write writes the message to the backing array
func (w *LocalReaderWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*LocalReaderWriter)(nil)
	return w.writeWork(mssg)
}

func (w *LocalReaderWriter) writeWork(mssg *Message) error {
	if w.messages == nil {
		w.messages = make([]Message, 0)
	}
	w.messages = append(w.messages, *mssg)
	return nil
}

// Read reads messages from the backing array. Will only return as many as are
// available; asking for too many is not an error.
func (w *LocalReaderWriter) Read(count int) ([]Message, error) {
	var _ Reader = (*LocalReaderWriter)(nil)

	if count < 0 {
		return nil, fmt.Errorf("invalid count: %d", count)
	}

	if w.messages == nil || count == 0 {
		return make([]Message, 0), nil
	}

	if count > len(w.messages) {
		count = len(w.messages)
	}

	n := len(w.messages)
	a := w.messages[n-count : n]

	return a, nil
}

func (w *LocalReaderWriter) GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error) {
	return nil, 0, fmt.Errorf("LocalReaderWriter.GetMessages() not supported")
}

func (w *LocalReaderWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// HttpWriter implements Writer, by talking to the actual pz-logger service
type HttpWriter struct {
	url string
	h   piazza.Http
}

func NewHttpWriter(url string, apiKey string) (*HttpWriter, error) {
	w := &HttpWriter{url: url}
	w.h = piazza.Http{
		BaseUrl: url,
		ApiKey:  apiKey,
		//FlightCheck: &piazza.SimpleFlightCheck{},
	}

	return w, nil
}

func (w *HttpWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*HttpWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *HttpWriter) writeWork(mssg *Message) error {
	if jresp := w.h.PzPost("/syslog", mssg); jresp.IsError() {
		return jresp.ToError()
	}
	return nil
}

func (w *HttpWriter) Close() error {
	return nil
}

func (w *HttpWriter) Read(count int) ([]Message, error) {
	var _ Reader = (*HttpWriter)(nil)

	format := &piazza.JsonPagination{
		Page:    0,
		PerPage: count,
		SortBy:  "timeStamp",
		Order:   "desc",
	}
	params := &piazza.HttpQueryParams{}

	mssgs, _, err := w.GetMessages(format, params)
	return mssgs, err

}

// GetMessages is only implemented for HttpWriter as it is most likely the only
// Writer to be used for reading back data.
func (w *HttpWriter) GetMessages(
	format *piazza.JsonPagination,
	params *piazza.HttpQueryParams) ([]Message, int, error) {
	var _ Reader = (*HttpWriter)(nil)

	formatString := format.String()
	paramString := params.String()

	var ext string
	if formatString != "" && paramString != "" {
		ext = "?" + formatString + "&" + paramString
	} else if formatString == "" && paramString != "" {
		ext = "?" + paramString
	} else if formatString != "" && paramString == "" {
		ext = "?" + formatString
	} else if formatString == "" && paramString == "" {
		ext = ""
	} else {
		return nil, 0, errors.New("Internal error: failed to parse query params")
	}

	endpoint := "/syslog" + ext
	jresp := w.h.PzGet(endpoint)
	if jresp.IsError() {
		return nil, 0, jresp.ToError()
	}
	var mssgs []Message
	err := jresp.ExtractData(&mssgs)
	if err != nil {
		return nil, 0, err
	}

	return mssgs, jresp.Pagination.Count, nil
}

//---------------------------------------------------------------------

// SyslogdWriter implements a Writer that writes to the syslogd system service.
// This will almost certainly not work on Windows, but that is okay because Piazza
// does not support Windows.
type SyslogdWriter struct {
	writer *DaemonWriter
}

func (w *SyslogdWriter) initWriter() error {
	if w.writer != nil {
		return nil
	}

	tw, err := Dial(SyslogdNetwork, SyslogdRaddr)
	if err != nil {
		return err
	}

	w.writer = tw

	return nil
}

// Write writes the message to the OS's syslogd system.
func (w *SyslogdWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*SyslogdWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *SyslogdWriter) writeWork(mssg *Message) error {
	if err := w.initWriter(); err != nil {
		return err
	}
	s := mssg.String()
	return w.writer.Write(s)
}

// Close closes the underlying network connection.
func (w *SyslogdWriter) Close() error {
	if w.writer == nil {
		return nil
	}
	return w.writer.Close()
}

//---------------------------------------------------------------------

// ElasticWriter implements the Writer, writing to elasticsearch
type ElasticWriter struct {
	Esi elasticsearch.IIndex
	typ string
	id  string
}

func NewElasticWriter(esi elasticsearch.IIndex, typ string) *ElasticWriter {
	ew := &ElasticWriter{
		Esi: esi,
		typ: typ,
	}
	return ew
}

// Write writes the message to the elasticsearch index, type, id
func (w *ElasticWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*ElasticWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *ElasticWriter) writeWork(mssg *Message) error {
	if w == nil || w.Esi == nil || w.typ == "" {
		return fmt.Errorf("writer not set not set")
	}

	_, err := w.Esi.PostData(w.typ, w.id, mssg)
	return err
}

// SetType sets the type to write to
func (w *ElasticWriter) SetType(typ string) error {
	if w == nil {
		return fmt.Errorf("writer not set not set")
	}
	w.typ = typ
	return nil
}

func (w *ElasticWriter) CreateIndex() (bool, error) {
	if w == nil || w.Esi == nil {
		return false, fmt.Errorf("writer not set not set")
	}
	exists, err := w.Esi.IndexExists()
	if err != nil {
		return exists, err
	}
	if !exists {
		return exists, w.Esi.Create("")
	}
	return exists, nil
}

func (w *ElasticWriter) CreateType(mapping string) (bool, error) {
	if w == nil || w.Esi == nil || w.typ == "" {
		return false, fmt.Errorf("writer not set not set")
	}
	exists, err := w.Esi.TypeExists(w.typ)
	if err != nil {
		return exists, err
	}
	if exists {
		var currentMapping, newMapi interface{}
		if currentMapping, err = w.Esi.GetMapping(w.typ); err != nil {
			return exists, err
		}
		if newMapi, err = piazza.StructStringToInterface(mapping); err != nil {
			return exists, err
		}
		newMap := map[string]interface{}{w.typ: newMapi}
		if !reflect.DeepEqual(currentMapping, newMap) {
			return exists, errors.New("Elasticsearch contains an invalid mapping for type: " + w.typ)
		}
	} else {
		return exists, w.Esi.SetMapping(w.typ, piazza.JsonString(mapping))
	}
	return exists, nil
}

// SetID sets the id to write to
func (w *ElasticWriter) SetID(id string) error {
	if w == nil {
		return fmt.Errorf("writer not set not set")
	}
	w.id = id
	return nil
}

// Close does nothing but satisfy an interface.
func (w *ElasticWriter) Close() error {
	return nil
}

func (w *ElasticWriter) GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error) {
	return nil, 0, fmt.Errorf("ElasticWriter.GetMessages not supported")
}
func (w *ElasticWriter) Read(count int) ([]Message, error) {
	return nil, fmt.Errorf("ElasticWriter.Read not supported")
}

//-------------------------------

// NilWriter doesn't do anything
type NilWriter struct {
}

func (*NilWriter) Write(*Message, bool) error {
	var _ Writer = (*NilWriter)(nil)
	return nil
}

func (*NilWriter) writeWork(*Message) error {
	return nil
}

func (*NilWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// MultiWriter will write to N different Writers at once. When doing a
// Write or a Close, we call the function on all of them. If any have failed,
// we return the error from the first one that failed.
type MultiWriter struct {
	writers []Writer
}

func NewMultiWriter(ws []Writer) *MultiWriter {
	mw := &MultiWriter{}
	mw.writers = make([]Writer, len(ws))
	copy(mw.writers, ws)
	return mw
}

func (mw *MultiWriter) Write(m *Message, async bool) (err error) {
	var _ Writer = (*HttpWriter)(nil)
	for _, w := range mw.writers {
		e := w.Write(m, async)
		if e != nil && err != nil {
			err = e
		}
	}
	return err
}

func (mw *MultiWriter) writeWork(m *Message) (err error) {
	return nil
}

func (mw *MultiWriter) Close() error {
	var err error

	for _, w := range mw.writers {
		e := w.Close()
		if e != nil && err != nil {
			err = e
		}
	}

	return err
}
<|endoftext|>