<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"log"
	"math"
	"os"
	"strings"
	"sync"
)

const (
	DEFAULT_DURATION = 24 * 60 * 60
	DEFAULT_DT       = 60
)

type Storage struct {
	metrics  map[string]*Metric
	duration int
	dt       int
}

type Metric struct {
	sync.RWMutex
	array       []float32
	dt          int
	duration    int
	latest_i    int
	latest_ts_k int64 // == timestamp / dt
	splitName   []string
}

type StoredMetric struct {
	Array       []float32
	Dt          int
	Duration    int
	Latest_i    int
	Latest_ts_k int64
}

func NewStorage() *Storage {
	s := new(Storage)
	s.duration = DEFAULT_DURATION
	s.dt = DEFAULT_DT
	s.metrics = make(map[string]*Metric)
	return s
}

func NewMetric(duration, dt int, starting_ts int64, name string) *Metric {
	m := new(Metric)
	m.splitName = strings.Split(name, ".")
	m.latest_i = 0
	m.duration = duration
	m.dt = dt
	m.array = make([]float32, duration/dt)
	m.latest_ts_k = starting_ts / int64(dt)
	return m
}

func (self *Storage) StoreMetric(metric_name string, value float64, ts int64) {
	metric, ok := self.metrics[metric_name]
	if !ok {
		metric = NewMetric(self.duration, self.dt, ts, metric_name)
		metric.array[0] += float32(value)
		self.metrics[metric_name] = metric
		return
	}
	metric.Store(float32(value), ts)
}

func (self *Storage) RemoveMetric(metric_name string) {
	delete(self.metrics, metric_name)
}

func (self *Storage) MetricCount() int {
	return len(self.metrics)
}

func (self *Storage) SetStorageParams(duration_hours int, precision_seconds int) {
	if duration_hours <= 0 {
		log.Fatal("duration must be greater than zero")
	}
	if precision_seconds <= 0 {
		log.Fatal("precision must be greater than zero")
	}
	self.duration = duration_hours * 60 * 60
	self.dt = precision_seconds
}

func matchesPattern(s []string, pattern []string) bool {
	if len(s) != len(pattern) {
		return false
	}
	for i := range s {
		if pattern[i] != "*" && s[i] != pattern[i] {
			return false
		}
	}
	return true
}

func (self *Storage) SumByPeriodGroupingQuery(metric_group_patterns []string, periods []int64, now int64, interpolate bool) [][]float64 {
	sums := make([][]float64, len(metric_group_patterns))
	split_patterns := make([][]string, len(metric_group_patterns))
	for i := range metric_group_patterns {
		sums[i] = make([]float64, len(periods))
		split_patterns[i] = strings.Split(metric_group_patterns[i], ".")
	}

	for _, m := range self.metrics {
		for i := range split_patterns {
			if matchesPattern(m.splitName, split_patterns[i]) {
				this_metric_sum := m.GetSumsPerPeriodUntilNowWithInterpolation(periods, now, interpolate)
				for j := range periods {
					sums[i][j] += this_metric_sum[j]
				}
				break // assume metric can match only one pattern
			}
		}
	}
	return sums
}

func (self *Storage) SaveToFile(filename string) error {
	temppath := filename + ".tmp"
	tempfile, err := os.Create(temppath)
	if err != nil {
		return err
	}

	enc := gob.NewEncoder(tempfile)
	err = enc.Encode(self.metrics)
	if err != nil {
		tempfile.Close()
		os.Remove(temppath)
		return err
	}
	tempfile.Close()
	err = os.Rename(temppath, filename)
	if err != nil {
		os.Remove(temppath)
		return err
	}
	return nil
}

func (self *Storage) LoadFromFile(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	dec := gob.NewDecoder(file)
	err = dec.Decode(&self.metrics)
	if err != nil {
		return err
	}
	return nil
}

func (self *Metric) Store(value float32, ts int64) {
	self.Lock()
	defer self.Unlock()
	dt_64 := int64(self.dt)
	ts_k := ts / dt_64
	/*log.Printf("(%f, %d) ts_k %d, latest_ts_k %d", value, ts, ts_k, self.latest_ts_k)*/
	if self.latest_ts_k > ts_k {
		// amend value in the past
		i := int64(self.latest_i) - (self.latest_ts_k - ts_k)
		if i < 0 {
			i += int64(len(self.array))
		}
		if i < 0 {
			// falls outside the storage period
			return
		}
		self.array[i] += value
		return
	}
	if ts_k > self.latest_ts_k + int64(len(self.array)) {
		// jump into the future, might as well erase the entire array and start over
		self.latest_ts_k = ts_k
		self.latest_i = 0
		self.array[0] = value
		return
	}
	for self.latest_ts_k < ts_k {
		self.latest_i = (self.latest_i + 1) % len(self.array)
		self.array[self.latest_i] = 0.0
		self.latest_ts_k += 1
	}
	self.array[self.latest_i] += value
}

func (self *Metric) GetValueAt(ts int64) float64 {
	self.RLock()
	defer self.RUnlock()
	ts_k := ts / int64(self.dt)
	if ts_k <= self.latest_ts_k-int64(len(self.array)) || ts_k > self.latest_ts_k {
		return 0.0
	}
	d_ts_k := self.latest_ts_k - ts_k
	i := (self.latest_i - int(d_ts_k))
	if i < 0 {
		i += len(self.array)
	}
	/*log.Printf("ts %v, ts_k %v, self.latest_ts_k %v --> i %v", ts, ts_k, self.latest_ts_k, i)*/
	return float64(self.array[i])
}

func (self *Metric) GetSumBetween(ts1 int64, ts2 int64) float64 {
	self.RLock()
	defer self.RUnlock()
	ts1_k := ts1 / int64(self.dt)
	ts2_k := ts2 / int64(self.dt)
	if ts2_k <= self.latest_ts_k-int64(len(self.array)) || ts1_k > self.latest_ts_k {
		return 0.0
	}

	if ts1_k <= self.latest_ts_k-int64(len(self.array)) {
		ts1_k = self.latest_ts_k - int64(len(self.array)) + 1
	}
	if ts2_k > self.latest_ts_k {
		ts2_k = self.latest_ts_k
	}

	d_ts1_k := self.latest_ts_k - ts1_k
	i := (self.latest_i - int(d_ts1_k))
	if i < 0 {
		i += len(self.array)
	}

	sum := 0.0
	for ts1_k <= ts2_k {
		sum += float64(self.array[i])
		i = (i + 1) % len(self.array)
		ts1_k += 1
	}
	return sum
}

func (self *Metric) GetSumForLastNSeconds(seconds int64, now_ts int64) float64 {
	ts2 := now_ts + int64(self.dt)
	ts1 := now_ts - seconds
	return self.GetSumBetween(ts1, ts2)
}

func (self *Metric) GetSumsPerPeriodUntilNow(periods []int64, now int64) []float64 {
	return self.GetSumsPerPeriodUntilNowWithInterpolation(periods, now, false)
}

func (self *Metric) GetSumsPerPeriodUntilNowWithInterpolation(periods []int64, now int64, interpolate bool) []float64 {
	self.RLock()
	defer self.RUnlock()
	dt_64 := int64(self.dt)
	now_k := now / dt_64
	period_starts_k := make([]int64, len(periods))
	period_sums := make([]float64, len(periods))
	min_k := now_k
	k_intr := float64(now-now_k*dt_64) / float64(self.dt)
	if now == now_k*dt_64 {
		k_intr = 1.0
	}

	for i := range periods {
		period_start_ts := now - periods[i]
		period_starts_k[i] = int64(math.Ceil(float64(period_start_ts) / float64(dt_64)))
		if period_starts_k[i] < min_k {
			min_k = period_starts_k[i]
		}
		period_sums[i] = 0.0
		if interpolate {
			additional_piece := (1 - k_intr) * self.GetValueAt(period_start_ts)
			period_sums[i] += additional_piece
		}
	}

	if now_k <= self.latest_ts_k-int64(len(self.array)) || min_k > self.latest_ts_k {
		return period_sums
	}

	if min_k <= self.latest_ts_k-int64(len(self.array)) {
		min_k = self.latest_ts_k - int64(len(self.array)) + 1
	}

	d_min_k := self.latest_ts_k - min_k
	i := (self.latest_i - int(d_min_k))
	if i < 0 {
		i += len(self.array)
	}

	for min_k <= now_k {
		var current_val float64
		if min_k <= self.latest_ts_k {
			current_val = float64(self.array[i])
		} else {
			current_val = 0.0
		}
		for j := range periods {
			if period_starts_k[j] <= min_k {
				period_sums[j] += current_val
			}
		}
		i = (i + 1) % len(self.array)
		min_k += 1
	}
	return period_sums
}

