<commit_before>package kill

import (
	"fmt"
	"os"

	"github.com/fatih/color"
	"github.com/gyuho/psn/ps"
	"github.com/gyuho/psn/ss"
	"github.com/spf13/cobra"
)

type Flags struct {
	Force         bool
	CleanUp       bool
	FilterStatus  *ps.Status
	FilterProcess *ss.Process
}

var (
	Command = &cobra.Command{
		Use:   "kill",
		Short: "Kills programs using syscall. Make sure to specify the flags to find the program.",
		RunE:  CommandFunc,
	}
	cmdFlag = Flags{FilterStatus: &ps.Status{}, FilterProcess: &ss.Process{}}
)

func init() {
	Command.PersistentFlags().BoolVarP(&cmdFlag.Force, "force", "f", false, "'true' to force kill parent processes.")
	Command.PersistentFlags().BoolVar(&cmdFlag.CleanUp, "clean-up", false, "'true' to clean up deleted programs (this overwrites program flag).")
	Command.PersistentFlags().StringVarP(&cmdFlag.FilterProcess.Program, "program", "s", "SPECIFY YOUR PROGRAM HERE", "Specify the program. Empty lists all programs.")
	Command.PersistentFlags().StringVarP(&cmdFlag.FilterProcess.LocalPort, "local-port", "l", "", "Specify the local port. Empty lists all local ports.")
}

func CommandFunc(cmd *cobra.Command, args []string) error {
	if cmdFlag.CleanUp {
		cmdFlag.FilterProcess.Program = "deleted)"
		cmdFlag.FilterStatus.State = "Z (zombie)"
	}
	if cmdFlag.FilterProcess.LocalPort != "" && cmdFlag.FilterProcess.Program == "SPECIFY YOUR PROGRAM HERE" {
		cmdFlag.FilterProcess.Program = ""
	}
	if cmdFlag.FilterProcess.Program != "" && !cmdFlag.CleanUp {
		cmdFlag.FilterStatus.Name = cmdFlag.FilterProcess.Program
	}

	color.Set(color.FgRed)
	fmt.Fprintf(os.Stdout, "\npsn kill with %q, %q (port %s)\n\n", cmdFlag.FilterProcess.Program, cmdFlag.FilterStatus.Name, cmdFlag.FilterProcess.LocalPort)
	color.Unset()

	if cmdFlag.FilterStatus.State != "" || cmdFlag.FilterStatus.Name != "" {
		pss, err := ps.ListStatus(cmdFlag.FilterStatus)
		if err != nil {
			return err
		}
		ps.WriteToTable(os.Stdout, 0, pss...)
		fmt.Fprintf(os.Stdout, "\n")
	}

	ssr, err := ss.List(cmdFlag.FilterProcess, ss.TCP, ss.TCP6)
	if err != nil {
		return err
	}
	ss.WriteToTable(os.Stdout, 0, ssr...)
	fmt.Fprintf(os.Stdout, "\n")
	ss.Kill(os.Stdout, ssr...)

	color.Set(color.FgGreen)
	fmt.Fprintf(os.Stdout, "\nDone.\n")
	color.Unset()

	return nil
}
<commit_msg>kill by pid<commit_after>package kill

import (
	"fmt"
	"os"

	"github.com/fatih/color"
	"github.com/gyuho/psn/ps"
	"github.com/gyuho/psn/ss"
	"github.com/spf13/cobra"
)

type Flags struct {
	Force         bool
	CleanUp       bool
	FilterStatus  *ps.Status
	FilterProcess *ss.Process
}

var (
	Command = &cobra.Command{
		Use:   "kill",
		Short: "Kills programs using syscall. Make sure to specify the flags to find the program.",
		RunE:  CommandFunc,
	}
	cmdFlag = Flags{FilterStatus: &ps.Status{}, FilterProcess: &ss.Process{}}
)

func init() {
	Command.PersistentFlags().BoolVarP(&cmdFlag.Force, "force", "f", false, "'true' to force kill parent processes.")
	Command.PersistentFlags().BoolVar(&cmdFlag.CleanUp, "clean-up", false, "'true' to clean up deleted programs (this overwrites program flag).")
	Command.PersistentFlags().StringVarP(&cmdFlag.FilterProcess.Program, "program", "s", "SPECIFY YOUR PROGRAM HERE", "Specify the program. Empty lists all programs.")
	Command.PersistentFlags().StringVarP(&cmdFlag.FilterProcess.LocalPort, "local-port", "l", "", "Specify the local port. Empty lists all local ports.")
}

func CommandFunc(cmd *cobra.Command, args []string) error {
	if cmdFlag.CleanUp {
		cmdFlag.FilterProcess.Program = "deleted)"
		cmdFlag.FilterStatus.State = "Z (zombie)"
	}
	if cmdFlag.FilterProcess.LocalPort != "" && cmdFlag.FilterProcess.Program == "SPECIFY YOUR PROGRAM HERE" {
		cmdFlag.FilterProcess.Program = ""
	}
	if cmdFlag.FilterProcess.Program != "" && !cmdFlag.CleanUp {
		cmdFlag.FilterStatus.Name = cmdFlag.FilterProcess.Program
	}

	color.Set(color.FgRed)
	fmt.Fprintf(os.Stdout, "\npsn kill with %q, %q (port %s)\n\n", cmdFlag.FilterProcess.Program, cmdFlag.FilterStatus.Name, cmdFlag.FilterProcess.LocalPort)
	color.Unset()

	if cmdFlag.FilterStatus.State != "" || cmdFlag.FilterStatus.Name != "" {
		pss, err := ps.ListStatus(cmdFlag.FilterStatus)
		if err != nil {
			return err
		}
		ps.WriteToTable(os.Stdout, 0, pss...)
		ps.Kill(os.Stdout, cmdFlag.Force, pss...)
		fmt.Fprintf(os.Stdout, "\n")
	}

	ssr, err := ss.List(cmdFlag.FilterProcess, ss.TCP, ss.TCP6)
	if err != nil {
		return err
	}
	ss.WriteToTable(os.Stdout, 0, ssr...)
	fmt.Fprintf(os.Stdout, "\n")
	ss.Kill(os.Stdout, ssr...)

	color.Set(color.FgGreen)
	fmt.Fprintf(os.Stdout, "\nDone.\n")
	color.Unset()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io"

	"bitbucket.org/pushkin_ivan/clever-snake/game"
	"github.com/golang/glog"
	"github.com/ivan1993spb/pwshandler"
	"golang.org/x/net/context"
	"golang.org/x/net/websocket"
)

const INPUT_MAX_LENGTH = 512

type PoolFeatures struct {
	startStreamConn StartStreamConnFunc
	stopStreamConn  StopStreamConnFunc
	// startPlayer starts player
	startPlayer game.StartPlayerFunc
	poolContext context.Context
}

type errConnProcessing struct {
	err error
}

func (e *errConnProcessing) Error() string {
	return "Error of connection processing in connection manager: " +
		e.err.Error()
}

type ConnManager struct{}

func NewConnManager() pwshandler.ConnManager {
	return &ConnManager{}
}

// Implementing pwshandler.ConnManager interface
func (m *ConnManager) Handle(ws *websocket.Conn,
	data pwshandler.Environment) error {
	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Websocket handler was started")
		defer glog.Infoln("Websocket handler was finished")
	}

	poolFeatures, ok := data.(*PoolFeatures)
	if !ok {
		return &errConnProcessing{
			errors.New("Pool data was not received"),
		}
	}

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Creating connection to common game stream")
	}
	if err := poolFeatures.startStreamConn(ws); err != nil {
		return &errConnProcessing{err}
	}

	// input is channel for transferring information from client to
	// player goroutine, for example: player commands
	input := make(chan []byte)

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Starting player")
	}

	// output is channel for transferring private game information
	// for only one player. This information are useful only for
	// current player
	output, err := poolFeatures.startPlayer(input)
	if err != nil {
		return &errConnProcessing{err}
	}

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Starting private game stream")
	}
	// Send game data which are useful only for current player
	go func() {
		for data := range output {
			if _, err := ws.Write(data); err != nil {
				glog.Warningln("Cannot send private game data:", err)
				break
			}
		}

		// Wait for closing output channel
		for range output {
		}

		if glog.V(INFOLOG_LEVEL_CONNS) {
			glog.Infoln("Private game stream finished")
		}
	}()

	stop := make(chan struct{})

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Starting player listener")
	}
	// Listen for player commands
	go func() {
		buffer := make([]byte, INPUT_MAX_LENGTH)

		for {
			n, err := ws.Read(buffer)
			if err != nil {
				if err != io.EOF {
					glog.Errorln("Cannot read data:", err)
				}
				break
			}
			input <- buffer[:n]
		}

		if glog.V(INFOLOG_LEVEL_CONNS) {
			glog.Infoln("Player listener finished")
		}

		close(stop)
	}()

	select {
	case <-stop:
	case <-poolFeatures.poolContext.Done():
	}

	close(input)

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Removing connection from common game stream")
	}
	if err := poolFeatures.stopStreamConn(ws); err != nil {
		return &errConnProcessing{err}
	}

	return nil

}

// Implementing pwshandler.ConnManager interface
func (m *ConnManager) HandleError(_ *websocket.Conn, err error) {
	if err == nil {
		err = errors.New("Passed nil errer for reporting")
	}
	glog.Errorln(err)
}
<commit_msg>synchronized connection processing<commit_after>package main

import (
	"errors"
	"io"

	"bitbucket.org/pushkin_ivan/clever-snake/game"
	"github.com/golang/glog"
	"github.com/ivan1993spb/pwshandler"
	"golang.org/x/net/context"
	"golang.org/x/net/websocket"
)

const INPUT_MAX_LENGTH = 512

type PoolFeatures struct {
	startStreamConn StartStreamConnFunc
	stopStreamConn  StopStreamConnFunc
	// startPlayer starts player
	startPlayer game.StartPlayerFunc
	poolContext context.Context
}

type errConnProcessing struct {
	err error
}

func (e *errConnProcessing) Error() string {
	return "Error of connection processing in connection manager: " +
		e.err.Error()
}

type ConnManager struct{}

func NewConnManager() pwshandler.ConnManager {
	return &ConnManager{}
}

// Implementing pwshandler.ConnManager interface
func (m *ConnManager) Handle(ws *websocket.Conn,
	data pwshandler.Environment) error {
	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Websocket handler was started")
		defer glog.Infoln("Websocket handler was finished")
	}

	poolFeatures, ok := data.(*PoolFeatures)
	if !ok {
		return &errConnProcessing{
			errors.New("Pool data was not received"),
		}
	}

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Creating connection to common game stream")
	}
	if err := poolFeatures.startStreamConn(ws); err != nil {
		return &errConnProcessing{err}
	}

	// input is channel for transferring information from client to
	// player goroutine, for example: player commands
	input := make(chan []byte)

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Starting player")
	}

	// output is channel for transferring private game information
	// for only one player. This information are useful only for
	// current player
	output, err := poolFeatures.startPlayer(input)
	if err != nil {
		return &errConnProcessing{err}
	}

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Starting private game stream")
	}
	// Send game data which are useful only for current player
	go func() {
		for data := range output {
			if _, err := ws.Write(data); err != nil {
				glog.Warningln("Cannot send private game data:", err)
				break
			}
		}

		// Wait for closing output channel
		for range output {
		}

		if glog.V(INFOLOG_LEVEL_CONNS) {
			glog.Infoln("Private game stream finished")
		}
	}()

	stop := make(chan struct{})

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Starting player listener")
	}
	// Listen for player commands
	go func() {
		buffer := make([]byte, INPUT_MAX_LENGTH)

		for {
			n, err := ws.Read(buffer)
			if err != nil {
				if err != io.EOF {
					glog.Errorln("Cannot read data:", err)
				}
				break
			}
			input <- buffer[:n]
		}

		if glog.V(INFOLOG_LEVEL_CONNS) {
			glog.Infoln("Player listener finished")
		}

		close(stop)
	}()

	select {
	case <-stop:
	case <-poolFeatures.poolContext.Done():
		if glog.V(INFOLOG_LEVEL_CONNS) {
			glog.Infof(
				"Forced connection closing [addr: %s]",
				ws.Request().RemoteAddr,
			)
		}
		if err = ws.Close(); err != nil {
			glog.Warningln("Forced connection closing error:", err)
		}
		// Waiting for stopping player command listener
		for range stop {
		}
	}

	// Closing input channel calls stopping player processes and
	// closing output channel
	close(input)

	// Waiting for stopping private game stream
	for range output {
	}

	if glog.V(INFOLOG_LEVEL_CONNS) {
		glog.Infoln("Removing connection from common game stream")
	}
	if err := poolFeatures.stopStreamConn(ws); err != nil {
		return &errConnProcessing{err}
	}

	return nil
}

// Implementing pwshandler.ConnManager interface
func (m *ConnManager) HandleError(_ *websocket.Conn, err error) {
	if err == nil {
		err = errors.New("Passed nil errer for reporting")
	}
	glog.Errorln(err)
}
<|endoftext|>


<commit_before>package db

import (
  "encoding/json"
  "errors"
  "fmt"
  "github.com/vole/gouuid"
  "io/ioutil"
  "os"
  osuser "os/user"
  "path"
  "strings"
  "time"
)

const VERSION = "v1"

var DIR = func() string {
  dir := "."
  user, err := osuser.Current()

  if err == nil {
    dir = user.HomeDir
  }

  return path.Join(dir, "Vole")
}()

func ReadFile(args ...string) ([]byte, error) {
  return ioutil.ReadFile(path.Join(args...))
}

func ReadDir(args ...string) ([]os.FileInfo, error) {
  return ioutil.ReadDir(path.Join(args...))
}

func Create(args ...string) (*os.File, error) {
  return os.Create(path.Join(args...))
}

/**
 * Posts
 */

type Post struct {
  Id      string `json:"id"`
  Title   string `json:"title"`
  User    string `json:"user"`
  Created int64  `json:"created"`
}

