<commit_before>package mackerel

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/version"
)

var logger = logging.GetLogger("api")

// CreatingMetricsValue parameters of metric values
type CreatingMetricsValue struct {
	HostID string      `json:"hostId"`
	Name   string      `json:"name"`
	Time   float64     `json:"time"`
	Value  interface{} `json:"value"`
}

// API is the main interface of Mackerel API.
type API struct {
	BaseURL *url.URL
	APIKey  string
	Verbose bool
}

// NewAPI creates a new instance of API.
func NewAPI(rawurl string, apiKey string, verbose bool) (*API, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}
	return &API{u, apiKey, verbose}, nil
}

func (api *API) urlFor(path string) *url.URL {
	newURL, _ := url.Parse(api.BaseURL.String())
	newURL.Path = path
	return newURL
}

var apiRequestTimeout = 30 * time.Second

func (api *API) do(req *http.Request) (resp *http.Response, err error) {
	req.Header.Add("X-Api-Key", api.APIKey)
	req.Header.Add("X-Agent-Version", version.VERSION)
	req.Header.Add("X-Revision", version.GITCOMMIT)
	req.Header.Set("User-Agent", version.UserAgent())

	if api.Verbose {
		dump, err := httputil.DumpRequest(req, true)
		if err == nil {
			logger.Tracef("%s", dump)
		}
	}

	client := &http.Client{} // same as http.DefaultClient
	client.Timeout = apiRequestTimeout
	resp, err = client.Do(req)
	if err != nil {
		return nil, err
	}
	if api.Verbose {
		dump, err := httputil.DumpResponse(resp, true)
		if err == nil {
			logger.Tracef("%s", dump)
		}
	}
	return resp, nil
}

func closeResp(resp *http.Response) {
	if resp != nil {
		resp.Body.Close()
	}
}

// FindHost find the host
func (api *API) FindHost(id string) (*Host, error) {
	resp, err := api.get(fmt.Sprintf("/api/v0/hosts/%s", id))
	defer closeResp(resp)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, errors.New("status code is not 200")
	}

	var data struct {
		Host *Host `json:"host"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return data.Host, err
}

// CreateHost register the host to mackerel
func (api *API) CreateHost(name string, meta map[string]interface{}, interfaces []map[string]interface{}, roleFullnames []string, displayName string) (string, error) {
	resp, err := api.postJSON("/api/v0/hosts", map[string]interface{}{
		"name":          name,
		"type":          "unknown",
		"status":        "working",
		"meta":          meta,
		"interfaces":    interfaces,
		"roleFullnames": roleFullnames,
		"displayName":   displayName,
	})
	defer closeResp(resp)
	if err != nil {
		return "", err
	}

	if resp.StatusCode != 200 {
		return "", fmt.Errorf("API result failed: %s", resp.Status)
	}

	var data struct {
		ID string `json:"id"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return "", err
	}

	return data.ID, nil
}

// UpdateHost updates the host information on Mackerel.
func (api *API) UpdateHost(hostID string, hostSpec HostSpec) error {
	resp, err := api.putJSON("/api/v0/hosts/"+hostID, hostSpec)
	defer closeResp(resp)
	if err != nil {
		return err
	}

	return nil
}

// UpdateHostStatus updates the status of the host
func (api *API) UpdateHostStatus(hostID string, status string) error {
	resp, err := api.postJSON(fmt.Sprintf("/api/v0/hosts/%s/status", hostID), map[string]string{
		"status": status,
	})
	defer closeResp(resp)
	if err != nil {
		return err
	}
	return nil
}

// PostMetricsValues post metrics
func (api *API) PostMetricsValues(metricsValues [](*CreatingMetricsValue)) error {
	resp, err := api.postJSON("/api/v0/tsdb", metricsValues)
	defer closeResp(resp)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return fmt.Errorf("API result failed: %s", resp.Status)
	}

	return nil
}

// CreateGraphDefsPayload payload for post graph defs
type CreateGraphDefsPayload struct {
	Name        string                         `json:"name"`
	DisplayName string                         `json:"displayName"`
	Unit        string                         `json:"unit"`
	Metrics     []CreateGraphDefsPayloadMetric `json:"metrics"`
}

// CreateGraphDefsPayloadMetric repreesnt graph defs of each metric
type CreateGraphDefsPayloadMetric struct {
	Name        string `json:"name"`
	DisplayName string `json:"displayName"`
	IsStacked   bool   `json:"isStacked"`
}

// CreateGraphDefs register graph defs
func (api *API) CreateGraphDefs(payloads []CreateGraphDefsPayload) error {
	resp, err := api.postJSON("/api/v0/graph-defs/create", payloads)
	defer closeResp(resp)
	if err != nil {
		return err
	}
	return nil
}

func (api *API) get(path string) (*http.Response, error) {
	req, err := http.NewRequest("GET", api.urlFor(path).String(), nil)
	if err != nil {
		return nil, err
	}
	return api.do(req)
}

func (api *API) requestJSON(method, path string, payload interface{}) (*http.Response, error) {
	var body bytes.Buffer

	err := json.NewEncoder(&body).Encode(payload)
	if err != nil {
		return nil, err
	}
	logger.Debugf("%s %s %s", method, path, body.String())

	req, err := http.NewRequest(method, api.urlFor(path).String(), &body)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err := api.do(req)
	if err != nil {
		return resp, err
	}

	logger.Debugf("%s %s status=%q", method, path, resp.Status)
	if resp.StatusCode >= 400 {
		return resp, fmt.Errorf("request failed: [%s]", resp.Status)
	}
	return resp, nil
}

func (api *API) postJSON(path string, payload interface{}) (*http.Response, error) {
	return api.requestJSON("POST", path, payload)
}

func (api *API) putJSON(path string, payload interface{}) (*http.Response, error) {
	return api.requestJSON("PUT", path, payload)
}

// Time is a type for sending time information to Mackerel API server.
// It is encoded as an epoch seconds integer in JSON.
type Time time.Time

// MarshalJSON implements json.Marshaler.
func (t Time) MarshalJSON() ([]byte, error) {
	return json.Marshal(time.Time(t).Unix())
}
<commit_msg>define type mackerel.Error<commit_after>package mackerel

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/version"
)

var logger = logging.GetLogger("api")

// CreatingMetricsValue parameters of metric values
type CreatingMetricsValue struct {
	HostID string      `json:"hostId"`
	Name   string      `json:"name"`
	Time   float64     `json:"time"`
	Value  interface{} `json:"value"`
}

// API is the main interface of Mackerel API.
type API struct {
	BaseURL *url.URL
	APIKey  string
	Verbose bool
}

type Error struct {
	StatusCode int
	Message    string
}

func (aperr *Error) Error() string {
	return fmt.Sprintf("API error. status: %d, msg: %s", aperr.StatusCode, aperr.Message)
}

func (aperr *Error) IsClientError() bool {
	return 400 <= aperr.StatusCode && aperr.StatusCode < 500
}

func (aperr *Error) IsServerError() bool {
	return 500 <= aperr.StatusCode && aperr.StatusCode < 600
}

func apiError(code int, msg string) *Error {
	return &Error{
		StatusCode: code,
		Message:    msg,
	}
}

// NewAPI creates a new instance of API.
func NewAPI(rawurl string, apiKey string, verbose bool) (*API, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}
	return &API{u, apiKey, verbose}, nil
}

func (api *API) urlFor(path string) *url.URL {
	newURL, _ := url.Parse(api.BaseURL.String())
	newURL.Path = path
	return newURL
}

var apiRequestTimeout = 30 * time.Second

func (api *API) do(req *http.Request) (resp *http.Response, err error) {
	req.Header.Add("X-Api-Key", api.APIKey)
	req.Header.Add("X-Agent-Version", version.VERSION)
	req.Header.Add("X-Revision", version.GITCOMMIT)
	req.Header.Set("User-Agent", version.UserAgent())

	if api.Verbose {
		dump, err := httputil.DumpRequest(req, true)
		if err == nil {
			logger.Tracef("%s", dump)
		}
	}

	client := &http.Client{} // same as http.DefaultClient
	client.Timeout = apiRequestTimeout
	resp, err = client.Do(req)
	if err != nil {
		return nil, err
	}
	if api.Verbose {
		dump, err := httputil.DumpResponse(resp, true)
		if err == nil {
			logger.Tracef("%s", dump)
		}
	}
	return resp, nil
}

func closeResp(resp *http.Response) {
	if resp != nil {
		resp.Body.Close()
	}
}