func (self *Metric) GobEncode() ([]byte, error) {
	var sm StoredMetric
	var buf bytes.Buffer
	sm.Array = self.array
	sm.Dt = self.dt
	sm.Duration = self.duration
	sm.Latest_i = self.latest_i
	sm.Latest_ts_k = self.latest_ts_k
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(&sm)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (self *Metric) GobDecode(_bytes []byte) error {
	var sm StoredMetric
	buf := bytes.NewBuffer(_bytes)
	dec := gob.NewDecoder(buf)
	err := dec.Decode(&sm)
	if err != nil {
		return err
	}
	self.array = sm.Array
	self.dt = sm.Dt
	self.duration = sm.Duration
	self.latest_i = sm.Latest_i
	self.latest_ts_k = sm.Latest_ts_k
	return nil
}
<commit_msg>Empty all buckets on far future value<commit_after>package main

import (
	"bytes"
	"encoding/gob"
	"log"
	"math"
	"os"
	"strings"
	"sync"
)

const (
	DEFAULT_DURATION = 24 * 60 * 60
	DEFAULT_DT       = 60
)

type Storage struct {
	metrics  map[string]*Metric
	duration int
	dt       int
}

type Metric struct {
	sync.RWMutex
	array       []float32
	dt          int
	duration    int
	latest_i    int
	latest_ts_k int64 // == timestamp / dt
	splitName   []string
}

type StoredMetric struct {
	Array       []float32
	Dt          int
	Duration    int
	Latest_i    int
	Latest_ts_k int64
}

func NewStorage() *Storage {
	s := new(Storage)
	s.duration = DEFAULT_DURATION
	s.dt = DEFAULT_DT
	s.metrics = make(map[string]*Metric)
	return s
}

func NewMetric(duration, dt int, starting_ts int64, name string) *Metric {
	m := new(Metric)
	m.splitName = strings.Split(name, ".")
	m.latest_i = 0
	m.duration = duration
	m.dt = dt
	m.array = make([]float32, duration/dt)
	m.latest_ts_k = starting_ts / int64(dt)
	return m
}

func (self *Storage) StoreMetric(metric_name string, value float64, ts int64) {
	metric, ok := self.metrics[metric_name]
	if !ok {
		metric = NewMetric(self.duration, self.dt, ts, metric_name)
		metric.array[0] += float32(value)
		self.metrics[metric_name] = metric
		return
	}
	metric.Store(float32(value), ts)
}

func (self *Storage) RemoveMetric(metric_name string) {
	delete(self.metrics, metric_name)
}

func (self *Storage) MetricCount() int {
	return len(self.metrics)
}

func (self *Storage) SetStorageParams(duration_hours int, precision_seconds int) {
	if duration_hours <= 0 {
		log.Fatal("duration must be greater than zero")
	}
	if precision_seconds <= 0 {
		log.Fatal("precision must be greater than zero")
	}
	self.duration = duration_hours * 60 * 60
	self.dt = precision_seconds
}

func matchesPattern(s []string, pattern []string) bool {
	if len(s) != len(pattern) {
		return false
	}
	for i := range s {
		if pattern[i] != "*" && s[i] != pattern[i] {
			return false
		}
	}
	return true
}

func (self *Storage) SumByPeriodGroupingQuery(metric_group_patterns []string, periods []int64, now int64, interpolate bool) [][]float64 {
	sums := make([][]float64, len(metric_group_patterns))
	split_patterns := make([][]string, len(metric_group_patterns))
	for i := range metric_group_patterns {
		sums[i] = make([]float64, len(periods))
		split_patterns[i] = strings.Split(metric_group_patterns[i], ".")
	}

	for _, m := range self.metrics {
		for i := range split_patterns {
			if matchesPattern(m.splitName, split_patterns[i]) {
				this_metric_sum := m.GetSumsPerPeriodUntilNowWithInterpolation(periods, now, interpolate)
				for j := range periods {
					sums[i][j] += this_metric_sum[j]
				}
				break // assume metric can match only one pattern
			}
		}
	}
	return sums
}

func (self *Storage) SaveToFile(filename string) error {
	temppath := filename + ".tmp"
	tempfile, err := os.Create(temppath)
	if err != nil {
		return err
	}

	enc := gob.NewEncoder(tempfile)
	err = enc.Encode(self.metrics)
	if err != nil {
		tempfile.Close()
		os.Remove(temppath)
		return err
	}
	tempfile.Close()
	err = os.Rename(temppath, filename)
	if err != nil {
		os.Remove(temppath)
		return err
	}
	return nil
}

func (self *Storage) LoadFromFile(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	dec := gob.NewDecoder(file)
	err = dec.Decode(&self.metrics)
	if err != nil {
		return err
	}
	return nil
}

func (self *Metric) Store(value float32, ts int64) {
	self.Lock()
	defer self.Unlock()
	dt_64 := int64(self.dt)
	ts_k := ts / dt_64
	/*log.Printf("(%f, %d) ts_k %d, latest_ts_k %d", value, ts, ts_k, self.latest_ts_k)*/
	if self.latest_ts_k > ts_k {
		// amend value in the past
		i := int64(self.latest_i) - (self.latest_ts_k - ts_k)
		if i < 0 {
			i += int64(len(self.array))
		}
		if i < 0 {
			// falls outside the storage period
			return
		}
		self.array[i] += value
		return
	}
	if ts_k > self.latest_ts_k+int64(len(self.array)) {
		// jump into the future, might as well erase the entire array and start over
		self.latest_ts_k = ts_k
		self.latest_i = 0
		for i := range self.array {
			self.array[i] = 0.0
		}
		self.array[0] = value
		return
	}
	for self.latest_ts_k < ts_k {
		self.latest_i = (self.latest_i + 1) % len(self.array)
		self.array[self.latest_i] = 0.0
		self.latest_ts_k += 1
	}
	self.array[self.latest_i] += value
}

func (self *Metric) GetValueAt(ts int64) float64 {
	self.RLock()
	defer self.RUnlock()
	ts_k := ts / int64(self.dt)
	if ts_k <= self.latest_ts_k-int64(len(self.array)) || ts_k > self.latest_ts_k {
		return 0.0
	}
	d_ts_k := self.latest_ts_k - ts_k
	i := (self.latest_i - int(d_ts_k))
	if i < 0 {
		i += len(self.array)
	}
	/*log.Printf("ts %v, ts_k %v, self.latest_ts_k %v --> i %v", ts, ts_k, self.latest_ts_k, i)*/
	return float64(self.array[i])
}

func (self *Metric) GetSumBetween(ts1 int64, ts2 int64) float64 {
	self.RLock()
	defer self.RUnlock()
	ts1_k := ts1 / int64(self.dt)
	ts2_k := ts2 / int64(self.dt)
	if ts2_k <= self.latest_ts_k-int64(len(self.array)) || ts1_k > self.latest_ts_k {
		return 0.0
	}

	if ts1_k <= self.latest_ts_k-int64(len(self.array)) {
		ts1_k = self.latest_ts_k - int64(len(self.array)) + 1
	}
	if ts2_k > self.latest_ts_k {
		ts2_k = self.latest_ts_k
	}

	d_ts1_k := self.latest_ts_k - ts1_k
	i := (self.latest_i - int(d_ts1_k))
	if i < 0 {
		i += len(self.array)
	}

	sum := 0.0
	for ts1_k <= ts2_k {
		sum += float64(self.array[i])
		i = (i + 1) % len(self.array)
		ts1_k += 1
	}
	return sum
}

func (self *Metric) GetSumForLastNSeconds(seconds int64, now_ts int64) float64 {
	ts2 := now_ts + int64(self.dt)
	ts1 := now_ts - seconds
	return self.GetSumBetween(ts1, ts2)
}

func (self *Metric) GetSumsPerPeriodUntilNow(periods []int64, now int64) []float64 {
	return self.GetSumsPerPeriodUntilNowWithInterpolation(periods, now, false)
}

func (self *Metric) GetSumsPerPeriodUntilNowWithInterpolation(periods []int64, now int64, interpolate bool) []float64 {
	self.RLock()
	defer self.RUnlock()
	dt_64 := int64(self.dt)
	now_k := now / dt_64
	period_starts_k := make([]int64, len(periods))
	period_sums := make([]float64, len(periods))
	min_k := now_k
	k_intr := float64(now-now_k*dt_64) / float64(self.dt)
	if now == now_k*dt_64 {
		k_intr = 1.0
	}

	for i := range periods {
		period_start_ts := now - periods[i]
		period_starts_k[i] = int64(math.Ceil(float64(period_start_ts) / float64(dt_64)))
		if period_starts_k[i] < min_k {
			min_k = period_starts_k[i]
		}
		period_sums[i] = 0.0
		if interpolate {
			additional_piece := (1 - k_intr) * self.GetValueAt(period_start_ts)
			period_sums[i] += additional_piece
		}
	}

	if now_k <= self.latest_ts_k-int64(len(self.array)) || min_k > self.latest_ts_k {
		return period_sums
	}

	if min_k <= self.latest_ts_k-int64(len(self.array)) {
		min_k = self.latest_ts_k - int64(len(self.array)) + 1
	}

	d_min_k := self.latest_ts_k - min_k
	i := (self.latest_i - int(d_min_k))
	if i < 0 {
		i += len(self.array)
	}

	for min_k <= now_k {
		var current_val float64
		if min_k <= self.latest_ts_k {
			current_val = float64(self.array[i])
		} else {
			current_val = 0.0
		}
		for j := range periods {
			if period_starts_k[j] <= min_k {
				period_sums[j] += current_val
			}
		}
		i = (i + 1) % len(self.array)
		min_k += 1
	}
	return period_sums
}

func (self *Metric) GobEncode() ([]byte, error) {
	var sm StoredMetric
	var buf bytes.Buffer
	sm.Array = self.array
	sm.Dt = self.dt
	sm.Duration = self.duration
	sm.Latest_i = self.latest_i
	sm.Latest_ts_k = self.latest_ts_k
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(&sm)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (self *Metric) GobDecode(_bytes []byte) error {
	var sm StoredMetric
	buf := bytes.NewBuffer(_bytes)
	dec := gob.NewDecoder(buf)
	err := dec.Decode(&sm)
	if err != nil {
		return err
	}
	self.array = sm.Array
	self.dt = sm.Dt
	self.duration = sm.Duration
	self.latest_i = sm.Latest_i
	self.latest_ts_k = sm.Latest_ts_k
	return nil
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

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

   This program 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 Affero General Public License for more details.

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

package monitor

import (
	"github.com/percona/percona-agent/mysql"
	"github.com/percona/percona-agent/pct"
	"sync"
	"time"
)

type MysqlInstance struct {
	logger      *pct.Logger
	mysqlConn   mysql.Connector
	Subscribers *Subscribers
	// --
	lastUptime      int64
	lastUptimeCheck time.Time
	sync.Mutex
}

func NewMysqlInstance(logger *pct.Logger, mysqlConn mysql.Connector, subscribers *Subscribers) (mi *MysqlInstance, err error) {
	if err := mysqlConn.Connect(2); err != nil {
		logger.Warn("Unable to connect to MySQL:", err)
		return nil, err
	}
	defer mysqlConn.Close()

	// Get current MySQL uptime - this is later used to detect if MySQL was restarted
	lastUptime := mysqlConn.Uptime()
	lastUptimeCheck := time.Now()

	mi = &MysqlInstance{
		logger:          logger,
		mysqlConn:       mysqlConn,
		Subscribers:     subscribers,
		lastUptime:      lastUptime,
		lastUptimeCheck: lastUptimeCheck,
	}

	return mi, nil
}

func (m *MysqlInstance) CheckIfMysqlRestarted() bool {
	m.Lock()
	defer m.Unlock()

	lastUptime := m.lastUptime
	lastUptimeCheck := m.lastUptimeCheck
	currentUptime := m.mysqlConn.Uptime()

	// Calculate expected uptime
	//   This protects against situation where after restarting MySQL
	//   we are unable to connect to it for period longer than last registered uptime
	//
	// Steps to reproduce:
	// * currentUptime=60 lastUptime=0
	// * Restart MySQL
	// * QAN connection problem for 120s
	// * currentUptime=120 lastUptime=60 (new uptime (120s) is higher than last registered (60s))
	// * elapsedTime=120s (time elapsed since last check)
	// * expectedUptime= 60s + 120s = 180s
	// * 120s < 180s (currentUptime < expectedUptime) => server was restarted
	elapsedTime := time.Now().Unix() - lastUptimeCheck.Unix()
	expectedUptime := lastUptime + elapsedTime

	// Save uptime from last check
	m.lastUptime = currentUptime
	m.lastUptimeCheck = time.Now()

	// If current server uptime is lower than last registered uptime
	// then we can assume that server was restarted
	if currentUptime < expectedUptime {
		return true
	}

	return false
}

func (m *MysqlInstance) DSN() string {
	return m.mysqlConn.DSN()
}
<commit_msg>Add debug to mrms MySQL instance to see past, current, and execpted uptime values.<commit_after>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

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

   This program 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 Affero General Public License for more details.

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

package monitor

import (
	"fmt"
	"github.com/percona/percona-agent/mysql"
	"github.com/percona/percona-agent/pct"
	"sync"
	"time"
)

type MysqlInstance struct {
	logger      *pct.Logger
	mysqlConn   mysql.Connector
	Subscribers *Subscribers
	// --
	lastUptime      int64
	lastUptimeCheck time.Time
	sync.Mutex
}

func NewMysqlInstance(logger *pct.Logger, mysqlConn mysql.Connector, subscribers *Subscribers) (mi *MysqlInstance, err error) {
	if err := mysqlConn.Connect(2); err != nil {
		logger.Warn("Unable to connect to MySQL:", err)
		return nil, err
	}
	defer mysqlConn.Close()

	// Get current MySQL uptime - this is later used to detect if MySQL was restarted
	lastUptime := mysqlConn.Uptime()
	lastUptimeCheck := time.Now()

	mi = &MysqlInstance{
		logger:          logger,
		mysqlConn:       mysqlConn,
		Subscribers:     subscribers,
		lastUptime:      lastUptime,
		lastUptimeCheck: lastUptimeCheck,
	}

	return mi, nil
}

func (m *MysqlInstance) CheckIfMysqlRestarted() bool {
	m.Lock()
	defer m.Unlock()

	lastUptime := m.lastUptime
	lastUptimeCheck := m.lastUptimeCheck
	currentUptime := m.mysqlConn.Uptime()
	m.logger.Debug(fmt.Sprintf("lastUptime=%d lastUptimeCheck=%s currentUptime=%d",
		lastUptime, lastUptimeCheck.UTC(), currentUptime))

	// Calculate expected uptime
	//   This protects against situation where after restarting MySQL
	//   we are unable to connect to it for period longer than last registered uptime
	//
	// Steps to reproduce:
	// * currentUptime=60 lastUptime=0
	// * Restart MySQL
	// * QAN connection problem for 120s
	// * currentUptime=120 lastUptime=60 (new uptime (120s) is higher than last registered (60s))
	// * elapsedTime=120s (time elapsed since last check)
	// * expectedUptime= 60s + 120s = 180s
	// * 120s < 180s (currentUptime < expectedUptime) => server was restarted
	elapsedTime := time.Now().Unix() - lastUptimeCheck.Unix()
	expectedUptime := lastUptime + elapsedTime
	m.logger.Debug(fmt.Sprintf("elapsedTime=%d expectedUptime=%d", elapsedTime, expectedUptime))

	// Save uptime from last check
	m.lastUptime = currentUptime
	m.lastUptimeCheck = time.Now()

	// If current server uptime is lower than last registered uptime
	// then we can assume that server was restarted
	if currentUptime < expectedUptime {
		return true
	}

	return false
}

func (m *MysqlInstance) DSN() string {
	return m.mysqlConn.DSN()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"

	"github.com/Knetic/govaluate"
	"github.com/rs/zerolog/log"
)

// WhenEvaluator evaluates when clauses from the manifest
type WhenEvaluator interface {
	evaluate(string, string, map[string]interface{}) (bool, error)
	getParameters() map[string]interface{}
}

type whenEvaluatorImpl struct {
	envvarHelper EnvvarHelper
}

// NewWhenEvaluator returns a new WhenEvaluator
func NewWhenEvaluator(envvarHelper EnvvarHelper) WhenEvaluator {
	return &whenEvaluatorImpl{
		envvarHelper: envvarHelper,
	}
}

func (we *whenEvaluatorImpl) evaluate(pipelineName, input string, parameters map[string]interface{}) (result bool, err error) {

	if input == "" {
		return false, errors.New("When expression is empty")
	}

	log.Info().Msgf("[%v] Evaluating when expression \"%v\" with parameters \"%v\"", pipelineName, input, parameters)

	expression, err := govaluate.NewEvaluableExpression(input)

	r, err := expression.Evaluate(parameters)

	log.Info().Msgf("[%v] Result of when expression \"%v\" is \"%v\"", pipelineName, input, r)

	if result, ok := r.(bool); ok {
		return result, err
	}

	return false, errors.New("Result of evaluating when expression is not of type boolean")
}

func (we *whenEvaluatorImpl) getParameters() map[string]interface{} {

	parameters := make(map[string]interface{}, 3)
	parameters["branch"] = we.envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_BRANCH")
	parameters["trigger"] = we.envvarHelper.getEstafetteEnv("ESTAFETTE_TRIGGER")
	parameters["status"] = we.envvarHelper.getEstafetteEnv("ESTAFETTE_BUILD_STATUS")
	parameters["server"] = we.envvarHelper.getEstafetteEnv("ESTAFETTE_CI_SERVER")

	return parameters
}
<commit_msg>return error from when evaluator<commit_after>package main

import (
	"errors"

	"github.com/Knetic/govaluate"
	"github.com/rs/zerolog/log"
)

// WhenEvaluator evaluates when clauses from the manifest
type WhenEvaluator interface {
	evaluate(string, string, map[string]interface{}) (bool, error)
	getParameters() map[string]interface{}
}

type whenEvaluatorImpl struct {
	envvarHelper EnvvarHelper
}

// NewWhenEvaluator returns a new WhenEvaluator
func NewWhenEvaluator(envvarHelper EnvvarHelper) WhenEvaluator {
	return &whenEvaluatorImpl{
		envvarHelper: envvarHelper,
	}
}

func (we *whenEvaluatorImpl) evaluate(pipelineName, input string, parameters map[string]interface{}) (result bool, err error) {

	if input == "" {
		return false, errors.New("When expression is empty")
	}

	log.Info().Msgf("[%v] Evaluating when expression \"%v\" with parameters \"%v\"", pipelineName, input, parameters)

	expression, err := govaluate.NewEvaluableExpression(input)
	if err != nil {
		return
	}

	r, err := expression.Evaluate(parameters)

	log.Info().Msgf("[%v] Result of when expression \"%v\" is \"%v\"", pipelineName, input, r)

	if result, ok := r.(bool); ok {
		return result, err
	}

	return false, errors.New("Result of evaluating when expression is not of type boolean")
}

func (we *whenEvaluatorImpl) getParameters() map[string]interface{} {

	parameters := make(map[string]interface{}, 3)
	parameters["branch"] = we.envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_BRANCH")
	parameters["trigger"] = we.envvarHelper.getEstafetteEnv("ESTAFETTE_TRIGGER")
	parameters["status"] = we.envvarHelper.getEstafetteEnv("ESTAFETTE_BUILD_STATUS")
	parameters["server"] = we.envvarHelper.getEstafetteEnv("ESTAFETTE_CI_SERVER")

	return parameters
}
<|endoftext|>


<commit_before>// This file contains a few routines for parsing form values
package widget

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
	"github.com/skypies/util/date"
)

// {{{ FormValueDateRange

// This widget assumes the values 'date', 'range_from', and 'range_to'
func FormValueDateRange(r *http.Request) (s,e time.Time, err error) {
	err = nil

	switch r.FormValue("date") {
	case "today":
		s,_ = date.WindowForToday()
		e=s
	case "yesterday":
		s,_ = date.WindowForYesterday()
		e=s
	case "range":
		s = date.ArbitraryDatestring2MidnightPdt(r.FormValue("range_from"), "2006/01/02")
		e = date.ArbitraryDatestring2MidnightPdt(r.FormValue("range_to"), "2006/01/02")
		if s.After(e) { s,e = e,s }
	}

	e = e.Add(23*time.Hour + 59*time.Minute + 59*time.Second) // make sure e covers its whole day

	return
}

// }}}
// {{{ FormValueInt64

func FormValueInt64(r *http.Request, name string) int64 {
	val,_ := strconv.ParseInt(r.FormValue(name), 10, 64)
	return val
}

// }}}
// {{{ FormValueEpochTime

func FormValueEpochTime(r *http.Request, name string) time.Time {
	return time.Unix(FormValueInt64(r,name), 0)
}

// }}}
// {{{ FormValueFloat64

func FormValueFloat64(w http.ResponseWriter, r *http.Request, name string) float64 {
	if val,err := strconv.ParseFloat(r.FormValue(name), 64); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return -1
	} else {
		return val
	}
}

// }}}
// {{{ FormValueFloat64EatErrs

func FormValueFloat64EatErrs(r *http.Request, name string) float64 {
	if val,err := strconv.ParseFloat(r.FormValue(name), 64); err != nil {
		return -1
	} else {
		return val
	}
}

// }}}
// {{{ FormValueCheckbox

func FormValueCheckbox(r *http.Request, name string) bool {
	if r.FormValue(name) != "" {
		return true
	}
	return false
}

// }}}
// {{{ FormValueCommaSepStrings

func FormValueCommaSepStrings(r *http.Request, name string) []string {
	ret := []string{}
	r.ParseForm()
	for _,v := range r.Form[name] {
		for _,str := range strings.Split(v, ",") {
			if str != "" {
				ret = append(ret, str)
			}
		}
	}
	return ret
}

// }}}

// {{{ DateRangeToCGIArgs

func DateRangeToCGIArgs(s,e time.Time) string {
	str := fmt.Sprintf("date=range&range_from=%s&range_to=%s",
		s.Format("2006/01/02"), e.Format("2006/01/02"))
	return str
}

// }}}

// {{{ -------------------------={ E N D }=----------------------------------

// Local variables:
// folded-file: t
// end:

// }}}
<commit_msg>Blanks should come back as zeroes, or it is all too crazy<commit_after>// This file contains a few routines for parsing form values
package widget

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
	"github.com/skypies/util/date"
)