type PostContainer struct {
  Post Post `json:"post"`
}

type PostCollection struct {
  Posts []Post `json:"posts"`
}

func (collection *PostCollection) Len() int {
  return len(collection.Posts)
}

func (collection *PostCollection) Less(i, j int) bool {
  return collection.Posts[i].Created < collection.Posts[j].Created
}

func (collection *PostCollection) Swap(i, j int) {
  collection.Posts[i], collection.Posts[j] = collection.Posts[j], collection.Posts[i]
}

func (post *Post) Save() error {
  if post.Id == "" {
    uuid, _ := uuid.NewV4()
    post.Id = fmt.Sprintf("%s", uuid)
  }

  if post.Created == 0 {
    post.Created = time.Now().UnixNano()
  }

  rawJson, err := json.Marshal(*post)
  if err != nil {
    return err
  }

  file, err := Create(DIR, "users", post.User, VERSION, "posts", post.FileName())
  if err != nil {
    return err
  }

  file.Write(rawJson)
  return nil
}

func (post *Post) FileName() string {
  return fmt.Sprintf("%d-post-%s", post.Created, post.Id)
}

func PostFromJson(rawJson []byte) (*Post, error) {
  var post Post
  json.Unmarshal(rawJson, &post)

  if post.Id == "" {
    return nil, errors.New("Unable to use this file")
  }

  return &post, nil
}

func PostContainerFromJson(rawJson []byte) *PostContainer {
  var container PostContainer
  json.Unmarshal(rawJson, &container)
  return &container
}

func NewPost(title string, user string) *Post {
  return &Post{
    Title: title,
    User:  user,
  }
}

func NewPostContainer(post *Post) *PostContainer {
  return &PostContainer{Post: *post}
}

func NewPostCollection(posts []Post) *PostCollection {
  return &PostCollection{Posts: posts}
}

func GetPosts() (*PostCollection, error) {
  posts := make([]Post, 0)

  users, _ := ReadDir(DIR, "users")

  for _, user := range users {
    dir := path.Join(DIR, "users", user.Name(), VERSION, "posts")

    files, err := ReadDir(dir)
    if err != nil {
      continue
    }

    for _, post := range files {
      data, err := ReadFile(dir, post.Name())

      if err != nil {
        continue
      }

      postData, err := PostFromJson(data)

      if err != nil {
        continue
      }

      posts = append(posts, *postData)
    }
  }

  return &PostCollection{posts}, nil
}

/**
 * Users
 */

type User struct {
  Id          string `json:"id"`
  Key         string `json:"key"`
  Hash        string `json:"hash"`
  User        string `json:"user"`
  DisplayName string `json:"display_name"`
  IsMyUser    bool   `json:"is_my_user,omitempty"`
}

func (user *User) Save() error {
  user.IsMyUser = false

  if user.Id == "" {
    uuid, _ := uuid.NewV4()
    user.Id = fmt.Sprintf("%s", uuid)
  }

  rawJson, err := json.Marshal(*user)
  if err != nil {
    return err
  }

  dir := path.Join(DIR, "users", user.User, VERSION)

  userDirErr := os.MkdirAll(path.Join(dir, "user"), 0755)
  if userDirErr != nil {
    return err
  }

  postsDirErr := os.MkdirAll(path.Join(dir, "posts"), 0755)
  if postsDirErr != nil {
    return err
  }

  file, err := Create(dir, "user", user.User)
  if err != nil {
    return err
  }

  file.Write(rawJson)
  return nil
}

type UserCollection struct {
  Users []User `json:"users"`
}

type UserContainer struct {
  User User `json:"user"`
}

func UserFromJson(rawJson []byte) (*User, error) {
  var user User
  json.Unmarshal(rawJson, &user)
  return &user, nil
}

func UserContainerFromJson(rawJson []byte) *UserContainer {
  var container UserContainer
  json.Unmarshal(rawJson, &container)
  return &container
}

func NewUser(user string, displayName string) *User {
  return &User{
    User:        user,
    DisplayName: displayName,
  }
}

func NewUserContainer(user *User) *UserContainer {
  return &UserContainer{User: *user}
}

func NewUserCollection(users []User) *UserCollection {
  return &UserCollection{Users: users}
}

func CurrentUser() (*User, error) {
  rawName, err := ReadFile(DIR, "my_user")
  if err != nil {
    return nil, err
  }

  name := strings.TrimSpace(string(rawName))

  data, err := ReadFile(DIR, "users", name, VERSION, "user", name)
  if err != nil {
    return nil, err
  }

  user, err := UserFromJson(data)
  if err != nil {
    return nil, errors.New("No current user.")
  }

  user.IsMyUser = true
  return user, nil
}

func GetUsers() (*UserCollection, error) {
  collection := make([]User, 0)

  currentUser, _ := CurrentUser()
  users, _ := ReadDir(DIR, "users")

  for _, dir := range users {
    data, err := ReadFile(DIR, "users", dir.Name(), VERSION, "user", dir.Name())
    if err != nil {
      continue
    }

    user, err := UserFromJson(data)

    user.IsMyUser = (user.User == currentUser.User)

    collection = append(collection, *user)
  }

  return &UserCollection{collection}, nil
}
<commit_msg>Close file handles after writing.<commit_after>package db

import (
  "encoding/json"
  "errors"
  "fmt"
  "github.com/vole/gouuid"
  "io/ioutil"
  "os"
  osuser "os/user"
  "path"
  "strings"
  "time"
)

const VERSION = "v1"

var DIR = func() string {
  dir := "."
  user, err := osuser.Current()

  if err == nil {
    dir = user.HomeDir
  }

  return path.Join(dir, "Vole")
}()

func ReadFile(args ...string) ([]byte, error) {
  return ioutil.ReadFile(path.Join(args...))
}

func ReadDir(args ...string) ([]os.FileInfo, error) {
  return ioutil.ReadDir(path.Join(args...))
}

func Create(args ...string) (*os.File, error) {
  return os.Create(path.Join(args...))
}

func Write(path string, data []byte) error {
  file, err := os.Create(path)
  if err != nil {
    return err
  }

  _, err = file.Write(data)
  if err != nil {
    return err
  }

  return file.Close()
}

/**
 * Posts
 */

type Post struct {
  Id      string `json:"id"`
  Title   string `json:"title"`
  User    string `json:"user"`
  Created int64  `json:"created"`
}

type PostContainer struct {
  Post Post `json:"post"`
}

type PostCollection struct {
  Posts []Post `json:"posts"`
}

func (collection *PostCollection) Len() int {
  return len(collection.Posts)
}

func (collection *PostCollection) Less(i, j int) bool {
  return collection.Posts[i].Created < collection.Posts[j].Created
}

func (collection *PostCollection) Swap(i, j int) {
  collection.Posts[i], collection.Posts[j] = collection.Posts[j], collection.Posts[i]
}

func (post *Post) Save() error {
  if post.Id == "" {
    uuid, _ := uuid.NewV4()
    post.Id = fmt.Sprintf("%s", uuid)
  }

  if post.Created == 0 {
    post.Created = time.Now().UnixNano()
  }

  rawJson, err := json.Marshal(*post)
  if err != nil {
    return err
  }

  filePath := path.Join(DIR, "users", post.User, VERSION, "posts", post.FileName())

  return Write(filePath, rawJson)
}

func (post *Post) FileName() string {
  return fmt.Sprintf("%d-post-%s", post.Created, post.Id)
}

func PostFromJson(rawJson []byte) (*Post, error) {
  var post Post
  json.Unmarshal(rawJson, &post)

  if post.Id == "" {
    return nil, errors.New("Unable to use this file")
  }

  return &post, nil
}

func PostContainerFromJson(rawJson []byte) *PostContainer {
  var container PostContainer
  json.Unmarshal(rawJson, &container)
  return &container
}

func NewPost(title string, user string) *Post {
  return &Post{
    Title: title,
    User:  user,
  }
}

func NewPostContainer(post *Post) *PostContainer {
  return &PostContainer{Post: *post}
}

func NewPostCollection(posts []Post) *PostCollection {
  return &PostCollection{Posts: posts}
}

func GetPosts() (*PostCollection, error) {
  posts := make([]Post, 0)

  users, _ := ReadDir(DIR, "users")

  for _, user := range users {
    dir := path.Join(DIR, "users", user.Name(), VERSION, "posts")

    files, err := ReadDir(dir)
    if err != nil {
      continue
    }

    for _, post := range files {
      data, err := ReadFile(dir, post.Name())

      if err != nil {
        continue
      }

      postData, err := PostFromJson(data)

      if err != nil {
        continue
      }

      posts = append(posts, *postData)
    }
  }

  return &PostCollection{posts}, nil
}

/**
 * Users
 */

type User struct {
  Id          string `json:"id"`
  Key         string `json:"key"`
  Hash        string `json:"hash"`
  User        string `json:"user"`
  DisplayName string `json:"display_name"`
  IsMyUser    bool   `json:"is_my_user,omitempty"`
}

func (user *User) Save() error {
  user.IsMyUser = false

  if user.Id == "" {
    uuid, _ := uuid.NewV4()
    user.Id = fmt.Sprintf("%s", uuid)
  }

  rawJson, err := json.Marshal(*user)
  if err != nil {
    return err
  }

  dir := path.Join(DIR, "users", user.User, VERSION)

  userDirErr := os.MkdirAll(path.Join(dir, "user"), 0755)
  if userDirErr != nil {
    return err
  }

  postsDirErr := os.MkdirAll(path.Join(dir, "posts"), 0755)
  if postsDirErr != nil {
    return err
  }

  return Write(path.Join(dir, "user", user.User), rawJson)
}

type UserCollection struct {
  Users []User `json:"users"`
}

type UserContainer struct {
  User User `json:"user"`
}

func UserFromJson(rawJson []byte) (*User, error) {
  var user User
  json.Unmarshal(rawJson, &user)
  return &user, nil
}

func UserContainerFromJson(rawJson []byte) *UserContainer {
  var container UserContainer
  json.Unmarshal(rawJson, &container)
  return &container
}

func NewUser(user string, displayName string) *User {
  return &User{
    User:        user,
    DisplayName: displayName,
  }
}

func NewUserContainer(user *User) *UserContainer {
  return &UserContainer{User: *user}
}

func NewUserCollection(users []User) *UserCollection {
  return &UserCollection{Users: users}
}

func CurrentUser() (*User, error) {
  rawName, err := ReadFile(DIR, "my_user")
  if err != nil {
    return nil, err
  }

  name := strings.TrimSpace(string(rawName))

  data, err := ReadFile(DIR, "users", name, VERSION, "user", name)
  if err != nil {
    return nil, err
  }

  user, err := UserFromJson(data)
  if err != nil {
    return nil, errors.New("No current user.")
  }

  user.IsMyUser = true
  return user, nil
}

func GetUsers() (*UserCollection, error) {
  collection := make([]User, 0)

  currentUser, _ := CurrentUser()
  users, _ := ReadDir(DIR, "users")

  for _, dir := range users {
    data, err := ReadFile(DIR, "users", dir.Name(), VERSION, "user", dir.Name())
    if err != nil {
      continue
    }

    user, err := UserFromJson(data)

    user.IsMyUser = (user.User == currentUser.User)

    collection = append(collection, *user)
  }

  return &UserCollection{collection}, nil
}
<|endoftext|>


<commit_before>package trident

import (
	"errors"
	"time"
	"trident.li/keyval"
	pf "trident.li/pitchfork/lib"
)

type TriGroup interface {
	pf.PfGroup
	Add_default_attestations(ctx pf.PfCtx) (err error)
	GetVouch_adminonly() bool
	GetAttestations() (output []TriGroupAttestation, err error)
	GetAttestationsKVS() (kvs keyval.KeyVals, err error)
}

type TriGroupS struct {
	pf.PfGroup
	Please_vouch    bool   `label:"Please Vouch" pfset:"group_admin" hint:"Members must vouch before becoming active"`
	Vouch_adminonly bool   `label:"Vouch group admins only" pfset:"group_admin" hint:"Only adminstators may Vouvh"`
	Min_invouch     int    `label:"Minimum Inbound Vouches" pfset:"group_admin" hint:"Number of incoming vouches required to vett."`
	Min_outvouch    int    `label:"Minimum Outbound Vouches" pfset:"group_admin" hint:"Number of outgoing vouches required"`
	Target_invouch  int    `label:"Target Invouches" pfset:"group_admin"`
	Max_inactivity  string `label:"Maximum Inactivity" pfset:"group_admin"`
	Can_time_out    bool   `label:"Can Time Out" pfset:"group_admin"`
	Max_vouchdays   int    `label:"Maximum Vouch Days" pfset:"group_admin"`
	Idle_guard      string `label:"Idle Guard" pfset:"group_admin"`
	Nom_enabled     bool   `label:"Nominations Enabled" pfset:"group_admin"`
}

type TriGroupMember interface {
	pf.PfGroupMember
	GetVouchesFor() int
	GetVouchesBy() int
	GetVouchesForMe() int
	GetVouchesByMe() int
}

type TriGroupMemberS struct {
	pf.PfGroupMember
	VouchesFor   int
	VouchesBy    int
	VouchesForMe int
	VouchesByMe  int
}

func (o *TriGroupMemberS) GetVouchesFor() int {
	return o.VouchesFor
}

func (o *TriGroupMemberS) GetVouchesBy() int {
	return o.VouchesBy
}

func (o *TriGroupMemberS) GetVouchesForMe() int {
	return o.VouchesForMe
}

func (o *TriGroupMemberS) GetVouchesByMe() int {
	return o.VouchesByMe
}

/* Don't call directly, use ctx.NewGroup() */
func NewTriGroup() pf.PfGroup {
	return &TriGroupS{PfGroup: pf.NewPfGroup()}
}

func NewTriGroupMember() TriGroupMember {
	pfg := pf.NewPfGroupMember()
	return &TriGroupMemberS{PfGroupMember: pfg}
}