// FindHost find the host
func (api *API) FindHost(id string) (*Host, error) {
	resp, err := api.get(fmt.Sprintf("/api/v0/hosts/%s", id))
	defer closeResp(resp)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, apiError(resp.StatusCode, "status code is not 200")
	}

	var data struct {
		Host *Host `json:"host"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return nil, err
	}
	return data.Host, err
}

// CreateHost register the host to mackerel
func (api *API) CreateHost(name string, meta map[string]interface{}, interfaces []map[string]interface{}, roleFullnames []string, displayName string) (string, error) {
	resp, err := api.postJSON("/api/v0/hosts", map[string]interface{}{
		"name":          name,
		"type":          "unknown",
		"status":        "working",
		"meta":          meta,
		"interfaces":    interfaces,
		"roleFullnames": roleFullnames,
		"displayName":   displayName,
	})
	defer closeResp(resp)
	if err != nil {
		return "", err
	}

	if resp.StatusCode != 200 {
		return "", apiError(resp.StatusCode, "API request failed")
	}

	var data struct {
		ID string `json:"id"`
	}
	err = json.NewDecoder(resp.Body).Decode(&data)
	if err != nil {
		return "", err
	}

	return data.ID, nil
}

// UpdateHost updates the host information on Mackerel.
func (api *API) UpdateHost(hostID string, hostSpec HostSpec) error {
	resp, err := api.putJSON("/api/v0/hosts/"+hostID, hostSpec)
	defer closeResp(resp)
	if err != nil {
		return err
	}

	return nil
}

// UpdateHostStatus updates the status of the host
func (api *API) UpdateHostStatus(hostID string, status string) error {
	resp, err := api.postJSON(fmt.Sprintf("/api/v0/hosts/%s/status", hostID), map[string]string{
		"status": status,
	})
	defer closeResp(resp)
	if err != nil {
		return err
	}
	return nil
}

// PostMetricsValues post metrics
func (api *API) PostMetricsValues(metricsValues [](*CreatingMetricsValue)) error {
	resp, err := api.postJSON("/api/v0/tsdb", metricsValues)
	defer closeResp(resp)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return apiError(resp.StatusCode, "api request failed")
	}

	return nil
}

// CreateGraphDefsPayload payload for post graph defs
type CreateGraphDefsPayload struct {
	Name        string                         `json:"name"`
	DisplayName string                         `json:"displayName"`
	Unit        string                         `json:"unit"`
	Metrics     []CreateGraphDefsPayloadMetric `json:"metrics"`
}

// CreateGraphDefsPayloadMetric repreesnt graph defs of each metric
type CreateGraphDefsPayloadMetric struct {
	Name        string `json:"name"`
	DisplayName string `json:"displayName"`
	IsStacked   bool   `json:"isStacked"`
}

// CreateGraphDefs register graph defs
func (api *API) CreateGraphDefs(payloads []CreateGraphDefsPayload) error {
	resp, err := api.postJSON("/api/v0/graph-defs/create", payloads)
	defer closeResp(resp)
	if err != nil {
		return err
	}
	return nil
}

func (api *API) get(path string) (*http.Response, error) {
	req, err := http.NewRequest("GET", api.urlFor(path).String(), nil)
	if err != nil {
		return nil, err
	}
	return api.do(req)
}

func (api *API) requestJSON(method, path string, payload interface{}) (*http.Response, error) {
	var body bytes.Buffer

	err := json.NewEncoder(&body).Encode(payload)
	if err != nil {
		return nil, err
	}
	logger.Debugf("%s %s %s", method, path, body.String())

	req, err := http.NewRequest(method, api.urlFor(path).String(), &body)
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err := api.do(req)
	if err != nil {
		return resp, err
	}

	logger.Debugf("%s %s status=%q", method, path, resp.Status)
	if resp.StatusCode >= 400 {
		return resp, apiError(resp.StatusCode, "api request failed")
	}
	return resp, nil
}

func (api *API) postJSON(path string, payload interface{}) (*http.Response, error) {
	return api.requestJSON("POST", path, payload)
}

func (api *API) putJSON(path string, payload interface{}) (*http.Response, error) {
	return api.requestJSON("PUT", path, payload)
}

// Time is a type for sending time information to Mackerel API server.
// It is encoded as an epoch seconds integer in JSON.
type Time time.Time

// MarshalJSON implements json.Marshaler.
func (t Time) MarshalJSON() ([]byte, error) {
	return json.Marshal(time.Time(t).Unix())
}
<|endoftext|>


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

package models

import (
	"fmt"

	"github.com/gogits/gogs/modules/log"
)

type AccessMode int

const (
	ACCESS_MODE_NONE AccessMode = iota
	ACCESS_MODE_READ
	ACCESS_MODE_WRITE
	ACCESS_MODE_ADMIN
	ACCESS_MODE_OWNER
)

// Access represents the highest access level of a user to the repository. The only access type
// that is not in this table is the real owner of a repository. In case of an organization
// repository, the members of the owners team are in this table.
type Access struct {
	ID     int64 `xorm:"pk autoincr"`
	UserID int64 `xorm:"UNIQUE(s)"`
	RepoID int64 `xorm:"UNIQUE(s)"`
	Mode   AccessMode
}

func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
	mode := ACCESS_MODE_NONE
	if !repo.IsPrivate {
		mode = ACCESS_MODE_READ
	}

	if u != nil {
		if u.Id == repo.OwnerId {
			return ACCESS_MODE_OWNER, nil
		}

		a := &Access{UserID: u.Id, RepoID: repo.Id}
		if has, err := e.Get(a); !has || err != nil {
			return mode, err
		}
		return a.Mode, nil
	}

	return mode, nil
}

// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
// user does not have access. User can be nil!
func AccessLevel(u *User, repo *Repository) (AccessMode, error) {
	return accessLevel(x, u, repo)
}

func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) {
	mode, err := accessLevel(e, u, repo)
	return testMode <= mode, err
}

// HasAccess returns true if someone has the request access level. User can be nil!
func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) {
	return hasAccess(x, u, repo, testMode)
}

// GetAccessibleRepositories finds all repositories where a user has access to,
// besides his own.
func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) {
	accesses := make([]*Access, 0, 10)
	if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil {
		return nil, err
	}

	repos := make(map[*Repository]AccessMode, len(accesses))
	for _, access := range accesses {
		repo, err := GetRepositoryById(access.RepoID)
		if err != nil {
			if IsErrRepoNotExist(err) {
				log.Error(4, "%v", err)
				continue
			}
			return nil, err
		}
		if err = repo.GetOwner(); err != nil {
			return nil, err
		} else if repo.OwnerId == u.Id {
			continue
		}
		repos[repo] = access.Mode
	}

	// FIXME: should we generate an ordered list here? Random looks weird.
	return repos, nil
}

func maxAccessMode(modes ...AccessMode) AccessMode {
	max := ACCESS_MODE_NONE
	for _, mode := range modes {
		if mode > max {
			max = mode
		}
	}
	return max
}

// FIXME: do corss-comparison so reduce deletions and additions to the minimum?
func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) {
	minMode := ACCESS_MODE_READ
	if !repo.IsPrivate {
		minMode = ACCESS_MODE_WRITE
	}

	newAccesses := make([]Access, 0, len(accessMap))
	for userID, mode := range accessMap {
		if mode < minMode {
			continue
		}
		newAccesses = append(newAccesses, Access{
			UserID: userID,
			RepoID: repo.Id,
			Mode:   mode,
		})
	}

	// Delete old accesses and insert new ones for repository.
	if _, err = e.Delete(&Access{RepoID: repo.Id}); err != nil {
		return fmt.Errorf("delete old accesses: %v", err)
	} else if _, err = e.Insert(newAccesses); err != nil {
		return fmt.Errorf("insert new accesses: %v", err)
	}
	return nil
}

// FIXME: should be able to have read-only access.
// Give all collaborators write access.
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
	collaborators, err := repo.getCollaborators(e)
	if err != nil {
		return fmt.Errorf("getCollaborators: %v", err)
	}
	for _, c := range collaborators {
		accessMap[c.Id] = ACCESS_MODE_WRITE
	}

	// Adds team members access.
	if repo.Owner.IsOrganization() {
		if err = repo.Owner.GetTeams(); err != nil {
			return fmt.Errorf("GetTeams: %v", err)
		}
		for _, t := range repo.Owner.Teams {
			if err = t.GetMembers(); err != nil {
				return fmt.Errorf("GetMembers: %v", err)
			}
			for _, m := range t.Members {
				if t.IsOwnerTeam() {
					accessMap[m.Id] = ACCESS_MODE_OWNER
				} else {
					accessMap[m.Id] = maxAccessMode(accessMap[m.Id], t.Authorize)
				}
			}
		}
	}
	return nil
}

// recalculateTeamAccesses recalculates new accesses for teams of an organization
// except the team whose ID is given. It is used to assign a team ID when
// remove repository from that team.
func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err error) {
	accessMap := make(map[int64]AccessMode, 20)

	if err = repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
		return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
	}

	if err = repo.getOwner(e); err != nil {
		return err
	}
	if repo.Owner.IsOrganization() {
		if err = repo.Owner.getTeams(e); err != nil {
			return err
		}

		for _, t := range repo.Owner.Teams {
			if t.ID == ignTeamID {
				continue
			}

			// Owner team gets owner access, and skip for teams that do not
			// have relations with repository.
			if t.IsOwnerTeam() {
				t.Authorize = ACCESS_MODE_OWNER
			} else if !t.hasRepository(e, repo.Id) {
				continue
			}

			if err = t.getMembers(e); err != nil {
				return fmt.Errorf("getMembers '%d': %v", t.ID, err)
			}
			for _, m := range t.Members {
				accessMap[m.Id] = maxAccessMode(accessMap[m.Id], t.Authorize)
			}
		}
	}

	return repo.refreshAccesses(e, accessMap)
}

func (repo *Repository) recalculateAccesses(e Engine) error {
	accessMap := make(map[int64]AccessMode, 20)
	if err := repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
		return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
	}
	return repo.refreshAccesses(e, accessMap)
}

// RecalculateAccesses recalculates all accesses for repository.
func (r *Repository) RecalculateAccesses() error {
	return r.recalculateAccesses(x)
}
<commit_msg>#1369: nil pointer when add repo to team<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"fmt"

	"github.com/gogits/gogs/modules/log"
)

type AccessMode int

const (
	ACCESS_MODE_NONE AccessMode = iota
	ACCESS_MODE_READ
	ACCESS_MODE_WRITE
	ACCESS_MODE_ADMIN
	ACCESS_MODE_OWNER
)

// Access represents the highest access level of a user to the repository. The only access type
// that is not in this table is the real owner of a repository. In case of an organization
// repository, the members of the owners team are in this table.
type Access struct {
	ID     int64 `xorm:"pk autoincr"`
	UserID int64 `xorm:"UNIQUE(s)"`
	RepoID int64 `xorm:"UNIQUE(s)"`
	Mode   AccessMode
}

func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
	mode := ACCESS_MODE_NONE
	if !repo.IsPrivate {
		mode = ACCESS_MODE_READ
	}

	if u != nil {
		if u.Id == repo.OwnerId {
			return ACCESS_MODE_OWNER, nil
		}

		a := &Access{UserID: u.Id, RepoID: repo.Id}
		if has, err := e.Get(a); !has || err != nil {
			return mode, err
		}
		return a.Mode, nil
	}

	return mode, nil
}

// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
// user does not have access. User can be nil!
func AccessLevel(u *User, repo *Repository) (AccessMode, error) {
	return accessLevel(x, u, repo)
}

func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) {
	mode, err := accessLevel(e, u, repo)
	return testMode <= mode, err
}

// HasAccess returns true if someone has the request access level. User can be nil!
func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) {
	return hasAccess(x, u, repo, testMode)
}

// GetAccessibleRepositories finds all repositories where a user has access to,
// besides his own.
func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) {
	accesses := make([]*Access, 0, 10)
	if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil {
		return nil, err
	}

	repos := make(map[*Repository]AccessMode, len(accesses))
	for _, access := range accesses {
		repo, err := GetRepositoryById(access.RepoID)
		if err != nil {
			if IsErrRepoNotExist(err) {
				log.Error(4, "%v", err)
				continue
			}
			return nil, err
		}
		if err = repo.GetOwner(); err != nil {
			return nil, err
		} else if repo.OwnerId == u.Id {
			continue
		}
		repos[repo] = access.Mode
	}

	// FIXME: should we generate an ordered list here? Random looks weird.
	return repos, nil
}

func maxAccessMode(modes ...AccessMode) AccessMode {
	max := ACCESS_MODE_NONE
	for _, mode := range modes {
		if mode > max {
			max = mode
		}
	}
	return max
}

// FIXME: do corss-comparison so reduce deletions and additions to the minimum?
func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) {
	minMode := ACCESS_MODE_READ
	if !repo.IsPrivate {
		minMode = ACCESS_MODE_WRITE
	}

	newAccesses := make([]Access, 0, len(accessMap))
	for userID, mode := range accessMap {
		if mode < minMode {
			continue
		}
		newAccesses = append(newAccesses, Access{
			UserID: userID,
			RepoID: repo.Id,
			Mode:   mode,
		})
	}

	// Delete old accesses and insert new ones for repository.
	if _, err = e.Delete(&Access{RepoID: repo.Id}); err != nil {
		return fmt.Errorf("delete old accesses: %v", err)
	} else if _, err = e.Insert(newAccesses); err != nil {
		return fmt.Errorf("insert new accesses: %v", err)
	}
	return nil
}

// FIXME: should be able to have read-only access.
// Give all collaborators write access.
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
	collaborators, err := repo.getCollaborators(e)
	if err != nil {
		return fmt.Errorf("getCollaborators: %v", err)
	}
	for _, c := range collaborators {
		accessMap[c.Id] = ACCESS_MODE_WRITE
	}

	// Adds team members access.
	if repo.Owner.IsOrganization() {
		if err = repo.Owner.GetTeams(); err != nil {
			return fmt.Errorf("GetTeams: %v", err)
		}
		for _, t := range repo.Owner.Teams {
			if err = t.GetMembers(); err != nil {
				return fmt.Errorf("GetMembers: %v", err)
			}
			for _, m := range t.Members {
				if t.IsOwnerTeam() {
					accessMap[m.Id] = ACCESS_MODE_OWNER
				} else {
					accessMap[m.Id] = maxAccessMode(accessMap[m.Id], t.Authorize)
				}
			}
		}
	}
	return nil
}

// recalculateTeamAccesses recalculates new accesses for teams of an organization
// except the team whose ID is given. It is used to assign a team ID when
// remove repository from that team.
func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err error) {
	accessMap := make(map[int64]AccessMode, 20)

	if err = repo.getOwner(e); err != nil {
		return err
	}
	if err = repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
		return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
	}
	if repo.Owner.IsOrganization() {
		if err = repo.Owner.getTeams(e); err != nil {
			return err
		}

		for _, t := range repo.Owner.Teams {
			if t.ID == ignTeamID {
				continue
			}

			// Owner team gets owner access, and skip for teams that do not
			// have relations with repository.
			if t.IsOwnerTeam() {
				t.Authorize = ACCESS_MODE_OWNER
			} else if !t.hasRepository(e, repo.Id) {
				continue
			}

			if err = t.getMembers(e); err != nil {
				return fmt.Errorf("getMembers '%d': %v", t.ID, err)
			}
			for _, m := range t.Members {
				accessMap[m.Id] = maxAccessMode(accessMap[m.Id], t.Authorize)
			}
		}
	}

	return repo.refreshAccesses(e, accessMap)
}

func (repo *Repository) recalculateAccesses(e Engine) error {
	accessMap := make(map[int64]AccessMode, 20)
	if err := repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
		return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
	}
	return repo.refreshAccesses(e, accessMap)
}

// RecalculateAccesses recalculates all accesses for repository.
func (r *Repository) RecalculateAccesses() error {
	return r.recalculateAccesses(x)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/docker/cli/cli/command"
	"github.com/docker/cli/cli/command/commands"
	"github.com/docker/docker/pkg/term"
	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
	"github.com/spf13/pflag"
)

const descriptionSourcePath = "man/src/"

func generateManPages(opts *options) error {
	header := &doc.GenManHeader{
		Title:   "DOCKER",
		Section: "1",
		Source:  "Docker Community",
	}

	stdin, stdout, stderr := term.StdStreams()
	dockerCli := command.NewDockerCli(stdin, stdout, stderr)
	cmd := &cobra.Command{Use: "docker"}
	commands.AddCommands(cmd, dockerCli)
	source := filepath.Join(opts.source, descriptionSourcePath)
	if err := loadLongDescription(cmd, source); err != nil {
		return err
	}

	cmd.DisableAutoGenTag = true
	return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{
		Header:           header,
		Path:             opts.target,
		CommandSeparator: "-",
	})
}

func loadLongDescription(cmd *cobra.Command, path string) error {
	for _, cmd := range cmd.Commands() {
		if cmd.Name() == "" {
			continue
		}
		fullpath := filepath.Join(path, cmd.Name()+".md")

		if cmd.HasSubCommands() {
			loadLongDescription(cmd, filepath.Join(path, cmd.Name()))
		}

		if _, err := os.Stat(fullpath); err != nil {
			log.Printf("WARN: %s does not exist, skipping\n", fullpath)
			continue
		}

		content, err := ioutil.ReadFile(fullpath)
		if err != nil {
			return err
		}
		cmd.Long = string(content)

		fullpath = filepath.Join(path, cmd.Name()+"-example.md")
		if _, err := os.Stat(fullpath); err != nil {
			continue
		}

		content, err = ioutil.ReadFile(fullpath)
		if err != nil {
			return err
		}
		cmd.Example = string(content)

	}
	return nil
}

type options struct {
	source string
	target string
}

func parseArgs() (*options, error) {
	opts := &options{}
	cwd, _ := os.Getwd()
	flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
	flags.StringVar(&opts.source, "root", cwd, "Path to project root")
	flags.StringVar(&opts.target, "target", "/tmp", "Target path for generated man pages")
	err := flags.Parse(os.Args[1:])
	return opts, err
}

func main() {
	opts, err := parseArgs()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
	}
	fmt.Printf("Project root: %s\n", opts.source)
	fmt.Printf("Generating man pages into %s\n", opts.target)
	if err := generateManPages(opts); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to generate man pages: %s\n", err.Error())
	}
}
<commit_msg>Disable adding [flags] to UseLine in man pages<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/docker/cli/cli/command"
	"github.com/docker/cli/cli/command/commands"
	"github.com/docker/docker/pkg/term"
	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
	"github.com/spf13/pflag"
)

const descriptionSourcePath = "man/src/"

func generateManPages(opts *options) error {
	header := &doc.GenManHeader{
		Title:   "DOCKER",
		Section: "1",
		Source:  "Docker Community",
	}

	stdin, stdout, stderr := term.StdStreams()
	dockerCli := command.NewDockerCli(stdin, stdout, stderr)
	cmd := &cobra.Command{Use: "docker"}
	commands.AddCommands(cmd, dockerCli)
	source := filepath.Join(opts.source, descriptionSourcePath)
	if err := loadLongDescription(cmd, source); err != nil {
		return err
	}

	cmd.DisableAutoGenTag = true
	cmd.DisableFlagsInUseLine = true
	return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{
		Header:           header,
		Path:             opts.target,
		CommandSeparator: "-",
	})
}

func loadLongDescription(cmd *cobra.Command, path string) error {
	for _, cmd := range cmd.Commands() {
		cmd.DisableFlagsInUseLine = true
		if cmd.Name() == "" {
			continue
		}
		fullpath := filepath.Join(path, cmd.Name()+".md")

		if cmd.HasSubCommands() {
			loadLongDescription(cmd, filepath.Join(path, cmd.Name()))
		}

		if _, err := os.Stat(fullpath); err != nil {
			log.Printf("WARN: %s does not exist, skipping\n", fullpath)
			continue
		}

		content, err := ioutil.ReadFile(fullpath)
		if err != nil {
			return err
		}
		cmd.Long = string(content)

		fullpath = filepath.Join(path, cmd.Name()+"-example.md")
		if _, err := os.Stat(fullpath); err != nil {
			continue
		}

		content, err = ioutil.ReadFile(fullpath)
		if err != nil {
			return err
		}
		cmd.Example = string(content)

	}
	return nil
}

type options struct {
	source string
	target string
}

func parseArgs() (*options, error) {
	opts := &options{}
	cwd, _ := os.Getwd()
	flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
	flags.StringVar(&opts.source, "root", cwd, "Path to project root")
	flags.StringVar(&opts.target, "target", "/tmp", "Target path for generated man pages")
	err := flags.Parse(os.Args[1:])
	return opts, err
}

func main() {
	opts, err := parseArgs()
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
	}
	fmt.Printf("Project root: %s\n", opts.source)
	fmt.Printf("Generating man pages into %s\n", opts.target)
	if err := generateManPages(opts); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to generate man pages: %s\n", err.Error())
	}
}
<|endoftext|>


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

package dlshared

import (
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

type DataSource struct {
	DbName string
	CollectionName string
	Mongo *Mongo
	Logger
}

// Insert a document into a collection with the base configured write concern.
func (self *DataSource) Insert(doc interface{}) error { return self.Mongo.Collection(self.DbName, self.CollectionName).Insert(doc) }

// Upsert a document in a collection with the base configured write concern.
func (self *DataSource) Upsert(selector interface{}, change interface{}) error {
	_, err := self.Mongo.Collection(self.DbName, self.CollectionName).Upsert(selector, change)
	return err
}

// Insert a document into a collection with the passed write concern.
func (self *DataSource) InsertSafe(doc interface{}, safeMode *mgo.Safe) error {
	session := self.SessionClone()

	defer session.Close()

	session.SetSafe(safeMode)
	return session.DB(self.DbName).C(self.CollectionName).Insert(doc)
}

// Find by the _id. Returns false if not found.
func (self *DataSource) FindById(id interface{}, result interface{}) error {
	return self.FindOne(&bson.M{ "_id": id }, result)
}

// Returns nil if this is a NOT a document not found error.
func (self *DataSource) RemoveNotFoundErr(err error) error {
	if self.NotFoundErr(err) {
		return nil
	}
	return err
}

// Returns true if this is a document not found error.
func (self *DataSource) NotFoundErr(err error) (bool) {
	return err == mgo.ErrNotFound
}

// Finds one document or returns false.
func (self *DataSource) FindOne(query *bson.M, result interface{}) error {
	return self.Collection().Find(query).One(result)
}

// Delete one or more documents from the collection. If the document(s) is/are not found, no error
// is returned.
func (self *DataSource) Delete(selector interface{}) error {
	_, err := self.Collection().RemoveAll(selector)
	return err
}

// Ensure a unique, non-sparse index is created. This does not create in the background. This does
// NOT drop duplicates if they exist. Duplicates will cause an error.
func (self *DataSource) EnsureUniqueIndex(fields []string) error {
	return self.Collection().EnsureIndex(mgo.Index{
		Key: fields,
		Unique: true,
		DropDups: true,
		Background: false,
		Sparse: false,
	})
}

// Ensure a non-unique, non-sparse index is created. This does not create in the background.
func (self *DataSource) EnsureIndex(fields []string) error {
	return self.Collection().EnsureIndex(mgo.Index{
		Key: fields,
		Unique: false,
		DropDups: true,
		Background: false,
		Sparse: false,
	})
}

// Ensure a non-unique, sparse index is created. This does not create in the background.
func (self *DataSource) EnsureSparseIndex(fields []string) error {
	return self.Collection().EnsureIndex(mgo.Index{
		Key: fields,
		Unique: false,
		DropDups: true,
		Background: false,
		Sparse: true,
	})
}

// Ensure a unique, sparse index is created. This does not create in the background. This does
// NOT drop duplicates if they exist. Duplicates will cause an error.
func (self *DataSource) EnsureUniqueSparseIndex(fields []string) error {
	return self.Collection().EnsureIndex(mgo.Index{
		Key: fields,
		Unique: true,
		DropDups: false,
		Background: false,
		Sparse: true,
	})
}

// Returns the collection from the session.
func (self *DataSource) Collection() *mgo.Collection { return self.Mongo.Collection(self.DbName, self.CollectionName) }

// Returns the database from the session.
func (self *DataSource) Db() *mgo.Database { return self.Mongo.Db(self.DbName) }

// Returns the session struct.
func (self *DataSource) Session() *mgo.Session { return self.Mongo.session }

// Returns a clone of the session struct.
func (self *DataSource) SessionClone() *mgo.Session { return self.Mongo.session.Clone() }

// Returns a copy of the session struct.
func (self *DataSource) SessionCopy() *mgo.Session { return self.Mongo.session.Clone() }

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

package dlshared

import (
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

type DataSource struct {
	DbName string
	CollectionName string
	Mongo *Mongo
	Logger
}

// Insert a document into a collection with the base configured write concern.
func (self *DataSource) Insert(doc interface{}) error { return self.Mongo.Collection(self.DbName, self.CollectionName).Insert(doc) }

// Upsert a document in a collection with the base configured write concern.
func (self *DataSource) Upsert(selector interface{}, change interface{}) error {
	_, err := self.Mongo.Collection(self.DbName, self.CollectionName).Upsert(selector, change)
	return err
}

// Insert a document into a collection with the passed write concern.
func (self *DataSource) InsertSafe(doc interface{}, safeMode *mgo.Safe) error {
	session := self.SessionClone()

	defer session.Close()

	session.SetSafe(safeMode)
	return session.DB(self.DbName).C(self.CollectionName).Insert(doc)
}

// Set a property using a specified safe mode.
func (self *DataSource) SetFieldSafe(query interface{}, safeMode *mgo.Safe, field string, value interface{}) error {
	session := self.SessionClone()
	defer session.Close()
	session.SetSafe(safeMode)

	doc := &bson.M{ "$set": &bson.M{ field: value } }

	return self.RemoveNotFoundErr(session.DB(self.DbName).C(self.CollectionName).Update(query, doc))
}

// Find by the _id. Returns false if not found.
func (self *DataSource) FindById(id interface{}, result interface{}) error {
	return self.FindOne(&bson.M{ "_id": id }, result)
}

// Returns nil if this is a NOT a document not found error.
func (self *DataSource) RemoveNotFoundErr(err error) error {
	if self.NotFoundErr(err) {
		return nil
	}
	return err
}

// Returns true if this is a document not found error.
func (self *DataSource) NotFoundErr(err error) (bool) {
	return err == mgo.ErrNotFound
}

// Finds one document or returns false.
func (self *DataSource) FindOne(query *bson.M, result interface{}) error {
	return self.Collection().Find(query).One(result)
}

// Delete one or more documents from the collection. If the document(s) is/are not found, no error
// is returned.
func (self *DataSource) Delete(selector interface{}) error {
	_, err := self.Collection().RemoveAll(selector)
	return err
}

// Ensure a unique, non-sparse index is created. This does not create in the background. This does
// NOT drop duplicates if they exist. Duplicates will cause an error.
func (self *DataSource) EnsureUniqueIndex(fields []string) error {
	return self.Collection().EnsureIndex(mgo.Index{
		Key: fields,
		Unique: true,
		DropDups: true,
		Background: false,
		Sparse: false,
	})
}

// Ensure a non-unique, non-sparse index is created. This does not create in the background.
func (self *DataSource) EnsureIndex(fields []string) error {
	return self.Collection().EnsureIndex(mgo.Index{
		Key: fields,
		Unique: false,
		DropDups: true,
		Background: false,
		Sparse: false,
	})
}

// Ensure a non-unique, sparse index is created. This does not create in the background.
func (self *DataSource) EnsureSparseIndex(fields []string) error {
	return self.Collection().EnsureIndex(mgo.Index{
		Key: fields,
		Unique: false,
		DropDups: true,
		Background: false,
		Sparse: true,
	})
}

// Ensure a unique, sparse index is created. This does not create in the background. This does
// NOT drop duplicates if they exist. Duplicates will cause an error.
func (self *DataSource) EnsureUniqueSparseIndex(fields []string) error {
	return self.Collection().EnsureIndex(mgo.Index{
		Key: fields,
		Unique: true,
		DropDups: false,
		Background: false,
		Sparse: true,
	})
}

// Returns the collection from the session.
func (self *DataSource) Collection() *mgo.Collection { return self.Mongo.Collection(self.DbName, self.CollectionName) }

// Returns the database from the session.
func (self *DataSource) Db() *mgo.Database { return self.Mongo.Db(self.DbName) }

// Returns the session struct.
func (self *DataSource) Session() *mgo.Session { return self.Mongo.session }

// Returns a clone of the session struct.
func (self *DataSource) SessionClone() *mgo.Session { return self.Mongo.session.Clone() }

// Returns a copy of the session struct.
func (self *DataSource) SessionCopy() *mgo.Session { return self.Mongo.session.Clone() }

<|endoftext|>


<commit_before>package gwr

import (
	"fmt"
	"io"
)

// The default data sources registry which data sources are added to by the
// module-level Add* functions.
var DefaultDataSources DataSources

func init() {
	DefaultDataSources.init()
}

// AddDataSource adds a data source to the default data sources registry.
func AddDataSource(ds DataSource) error {
	return DefaultDataSources.AddDataSource(ds)
}

// AddMarshaledDataSource adds a generically marshaled data source to the
// default data sources registry.
func AddMarshaledDataSource(gds GenericDataSource) error {
	return DefaultDataSources.AddMarshaledDataSource(gds)
}

// DataSource is the interface implemented by all
// data sources.
type DataSource interface {
	// NOTE: implementation is self encoding, but may abstract internally
	Info() DataSourceInfo
	Get(format string, w io.Writer) error
	Watch(format string, w io.Writer) error
}

// DataSourceInfo provides a description of each
// data source, such as name and supported formats.
type DataSourceInfo struct {
	Name    string                 `json:"name"`
	Formats []string               `json:"formats"`
	Attrs   map[string]interface{} `json:"attrs"`
}

// DataSources is a flat collection of DataSources
// with a meta introspection data source.
type DataSources struct {
	sources   map[string]DataSource
	metaNouns metaNounDataSource
}

// NewDataSources creates a DataSources structure
// an sets up its "/meta/nouns" data source.
func NewDataSources() *DataSources {
	dss := &DataSources{}
	dss.init()
	return dss
}

func (dss *DataSources) init() {
	dss.sources = make(map[string]DataSource, 2)
	dss.metaNouns.sources = dss
	dss.AddMarshaledDataSource(&dss.metaNouns)
}

// Get returns the named data source or nil if none is defined.
func (dss *DataSources) Get(name string) DataSource {
	source, ok := dss.sources[name]
	if ok {
		return source
	}
	return nil
}

// Info returns a map of all DataSource.Info() data
func (dss *DataSources) Info() map[string]DataSourceInfo {
	info := make(map[string]DataSourceInfo, len(dss.sources))
	for name, ds := range dss.sources {
		info[name] = ds.Info()
	}
	return info
}

// AddMarshaledDataSource adds a generically-marshaled data source. It is a
// convenience for AddDataSource(NewMarshaledDataSource(gds, nil))
func (dss *DataSources) AddMarshaledDataSource(gds GenericDataSource) error {
	mds := NewMarshaledDataSource(gds, nil)
	// TODO: useful to return mds?
	return dss.AddDataSource(mds)
}

// AddDataSource adds a DataSource, if none is
// already defined for the given name.
func (dss *DataSources) AddDataSource(ds DataSource) error {
	info := ds.Info()
	_, ok := dss.sources[info.Name]
	if ok {
		return fmt.Errorf("data source already defined")
	}
	dss.sources[info.Name] = ds
	dss.metaNouns.dataSourceAdded(ds)
	return nil
}

// TODO: do we really need to support removing data sources?  I can see the
// case for intermediaries perhaps, and suspect that is needed... but punting
// for now.
<commit_msg>DefaultDataSources: fix doc string<commit_after>package gwr

import (
	"fmt"
	"io"
)

// DefaultDataSources is default data sources registry which data sources are
// added to by the module-level Add* functions.  It is used by all of the
// protocol servers if no data sources are provided.
var DefaultDataSources DataSources

func init() {
	DefaultDataSources.init()
}

// AddDataSource adds a data source to the default data sources registry.
func AddDataSource(ds DataSource) error {
	return DefaultDataSources.AddDataSource(ds)
}

// AddMarshaledDataSource adds a generically marshaled data source to the
// default data sources registry.
func AddMarshaledDataSource(gds GenericDataSource) error {
	return DefaultDataSources.AddMarshaledDataSource(gds)
}

// DataSource is the interface implemented by all
// data sources.
type DataSource interface {
	// NOTE: implementation is self encoding, but may abstract internally
	Info() DataSourceInfo
	Get(format string, w io.Writer) error
	Watch(format string, w io.Writer) error
}

// DataSourceInfo provides a description of each
// data source, such as name and supported formats.
type DataSourceInfo struct {
	Name    string                 `json:"name"`
	Formats []string               `json:"formats"`
	Attrs   map[string]interface{} `json:"attrs"`
}

// DataSources is a flat collection of DataSources
// with a meta introspection data source.
type DataSources struct {
	sources   map[string]DataSource
	metaNouns metaNounDataSource
}

// NewDataSources creates a DataSources structure
// an sets up its "/meta/nouns" data source.
func NewDataSources() *DataSources {
	dss := &DataSources{}
	dss.init()
	return dss
}

func (dss *DataSources) init() {
	dss.sources = make(map[string]DataSource, 2)
	dss.metaNouns.sources = dss
	dss.AddMarshaledDataSource(&dss.metaNouns)
}

// Get returns the named data source or nil if none is defined.
func (dss *DataSources) Get(name string) DataSource {
	source, ok := dss.sources[name]
	if ok {
		return source
	}
	return nil
}

// Info returns a map of all DataSource.Info() data
func (dss *DataSources) Info() map[string]DataSourceInfo {
	info := make(map[string]DataSourceInfo, len(dss.sources))
	for name, ds := range dss.sources {
		info[name] = ds.Info()
	}
	return info
}

// AddMarshaledDataSource adds a generically-marshaled data source. It is a
// convenience for AddDataSource(NewMarshaledDataSource(gds, nil))
func (dss *DataSources) AddMarshaledDataSource(gds GenericDataSource) error {
	mds := NewMarshaledDataSource(gds, nil)
	// TODO: useful to return mds?
	return dss.AddDataSource(mds)
}

// AddDataSource adds a DataSource, if none is
// already defined for the given name.
func (dss *DataSources) AddDataSource(ds DataSource) error {
	info := ds.Info()
	_, ok := dss.sources[info.Name]
	if ok {
		return fmt.Errorf("data source already defined")
	}
	dss.sources[info.Name] = ds
	dss.metaNouns.dataSourceAdded(ds)
	return nil
}

// TODO: do we really need to support removing data sources?  I can see the
// case for intermediaries perhaps, and suspect that is needed... but punting
// for now.
<|endoftext|>


<commit_before>package database

import (
	"database/sql"
	"log"
	"net/url"

	"github.com/TF2Stadium/Helen/models"
	_ "github.com/lib/pq"
)

var (
	db *sql.DB
)

func Connect(dburl, database, username, password string) {
	DBUrl := url.URL{
		Scheme:   "postgres",
		Host:     dburl,
		Path:     database,
		RawQuery: "sslmode=disable",
	}

	log.Printf("Connecting to DB on %s", DBUrl.String())

	DBUrl.User = url.UserPassword(username, password)
	var err error

	db, err = sql.Open("postgres", DBUrl.String())
	if err != nil {
		log.Fatal(err)
	}
}

func IsAllowed(userid uint32, lobbyid uint) bool {
	var state int
	db.QueryRow("SELECT state FROM player_slots WHERE id = $1 AND lobby_id = $2", userid, lobbyid).Scan(&state)

	return state != 0
}

func IsLobbyClosed(lobbyid uint) bool {
	var state int
	db.QueryRow("SELECT state FROM lobbies where id = $1", lobbyid).Scan(&state)
	return state != int(models.LobbyStateEnded)
}

func GetSteamID(userid uint32) string {
	var steamid string
	db.QueryRow("SELECT steam_id FROM players WHERE id = $1", userid).Scan(&steamid)
	return steamid
}
<commit_msg>Don't use package models<commit_after>package database

import (
	"database/sql"
	"log"
	"net/url"

	_ "github.com/lib/pq"
)

var (
	db *sql.DB
)

func Connect(dburl, database, username, password string) {
	DBUrl := url.URL{
		Scheme:   "postgres",
		Host:     dburl,
		Path:     database,
		RawQuery: "sslmode=disable",
	}

	log.Printf("Connecting to DB on %s", DBUrl.String())

	DBUrl.User = url.UserPassword(username, password)
	var err error

	db, err = sql.Open("postgres", DBUrl.String())
	if err != nil {
		log.Fatal(err)
	}
}

func IsAllowed(userid uint32, lobbyid uint) bool {
	var state int
	db.QueryRow("SELECT state FROM player_slots WHERE id = $1 AND lobby_id = $2", userid, lobbyid).Scan(&state)

	return state != 0
}

func IsLobbyClosed(lobbyid uint) bool {
	var state int
	db.QueryRow("SELECT state FROM lobbies where id = $1", lobbyid).Scan(&state)
	return state != 5
}

func GetSteamID(userid uint32) string {
	var steamid string
	db.QueryRow("SELECT steam_id FROM players WHERE id = $1", userid).Scan(&steamid)
	return steamid
}
<|endoftext|>


<commit_before>package data

import (
	"github.com/rubblelabs/ripple/crypto"
)

func Sign(s Signer, key crypto.Key, sequence *uint32) error {
	s.InitialiseForSigning()
	copy(s.GetPublicKey().Bytes(), key.Public(sequence))
	hash, msg, err := SigningHash(s)
	if err != nil {
		return err
	}
	sig, err := crypto.Sign(key.Private(sequence), hash.Bytes(), msg)
	if err != nil {
		return err
	}
	hash, _, err = Raw(s)
	if err != nil {
		return err
	}
	copy(s.GetHash().Bytes(), hash.Bytes())
	*s.GetSignature() = VariableLength(sig)
	return nil
}

func CheckSignature(s Signer) (bool, error) {
	hash, msg, err := SigningHash(s)
	if err != nil {
		return false, err
	}
	return crypto.Verify(s.GetPublicKey().Bytes(), hash.Bytes(), msg, s.GetSignature().Bytes())
}
<commit_msg>Fix signing<commit_after>package data

import "github.com/rubblelabs/ripple/crypto"

func Sign(s Signer, key crypto.Key, sequence *uint32) error {
	s.InitialiseForSigning()
	*s.GetSignature() = nil
	copy(s.GetPublicKey().Bytes(), key.Public(sequence))
	hash, msg, err := SigningHash(s)
	if err != nil {
		return err
	}
	sig, err := crypto.Sign(key.Private(sequence), hash.Bytes(), msg)
	if err != nil {
		return err
	}
	hash, _, err = Raw(s)
	if err != nil {
		return err
	}
	copy(s.GetHash().Bytes(), hash.Bytes())
	*s.GetSignature() = VariableLength(sig)
	return nil
}

func CheckSignature(s Signer) (bool, error) {
	hash, msg, err := SigningHash(s)
	if err != nil {
		return false, err
	}
	return crypto.Verify(s.GetPublicKey().Bytes(), hash.Bytes(), msg, s.GetSignature().Bytes())
}
<|endoftext|>


<commit_before>package flow

// TODO(waigani) currently hardcoded to rewrite. Generalise this so it can be
// used for all Flow endpoints. It may be possible to use the command config
// structs to do this.

import (
	"bufio"
	"context"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	rewriterpc "github.com/codelingo/codelingo/flows/codelingo/rewrite/rpc"

	"github.com/briandowns/spinner"
	"github.com/codelingo/lingo/app/util"
	"github.com/codelingo/rpc/flow"
	"github.com/fatih/color"
	"github.com/juju/errors"
	"github.com/waigani/diffparser"
	"github.com/waigani/xxx"
)

type hunkconfirmer struct {
	keepAll bool
	output  bool
}

func NewConfirmer(output, keepAll bool, d *diffparser.Diff) (*hunkconfirmer, error) {
	cfm := hunkconfirmer{
		keepAll: keepAll,
		output:  output,
	}

	return &cfm, nil
}

func ConfirmIssues(cancel context.CancelFunc, hunkc chan *rewriterpc.Hunk, errorc chan error, keepAll bool, saveToFile string) ([]*rewriterpc.Hunk, error) {
	defer util.Logger.Sync()

	var confirmedHunks []*rewriterpc.Hunk
	spnr := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
	spnr.Start()
	defer spnr.Stop()

	output := saveToFile == ""
	cfm, err := NewConfirmer(output, keepAll, nil)

	if err != nil {
		cancel()
		return nil, errors.Trace(err)
	}

	// If user is manually confirming reviews, set a long timeout.
	timeout := time.After(time.Hour * 1)
	if keepAll {
		timeout = time.After(time.Minute * 11)
	}

l:
	for {
		select {
		case err, ok := <-errorc:
			if !ok {
				errorc = nil
				break
			}

			// Abort review
			cancel()
			util.Logger.Debugf("error: %s", errors.ErrorStack(err))
			return nil, errors.Trace(err)
		case hunk, ok := <-hunkc:
			if !keepAll {
				spnr.Stop()
			}
			if !ok {
				hunkc = nil
				break
			}

			if cfm.Confirm(0, hunk) {
				confirmedHunks = append(confirmedHunks, hunk)
			}

			if !keepAll {
				spnr.Restart()
			}
		case <-timeout:
			cancel()
			return nil, errors.New("timed out waiting for response")
		}
		if hunkc == nil && errorc == nil {
			break l
		}
	}

	// Stop spinner if it hasn't been stopped already
	if keepAll {
		spnr.Stop()
	}
	return confirmedHunks, nil
}

// returns the full lines of the SRC for the hunk.
func fullLineSRC(hunk *flow.Issue, newSRC string) (string, error) {

	pos := hunk.GetPosition()
	startPos := pos.GetStart()
	endPos := pos.GetEnd()

	file, err := os.Open(startPos.Filename)
	if err != nil {
		return "", errors.Trace(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)

	var byt int64
	var src []byte
	var strtLineByt int64
	var endLineByt int64
	var foundStart bool
	var foundEnd bool
	for scanner.Scan() {
		src = append(src, append(scanner.Bytes(), []byte("\n")...)...)

		startByt := byt
		endByt := startByt + int64(len(scanner.Bytes())+1) // +1 for \n char

		if startPos.Offset >= startByt && startPos.Offset <= endByt {

			// found start line
			strtLineByt = startByt
			foundStart = true
		}

		if endPos.Offset >= startByt && endPos.Offset <= endByt {

			// found end line
			endLineByt = endByt
			foundEnd = true
		}

		if foundStart && foundEnd {

			// xxx.Print(string(src))
			// fmt.Printf("\n[%d:%d]", strtLineByt, startPos.Offset)
			beforeNewSRC := string(src[strtLineByt:startPos.Offset])
			endNewSRC := string(src[endPos.Offset:endLineByt])

			return beforeNewSRC + newSRC + endNewSRC, nil

		}

		byt = endByt
	}

	return "", errors.Trace(scanner.Err())
}

func GetDiffRootPath(filename string) string {
	// Get filename relative to git root folder
	// TODO: Handle error in case of git not being installed
	// https://github.com/codelingo/demo/hunks/2
	out, err := exec.Command("git", "ls-tree", "--full-name", "--name-only", "HEAD", filename).Output()
	if err == nil && len(out) != 0 {
		if len(out) != 0 {
			filename = strings.TrimSuffix(string(out), "\n")
		}
	}
	return filename
}

var editor string

// confirm returns true if the hunk should be kept or false if it should be
// dropped.
func (c hunkconfirmer) Confirm(attempt int, hunk *rewriterpc.Hunk) bool {
	if c.keepAll {
		return true
	}
	if attempt == 0 {
		fmt.Println(c.FormatPlainText(hunk))
	}
	attempt++
	var options string
	fmt.Print("\n[o]pen")
	if c.output {
		fmt.Print(" [d]iscard [k]eep")
	}
	fmt.Print(": ")

	fmt.Scanln(&options)

	switch options {
	case "o":
		var app string
		defaultEditor := "vi" // TODO(waigani) use EDITOR or VISUAL env vars
		// https://github.com/codelingo/demo/hunks/3
		if editor != "" {
			defaultEditor = editor
		}
		fmt.Printf("application (%s):", defaultEditor)
		fmt.Scanln(&app)
		filename := hunk.Filename
		if app == "" {
			app = defaultEditor
		}
		// c := hunk.Position.Start.Column // TODO(waigani) use column
		// https://github.com/codelingo/demo/hunks/4

		// TODO(waigani) calc line from offset
		l := int64(0)
		cmd, err := util.OpenFileCmd(app, filename, l)
		if err != nil {
			fmt.Println(err)
			return c.Confirm(attempt, hunk)
		}

		if err = cmd.Start(); err != nil {
			log.Println(err)
		}
		if err = cmd.Wait(); err != nil {
			log.Println(err)
		}

		editor = app

		c.Confirm(attempt, hunk)
	case "d":
		return false
	case "", "k", "K", "\n":
		return true
	default:
		fmt.Printf("invalid input: %s\n", options)
		fmt.Println(options)
		c.Confirm(attempt, hunk)
	}

	// TODO(waigani) build up hunks here.

	return true
}

func (c *hunkconfirmer) FormatPlainText(hunk *rewriterpc.Hunk) string {

	xxx.Dump(hunk)

	g := color.New(color.FgGreen).SprintfFunc()
	return indent(g("\n%s", hunk.SRC), true, false)

	// TODO(waigani) generate a diff hunk

	// m := color.New(color.FgWhite, color.Faint).SprintfFunc()
	// y := color.New(color.FgRed).SprintfFunc()
	// yf := color.New(color.FgWhite, color.Faint).SprintfFunc()
	// filename := hunk.Filename

	// // TODO(waigani) get line from offset
	// line := 0
	// addrFmtStr := fmt.Sprintf("%s:%d", filename, line)

	// // TODO(waigani) get column from offset
	// col := 0
	// addrFmtStr += fmt.Sprintf(":%d", col)
	// address := m(addrFmtStr)

	// ctxBefore := indent(yf("\n...\n%s", hunk.CtxBefore), false, false)
	// oldLines := indent(y("\n%s", hunk.LineText), false, true)

	// newLines := indent(g("\n%s", newSRC), true, false)
	// ctxAfter := indent(yf("\n%s\n...", hunk.CtxAfter), false, false)
	// src := ctxBefore + oldLines + newLines + ctxAfter

	// return fmt.Sprintf("%s\n%s", address, src)
}

func indent(str string, add, remove bool) string {
	replace := "\n    "
	if add {
		replace = "\n  + "
	}
	if remove {
		replace = "\n  - "
	}
	return strings.Replace(str, "\n", replace, -1)
}
<commit_msg>remove xxx<commit_after>package flow

// TODO(waigani) currently hardcoded to rewrite. Generalise this so it can be
// used for all Flow endpoints. It may be possible to use the command config
// structs to do this.

import (
	"bufio"
	"context"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	rewriterpc "github.com/codelingo/codelingo/flows/codelingo/rewrite/rpc"

	"github.com/briandowns/spinner"
	"github.com/codelingo/lingo/app/util"
	"github.com/codelingo/rpc/flow"
	"github.com/fatih/color"
	"github.com/juju/errors"
	"github.com/waigani/diffparser"
)

type hunkconfirmer struct {
	keepAll bool
	output  bool
}

func NewConfirmer(output, keepAll bool, d *diffparser.Diff) (*hunkconfirmer, error) {
	cfm := hunkconfirmer{
		keepAll: keepAll,
		output:  output,
	}

	return &cfm, nil
}

func ConfirmIssues(cancel context.CancelFunc, hunkc chan *rewriterpc.Hunk, errorc chan error, keepAll bool, saveToFile string) ([]*rewriterpc.Hunk, error) {
	defer util.Logger.Sync()

	var confirmedHunks []*rewriterpc.Hunk
	spnr := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
	spnr.Start()
	defer spnr.Stop()

	output := saveToFile == ""
	cfm, err := NewConfirmer(output, keepAll, nil)

	if err != nil {
		cancel()
		return nil, errors.Trace(err)
	}

	// If user is manually confirming reviews, set a long timeout.
	timeout := time.After(time.Hour * 1)
	if keepAll {
		timeout = time.After(time.Minute * 11)
	}

l:
	for {
		select {
		case err, ok := <-errorc:
			if !ok {
				errorc = nil
				break
			}

			// Abort review
			cancel()
			util.Logger.Debugf("error: %s", errors.ErrorStack(err))
			return nil, errors.Trace(err)
		case hunk, ok := <-hunkc:
			if !keepAll {
				spnr.Stop()
			}
			if !ok {
				hunkc = nil
				break
			}

			if cfm.Confirm(0, hunk) {
				confirmedHunks = append(confirmedHunks, hunk)
			}

			if !keepAll {
				spnr.Restart()
			}
		case <-timeout:
			cancel()
			return nil, errors.New("timed out waiting for response")
		}
		if hunkc == nil && errorc == nil {
			break l
		}
	}

	// Stop spinner if it hasn't been stopped already
	if keepAll {
		spnr.Stop()
	}
	return confirmedHunks, nil
}

// returns the full lines of the SRC for the hunk.
func fullLineSRC(hunk *flow.Issue, newSRC string) (string, error) {

	pos := hunk.GetPosition()
	startPos := pos.GetStart()
	endPos := pos.GetEnd()

	file, err := os.Open(startPos.Filename)
	if err != nil {
		return "", errors.Trace(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)

	var byt int64
	var src []byte
	var strtLineByt int64
	var endLineByt int64
	var foundStart bool
	var foundEnd bool
	for scanner.Scan() {
		src = append(src, append(scanner.Bytes(), []byte("\n")...)...)

		startByt := byt
		endByt := startByt + int64(len(scanner.Bytes())+1) // +1 for \n char

		if startPos.Offset >= startByt && startPos.Offset <= endByt {

			// found start line
			strtLineByt = startByt
			foundStart = true
		}

		if endPos.Offset >= startByt && endPos.Offset <= endByt {

			// found end line
			endLineByt = endByt
			foundEnd = true
		}

		if foundStart && foundEnd {

			// xxx.Print(string(src))
			// fmt.Printf("\n[%d:%d]", strtLineByt, startPos.Offset)
			beforeNewSRC := string(src[strtLineByt:startPos.Offset])
			endNewSRC := string(src[endPos.Offset:endLineByt])

			return beforeNewSRC + newSRC + endNewSRC, nil

		}

		byt = endByt
	}

	return "", errors.Trace(scanner.Err())
}

func GetDiffRootPath(filename string) string {
	// Get filename relative to git root folder
	// TODO: Handle error in case of git not being installed
	// https://github.com/codelingo/demo/hunks/2
	out, err := exec.Command("git", "ls-tree", "--full-name", "--name-only", "HEAD", filename).Output()
	if err == nil && len(out) != 0 {
		if len(out) != 0 {
			filename = strings.TrimSuffix(string(out), "\n")
		}
	}
	return filename
}

var editor string

// confirm returns true if the hunk should be kept or false if it should be
// dropped.
func (c hunkconfirmer) Confirm(attempt int, hunk *rewriterpc.Hunk) bool {
	if c.keepAll {
		return true
	}
	if attempt == 0 {
		fmt.Println(c.FormatPlainText(hunk))
	}
	attempt++
	var options string
	fmt.Print("\n[o]pen")
	if c.output {
		fmt.Print(" [d]iscard [k]eep")
	}
	fmt.Print(": ")

	fmt.Scanln(&options)

	switch options {
	case "o":
		var app string
		defaultEditor := "vi" // TODO(waigani) use EDITOR or VISUAL env vars
		// https://github.com/codelingo/demo/hunks/3
		if editor != "" {
			defaultEditor = editor
		}
		fmt.Printf("application (%s):", defaultEditor)
		fmt.Scanln(&app)
		filename := hunk.Filename
		if app == "" {
			app = defaultEditor
		}
		// c := hunk.Position.Start.Column // TODO(waigani) use column
		// https://github.com/codelingo/demo/hunks/4

		// TODO(waigani) calc line from offset
		l := int64(0)
		cmd, err := util.OpenFileCmd(app, filename, l)
		if err != nil {
			fmt.Println(err)
			return c.Confirm(attempt, hunk)
		}

		if err = cmd.Start(); err != nil {
			log.Println(err)
		}
		if err = cmd.Wait(); err != nil {
			log.Println(err)
		}

		editor = app

		c.Confirm(attempt, hunk)
	case "d":
		return false
	case "", "k", "K", "\n":
		return true
	default:
		fmt.Printf("invalid input: %s\n", options)
		fmt.Println(options)
		c.Confirm(attempt, hunk)
	}

	// TODO(waigani) build up hunks here.

	return true
}

func (c *hunkconfirmer) FormatPlainText(hunk *rewriterpc.Hunk) string {

	g := color.New(color.FgGreen).SprintfFunc()
	return indent(g("\n%s", hunk.SRC), true, false)

	// TODO(waigani) generate a diff hunk

	// m := color.New(color.FgWhite, color.Faint).SprintfFunc()
	// y := color.New(color.FgRed).SprintfFunc()
	// yf := color.New(color.FgWhite, color.Faint).SprintfFunc()
	// filename := hunk.Filename

	// // TODO(waigani) get line from offset
	// line := 0
	// addrFmtStr := fmt.Sprintf("%s:%d", filename, line)

	// // TODO(waigani) get column from offset
	// col := 0
	// addrFmtStr += fmt.Sprintf(":%d", col)
	// address := m(addrFmtStr)

	// ctxBefore := indent(yf("\n...\n%s", hunk.CtxBefore), false, false)
	// oldLines := indent(y("\n%s", hunk.LineText), false, true)

	// newLines := indent(g("\n%s", newSRC), true, false)
	// ctxAfter := indent(yf("\n%s\n...", hunk.CtxAfter), false, false)
	// src := ctxBefore + oldLines + newLines + ctxAfter

	// return fmt.Sprintf("%s\n%s", address, src)
}

func indent(str string, add, remove bool) string {
	replace := "\n    "
	if add {
		replace = "\n  + "
	}
	if remove {
		replace = "\n  - "
	}
	return strings.Replace(str, "\n", replace, -1)
}
<|endoftext|>


<commit_before>package service

import (
	"encoding/json"
	"net/http"
	"net/url"
	"strings"

	"github.com/garyburd/redigo/redis"
	"github.com/zenazn/goji/web"
)

// RequestError is an error that occurred during a request.
type RequestError struct {
	msg    string
	Status int
}

func (e *RequestError) Error() string {
	return e.msg
}

// RestdisResource represents a Restdis web service resource handler.
type RestdisResource struct {
	version string
	pool    *redis.Pool
	config  *Config
}

// Default is the default request handler.
func (r *RestdisResource) Default(c web.C, w http.ResponseWriter, req *http.Request) {
	err := writeJSON(map[string]string{"version": r.version}, w)
	if err != nil {
		c.Env["error"] = err
	}
}

// RedisCommand handles all requests for Redis commands.
func (r *RestdisResource) RedisCommand(c web.C, w http.ResponseWriter, req *http.Request) {
	command, args, err := buildRedisCommandFromURI(req.RequestURI)
	if err != nil {
		writeError(c, err, http.StatusInternalServerError)
		return
	}

	conn := r.pool.Get()
	defer conn.Close()

	res, err := conn.Do(command, args...)
	if err != nil {
		if res == nil {
			// If there's no response, it's most likely a Redis connection error.
			writeError(c, err, http.StatusInternalServerError)
			return
		}

		// Otherwise, the user entered an invalid command.
		writeError(c, err, http.StatusBadRequest)
		return
	}

	err = writeJSON(map[string]interface{}{
		"response": formatResponse(res),
	}, w)
	if err != nil {
		writeError(c, err, http.StatusInternalServerError)
	}
}

// writeError sets an error in the middleware context for the ErrorHandler middleware to handle later.
func writeError(c web.C, err error, status int) {
	c.Env[ErrorKey] = &RequestError{
		msg:    err.Error(),
		Status: status,
	}
}

func writeJSON(data interface{}, w http.ResponseWriter) error {
	j, err := json.Marshal(data)
	if err != nil {
		return &RequestError{
			msg:    err.Error(),
			Status: http.StatusInternalServerError,
		}
	}

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

	return nil
}

// buildRedisCommandFromURI splits a URI into a Redis command and arguments.
func buildRedisCommandFromURI(uri string) (string, redis.Args, error) {
	uri, err := url.QueryUnescape(uri)
	if err != nil {
		return "", nil, err
	}

	args := strings.Split(strings.Trim(uri, "/"), "/")
	redisCommand := args[0]
	redisArgs := redis.Args{}
	redisArgs = redisArgs.AddFlat(args[1:])

	return redisCommand, redisArgs, nil
}

// formatResponse formats the response from Redis into a usable map for converting to JSON.
func formatResponse(v interface{}) interface{} {
	switch v.(type) {
	case int64:
		return v.(int64)
	case string:
		return v.(string)
	case []byte:
		return string(v.([]byte)[:])
	case []interface{}:
		res := make([]interface{}, len(v.([]interface{})))

		for i, val := range v.([]interface{}) {
			res[i] = formatResponse(val)
		}

		return res
	default:
		return nil
	}
}
<commit_msg>Unescape args after splitting<commit_after>package service

import (
	"encoding/json"
	"net/http"
	"net/url"
	"strings"

	"github.com/garyburd/redigo/redis"
	"github.com/zenazn/goji/web"
)

// RequestError is an error that occurred during a request.
type RequestError struct {
	msg    string
	Status int
}

func (e *RequestError) Error() string {
	return e.msg
}

// RestdisResource represents a Restdis web service resource handler.
type RestdisResource struct {
	version string
	pool    *redis.Pool
	config  *Config
}

// Default is the default request handler.
func (r *RestdisResource) Default(c web.C, w http.ResponseWriter, req *http.Request) {
	err := writeJSON(map[string]string{"version": r.version}, w)
	if err != nil {
		c.Env["error"] = err
	}
}

// RedisCommand handles all requests for Redis commands.
func (r *RestdisResource) RedisCommand(c web.C, w http.ResponseWriter, req *http.Request) {
	command, args, err := buildRedisCommandFromURI(req.RequestURI)
	if err != nil {
		writeError(c, err, http.StatusInternalServerError)
		return
	}

	conn := r.pool.Get()
	defer conn.Close()

	res, err := conn.Do(command, args...)
	if err != nil {
		if res == nil {
			// If there's no response, it's most likely a Redis connection error.
			writeError(c, err, http.StatusInternalServerError)
			return
		}

		// Otherwise, the user entered an invalid command.
		writeError(c, err, http.StatusBadRequest)
		return
	}

	err = writeJSON(map[string]interface{}{
		"response": formatResponse(res),
	}, w)
	if err != nil {
		writeError(c, err, http.StatusInternalServerError)
	}
}

// writeError sets an error in the middleware context for the ErrorHandler middleware to handle later.
func writeError(c web.C, err error, status int) {
	c.Env[ErrorKey] = &RequestError{
		msg:    err.Error(),
		Status: status,
	}
}

func writeJSON(data interface{}, w http.ResponseWriter) error {
	j, err := json.Marshal(data)
	if err != nil {
		return &RequestError{
			msg:    err.Error(),
			Status: http.StatusInternalServerError,
		}
	}

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

	return nil
}

// buildRedisCommandFromURI splits a URI into a Redis command and arguments.
func buildRedisCommandFromURI(uri string) (string, redis.Args, error) {
	args := strings.Split(strings.Trim(uri, "/"), "/")

	for k, v := range args {
		v, err := url.QueryUnescape(v)
		if err != nil {
			return "", nil, err
		}

		args[k] = v
	}

	redisCommand := args[0]
	redisArgs := redis.Args{}
	redisArgs = redisArgs.AddFlat(args[1:])

	return redisCommand, redisArgs, nil
}

// formatResponse formats the response from Redis into a usable map for converting to JSON.
func formatResponse(v interface{}) interface{} {
	switch v.(type) {
	case int64:
		return v.(int64)
	case string:
		return v.(string)
	case []byte:
		return string(v.([]byte)[:])
	case []interface{}:
		res := make([]interface{}, len(v.([]interface{})))

		for i, val := range v.([]interface{}) {
			res[i] = formatResponse(val)
		}

		return res
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/config"
)

//go:generate go run constants.go

func main() {
	createGaugeProperties()
	filesToRead := map[string][]string{
		"defaultProperties": {"skel", "default.properties"},
		"exampleSpec":       {"skel", "example.spec"},
		"notice":            {"notice.md"},
		"gaugeProperties":   {"skel", common.GaugePropertiesFile},
		"gitignore":         {"skel", ".gitignore"},
	}
	goPath := os.Getenv("GOPATH")
	outF := filepath.Join(goPath, "src", "github.com", "getgauge", "gauge", "skel", "skel.go")
	out, err := os.Create(outF)
	if err != nil {
		log.Fatalf("Error creating %s\n", outF)
	}
	defer out.Close()
	out.WriteString("package skel\n\n")
	for k, v := range filesToRead {
		fp := filepath.Join(append([]string{"..", ".."}, v...)...)
		c, err := ioutil.ReadFile(fp)
		if err != nil {
			log.Fatalf("Error reading file %s\n", fp)
		}
		out.Write(append(append([]byte(fmt.Sprintf("var %s = `", k)), c...), []byte("`\n")...))
	}
}

func createGaugeProperties() {
	goPath := os.Getenv("GOPATH")
	outF := filepath.Join(goPath, "src", "github.com", "getgauge", "gauge", "skel", common.GaugePropertiesFile)
	out, err := os.Create(outF)
	if err != nil {
		log.Fatalf("Error creating %s\n", outF)
	}
	defer out.Close()
	if _, err := (config.Properties()).Write(out); err != nil {
		log.Fatalf("Error Writing %s\n", outF)
	}
}
<commit_msg>Read file form GOPATH in `go generate`<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/config"
)

//go:generate go run constants.go

func main() {
	createGaugeProperties()
	filesToRead := map[string][]string{
		"defaultProperties": {"skel", "default.properties"},
		"exampleSpec":       {"skel", "example.spec"},
		"notice":            {"notice.md"},
		"gaugeProperties":   {"skel", common.GaugePropertiesFile},
		"gitignore":         {"skel", ".gitignore"},
	}
	goPath := os.Getenv("GOPATH")
	outF := filepath.Join(goPath, "src", "github.com", "getgauge", "gauge", "skel", "skel.go")
	gaugePath := filepath.Join(goPath, "src", "github.com", "getgauge", "gauge")
	out, err := os.Create(outF)
	if err != nil {
		log.Fatalf("Error creating %s\n", outF)
	}
	defer out.Close()
	out.WriteString("package skel\n\n")
	for k, v := range filesToRead {
		fp := filepath.Join(append([]string{gaugePath}, v...)...)
		c, err := ioutil.ReadFile(fp)
		if err != nil {
			log.Fatalf("Error reading file %s\n", fp)
		}
		out.Write(append(append([]byte(fmt.Sprintf("var %s = `", k)), c...), []byte("`\n")...))
	}
}

func createGaugeProperties() {
	goPath := os.Getenv("GOPATH")
	outF := filepath.Join(goPath, "src", "github.com", "getgauge", "gauge", "skel", common.GaugePropertiesFile)
	out, err := os.Create(outF)
	if err != nil {
		log.Fatalf("Error creating %s\n", outF)
	}
	defer out.Close()
	if _, err := (config.Properties()).Write(out); err != nil {
		log.Fatalf("Error Writing %s\n", outF)
	}
}
<|endoftext|>


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

package markdowntohtml

import (
	"fmt"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/paths"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/allmark2/model"
	"github.com/andreaskoch/allmark2/services/conversion/filetreerenderer"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/audio"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/csvtable"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/filepreview"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/files"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/imagegallery"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/markdown"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/pdf"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/presentation"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/video"
	"regexp"
	"strings"
)

var (
	// [*description text*](*folder path*)
	markdownLinkPattern = regexp.MustCompile(`\[(.*)\]\(([^)]+)\)`)

	markdownItemLinkPattern = regexp.MustCompile(`\[(.*)\]\(/([^)]+)\)`)
)

type Converter struct {
	logger logger.Logger
}

func New(logger logger.Logger) (*Converter, error) {
	return &Converter{
		logger: logger,
	}, nil
}

// Convert the supplied item with all paths relative to the supplied base route
func (converter *Converter) Convert(pathProvider paths.Pather, item *model.Item) (convertedContent string, conversionError error) {

	converter.logger.Debug("Converting item %q.", item)

	itemRoute := *item.Route()
	content := item.Content

	// markdown extension: audio
	audioConverter := audio.New(pathProvider, item.Files())
	content, audioConversionError := audioConverter.Convert(content)
	if audioConversionError != nil {
		converter.logger.Warn("Error while converting audio extensions. Error: %s", audioConversionError)
	}

	// markdown extension: video
	videoConverter := video.New(pathProvider, item.Files())
	content, videoConversionError := videoConverter.Convert(content)
	if videoConversionError != nil {
		converter.logger.Warn("Error while converting video extensions. Error: %s", videoConversionError)
	}

	// markdown extension: pdf
	pdfConverter := pdf.New(pathProvider, item.Files())
	content, pdfConversionError := pdfConverter.Convert(content)
	if pdfConversionError != nil {
		converter.logger.Warn("Error while converting pdf extensions. Error: %s", pdfConversionError)
	}

	// markdown extension: files
	filesConverter := files.New(pathProvider, itemRoute, item.Files())
	content, filesConversionError := filesConverter.Convert(content)
	if filesConversionError != nil {
		converter.logger.Warn("Error while converting files extensions. Error: %s", filesConversionError)
	}

	// markdown extension: filepreview
	filePreviewConverter := filepreview.New(pathProvider, item.Files())
	content, filePreviewConversionError := filePreviewConverter.Convert(content)
	if filePreviewConversionError != nil {
		converter.logger.Warn("Error while converting file preview extensions. Error: %s", filePreviewConversionError)
	}

	// markdown extension: imagegallery
	imagegalleryConverter := imagegallery.New(pathProvider, itemRoute, item.Files())
	content, imagegalleryConversionError := imagegalleryConverter.Convert(content)
	if imagegalleryConversionError != nil {
		converter.logger.Warn("Error while converting image gallery extensions. Error: %s", imagegalleryConversionError)
	}

	// markdown extension: csv table
	csvTableConverter := csvtable.New(pathProvider, item.Files())
	content, csvTableConversionError := csvTableConverter.Convert(content)
	if csvTableConversionError != nil {
		converter.logger.Warn("Error while converting csv table extensions. Error: %s", csvTableConversionError)
	}

	// fix links
	content = converter.rewireLinks(pathProvider, item, content)

	// markdown to html
	content = markdown.Convert(content)

	// presentation
	if item.Type == model.TypePresentation {

		content := content
		presentationConverter := presentation.New(pathProvider, item.Files())
		content, presentationConversionError := presentationConverter.Convert(content)
		if presentationConversionError != nil {
			converter.logger.Warn("Error while converting presentation extensions. Error: %s", presentationConversionError)
		}

	}

	// append the file list
	if !item.IsVirtual() && len(item.Files()) > 0 {
		fileTreeRenderer := filetreerenderer.New(pathProvider, itemRoute, item.Files())
		content += fileTreeRenderer.Render("Attachments", "attachments", getBaseFolder(itemRoute, item.Files()))
	}

	return content, nil
}

func getBaseFolder(referenceRoute route.Route, files []*model.File) string {

	baseFolder := ""

	for _, file := range files {
		partialRoute := route.Intersect(referenceRoute, *file.Route())

		if baseFolder == "" {
			baseFolder = partialRoute.FirstComponentName()
			continue
		}

		// abort if the base folders differ
		if baseFolder != partialRoute.FirstComponentName() {
			return ""
		}
	}

	return baseFolder
}

func (converter *Converter) rewireLinks(pathProvider paths.Pather, item *model.Item, markdown string) string {

	allMatches := markdownLinkPattern.FindAllStringSubmatch(markdown, -1)
	for _, matches := range allMatches {

		if len(matches) != 3 {
			continue
		}

		// components
		originalText := strings.TrimSpace(matches[0])
		descriptionText := strings.TrimSpace(matches[1])
		path := strings.TrimSpace(matches[2])

		// get matching file
		matchingFile := getMatchingFiles(path, item)

		// skip if no matching files are found
		if matchingFile == nil {
			continue
		}

		// assemble the new link path
		fullFileRoute, err := route.Combine(matchingFile.Parent(), matchingFile.Route())
		if err != nil {
			converter.logger.Error("%s", err)
			continue
		}

		matchingFilePath := pathProvider.Path(fullFileRoute.Value())

		// assemble the new link
		newLinkText := fmt.Sprintf("[%s](%s)", descriptionText, matchingFilePath)

		// replace the old text
		markdown = strings.Replace(markdown, originalText, newLinkText, 1)

	}

	return markdown
}

func getMatchingFiles(path string, item *model.Item) *model.File {
	for _, file := range item.Files() {
		if file.Route().IsMatch(path) {
			return file
		}
	}

	return nil
}
<commit_msg>Fixed the single-file issue of the attachment tree for file-only items.<commit_after>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package markdowntohtml

import (
	"fmt"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/paths"
	"github.com/andreaskoch/allmark2/common/route"
	"github.com/andreaskoch/allmark2/model"
	"github.com/andreaskoch/allmark2/services/conversion/filetreerenderer"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/audio"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/csvtable"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/filepreview"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/files"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/imagegallery"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/markdown"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/pdf"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/presentation"
	"github.com/andreaskoch/allmark2/services/conversion/markdowntohtml/video"
	"regexp"
	"strings"
)

var (
	// [*description text*](*folder path*)
	markdownLinkPattern = regexp.MustCompile(`\[(.*)\]\(([^)]+)\)`)

	markdownItemLinkPattern = regexp.MustCompile(`\[(.*)\]\(/([^)]+)\)`)
)

type Converter struct {
	logger logger.Logger
}

func New(logger logger.Logger) (*Converter, error) {
	return &Converter{
		logger: logger,
	}, nil
}

// Convert the supplied item with all paths relative to the supplied base route
func (converter *Converter) Convert(pathProvider paths.Pather, item *model.Item) (convertedContent string, conversionError error) {

	converter.logger.Debug("Converting item %q.", item)

	itemRoute := *item.Route()
	content := item.Content

	// markdown extension: audio
	audioConverter := audio.New(pathProvider, item.Files())
	content, audioConversionError := audioConverter.Convert(content)
	if audioConversionError != nil {
		converter.logger.Warn("Error while converting audio extensions. Error: %s", audioConversionError)
	}

	// markdown extension: video
	videoConverter := video.New(pathProvider, item.Files())
	content, videoConversionError := videoConverter.Convert(content)
	if videoConversionError != nil {
		converter.logger.Warn("Error while converting video extensions. Error: %s", videoConversionError)
	}

	// markdown extension: pdf
	pdfConverter := pdf.New(pathProvider, item.Files())
	content, pdfConversionError := pdfConverter.Convert(content)
	if pdfConversionError != nil {
		converter.logger.Warn("Error while converting pdf extensions. Error: %s", pdfConversionError)
	}

	// markdown extension: files
	filesConverter := files.New(pathProvider, itemRoute, item.Files())
	content, filesConversionError := filesConverter.Convert(content)
	if filesConversionError != nil {
		converter.logger.Warn("Error while converting files extensions. Error: %s", filesConversionError)
	}

	// markdown extension: filepreview
	filePreviewConverter := filepreview.New(pathProvider, item.Files())
	content, filePreviewConversionError := filePreviewConverter.Convert(content)
	if filePreviewConversionError != nil {
		converter.logger.Warn("Error while converting file preview extensions. Error: %s", filePreviewConversionError)
	}

	// markdown extension: imagegallery
	imagegalleryConverter := imagegallery.New(pathProvider, itemRoute, item.Files())
	content, imagegalleryConversionError := imagegalleryConverter.Convert(content)
	if imagegalleryConversionError != nil {
		converter.logger.Warn("Error while converting image gallery extensions. Error: %s", imagegalleryConversionError)
	}

	// markdown extension: csv table
	csvTableConverter := csvtable.New(pathProvider, item.Files())
	content, csvTableConversionError := csvTableConverter.Convert(content)
	if csvTableConversionError != nil {
		converter.logger.Warn("Error while converting csv table extensions. Error: %s", csvTableConversionError)
	}

	// fix links
	content = converter.rewireLinks(pathProvider, item, content)

	// markdown to html
	content = markdown.Convert(content)

	// presentation
	if item.Type == model.TypePresentation {

		content := content
		presentationConverter := presentation.New(pathProvider, item.Files())
		content, presentationConversionError := presentationConverter.Convert(content)
		if presentationConversionError != nil {
			converter.logger.Warn("Error while converting presentation extensions. Error: %s", presentationConversionError)
		}

	}

	// append the file list
	if !item.IsVirtual() && len(item.Files()) > 0 {
		fileTreeRenderer := filetreerenderer.New(pathProvider, itemRoute, item.Files())
		fileBaseFolder := getBaseFolder(itemRoute, item.Files())
		content += fileTreeRenderer.Render("Attachments", "attachments", fileBaseFolder)
	}

	return content, nil
}

func getBaseFolder(referenceRoute route.Route, files []*model.File) string {

	baseFolder := ""

	for _, file := range files {
		partialRoute := route.Intersect(referenceRoute, *file.Route())

		if baseFolder == "" {
			if file.Route().LastComponentName() != partialRoute.FirstComponentName() {
				baseFolder = partialRoute.FirstComponentName()
			}

			continue
		}

		// abort if the base folders differ
		if baseFolder != partialRoute.FirstComponentName() {
			return ""
		}
	}

	return baseFolder
}

func (converter *Converter) rewireLinks(pathProvider paths.Pather, item *model.Item, markdown string) string {

	allMatches := markdownLinkPattern.FindAllStringSubmatch(markdown, -1)
	for _, matches := range allMatches {

		if len(matches) != 3 {
			continue
		}

		// components
		originalText := strings.TrimSpace(matches[0])
		descriptionText := strings.TrimSpace(matches[1])
		path := strings.TrimSpace(matches[2])

		// get matching file
		matchingFile := getMatchingFiles(path, item)

		// skip if no matching files are found
		if matchingFile == nil {
			continue
		}

		// assemble the new link path
		fullFileRoute, err := route.Combine(matchingFile.Parent(), matchingFile.Route())
		if err != nil {
			converter.logger.Error("%s", err)
			continue
		}

		matchingFilePath := pathProvider.Path(fullFileRoute.Value())

		// assemble the new link
		newLinkText := fmt.Sprintf("[%s](%s)", descriptionText, matchingFilePath)

		// replace the old text
		markdown = strings.Replace(markdown, originalText, newLinkText, 1)

	}

	return markdown
}

func getMatchingFiles(path string, item *model.Item) *model.File {
	for _, file := range item.Files() {
		if file.Route().IsMatch(path) {
			return file
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package db

import (
	"fmt"
	"os"
	osuser "os/user"
	"path"
	"path/filepath"
	"strings"

	"github.com/cfstras/cfmedias/config"
	"github.com/cfstras/cfmedias/errrs"
	log "github.com/cfstras/cfmedias/logger"
	"github.com/cfstras/go-taglib"
	"github.com/jinzhu/gorm"
)

type entry struct {
	Path     string
	Filename string
}

const bufferSize = 128

type updater struct {
	tx           chan *gorm.DB
	allFiles     chan entry // all files seen
	newFiles     chan entry // files not yet in db
	importFiles  chan entry // files to import
	success      chan bool
	stopStepping chan bool

	// the receiving goroutine shall increment these
	numAllFiles      int
	numNewFiles      int
	numImportFiles   int
	numInvalidFiles  int
	numFailedFiles   int
	numImportedFiles int
}

var IgnoredTypes = []string{
	"jpg", "jpeg", "png", "gif", "nfo", "m3u", "log", "sfv", "txt", "cue",
	"itc2", "html", "xml", "ipa", "asd", "plist", "itdb", "itl", "tmp", "ini",
	"sh", "sha1", "blb"}

func (d *DB) Update() {
	// keep file base up to date
	searchPath := config.Current.MediaPath
	if strings.Contains(searchPath, "~") {
		user, err := osuser.Current()
		if err != nil {
			log.Log.Println("Error getting user home directory:", err)
			return
		}
		searchPath = strings.Replace(searchPath, "~", user.HomeDir, -1)
	}
	if _, err := os.Stat(searchPath); os.IsNotExist(err) {
		log.Log.Println("Error: Music path", searchPath, "does not exist!")
		return
	}
	tx := d.db.Begin()
	up := &updater{tx: make(chan *gorm.DB, 1),
		allFiles:     make(chan entry, bufferSize),
		newFiles:     make(chan entry, bufferSize),
		importFiles:  make(chan entry, bufferSize),
		success:      make(chan bool),
		stopStepping: make(chan bool, 1)}
	up.tx <- tx

	go func() {
		err := filepath.Walk(searchPath, up.step)
		if err != nil {
			log.Log.Println("Updater error:", err)
		}
		close(up.allFiles)
	}()

	// filter out ignored
	go func(input, output chan entry) {
		for entry := range input {
			up.numAllFiles++
			//fmt.Println("suffix filter gets:", entry)
			do := true
			for _, v := range IgnoredTypes {
				if strings.HasSuffix(entry.Filename, v) {
					//TODO do something with the cover jpgs
					do = false
					break
				}
			}
			if do {
				output <- entry
			}
		}
		close(output)
	}(up.allFiles, up.importFiles)

	// filter out already inserted files
	go func(input, output chan entry) {
		for entry := range input {
			up.numImportFiles++
			//fmt.Println("seen filter gets:", entry)
			// check if we already did this one
			tx := <-up.tx
			rows, err := d.db.Table(ItemTable).
				Select(ItemTable + ".id, " +
				ItemTable + ".filename, " +
				FolderTable + ".path").
				Joins("join " + FolderTable + " on " +
				FolderTable + ".id = " + ItemTable + ".folder_id").
				Where(entry).
				Rows()
			if err != nil {
				fmt.Println("sql error:", err)
				up.success <- false
			}
			up.tx <- tx
			if rows.Next() {
				// this one is already in the db
				//TODO check if the tags have changed anyway
				//log.Log.Println("skipping", entry)
			} else {
				output <- entry
			}
		}
		close(output)
	}(up.importFiles, up.newFiles)

	// analyze remaining files
	go func(input chan entry, success chan bool) {
		for entry := range input {
			up.numNewFiles++
			err := up.analyze(path.Join(entry.Path, entry.Filename),
				entry.Path, entry.Filename)
			if err != nil {
				up.numFailedFiles++
				fmt.Println("import error: ", err)
			}
		}
		up.success <- true
		close(up.success)
	}(up.newFiles, up.success)

	success := true
	for v := range up.success {
		if !v {
			up.stopStepping <- true
			success = false
			break
			if err := tx.Rollback(); err != nil {
				log.Log.Println("rollback error:", err)
			}
		}
	}
	if success {
		tx := <-up.tx
		if err := tx.Commit().Error; err != nil {
			log.Log.Println("Updater error:", err)
		}
	}
	log.Log.Println("Filebase updated:",
		"\nTotal Files:      ", up.numAllFiles,
		"\nNon-ignored Files:", up.numImportFiles,
		"\nNew Files:        ", up.numNewFiles,
		"\nImported Files:   ", up.numImportedFiles,
		"\nInvalid/Non-media:", up.numInvalidFiles,
		"\nFailed Files:     ", up.numFailedFiles)
}

func (up *updater) step(file string, info os.FileInfo, err error) error {
	if info == nil ||
		info.Name() == "." ||
		info.Name() == ".." {
		return nil
	}
	if info.IsDir() {
		//log.Log.Println("in", file)
	} else if linked, err := filepath.EvalSymlinks(file); err != nil || file != linked {
		if err != nil {
			log.Log.Println("Error walking files:", err)
			return nil
		}
		filepath.Walk(linked, up.step)
	} else if !strings.HasPrefix(info.Name(), ".") {
		select {
		case <-up.stopStepping:
			return errrs.New("aborting")
		case up.allFiles <- entry{path.Dir(file), info.Name()}:
		}
	}
	return nil
}

func (up *updater) analyze(path string, parent string, file string) error {
	//log.Log.Println("doing", path)

	tag, err := taglib.Read(path)
	if err != nil {
		log.Log.Println("error reading file", path, "-", err)
		up.numInvalidFiles++
		return nil
	}

	defer tag.Close()

	title := tag.Title()
	artist := tag.Artist()
	if title == nil || artist == nil {
		return errrs.New("Title and Artist cannot be nil. File " + path)
	}
	item := Item{
		Title:       *title,
		Artist:      *artist,
		AlbumArtist: NullStr(nil),
		Album:       NullStr(tag.Album()),
		Genre:       NullStr(tag.Genre()),
		TrackNumber: uint32(tag.Track()),
		Folder:      Folder{Path: parent},
		Filename:    NullStr(&file),
	}
	//TODO get album, check ID etc

	tx := <-up.tx
	err = tx.FirstOrCreate(&item.Folder, item.Folder).Error
	if err != nil {
		log.Log.Println("error inserting folder", item.Folder, err)
		return err
	}
	err = tx.Save(&item).Error
	up.tx <- tx
	if err != nil {
		log.Log.Println("error inserting item", item, err)
	} else {
		up.numImportedFiles++
		log.Log.Println("inserted", item)
	}
	return err
}
<commit_msg>filebase: bugfix and perf increase<commit_after>package db

import (
	"fmt"
	"os"
	osuser "os/user"
	"path"
	"path/filepath"
	"strings"

	"github.com/cfstras/cfmedias/config"
	"github.com/cfstras/cfmedias/errrs"
	log "github.com/cfstras/cfmedias/logger"
	"github.com/cfstras/go-taglib"
	"github.com/jinzhu/gorm"
)

type entry struct {
	Path     string
	Filename string
}

const bufferSize = 128

type updater struct {
	tx           chan *gorm.DB
	allFiles     chan entry // all files seen
	newFiles     chan entry // files not yet in db
	importFiles  chan entry // files to import
	success      chan bool
	stopStepping chan bool

	// the receiving goroutine shall increment these
	numAllFiles      int
	numNewFiles      int
	numImportFiles   int
	numInvalidFiles  int
	numFailedFiles   int
	numImportedFiles int
}

var IgnoredTypes = []string{
	"jpg", "jpeg", "png", "gif", "nfo", "m3u", "log", "sfv", "txt", "cue",
	"itc2", "html", "xml", "ipa", "asd", "plist", "itdb", "itl", "tmp", "ini",
	"sh", "sha1", "blb"}

func (d *DB) Update() {
	// keep file base up to date
	searchPath := config.Current.MediaPath
	if strings.Contains(searchPath, "~") {
		user, err := osuser.Current()
		if err != nil {
			log.Log.Println("Error getting user home directory:", err)
			return
		}
		searchPath = strings.Replace(searchPath, "~", user.HomeDir, -1)
	}
	if _, err := os.Stat(searchPath); os.IsNotExist(err) {
		log.Log.Println("Error: Music path", searchPath, "does not exist!")
		return
	}
	tx := d.db.Begin()
	up := &updater{tx: make(chan *gorm.DB, 1),
		allFiles:     make(chan entry, bufferSize),
		newFiles:     make(chan entry, bufferSize),
		importFiles:  make(chan entry, bufferSize),
		success:      make(chan bool),
		stopStepping: make(chan bool, 1)}
	up.tx <- tx

	go func() {
		err := filepath.Walk(searchPath, up.step)
		if err != nil {
			log.Log.Println("Updater error:", err)
		}
		close(up.allFiles)
	}()

	// filter out ignored
	go func(input, output chan entry) {
		for entry := range input {
			up.numAllFiles++
			//fmt.Println("suffix filter gets:", entry)
			do := true
			for _, v := range IgnoredTypes {
				if strings.HasSuffix(entry.Filename, v) {
					//TODO do something with the cover jpgs
					do = false
					break
				}
			}
			if do {
				output <- entry
			}
		}
		close(output)
	}(up.allFiles, up.importFiles)

	// filter out already inserted files
	go func(input, output chan entry) {
		for entry := range input {
			up.numImportFiles++
			//fmt.Println("seen filter gets:", entry)
			// check if we already did this one
			tx := <-up.tx
			var c int
			err := d.db.Table(ItemTable).
				Joins("join " + FolderTable + " on " +
				FolderTable + ".id = " + ItemTable + ".folder_id").
				Where(entry).Count(&c).Error
			if err != nil {
				fmt.Println("sql error:", err)
				up.success <- false
			}
			up.tx <- tx
			if c != 0 {
				// this one is already in the db
				//TODO check if the tags have changed anyway
				//log.Log.Println("skipping", entry)
			} else {
				output <- entry
			}
		}
		close(output)
	}(up.importFiles, up.newFiles)

	// analyze remaining files
	go func(input chan entry, success chan bool) {
		for entry := range input {
			up.numNewFiles++
			err := up.analyze(path.Join(entry.Path, entry.Filename),
				entry.Path, entry.Filename)
			if err != nil {
				up.numFailedFiles++
				fmt.Println("import error: ", err)
			}
		}
		up.success <- true
		close(up.success)
	}(up.newFiles, up.success)

	success := true
	for v := range up.success {
		if !v {
			up.stopStepping <- true
			success = false
			break
			if err := tx.Rollback(); err != nil {
				log.Log.Println("rollback error:", err)
			}
		}
	}
	if success {
		tx := <-up.tx
		if err := tx.Commit().Error; err != nil {
			log.Log.Println("Updater error:", err)
		}
	}
	log.Log.Println("Filebase updated:",
		"\nTotal Files:      ", up.numAllFiles,
		"\nNon-ignored Files:", up.numImportFiles,
		"\nNew Files:        ", up.numNewFiles,
		"\nImported Files:   ", up.numImportedFiles,
		"\nInvalid/Non-media:", up.numInvalidFiles,
		"\nFailed Files:     ", up.numFailedFiles)
}

func (up *updater) step(file string, info os.FileInfo, err error) error {
	if info == nil ||
		info.Name() == "." ||
		info.Name() == ".." {
		return nil
	}
	if info.IsDir() {
		//log.Log.Println("in", file)
	} else if linked, err := filepath.EvalSymlinks(file); err != nil || file != linked {
		if err != nil {
			log.Log.Println("Error walking files:", err)
			return nil
		}
		filepath.Walk(linked, up.step)
	} else if !strings.HasPrefix(info.Name(), ".") {
		select {
		case <-up.stopStepping:
			return errrs.New("aborting")
		case up.allFiles <- entry{path.Dir(file), info.Name()}:
		}
	}
	return nil
}

func (up *updater) analyze(path string, parent string, file string) error {
	//log.Log.Println("doing", path)

	tag, err := taglib.Read(path)
	if err != nil {
		log.Log.Println("error reading file", path, "-", err)
		up.numInvalidFiles++
		return nil
	}

	defer tag.Close()

	title := tag.Title()
	artist := tag.Artist()
	if title == nil || artist == nil {
		return errrs.New("Title and Artist cannot be nil. File " + path)
	}
	item := Item{
		Title:       *title,
		Artist:      *artist,
		AlbumArtist: NullStr(nil),
		Album:       NullStr(tag.Album()),
		Genre:       NullStr(tag.Genre()),
		TrackNumber: uint32(tag.Track()),
		Folder:      Folder{Path: parent},
		Filename:    NullStr(&file),
	}
	//TODO get album, check ID etc

	tx := <-up.tx
	err = tx.FirstOrCreate(&item.Folder, item.Folder).Error
	if err != nil {
		log.Log.Println("error inserting folder", item.Folder, err)
		return err
	}
	err = tx.Save(&item).Error
	up.tx <- tx
	if err != nil {
		log.Log.Println("error inserting item", item, err)
	} else {
		up.numImportedFiles++
		log.Log.Println("inserted", item)
	}
	return err
}
<|endoftext|>