// {{{ FormValueDateRange

// This widget assumes the values 'date', 'range_from', and 'range_to'
func FormValueDateRange(r *http.Request) (s,e time.Time, err error) {
	err = nil

	switch r.FormValue("date") {
	case "today":
		s,_ = date.WindowForToday()
		e=s
	case "yesterday":
		s,_ = date.WindowForYesterday()
		e=s
	case "range":
		s = date.ArbitraryDatestring2MidnightPdt(r.FormValue("range_from"), "2006/01/02")
		e = date.ArbitraryDatestring2MidnightPdt(r.FormValue("range_to"), "2006/01/02")
		if s.After(e) { s,e = e,s }
	}

	e = e.Add(23*time.Hour + 59*time.Minute + 59*time.Second) // make sure e covers its whole day

	return
}

// }}}
// {{{ FormValueInt64

func FormValueInt64(r *http.Request, name string) int64 {
	val,_ := strconv.ParseInt(r.FormValue(name), 10, 64)
	return val
}

// }}}
// {{{ FormValueEpochTime

func FormValueEpochTime(r *http.Request, name string) time.Time {
	return time.Unix(FormValueInt64(r,name), 0)
}

// }}}
// {{{ FormValueFloat64

func FormValueFloat64(w http.ResponseWriter, r *http.Request, name string) float64 {
	if val,err := strconv.ParseFloat(r.FormValue(name), 64); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return -1
	} else {
		return val
	}
}

// }}}
// {{{ FormValueFloat64EatErrs

func FormValueFloat64EatErrs(r *http.Request, name string) float64 {
	if val,err := strconv.ParseFloat(r.FormValue(name), 64); err != nil {
		return 0.0
	} else {
		return val
	}
}

// }}}
// {{{ FormValueCheckbox

func FormValueCheckbox(r *http.Request, name string) bool {
	if r.FormValue(name) != "" {
		return true
	}
	return false
}

// }}}
// {{{ FormValueCommaSepStrings

func FormValueCommaSepStrings(r *http.Request, name string) []string {
	ret := []string{}
	r.ParseForm()
	for _,v := range r.Form[name] {
		for _,str := range strings.Split(v, ",") {
			if str != "" {
				ret = append(ret, str)
			}
		}
	}
	return ret
}

// }}}

// {{{ DateRangeToCGIArgs

func DateRangeToCGIArgs(s,e time.Time) string {
	str := fmt.Sprintf("date=range&range_from=%s&range_to=%s",
		s.Format("2006/01/02"), e.Format("2006/01/02"))
	return str
}

// }}}

// {{{ -------------------------={ E N D }=----------------------------------

// Local variables:
// folded-file: t
// end:

// }}}
<|endoftext|>


<commit_before>package cmd

const Version = "3.31.0"
<commit_msg>version bump<commit_after>package cmd

const Version = "3.32.0"
<|endoftext|>


<commit_before>package main

import "strconv"

type decl struct {
	Name string
	Typ  string
	Und  string
}

type reg struct {
	Reg   string
	Bits  string
	Field string
	N     string
	Decls []decl
}

func regs(f string, lines []string) ([]reg, int) {
	var (
		regs []reg
		max  int
	)
	for _, line := range lines {
		num, name := nameval(line, ':')
		if num == "" || name == "" {
			continue
		}
		n, err := strconv.ParseUint(num, 0, 0)
		if err != nil {
			fdie(f, "bad index %s: %v", num, err)
		}
		regs = append(regs, reg{
			Reg:   name,
			Bits:  name + "_Bits",
			Field: name + "_Field",
			N:     num,
		})
		if max < int(n) {
			max = int(n)
		}
	}
	return regs, max + 1
}
<commit_msg>xgen: Use offset instead of index for register declararions.<commit_after>package main

import "strconv"

type decl struct {
	Name string
	Typ  string
	Und  string
}

type reg struct {
	Reg   string
	Bits  string
	Field string
	N     int
	Decls []decl
}

func regs(f string, lines []string) ([]reg, int) {
	var (
		regs []reg
		max  int
	)
	for _, line := range lines {
		num, name := nameval(line, ':')
		if num == "" || name == "" {
			continue
		}
		n, err := strconv.ParseUint(num, 0, 0)
		if err != nil || n&3 != 0 {
			fdie(f, "bad index %s: %v", num, err)
		}
		n >>= 2
		regs = append(regs, reg{
			Reg:   name,
			Bits:  name + "_Bits",
			Field: name + "_Field",
			N:     int(n),
		})
		if max < int(n) {
			max = int(n)
		}
	}
	return regs, max + 1
}
<|endoftext|>


<commit_before>package radioman

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/kr/fs"
	"github.com/moul/radioman/radioman/pkg/liquidsoap"
)

type Radio struct {
	Name             string    `json:"name"`
	DefaultPlaylist  *Playlist `json:"default_playlist"`
	CreationDate     time.Time `json:"creation_date"`
	ModificationDate time.Time `json:"modification_date"`
	Stats            struct {
		Playlists int `json:"playlists"`
		Tracks    int `json:"tracks"`
	} `json:"stats"`
	Playlists []*Playlist        `json:"-"`
	Telnet    *liquidsoap.Telnet `json:"-"`
}

func (r *Radio) NewPlaylist(name string) (*Playlist, error) {
	logrus.Infof("New playlist %q", name)
	playlist := &Playlist{
		Name:             name,
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
		Tracks:           make(map[string]*Track, 0),
		Status:           "new",
	}
	r.Playlists = append(r.Playlists, playlist)
	r.Stats.Playlists++
	return playlist, nil
}

func (r *Radio) NewDirectoryPlaylist(name string, path string) (*Playlist, error) {
	if _, err := os.Stat(path); err != nil {
		return nil, err
	}

	playlist, err := r.NewPlaylist(name)
	if err != nil {
		return nil, err
	}
	expandedPath, err := expandUser(path)
	if err != nil {
		return nil, err
	}
	playlist.Path = expandedPath
	return playlist, nil
}

func (r *Radio) GetPlaylistByName(name string) (*Playlist, error) {
	for _, playlist := range r.Playlists {
		if playlist.Name == name {
			return playlist, nil
		}
	}
	return nil, fmt.Errorf("no such playlist")
}

func (r *Radio) GetTrackByHash(hash string) (*Track, error) {
	// FIXME: do not iterate over playlists, use a global map instead
	for _, playlist := range r.Playlists {
		if track, found := playlist.Tracks[hash]; found {
			return track, nil
		}
	}
	return nil, fmt.Errorf("no such track")
}

func NewRadio(name string) *Radio {
	return &Radio{
		Name:             name,
		Playlists:        make([]*Playlist, 0),
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
	}
}

func (r *Radio) InitTelnet() error {
	liquidsoapAddr := strings.Split(strings.Replace(os.Getenv("LIQUIDSOAP_PORT_2300_TCP"), "tcp://", "", -1), ":")
	liquidsoapHost := liquidsoapAddr[0]
	liquidsoapPort, _ := strconv.Atoi(liquidsoapAddr[1])

	r.Telnet = liquidsoap.NewTelnet(liquidsoapHost, liquidsoapPort)

	if err := r.Telnet.Open(); err != nil {
		logrus.Fatalf("Failed to connect to liquidsoap")
	}
	defer r.Telnet.Close()

	radiomandHost := strings.Split(r.Telnet.Conn.LocalAddr().String(), ":")[0]
	_, err := r.Telnet.Command(fmt.Sprintf(`var.set radiomand_url = "http://%s:%d"`, radiomandHost, 8000))

	return err
}

func (r *Radio) SkipSong() error {
	if err := r.Telnet.Open(); err != nil {
		return err
	}
	defer r.Telnet.Close()

	if _, err := r.Telnet.Command("manager.skip"); err != nil {
		return err
	}

	return nil
}

func (r *Radio) PlayTrack(track *Track) error {
	if err := r.Telnet.Open(); err != nil {
		return err
	}
	defer r.Telnet.Close()

	if _, err := r.Telnet.Command(fmt.Sprintf("request.push %s", track.Path)); err != nil {
		return err
	}

	return nil
}