func (grp *TriGroupS) GetVouch_adminonly() bool {
	return grp.Vouch_adminonly
}

func (grp *TriGroupS) ListGroupMembers(search string, username string, offset int, max int, nominated bool, inclhidden bool, exact bool) (members []pf.PfGroupMember, err error) {
	var rows *pf.Rows

	grpname := grp.GetGroupName()
	grpdesc := grp.GetGroupDesc()

	members = nil

	ord := "ORDER BY m.descr"

	m := pf.NewPfGroupMember()

	q := m.SQL_Selects() + ", " +
		"COALESCE(for_vouches.num, 0) AS vouches_for, " +
		"COALESCE(for_me_vouches.num, 0) AS vouches_for_me, " +
		"COALESCE(by_vouches.num, 0) AS vouches_by, " +
		"COALESCE(by_me_vouches.num, 0) AS vouches_by_me " +
		m.SQL_Froms() + " " +
		"LEFT OUTER JOIN ( " +
		"  SELECT 'for' AS dir, mv.vouchee AS member, COUNT(*) AS num " +
		"  FROM member_vouch mv " +
		"  WHERE mv.trustgroup = $1 " +
		"  AND mv.positive " +
		"  GROUP BY mv.vouchee " +
		") as for_vouches on (m.ident = for_vouches.member) " +
		"LEFT OUTER JOIN ( " +
		"  SELECT 'by' AS dir, mv.vouchor AS member, COUNT(*) AS num " +
		"  FROM member_vouch mv " +
		"  WHERE mv.trustgroup = $1 " +
		"  AND mv.positive " +
		"  GROUP BY mv.vouchor " +
		") as by_vouches on (m.ident = by_vouches.member) " +
		"LEFT OUTER JOIN ( " +
		"  SELECT 'for_me' AS dir, mv.vouchor AS member, COUNT(*) AS num " +
		"  FROM member_vouch mv " +
		"  WHERE ROW(mv.trustgroup, mv.vouchee) = ROW($1, $2) " +
		"  AND mv.positive " +
		"  GROUP BY mv.vouchor " +
		") as for_me_vouches on (m.ident = for_me_vouches.member) " +
		"LEFT OUTER JOIN ( " +
		"  SELECT 'by_me' AS dir, mv.vouchee AS member, COUNT(*) AS num " +
		"  FROM member_vouch mv " +
		"  WHERE ROW(mv.trustgroup, mv.vouchor) = ROW($1, $2) " +
		"  AND mv.positive " +
		"  GROUP BY mv.vouchee " +
		") as by_me_vouches on (m.ident = by_me_vouches.member) " +
		"WHERE grp.ident = $1 " +
		"AND me.email = mt.email "

	if inclhidden {
		if nominated {
			q += "AND ms.ident = 'nominated' "
		}
	} else {
		if nominated {
			q += "AND (NOT ms.hidden OR ms.ident = 'nominated') "
		} else {
			q += "AND NOT ms.hidden "
		}
	}

	if search == "" {
		if max != 0 {
			q += ord + " LIMIT $4 OFFSET $3"
			rows, err = pf.DB.Query(q, grpname, username, offset, max)
		} else {
			q += ord
			rows, err = pf.DB.Query(q, grpname, username)
		}
	} else {
		if exact {
			q += "AND (m.ident ~* $3) " +
				ord

		} else {
			q += "AND (m.ident ~* $3 " +
				"OR m.descr ~* $3 " +
				"OR m.affiliation ~* $3) " +
				ord
		}

		if max != 0 {
			q += " LIMIT $5 OFFSET $4"
			rows, err = pf.DB.Query(q, grpname, username, search, offset, max)
		} else {
			rows, err = pf.DB.Query(q, grpname, username, search)
		}
	}

	if err != nil {
		pf.Log("Query failed: " + err.Error())
		return
	}

	defer rows.Close()

	for rows.Next() {
		var fullname string
		var username string
		var affiliation string
		var groupadmin bool
		var groupstate string
		var groupcansee bool
		var email string
		var pgpkey_id string
		var entered string
		var activity string
		var tel string
		var sms string
		var airport string

		member := NewTriGroupMember().(*TriGroupMemberS)

		err = rows.Scan(
			&username,
			&fullname,
			&affiliation,
			&groupadmin,
			&groupstate,
			&groupcansee,
			&email,
			&pgpkey_id,
			&entered,
			&activity,
			&tel,
			&sms,
			&airport,
			&member.VouchesFor,
			&member.VouchesForMe,
			&member.VouchesBy,
			&member.VouchesByMe)
		if err != nil {
			pf.Log("Error listing members: " + err.Error())
			return nil, err
		}

		member.Set(grpname, grpdesc, username, fullname, affiliation, groupadmin, groupstate, groupcansee, email, pgpkey_id, entered, activity, sms, tel, airport)
		members = append(members, member)
	}

	return members, nil
}

func (grp *TriGroupS) Add_default_attestations(ctx pf.PfCtx) (err error) {
	att := make(map[string]string)
	att["met"] = "I have met them in person more than once"
	att["trust"] = "I trust them to take action"
	att["fate"] = "I will share membership fate with them"

	for a, descr := range att {
		q := "INSERT INTO attestations " +
			"(ident, descr, trustgroup) " +
			"VALUES($1, $2, $3)"
		err = pf.DB.Exec(ctx,
			"Added default attestation $1 to group $3",
			1, q,
			a, descr, grp.GetGroupName())

		if err != nil {
			return
		}
	}

	return
}

func (grp *TriGroupS) Add_default_mailinglists(ctx pf.PfCtx) (err error) {
	err = grp.PfGroup.Add_default_mailinglists(ctx)
	if err != nil {
		return
	}

	mls := make(map[string]string)
	mls["vetting"] = "Vetting and Vouching"

	for lhs, descr := range mls {
		err = pf.Ml_addv(ctx, grp.PfGroup, lhs, descr, true, true, true)
		if err != nil {
			return
		}
	}

	return
}

func group_add(ctx pf.PfCtx, args []string) (err error) {
	var group_name string

	/* Make sure the name is mostly sane */
	group_name, err = pf.Chk_ident("Group Name", args[0])
	if err != nil {
		return
	}

	d_maxin := 180 * 24 * time.Hour
	i_maxin := d_maxin.Seconds()

	d_guard := 7 * 24 * time.Hour
	i_guard := d_guard.Seconds()

	grp := ctx.NewGroup().(TriGroup)
	exists := grp.Exists(group_name)
	if exists {
		err = errors.New("Group already exists")
		return
	}

	q := "INSERT INTO trustgroup " +
		"(ident, descr, shortname, min_invouch, pgp_required, " +
		" please_vouch, vouch_adminonly, min_outvouch, max_inactivity, can_time_out, " +
		" max_vouchdays, idle_guard, nom_enabled, target_invouch, has_wiki) " +
		"VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) "
	err = pf.DB.Exec(ctx,
		"Created group $1",
		1, q,
		group_name, group_name, group_name, 0, false,
		true, false, 0, i_maxin, false,
		0, i_guard, true, 0, false)

	if err != nil {
		err = errors.New("Group creation failed")
		return
	}

	err = ctx.SelectGroup(group_name, pf.PERM_SYS_ADMIN)
	if err != nil {
		err = errors.New("Group creation failed")
		return
	}

	/* Fetch our newly created group */
	tctx := TriGetCtx(ctx)
	grp = tctx.TriSelectedGroup()

	/* Select yourself */
	ctx.SelectMe()
	if err != nil {
		return
	}

	err = grp.Add_default_attestations(ctx)
	if err != nil {
		return
	}

	err = grp.Add_default_mailinglists(ctx)
	if err != nil {
		return
	}

	grp.Member_add(ctx)
	grp.Member_set_state(ctx, pf.GROUP_STATE_APPROVED)
	grp.Member_set_admin(ctx, true)

	/* All worked */
	ctx.OutLn("Creation of group %s complete", group_name)
	return
}

func group_member_nominate(ctx pf.PfCtx, args []string) (err error) {
	grp := ctx.SelectedGroup()
	return grp.Member_add(ctx)
}

func group_menu(ctx pf.PfCtx, menu *pf.PfMenu) {
	menu.Replace("add", group_add)

	m := []pf.PfMEntry{
		{"vouch", vouch_menu, 0, -1, nil, pf.PERM_USER, "Vouch Commands"},
		{"nominate", group_member_nominate, 2, 2, []string{"group", "username"}, pf.PERM_GROUP_MEMBER, "Nominate a member for a group"},
	}

	menu.Add(m...)
}
<commit_msg>Groupname/desc now come from SQL<commit_after>package trident

import (
	"errors"
	"time"
	"trident.li/keyval"
	pf "trident.li/pitchfork/lib"
)

type TriGroup interface {
	pf.PfGroup
	Add_default_attestations(ctx pf.PfCtx) (err error)
	GetVouch_adminonly() bool
	GetAttestations() (output []TriGroupAttestation, err error)
	GetAttestationsKVS() (kvs keyval.KeyVals, err error)
}

type TriGroupS struct {
	pf.PfGroup
	Please_vouch    bool   `label:"Please Vouch" pfset:"group_admin" hint:"Members must vouch before becoming active"`
	Vouch_adminonly bool   `label:"Vouch group admins only" pfset:"group_admin" hint:"Only adminstators may Vouvh"`
	Min_invouch     int    `label:"Minimum Inbound Vouches" pfset:"group_admin" hint:"Number of incoming vouches required to vett."`
	Min_outvouch    int    `label:"Minimum Outbound Vouches" pfset:"group_admin" hint:"Number of outgoing vouches required"`
	Target_invouch  int    `label:"Target Invouches" pfset:"group_admin"`
	Max_inactivity  string `label:"Maximum Inactivity" pfset:"group_admin"`
	Can_time_out    bool   `label:"Can Time Out" pfset:"group_admin"`
	Max_vouchdays   int    `label:"Maximum Vouch Days" pfset:"group_admin"`
	Idle_guard      string `label:"Idle Guard" pfset:"group_admin"`
	Nom_enabled     bool   `label:"Nominations Enabled" pfset:"group_admin"`
}

type TriGroupMember interface {
	pf.PfGroupMember
	GetVouchesFor() int
	GetVouchesBy() int
	GetVouchesForMe() int
	GetVouchesByMe() int
}

type TriGroupMemberS struct {
	pf.PfGroupMember
	VouchesFor   int
	VouchesBy    int
	VouchesForMe int
	VouchesByMe  int
}

func (o *TriGroupMemberS) GetVouchesFor() int {
	return o.VouchesFor
}

func (o *TriGroupMemberS) GetVouchesBy() int {
	return o.VouchesBy
}

func (o *TriGroupMemberS) GetVouchesForMe() int {
	return o.VouchesForMe
}

func (o *TriGroupMemberS) GetVouchesByMe() int {
	return o.VouchesByMe
}

/* Don't call directly, use ctx.NewGroup() */
func NewTriGroup() pf.PfGroup {
	return &TriGroupS{PfGroup: pf.NewPfGroup()}
}

func NewTriGroupMember() TriGroupMember {
	pfg := pf.NewPfGroupMember()
	return &TriGroupMemberS{PfGroupMember: pfg}
}

func (grp *TriGroupS) GetVouch_adminonly() bool {
	return grp.Vouch_adminonly
}

func (grp *TriGroupS) ListGroupMembers(search string, username string, offset int, max int, nominated bool, inclhidden bool, exact bool) (members []pf.PfGroupMember, err error) {
	var rows *pf.Rows

	grpname := grp.GetGroupName()

	members = nil

	ord := "ORDER BY m.descr"

	m := pf.NewPfGroupMember()

	q := m.SQL_Selects() + ", " +
		"COALESCE(for_vouches.num, 0) AS vouches_for, " +
		"COALESCE(for_me_vouches.num, 0) AS vouches_for_me, " +
		"COALESCE(by_vouches.num, 0) AS vouches_by, " +
		"COALESCE(by_me_vouches.num, 0) AS vouches_by_me " +
		m.SQL_Froms() + " " +
		"LEFT OUTER JOIN ( " +
		"  SELECT 'for' AS dir, mv.vouchee AS member, COUNT(*) AS num " +
		"  FROM member_vouch mv " +
		"  WHERE mv.trustgroup = $1 " +
		"  AND mv.positive " +
		"  GROUP BY mv.vouchee " +
		") as for_vouches on (m.ident = for_vouches.member) " +
		"LEFT OUTER JOIN ( " +
		"  SELECT 'by' AS dir, mv.vouchor AS member, COUNT(*) AS num " +
		"  FROM member_vouch mv " +
		"  WHERE mv.trustgroup = $1 " +
		"  AND mv.positive " +
		"  GROUP BY mv.vouchor " +
		") as by_vouches on (m.ident = by_vouches.member) " +
		"LEFT OUTER JOIN ( " +
		"  SELECT 'for_me' AS dir, mv.vouchor AS member, COUNT(*) AS num " +
		"  FROM member_vouch mv " +
		"  WHERE ROW(mv.trustgroup, mv.vouchee) = ROW($1, $2) " +
		"  AND mv.positive " +
		"  GROUP BY mv.vouchor " +
		") as for_me_vouches on (m.ident = for_me_vouches.member) " +
		"LEFT OUTER JOIN ( " +
		"  SELECT 'by_me' AS dir, mv.vouchee AS member, COUNT(*) AS num " +
		"  FROM member_vouch mv " +
		"  WHERE ROW(mv.trustgroup, mv.vouchor) = ROW($1, $2) " +
		"  AND mv.positive " +
		"  GROUP BY mv.vouchee " +
		") as by_me_vouches on (m.ident = by_me_vouches.member) " +
		"WHERE grp.ident = $1 " +
		"AND me.email = mt.email "

	if inclhidden {
		if nominated {
			q += "AND ms.ident = 'nominated' "
		}
	} else {
		if nominated {
			q += "AND (NOT ms.hidden OR ms.ident = 'nominated') "
		} else {
			q += "AND NOT ms.hidden "
		}
	}

	if search == "" {
		if max != 0 {
			q += ord + " LIMIT $4 OFFSET $3"
			rows, err = pf.DB.Query(q, grpname, username, offset, max)
		} else {
			q += ord
			rows, err = pf.DB.Query(q, grpname, username)
		}
	} else {
		if exact {
			q += "AND (m.ident ~* $3) " +
				ord

		} else {
			q += "AND (m.ident ~* $3 " +
				"OR m.descr ~* $3 " +
				"OR m.affiliation ~* $3) " +
				ord
		}

		if max != 0 {
			q += " LIMIT $5 OFFSET $4"
			rows, err = pf.DB.Query(q, grpname, username, search, offset, max)
		} else {
			rows, err = pf.DB.Query(q, grpname, username, search)
		}
	}

	if err != nil {
		pf.Log("Query failed: " + err.Error())
		return
	}

	defer rows.Close()

	for rows.Next() {
		var fullname string
		var username string
		var affiliation string
		var groupname string
		var groupdesc string
		var groupadmin bool
		var groupstate string
		var groupcansee bool
		var email string
		var pgpkey_id string
		var entered string
		var activity string
		var tel string
		var sms string
		var airport string

		member := NewTriGroupMember().(*TriGroupMemberS)

		err = rows.Scan(
			&username,
			&fullname,
			&affiliation,
			&groupname,
			&groupdesc,
			&groupadmin,
			&groupstate,
			&groupcansee,
			&email,
			&pgpkey_id,
			&entered,
			&activity,
			&tel,
			&sms,
			&airport,
			&member.VouchesFor,
			&member.VouchesForMe,
			&member.VouchesBy,
			&member.VouchesByMe)
		if err != nil {
			pf.Log("Error listing members: " + err.Error())
			return nil, err
		}

		member.Set(groupname, groupdesc, username, fullname, affiliation, groupadmin, groupstate, groupcansee, email, pgpkey_id, entered, activity, sms, tel, airport)
		members = append(members, member)
	}

	return members, nil
}