func (r *Radio) UpdatePlaylistsRoutine() {
	for {
		defaultUpdated := false
		tracksSum := 0

		for _, playlist := range r.Playlists {
			// automatically update playlist
			if err := playlist.AutoUpdate(); err != nil {
				playlist.Status = "error"
				logrus.Warnf("Failed to update playlist: %v", err)
				continue
			}

			tracksSum += playlist.Stats.Tracks

			// Set default playlist if needed
			if r.DefaultPlaylist == nil && playlist.Status == "ready" {
				r.DefaultPlaylist = playlist
				defaultUpdated = true
			}
		}

		r.Stats.Tracks = tracksSum

		// when getting a new playlist for the first time, a skipsong will act like the first "play"
		if defaultUpdated {
			r.SkipSong()
		}

		// sleep 5 minutes before next run
		time.Sleep(5 * time.Minute)
	}
}

func (r *Radio) Init() error {
	if err := r.InitTelnet(); err != nil {
		return err
	}
	return nil
}

func (r *Radio) StdPopulate() error {
	// Add a dummy manual playlist
	r.NewPlaylist("manual")

	// Add 'standard' music paths
	r.NewDirectoryPlaylist("iTunes Music", "~/Music/iTunes/iTunes Media/Music/")
	r.NewDirectoryPlaylist("iTunes Podcasts", "~/Music/iTunes/iTunes Media/Podcasts/")

	// Add local directory
	playlistsDirs := []string{"/playlists"}
	playlistsDirs = append(playlistsDirs, path.Join(os.Getenv("HOME"), "playlists"))
	playlistsDirs = append(playlistsDirs, path.Join("/home", "playlists"))
	dir, err := os.Getwd()
	if err == nil && os.Getenv("NO_LOCAL_PLAYLISTS") != "1" {
		r.NewDirectoryPlaylist("local directory", dir)
		playlistsDirs = append(playlistsDirs, path.Join(dir, "playlists"))
	}

	// Add each folders in '/playlists' and './playlists'
	for _, playlistsDir := range playlistsDirs {
		walker := fs.Walk(playlistsDir)
		for walker.Step() {
			if walker.Path() == playlistsDir {
				continue
			}
			if err := walker.Err(); err != nil {
				logrus.Warnf("walker error: %v", err)
				continue
			}

			var realpath string
			if walker.Stat().IsDir() {
				realpath = walker.Path()
				walker.SkipDir()
			} else {
				realpath, err = filepath.EvalSymlinks(walker.Path())
				if err != nil {
					logrus.Warnf("filepath.EvalSymlinks error for %q: %v", walker.Path(), err)
					continue
				}
			}

			stat, err := os.Stat(realpath)
			if err != nil {
				logrus.Warnf("os.Stat error: %v", err)
				continue
			}
			if stat.IsDir() {
				r.NewDirectoryPlaylist(fmt.Sprintf("playlist: %s", walker.Stat().Name()), realpath)
			}
		}
	}

	return nil
}

func (r *Radio) GetNextSong() (*Track, error) {
	// FIXME: shuffle playlist instead of getting a random track
	// FIXME: do not iterate over a map

	playlist := r.DefaultPlaylist
	track, err := playlist.GetRandomTrack()
	if err == nil {
		return track, nil
	}

	for _, playlist := range r.Playlists {
		track, err := playlist.GetRandomTrack()
		if err != nil {
			continue
		}
		return track, nil
	}

	return nil, fmt.Errorf("no such next song, are your playlists empty ?")
}
<commit_msg>Updated default playlists<commit_after>package radioman

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/kr/fs"
	"github.com/moul/radioman/radioman/pkg/liquidsoap"
)

type Radio struct {
	Name             string    `json:"name"`
	DefaultPlaylist  *Playlist `json:"default_playlist"`
	CreationDate     time.Time `json:"creation_date"`
	ModificationDate time.Time `json:"modification_date"`
	Stats            struct {
		Playlists int `json:"playlists"`
		Tracks    int `json:"tracks"`
	} `json:"stats"`
	Playlists []*Playlist        `json:"-"`
	Telnet    *liquidsoap.Telnet `json:"-"`
}

func (r *Radio) NewPlaylist(name string) (*Playlist, error) {
	logrus.Infof("New playlist %q", name)
	playlist := &Playlist{
		Name:             name,
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
		Tracks:           make(map[string]*Track, 0),
		Status:           "new",
	}
	r.Playlists = append(r.Playlists, playlist)
	r.Stats.Playlists++
	return playlist, nil
}

func (r *Radio) NewDirectoryPlaylist(name string, path string) (*Playlist, error) {
	if _, err := os.Stat(path); err != nil {
		return nil, err
	}

	playlist, err := r.NewPlaylist(name)
	if err != nil {
		return nil, err
	}
	expandedPath, err := expandUser(path)
	if err != nil {
		return nil, err
	}
	playlist.Path = expandedPath
	return playlist, nil
}

func (r *Radio) GetPlaylistByName(name string) (*Playlist, error) {
	for _, playlist := range r.Playlists {
		if playlist.Name == name {
			return playlist, nil
		}
	}
	return nil, fmt.Errorf("no such playlist")
}

func (r *Radio) GetTrackByHash(hash string) (*Track, error) {
	// FIXME: do not iterate over playlists, use a global map instead
	for _, playlist := range r.Playlists {
		if track, found := playlist.Tracks[hash]; found {
			return track, nil
		}
	}
	return nil, fmt.Errorf("no such track")
}

func NewRadio(name string) *Radio {
	return &Radio{
		Name:             name,
		Playlists:        make([]*Playlist, 0),
		CreationDate:     time.Now(),
		ModificationDate: time.Now(),
	}
}

func (r *Radio) InitTelnet() error {
	liquidsoapAddr := strings.Split(strings.Replace(os.Getenv("LIQUIDSOAP_PORT_2300_TCP"), "tcp://", "", -1), ":")
	liquidsoapHost := liquidsoapAddr[0]
	liquidsoapPort, _ := strconv.Atoi(liquidsoapAddr[1])

	r.Telnet = liquidsoap.NewTelnet(liquidsoapHost, liquidsoapPort)

	if err := r.Telnet.Open(); err != nil {
		logrus.Fatalf("Failed to connect to liquidsoap")
	}
	defer r.Telnet.Close()

	radiomandHost := strings.Split(r.Telnet.Conn.LocalAddr().String(), ":")[0]
	_, err := r.Telnet.Command(fmt.Sprintf(`var.set radiomand_url = "http://%s:%d"`, radiomandHost, 8000))

	return err
}

func (r *Radio) SkipSong() error {
	if err := r.Telnet.Open(); err != nil {
		return err
	}
	defer r.Telnet.Close()

	if _, err := r.Telnet.Command("manager.skip"); err != nil {
		return err
	}

	return nil
}

func (r *Radio) PlayTrack(track *Track) error {
	if err := r.Telnet.Open(); err != nil {
		return err
	}
	defer r.Telnet.Close()

	if _, err := r.Telnet.Command(fmt.Sprintf("request.push %s", track.Path)); err != nil {
		return err
	}

	return nil
}

func (r *Radio) UpdatePlaylistsRoutine() {
	for {
		defaultUpdated := false
		tracksSum := 0

		for _, playlist := range r.Playlists {
			// automatically update playlist
			if err := playlist.AutoUpdate(); err != nil {
				playlist.Status = "error"
				logrus.Warnf("Failed to update playlist: %v", err)
				continue
			}

			tracksSum += playlist.Stats.Tracks

			// Set default playlist if needed
			if r.DefaultPlaylist == nil && playlist.Status == "ready" {
				r.DefaultPlaylist = playlist
				defaultUpdated = true
			}
		}

		r.Stats.Tracks = tracksSum

		// when getting a new playlist for the first time, a skipsong will act like the first "play"
		if defaultUpdated {
			r.SkipSong()
		}

		// sleep 5 minutes before next run
		time.Sleep(5 * time.Minute)
	}
}

func (r *Radio) Init() error {
	if err := r.InitTelnet(); err != nil {
		return err
	}
	return nil
}

func (r *Radio) StdPopulate() error {
	// Add a dummy manual playlist
	r.NewPlaylist("manual")

	// Add local directory
	playlistsDirs := []string{"/playlists"}
	playlistsDirs = append(playlistsDirs, path.Join(os.Getenv("HOME"), "playlists"))
	playlistsDirs = append(playlistsDirs, path.Join("/home", "playlists"))
	dir, err := os.Getwd()
	if err == nil && os.Getenv("NO_LOCAL_PLAYLISTS") != "1" {
		r.NewDirectoryPlaylist("local directory", dir)
		playlistsDirs = append(playlistsDirs, path.Join(dir, "playlists"))
	}

	// Add each folders in '/playlists' and './playlists'
	for _, playlistsDir := range playlistsDirs {
		walker := fs.Walk(playlistsDir)
		for walker.Step() {
			if walker.Path() == playlistsDir {
				continue
			}
			if err := walker.Err(); err != nil {
				logrus.Warnf("walker error: %v", err)
				continue
			}

			var realpath string
			if walker.Stat().IsDir() {
				realpath = walker.Path()
				walker.SkipDir()
			} else {
				realpath, err = filepath.EvalSymlinks(walker.Path())
				if err != nil {
					logrus.Warnf("filepath.EvalSymlinks error for %q: %v", walker.Path(), err)
					continue
				}
			}

			stat, err := os.Stat(realpath)
			if err != nil {
				logrus.Warnf("os.Stat error: %v", err)
				continue
			}
			if stat.IsDir() {
				r.NewDirectoryPlaylist(fmt.Sprintf("playlist: %s", walker.Stat().Name()), realpath)
			}
		}
	}

	// Add 'standard' music paths
	r.NewDirectoryPlaylist("iTunes Music", "~/Music/iTunes/iTunes Media/Music/")
	r.NewDirectoryPlaylist("iTunes Podcasts", "~/Music/iTunes/iTunes Media/Podcasts/")
	r.NewDirectoryPlaylist("iTunes Music", "/home/Music/iTunes/iTunes Media/Music/")
	r.NewDirectoryPlaylist("iTunes Podcasts", "/home/Music/iTunes/iTunes Media/Podcasts/")

	return nil
}

func (r *Radio) GetNextSong() (*Track, error) {
	// FIXME: shuffle playlist instead of getting a random track
	// FIXME: do not iterate over a map

	playlist := r.DefaultPlaylist
	track, err := playlist.GetRandomTrack()
	if err == nil {
		return track, nil
	}

	for _, playlist := range r.Playlists {
		track, err := playlist.GetRandomTrack()
		if err != nil {
			continue
		}
		return track, nil
	}

	return nil, fmt.Errorf("no such next song, are your playlists empty ?")
}
<|endoftext|>


<commit_before>package torrent

import (
	"time"

	"github.com/cenkalti/rain/internal/metainfo"
)

var (
	publicPeerIDPrefix                    = "-RN" + Version + "-"
	publicExtensionHandshakeClientVersion = "Rain " + Version
	trackerHTTPPublicUserAgent            = "Rain/" + Version
)

func init() {
	metainfo.Creator = publicExtensionHandshakeClientVersion
}

// Config for Session.
type Config struct {
	// Database file to save resume data.
	Database string
	// DataDir is where files are downloaded.
	DataDir string
	// If true, torrent files are saved into <data_dir>/<torrent_id>/<torrent_name>.
	// Useful if downloading the same torrent from multiple sources.
	DataDirIncludesTorrentID bool
	// New torrents will be listened at selected port in this range.
	PortBegin, PortEnd uint16
	// At start, client will set max open files limit to this number. (like "ulimit -n" command)
	MaxOpenFiles uint64
	// Enable peer exchange protocol.
	PEXEnabled bool
	// Resume data (bitfield & stats) are saved to disk at interval to keep IO lower.
	ResumeWriteInterval time.Duration
	// Peer id is prefixed with this string. See BEP 20. Remaining bytes of peer id will be randomized.
	// Only applies to private torrents.
	PrivatePeerIDPrefix string
	// Client version that is sent in BEP 10 handshake message.
	// Only applies to private torrents.
	PrivateExtensionHandshakeClientVersion string
	// URL to the blocklist file in CIDR format.
	BlocklistURL string
	// When to refresh blocklist
	BlocklistUpdateInterval time.Duration
	// HTTP timeout for downloading blocklist
	BlocklistUpdateTimeout time.Duration
	// Do not contact tracker if it's IP is blocked
	BlocklistEnabledForTrackers bool
	// Do not connect to peer if it's IP is blocked
	BlocklistEnabledForOutgoingConnections bool
	// Do not accept connections from peer if it's IP is blocked
	BlocklistEnabledForIncomingConnections bool
	// Do not accept response larger than this size
	BlocklistMaxResponseSize int64
	// Time to wait when adding torrent with AddURI().
	TorrentAddHTTPTimeout time.Duration
	// Maximum allowed size to be received by metadata extension.
	MaxMetadataSize uint
	// Maximum allowed size to be read when adding torrent.
	MaxTorrentSize uint
	// Maximum allowed number of pieces in a torrent.
	MaxPieces uint32
	// Time to wait when resolving host names for trackers and peers.
	DNSResolveTimeout time.Duration
	// Global download speed limit in KB/s.
	SpeedLimitDownload int64
	// Global upload speed limit in KB/s.
	SpeedLimitUpload int64
	// Start torrent automatically if it was running when previous session was closed.
	ResumeOnStartup bool

	// Enable RPC server
	RPCEnabled bool
	// Host to listen for RPC server
	RPCHost string
	// Listen port for RPC server
	RPCPort int
	// Time to wait for ongoing requests before shutting down RPC HTTP server.
	RPCShutdownTimeout time.Duration

	// Enable DHT node.
	DHTEnabled bool
	// DHT node will listen on this IP.
	DHTHost string
	// DHT node will listen on this UDP port.
	DHTPort uint16
	// DHT announce interval
	DHTAnnounceInterval time.Duration
	// Minimum announce interval when announcing to DHT.
	DHTMinAnnounceInterval time.Duration
	// Known routers to bootstrap local DHT node.
	DHTBootstrapNodes []string

	// Number of peer addresses to request in announce request.
	TrackerNumWant int
	// Time to wait for announcing stopped event.
	// Stopped event is sent to the tracker when torrent is stopped.
	TrackerStopTimeout time.Duration
	// When the client needs new peer addresses to connect, it ask to the tracker.
	// To prevent spamming the tracker an interval is set to wait before the next announce.
	TrackerMinAnnounceInterval time.Duration
	// Total time to wait for response to be read.
	// This includes ConnectTimeout and TLSHandshakeTimeout.
	TrackerHTTPTimeout time.Duration
	// User agent sent when communicating with HTTP trackers.
	// Only applies to private torrents.
	TrackerHTTPPrivateUserAgent string
	// Max number of bytes in a tracker response.
	TrackerHTTPMaxResponseSize uint
	// Check and validate TLS ceritificates.
	TrackerHTTPVerifyTLS bool

	// Number of unchoked peers.
	UnchokedPeers int
	// Number of optimistic unchoked peers.
	OptimisticUnchokedPeers int
	// Max number of blocks allowed to be queued without dropping any.
	MaxRequestsIn int
	// Max number of blocks requested from a peer but not received yet.
	// `rreq` value from extended handshake cannot exceed this limit.
	MaxRequestsOut int
	// Number of bloks requested from peer if it does not send `rreq` value in extended handshake.
	DefaultRequestsOut int
	// Time to wait for a requested block to be received before marking peer as snubbed
	RequestTimeout time.Duration
	// Max number of running downloads on piece in endgame mode, snubbed and choed peers don't count
	EndgameMaxDuplicateDownloads int
	// Max number of outgoing connections to dial
	MaxPeerDial int
	// Max number of incoming connections to accept
	MaxPeerAccept int
	// Running metadata downloads, snubbed peers don't count
	ParallelMetadataDownloads int
	// Time to wait for TCP connection to open.
	PeerConnectTimeout time.Duration
	// Time to wait for BitTorrent handshake to complete.
	PeerHandshakeTimeout time.Duration
	// When peer has started to send piece block, if it does not send any bytes in PieceReadTimeout, the connection is closed.
	PieceReadTimeout time.Duration
	// Max number of peer addresses to keep in connect queue.
	MaxPeerAddresses int
	// Number of allowed-fast messages to send after handshake.
	AllowedFastSet int

	// Number of bytes to read when a piece is requested by a peer.
	ReadCacheBlockSize int64
	// Number of cached bytes for piece read requests.
	ReadCacheSize int64
	// Read bytes for a piece part expires after duration.
	ReadCacheTTL time.Duration
	// Number of read operations to do in parallel.
	ParallelReads uint
	// Number of write operations to do in parallel.
	ParallelWrites uint
	// Number of bytes allocated in memory for downloading piece data.
	WriteCacheSize int64

	// When the client want to connect a peer, first it tries to do encrypted handshake.
	// If it does not work, it connects to same peer again and does unencrypted handshake.
	// This behavior can be changed via this variable.
	DisableOutgoingEncryption bool
	// Dial only encrypted connections.
	ForceOutgoingEncryption bool
	// Do not accept unencrypted connections.
	ForceIncomingEncryption bool

	// TCP connect timeout for WebSeed sources
	WebseedDialTimeout time.Duration
	// TLS handshake timeout for WebSeed sources
	WebseedTLSHandshakeTimeout time.Duration
	// HTTP header timeout for WebSeed sources
	WebseedResponseHeaderTimeout time.Duration
	// HTTP body read timeout for Webseed sources
	WebseedResponseBodyReadTimeout time.Duration
	// Retry interval for restarting failed downloads
	WebseedRetryInterval time.Duration
	// Verify TLS certificate for WebSeed URLs
	WebseedVerifyTLS bool
	// Limit the number of WebSeed sources in torrent.
	WebseedMaxSources int
	// Number of maximum simulateous downloads from WebSeed sources.
	WebseedMaxDownloads int

	// Shell command to execute on torrent completion.
	OnCompleteCmd []string
}

// DefaultConfig for Session. Do not pass zero value Config to NewSession. Copy this struct and modify instead.
var DefaultConfig = Config{
	// Session
	Database:                               "~/rain/session.db",
	DataDir:                                "~/rain/data",
	DataDirIncludesTorrentID:               true,
	PortBegin:                              50000,
	PortEnd:                                60000,
	MaxOpenFiles:                           10240,
	PEXEnabled:                             true,
	ResumeWriteInterval:                    30 * time.Second,
	PrivatePeerIDPrefix:                    "-RN" + Version + "-",
	PrivateExtensionHandshakeClientVersion: "Rain " + Version,
	BlocklistUpdateInterval:                24 * time.Hour,
	BlocklistUpdateTimeout:                 10 * time.Minute,
	BlocklistEnabledForTrackers:            true,
	BlocklistEnabledForOutgoingConnections: true,
	BlocklistEnabledForIncomingConnections: true,
	BlocklistMaxResponseSize:               100 << 20,
	TorrentAddHTTPTimeout:                  30 * time.Second,
	MaxMetadataSize:                        30 << 20,
	MaxTorrentSize:                         10 << 20,
	MaxPieces:                              64 << 10,
	DNSResolveTimeout:                      5 * time.Second,
	ResumeOnStartup:                        true,

	// RPC Server
	RPCEnabled:         true,
	RPCHost:            "127.0.0.1",
	RPCPort:            7246,
	RPCShutdownTimeout: 5 * time.Second,

	// Tracker
	TrackerNumWant:              200,
	TrackerStopTimeout:          5 * time.Second,
	TrackerMinAnnounceInterval:  time.Minute,
	TrackerHTTPTimeout:          10 * time.Second,
	TrackerHTTPPrivateUserAgent: "Rain/" + Version,
	TrackerHTTPMaxResponseSize:  2 << 20,
	TrackerHTTPVerifyTLS:        true,

	// DHT node
	DHTEnabled:             true,
	DHTHost:                "0.0.0.0",
	DHTPort:                7246,
	DHTAnnounceInterval:    30 * time.Minute,
	DHTMinAnnounceInterval: time.Minute,
	DHTBootstrapNodes: []string{
		"router.bittorrent.com:6881",
		"dht.transmissionbt.com:6881",
		"router.utorrent.com:6881",
		"dht.libtorrent.org:25401",
		"dht.aelitis.com:6881",
	},

	// Peer
	UnchokedPeers:                3,
	OptimisticUnchokedPeers:      1,
	MaxRequestsIn:                250,
	MaxRequestsOut:               250,
	DefaultRequestsOut:           50,
	RequestTimeout:               20 * time.Second,
	EndgameMaxDuplicateDownloads: 20,
	MaxPeerDial:                  80,
	MaxPeerAccept:                20,
	ParallelMetadataDownloads:    2,
	PeerConnectTimeout:           5 * time.Second,
	PeerHandshakeTimeout:         10 * time.Second,
	PieceReadTimeout:             30 * time.Second,
	MaxPeerAddresses:             2000,
	AllowedFastSet:               10,

	// IO
	ReadCacheBlockSize: 128 << 10,
	ReadCacheSize:      256 << 20,
	ReadCacheTTL:       1 * time.Minute,
	ParallelReads:      1,
	ParallelWrites:     1,
	WriteCacheSize:     1 << 30,

	// Webseed settings
	WebseedDialTimeout:             10 * time.Second,
	WebseedTLSHandshakeTimeout:     10 * time.Second,
	WebseedResponseHeaderTimeout:   10 * time.Second,
	WebseedResponseBodyReadTimeout: 10 * time.Second,
	WebseedRetryInterval:           time.Minute,
	WebseedVerifyTLS:               true,
	WebseedMaxSources:              10,
	WebseedMaxDownloads:            4,
}
<commit_msg>change default peer port range #49<commit_after>package torrent

import (
	"time"

	"github.com/cenkalti/rain/internal/metainfo"
)

var (
	publicPeerIDPrefix                    = "-RN" + Version + "-"
	publicExtensionHandshakeClientVersion = "Rain " + Version
	trackerHTTPPublicUserAgent            = "Rain/" + Version
)

func init() {
	metainfo.Creator = publicExtensionHandshakeClientVersion
}

// Config for Session.
type Config struct {
	// Database file to save resume data.
	Database string
	// DataDir is where files are downloaded.
	DataDir string
	// If true, torrent files are saved into <data_dir>/<torrent_id>/<torrent_name>.
	// Useful if downloading the same torrent from multiple sources.
	DataDirIncludesTorrentID bool
	// New torrents will be listened at selected port in this range.
	PortBegin, PortEnd uint16
	// At start, client will set max open files limit to this number. (like "ulimit -n" command)
	MaxOpenFiles uint64
	// Enable peer exchange protocol.
	PEXEnabled bool
	// Resume data (bitfield & stats) are saved to disk at interval to keep IO lower.
	ResumeWriteInterval time.Duration
	// Peer id is prefixed with this string. See BEP 20. Remaining bytes of peer id will be randomized.
	// Only applies to private torrents.
	PrivatePeerIDPrefix string
	// Client version that is sent in BEP 10 handshake message.
	// Only applies to private torrents.
	PrivateExtensionHandshakeClientVersion string
	// URL to the blocklist file in CIDR format.
	BlocklistURL string
	// When to refresh blocklist
	BlocklistUpdateInterval time.Duration
	// HTTP timeout for downloading blocklist
	BlocklistUpdateTimeout time.Duration
	// Do not contact tracker if it's IP is blocked
	BlocklistEnabledForTrackers bool
	// Do not connect to peer if it's IP is blocked
	BlocklistEnabledForOutgoingConnections bool
	// Do not accept connections from peer if it's IP is blocked
	BlocklistEnabledForIncomingConnections bool
	// Do not accept response larger than this size
	BlocklistMaxResponseSize int64
	// Time to wait when adding torrent with AddURI().
	TorrentAddHTTPTimeout time.Duration
	// Maximum allowed size to be received by metadata extension.
	MaxMetadataSize uint
	// Maximum allowed size to be read when adding torrent.
	MaxTorrentSize uint
	// Maximum allowed number of pieces in a torrent.
	MaxPieces uint32
	// Time to wait when resolving host names for trackers and peers.
	DNSResolveTimeout time.Duration
	// Global download speed limit in KB/s.
	SpeedLimitDownload int64
	// Global upload speed limit in KB/s.
	SpeedLimitUpload int64
	// Start torrent automatically if it was running when previous session was closed.
	ResumeOnStartup bool

	// Enable RPC server
	RPCEnabled bool
	// Host to listen for RPC server
	RPCHost string
	// Listen port for RPC server
	RPCPort int
	// Time to wait for ongoing requests before shutting down RPC HTTP server.
	RPCShutdownTimeout time.Duration

	// Enable DHT node.
	DHTEnabled bool
	// DHT node will listen on this IP.
	DHTHost string
	// DHT node will listen on this UDP port.
	DHTPort uint16
	// DHT announce interval
	DHTAnnounceInterval time.Duration
	// Minimum announce interval when announcing to DHT.
	DHTMinAnnounceInterval time.Duration
	// Known routers to bootstrap local DHT node.
	DHTBootstrapNodes []string

	// Number of peer addresses to request in announce request.
	TrackerNumWant int
	// Time to wait for announcing stopped event.
	// Stopped event is sent to the tracker when torrent is stopped.
	TrackerStopTimeout time.Duration
	// When the client needs new peer addresses to connect, it ask to the tracker.
	// To prevent spamming the tracker an interval is set to wait before the next announce.
	TrackerMinAnnounceInterval time.Duration
	// Total time to wait for response to be read.
	// This includes ConnectTimeout and TLSHandshakeTimeout.
	TrackerHTTPTimeout time.Duration
	// User agent sent when communicating with HTTP trackers.
	// Only applies to private torrents.
	TrackerHTTPPrivateUserAgent string
	// Max number of bytes in a tracker response.
	TrackerHTTPMaxResponseSize uint
	// Check and validate TLS ceritificates.
	TrackerHTTPVerifyTLS bool

	// Number of unchoked peers.
	UnchokedPeers int
	// Number of optimistic unchoked peers.
	OptimisticUnchokedPeers int
	// Max number of blocks allowed to be queued without dropping any.
	MaxRequestsIn int
	// Max number of blocks requested from a peer but not received yet.
	// `rreq` value from extended handshake cannot exceed this limit.
	MaxRequestsOut int
	// Number of bloks requested from peer if it does not send `rreq` value in extended handshake.
	DefaultRequestsOut int
	// Time to wait for a requested block to be received before marking peer as snubbed
	RequestTimeout time.Duration
	// Max number of running downloads on piece in endgame mode, snubbed and choed peers don't count
	EndgameMaxDuplicateDownloads int
	// Max number of outgoing connections to dial
	MaxPeerDial int
	// Max number of incoming connections to accept
	MaxPeerAccept int
	// Running metadata downloads, snubbed peers don't count
	ParallelMetadataDownloads int
	// Time to wait for TCP connection to open.
	PeerConnectTimeout time.Duration
	// Time to wait for BitTorrent handshake to complete.
	PeerHandshakeTimeout time.Duration
	// When peer has started to send piece block, if it does not send any bytes in PieceReadTimeout, the connection is closed.
	PieceReadTimeout time.Duration
	// Max number of peer addresses to keep in connect queue.
	MaxPeerAddresses int
	// Number of allowed-fast messages to send after handshake.
	AllowedFastSet int

	// Number of bytes to read when a piece is requested by a peer.
	ReadCacheBlockSize int64
	// Number of cached bytes for piece read requests.
	ReadCacheSize int64
	// Read bytes for a piece part expires after duration.
	ReadCacheTTL time.Duration
	// Number of read operations to do in parallel.
	ParallelReads uint
	// Number of write operations to do in parallel.
	ParallelWrites uint
	// Number of bytes allocated in memory for downloading piece data.
	WriteCacheSize int64

	// When the client want to connect a peer, first it tries to do encrypted handshake.
	// If it does not work, it connects to same peer again and does unencrypted handshake.
	// This behavior can be changed via this variable.
	DisableOutgoingEncryption bool
	// Dial only encrypted connections.
	ForceOutgoingEncryption bool
	// Do not accept unencrypted connections.
	ForceIncomingEncryption bool

	// TCP connect timeout for WebSeed sources
	WebseedDialTimeout time.Duration
	// TLS handshake timeout for WebSeed sources
	WebseedTLSHandshakeTimeout time.Duration
	// HTTP header timeout for WebSeed sources
	WebseedResponseHeaderTimeout time.Duration
	// HTTP body read timeout for Webseed sources
	WebseedResponseBodyReadTimeout time.Duration
	// Retry interval for restarting failed downloads
	WebseedRetryInterval time.Duration
	// Verify TLS certificate for WebSeed URLs
	WebseedVerifyTLS bool
	// Limit the number of WebSeed sources in torrent.
	WebseedMaxSources int
	// Number of maximum simulateous downloads from WebSeed sources.
	WebseedMaxDownloads int

	// Shell command to execute on torrent completion.
	OnCompleteCmd []string
}