func (grp *TriGroupS) Add_default_attestations(ctx pf.PfCtx) (err error) {
	att := make(map[string]string)
	att["met"] = "I have met them in person more than once"
	att["trust"] = "I trust them to take action"
	att["fate"] = "I will share membership fate with them"

	for a, descr := range att {
		q := "INSERT INTO attestations " +
			"(ident, descr, trustgroup) " +
			"VALUES($1, $2, $3)"
		err = pf.DB.Exec(ctx,
			"Added default attestation $1 to group $3",
			1, q,
			a, descr, grp.GetGroupName())

		if err != nil {
			return
		}
	}

	return
}

func (grp *TriGroupS) Add_default_mailinglists(ctx pf.PfCtx) (err error) {
	err = grp.PfGroup.Add_default_mailinglists(ctx)
	if err != nil {
		return
	}

	mls := make(map[string]string)
	mls["vetting"] = "Vetting and Vouching"

	for lhs, descr := range mls {
		err = pf.Ml_addv(ctx, grp.PfGroup, lhs, descr, true, true, true)
		if err != nil {
			return
		}
	}

	return
}

func group_add(ctx pf.PfCtx, args []string) (err error) {
	var group_name string

	/* Make sure the name is mostly sane */
	group_name, err = pf.Chk_ident("Group Name", args[0])
	if err != nil {
		return
	}

	d_maxin := 180 * 24 * time.Hour
	i_maxin := d_maxin.Seconds()

	d_guard := 7 * 24 * time.Hour
	i_guard := d_guard.Seconds()

	grp := ctx.NewGroup().(TriGroup)
	exists := grp.Exists(group_name)
	if exists {
		err = errors.New("Group already exists")
		return
	}

	q := "INSERT INTO trustgroup " +
		"(ident, descr, shortname, min_invouch, pgp_required, " +
		" please_vouch, vouch_adminonly, min_outvouch, max_inactivity, can_time_out, " +
		" max_vouchdays, idle_guard, nom_enabled, target_invouch, has_wiki) " +
		"VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) "
	err = pf.DB.Exec(ctx,
		"Created group $1",
		1, q,
		group_name, group_name, group_name, 0, false,
		true, false, 0, i_maxin, false,
		0, i_guard, true, 0, false)

	if err != nil {
		err = errors.New("Group creation failed")
		return
	}

	err = ctx.SelectGroup(group_name, pf.PERM_SYS_ADMIN)
	if err != nil {
		err = errors.New("Group creation failed")
		return
	}

	/* Fetch our newly created group */
	tctx := TriGetCtx(ctx)
	grp = tctx.TriSelectedGroup()

	/* Select yourself */
	ctx.SelectMe()
	if err != nil {
		return
	}

	err = grp.Add_default_attestations(ctx)
	if err != nil {
		return
	}

	err = grp.Add_default_mailinglists(ctx)
	if err != nil {
		return
	}

	grp.Member_add(ctx)
	grp.Member_set_state(ctx, pf.GROUP_STATE_APPROVED)
	grp.Member_set_admin(ctx, true)

	/* All worked */
	ctx.OutLn("Creation of group %s complete", group_name)
	return
}

func group_member_nominate(ctx pf.PfCtx, args []string) (err error) {
	grp := ctx.SelectedGroup()
	return grp.Member_add(ctx)
}

func group_menu(ctx pf.PfCtx, menu *pf.PfMenu) {
	menu.Replace("add", group_add)

	m := []pf.PfMEntry{
		{"vouch", vouch_menu, 0, -1, nil, pf.PERM_USER, "Vouch Commands"},
		{"nominate", group_member_nominate, 2, 2, []string{"group", "username"}, pf.PERM_GROUP_MEMBER, "Nominate a member for a group"},
	}

	menu.Add(m...)
}
<|endoftext|>


<commit_before>package connectivity

// naive findQuery, O(n) in single component
func (s *Component) FindQueryInSingleSetNaive(a, b int) bool {
	//naive
	var first, second bool
	for _, it := range *s {
		if a == it {
			first = true
		}
		if b == it {
			second = true
		}
	}
	return first && second
}

type Component []int
type Components []Component

// return true if a and b are in the same component
func (s *Components) FindQuery(a, b int) bool {
	for _, c := range *s {
		if c.FindQueryInSingleSetNaive(a, b) {
			return true
		}
	}
	return false
}

// return queried Component in which the q resides
// and the index of q in the component
func (s *Components) queryComponent(q int) (int, Component) {
	for i, c := range *s {
		for _, d := range c {
			if d == q {
				return i, c
			}
		}
	}
	return -1, nil
}

type Connectivity struct {
	Size     int
	elements []int
	Components
}

// Construct a array of number 0...N-1
func NewConnectivity(N int) *Connectivity {
	result := &Connectivity{
		Size: N,
	}
	items := make([]int, N)
	components := make([]Component, N)
	for index := 0; index != N; index++ {
		items[index] = index
		components[index] = []int{index}
	}
	result.elements = items
	result.Components = components
	return result
}

// union
// reduce the []component dimension
// and and merge the related 2 components
func (c *Components) Union(a, b int) {
	iA, _ := c.queryComponent(a)
	iB, B := c.queryComponent(b)
	(*c)[iA] = append((*c)[iA], B...)
	copy((*c)[iB:], (*c)[iB+1:])
	(*c)[len(*c)-1] = nil
	*c = (*c)[:len(*c)-1]
}

// in order to calculate weight of the tree we maintain
// a second array storing the size of the tree at idx
type WeightedCompressed struct {
	parent []int
	size   []int
}

// constructor for weighted compressed connectivity
func NewWeightedCompression(n int) *WeightedCompressed {
	p := make([]int, n)
	sz := make([]int, n)
	for i := 0; i != n; i++ {
		p[i] = i
		sz[i] = i
	}
	return &WeightedCompressed{
		p,
		sz,
	}
}

// finally we hammer the final nail on the API
type UnionFind interface {
	Find(a, b int) bool
	Union(a, b int)
}

func (w *WeightedCompressed) Find(a, b int) bool {
	return w.root(a) == w.root(b)
}

func (w *WeightedCompressed) root(pos int) int {
	for pos != w.parent[pos] {
		w.parent[pos] = w.parent[w.parent[pos]]
		pos = w.parent[pos]
	}
	return pos
}

func (w *WeightedCompressed) Union(a, b int) {
	i := w.root(a)
	j := w.root(b)
	if i == j {
		return
	}
	if w.size[i] < w.size[j] {
		w.parent[i] = j
		w.size[j] += w.size[i]
	} else {
		w.parent[j] = i
		w.size[i] += w.size[j]
	}
}
<commit_msg>add description<commit_after>package connectivity

// naive findQuery, O(n) in single component
func (s *Component) FindQueryInSingleSetNaive(a, b int) bool {
	//naive
	var first, second bool
	for _, it := range *s {
		if a == it {
			first = true
		}
		if b == it {
			second = true
		}
	}
	return first && second
}

type Component []int
type Components []Component

// return true if a and b are in the same component
func (s *Components) FindQuery(a, b int) bool {
	for _, c := range *s {
		if c.FindQueryInSingleSetNaive(a, b) {
			return true
		}
	}
	return false
}

// return queried Component in which the q resides
// and the index of q in the component
func (s *Components) queryComponent(q int) (int, Component) {
	for i, c := range *s {
		for _, d := range c {
			if d == q {
				return i, c
			}
		}
	}
	return -1, nil
}

type Connectivity struct {
	Size     int
	elements []int
	Components
}

// Construct a array of number 0...N-1
func NewConnectivity(N int) *Connectivity {
	result := &Connectivity{
		Size: N,
	}
	items := make([]int, N)
	components := make([]Component, N)
	for index := 0; index != N; index++ {
		items[index] = index
		components[index] = []int{index}
	}
	result.elements = items
	result.Components = components
	return result
}

// union
// reduce the []component dimension
// and and merge the related 2 components
func (c *Components) Union(a, b int) {
	iA, _ := c.queryComponent(a)
	iB, B := c.queryComponent(b)
	(*c)[iA] = append((*c)[iA], B...)
	copy((*c)[iB:], (*c)[iB+1:])
	(*c)[len(*c)-1] = nil
	*c = (*c)[:len(*c)-1]
}

//////////////////////////////////////////////////////////
//  Improved UnionFind with weight and compression
//////////////////////////////////////////////////////////

// in order to calculate weight of the tree we maintain
// a second array storing the size of the tree at idx
type WeightedCompressed struct {
	parent []int
	size   []int
}

// constructor for weighted compressed connectivity
func NewWeightedCompression(n int) *WeightedCompressed {
	p := make([]int, n)
	sz := make([]int, n)
	for i := 0; i != n; i++ {
		p[i] = i
		sz[i] = i
	}
	return &WeightedCompressed{
		p,
		sz,
	}
}

// finally we hammer the final nail on the API
type UnionFind interface {
	Find(a, b int) bool
	Union(a, b int)
}

func (w *WeightedCompressed) Find(a, b int) bool {
	return w.root(a) == w.root(b)
}

func (w *WeightedCompressed) root(pos int) int {
	for pos != w.parent[pos] {
		w.parent[pos] = w.parent[w.parent[pos]]
		pos = w.parent[pos]
	}
	return pos
}

func (w *WeightedCompressed) Union(a, b int) {
	i := w.root(a)
	j := w.root(b)
	if i == j {
		return
	}
	if w.size[i] < w.size[j] {
		w.parent[i] = j
		w.size[j] += w.size[i]
	} else {
		w.parent[j] = i
		w.size[i] += w.size[j]
	}
}
<|endoftext|>


<commit_before>package xlog

import (
	"fmt"
	"io"
	"os"
	"runtime"
	"sync"
	"time"
)

const (
	Ldate = 1 << iota
	Ltime
	Lmicroseconds
	Llongfile
	Lshortfile
	Lnopanic
	Lnofatal
	Lnowarn
	Lnoprint
	Lnodebug
	Lstdflags = Ldate | Ltime
)

type Logger struct {
	mu     sync.Mutex
	prefix string
	flag   int
	out    io.Writer
	buf    []byte
}

func New(out io.Writer, prefix string, flag int) *Logger {
	return &Logger{out: out, prefix: prefix, flag: flag}
}

var std = New(os.Stderr, "", Lstdflags)

func itoa(buf *[]byte, i int, wid int) {
	var u uint = uint(i)
	if u == 0 && wid <= 1 {
		*buf = append(*buf, '0')
		return
	}
	var b [32]byte
	bp := len(b)
	for ; u > 0 || wid > 0; u /= 10 {
		bp--
		wid--
		b[bp] = byte(u%10) + '0'
	}
	*buf = append(*buf, b[bp:]...)
}

func (l *Logger) formatHeader(t time.Time, file string, line int) {
	l.buf = append(l.buf, l.prefix...)
	if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
		if l.flag&Ldate != 0 {
			year, month, day := t.Date()
			itoa(&l.buf, year, 4)
			l.buf = append(l.buf, '-')
			itoa(&l.buf, int(month), 2)
			l.buf = append(l.buf, '-')
			itoa(&l.buf, day, 2)
			l.buf = append(l.buf, ' ')
		}
		if l.flag&(Ltime|Lmicroseconds) != 0 {
			hour, min, sec := t.Clock()
			itoa(&l.buf, hour, 2)
			l.buf = append(l.buf, ':')
			itoa(&l.buf, min, 2)
			l.buf = append(l.buf, ':')
			itoa(&l.buf, sec, 2)
			if l.flag&Lmicroseconds != 0 {
				l.buf = append(l.buf, '.')
				itoa(&l.buf, t.Nanosecond()/1e3, 6)
			}
			l.buf = append(l.buf, ' ')
		}
	}
	if l.flag&(Lshortfile|Llongfile) != 0 {
		if l.flag&Lshortfile != 0 {
			short := file
			for i := len(file) - 1; i > 0; i-- {
				if file[i] == '/' {
					short = file[i+1:]
					break
				}
			}
			file = short
		}
		l.buf = append(l.buf, file...)
		l.buf = append(l.buf, ':')
		itoa(&l.buf, line, -1)
		l.buf = append(l.buf, ": "...)
	}
}