// DefaultConfig for Session. Do not pass zero value Config to NewSession. Copy this struct and modify instead.
var DefaultConfig = Config{
	// Session
	Database:                               "~/rain/session.db",
	DataDir:                                "~/rain/data",
	DataDirIncludesTorrentID:               true,
	PortBegin:                              20000,
	PortEnd:                                30000,
	MaxOpenFiles:                           10240,
	PEXEnabled:                             true,
	ResumeWriteInterval:                    30 * time.Second,
	PrivatePeerIDPrefix:                    "-RN" + Version + "-",
	PrivateExtensionHandshakeClientVersion: "Rain " + Version,
	BlocklistUpdateInterval:                24 * time.Hour,
	BlocklistUpdateTimeout:                 10 * time.Minute,
	BlocklistEnabledForTrackers:            true,
	BlocklistEnabledForOutgoingConnections: true,
	BlocklistEnabledForIncomingConnections: true,
	BlocklistMaxResponseSize:               100 << 20,
	TorrentAddHTTPTimeout:                  30 * time.Second,
	MaxMetadataSize:                        30 << 20,
	MaxTorrentSize:                         10 << 20,
	MaxPieces:                              64 << 10,
	DNSResolveTimeout:                      5 * time.Second,
	ResumeOnStartup:                        true,

	// RPC Server
	RPCEnabled:         true,
	RPCHost:            "127.0.0.1",
	RPCPort:            7246,
	RPCShutdownTimeout: 5 * time.Second,

	// Tracker
	TrackerNumWant:              200,
	TrackerStopTimeout:          5 * time.Second,
	TrackerMinAnnounceInterval:  time.Minute,
	TrackerHTTPTimeout:          10 * time.Second,
	TrackerHTTPPrivateUserAgent: "Rain/" + Version,
	TrackerHTTPMaxResponseSize:  2 << 20,
	TrackerHTTPVerifyTLS:        true,

	// DHT node
	DHTEnabled:             true,
	DHTHost:                "0.0.0.0",
	DHTPort:                7246,
	DHTAnnounceInterval:    30 * time.Minute,
	DHTMinAnnounceInterval: time.Minute,
	DHTBootstrapNodes: []string{
		"router.bittorrent.com:6881",
		"dht.transmissionbt.com:6881",
		"router.utorrent.com:6881",
		"dht.libtorrent.org:25401",
		"dht.aelitis.com:6881",
	},

	// Peer
	UnchokedPeers:                3,
	OptimisticUnchokedPeers:      1,
	MaxRequestsIn:                250,
	MaxRequestsOut:               250,
	DefaultRequestsOut:           50,
	RequestTimeout:               20 * time.Second,
	EndgameMaxDuplicateDownloads: 20,
	MaxPeerDial:                  80,
	MaxPeerAccept:                20,
	ParallelMetadataDownloads:    2,
	PeerConnectTimeout:           5 * time.Second,
	PeerHandshakeTimeout:         10 * time.Second,
	PieceReadTimeout:             30 * time.Second,
	MaxPeerAddresses:             2000,
	AllowedFastSet:               10,

	// IO
	ReadCacheBlockSize: 128 << 10,
	ReadCacheSize:      256 << 20,
	ReadCacheTTL:       1 * time.Minute,
	ParallelReads:      1,
	ParallelWrites:     1,
	WriteCacheSize:     1 << 30,

	// Webseed settings
	WebseedDialTimeout:             10 * time.Second,
	WebseedTLSHandshakeTimeout:     10 * time.Second,
	WebseedResponseHeaderTimeout:   10 * time.Second,
	WebseedResponseBodyReadTimeout: 10 * time.Second,
	WebseedRetryInterval:           time.Minute,
	WebseedVerifyTLS:               true,
	WebseedMaxSources:              10,
	WebseedMaxDownloads:            4,
}
<|endoftext|>


<commit_before>package business

import (
	"database/sql"
	"fmt"
	"io"
	"net/http"
	"time"

	"golang.org/x/crypto/bcrypt"

	"github.com/NAExpire/API/src/seshin"
	"github.com/NAExpire/API/src/util"
)

type LogoutHandler struct {
	DB *sql.DB
}

type LoginHandler struct {
	DB *sql.DB
}

type businessLoginCredentials struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

func (handler LoginHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	x := &businessLoginCredentials{}
	err := util.DecodeJSON(request.Body, x)
	fmt.Printf("Got %s request to LoginHandler\n", request.Method)
	if request.Method != "POST" {
		writer.WriteHeader(http.StatusMethodNotAllowed)
	} else if err != nil {
		io.WriteString(writer, err.Error()+"\n")
		return
	}

	rows, err := handler.DB.Query("SELECT password, confirmed FROM users WHERE email=?", x.Email)

	defer rows.Close()

	if err != nil {
		io.WriteString(writer, err.Error()+"\n")
		return
	}

	if rows.Next() {
		passwordHash := ""
		confirmed := 0
		err := rows.Scan(&passwordHash, &confirmed)
		if err != nil {
			io.WriteString(writer, err.Error()+"\n")
		}

		fmt.Printf("passwordHash: %s\n", passwordHash)

		if bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(x.Password)) == nil {
			if confirmed == 0 {
				io.WriteString(writer, "Email has not been confirmed yet\n")
			} else {
				myUniqueSessionID := seshin.GenerateSessionID()
				seshin.CreateSession(handler.DB, myUniqueSessionID)
				_, err = handler.DB.Exec("INSERT INTO users (`last-login`) VALUES (?)", time.Now())

				writer.WriteHeader(http.StatusOK)
				writer.Header().Add("session", myUniqueSessionID)
				io.WriteString(writer, "{\"ok\": true}")
			}
		} else {
			writer.WriteHeader(http.StatusConflict)
			io.WriteString(writer, "Email or password is incorrect!\n")
			return
		}
	} else {
		writer.WriteHeader(http.StatusConflict)
		io.WriteString(writer, "Email or password is incorrect\n")
		return
	}
}

func (handler LogoutHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	fmt.Printf("Got %s request to LogoutHandler\n", request.Method)
	if request.Method != "GET" {
		writer.WriteHeader(http.StatusMethodNotAllowed)
	}
	sessionID := request.Header.Get("session")

	isValidSession, err := seshin.ValidateSession(handler.DB, sessionID)
	if isValidSession {
		seshin.InvalidateSession(handler.DB, sessionID)
		writer.WriteHeader(http.StatusOK)
		io.WriteString(writer, "{\"ok\": true}")
	} else {
		if err != nil {
			io.WriteString(writer, err.Error()+"\n")
		} else {
			io.WriteString(writer, "{\"ok\": false}")
		}
	}
}
<commit_msg>auth returns session in response body<commit_after>package business

import (
	"database/sql"
	"fmt"
	"io"
	"net/http"
	"time"

	"golang.org/x/crypto/bcrypt"

	"github.com/NAExpire/API/src/seshin"
	"github.com/NAExpire/API/src/util"
)

type LogoutHandler struct {
	DB *sql.DB
}

type LoginHandler struct {
	DB *sql.DB
}

type businessLoginCredentials struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

func (handler LoginHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	x := &businessLoginCredentials{}
	err := util.DecodeJSON(request.Body, x)
	fmt.Printf("Got %s request to LoginHandler\n", request.Method)
	if request.Method != "POST" {
		writer.WriteHeader(http.StatusMethodNotAllowed)
	} else if err != nil {
		io.WriteString(writer, err.Error()+"\n")
		return
	}

	rows, err := handler.DB.Query("SELECT password, confirmed FROM users WHERE email=?", x.Email)

	defer rows.Close()

	if err != nil {
		io.WriteString(writer, err.Error()+"\n")
		return
	}

	if rows.Next() {
		passwordHash := ""
		confirmed := 0
		err := rows.Scan(&passwordHash, &confirmed)
		if err != nil {
			io.WriteString(writer, err.Error()+"\n")
		}
		if bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(x.Password)) == nil {
			if confirmed == 0 {
				io.WriteString(writer, "Email has not been confirmed yet\n")
			} else {
				myUniqueSessionID := seshin.GenerateSessionID()
				fmt.Printf("myUniqueSessionID: %s\n", myUniqueSessionID)
				seshin.CreateSession(handler.DB, myUniqueSessionID)
				_, err = handler.DB.Exec("INSERT INTO users (`last-login`) VALUES (?)", time.Now())

				writer.WriteHeader(http.StatusOK)
				responseBody := "{\"ok\": true,\"sessionID\": " + myUniqueSessionID + "}"
				io.WriteString(writer, responseBody)
			}
		} else {
			writer.WriteHeader(http.StatusConflict)
			io.WriteString(writer, "{\"ok\": false}")
			return
		}
	} else {
		writer.WriteHeader(http.StatusConflict)
		io.WriteString(writer, "{\"ok\": false}")
		return
	}
}

func (handler LogoutHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	fmt.Printf("Got %s request to LogoutHandler\n", request.Method)
	if request.Method != "GET" {
		writer.WriteHeader(http.StatusMethodNotAllowed)
	}
	sessionID := request.Header.Get("session")

	isValidSession, err := seshin.ValidateSession(handler.DB, sessionID)
	if isValidSession {
		seshin.InvalidateSession(handler.DB, sessionID)
		writer.WriteHeader(http.StatusOK)
		io.WriteString(writer, "{\"ok\": true}")
	} else {
		if err != nil {
			io.WriteString(writer, err.Error()+"\n")
		} else {
			io.WriteString(writer, "{\"ok\": false}")
		}
	}
}
<|endoftext|>


<commit_before>package tsar

import (
	"testing"
	"io/ioutil"
	"os"
	"time"

	"gopkg.in/libgit2/git2go.v22"
	"github.com/SearchSpring/RepoTsar/config"
)

func TestRepoTsarStruct(t *testing.T){
	testpath, err := ioutil.TempDir("", "RepoTsar")
	if err != nil {
		t.Error(err)
	}

	defer os.RemoveAll(testpath)
	config := config.Config{
		Repos: map[string]config.Repo{
			"testrepo1": config.Repo{
				URL: "https://github.com/libgit2/git2go.git",
				Path: testpath+"/Test1",
				Branch: "master",
			},
			"testrepo2": config.Repo{
				URL: "https://github.com/libgit2/git2go.git",
				Path: testpath+"Test2",
				Branch: "master",
			},

		},
		Signature: config.Signature{
			Name: "Testy Testerson",
			Email: "test@test.com",
		},
	}

	// one repo
	repotsar := RepoTsar{
		Config: config,
		Branch: "test",
		ReposList: []string{"testrepo1"},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err != nil {
		t.Error(err)
	}
	os.RemoveAll(testpath)

	// no reposlist
	repotsar = RepoTsar{
		Config: config,
		Branch: "test",
		ReposList: []string{""},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err != nil {
		t.Error(err)
	}
	os.RemoveAll(testpath)

	// no branch
	// one repo
	repotsar = RepoTsar{
		Config: config,
		Branch: "",
		ReposList: []string{"testrepo1"},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err != nil {
		t.Error(err)
	}

	// repo already exists
	repotsar = RepoTsar{
		Config: config,
		Branch: "",
		ReposList: []string{"testrepo1"},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err != nil {
		t.Error(err)
	}
	os.RemoveAll(testpath)

	// repo already exists
	repotsar = RepoTsar{
		Config: config,
		Branch: "",
		ReposList: []string{"bogusrepo"},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err == nil {
		t.Error("Repo doesn't exist, should have generated an error and didn't")
	}
	os.RemoveAll(testpath)

}<commit_msg>Update tsar_test.go<commit_after>package tsar

import (
	"testing"
	"io/ioutil"
	"os"
	"time"

	"gopkg.in/libgit2/git2go.v22"
	"github.com/searchspring/repo-tsar/config"
)

func TestRepoTsarStruct(t *testing.T){
	testpath, err := ioutil.TempDir("", "RepoTsar")
	if err != nil {
		t.Error(err)
	}

	defer os.RemoveAll(testpath)
	config := config.Config{
		Repos: map[string]config.Repo{
			"testrepo1": config.Repo{
				URL: "https://github.com/libgit2/git2go.git",
				Path: testpath+"/Test1",
				Branch: "master",
			},
			"testrepo2": config.Repo{
				URL: "https://github.com/libgit2/git2go.git",
				Path: testpath+"Test2",
				Branch: "master",
			},

		},
		Signature: config.Signature{
			Name: "Testy Testerson",
			Email: "test@test.com",
		},
	}

	// one repo
	repotsar := RepoTsar{
		Config: config,
		Branch: "test",
		ReposList: []string{"testrepo1"},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err != nil {
		t.Error(err)
	}
	os.RemoveAll(testpath)

	// no reposlist
	repotsar = RepoTsar{
		Config: config,
		Branch: "test",
		ReposList: []string{""},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err != nil {
		t.Error(err)
	}
	os.RemoveAll(testpath)

	// no branch
	// one repo
	repotsar = RepoTsar{
		Config: config,
		Branch: "",
		ReposList: []string{"testrepo1"},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err != nil {
		t.Error(err)
	}

	// repo already exists
	repotsar = RepoTsar{
		Config: config,
		Branch: "",
		ReposList: []string{"testrepo1"},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err != nil {
		t.Error(err)
	}
	os.RemoveAll(testpath)

	// repo already exists
	repotsar = RepoTsar{
		Config: config,
		Branch: "",
		ReposList: []string{"bogusrepo"},
		Signature: &git.Signature{
			Name: config.Signature.Name,
			Email: config.Signature.Email,
			When: time.Now(),
		},
	}
	err = repotsar.Run()
	if err == nil {
		t.Error("Repo doesn't exist, should have generated an error and didn't")
	}
	os.RemoveAll(testpath)

}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	fg "github.com/audrenbdb/goforeground"
	ps "github.com/mitchellh/go-ps"
	"os"
	"runtime"
	"strings"
	//"github.com/lxn/win"  I can't get this to be useful.
	//w32 "github.com/gonutz/w32/v2"  I also can't get this to be useful.
)

/*
  HISTORY
  -------
  8 Jun 22 -- Started playing w/ this.  This will take a while, as I have SIR in Boston soon.

*/

const lastModified = "June 9, 2022"

var verboseFlag bool
var pid int
var target string

func main() {
	fmt.Printf("newclickgo is my attempt to use Go to activate a process so I can click on the screen.  Last modified %s.  Compiled by %s \n",
		lastModified, runtime.Version())

	flag.BoolVar(&verboseFlag, "v", false, " Verbose flag")
	flag.StringVar(&target, "target", "", " Process name search target")
	flag.Parse()

	target = strings.ToLower(target)

	processes, err := ps.Processes()
	if err != nil {
		fmt.Printf(" Error from ps.Processes is %v.  Exiting \n", err)
		os.Exit(1)
	}

	fmt.Printf(" There are %d processes found by go-ps.\n", len(processes))

	for i := range processes {
		fmt.Printf("i = %d, name = %q, PID = %d, PPID = %d.\n", i, processes[i].Executable(), processes[i].Pid(), processes[i].PPid())
		processNameLower := strings.ToLower(processes[i].Executable())
		if target != "" && strings.Contains(processNameLower, target) {
			pid = processes[i].Pid()
			if !verboseFlag { // if verbose, show all processes even after find a match w/ target.
				break
			}
		}
	}

	fmt.Printf(" Target is %q, matched pid = %d.\n", target, pid)

	if pid != 0 { // pid == 0 when target is not found.  Don't want to activate process 0.
		err2 := fg.Activate(pid)
		if err2 != nil {
			fmt.Printf(" Error from fg.Activate is %v.  Exiting \n", err2)
			os.Exit(1)
		}
	}

	fmt.Printf(" There are %d processes found by go-ps.\n", len(processes))

}
<commit_msg>06/10/2022 21:52:47 newclickgo/newclickgo.go -- pets not quite working.  Processes part is working<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	fg "github.com/audrenbdb/goforeground"
	"github.com/go-vgo/robotgo"
	ps "github.com/mitchellh/go-ps"
	"os"
	"runtime"
	"strings"
	//"github.com/lxn/win"  I can't get this to be useful.
	//w32 "github.com/gonutz/w32/v2"  I also can't get this to be useful.
)

/*
  HISTORY
  -------
  8 Jun 22 -- Started playing w/ this.  This will take a while, as I have SIR in Boston soon.
  10 June 22 -- Seems to be mostly working.  Tomorrow going to Boston.

*/

const lastModified = "June 10, 2022"

var verboseFlag bool
var pid int
var target string

type pet struct {
	pid   int32
	exec  string
	title string
}

func main() {
	fmt.Printf("newclickgo is my attempt to use Go to activate a process so I can click on the screen.  Last modified %s.  Compiled by %s \n",
		lastModified, runtime.Version())

	flag.BoolVar(&verboseFlag, "v", false, " Verbose flag")
	flag.StringVar(&target, "target", "", " Process name search target")
	flag.Parse()

	target = strings.ToLower(target)

	processes, err := ps.Processes()
	if err != nil {
		fmt.Printf(" Error from ps.Processes is %v.  Exiting \n", err)
		os.Exit(1)
	}

	fmt.Printf(" There are %d processes found by go-ps.\n", len(processes))

	for i := range processes {
		fmt.Printf("i = %d, name = %q, PID = %d, PPID = %d.\n", i, processes[i].Executable(), processes[i].Pid(), processes[i].PPid())
		processNameLower := strings.ToLower(processes[i].Executable())
		if target != "" && strings.Contains(processNameLower, target) {
			pid = processes[i].Pid()
			if !verboseFlag { // if verbose, show all processes even after find a match w/ target.
				break
			}
		}
	}

	fmt.Printf(" Target is %q, matched pid = %d.\n", target, pid)

	if pid != 0 { // pid == 0 when target is not found.  Don't want to activate process 0.
		err2 := fg.Activate(pid)
		if err2 != nil {
			fmt.Printf(" Error from fg.Activate is %v.  Exiting \n", err2)
			os.Exit(1)
		}
	}

	fmt.Printf(" There are %d processes found by go-ps.\n", len(processes))

	pause()

	pets := make([]pet, 0, len(processes))
	for i := range processes {
		piD := int32(processes[i].Pid())
		apet := pet{ // meaning a pet
			pid:   piD,
			exec:  strings.ToLower(processes[i].Executable()),
			title: robotgo.GetTitle(piD),
		}
		pets = append(pets, apet)
	}

	fmt.Println(robotgo.GetTitle())
	ids, er := robotgo.FindIds("")
	if er != nil {
		fmt.Printf(" Error from robotgo FindIds is %v.  Exiting\n")
		os.Exit(1)
	}
	name, _ := robotgo.FindName(ids[100])
	fmt.Printf(" robotgo GetTitle for id[%d], title is %q, and name is %q\n", ids[100], robotgo.GetTitle(ids[100]), name)

	fmt.Printf(" Will now show you my pets.\n")
	pause()

	for _, peT := range pets {
		fmt.Printf(" PID=%d, exe=%q, Title=%q\n", peT.pid, peT.exec, peT.title)
	}
	fmt.Printf(" There are %d pets and %d processes.\n", len(pets), len(processes))

	pause()

	var piD int32
	for _, peT := range pets {
		if target != "" && (strings.Contains(peT.title, target) || strings.Contains(peT.exec, target)) {
			piD = peT.pid
			if !verboseFlag { // if verbose, show all processes even after find a match w/ target.
				break
			}
		}
	}

	if piD != 0 { // piD == 0 when target is not found.  Don't want to activate process 0.
		err2 := fg.Activate(int(piD))
		if err2 != nil {
			fmt.Printf(" Error from fg.Activate is %v.  Exiting \n", err2)
			os.Exit(1)
		}
	}

}

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

func pause() {
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print(" Pausing.  Hit <enter> to continue  ")
	scanner.Scan()
	_ = scanner.Text()
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 cli

import (
	"os"
	"sort"
	"strings"

	"github.com/spf13/cobra"
	"mynewt.apache.org/newt/newt/downloader"
	"mynewt.apache.org/newt/newt/interfaces"
	"mynewt.apache.org/newt/newt/project"
	"mynewt.apache.org/newt/util"
)

var projectForce bool = false

func newRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify "+
			"a project directory to newt new"))
	}

	newDir := args[0]

	util.StatusMessage(util.VERBOSITY_DEFAULT, "Downloading "+
		"project skeleton from apache/incubator-mynewt-blinky...\n")
	dl := downloader.NewGithubDownloader()
	dl.User = "apache"
	dl.Repo = "incubator-mynewt-blinky"

	dir, err := dl.DownloadRepo("develop")
	if err != nil {
		NewtUsage(cmd, err)
	}

	util.StatusMessage(util.VERBOSITY_DEFAULT, "Installing "+
		"skeleton in %s...\n", newDir)

	if err := util.CopyDir(dir, newDir); err != nil {
		NewtUsage(cmd, err)
	}

	if err := os.RemoveAll(newDir + "/" + "/.git/"); err != nil {
		NewtUsage(cmd, err)
	}

	util.StatusMessage(util.VERBOSITY_DEFAULT,
		"Project %s successfully created.\n", newDir)
}

func installRunCmd(cmd *cobra.Command, args []string) {
	if err := project.Initialize(); err != nil {
		NewtUsage(cmd, err)
	}
	proj := project.GetProject()
	interfaces.SetProject(proj)

	if err := proj.Install(false, projectForce); err != nil {
		NewtUsage(cmd, err)
	}
}

func upgradeRunCmd(cmd *cobra.Command, args []string) {
	if err := project.Initialize(); err != nil {
		NewtUsage(cmd, err)
	}
	proj := project.GetProject()
	interfaces.SetProject(proj)

	if err := proj.Upgrade(projectForce); err != nil {
		NewtUsage(cmd, err)
	}
}

func infoRunCmd(cmd *cobra.Command, args []string) {
	reqRepoName := ""
	if len(args) >= 1 {
		reqRepoName = strings.TrimPrefix(args[0], "@")
	}

	if err := project.Initialize(); err != nil {
		NewtUsage(nil, err)
	}
	proj := project.GetProject()

	repoNames := []string{}
	for repoName, _ := range proj.PackageList() {
		repoNames = append(repoNames, repoName)
	}
	sort.Strings(repoNames)

	if reqRepoName == "" {
		util.StatusMessage(util.VERBOSITY_DEFAULT, "Repositories in %s:\n",
			proj.Name())

		for _, repoName := range repoNames {
			util.StatusMessage(util.VERBOSITY_DEFAULT, "    * @%s\n", repoName)
		}

		// Now display the packages in the local repository.
		util.StatusMessage(util.VERBOSITY_DEFAULT, "\n")
		reqRepoName = "local"
	}

	firstRepo := true
	for _, repoName := range repoNames {
		if reqRepoName == "all" || reqRepoName == repoName {
			packNames := []string{}
			for _, pack := range *proj.PackageList()[repoName] {
				packNames = append(packNames, pack.Name())
			}

			sort.Strings(packNames)
			if !firstRepo {
				util.StatusMessage(util.VERBOSITY_DEFAULT, "\n")
			} else {
				firstRepo = false
			}
			util.StatusMessage(util.VERBOSITY_DEFAULT, "Packages in @%s:\n",
				repoName)
			for _, pkgName := range packNames {
				util.StatusMessage(util.VERBOSITY_DEFAULT, "    * %s\n",
					pkgName)
			}
		}
	}
}

func AddProjectCommands(cmd *cobra.Command) {
	installHelpText := ""
	installHelpEx := ""
	installCmd := &cobra.Command{
		Use:     "install",
		Short:   "Install project dependencies",
		Long:    installHelpText,
		Example: installHelpEx,
		Run:     installRunCmd,
	}
	installCmd.PersistentFlags().BoolVarP(&projectForce, "force", "f", false,
		"Force install of the repositories in project, regardless of what "+
			"exists in repos directory")

	cmd.AddCommand(installCmd)

	upgradeHelpText := ""
	upgradeHelpEx := ""
	upgradeCmd := &cobra.Command{
		Use:     "upgrade",
		Short:   "Upgrade project dependencies",
		Long:    upgradeHelpText,
		Example: upgradeHelpEx,
		Run:     upgradeRunCmd,
	}
	upgradeCmd.PersistentFlags().BoolVarP(&projectForce, "force", "f", false,
		"Force upgrade of the repositories to latest state in project.yml")

	cmd.AddCommand(upgradeCmd)

	newHelpText := ""
	newHelpEx := ""
	newCmd := &cobra.Command{
		Use:     "new <project-dir>",
		Short:   "Create a new project",
		Long:    newHelpText,
		Example: newHelpEx,
		Run:     newRunCmd,
	}

	cmd.AddCommand(newCmd)

	infoHelpText := "Show information about the current project."
	infoHelpEx := "  newt info\n"

	infoCmd := &cobra.Command{
		Use:     "info",
		Short:   "Show project info",
		Long:    infoHelpText,
		Example: infoHelpEx,
		Run:     infoRunCmd,
	}

	cmd.AddCommand(infoCmd)
}
<commit_msg>Newt new should fail if proj dir already exists.<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 cli

import (
	"os"
	"sort"
	"strings"

	"github.com/spf13/cobra"
	"mynewt.apache.org/newt/newt/downloader"
	"mynewt.apache.org/newt/newt/interfaces"
	"mynewt.apache.org/newt/newt/project"
	"mynewt.apache.org/newt/util"
)

var projectForce bool = false

func newRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify "+
			"a project directory to newt new"))
	}

	newDir := args[0]

	if util.NodeExist(newDir) {
		NewtUsage(cmd, util.NewNewtError("Cannot create new project, "+
			"directory already exists"))
	}

	util.StatusMessage(util.VERBOSITY_DEFAULT, "Downloading "+
		"project skeleton from apache/incubator-mynewt-blinky...\n")
	dl := downloader.NewGithubDownloader()
	dl.User = "apache"
	dl.Repo = "incubator-mynewt-blinky"

	dir, err := dl.DownloadRepo("develop")
	if err != nil {
		NewtUsage(cmd, err)
	}

	util.StatusMessage(util.VERBOSITY_DEFAULT, "Installing "+
		"skeleton in %s...\n", newDir)

	if err := util.CopyDir(dir, newDir); err != nil {
		NewtUsage(cmd, err)
	}

	if err := os.RemoveAll(newDir + "/" + "/.git/"); err != nil {
		NewtUsage(cmd, err)
	}

	util.StatusMessage(util.VERBOSITY_DEFAULT,
		"Project %s successfully created.\n", newDir)
}