func (l *Logger) Output(calldepth int, s string) error {
	now := time.Now()
	var file string
	var line int
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.flag&(Lshortfile|Llongfile) != 0 {
		l.mu.Unlock()
		var ok bool
		_, file, line, ok = runtime.Caller(calldepth)
		if !ok {
			file = "???"
			line = 0
		}
		l.mu.Lock()
	}
	l.buf = l.buf[:0]
	l.formatHeader(now, file, line)
	l.buf = append(l.buf, s...)
	if len(s) == 0 || s[len(s)-1] != '\n' {
		l.buf = append(l.buf, '\n')
	}
	_, err := l.out.Write(l.buf)
	return err
}

func (l *Logger) Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	// no need for locking because integer access is atomic
	if l.flag&Lnopanic == 0 {
		l.Output(2, s)
	}
	panic(s)
}

func Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	// no need for locking because integer access is atomic
	if std.flag&Lnopanic == 0 {
		std.Output(2, s)
	}
	panic(s)
}

func (l *Logger) Panicf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	if l.flag&Lnopanic == 0 {
		l.Output(2, s)
	}
	panic(s)
}

func Panicf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	if std.flag&Lnopanic == 0 {
		std.Output(2, s)
	}
	panic(s)
}

func (l *Logger) Panicln(v ...interface{}) {
	s := fmt.Sprintln(v...)
	if l.flag&Lnopanic == 0 {
		l.Output(2, s)
	}
	panic(s)
}

func Panicln(v ...interface{}) {
	s := fmt.Sprintln(v...)
	if std.flag&Lnopanic == 0 {
		std.Output(2, s)
	}
	panic(s)
}

func (l *Logger) Fatal(v ...interface{}) {
	if l.flag&Lnofatal == 0 {
		l.Output(2, fmt.Sprint(v...))
	}
	os.Exit(1)
}

func Fatal(v ...interface{}) {
	if std.flag&Lnofatal == 0 {
		std.Output(2, fmt.Sprint(v...))
	}
	os.Exit(1)
}

func (l *Logger) Fatalf(format string, v ...interface{}) {
	if l.flag&Lnofatal == 0 {
		l.Output(2, fmt.Sprintf(format, v...))
	}
	os.Exit(1)
}

func Fatalf(format string, v ...interface{}) {
	if std.flag&Lnofatal == 0 {
		std.Output(2, fmt.Sprintf(format, v...))
	}
	os.Exit(1)
}

func (l *Logger) Fatalln(format string, v ...interface{}) {
	if l.flag&Lnofatal == 0 {
		l.Output(2, fmt.Sprintln(v...))
	}
	os.Exit(1)
}

func Fatalln(format string, v ...interface{}) {
	if std.flag&Lnofatal == 0 {
		std.Output(2, fmt.Sprintln(v...))
	}
	os.Exit(1)
}

func (l *Logger) Warn(v ...interface{}) {
	if l.flag&Lnowarn == 0 {
		l.Output(2, fmt.Sprint(v...))
	}
}

func Warn(v ...interface{}) {
	if std.flag&Lnowarn == 0 {
		std.Output(2, fmt.Sprint(v...))
	}
}

func (l *Logger) Warnf(format string, v ...interface{}) {
	if l.flag&Lnowarn == 0 {
		l.Output(2, fmt.Sprintf(format, v...))
	}
}

func Warnf(format string, v ...interface{}) {
	if std.flag&Lnowarn == 0 {
		std.Output(2, fmt.Sprintf(format, v...))
	}
}

func (l *Logger) Warnln(v ...interface{}) {
	if l.flag&Lnowarn == 0 {
		l.Output(2, fmt.Sprintln(v...))
	}
}

func Warnln(v ...interface{}) {
	if std.flag&Lnowarn == 0 {
		std.Output(2, fmt.Sprintln(v...))
	}
}

func (l *Logger) Print(v ...interface{}) {
	if l.flag&Lnoprint == 0 {
		l.Output(2, fmt.Sprint(v...))
	}
}

func Print(v ...interface{}) {
	if std.flag&Lnoprint == 0 {
		std.Output(2, fmt.Sprint(v...))
	}
}

func (l *Logger) Printf(format string, v ...interface{}) {
	if l.flag&Lnoprint == 0 {
		l.Output(2, fmt.Sprintf(format, v...))
	}
}

func Printf(format string, v ...interface{}) {
	if std.flag&Lnoprint == 0 {
		std.Output(2, fmt.Sprintf(format, v...))
	}
}

func (l *Logger) Println(v ...interface{}) {
	if l.flag&Lnoprint == 0 {
		l.Output(2, fmt.Sprintln(v...))
	}
}

func Println(v ...interface{}) {
	if std.flag&Lnoprint == 0 {
		std.Output(2, fmt.Sprintln(v...))
	}
}

func (l *Logger) Debug(v ...interface{}) {
	if l.flag&Lnodebug == 0 {
		l.Output(2, fmt.Sprint(v...))
	}
}

func Debug(v ...interface{}) {
	if std.flag&Lnodebug == 0 {
		std.Output(2, fmt.Sprint(v...))
	}
}

func (l *Logger) Debugf(format string, v ...interface{}) {
	if l.flag&Lnodebug == 0 {
		l.Output(2, fmt.Sprintf(format, v...))
	}
}

func Debugf(format string, v ...interface{}) {
	if std.flag&Lnodebug == 0 {
		std.Output(2, fmt.Sprintf(format, v...))
	}
}

func (l *Logger) Debugln(v ...interface{}) {
	if l.flag&Lnodebug == 0 {
		l.Output(2, fmt.Sprintln(v...))
	}
}

func Debugln(v ...interface{}) {
	if std.flag&Lnodebug == 0 {
		std.Output(2, fmt.Sprintln(v...))
	}
}

func (l *Logger) Flags() int {
	// access to an int int is always atomic
	return l.flag
}

func Flags() int {
	// access to an int int is always atomic
	return std.flag
}

func (l *Logger) SetFlags(flag int) {
	l.flag = flag
}

func SetFlags(flag int) {
	std.flag = flag
}

func (l *Logger) Prefix() string {
	l.mu.Lock()
	defer l.mu.Unlock()
	return l.prefix
}

func Prefix() string {
	return std.Prefix()
}

func (l *Logger) SetPrefix(prefix string) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.prefix = prefix
}

func SetPrefix(prefix string) {
	std.SetPrefix(prefix)
}

func (l *Logger) SetOutput(w io.Writer) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.out = w
}

func SetOutput(w io.Writer) {
	std.SetOutput(w)
}
<commit_msg>xlog: added documentation comment and serialized all access to l.flag.<commit_after>// Package xlog provides a simple logging package that allows to disable
// certain message categories. It defines a type, Logger, with multiple
// methods for formatting output. The package has also a predefined
// 'standard' Logger accessible through helper function Print[f|ln],
// Fatal[f|ln], Panic[f|ln], Warn[f|ln], Print[f|ln] and Debug[f|ln]
// that are easier to use then creating a Logger manually. That logger
// writes to standard error and prints the date and time of each logged
// message, which can be configured using the function SetFlags.
//
// The Fatal functions call os.Exit(1) after the message is output
// unless not suppressed by the flags. The Panic functions call panic
// after the writing the log message unless suppressed.
package xlog

import (
	"fmt"
	"io"
	"os"
	"runtime"
	"sync"
	"time"
)

// The flags define what information is prefixed to each log entry
// generated by the Logger. The Lno* versions allow the suppression of
// specific output. The bits are or'ed together to control what will be
// printed. There is no control over the order of the items printed and
// the format. The full format is:
//
//   2009-01-23 01:23:23.123123 /a/b/c/d.go:23: message
//
const (
	Ldate         = 1 << iota     // the date: 2009-01-23
	Ltime                         // the time: 01:23:23
	Lmicroseconds                 // microsecond resolution: 01:23:23.123123
	Llongfile                     // full file name and line number: /a/b/c/d.go:23
	Lshortfile                    // final file name element and line number: d.go:23
	Lnopanic                      // suppresses output from Panic[f|ln] but not the panic call
	Lnofatal                      // suppresses output from Fatal[f|ln] but not the exit
	Lnowarn                       // suppresses output from Warn[f|ln]
	Lnoprint                      // suppresses output from Print[f|ln]
	Lnodebug                      // suppresses output from Debug[f|ln]
	Lstdflags     = Ldate | Ltime // initial values for the standard logger
)

// A Logger represents an active logging object that generates lines of
// output to an io.Writer. Each logging operation if not suppressed
// makes a single call to the Writer's Write method. A Logger can be
// used simultaneously from multiple goroutines; it guarantees to
// serialize access to the Writer.
type Logger struct {
	mu sync.Mutex // ensures atomic writes; and protects the following
	// fields
	prefix string    // prefix to write at beginning of each line
	flag   int       // properties
	out    io.Writer // destination for output
	buf    []byte    // for accumulating text to write
}

// New creates a new Logger. The out argument sets the destination to
// which the log output will be written. The prefix appears at the
// beginning of each log line. The flag argument defines the logging
// properties.
func New(out io.Writer, prefix string, flag int) *Logger {
	return &Logger{out: out, prefix: prefix, flag: flag}
}

// std is the standard logger used by the package scope functions.
var std = New(os.Stderr, "", Lstdflags)

// itoa converts the integer to ASCII. A negative widths will avoid
// zero-padding. The function supports only non-negative integers.
func itoa(buf *[]byte, i int, wid int) {
	var u = uint(i)
	if u == 0 && wid <= 1 {
		*buf = append(*buf, '0')
		return
	}
	var b [32]byte
	bp := len(b)
	for ; u > 0 || wid > 0; u /= 10 {
		bp--
		wid--
		b[bp] = byte(u%10) + '0'
	}
	*buf = append(*buf, b[bp:]...)
}

// formatHeader puts the header into the buf field of the buffer.
func (l *Logger) formatHeader(t time.Time, file string, line int) {
	l.buf = append(l.buf, l.prefix...)
	if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
		if l.flag&Ldate != 0 {
			year, month, day := t.Date()
			itoa(&l.buf, year, 4)
			l.buf = append(l.buf, '-')
			itoa(&l.buf, int(month), 2)
			l.buf = append(l.buf, '-')
			itoa(&l.buf, day, 2)
			l.buf = append(l.buf, ' ')
		}
		if l.flag&(Ltime|Lmicroseconds) != 0 {
			hour, min, sec := t.Clock()
			itoa(&l.buf, hour, 2)
			l.buf = append(l.buf, ':')
			itoa(&l.buf, min, 2)
			l.buf = append(l.buf, ':')
			itoa(&l.buf, sec, 2)
			if l.flag&Lmicroseconds != 0 {
				l.buf = append(l.buf, '.')
				itoa(&l.buf, t.Nanosecond()/1e3, 6)
			}
			l.buf = append(l.buf, ' ')
		}
	}
	if l.flag&(Lshortfile|Llongfile) != 0 {
		if l.flag&Lshortfile != 0 {
			short := file
			for i := len(file) - 1; i > 0; i-- {
				if file[i] == '/' {
					short = file[i+1:]
					break
				}
			}
			file = short
		}
		l.buf = append(l.buf, file...)
		l.buf = append(l.buf, ':')
		itoa(&l.buf, line, -1)
		l.buf = append(l.buf, ": "...)
	}
}

func (l *Logger) output(calldepth int, now time.Time, s string) error {
	var file string
	var line int
	if l.flag&(Lshortfile|Llongfile) != 0 {
		l.mu.Unlock()
		var ok bool
		_, file, line, ok = runtime.Caller(calldepth)
		if !ok {
			file = "???"
			line = 0
		}
		l.mu.Lock()
	}
	l.buf = l.buf[:0]
	l.formatHeader(now, file, line)
	l.buf = append(l.buf, s...)
	if len(s) == 0 || s[len(s)-1] != '\n' {
		l.buf = append(l.buf, '\n')
	}
	_, err := l.out.Write(l.buf)
	return err
}

// Output writes the string s with the header controlled by the flags to
// the l.out writer. A newline will be appended if s doesn't end in a
// newline. Calldepth is used to recover the PC, although all current
// calls of Output use the call depth 2. Access to the function is serialized.
func (l *Logger) Output(calldepth, noflag int, v ...interface{}) error {
	now := time.Now()
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.flag&noflag != 0 {
		return nil
	}
	s := fmt.Sprint(v...)
	return l.output(calldepth+1, now, s)
}

// Outputf works like output but formats the output like Printf.
func (l *Logger) Outputf(calldepth int, noflag int, format string, v ...interface{}) error {
	now := time.Now()
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.flag&noflag != 0 {
		return nil
	}
	s := fmt.Sprintf(format, v...)
	return l.output(calldepth+1, now, s)
}

// Outputln works like output but formats the output like Println.
func (l *Logger) Outputln(calldepth int, noflag int, v ...interface{}) error {
	now := time.Now()
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.flag&noflag != 0 {
		return nil
	}
	s := fmt.Sprintln(v...)
	return l.output(calldepth+1, now, s)
}

// Panic prints the message like Print and calls panic. The printing
// might be suppressed by the flag Lnopanic.
func (l *Logger) Panic(v ...interface{}) {
	l.Output(2, Lnopanic, v...)
	s := fmt.Sprint(v...)
	panic(s)
}

// Panic prints the message like Print and calls panic. The printing
// might be suppressed by the flag Lnopanic.
func Panic(v ...interface{}) {
	std.Output(2, Lnopanic, v...)
	s := fmt.Sprint(v...)
	panic(s)
}

// Panicf prints the message like Printf and calls panic. The printing
// might be suppressed by the flag Lnopanic.
func (l *Logger) Panicf(format string, v ...interface{}) {
	l.Outputf(2, Lnopanic, format, v...)
	s := fmt.Sprintf(format, v...)
	panic(s)
}