func installRunCmd(cmd *cobra.Command, args []string) {
	if err := project.Initialize(); err != nil {
		NewtUsage(cmd, err)
	}
	proj := project.GetProject()
	interfaces.SetProject(proj)

	if err := proj.Install(false, projectForce); err != nil {
		NewtUsage(cmd, err)
	}
}

func upgradeRunCmd(cmd *cobra.Command, args []string) {
	if err := project.Initialize(); err != nil {
		NewtUsage(cmd, err)
	}
	proj := project.GetProject()
	interfaces.SetProject(proj)

	if err := proj.Upgrade(projectForce); err != nil {
		NewtUsage(cmd, err)
	}
}

func infoRunCmd(cmd *cobra.Command, args []string) {
	reqRepoName := ""
	if len(args) >= 1 {
		reqRepoName = strings.TrimPrefix(args[0], "@")
	}

	if err := project.Initialize(); err != nil {
		NewtUsage(nil, err)
	}
	proj := project.GetProject()

	repoNames := []string{}
	for repoName, _ := range proj.PackageList() {
		repoNames = append(repoNames, repoName)
	}
	sort.Strings(repoNames)

	if reqRepoName == "" {
		util.StatusMessage(util.VERBOSITY_DEFAULT, "Repositories in %s:\n",
			proj.Name())

		for _, repoName := range repoNames {
			util.StatusMessage(util.VERBOSITY_DEFAULT, "    * @%s\n", repoName)
		}

		// Now display the packages in the local repository.
		util.StatusMessage(util.VERBOSITY_DEFAULT, "\n")
		reqRepoName = "local"
	}

	firstRepo := true
	for _, repoName := range repoNames {
		if reqRepoName == "all" || reqRepoName == repoName {
			packNames := []string{}
			for _, pack := range *proj.PackageList()[repoName] {
				packNames = append(packNames, pack.Name())
			}

			sort.Strings(packNames)
			if !firstRepo {
				util.StatusMessage(util.VERBOSITY_DEFAULT, "\n")
			} else {
				firstRepo = false
			}
			util.StatusMessage(util.VERBOSITY_DEFAULT, "Packages in @%s:\n",
				repoName)
			for _, pkgName := range packNames {
				util.StatusMessage(util.VERBOSITY_DEFAULT, "    * %s\n",
					pkgName)
			}
		}
	}
}

func AddProjectCommands(cmd *cobra.Command) {
	installHelpText := ""
	installHelpEx := ""
	installCmd := &cobra.Command{
		Use:     "install",
		Short:   "Install project dependencies",
		Long:    installHelpText,
		Example: installHelpEx,
		Run:     installRunCmd,
	}
	installCmd.PersistentFlags().BoolVarP(&projectForce, "force", "f", false,
		"Force install of the repositories in project, regardless of what "+
			"exists in repos directory")

	cmd.AddCommand(installCmd)

	upgradeHelpText := ""
	upgradeHelpEx := ""
	upgradeCmd := &cobra.Command{
		Use:     "upgrade",
		Short:   "Upgrade project dependencies",
		Long:    upgradeHelpText,
		Example: upgradeHelpEx,
		Run:     upgradeRunCmd,
	}
	upgradeCmd.PersistentFlags().BoolVarP(&projectForce, "force", "f", false,
		"Force upgrade of the repositories to latest state in project.yml")

	cmd.AddCommand(upgradeCmd)

	newHelpText := ""
	newHelpEx := ""
	newCmd := &cobra.Command{
		Use:     "new <project-dir>",
		Short:   "Create a new project",
		Long:    newHelpText,
		Example: newHelpEx,
		Run:     newRunCmd,
	}

	cmd.AddCommand(newCmd)

	infoHelpText := "Show information about the current project."
	infoHelpEx := "  newt info\n"

	infoCmd := &cobra.Command{
		Use:     "info",
		Short:   "Show project info",
		Long:    infoHelpText,
		Example: infoHelpEx,
		Run:     infoRunCmd,
	}

	cmd.AddCommand(infoCmd)
}
<|endoftext|>


<commit_before>/*
 * The GraphicsManager deals with initialising and scheduling all graphics events.
 *
 * It will initialise the SDL module, and then arrange the piping of all Entities through
 * the graphics pipeline.
 *
 * The current graphics pipeline is:
 * External -> Interpreter -> Artist -> Screen
 *
 * The Interpreter breaks entities down into primitive shapes, which the artist draws
 * to the screen.
 */

package graphics

import "fmt"
import "github.com/DiscoViking/goBrains/entity"

import "github.com/banthar/Go-SDL/sdl"

const (
	WIDTH  = 640
	HEIGHT = 480
)

// Starts the graphics engine up.
// Once called, pass a slice of Entities into the passed in channel
// once per frame for drawing.
// Then wait on the done channel for drawing to finish before continuing.
func Start(data chan []entity.Entity, done chan struct{}) {

	// Initialise SDL
	fmt.Printf("Initialising SDL.")
	if sdl.Init(sdl.INIT_EVERYTHING) != 0 {
		panic(sdl.GetError())
	}

	// Ensure that SDL will exit gracefully when we're done.
	defer sdl.Quit()

	// Create the screen surface.
	fmt.Printf("Creating screen\n")
	screen := sdl.SetVideoMode(WIDTH, HEIGHT, 32, sdl.RESIZABLE|sdl.DOUBLEBUF|sdl.SWSURFACE)
	canvas := sdl.CreateRGBSurface(0, WIDTH, HEIGHT, 32, 0, 0, 0, 0)
	background := sdl.MapRGB(sdl.GetVideoInfo().Vfmt, 200, 200, 200)

	fmt.Printf("Entering main loop\n")
	// Main drawing loop
	time := uint32(0)
	frame := 0
	for entities := range data {
		frame = (frame + 1) % 100
		if frame == 0 {
			newTime := sdl.GetTicks()
			dt := newTime - time
			time = newTime
			fps := 100000 / float32(dt)
			fmt.Printf("Dt: %v, FPS: %v\n", dt, fps)
		}

		canvas.FillRect(&sdl.Rect{0, 0, WIDTH, HEIGHT}, background)

		// Construct the graphics pipeline.
		interpret := make(chan entity.Entity)
		draw := make(chan Primitive)
		drawn := make(chan struct{})

		// Set off the interpreter and artist goroutines.
		go Interpret(interpret, draw)
		go Draw(draw, canvas, drawn)

		for _, e := range entities {
			// Send entities to the interpreter
			interpret <- e
		}

		// Close the interpret pipe. This will cause the interpreter to finish.
		// It will then close the drawing pipe, causing the artist to finish.
		// He will then signal to us that he is done via the drawn pipe.
		close(interpret)
		<-drawn

		// This is pretty dangerous, since we're continuing without waiting for the artist
		// to finish drawing the final entity to screen.
		// I think that the worst that will happen is that one entity will be drawn a single
		// frame behind the others, which is no great loss. However if necessary this can
		// be fixed, it just requires a bit more synchronization rubbish and slightly ruins
		// the cleanliness of this pipeline design.
		screen.Flip()
		screen.Blit(nil, canvas, nil)
		done <- struct{}{}
	}
}
<commit_msg>Moved blit before flip. This is safe now that we wait for the artist to finish before continuing.<commit_after>/*
 * The GraphicsManager deals with initialising and scheduling all graphics events.
 *
 * It will initialise the SDL module, and then arrange the piping of all Entities through
 * the graphics pipeline.
 *
 * The current graphics pipeline is:
 * External -> Interpreter -> Artist -> Screen
 *
 * The Interpreter breaks entities down into primitive shapes, which the artist draws
 * to the screen.
 */

package graphics

import "fmt"
import "github.com/DiscoViking/goBrains/entity"

import "github.com/banthar/Go-SDL/sdl"

const (
	WIDTH  = 640
	HEIGHT = 480
)

// Starts the graphics engine up.
// Once called, pass a slice of Entities into the passed in channel
// once per frame for drawing.
// Then wait on the done channel for drawing to finish before continuing.
func Start(data chan []entity.Entity, done chan struct{}) {

	// Initialise SDL
	fmt.Printf("Initialising SDL.")
	if sdl.Init(sdl.INIT_EVERYTHING) != 0 {
		panic(sdl.GetError())
	}

	// Ensure that SDL will exit gracefully when we're done.
	defer sdl.Quit()

	// Create the screen surface.
	fmt.Printf("Creating screen\n")
	screen := sdl.SetVideoMode(WIDTH, HEIGHT, 32, sdl.RESIZABLE|sdl.DOUBLEBUF|sdl.SWSURFACE)
	canvas := sdl.CreateRGBSurface(0, WIDTH, HEIGHT, 32, 0, 0, 0, 0)
	background := sdl.MapRGB(sdl.GetVideoInfo().Vfmt, 200, 200, 200)

	fmt.Printf("Entering main loop\n")
	// Main drawing loop
	time := uint32(0)
	frame := 0
	for entities := range data {
		frame = (frame + 1) % 100
		if frame == 0 {
			newTime := sdl.GetTicks()
			dt := newTime - time
			time = newTime
			fps := 100000 / float32(dt)
			fmt.Printf("Dt: %v, FPS: %v\n", dt, fps)
		}

		canvas.FillRect(&sdl.Rect{0, 0, WIDTH, HEIGHT}, background)

		// Construct the graphics pipeline.
		interpret := make(chan entity.Entity)
		draw := make(chan Primitive)
		drawn := make(chan struct{})

		// Set off the interpreter and artist goroutines.
		go Interpret(interpret, draw)
		go Draw(draw, canvas, drawn)

		for _, e := range entities {
			// Send entities to the interpreter
			interpret <- e
		}

		// Close the interpret pipe. This will cause the interpreter to finish.
		// It will then close the drawing pipe, causing the artist to finish.
		// He will then signal to us that he is done via the drawn pipe.
		close(interpret)
		<-drawn

		// Finally flip the surface to paint to the screen.
		screen.Blit(nil, canvas, nil)
		screen.Flip()
		done <- struct{}{}
	}
}
<|endoftext|>