// Panicf prints the message like Printf and calls panic. The printing
// might be suppressed by the flag Lnopanic.
func Panicf(format string, v ...interface{}) {
	std.Outputf(2, Lnopanic, format, v...)
	s := fmt.Sprintf(format, v...)
	panic(s)
}

// Panicln prints the message like Println and calls panic. The printing
// might be suppressed by the flag Lnopanic.
func (l *Logger) Panicln(v ...interface{}) {
	l.Outputln(2, Lnopanic, v...)
	s := fmt.Sprintln(v...)
	panic(s)
}

// Panicln prints the message like Println and calls panic. The printing
// might be suppressed by the flag Lnopanic.
func Panicln(v ...interface{}) {
	std.Outputln(2, Lnopanic, v...)
	s := fmt.Sprintln(v...)
	panic(s)
}

// Fatal prints the message like Print and calls os.Exit(1). The
// printing might be suppressed by the flag Lnofatal.
func (l *Logger) Fatal(v ...interface{}) {
	l.Output(2, Lnofatal, v...)
	os.Exit(1)
}

// Fatal prints the message like Print and calls os.Exit(1). The
// printing might be suppressed by the flag Lnofatal.
func Fatal(v ...interface{}) {
	std.Output(2, Lnofatal, v...)
	os.Exit(1)
}

// Fatalf prints the message like Printf and calls os.Exit(1). The
// printing might be suppressed by the flag Lnofatal.
func (l *Logger) Fatalf(format string, v ...interface{}) {
	l.Outputf(2, Lnofatal, format, v...)
	os.Exit(1)
}

// Fatalf prints the message like Printf and calls os.Exit(1). The
// printing might be suppressed by the flag Lnofatal.
func Fatalf(format string, v ...interface{}) {
	std.Outputf(2, Lnofatal, format, v...)
	os.Exit(1)
}

// Fatalln prints the message like Println and calls os.Exit(1). The
// printing might be suppressed by the flag Lnofatal.
func (l *Logger) Fatalln(format string, v ...interface{}) {
	l.Outputln(2, Lnofatal, v...)
	os.Exit(1)
}

// Fatalln prints the message like Println and calls os.Exit(1). The
// printing might be suppressed by the flag Lnofatal.
func Fatalln(format string, v ...interface{}) {
	std.Outputln(2, Lnofatal, v...)
	os.Exit(1)
}

// Warn prints the message like Print. The printing might be suppressed
// by the flag Lnowarn.
func (l *Logger) Warn(v ...interface{}) {
	l.Output(2, Lnowarn, v...)
}

// Warn prints the message like Print. The printing might be suppressed
// by the flag Lnowarn.
func Warn(v ...interface{}) {
	std.Output(2, Lnowarn, v...)
}

// Warnf prints the message like Printf. The printing might be suppressed
// by the flag Lnowarn.
func (l *Logger) Warnf(format string, v ...interface{}) {
	l.Outputf(2, Lnowarn, format, v...)
}

// Warnf prints the message like Printf. The printing might be suppressed
// by the flag Lnowarn.
func Warnf(format string, v ...interface{}) {
	std.Outputf(2, Lnowarn, format, v...)
}

// Warnln prints the message like Println. The printing might be suppressed
// by the flag Lnowarn.
func (l *Logger) Warnln(v ...interface{}) {
	l.Outputln(2, Lnowarn, v...)
}

// Warnln prints the message like Println. The printing might be suppressed
// by the flag Lnowarn.
func Warnln(v ...interface{}) {
	std.Outputln(2, Lnowarn, v...)
}

// Print prints the message like fmt.Print. The printing might be suppressed
// by the flag Lnoprint.
func (l *Logger) Print(v ...interface{}) {
	l.Output(2, Lnoprint, v...)
}

// Print prints the message like fmt.Print. The printing might be suppressed
// by the flag Lnoprint.
func Print(v ...interface{}) {
	std.Output(2, Lnoprint, v...)
}

// Printf prints the message like fmt.Printf. The printing might be suppressed
// by the flag Lnoprint.
func (l *Logger) Printf(format string, v ...interface{}) {
	l.Outputf(2, Lnoprint, format, v...)
}

// Printf prints the message like fmt.Printf. The printing might be suppressed
// by the flag Lnoprint.
func Printf(format string, v ...interface{}) {
	std.Outputf(2, Lnoprint, format, v...)
}

// Println prints the message like fmt.Println. The printing might be
// suppressed by the flag Lnoprint.
func (l *Logger) Println(v ...interface{}) {
	l.Outputln(2, Lnoprint, v...)
}

// Println prints the message like fmt.Println. The printing might be
// suppressed by the flag Lnoprint.
func Println(v ...interface{}) {
	std.Outputln(2, Lnoprint, v...)
}

// Debug prints the message like Print. The printing might be suppressed
// by the flag Lnodebug.
func (l *Logger) Debug(v ...interface{}) {
	l.Output(2, Lnodebug, v...)
}

// Debug prints the message like Print. The printing might be suppressed
// by the flag Lnodebug.
func Debug(v ...interface{}) {
	std.Output(2, Lnodebug, v...)
}

// Debugf prints the message like Printf. The printing might be suppressed
// by the flag Lnodebug.
func (l *Logger) Debugf(format string, v ...interface{}) {
	l.Outputf(2, Lnodebug, format, v...)
}

// Debugf prints the message like Printf. The printing might be suppressed
// by the flag Lnodebug.
func Debugf(format string, v ...interface{}) {
	std.Outputf(2, Lnodebug, format, v...)
}

// Debugln prints the message like Println. The printing might be suppressed
// by the flag Lnodebug.
func (l *Logger) Debugln(v ...interface{}) {
	l.Outputln(2, Lnodebug, v...)
}

// Debugln prints the message like Println. The printing might be suppressed
// by the flag Lnodebug.
func Debugln(v ...interface{}) {
	std.Outputln(2, Lnodebug, v...)
}

// Flags returns the current flags used by the logger.
func (l *Logger) Flags() int {
	l.mu.Lock()
	defer l.mu.Unlock()
	return l.flag
}

// Flags returns the current flags used by the standard logger.
func Flags() int {
	return std.Flags()
}

// SetFlags sets the flags of the logger.
func (l *Logger) SetFlags(flag int) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.flag = flag
}

// SetFlags sets the flags for the standard logger.
func SetFlags(flag int) {
	std.SetFlags(flag)
}

// Prefix returns the prefix used by the logger.
func (l *Logger) Prefix() string {
	l.mu.Lock()
	defer l.mu.Unlock()
	return l.prefix
}

// Prefix returns the prefix used by the standard logger of the package.
func Prefix() string {
	return std.Prefix()
}

// SetPrefix sets the prefix for the logger.
func (l *Logger) SetPrefix(prefix string) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.prefix = prefix
}

// SetPrefix sets the prefix of the standard logger of the package.
func SetPrefix(prefix string) {
	std.SetPrefix(prefix)
}

// SetOutput sets the output of the logger.
func (l *Logger) SetOutput(w io.Writer) {
	l.mu.Lock()
	defer l.mu.Unlock()
	l.out = w
}

// SetOutput sets the output for the standard logger of the package.
func SetOutput(w io.Writer) {
	std.SetOutput(w)
}
<|endoftext|>


<commit_before>package polyline

import (
	"fmt"
	"strings"
)

type InvalidCharacterError struct {
	pos int
	char byte
}

func (de InvalidCharacterError) Error() string {
	return fmt.Sprintf("invalid character %q at position %d", de.char, de.pos)
}

type UnterminatedError struct {
}

func (ie UnterminatedError) Error() string {
	return "unterminated string"
}

func DecodeUints(s string) ([]uint, error) {
	xs := make([]uint, 0)
	var x, shift uint
	for i, c := range []byte(s) {
		if c < 95 {
			if c < 63 {
				return nil, &InvalidCharacterError{i, c}
			} else {
				xs = append(xs, x+(uint(c)-63)<<shift)
				x = 0
				shift = 0
			}
		} else if c < 127 {
			x += (uint(c) - 95) << shift
			shift += 5
		} else {
			return nil, &InvalidCharacterError{i, c}
		}
	}
	if shift != 0 {
		return nil, &UnterminatedError{}
	}
	return xs, nil
}

func DecodeInts(s string) ([]int, error) {
	xs, err := DecodeUints(s)
	if err != nil {
		return nil, err
	}
	ys := make([]int, len(xs))
	for i, u := range xs {
		if u&1 == 0 {
			ys[i] = int(u >> 1)
		} else {
			ys[i] = -int((u + 1) >> 1)
		}
	}
	return ys, nil
}

func Decode(s string, dim int) ([]float64, error) {
	xs, err := DecodeInts(s)
	if err != nil {
		return nil, err
	}
	ys := make([]float64, len(xs))
	for j, i := range xs {
		ys[j] = float64(i) / 1e5
		if j >= dim {
			ys[j] += ys[j-dim]
		}
	}
	return ys, nil
}

func EncodeUint(x uint) string {
	bs := make([]byte, 0, 7)
	for ; x >= 32; x >>= 5 {
		bs = append(bs, byte((x&31)+95))
	}
	bs = append(bs, byte(x+63))
	return string(bs)
}

func EncodeUints(xs []uint) string {
	ss := make([]string, len(xs))
	for i, x := range xs {
		ss[i] = EncodeUint(x)
	}
	return strings.Join(ss, "")
}

func EncodeInt(x int) string {
	y := uint(x) << 1
	if x < 0 {
		y = ^y
	}
	return EncodeUint(y)
}

func EncodeInts(xs []int) string {
	ss := make([]string, len(xs))
	for i, x := range xs {
		ss[i] = EncodeInt(x)
	}
	return strings.Join(ss, "")
}

func Encode(xs []float64, dim int) string {
	n := len(xs)
	ys := make([]int, n)
	for i := 0; i < n; i++ {
		ys[i] = int(1e5 * xs[i])
	}
	for i := n - 1; i >= dim; i-- {
		ys[i] -= ys[i-dim]
	}
	return EncodeInts(ys)
}
<commit_msg>Add documentation<commit_after>// Package polyline provides functions for Google Maps' Encoded Polyline Algorithm Format.
// See https://developers.google.com/maps/documentation/utilities/polylinealgorithm
package polyline

import (
	"fmt"
	"strings"
)

// InvalidCharacterError is returned when an invalid character is encountered.
type InvalidCharacterError struct {
	pos int
	char byte
}

func (de InvalidCharacterError) Error() string {
	return fmt.Sprintf("invalid character %q at position %d", de.char, de.pos)
}

// UnterminatedError is returned when the string is unterminated.
type UnterminatedError struct {
}

func (ie UnterminatedError) Error() string {
	return "unterminated string"
}

// DecodeUnits decodes a slice of uints from a string.
func DecodeUints(s string) ([]uint, error) {
	xs := make([]uint, 0)
	var x, shift uint
	for i, c := range []byte(s) {
		if c < 95 {
			if c < 63 {
				return nil, &InvalidCharacterError{i, c}
			} else {
				xs = append(xs, x+(uint(c)-63)<<shift)
				x = 0
				shift = 0
			}
		} else if c < 127 {
			x += (uint(c) - 95) << shift
			shift += 5
		} else {
			return nil, &InvalidCharacterError{i, c}
		}
	}
	if shift != 0 {
		return nil, &UnterminatedError{}
	}
	return xs, nil
}

// DecodeInts decodes a slice of ints from a string.
func DecodeInts(s string) ([]int, error) {
	xs, err := DecodeUints(s)
	if err != nil {
		return nil, err
	}
	ys := make([]int, len(xs))
	for i, u := range xs {
		if u&1 == 0 {
			ys[i] = int(u >> 1)
		} else {
			ys[i] = -int((u + 1) >> 1)
		}
	}
	return ys, nil
}

// Decode decodes a polyline of dimension dim from a string.
// The polyline is returned as a flat slice, e.g.
//     []float64{lat1, lng1, lat2, lng2...}
// The dimension dim should normally be 2.
func Decode(s string, dim int) ([]float64, error) {
	xs, err := DecodeInts(s)
	if err != nil {
		return nil, err
	}
	ys := make([]float64, len(xs))
	for j, i := range xs {
		ys[j] = float64(i) / 1e5
		if j >= dim {
			ys[j] += ys[j-dim]
		}
	}
	return ys, nil
}

// EncodeUnit encodes a single uint as a string.
func EncodeUint(x uint) string {
	bs := make([]byte, 0, 7)
	for ; x >= 32; x >>= 5 {
		bs = append(bs, byte((x&31)+95))
	}
	bs = append(bs, byte(x+63))
	return string(bs)
}

// EncodeUnits encodes a slice of uints as a string.
func EncodeUints(xs []uint) string {
	ss := make([]string, len(xs))
	for i, x := range xs {
		ss[i] = EncodeUint(x)
	}
	return strings.Join(ss, "")
}

// EncodeInt encodes a single int as a string.
func EncodeInt(x int) string {
	y := uint(x) << 1
	if x < 0 {
		y = ^y
	}
	return EncodeUint(y)
}

// EncodeInts encodes a slice of ints as a string.
func EncodeInts(xs []int) string {
	ss := make([]string, len(xs))
	for i, x := range xs {
		ss[i] = EncodeInt(x)
	}
	return strings.Join(ss, "")
}

// Encode encodes a polyling as a string.
// The polyline must be structured as a flat slice, e.g.
//     []float64{lat1, lng1, lat2, lng2...}
// The dimension dim should normally be 2.
func Encode(xs []float64, dim int) string {
	n := len(xs)
	ys := make([]int, n)
	for i := 0; i < n; i++ {
		ys[i] = int(1e5 * xs[i])
	}
	for i := n - 1; i >= dim; i-- {
		ys[i] -= ys[i-dim]
	}
	return EncodeInts(ys)
}
<|endoftext|>


<commit_before>// The author disclaims copyright to this source code.
package yacr

import (
	"os"
	"reflect"
	"strings"
	"testing"
)

func makeReader(s string, quoted bool) *Reader {
	return NewReaderString(s, ',', quoted)
}

func checkValueCount(t *testing.T, expected int, values [][]byte) {
	if len(values) != expected {
		t.Errorf("Expected %d value(s), but got %d (%#v)", expected, len(values), values)
	}
}

func checkNoError(t *testing.T, e os.Error) {
	if e != nil {
		t.Error(e)
	}
}

func checkEquals(t *testing.T, expected, actual [][]byte) {
	if !reflect.DeepEqual(expected, actual) {
		t.Errorf("Expected %#v, got %#v", expected, actual)
	}
}

func TestSingleValue(t *testing.T) {
	r := makeReader("Foo", true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 1, values)
	values, e = r.ReadRow()
	if values != nil {
		t.Errorf("No value expected, but got %#v", values)
	}
	if e == nil {
		t.Error("EOF expected")
	}
	if e != os.EOF {
		t.Error(e)
	}
}

func TestTwoValues(t *testing.T) {
	r := makeReader("Foo,Bar", true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 2, values)
	expected := [][]byte{[]byte("Foo"), []byte("Bar")}
	checkEquals(t, expected, values)
}

func TestTwoLines(t *testing.T) {
	row1 := strings.Repeat("1,2,3,4,5,6,7,8,9,10,", 5)
	row2 := strings.Repeat("a,b,c,d,e,f,g,h,i,j,", 3)
	content := strings.Join([]string{row1, row2}, "\n")
	r := makeReader(content, true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 51, values)
	values, e = r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 31, values)
}

func TestLongLine(t *testing.T) {
	content := strings.Repeat("1,2,3,4,5,6,7,8,9,10,", 200)
	r := makeReader(content, true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 2001, values)
}

func TestQuotedLine(t *testing.T) {
	r:= makeReader("\"a\",b,\"c,d\"", true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 3, values)
	expected := [][]byte{[]byte("a"), []byte("b"), []byte("c,d")}
	checkEquals(t, expected, values)
}

func TestEscapedQuoteLine(t *testing.T) {
	r:= makeReader("\"a\",b,\"c\"\"d\"", true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 3, values)
	expected := [][]byte{[]byte("a"), []byte("b"), []byte("c\"d")}
	checkEquals(t, expected, values)
}

func BenchmarkParsing(b *testing.B) {
	benchmarkParsing(b, "aaaaaaaa,b b b b b b b,cc cc cc cc cc, ddddd ddd\n", false)
}
func BenchmarkQuotedParsing(b *testing.B) {
	benchmarkParsing(b, "aaaaaaaa,b b b b b b b,\"cc cc cc,cc\",cc, ddddd ddd\n", true)
}

func benchmarkParsing(b *testing.B, s string, quoted bool) {
	b.StopTimer()
	str := strings.Repeat(s, 2000)
	b.SetBytes(int64(len(str)))
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		r := makeReader(str, quoted)
		nb := 0
		for {
			_, e := r.ReadRow()
			if e == os.EOF {
				break
			}
			if e != nil {
				panic(e.String())
			}
			nb++
		}
		if nb != 2000 {
			panic("wrong # rows")
		}
	}
}
<commit_msg>Formats.<commit_after>// The author disclaims copyright to this source code.
package yacr

import (
	"os"
	"reflect"
	"strings"
	"testing"
)

func makeReader(s string, quoted bool) *Reader {
	return NewReaderString(s, ',', quoted)
}

func checkValueCount(t *testing.T, expected int, values [][]byte) {
	if len(values) != expected {
		t.Errorf("Expected %d value(s), but got %d (%#v)", expected, len(values), values)
	}
}

func checkNoError(t *testing.T, e os.Error) {
	if e != nil {
		t.Error(e)
	}
}

func checkEquals(t *testing.T, expected, actual [][]byte) {
	if !reflect.DeepEqual(expected, actual) {
		t.Errorf("Expected %#v, got %#v", expected, actual)
	}
}

func TestSingleValue(t *testing.T) {
	r := makeReader("Foo", true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 1, values)
	values, e = r.ReadRow()
	if values != nil {
		t.Errorf("No value expected, but got %#v", values)
	}
	if e == nil {
		t.Error("EOF expected")
	}
	if e != os.EOF {
		t.Error(e)
	}
}

func TestTwoValues(t *testing.T) {
	r := makeReader("Foo,Bar", true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 2, values)
	expected := [][]byte{[]byte("Foo"), []byte("Bar")}
	checkEquals(t, expected, values)
}

func TestTwoLines(t *testing.T) {
	row1 := strings.Repeat("1,2,3,4,5,6,7,8,9,10,", 5)
	row2 := strings.Repeat("a,b,c,d,e,f,g,h,i,j,", 3)
	content := strings.Join([]string{row1, row2}, "\n")
	r := makeReader(content, true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 51, values)
	values, e = r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 31, values)
}

func TestLongLine(t *testing.T) {
	content := strings.Repeat("1,2,3,4,5,6,7,8,9,10,", 200)
	r := makeReader(content, true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 2001, values)
}

func TestQuotedLine(t *testing.T) {
	r := makeReader("\"a\",b,\"c,d\"", true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 3, values)
	expected := [][]byte{[]byte("a"), []byte("b"), []byte("c,d")}
	checkEquals(t, expected, values)
}

func TestEscapedQuoteLine(t *testing.T) {
	r := makeReader("\"a\",b,\"c\"\"d\"", true)
	values, e := r.ReadRow()
	checkNoError(t, e)
	checkValueCount(t, 3, values)
	expected := [][]byte{[]byte("a"), []byte("b"), []byte("c\"d")}
	checkEquals(t, expected, values)
}

func BenchmarkParsing(b *testing.B) {
	benchmarkParsing(b, "aaaaaaaa,b b b b b b b,cc cc cc cc cc, ddddd ddd\n", false)
}
func BenchmarkQuotedParsing(b *testing.B) {
	benchmarkParsing(b, "aaaaaaaa,b b b b b b b,\"cc cc cc,cc\",cc, ddddd ddd\n", true)
}

func benchmarkParsing(b *testing.B, s string, quoted bool) {
	b.StopTimer()
	str := strings.Repeat(s, 2000)
	b.SetBytes(int64(len(str)))
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		r := makeReader(str, quoted)
		nb := 0
		for {
			_, e := r.ReadRow()
			if e == os.EOF {
				break
			}
			if e != nil {
				panic(e.String())
			}
			nb++
		}
		if nb != 2000 {
			panic("wrong # rows")
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"database/sql"
	"fmt"
	"github.com/moovweb/gokogiri"
	"github.com/moovweb/gokogiri/xml"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

type RssCloudRequest struct {
	RequestMethodName string
	MethodName        string
	Host              string
	Port              uint16
	Path              string
	IsXmlRpc          bool
	FeedURL           string
}

func (r *RssCloudRequest) Unpack(doc *xml.XmlDocument) error {
	root := doc.Root()

	requestMethods, err := root.Search("methodName/text()")
	if err != nil {
		return err
	}
	if len(requestMethods) != 1 {
		return fmt.Errorf("Could not find cloud request's request method")
	}
	r.RequestMethodName = requestMethods[0].Content()

	// Would be nice to get the child nodes but xpath will give elements only, not text nodes.
	params, err := root.Search("params/param/value")
	if err != nil {
		return err
	}
	if len(params) != 5 {
		return fmt.Errorf("Could not unpack cloud request with %d params", len(params))
	}

	node := params[0].FirstChild()
	if node.NodeType() != xml.XML_TEXT_NODE {
		return fmt.Errorf("Could not unpack cloud request with first param not text")
	}
	r.MethodName = node.Content()

	node = params[1].FirstChild()
	if node.NodeType() != xml.XML_ELEMENT_NODE {
		return fmt.Errorf("Could not unpack cloud request with second param not an element")
	}
	if !(node.Name() == "i4" || node.Name() == "int") {
		return fmt.Errorf("Could not unpack cloud request with second param a %s element, not int or i4", node.Name())
	}
	node = node.FirstChild()
	if node.NodeType() != xml.XML_TEXT_NODE {
		return fmt.Errorf("Could not unpack cloud request with second param not containing text")
	}
	port, err := strconv.Atoi(node.Content())
	if err != nil {
		return err
	}
	r.Port = uint16(port)

	node = params[2].FirstChild()
	if node.NodeType() != xml.XML_TEXT_NODE {
		return fmt.Errorf("Could not unpack cloud request with third param not text")
	}
	r.Path = node.Content()

	node = params[3].FirstChild()
	if node.NodeType() != xml.XML_TEXT_NODE {
		return fmt.Errorf("Could not unpack cloud request with fourth param not text")
	}
	r.IsXmlRpc = strings.TrimSpace(node.Content()) == "xml-rpc"

	node = params[4].FirstChild()
	if node.NodeType() != xml.XML_ELEMENT_NODE {
		return fmt.Errorf("Could not unpack cloud request with fifth param not an element")
	}
	if node.Name() != "array" {
		return fmt.Errorf("Could not unpack cloud request with fifth param a %s element, not array", node.Name())
	}
	params, err = node.Search("data/value/text()")
	if err != nil {
		return err
	}
	if len(params) < 1 {
		return fmt.Errorf("Could not unpack cloud request with fifth param containing no data values")
	}
	r.FeedURL = params[0].Content()

	logr.Debugln("Unpacked cloud request!")
	return nil
}

type RssCloud struct {
	Id              uint64
	URL             string
	Method          string
	SubscribedUntil time.Time
	Created         time.Time
}

func NewRssCloud() *RssCloud {
	return &RssCloud{0, "", "", time.Unix(0, 0), time.Now().UTC()}
}

func (r *RssCloud) Notify(feedurl string) {
	logr.Debugln("Building RSS cloud notification for", r.URL)

	body := new(bytes.Buffer)
	body.WriteString(`<?xml version="1.0"?>
		<methodCall>
			<methodName>`)
	body.WriteString(r.Method)
	body.WriteString(`</methodName>
			<params>
				<param>
					<value>`)
	body.WriteString(feedurl)
	body.WriteString(`</value>
				</param>
			</params>
		</methodCall>`)

	resp, err := http.Post(r.URL, "text/xml", body)
	if err != nil {
		logr.Errln("Error posting RSS cloud notification to", r.URL, ":", err.Error())
		return
	}
	// TODO: parse resp and see if it was a fault.
	if resp == nil {
		logr.Errln("Didn't get a response back posting RSS cloud notification", r.URL, "!!!")
		return
	}

	logr.Debugln("Sent RSS cloud notification to", r.URL)
}

func (r *RssCloud) Save() error {
	if r.Id == 0 {
		row := db.QueryRow("INSERT INTO rsscloud (url, method, subscribedUntil, created) VALUES ($1, $2, $3, $4) RETURNING id",
			r.URL, r.Method, r.SubscribedUntil, r.Created)

		var id uint64
		err := row.Scan(&id)
		if err != nil {
			return err
		}
		r.Id = id
	} else {
		_, err := db.Exec("UPDATE rsscloud SET url = $2, method = $3, subscribedUntil = $4, created = $5 WHERE id = $1",
			r.Id, r.URL, r.Method, r.SubscribedUntil, r.Created)
		if err != nil {
			return err
		}
	}
	return nil
}

func RssCloudByURL(url string) (*RssCloud, error) {
	row := db.QueryRow("SELECT id, method, subscribedUntil, created FROM rsscloud WHERE url = $1", url)

	var id uint64
	var method string
	var subscribedUntil time.Time
	var created time.Time
	err := row.Scan(&id, &method, &subscribedUntil, &created)
	if err != nil {
		return nil, err
	}

	rssCloud := &RssCloud{id, url, method, subscribedUntil, created}
	return rssCloud, nil
}

func ActiveRssClouds() ([]*RssCloud, error) {
	rows, err := db.Query("SELECT id, url, method, subscribedUntil, created FROM rsscloud WHERE subscribedUntil > $1", time.Now().UTC())
	if err != nil {
		return nil, err
	}

	clouds := make([]*RssCloud, 0, 10)
	var id uint64
	var url string
	var method string
	var subscribedUntil time.Time
	var created time.Time
	var cloud *RssCloud
	i := 0
	for rows.Next() {
		err = rows.Scan(&id, &url, &method, &subscribedUntil, &created)
		if err != nil {
			return nil, err
		}
		cloud = &RssCloud{id, url, method, subscribedUntil, created}

		if cap(clouds) < i+1 {
			newClouds := make([]*RssCloud, len(clouds), cap(clouds)*2)
			copy(newClouds, clouds)
			clouds = newClouds
		}
		clouds = clouds[0 : i+1]
		clouds[i] = cloud
		i++
	}

	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return clouds, nil
}

func NotifyRssCloud(feedurl string) {
	logr.Debugln("Sending RSS cloud notifications")

	clouds, err := ActiveRssClouds()
	if err != nil {
		logr.Errln("Error finding RSS clouds to notify of feed update:", err.Error())
		return
	}

	for _, cloud := range clouds {
		go cloud.Notify(feedurl)
	}
}

func writeXmlRpcError(w http.ResponseWriter, err error) {
	logr.Errln("Error serving rss cloud request:", err.Error())
	output := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
		<methodResponse>
		<fault>
		  <value>
		     <struct>
		        <member>
		           <name>faultCode</name>
		           <value><int>4</int></value>
		           </member>
		        <member>
		           <name>faultString</name>
		           <value><string>%s</string></value>
		           </member>
		        </struct>
		     </value>
		  </fault>
		</methodResponse>`, err.Error())
	w.Header().Set("Content-Type", "text/xml")
	w.Header().Set("Content-Length", strconv.Itoa(len(output)))
	w.Write([]byte(output))
}

func rssCloud(w http.ResponseWriter, r *http.Request) {
	logr.Debugln("Yay a cloud request!")

	if r.Method != "POST" {
		w.Header().Set("Allow", "POST")
		http.Error(w, "POST is required", http.StatusMethodNotAllowed)
		return
	}

	bodyBytes := make([]byte, r.ContentLength)
	_, err := r.Body.Read(bodyBytes)
	if err != nil {
		logr.Errln("Could not read request body:", err.Error())
		http.Error(w, "Could not read body: "+err.Error(), http.StatusInternalServerError)
		return
	}
	requestDoc, err := gokogiri.ParseXml(bodyBytes)
	if err != nil {
		writeXmlRpcError(w, err)
		return
	}

	request := new(RssCloudRequest)
	err = request.Unpack(requestDoc)
	if err != nil {
		writeXmlRpcError(w, err)
		return
	}

	host, _, err := net.SplitHostPort(r.RemoteAddr)
	request.Host = host

	if request.RequestMethodName != "cloud.notify" {
		writeXmlRpcError(w, fmt.Errorf("Unknown method %s", request.RequestMethodName))
		return
	}

	if !request.IsXmlRpc {
		writeXmlRpcError(w, fmt.Errorf("Only XML-RPC is supported"))
		return
	}
	// TODO: use https as appropriate here? er, does river2 support cloud endpoints on HTTPS?
	if request.FeedURL != fmt.Sprintf("http://%s/rss", r.Host) {
		writeXmlRpcError(w, fmt.Errorf("RSS URL %s is not a feed managed here", request.FeedURL))
		return
	}

	logr.Debugln("Yay, asked to call back to http://", request.Host, ":", request.Port, request.Path,
		"with method", request.MethodName, "!")

	url, _ := url.Parse("/")
	if request.Port == 443 {
		url.Scheme = "https"
	} else {
		url.Scheme = "http"
	}
	if request.Port == 80 || request.Port == 443 {
		url.Host = request.Host
	} else {
		url.Host = net.JoinHostPort(request.Host, strconv.Itoa(int(request.Port)))
	}
	url.Path = request.Path
	urlString := url.String()

	rssCloud, err := RssCloudByURL(urlString)
	if err == sql.ErrNoRows {
		// That's cool.
	} else if err != nil {
		logr.Errln("Error loading rsscloud for URL", urlString, ":", err.Error())
		http.Error(w, "error looking for rsscloud for URL "+urlString, http.StatusInternalServerError)
		return
	}
	if rssCloud == nil {
		rssCloud = NewRssCloud()
		rssCloud.URL = urlString
	}
	rssCloud.Method = request.MethodName
	// Subscribe until 25 hours from now.
	rssCloud.SubscribedUntil = time.Now().Add(time.Duration(25) * time.Hour).UTC()
	err = rssCloud.Save()
	if err != nil {
		logr.Errln("Error saving rsscloud for URL", urlString, ":", err.Error())
		http.Error(w, "error saving rsscloud for URL "+urlString, http.StatusInternalServerError)
		return
	}

	output := `<?xml version="1.0" encoding="UTF-8"?>
		<methodResponse>
			<params>
				<param>
					<value><boolean>1</boolean></value>
				</param>
			</params>
		</methodResponse>`

	w.Header().Set("Content-Type", "text/xml")
	w.Header().Set("Content-Length", strconv.Itoa(len(output)))
	w.Write([]byte(output))
}
<commit_msg>Honor proxied RSS cloud clients<commit_after>package main

import (
	"bytes"
	"database/sql"
	"fmt"
	"github.com/moovweb/gokogiri"
	"github.com/moovweb/gokogiri/xml"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

type RssCloudRequest struct {
	RequestMethodName string
	MethodName        string
	Host              string
	Port              uint16
	Path              string
	IsXmlRpc          bool
	FeedURL           string
}

func (r *RssCloudRequest) Unpack(doc *xml.XmlDocument) error {
	root := doc.Root()

	requestMethods, err := root.Search("methodName/text()")
	if err != nil {
		return err
	}
	if len(requestMethods) != 1 {
		return fmt.Errorf("Could not find cloud request's request method")
	}
	r.RequestMethodName = requestMethods[0].Content()

	// Would be nice to get the child nodes but xpath will give elements only, not text nodes.
	params, err := root.Search("params/param/value")
	if err != nil {
		return err
	}
	if len(params) != 5 {
		return fmt.Errorf("Could not unpack cloud request with %d params", len(params))
	}

	node := params[0].FirstChild()
	if node.NodeType() != xml.XML_TEXT_NODE {
		return fmt.Errorf("Could not unpack cloud request with first param not text")
	}
	r.MethodName = node.Content()

	node = params[1].FirstChild()
	if node.NodeType() != xml.XML_ELEMENT_NODE {
		return fmt.Errorf("Could not unpack cloud request with second param not an element")
	}
	if !(node.Name() == "i4" || node.Name() == "int") {
		return fmt.Errorf("Could not unpack cloud request with second param a %s element, not int or i4", node.Name())
	}
	node = node.FirstChild()
	if node.NodeType() != xml.XML_TEXT_NODE {
		return fmt.Errorf("Could not unpack cloud request with second param not containing text")
	}
	port, err := strconv.Atoi(node.Content())
	if err != nil {
		return err
	}
	r.Port = uint16(port)

	node = params[2].FirstChild()
	if node.NodeType() != xml.XML_TEXT_NODE {
		return fmt.Errorf("Could not unpack cloud request with third param not text")
	}
	r.Path = node.Content()

	node = params[3].FirstChild()
	if node.NodeType() != xml.XML_TEXT_NODE {
		return fmt.Errorf("Could not unpack cloud request with fourth param not text")
	}
	r.IsXmlRpc = strings.TrimSpace(node.Content()) == "xml-rpc"

	node = params[4].FirstChild()
	if node.NodeType() != xml.XML_ELEMENT_NODE {
		return fmt.Errorf("Could not unpack cloud request with fifth param not an element")
	}
	if node.Name() != "array" {
		return fmt.Errorf("Could not unpack cloud request with fifth param a %s element, not array", node.Name())
	}
	params, err = node.Search("data/value/text()")
	if err != nil {
		return err
	}
	if len(params) < 1 {
		return fmt.Errorf("Could not unpack cloud request with fifth param containing no data values")
	}
	r.FeedURL = params[0].Content()

	logr.Debugln("Unpacked cloud request!")
	return nil
}

type RssCloud struct {
	Id              uint64
	URL             string
	Method          string
	SubscribedUntil time.Time
	Created         time.Time
}

func NewRssCloud() *RssCloud {
	return &RssCloud{0, "", "", time.Unix(0, 0), time.Now().UTC()}
}

func (r *RssCloud) Notify(feedurl string) {
	logr.Debugln("Building RSS cloud notification for", r.URL)

	body := new(bytes.Buffer)
	body.WriteString(`<?xml version="1.0"?>
		<methodCall>
			<methodName>`)
	body.WriteString(r.Method)
	body.WriteString(`</methodName>
			<params>
				<param>
					<value>`)
	body.WriteString(feedurl)
	body.WriteString(`</value>
				</param>
			</params>
		</methodCall>`)

	resp, err := http.Post(r.URL, "text/xml", body)
	if err != nil {
		logr.Errln("Error posting RSS cloud notification to", r.URL, ":", err.Error())
		return
	}
	// TODO: parse resp and see if it was a fault.
	if resp == nil {
		logr.Errln("Didn't get a response back posting RSS cloud notification", r.URL, "!!!")
		return
	}

	logr.Debugln("Sent RSS cloud notification to", r.URL)
}

func (r *RssCloud) Save() error {
	if r.Id == 0 {
		row := db.QueryRow("INSERT INTO rsscloud (url, method, subscribedUntil, created) VALUES ($1, $2, $3, $4) RETURNING id",
			r.URL, r.Method, r.SubscribedUntil, r.Created)

		var id uint64
		err := row.Scan(&id)
		if err != nil {
			return err
		}
		r.Id = id
	} else {
		_, err := db.Exec("UPDATE rsscloud SET url = $2, method = $3, subscribedUntil = $4, created = $5 WHERE id = $1",
			r.Id, r.URL, r.Method, r.SubscribedUntil, r.Created)
		if err != nil {
			return err
		}
	}
	return nil
}

func RssCloudByURL(url string) (*RssCloud, error) {
	row := db.QueryRow("SELECT id, method, subscribedUntil, created FROM rsscloud WHERE url = $1", url)

	var id uint64
	var method string
	var subscribedUntil time.Time
	var created time.Time
	err := row.Scan(&id, &method, &subscribedUntil, &created)
	if err != nil {
		return nil, err
	}

	rssCloud := &RssCloud{id, url, method, subscribedUntil, created}
	return rssCloud, nil
}

func ActiveRssClouds() ([]*RssCloud, error) {
	rows, err := db.Query("SELECT id, url, method, subscribedUntil, created FROM rsscloud WHERE subscribedUntil > $1", time.Now().UTC())
	if err != nil {
		return nil, err
	}

	clouds := make([]*RssCloud, 0, 10)
	var id uint64
	var url string
	var method string
	var subscribedUntil time.Time
	var created time.Time
	var cloud *RssCloud
	i := 0
	for rows.Next() {
		err = rows.Scan(&id, &url, &method, &subscribedUntil, &created)
		if err != nil {
			return nil, err
		}
		cloud = &RssCloud{id, url, method, subscribedUntil, created}

		if cap(clouds) < i+1 {
			newClouds := make([]*RssCloud, len(clouds), cap(clouds)*2)
			copy(newClouds, clouds)
			clouds = newClouds
		}
		clouds = clouds[0 : i+1]
		clouds[i] = cloud
		i++
	}

	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return clouds, nil
}

func NotifyRssCloud(feedurl string) {
	logr.Debugln("Sending RSS cloud notifications")

	clouds, err := ActiveRssClouds()
	if err != nil {
		logr.Errln("Error finding RSS clouds to notify of feed update:", err.Error())
		return
	}

	for _, cloud := range clouds {
		go cloud.Notify(feedurl)
	}
}

func writeXmlRpcError(w http.ResponseWriter, err error) {
	logr.Errln("Error serving rss cloud request:", err.Error())
	output := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
		<methodResponse>
		<fault>
		  <value>
		     <struct>
		        <member>
		           <name>faultCode</name>
		           <value><int>4</int></value>
		           </member>
		        <member>
		           <name>faultString</name>
		           <value><string>%s</string></value>
		           </member>
		        </struct>
		     </value>
		  </fault>
		</methodResponse>`, err.Error())
	w.Header().Set("Content-Type", "text/xml")
	w.Header().Set("Content-Length", strconv.Itoa(len(output)))
	w.Write([]byte(output))
}

func rssCloud(w http.ResponseWriter, r *http.Request) {
	logr.Debugln("Yay a cloud request!")

	if r.Method != "POST" {
		w.Header().Set("Allow", "POST")
		http.Error(w, "POST is required", http.StatusMethodNotAllowed)
		return
	}

	bodyBytes := make([]byte, r.ContentLength)
	_, err := r.Body.Read(bodyBytes)
	if err != nil {
		logr.Errln("Could not read request body:", err.Error())
		http.Error(w, "Could not read body: "+err.Error(), http.StatusInternalServerError)
		return
	}
	requestDoc, err := gokogiri.ParseXml(bodyBytes)
	if err != nil {
		writeXmlRpcError(w, err)
		return
	}

	request := new(RssCloudRequest)
	err = request.Unpack(requestDoc)
	if err != nil {
		writeXmlRpcError(w, err)
		return
	}

	hostname := r.Header.Get("X-Forwarded-For")
	if hostname == "" {
		hostname = r.RemoteAddr
	} else {
		hostnames := strings.SplitN(hostname, ",", 2)
		hostname = hostnames[0]
	}
	host, _, err := net.SplitHostPort(hostname)
	request.Host = host

	if request.RequestMethodName != "cloud.notify" {
		writeXmlRpcError(w, fmt.Errorf("Unknown method %s", request.RequestMethodName))
		return
	}

	if !request.IsXmlRpc {
		writeXmlRpcError(w, fmt.Errorf("Only XML-RPC is supported"))
		return
	}
	// TODO: use https as appropriate here? er, does river2 support cloud endpoints on HTTPS?
	if request.FeedURL != fmt.Sprintf("http://%s/rss", r.Host) {
		writeXmlRpcError(w, fmt.Errorf("RSS URL %s is not a feed managed here", request.FeedURL))
		return
	}

	logr.Debugln("Yay, asked to call back to http://", request.Host, ":", request.Port, request.Path,
		"with method", request.MethodName, "!")

	url, _ := url.Parse("/")
	if request.Port == 443 {
		url.Scheme = "https"
	} else {
		url.Scheme = "http"
	}
	if request.Port == 80 || request.Port == 443 {
		url.Host = request.Host
	} else {
		url.Host = net.JoinHostPort(request.Host, strconv.Itoa(int(request.Port)))
	}
	url.Path = request.Path
	urlString := url.String()

	rssCloud, err := RssCloudByURL(urlString)
	if err == sql.ErrNoRows {
		// That's cool.
	} else if err != nil {
		logr.Errln("Error loading rsscloud for URL", urlString, ":", err.Error())
		http.Error(w, "error looking for rsscloud for URL "+urlString, http.StatusInternalServerError)
		return
	}
	if rssCloud == nil {
		rssCloud = NewRssCloud()
		rssCloud.URL = urlString
	}
	rssCloud.Method = request.MethodName
	// Subscribe until 25 hours from now.
	rssCloud.SubscribedUntil = time.Now().Add(time.Duration(25) * time.Hour).UTC()
	err = rssCloud.Save()
	if err != nil {
		logr.Errln("Error saving rsscloud for URL", urlString, ":", err.Error())
		http.Error(w, "error saving rsscloud for URL "+urlString, http.StatusInternalServerError)
		return
	}

	output := `<?xml version="1.0" encoding="UTF-8"?>
		<methodResponse>
			<params>
				<param>
					<value><boolean>1</boolean></value>
				</param>
			</params>
		</methodResponse>`

	w.Header().Set("Content-Type", "text/xml")
	w.Header().Set("Content-Length", strconv.Itoa(len(output)))
	w.Write([]byte(output))
}
<|endoftext|>