<commit_before>package gandi

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/StackExchange/dnscontrol/models"
	"github.com/StackExchange/dnscontrol/providers"
	"github.com/StackExchange/dnscontrol/providers/diff"

	"strings"

	gandidomain "github.com/prasmussen/gandi-api/domain"
	gandirecord "github.com/prasmussen/gandi-api/domain/zone/record"
)

/*

Gandi API DNS provider:

Info required in `creds.json`:
   - apikey

*/

type GandiApi struct {
	ApiKey      string
	domainIndex map[string]int64 // Map of domainname to index
	nameservers map[string][]*models.Nameserver
	ZoneId      int64
}

type gandiRecord struct {
	gandirecord.RecordInfo
}

func (c *GandiApi) getDomainInfo(domain string) (*gandidomain.DomainInfo, error) {
	if err := c.fetchDomainList(); err != nil {
		return nil, err
	}
	_, ok := c.domainIndex[domain]
	if !ok {
		return nil, fmt.Errorf("%s not listed in zones for gandi account", domain)
	}
	return c.fetchDomainInfo(domain)
}

func (c *GandiApi) GetNameservers(domain string) ([]*models.Nameserver, error) {
	domaininfo, err := c.getDomainInfo(domain)
	if err != nil {
		return nil, err
	}
	ns := []*models.Nameserver{}
	for _, nsname := range domaininfo.Nameservers {
		ns = append(ns, &models.Nameserver{Name: nsname})
	}
	return ns, nil
}

func (c *GandiApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	dc.Punycode()
	dc.CombineMXs()
	domaininfo, err := c.getDomainInfo(dc.Name)
	if err != nil {
		return nil, err
	}
	foundRecords, err := c.getZoneRecords(domaininfo.ZoneId, dc.Name)
	if err != nil {
		return nil, err
	}

	expectedRecordSets := make([]gandirecord.RecordSet, 0, len(dc.Records))
	recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
	for _, rec := range dc.Records {
		if rec.TTL < 300 {
			log.Printf("WARNING: Gandi does not support ttls < 300. %s will not be set to %d.", rec.NameFQDN, rec.TTL)
			rec.TTL = 300
		}
		if rec.Type == "TXT" {
			rec.Target = "\"" + rec.Target + "\"" // FIXME(tlim): Should do proper quoting.
		}
		if rec.Type == "NS" && rec.Name == "@" {
			if !strings.HasSuffix(rec.Target, ".gandi.net.") {
				log.Printf("WARNING: Gandi does not support changing apex NS records. %s will not be added.", rec.Target)
			}
			continue
		}
		rs := gandirecord.RecordSet{
			"type":  rec.Type,
			"name":  rec.Name,
			"value": rec.Target,
			"ttl":   rec.TTL,
		}
		expectedRecordSets = append(expectedRecordSets, rs)
		recordsToKeep = append(recordsToKeep, rec)
	}
	dc.Records = recordsToKeep
	differ := diff.New(dc)
	_, create, del, mod := differ.IncrementalDiff(foundRecords)

	// Print a list of changes. Generate an actual change that is the zone
	changes := false
	desc := ""
	for _, i := range create {
		changes = true
		desc += "\n" + i.String()
	}
	for _, i := range del {
		changes = true
		desc += "\n" + i.String()
	}
	for _, i := range mod {
		changes = true
		desc += "\n" + i.String()
	}

	msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)%s", dc.Name, len(dc.Records), desc)
	corrections := []*models.Correction{}
	if changes {
		corrections = append(corrections,
			&models.Correction{
				Msg: msg,
				F: func() error {
					fmt.Printf("CREATING ZONE: %v\n", dc.Name)
					return c.createGandiZone(dc.Name, domaininfo.ZoneId, expectedRecordSets)
				},
			})
	}

	return corrections, nil
}

func newGandi(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
	api := &GandiApi{}
	api.ApiKey = m["apikey"]
	if api.ApiKey == "" {
		return nil, fmt.Errorf("Gandi apikey must be provided.")
	}

	return api, nil
}

func init() {
	providers.RegisterDomainServiceProviderType("GANDI", newGandi)
}
<commit_msg>Gandi does not support TTLs > 30 days (#139)<commit_after>package gandi

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/StackExchange/dnscontrol/models"
	"github.com/StackExchange/dnscontrol/providers"
	"github.com/StackExchange/dnscontrol/providers/diff"
	"github.com/pkg/errors"

	"strings"

	gandidomain "github.com/prasmussen/gandi-api/domain"
	gandirecord "github.com/prasmussen/gandi-api/domain/zone/record"
)

/*

Gandi API DNS provider:

Info required in `creds.json`:
   - apikey

*/

type GandiApi struct {
	ApiKey      string
	domainIndex map[string]int64 // Map of domainname to index
	nameservers map[string][]*models.Nameserver
	ZoneId      int64
}

type gandiRecord struct {
	gandirecord.RecordInfo
}

func (c *GandiApi) getDomainInfo(domain string) (*gandidomain.DomainInfo, error) {
	if err := c.fetchDomainList(); err != nil {
		return nil, err
	}
	_, ok := c.domainIndex[domain]
	if !ok {
		return nil, fmt.Errorf("%s not listed in zones for gandi account", domain)
	}
	return c.fetchDomainInfo(domain)
}

func (c *GandiApi) GetNameservers(domain string) ([]*models.Nameserver, error) {
	domaininfo, err := c.getDomainInfo(domain)
	if err != nil {
		return nil, err
	}
	ns := []*models.Nameserver{}
	for _, nsname := range domaininfo.Nameservers {
		ns = append(ns, &models.Nameserver{Name: nsname})
	}
	return ns, nil
}

func (c *GandiApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
	dc.Punycode()
	dc.CombineMXs()
	domaininfo, err := c.getDomainInfo(dc.Name)
	if err != nil {
		return nil, err
	}
	foundRecords, err := c.getZoneRecords(domaininfo.ZoneId, dc.Name)
	if err != nil {
		return nil, err
	}

	expectedRecordSets := make([]gandirecord.RecordSet, 0, len(dc.Records))
	recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
	for _, rec := range dc.Records {
		if rec.TTL < 300 {
			log.Printf("WARNING: Gandi does not support ttls < 300. %s will not be set to %d.", rec.NameFQDN, rec.TTL)
			rec.TTL = 300
		}
		if rec.TTL > 2592000 {
			return nil, errors.Errorf("ERROR: Gandi does not support TTLs > 30 days (TTL=%d)", rec.TTL)
		}
		if rec.Type == "TXT" {
			rec.Target = "\"" + rec.Target + "\"" // FIXME(tlim): Should do proper quoting.
		}
		if rec.Type == "NS" && rec.Name == "@" {
			if !strings.HasSuffix(rec.Target, ".gandi.net.") {
				log.Printf("WARNING: Gandi does not support changing apex NS records. %s will not be added.", rec.Target)
			}
			continue
		}
		rs := gandirecord.RecordSet{
			"type":  rec.Type,
			"name":  rec.Name,
			"value": rec.Target,
			"ttl":   rec.TTL,
		}
		expectedRecordSets = append(expectedRecordSets, rs)
		recordsToKeep = append(recordsToKeep, rec)
	}
	dc.Records = recordsToKeep
	differ := diff.New(dc)
	_, create, del, mod := differ.IncrementalDiff(foundRecords)

	// Print a list of changes. Generate an actual change that is the zone
	changes := false
	desc := ""
	for _, i := range create {
		changes = true
		desc += "\n" + i.String()
	}
	for _, i := range del {
		changes = true
		desc += "\n" + i.String()
	}
	for _, i := range mod {
		changes = true
		desc += "\n" + i.String()
	}

	msg := fmt.Sprintf("GENERATE_ZONE: %s (%d records)%s", dc.Name, len(dc.Records), desc)
	corrections := []*models.Correction{}
	if changes {
		corrections = append(corrections,
			&models.Correction{
				Msg: msg,
				F: func() error {
					fmt.Printf("CREATING ZONE: %v\n", dc.Name)
					return c.createGandiZone(dc.Name, domaininfo.ZoneId, expectedRecordSets)
				},
			})
	}

	return corrections, nil
}

func newGandi(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
	api := &GandiApi{}
	api.ApiKey = m["apikey"]
	if api.ApiKey == "" {
		return nil, fmt.Errorf("Gandi apikey must be provided.")
	}

	return api, nil
}

func init() {
	providers.RegisterDomainServiceProviderType("GANDI", newGandi)
}
<|endoftext|>


<commit_before>package flightid

import(
	"fmt"
	"net/http"
	"regexp"
	"sort"
	"strings"
	"time"

	"google.golang.org/appengine"
	"google.golang.org/appengine/urlfetch"

	fdb "github.com/skypies/flightdb"
	"github.com/skypies/flightdb/fr24"
	"github.com/skypies/geo"
	"github.com/skypies/pi/airspace"
)

func init() {
	http.HandleFunc("/cdb/airspace", airspaceHandler)
}

// {{{ airspaceHandler

func airspaceHandler(w http.ResponseWriter, r *http.Request) {
	client := urlfetch.Client(appengine.NewContext(r))
	pos := geo.Latlong{37.060312,-121.990814}
	str := ""

	if as,err := fr24.FetchAirspace(client, pos.Box(100,100)); err != nil {
		http.Error(w, fmt.Sprintf("FetchAirspace: %v", err), http.StatusInternalServerError)
		return
	} else {
		names := SelectorNames
		names = append([]string{"conservative"}, names...)
		for _,name := range names {
			algo := NewSelector(name)
			oh,debstr := IdentifyOverhead(as, pos, 0.0, algo)
			str += fmt.Sprintf("--{ IdentifyOverhead, algo: %s }--\n -{ OH: %s }-\n\n%s\n",
				algo, oh, debstr)
		}
	}

	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte(str))
}

// }}}

// {{{ AirspaceToLocalizedAircraft

func AirspaceToLocalizedAircraft(as *airspace.Airspace, pos geo.Latlong, elev float64) []Aircraft {
	ret := []Aircraft{}

	if as == nil { return ret }

	for _,ad := range as.Aircraft {
		tp := fdb.TrackpointFromADSB(ad.Msg)
		altitudeDelta := tp.Altitude - elev

		icaoid := string(ad.Msg.Icao24)
		icaoid = strings.TrimPrefix(icaoid, "EE") // fr24 airspaces use this prefix
		icaoid = strings.TrimPrefix(icaoid, "FF") // fa airspaces use this prefix

		a := Aircraft{
			Dist: pos.DistKM(tp.Latlong),
			Dist3: pos.Dist3(tp.Latlong, altitudeDelta),
			BearingFromObserver: pos.BearingTowards(tp.Latlong),
			//Id: "someid", // This is set via an IdSpec string below
			Id2: icaoid,
			// Squawk string
			EquipType: ad.Airframe.EquipmentType,
			Registration: ad.Airframe.Registration,
			////Epoch: float64(tp.TimestampUTC.Unix()),
			Origin: ad.Schedule.Origin,
			Destination: ad.Schedule.Destination,
			FlightNumber: ad.Schedule.IataFlight(),
			// Unknown float64
			Callsign: ad.Msg.Callsign,
			// Unknown2 float64
		}

		a.FromTrackpoint(tp) // Populate basic fields from the trackpoint

		// Even though we may be parsing an fr24 airspace, generate a (perhaps invalid) skypi idspec
		if icaoid != "" {
			a.Id = fdb.IdSpec{ IcaoId:icaoid, Time:tp.TimestampUTC }.String()
		}

		// Hack for Surf Air; promote their callsigns into flightnumbers.
		// The goal is to allow them to get past the filter, and be printable etc.
		// There is probably a much better place for this kind of munging. But it needs to happen
		//  regardless of the source of the airspace, so it can't be pushed upstream. (...?)
		if a.FlightNumber == "" && regexp.MustCompile("^URF\\d+$").MatchString(a.Callsign) {
			a.FlightNumber = a.Callsign
		}

		ret = append(ret, a)
	}

	sort.Sort(AircraftByDist3(ret))

	return ret
}

// }}}
// {{{ AircraftToString

func AircraftToString(list []Aircraft) string {
	header := "3Dist  2Dist  Brng   Hdng    Alt      Speed VertSpd  Equp Flight   Orig:Dest IcaoID "+
		"Callsign Source  Age\n"

	lines := []string{}

	for _,a := range list {
		line := fmt.Sprintf(
			"%4.1fKM %4.1fKM %3.0fdeg %3.0fdeg %6.0fft %4.0fkt %5.0ffpm "+
			"%4.4s %-8.8s %4.4s:%-4.4s %6.6s %-8.8s %-7.7s "+
			"%2.0fs\n",
			a.Dist3, a.Dist, a.BearingFromObserver, a.Track, a.Altitude, a.Speed, a.VerticalSpeed,
			a.EquipType, a.FlightNumber,
			a.Origin, a.Destination, a.Id2, a.Callsign, a.Radar,
			time.Since(time.Unix(int64(a.Epoch),0)).Seconds())

		lines = append(lines, line)
	}

	//sort.Strings(lines)

	return header + strings.Join(lines, "")
}

// }}}
// {{{ FilterAircraft

func FilterAircraft(in []Aircraft) []Aircraft {
	out := []Aircraft{}

	for _,a := range in {
		age := time.Since(time.Unix(int64(a.Epoch),0))

		if age > 30 * time.Second { continue } // Data too old to use
		if a.FlightNumber == "" { continue }   // Drop unscheduled traffic; poor way of skipping GA
		if a.Altitude > 28000 { continue }     // Too high to be the problem
		if a.Altitude <   750 { continue }     // Too low to be the problem

		out = append(out, a)
	}

	return out
}

// }}}
// {{{ TimeSyncAircraft

// datapoints have ages from 3 to 30s. We sync them to a target age, doing some linear extrapolation
// to adjust the position and altitude. The idea is that they're a more accurate set of data to
// pick a culprit from.

func TimeSyncAircraft(in []Aircraft, pos geo.Latlong, elev float64, targetAge time.Duration) []Aircraft {
	out := []Aircraft{}

	for i,_ := range in {
		new := in[i] // copy it over, then move the new one

		actualAge := time.Since(time.Unix(int64(new.Epoch),0))
		timeToWind := actualAge - targetAge

		newTP := new.Trackpoint().RepositionByTime(timeToWind)
		new.FromTrackpoint(newTP)

		new.Dist = pos.DistKM(new.Latlong())
		new.Dist3 = pos.Dist3(new.Latlong(), new.Altitude-elev)
		new.BearingFromObserver = pos.BearingTowards(new.Latlong())

		out = append(out, new)
	}

	// Resort the array
	sort.Sort(AircraftByDist3(out))

	return out
}

// }}}
// {{{ IdentifyOverhead

func IdentifyOverhead(as *airspace.Airspace, pos geo.Latlong, elev float64, algo Selector) (*Aircraft, string) {
	if as == nil { return nil, "** airspace was nil\n" }

	nearby := AirspaceToLocalizedAircraft(as, pos, elev)
	filtered := FilterAircraft(nearby)

	if false {
		targetAge := 9 * time.Second
		filtered = TimeSyncAircraft(filtered, pos, elev, targetAge)
	}

	ret,outcome := algo.Identify(pos,elev,filtered)

	str := fmt.Sprintf("**** identification method: %s\n**** outcome: %s\n\n", algo, outcome)
	str += fmt.Sprintf("** Processed [%d] **\n%s\n", len(filtered), AircraftToString(filtered))
	str += fmt.Sprintf("** Raw [%d] **\n%s\n", len(nearby), AircraftToString(nearby))

	return ret,str
}

// }}}

// Prob 1: URF on skypi. fr24 rewrites callsign into 'schedule number'; but the raw MLAT data
//  has the registration as the callsign. So fr24 know how to map registration into 'schedule'.
// Even more annoying, fr24 doesn't have IcaoID at this time, because it's on near-time FAA.
//  fr24:  ["bc96f5f","",36.8055,-122.1902,162,15700,271,"0000","T-F5M","PC12","",1480729635,"SQL","SBA","",0,1088,"URF133",0]
//  skypi: http://fdb.serfr1.org/fdb/tracks?idspec=AC06C3@1480729827
// BUT BUT BUT ... the schedcache may save us, as it maps icaoids to flightnumbers; and with
//  the fr24/URF hack, we'll now have those flightnumbers. Maybe !

// The datatypes in this file can be confusing. The input:
//
//   pi/airspace.Airspace         : the input (from skypi, or fr24, or anything else)
//     pi/airspace.AircraftData   : an ADSB message-based model of a flight at a point in time
//       fdb/schedule.Schedule    : flightnumber, origin.dest, etc. If we have it.
//       fdb/airframe.Airframe    : equipment type, registration, keyed from IcaoId. If we have it.
//
// And the output, only used within the complaints system:
//
//   complaints/flightid.Aircraft : a flattened subset of the above, localized to the user

/* New algorithm ...

Inputs:
 list of flights, each with a position, groundspeed, altitude, and timestamp.
 user, also with a position, elevation, and timestamp (of button press)

0. [Optional fudge]: apply a handicap delay to the user (assume they're late pressing the button)

1. Forward-extrapolate aircraft's position, based on (user.tstamp - flight.tstamp) * flight.velocity
This is approximate; assumes level flight, and no acceleration.

2. Compute the time delay for sound propagation, based on 3D distance
This is approximate; speed of sound will vary with atmosphere.

3. Back-extrapolate the position of the aircraft at the time the button was pressed.

4. Compute distance between aircraft and user.

5. Sort aircraft by this distance.

A jet at 10,000', and 4KM lateral, is ~5KM away.
A cessna at 2,000', and 4KM lateral, is <5KM away, and would be closest.

 */

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

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

// }}}
<commit_msg>Put the time-sync back, now that the randomness is fixed. Move to 6s, too.<commit_after>package flightid

import(
	"fmt"
	"net/http"
	"regexp"
	"sort"
	"strings"
	"time"

	"google.golang.org/appengine"
	"google.golang.org/appengine/urlfetch"

	fdb "github.com/skypies/flightdb"
	"github.com/skypies/flightdb/fr24"
	"github.com/skypies/geo"
	"github.com/skypies/pi/airspace"
)

func init() {
	http.HandleFunc("/cdb/airspace", airspaceHandler)
}

// {{{ airspaceHandler

func airspaceHandler(w http.ResponseWriter, r *http.Request) {
	client := urlfetch.Client(appengine.NewContext(r))
	pos := geo.Latlong{37.060312,-121.990814}
	str := ""

	if as,err := fr24.FetchAirspace(client, pos.Box(100,100)); err != nil {
		http.Error(w, fmt.Sprintf("FetchAirspace: %v", err), http.StatusInternalServerError)
		return
	} else {
		names := SelectorNames
		names = append([]string{"conservative"}, names...)
		for _,name := range names {
			algo := NewSelector(name)
			oh,debstr := IdentifyOverhead(as, pos, 0.0, algo)
			str += fmt.Sprintf("--{ IdentifyOverhead, algo: %s }--\n -{ OH: %s }-\n\n%s\n",
				algo, oh, debstr)
		}
	}

	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte(str))
}

// }}}

// {{{ AirspaceToLocalizedAircraft

func AirspaceToLocalizedAircraft(as *airspace.Airspace, pos geo.Latlong, elev float64) []Aircraft {
	ret := []Aircraft{}

	if as == nil { return ret }

	for _,ad := range as.Aircraft {
		tp := fdb.TrackpointFromADSB(ad.Msg)
		altitudeDelta := tp.Altitude - elev

		icaoid := string(ad.Msg.Icao24)
		icaoid = strings.TrimPrefix(icaoid, "EE") // fr24 airspaces use this prefix
		icaoid = strings.TrimPrefix(icaoid, "FF") // fa airspaces use this prefix

		a := Aircraft{
			Dist: pos.DistKM(tp.Latlong),
			Dist3: pos.Dist3(tp.Latlong, altitudeDelta),
			BearingFromObserver: pos.BearingTowards(tp.Latlong),
			//Id: "someid", // This is set via an IdSpec string below
			Id2: icaoid,
			// Squawk string
			EquipType: ad.Airframe.EquipmentType,
			Registration: ad.Airframe.Registration,
			////Epoch: float64(tp.TimestampUTC.Unix()),
			Origin: ad.Schedule.Origin,
			Destination: ad.Schedule.Destination,
			FlightNumber: ad.Schedule.IataFlight(),
			// Unknown float64
			Callsign: ad.Msg.Callsign,
			// Unknown2 float64
		}

		a.FromTrackpoint(tp) // Populate basic fields from the trackpoint

		// Even though we may be parsing an fr24 airspace, generate a (perhaps invalid) skypi idspec
		if icaoid != "" {
			a.Id = fdb.IdSpec{ IcaoId:icaoid, Time:tp.TimestampUTC }.String()
		}

		// Hack for Surf Air; promote their callsigns into flightnumbers.
		// The goal is to allow them to get past the filter, and be printable etc.
		// There is probably a much better place for this kind of munging. But it needs to happen
		//  regardless of the source of the airspace, so it can't be pushed upstream. (...?)
		if a.FlightNumber == "" && regexp.MustCompile("^URF\\d+$").MatchString(a.Callsign) {
			a.FlightNumber = a.Callsign
		}

		ret = append(ret, a)
	}

	sort.Sort(AircraftByDist3(ret))

	return ret
}

// }}}
// {{{ AircraftToString

func AircraftToString(list []Aircraft) string {
	header := "3Dist  2Dist  Brng   Hdng    Alt      Speed VertSpd  Equp Flight   Orig:Dest IcaoID "+
		"Callsign Source  Age\n"

	lines := []string{}

	for _,a := range list {
		line := fmt.Sprintf(
			"%4.1fKM %4.1fKM %3.0fdeg %3.0fdeg %6.0fft %4.0fkt %5.0ffpm "+
			"%4.4s %-8.8s %4.4s:%-4.4s %6.6s %-8.8s %-7.7s "+
			"%2.0fs\n",
			a.Dist3, a.Dist, a.BearingFromObserver, a.Track, a.Altitude, a.Speed, a.VerticalSpeed,
			a.EquipType, a.FlightNumber,
			a.Origin, a.Destination, a.Id2, a.Callsign, a.Radar,
			time.Since(time.Unix(int64(a.Epoch),0)).Seconds())

		lines = append(lines, line)
	}

	//sort.Strings(lines)

	return header + strings.Join(lines, "")
}

// }}}
// {{{ FilterAircraft

func FilterAircraft(in []Aircraft) []Aircraft {
	out := []Aircraft{}

	for _,a := range in {
		age := time.Since(time.Unix(int64(a.Epoch),0))

		if age > 30 * time.Second { continue } // Data too old to use
		if a.FlightNumber == "" { continue }   // Drop unscheduled traffic; poor way of skipping GA
		if a.Altitude > 28000 { continue }     // Too high to be the problem
		if a.Altitude <   750 { continue }     // Too low to be the problem

		out = append(out, a)
	}

	return out
}

// }}}
// {{{ TimeSyncAircraft

// datapoints have ages from 3 to 30s. We sync them to a target age, doing some linear extrapolation
// to adjust the position and altitude. The idea is that they're a more accurate set of data to
// pick a culprit from.

func TimeSyncAircraft(in []Aircraft, pos geo.Latlong, elev float64, targetAge time.Duration) []Aircraft {
	out := []Aircraft{}

	for i,_ := range in {
		new := in[i] // copy it over, then move the new one

		actualAge := time.Since(time.Unix(int64(new.Epoch),0))
		timeToWind := actualAge - targetAge

		newTP := new.Trackpoint().RepositionByTime(timeToWind)
		new.FromTrackpoint(newTP)

		new.Dist = pos.DistKM(new.Latlong())
		new.Dist3 = pos.Dist3(new.Latlong(), new.Altitude-elev)
		new.BearingFromObserver = pos.BearingTowards(new.Latlong())

		out = append(out, new)
	}

	// Resort the array
	sort.Sort(AircraftByDist3(out))

	return out
}

// }}}
// {{{ IdentifyOverhead

func IdentifyOverhead(as *airspace.Airspace, pos geo.Latlong, elev float64, algo Selector) (*Aircraft, string) {
	if as == nil { return nil, "** airspace was nil\n" }

	nearby := AirspaceToLocalizedAircraft(as, pos, elev)
	filtered := FilterAircraft(nearby)

	if true {
		targetAge := 6 * time.Second
		filtered = TimeSyncAircraft(filtered, pos, elev, targetAge)
	}

	ret,outcome := algo.Identify(pos,elev,filtered)

	str := fmt.Sprintf("**** identification method: %s\n**** outcome: %s\n\n", algo, outcome)
	str += fmt.Sprintf("** Processed [%d] **\n%s\n", len(filtered), AircraftToString(filtered))
	str += fmt.Sprintf("** Raw [%d] **\n%s\n", len(nearby), AircraftToString(nearby))

	return ret,str
}

// }}}

// Prob 1: URF on skypi. fr24 rewrites callsign into 'schedule number'; but the raw MLAT data
//  has the registration as the callsign. So fr24 know how to map registration into 'schedule'.
// Even more annoying, fr24 doesn't have IcaoID at this time, because it's on near-time FAA.
//  fr24:  ["bc96f5f","",36.8055,-122.1902,162,15700,271,"0000","T-F5M","PC12","",1480729635,"SQL","SBA","",0,1088,"URF133",0]
//  skypi: http://fdb.serfr1.org/fdb/tracks?idspec=AC06C3@1480729827
// BUT BUT BUT ... the schedcache may save us, as it maps icaoids to flightnumbers; and with
//  the fr24/URF hack, we'll now have those flightnumbers. Maybe !

// The datatypes in this file can be confusing. The input:
//
//   pi/airspace.Airspace         : the input (from skypi, or fr24, or anything else)
//     pi/airspace.AircraftData   : an ADSB message-based model of a flight at a point in time
//       fdb/schedule.Schedule    : flightnumber, origin.dest, etc. If we have it.
//       fdb/airframe.Airframe    : equipment type, registration, keyed from IcaoId. If we have it.
//
// And the output, only used within the complaints system:
//
//   complaints/flightid.Aircraft : a flattened subset of the above, localized to the user

/* New algorithm ...

Inputs:
 list of flights, each with a position, groundspeed, altitude, and timestamp.
 user, also with a position, elevation, and timestamp (of button press)

0. [Optional fudge]: apply a handicap delay to the user (assume they're late pressing the button)

1. Forward-extrapolate aircraft's position, based on (user.tstamp - flight.tstamp) * flight.velocity
This is approximate; assumes level flight, and no acceleration.

2. Compute the time delay for sound propagation, based on 3D distance
This is approximate; speed of sound will vary with atmosphere.

3. Back-extrapolate the position of the aircraft at the time the button was pressed.

4. Compute distance between aircraft and user.

5. Sort aircraft by this distance.

A jet at 10,000', and 4KM lateral, is ~5KM away.
A cessna at 2,000', and 4KM lateral, is <5KM away, and would be closest.

 */

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

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

// }}}
<|endoftext|>


<commit_before>package instagram

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/markbates/goth"
	"golang.org/x/oauth2"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

var (
	authURL         = "https://api.instagram.com/oauth/authorize/"
	tokenURL        = "https://api.instagram.com/oauth/access_token"
	endPointProfile = "https://api.instagram.com/v1/users/self/"
)

// New creates a new Instagram provider, and sets up important connection details.
// You should always call `instagram.New` to get a new Provider. Never try to craete
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
	p := &Provider{
		ClientKey:   clientKey,
		Secret:      secret,
		CallbackURL: callbackURL,
	}
	p.config = newConfig(p, scopes)
	return p
}

// Provider is the implementation of `goth.Provider` for accessing Instagram
type Provider struct {
	ClientKey   string
	Secret      string
	CallbackURL string
	UserAgent   string
	config      *oauth2.Config
}

// Name is the name used to retrive this provider later.
func (p *Provider) Name() string {
	return "instagram"
}

//Debug TODO
func (p *Provider) Debug(debug bool) {}

// BeginAuth asks Instagram for an authentication end-point.
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
	url := p.config.AuthCodeURL(state)
	session := &Session{
		AuthURL: url,
	}
	return session, nil
}

// FetchUser will go to Instagram and access basic information about the user.
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
	sess := session.(*Session)
	user := goth.User{
		AccessToken: sess.AccessToken,
		Provider:    p.Name(),
	}

	response, err := http.Get(endPointProfile + "?access_token=" + url.QueryEscape(sess.AccessToken))
	if err != nil {
		return user, err
	}
	defer response.Body.Close()

	bits, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return user, err
	}
	err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
	if err != nil {
		return user, err
	}
	err = userFromReader(bytes.NewReader(bits), &user)
	return user, err
}

func userFromReader(reader io.Reader, user *goth.User) error {
	u := struct {
		Data struct {
			ID             string `json:"id"`
			UserName       string `json:"username"`
			FullName       string `json:"full_name"`
			ProfilePicture string `json:"profile_picture"`
			Bio            string `json:"bio"`
			Website        string `json:"website"`
			Counts         struct {
				Media      int `json:"media"`
				Follows    int `json:"follows"`
				FollowedBy int `json:"followed_by"`
			} `json:"counts"`
		} `json:"data"`
	}{}
	err := json.NewDecoder(reader).Decode(&u)
	if err != nil {
		return err
	}
	user.Name = u.Data.UserName
	user.NickName = u.Data.UserName
	user.AvatarURL = u.Data.ProfilePicture
	user.Description = u.Data.Bio
	return err
}

func newConfig(p *Provider, scopes []string) *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     p.ClientKey,
		ClientSecret: p.Secret,
		RedirectURL:  p.CallbackURL,
		Endpoint: oauth2.Endpoint{
			AuthURL:  authURL,
			TokenURL: tokenURL,
		},
		Scopes: []string{
			"basic",
		},
	}
	defaultScopes := map[string]struct{}{
		"basic": {},
	}

	for _, scope := range scopes {
		if _, exists := defaultScopes[scope]; !exists {
			c.Scopes = append(c.Scopes, scope)
		}
	}

	return c
}

//RefreshToken refresh token is not provided by instagram
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
	return nil, errors.New("Refresh token is not provided by instagram")
}

//RefreshTokenAvailable refresh token is not provided by instagram
func (p *Provider) RefreshTokenAvailable() bool {
	return false
}
<commit_msg>Add package docs for the Instagram provider (minor change)<commit_after>// Package instagram implements the OAuth2 protocol for authenticating users through Instagram.
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
package instagram

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/markbates/goth"
	"golang.org/x/oauth2"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

var (
	authURL         = "https://api.instagram.com/oauth/authorize/"
	tokenURL        = "https://api.instagram.com/oauth/access_token"
	endPointProfile = "https://api.instagram.com/v1/users/self/"
)

// New creates a new Instagram provider, and sets up important connection details.
// You should always call `instagram.New` to get a new Provider. Never try to craete
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
	p := &Provider{
		ClientKey:   clientKey,
		Secret:      secret,
		CallbackURL: callbackURL,
	}
	p.config = newConfig(p, scopes)
	return p
}

// Provider is the implementation of `goth.Provider` for accessing Instagram
type Provider struct {
	ClientKey   string
	Secret      string
	CallbackURL string
	UserAgent   string
	config      *oauth2.Config
}

// Name is the name used to retrive this provider later.
func (p *Provider) Name() string {
	return "instagram"
}

//Debug TODO
func (p *Provider) Debug(debug bool) {}

// BeginAuth asks Instagram for an authentication end-point.
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
	url := p.config.AuthCodeURL(state)
	session := &Session{
		AuthURL: url,
	}
	return session, nil
}

// FetchUser will go to Instagram and access basic information about the user.
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
	sess := session.(*Session)
	user := goth.User{
		AccessToken: sess.AccessToken,
		Provider:    p.Name(),
	}

	response, err := http.Get(endPointProfile + "?access_token=" + url.QueryEscape(sess.AccessToken))
	if err != nil {
		return user, err
	}
	defer response.Body.Close()

	bits, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return user, err
	}
	err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
	if err != nil {
		return user, err
	}
	err = userFromReader(bytes.NewReader(bits), &user)
	return user, err
}

func userFromReader(reader io.Reader, user *goth.User) error {
	u := struct {
		Data struct {
			ID             string `json:"id"`
			UserName       string `json:"username"`
			FullName       string `json:"full_name"`
			ProfilePicture string `json:"profile_picture"`
			Bio            string `json:"bio"`
			Website        string `json:"website"`
			Counts         struct {
				Media      int `json:"media"`
				Follows    int `json:"follows"`
				FollowedBy int `json:"followed_by"`
			} `json:"counts"`
		} `json:"data"`
	}{}
	err := json.NewDecoder(reader).Decode(&u)
	if err != nil {
		return err
	}
	user.Name = u.Data.UserName
	user.NickName = u.Data.UserName
	user.AvatarURL = u.Data.ProfilePicture
	user.Description = u.Data.Bio
	return err
}

func newConfig(p *Provider, scopes []string) *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     p.ClientKey,
		ClientSecret: p.Secret,
		RedirectURL:  p.CallbackURL,
		Endpoint: oauth2.Endpoint{
			AuthURL:  authURL,
			TokenURL: tokenURL,
		},
		Scopes: []string{
			"basic",
		},
	}
	defaultScopes := map[string]struct{}{
		"basic": {},
	}

	for _, scope := range scopes {
		if _, exists := defaultScopes[scope]; !exists {
			c.Scopes = append(c.Scopes, scope)
		}
	}

	return c
}

//RefreshToken refresh token is not provided by instagram
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
	return nil, errors.New("Refresh token is not provided by instagram")
}

//RefreshTokenAvailable refresh token is not provided by instagram
func (p *Provider) RefreshTokenAvailable() bool {
	return false
}
<|endoftext|>


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

// Package httpresponse defines an Analyzer that checks for mistakes
// using HTTP responses.
package httpresponse

import (
	"go/ast"
	"go/types"

	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/analysis/passes/inspect"
	"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
	"golang.org/x/tools/go/ast/inspector"
)

const Doc = `check for mistakes using HTTP responses

A common mistake when using the net/http package is to defer a function
call to close the http.Response Body before checking the error that
determines whether the response is valid:

	resp, err := http.Head(url)
	defer resp.Body.Close()
	if err != nil {
		log.Fatal(err)
	}
	// (defer statement belongs here)

This checker helps uncover latent nil dereference bugs by reporting a
diagnostic for such mistakes.`

var Analyzer = &analysis.Analyzer{
	Name:     "httpresponse",
	Doc:      Doc,
	Requires: []*analysis.Analyzer{inspect.Analyzer},
	Run:      run,
}

func run(pass *analysis.Pass) (interface{}, error) {
	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)

	// Fast path: if the package doesn't import net/http,
	// skip the traversal.
	if !analysisutil.Imports(pass.Pkg, "net/http") {
		return nil, nil
	}

	nodeFilter := []ast.Node{
		(*ast.CallExpr)(nil),
	}
	inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool {
		if !push {
			return true
		}
		call := n.(*ast.CallExpr)
		if !isHTTPFuncOrMethodOnClient(pass.TypesInfo, call) {
			return true // the function call is not related to this check.
		}

		// Find the innermost containing block, and get the list
		// of statements starting with the one containing call.
		stmts, withinAnotherCall := restOfBlock(stack)
		if withinAnotherCall {
			// We skip cases when the results of a call to http member
			// are passed directly to another call, as that later call
			// could check err != nil and create false positives (#52661).
			return true
		}
		if len(stmts) < 2 {
			return true // the call to the http function is the last statement of the block.
		}

		asg, ok := stmts[0].(*ast.AssignStmt)
		if !ok {
			return true // the first statement is not assignment.
		}

		resp := rootIdent(asg.Lhs[0])
		if resp == nil {
			return true // could not find the http.Response in the assignment.
		}

		def, ok := stmts[1].(*ast.DeferStmt)
		if !ok {
			return true // the following statement is not a defer.
		}
		root := rootIdent(def.Call.Fun)
		if root == nil {
			return true // could not find the receiver of the defer call.
		}

		if resp.Obj == root.Obj {
			pass.ReportRangef(root, "using %s before checking for errors", resp.Name)
		}
		return true
	})
	return nil, nil
}

// isHTTPFuncOrMethodOnClient checks whether the given call expression is on
// either a function of the net/http package or a method of http.Client that
// returns (*http.Response, error).
func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool {
	fun, _ := expr.Fun.(*ast.SelectorExpr)
	sig, _ := info.Types[fun].Type.(*types.Signature)
	if sig == nil {
		return false // the call is not of the form x.f()
	}

	res := sig.Results()
	if res.Len() != 2 {
		return false // the function called does not return two values.
	}
	if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !isNamedType(ptr.Elem(), "net/http", "Response") {
		return false // the first return type is not *http.Response.
	}

	errorType := types.Universe.Lookup("error").Type()
	if !types.Identical(res.At(1).Type(), errorType) {
		return false // the second return type is not error
	}

	typ := info.Types[fun.X].Type
	if typ == nil {
		id, ok := fun.X.(*ast.Ident)
		return ok && id.Name == "http" // function in net/http package.
	}

	if isNamedType(typ, "net/http", "Client") {
		return true // method on http.Client.
	}
	ptr, ok := typ.(*types.Pointer)
	return ok && isNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client.
}

// restOfBlock, given a traversal stack, checks if the current node
// (the last element of stack) appears as an argument to another call.
// If not, it finds the innermost containing block and returns the
// suffix of its statements starting with the current node. Otherwise,
// returns an empty slice.
func restOfBlock(stack []ast.Node) ([]ast.Stmt, bool) {
	for i := len(stack) - 1; i >= 0; i-- {
		// If the current node appears within another call, then
		// this has to happen within the same block. We can thus
		// immediately return on whichever we see first, a block
		// statement or a call statement.

		if b, ok := stack[i].(*ast.BlockStmt); ok {
			for j, v := range b.List {
				if v == stack[i+1] {
					return b.List[j:], false
				}
			}
			break
		}

		// The call to an http member currently analyzed is at len(stack)-1.
		if _, ok := stack[i].(*ast.CallExpr); ok && i != len(stack)-1 {
			return nil, true // e.g. "resp, err := wrap(http.Get(...))"
		}

	}
	return nil, false
}

// rootIdent finds the root identifier x in a chain of selections x.y.z, or nil if not found.
func rootIdent(n ast.Node) *ast.Ident {
	switch n := n.(type) {
	case *ast.SelectorExpr:
		return rootIdent(n.X)
	case *ast.Ident:
		return n
	default:
		return nil
	}
}

// isNamedType reports whether t is the named type path.name.
func isNamedType(t types.Type, path, name string) bool {
	n, ok := t.(*types.Named)
	if !ok {
		return false
	}
	obj := n.Obj()
	return obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == path
}
<commit_msg>go/analysis/passes/httpresponse: minor clarification<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package httpresponse defines an Analyzer that checks for mistakes
// using HTTP responses.
package httpresponse

import (
	"go/ast"
	"go/types"

	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/analysis/passes/inspect"
	"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
	"golang.org/x/tools/go/ast/inspector"
)

const Doc = `check for mistakes using HTTP responses

A common mistake when using the net/http package is to defer a function
call to close the http.Response Body before checking the error that
determines whether the response is valid:

	resp, err := http.Head(url)
	defer resp.Body.Close()
	if err != nil {
		log.Fatal(err)
	}
	// (defer statement belongs here)

This checker helps uncover latent nil dereference bugs by reporting a
diagnostic for such mistakes.`

var Analyzer = &analysis.Analyzer{
	Name:     "httpresponse",
	Doc:      Doc,
	Requires: []*analysis.Analyzer{inspect.Analyzer},
	Run:      run,
}

func run(pass *analysis.Pass) (interface{}, error) {
	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)

	// Fast path: if the package doesn't import net/http,
	// skip the traversal.
	if !analysisutil.Imports(pass.Pkg, "net/http") {
		return nil, nil
	}

	nodeFilter := []ast.Node{
		(*ast.CallExpr)(nil),
	}
	inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool {
		if !push {
			return true
		}
		call := n.(*ast.CallExpr)
		if !isHTTPFuncOrMethodOnClient(pass.TypesInfo, call) {
			return true // the function call is not related to this check.
		}

		// Find the innermost containing block, and get the list
		// of statements starting with the one containing call.
		stmts, ncalls := restOfBlock(stack)
		if len(stmts) < 2 {
			// The call to the http function is the last statement of the block.
			return true
		}

		// Skip cases in which the call is wrapped by another (#52661).
		// Example:  resp, err := checkError(http.Get(url))
		if ncalls > 1 {
			return true
		}

		asg, ok := stmts[0].(*ast.AssignStmt)
		if !ok {
			return true // the first statement is not assignment.
		}

		resp := rootIdent(asg.Lhs[0])
		if resp == nil {
			return true // could not find the http.Response in the assignment.
		}

		def, ok := stmts[1].(*ast.DeferStmt)
		if !ok {
			return true // the following statement is not a defer.
		}
		root := rootIdent(def.Call.Fun)
		if root == nil {
			return true // could not find the receiver of the defer call.
		}

		if resp.Obj == root.Obj {
			pass.ReportRangef(root, "using %s before checking for errors", resp.Name)
		}
		return true
	})
	return nil, nil
}

// isHTTPFuncOrMethodOnClient checks whether the given call expression is on
// either a function of the net/http package or a method of http.Client that
// returns (*http.Response, error).
func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool {
	fun, _ := expr.Fun.(*ast.SelectorExpr)
	sig, _ := info.Types[fun].Type.(*types.Signature)
	if sig == nil {
		return false // the call is not of the form x.f()
	}

	res := sig.Results()
	if res.Len() != 2 {
		return false // the function called does not return two values.
	}
	if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !isNamedType(ptr.Elem(), "net/http", "Response") {
		return false // the first return type is not *http.Response.
	}

	errorType := types.Universe.Lookup("error").Type()
	if !types.Identical(res.At(1).Type(), errorType) {
		return false // the second return type is not error
	}

	typ := info.Types[fun.X].Type
	if typ == nil {
		id, ok := fun.X.(*ast.Ident)
		return ok && id.Name == "http" // function in net/http package.
	}

	if isNamedType(typ, "net/http", "Client") {
		return true // method on http.Client.
	}
	ptr, ok := typ.(*types.Pointer)
	return ok && isNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client.
}

// restOfBlock, given a traversal stack, finds the innermost containing
// block and returns the suffix of its statements starting with the current
// node, along with the number of call expressions encountered.
func restOfBlock(stack []ast.Node) ([]ast.Stmt, int) {
	var ncalls int
	for i := len(stack) - 1; i >= 0; i-- {
		if b, ok := stack[i].(*ast.BlockStmt); ok {
			for j, v := range b.List {
				if v == stack[i+1] {
					return b.List[j:], ncalls
				}
			}
			break
		}

		if _, ok := stack[i].(*ast.CallExpr); ok {
			ncalls++
		}
	}
	return nil, 0
}

// rootIdent finds the root identifier x in a chain of selections x.y.z, or nil if not found.
func rootIdent(n ast.Node) *ast.Ident {
	switch n := n.(type) {
	case *ast.SelectorExpr:
		return rootIdent(n.X)
	case *ast.Ident:
		return n
	default:
		return nil
	}
}

// isNamedType reports whether t is the named type path.name.
func isNamedType(t types.Type, path, name string) bool {
	n, ok := t.(*types.Named)
	if !ok {
		return false
	}
	obj := n.Obj()
	return obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == path
}
<|endoftext|>


<commit_before>// Package terminal provides a tty emulation and session handlings that is
// supported via Screen
package terminal

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"koding/tools/pty"
	"os/exec"
	"os/user"
	"syscall"
	"time"
	"unicode/utf8"

	"github.com/koding/kite"
	"github.com/koding/kite/dnode"
)

const randomStringLength = 24 // 144 bit base64 encoded

// Server is the type of object that is sent to the connected client.
// Represents a running shell process on the server.
type Server struct {
	Session          string `json:"session"`
	remote           Remote
	pty              *pty.PTY
	currentSecond    int64
	messageCounter   int
	byteCounter      int
	lineFeeedCounter int
}

type Remote struct {
	Output       dnode.Function
	SessionEnded dnode.Function
}

func KillSession(r *kite.Request) (interface{}, error) {
	var params struct {
		Session string
	}

	if r.Args.One().Unmarshal(&params) != nil {
		return nil, errors.New("{ session: [string] }")
	}

	if params.Session == "" {
		return nil, errors.New("session is empty")
	}

	if err := killSession(params.Session); err != nil {
		return nil, err
	}

	return true, nil
}

func GetSessions(r *kite.Request) (interface{}, error) {
	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	sessions := screenSessions(user.Username)
	if len(sessions) == 0 {
		return nil, errors.New("no sessions available")
	}

	return sessions, nil
}

func Connect(r *kite.Request) (interface{}, error) {
	var params struct {
		Remote       Remote
		Session      string
		SizeX, SizeY int
		Mode         string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, fmt.Errorf("{ remote: [object], session: %s, noScreen: [bool] }, err: %s",
			params.Session, err)
	}

	if params.SizeX <= 0 || params.SizeY <= 0 {
		return nil, fmt.Errorf("{ sizeX: %d, sizeY: %d } { raw JSON : %v }", params.SizeX, params.SizeY, r.Args.One())
	}

	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	command, err := newCommand(params.Mode, params.Session, user.Username)
	if err != nil {
		return nil, err
	}

	// We will return this object to the client.
	server := &Server{
		Session: command.Session,
		remote:  params.Remote,
		pty:     pty.New("/dev/pts"),
	}
	server.setSize(float64(params.SizeX), float64(params.SizeY))

	// wrap the command with sudo -i for initiation login shell. This is needed
	// in order to have Environments and other to be initialized correctly.
	args := []string{"-i", command.Name}
	args = append(args, command.Args...)
	cmd := exec.Command("/usr/bin/sudo", args...)

	// For test use this, sude is not going to work
	// cmd := exec.Command(command.Name, command.Args...)

	cmd.Env = []string{"TERM=xterm-256color", "HOME=" + user.HomeDir}
	cmd.Stdin = server.pty.Slave
	cmd.Stdout = server.pty.Slave
	cmd.Dir = user.HomeDir
	// cmd.Stderr = server.pty.Slave

	// Open in background, this is needed otherwise the process will be killed
	// if you hit close on the client side.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
	err = cmd.Start()
	if err != nil {
		fmt.Println("could not start", err)
	}

	// Wait until the shell process is closed and notify the client.
	go func() {
		err := cmd.Wait()
		if err != nil {
			fmt.Println("cmd.wait err", err)
		}

		server.pty.Slave.Close()
		server.pty.Master.Close()
		server.remote.SessionEnded.Call()
	}()

	// Read the STDOUT from shell process and send to the connected client.
	go func() {
		buf := make([]byte, (1<<12)-utf8.UTFMax, 1<<12)
		for {
			n, err := server.pty.Master.Read(buf)
			for n < cap(buf)-1 {
				r, _ := utf8.DecodeLastRune(buf[:n])
				if r != utf8.RuneError {
					break
				}
				server.pty.Master.Read(buf[n : n+1])
				n++
			}

			// Rate limiting...
			s := time.Now().Unix()
			if server.currentSecond != s {
				server.currentSecond = s
				server.messageCounter = 0
				server.byteCounter = 0
				server.lineFeeedCounter = 0
			}
			server.messageCounter += 1
			server.byteCounter += n
			server.lineFeeedCounter += bytes.Count(buf[:n], []byte{'\n'})
			if server.messageCounter > 100 || server.byteCounter > 1<<18 || server.lineFeeedCounter > 300 {
				time.Sleep(time.Second)
			}

			server.remote.Output.Call(string(filterInvalidUTF8(buf[:n])))
			if err != nil {
				break
			}
		}
	}()

	return server, nil
}

// Input is called when some text is written to the terminal.
func (s *Server) Input(d *dnode.Partial) {
	data := d.MustSliceOfLength(1)[0].MustString()

	// There is no need to protect the Write() with a mutex because
	// Kite Library guarantees that only one message is processed at a time.
	s.pty.Master.Write([]byte(data))
}

// ControlSequence is called when a non-printable key is pressed on the terminal.
func (s *Server) ControlSequence(d *dnode.Partial) {
	data := d.MustSliceOfLength(1)[0].MustString()
	s.pty.MasterEncoded.Write([]byte(data))
}

func (s *Server) SetSize(d *dnode.Partial) {
	args := d.MustSliceOfLength(2)
	x := args[0].MustFloat64()
	y := args[1].MustFloat64()
	s.setSize(x, y)
}

func (s *Server) setSize(x, y float64) {
	s.pty.SetSize(uint16(x), uint16(y))
}

func (s *Server) Close(d *dnode.Partial) {
	s.pty.Signal(syscall.SIGHUP)
}

func (s *Server) Terminate(d *dnode.Partial) {
	s.Close(nil)
}

func filterInvalidUTF8(buf []byte) []byte {
	i := 0
	j := 0
	for {
		r, l := utf8.DecodeRune(buf[i:])
		if l == 0 {
			break
		}
		if r < 0xD800 {
			if i != j {
				copy(buf[j:], buf[i:i+l])
			}
			j += l
		}
		i += l
	}
	return buf[:j]
}

func randomString() string {
	r := make([]byte, randomStringLength*6/8)
	rand.Read(r)
	return base64.URLEncoding.EncodeToString(r)
}
<commit_msg>terminal: add throttling mode<commit_after>// Package terminal provides a tty emulation and session handlings that is
// supported via Screen
package terminal

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"koding/tools/pty"
	"os/exec"
	"os/user"
	"syscall"
	"time"
	"unicode/utf8"

	"github.com/koding/kite"
	"github.com/koding/kite/dnode"
)

const randomStringLength = 24 // 144 bit base64 encoded

// Server is the type of object that is sent to the connected client.
// Represents a running shell process on the server.
type Server struct {
	Session          string `json:"session"`
	remote           Remote
	pty              *pty.PTY
	currentSecond    int64
	messageCounter   int
	byteCounter      int
	lineFeeedCounter int
	throttling       bool
}

type Remote struct {
	Output       dnode.Function
	SessionEnded dnode.Function
}

func KillSession(r *kite.Request) (interface{}, error) {
	var params struct {
		Session string
	}

	if r.Args.One().Unmarshal(&params) != nil {
		return nil, errors.New("{ session: [string] }")
	}

	if params.Session == "" {
		return nil, errors.New("session is empty")
	}

	if err := killSession(params.Session); err != nil {
		return nil, err
	}

	return true, nil
}

func GetSessions(r *kite.Request) (interface{}, error) {
	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	sessions := screenSessions(user.Username)
	if len(sessions) == 0 {
		return nil, errors.New("no sessions available")
	}

	return sessions, nil
}

func Connect(r *kite.Request) (interface{}, error) {
	var params struct {
		Remote       Remote
		Session      string
		SizeX, SizeY int
		Mode         string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, fmt.Errorf("{ remote: [object], session: %s, noScreen: [bool] }, err: %s",
			params.Session, err)
	}

	if params.SizeX <= 0 || params.SizeY <= 0 {
		return nil, fmt.Errorf("{ sizeX: %d, sizeY: %d } { raw JSON : %v }", params.SizeX, params.SizeY, r.Args.One())
	}

	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	command, err := newCommand(params.Mode, params.Session, user.Username)
	if err != nil {
		return nil, err
	}

	// We will return this object to the client.
	server := &Server{
		Session: command.Session,
		remote:  params.Remote,
		pty:     pty.New("/dev/pts"),
	}
	server.setSize(float64(params.SizeX), float64(params.SizeY))

	// wrap the command with sudo -i for initiation login shell. This is needed
	// in order to have Environments and other to be initialized correctly.
	args := []string{"-i", command.Name}
	args = append(args, command.Args...)
	cmd := exec.Command("/usr/bin/sudo", args...)

	// For test use this, sudo is not going to work
	// cmd := exec.Command(command.Name, command.Args...)

	cmd.Env = []string{"TERM=xterm-256color", "HOME=" + user.HomeDir}
	cmd.Stdin = server.pty.Slave
	cmd.Stdout = server.pty.Slave
	cmd.Dir = user.HomeDir
	// cmd.Stderr = server.pty.Slave

	// Open in background, this is needed otherwise the process will be killed
	// if you hit close on the client side.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
	err = cmd.Start()
	if err != nil {
		fmt.Println("could not start", err)
	}

	// Wait until the shell process is closed and notify the client.
	go func() {
		err := cmd.Wait()
		if err != nil {
			fmt.Println("cmd.wait err", err)
		}

		server.pty.Slave.Close()
		server.pty.Master.Close()
		server.remote.SessionEnded.Call()
	}()

	// Read the STDOUT from shell process and send to the connected client.
	go func() {
		buf := make([]byte, (1<<12)-utf8.UTFMax, 1<<12)
		for {
			n, err := server.pty.Master.Read(buf)
			for n < cap(buf)-1 {
				r, _ := utf8.DecodeLastRune(buf[:n])
				if r != utf8.RuneError {
					break
				}
				server.pty.Master.Read(buf[n : n+1])
				n++
			}

			// Rate limiting...
			if server.throttling {
				s := time.Now().Unix()
				if server.currentSecond != s {
					server.currentSecond = s
					server.messageCounter = 0
					server.byteCounter = 0
					server.lineFeeedCounter = 0
				}
				server.messageCounter += 1
				server.byteCounter += n
				server.lineFeeedCounter += bytes.Count(buf[:n], []byte{'\n'})
				if server.messageCounter > 100 || server.byteCounter > 1<<18 || server.lineFeeedCounter > 300 {
					time.Sleep(time.Second)
				}
			}

			server.remote.Output.Call(string(filterInvalidUTF8(buf[:n])))
			if err != nil {
				break
			}
		}
	}()

	return server, nil
}

// Input is called when some text is written to the terminal.
func (s *Server) Input(d *dnode.Partial) {
	data := d.MustSliceOfLength(1)[0].MustString()

	// There is no need to protect the Write() with a mutex because
	// Kite Library guarantees that only one message is processed at a time.
	s.pty.Master.Write([]byte(data))
}

// ControlSequence is called when a non-printable key is pressed on the terminal.
func (s *Server) ControlSequence(d *dnode.Partial) {
	data := d.MustSliceOfLength(1)[0].MustString()
	s.pty.MasterEncoded.Write([]byte(data))
}

func (s *Server) SetSize(d *dnode.Partial) {
	args := d.MustSliceOfLength(2)
	x := args[0].MustFloat64()
	y := args[1].MustFloat64()
	s.setSize(x, y)
}

func (s *Server) setSize(x, y float64) {
	s.pty.SetSize(uint16(x), uint16(y))
}

func (s *Server) Close(d *dnode.Partial) {
	s.pty.Signal(syscall.SIGHUP)
}

func (s *Server) Terminate(d *dnode.Partial) {
	s.Close(nil)
}

func filterInvalidUTF8(buf []byte) []byte {
	i := 0
	j := 0
	for {
		r, l := utf8.DecodeRune(buf[i:])
		if l == 0 {
			break
		}
		if r < 0xD800 {
			if i != j {
				copy(buf[j:], buf[i:i+l])
			}
			j += l
		}
		i += l
	}
	return buf[:j]
}

func randomString() string {
	r := make([]byte, randomStringLength*6/8)
	rand.Read(r)
	return base64.URLEncoding.EncodeToString(r)
}
<|endoftext|>


<commit_before>package stripe

import (
	"socialapi/workers/payment/paymenterrors"
	"socialapi/workers/payment/paymentmodels"

	stripe "github.com/stripe/stripe-go"
	stripeCard "github.com/stripe/stripe-go/card"
	stripeCustomer "github.com/stripe/stripe-go/customer"
)

type CreditCardResponse struct {
	LastFour string `json:"last4"`
	Month    uint8  `json:"month"`
	Year     uint16 `json:"year"`
	Name     string `json:"name"`
}

func GetCreditCard(oldId string) (*CreditCardResponse, error) {
	customer, err := FindCustomerByOldId(oldId)
	if err != nil {
		return nil, err
	}

	externalCustomer, err := GetCustomer(customer.ProviderCustomerId)
	if err != nil {
		return nil, err
	}

	creditCardList := externalCustomer.Cards
	if IsNoCreditCards(creditCardList) {
		return &CreditCardResponse{}, nil
	}

	if IsTooManyCreditCards(creditCardList) {
		//TODO: how to handle too many ccs?
	}

	creditCard := creditCardList.Values[0]
	creditCardResponse := &CreditCardResponse{
		LastFour: creditCard.LastFour,
		Month:    creditCard.Month,
		Year:     creditCard.Year,
		Name:     creditCard.Name,
	}

	return creditCardResponse, nil
}

func UpdateCreditCard(oldId, token string) error {
	if IsEmpty(token) {
		return paymenterrors.ErrTokenIsEmpty
	}

	customer, err := FindCustomerByOldId(oldId)
	if err != nil {
		return err
	}

	customerParams := &stripe.CustomerParams{Token: token}

	_, err = stripeCustomer.Update(customer.ProviderCustomerId, customerParams)
	if err != nil {
		return handleStripeError(err)
	}

	return nil
}

func RemoveCreditCard(customer *paymentmodel.Customer) error {
	externalCustomer, err := GetCustomer(customer.ProviderCustomerId)
	if err != nil {
		return err
	}

	creditCardList := externalCustomer.Cards
	if IsNoCreditCards(creditCardList) {
		return paymenterrors.ErrNoCreditCard
	}

	if IsTooManyCreditCards(creditCardList) {
		Log.Error(
			"Customer (stripe): %s has too many: %s credit cards.",
			customer.ProviderCustomerId, creditCardList.Count,
		)
	}

	creditCard := creditCardList.Values[0]

	creditCardParams := &stripe.CardParams{
		Customer: externalCustomer.Id,
	}
	err = stripeCard.Del(creditCard.Id, creditCardParams)
	if err != nil {
		return err
	}

	return nil
}

func UpdateCreditCardIfEmpty(accId, token string) error {
	ccResp, err := GetCreditCard(accId)
	if err != nil {
		return err
	}

	if IsCreditCardEmpty(ccResp) {
		err := UpdateCreditCard(accId, token)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>payment: log when customer has too many ccs in all places<commit_after>package stripe

import (
	"socialapi/workers/payment/paymenterrors"
	"socialapi/workers/payment/paymentmodels"

	stripe "github.com/stripe/stripe-go"
	stripeCard "github.com/stripe/stripe-go/card"
	stripeCustomer "github.com/stripe/stripe-go/customer"
)

type CreditCardResponse struct {
	LastFour string `json:"last4"`
	Month    uint8  `json:"month"`
	Year     uint16 `json:"year"`
	Name     string `json:"name"`
}

func GetCreditCard(oldId string) (*CreditCardResponse, error) {
	customer, err := FindCustomerByOldId(oldId)
	if err != nil {
		return nil, err
	}

	externalCustomer, err := GetCustomer(customer.ProviderCustomerId)
	if err != nil {
		return nil, err
	}

	creditCardList := externalCustomer.Cards
	if IsNoCreditCards(creditCardList) {
		return &CreditCardResponse{}, nil
	}

	if IsTooManyCreditCards(creditCardList) {
		Log.Error(
			"Customer (stripe): %s has too many: %s credit cards.",
			customer.ProviderCustomerId, creditCardList.Count,
		)
	}

	creditCard := creditCardList.Values[0]
	creditCardResponse := &CreditCardResponse{
		LastFour: creditCard.LastFour,
		Month:    creditCard.Month,
		Year:     creditCard.Year,
		Name:     creditCard.Name,
	}

	return creditCardResponse, nil
}

func UpdateCreditCard(oldId, token string) error {
	if IsEmpty(token) {
		return paymenterrors.ErrTokenIsEmpty
	}

	customer, err := FindCustomerByOldId(oldId)
	if err != nil {
		return err
	}

	customerParams := &stripe.CustomerParams{Token: token}

	_, err = stripeCustomer.Update(customer.ProviderCustomerId, customerParams)
	if err != nil {
		return handleStripeError(err)
	}

	return nil
}

func RemoveCreditCard(customer *paymentmodel.Customer) error {
	externalCustomer, err := GetCustomer(customer.ProviderCustomerId)
	if err != nil {
		return err
	}

	creditCardList := externalCustomer.Cards
	if IsNoCreditCards(creditCardList) {
		return paymenterrors.ErrNoCreditCard
	}

	if IsTooManyCreditCards(creditCardList) {
		Log.Error(
			"Customer (stripe): %s has too many: %s credit cards.",
			customer.ProviderCustomerId, creditCardList.Count,
		)
	}

	creditCard := creditCardList.Values[0]

	creditCardParams := &stripe.CardParams{
		Customer: externalCustomer.Id,
	}
	err = stripeCard.Del(creditCard.Id, creditCardParams)
	if err != nil {
		return err
	}

	return nil
}

func UpdateCreditCardIfEmpty(accId, token string) error {
	ccResp, err := GetCreditCard(accId)
	if err != nil {
		return err
	}

	if IsCreditCardEmpty(ccResp) {
		err := UpdateCreditCard(accId, token)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package datastore

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

	"github.com/pkg/errors"
	"go.uber.org/zap/zapcore"

	"github.com/swagchat/chat-api/logging"
	"github.com/swagchat/chat-api/models"
	"github.com/swagchat/chat-api/utils"
)

func rdbCreateMessageStore(db string) {
	master := RdbStore(db).master()

	tableMap := master.AddTableWithName(models.Message{}, tableNameMessage)
	tableMap.SetKeys(true, "id")
	for _, columnMap := range tableMap.Columns {
		if columnMap.ColumnName == "message_id" {
			columnMap.SetUnique(true)
		}
	}
	err := master.CreateTablesIfNotExists()
	if err != nil {
		logging.Log(zapcore.FatalLevel, &logging.AppLog{
			Message: "Create message table error",
			Error:   err,
		})
	}

	var addIndexQuery string
	if utils.Config().Datastore.Provider == "sqlite" {
		addIndexQuery = utils.AppendStrings("CREATE INDEX room_id_deleted_created ON ", tableNameMessage, "(room_id, deleted, created)")
	} else {
		addIndexQuery = utils.AppendStrings("ALTER TABLE ", tableNameMessage, " ADD INDEX room_id_deleted_created (room_id, deleted, created)")
		_, err = master.Exec(addIndexQuery)
		if err != nil {
			errMessage := err.Error()
			if strings.Index(errMessage, "Duplicate key name") < 0 {
				logging.Log(zapcore.FatalLevel, &logging.AppLog{
					Message: "Duplicate key name",
					Error:   err,
				})
			}
		}
	}
}

func rdbInsertMessage(db string, message *models.Message) (string, error) {
	master := RdbStore(db).master()
	trans, err := master.Begin()
	if err != nil {
		return "", errors.Wrap(err, "An error occurred while transaction beginning")
	}

	err = trans.Insert(message)
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while creating message")
	}

	var rooms []*models.Room
	query := utils.AppendStrings("SELECT * FROM ", tableNameRoom, " WHERE room_id=:roomId AND deleted=0;")
	params := map[string]interface{}{"roomId": message.RoomID}
	if _, err = trans.Select(&rooms, query, params); err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while getting room")
	}
	if len(rooms) != 1 {
		return "", errors.New("An error occurred while getting room. Room count is not 1")
	}

	room := rooms[0]
	var lastMessage string
	switch message.Type {
	case "text":
		var payloadText models.PayloadText
		json.Unmarshal(message.Payload, &payloadText)
		lastMessage = payloadText.Text
	case "image":
		lastMessage = "画像を受信しました"
	case "buttons":
		var payloadButtons models.PayloadButtons
		json.Unmarshal(message.Payload, &payloadButtons)
		lastMessage = payloadButtons.Title
	case "confirm":
		var payloadConfirm models.PayloadConfirm
		json.Unmarshal(message.Payload, &payloadConfirm)
		lastMessage = payloadConfirm.Text
	default:
		lastMessage = "メッセージを受信しました"
	}
	room.LastMessage = lastMessage
	room.LastMessageUpdated = time.Now().Unix()
	_, err = trans.Update(room)
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while updating room")
	}

	query = utils.AppendStrings("UPDATE ", tableNameRoomUser, " SET unread_count=unread_count+1 WHERE room_id=:roomId AND user_id!=:userId;")
	params = map[string]interface{}{
		"roomId": message.RoomID,
		"userId": message.UserID,
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while updating room's user unread count")
	}

	var users []*models.User
	query = utils.AppendStrings("SELECT u.* ",
		"FROM ", tableNameRoomUser, " AS ru ",
		"LEFT JOIN ", tableNameUser, " AS u ",
		"ON ru.user_id = u.user_id ",
		"WHERE room_id = :roomId;")
	params = map[string]interface{}{"roomId": message.RoomID}
	_, err = trans.Select(&users, query, params)
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while getting room's users")
	}
	for _, user := range users {
		if user.UserID == message.UserID {
			continue
		}
		query := utils.AppendStrings("UPDATE ", tableNameUser, " SET unread_count=unread_count+1 WHERE user_id=:userId;")
		params := map[string]interface{}{"userId": user.UserID}
		_, err = trans.Exec(query, params)
		if err != nil {
			err = trans.Rollback()
			return "", errors.Wrap(err, "An error occurred while updating user unread count")
		}
	}

	err = trans.Commit()
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while commit creating message")
	}

	return lastMessage, nil
}

func rdbSelectMessage(db, messageID string) (*models.Message, error) {
	replica := RdbStore(db).replica()

	var messages []*models.Message
	query := utils.AppendStrings("SELECT * FROM ", tableNameMessage, " WHERE message_id=:messageId;")
	params := map[string]interface{}{"messageId": messageID}
	_, err := replica.Select(&messages, query, params)
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred while getting message")
	}

	if len(messages) == 1 {
		return messages[0], nil
	}

	return nil, nil
}

func rdbSelectMessages(db, roomID string, limit, offset int, order string) ([]*models.Message, error) {
	replica := RdbStore(db).replica()

	var messages []*models.Message
	query := utils.AppendStrings("SELECT * ",
		"FROM ", tableNameMessage, " ",
		"WHERE room_id = :roomId ",
		"AND deleted = 0 ",
		"ORDER BY created ", order, " ",
		"LIMIT :limit ",
		"OFFSET :offset;")
	params := map[string]interface{}{
		"roomId": roomID,
		"limit":  limit,
		"offset": offset,
	}
	_, err := replica.Select(&messages, query, params)
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred while getting messages")
	}

	return messages, nil
}

func rdbSelectCountMessagesByRoomID(db, roomID string) (int64, error) {
	replica := RdbStore(db).replica()

	query := utils.AppendStrings("SELECT count(id) ",
		"FROM ", tableNameMessage, " ",
		"WHERE room_id = :roomId ",
		"AND deleted = 0;")
	params := map[string]interface{}{
		"roomId": roomID,
	}
	count, err := replica.SelectInt(query, params)
	if err != nil {
		return 0, errors.Wrap(err, "An error occurred while getting message count")
	}

	return count, nil
}

func rdbUpdateMessage(db string, message *models.Message) (*models.Message, error) {
	master := RdbStore(db).master()

	_, err := master.Update(message)
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred while updating message")
	}

	return message, nil
}
<commit_msg>Add message item struct<commit_after>package datastore

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

	"github.com/pkg/errors"
	"go.uber.org/zap/zapcore"

	"github.com/swagchat/chat-api/logging"
	"github.com/swagchat/chat-api/models"
	"github.com/swagchat/chat-api/utils"
)

func rdbCreateMessageStore(db string) {
	master := RdbStore(db).master()

	tableMap := master.AddTableWithName(models.Message{}, tableNameMessage)
	tableMap.SetKeys(true, "id")
	for _, columnMap := range tableMap.Columns {
		if columnMap.ColumnName == "message_id" {
			columnMap.SetUnique(true)
		}
	}
	err := master.CreateTablesIfNotExists()
	if err != nil {
		logging.Log(zapcore.FatalLevel, &logging.AppLog{
			Message: "Create message table error",
			Error:   err,
		})
	}

	var addIndexQuery string
	if utils.Config().Datastore.Provider == "sqlite" {
		addIndexQuery = utils.AppendStrings("CREATE INDEX room_id_deleted_created ON ", tableNameMessage, "(room_id, deleted, created)")
	} else {
		addIndexQuery = utils.AppendStrings("ALTER TABLE ", tableNameMessage, " ADD INDEX room_id_deleted_created (room_id, deleted, created)")
		_, err = master.Exec(addIndexQuery)
		if err != nil {
			errMessage := err.Error()
			if strings.Index(errMessage, "Duplicate key name") < 0 {
				logging.Log(zapcore.FatalLevel, &logging.AppLog{
					Message: "Duplicate key name",
					Error:   err,
				})
			}
		}
	}
}

func rdbInsertMessage(db string, message *models.Message) (string, error) {
	master := RdbStore(db).master()
	trans, err := master.Begin()
	if err != nil {
		return "", errors.Wrap(err, "An error occurred while transaction beginning")
	}

	err = trans.Insert(message)
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while creating message")
	}

	var rooms []*models.Room
	query := utils.AppendStrings("SELECT * FROM ", tableNameRoom, " WHERE room_id=:roomId AND deleted=0;")
	params := map[string]interface{}{"roomId": message.RoomID}
	if _, err = trans.Select(&rooms, query, params); err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while getting room")
	}
	if len(rooms) != 1 {
		return "", errors.New("An error occurred while getting room. Room count is not 1")
	}

	room := rooms[0]
	var lastMessage string
	switch message.Type {
	case "text":
		var payloadText models.PayloadText
		json.Unmarshal(message.Payload, &payloadText)
		lastMessage = payloadText.Text
	case "image":
		lastMessage = "画像を受信しました"
	case "buttons":
		var payloadButtons models.PayloadButtons
		json.Unmarshal(message.Payload, &payloadButtons)
		lastMessage = payloadButtons.Title
	case "confirm":
		var payloadConfirm models.PayloadConfirm
		json.Unmarshal(message.Payload, &payloadConfirm)
		lastMessage = payloadConfirm.Text
	case "list":
		var payloadList models.PayloadList
		json.Unmarshal(message.Payload, &payloadList)
		lastMessage = payloadList.Text
	case "carousel":
		var payloadCarousel models.PayloadCarousel
		json.Unmarshal(message.Payload, &payloadCarousel)
		lastMessage = payloadCarousel.Columns[0].Title
	case "imageCarousel":
		lastMessage = "画像を受信しました"
	default:
		lastMessage = "メッセージを受信しました"
	}
	room.LastMessage = lastMessage
	room.LastMessageUpdated = time.Now().Unix()
	_, err = trans.Update(room)
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while updating room")
	}

	query = utils.AppendStrings("UPDATE ", tableNameRoomUser, " SET unread_count=unread_count+1 WHERE room_id=:roomId AND user_id!=:userId;")
	params = map[string]interface{}{
		"roomId": message.RoomID,
		"userId": message.UserID,
	}
	_, err = trans.Exec(query, params)
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while updating room's user unread count")
	}

	var users []*models.User
	query = utils.AppendStrings("SELECT u.* ",
		"FROM ", tableNameRoomUser, " AS ru ",
		"LEFT JOIN ", tableNameUser, " AS u ",
		"ON ru.user_id = u.user_id ",
		"WHERE room_id = :roomId;")
	params = map[string]interface{}{"roomId": message.RoomID}
	_, err = trans.Select(&users, query, params)
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while getting room's users")
	}
	for _, user := range users {
		if user.UserID == message.UserID {
			continue
		}
		query := utils.AppendStrings("UPDATE ", tableNameUser, " SET unread_count=unread_count+1 WHERE user_id=:userId;")
		params := map[string]interface{}{"userId": user.UserID}
		_, err = trans.Exec(query, params)
		if err != nil {
			err = trans.Rollback()
			return "", errors.Wrap(err, "An error occurred while updating user unread count")
		}
	}

	err = trans.Commit()
	if err != nil {
		err = trans.Rollback()
		return "", errors.Wrap(err, "An error occurred while commit creating message")
	}

	return lastMessage, nil
}

func rdbSelectMessage(db, messageID string) (*models.Message, error) {
	replica := RdbStore(db).replica()

	var messages []*models.Message
	query := utils.AppendStrings("SELECT * FROM ", tableNameMessage, " WHERE message_id=:messageId;")
	params := map[string]interface{}{"messageId": messageID}
	_, err := replica.Select(&messages, query, params)
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred while getting message")
	}

	if len(messages) == 1 {
		return messages[0], nil
	}

	return nil, nil
}

func rdbSelectMessages(db, roomID string, limit, offset int, order string) ([]*models.Message, error) {
	replica := RdbStore(db).replica()

	var messages []*models.Message
	query := utils.AppendStrings("SELECT * ",
		"FROM ", tableNameMessage, " ",
		"WHERE room_id = :roomId ",
		"AND deleted = 0 ",
		"ORDER BY created ", order, " ",
		"LIMIT :limit ",
		"OFFSET :offset;")
	params := map[string]interface{}{
		"roomId": roomID,
		"limit":  limit,
		"offset": offset,
	}
	_, err := replica.Select(&messages, query, params)
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred while getting messages")
	}

	return messages, nil
}

func rdbSelectCountMessagesByRoomID(db, roomID string) (int64, error) {
	replica := RdbStore(db).replica()

	query := utils.AppendStrings("SELECT count(id) ",
		"FROM ", tableNameMessage, " ",
		"WHERE room_id = :roomId ",
		"AND deleted = 0;")
	params := map[string]interface{}{
		"roomId": roomID,
	}
	count, err := replica.SelectInt(query, params)
	if err != nil {
		return 0, errors.Wrap(err, "An error occurred while getting message count")
	}

	return count, nil
}

func rdbUpdateMessage(db string, message *models.Message) (*models.Message, error) {
	master := RdbStore(db).master()

	_, err := master.Update(message)
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred while updating message")
	}

	return message, nil
}
<|endoftext|>


<commit_before>package db

import (
	"database/sql"
	"errors"

	sq "github.com/Masterminds/squirrel"
	"github.com/lib/pq"
)

type WorkerResourceCache struct {
	WorkerName    string
	ResourceCache *UsedResourceCache
}

type UsedWorkerResourceCache struct {
	ID int
}

var ErrWorkerBaseResourceTypeDisappeared = errors.New("worker base resource type disappeared")

func (workerResourceCache WorkerResourceCache) FindOrCreate(tx Tx) (*UsedWorkerResourceCache, error) {
	baseResourceType := workerResourceCache.ResourceCache.BaseResourceType()
	usedWorkerBaseResourceType, found, err := WorkerBaseResourceType{
		Name:       baseResourceType.Name,
		WorkerName: workerResourceCache.WorkerName,
	}.Find(tx)
	if err != nil {
		return nil, err
	}

	if !found {
		return nil, ErrWorkerBaseResourceTypeDisappeared
	}

	id, found, err := workerResourceCache.find(tx, usedWorkerBaseResourceType)
	if err != nil {
		return nil, err
	}

	if found {
		return &UsedWorkerResourceCache{
			ID: id,
		}, nil
	}

	err = psql.Insert("worker_resource_caches").
		Columns(
			"resource_cache_id",
			"worker_base_resource_type_id",
		).
		Values(
			workerResourceCache.ResourceCache.ID,
			usedWorkerBaseResourceType.ID,
		).
		Suffix("RETURNING id").
		RunWith(tx).
		QueryRow().
		Scan(&id)
	if err != nil {
		if pqErr, ok := err.(*pq.Error); ok && pqErr.Code.Name() == "foreign_key_violation" {
			return nil, ErrSafeRetryFindOrCreate
		}

		if pqErr, ok := err.(*pq.Error); ok && pqErr.Code.Name() == "unique_violation" {
			return nil, ErrSafeRetryFindOrCreate
		}

		return nil, err
	}

	return &UsedWorkerResourceCache{
		ID: id,
	}, nil
}

func (workerResourceCache WorkerResourceCache) Find(runner sq.Runner) (*UsedWorkerResourceCache, bool, error) {
	baseResourceType := workerResourceCache.ResourceCache.BaseResourceType()
	usedWorkerBaseResourceType, found, err := WorkerBaseResourceType{
		Name:       baseResourceType.Name,
		WorkerName: workerResourceCache.WorkerName,
	}.Find(runner)
	if err != nil {
		return nil, false, err
	}

	if !found {
		return nil, false, nil
	}

	id, found, err := workerResourceCache.find(runner, usedWorkerBaseResourceType)
	if err != nil {
		return nil, false, err
	}

	if found {
		return &UsedWorkerResourceCache{
			ID: id,
		}, true, nil
	}

	return nil, false, nil
}

func (workerResourceCache WorkerResourceCache) find(runner sq.Runner, usedWorkerBaseResourceType *UsedWorkerBaseResourceType) (int, bool, error) {
	var id int

	err := psql.Select("id").
		From("worker_resource_caches").
		Where(sq.Eq{
			"resource_cache_id":            workerResourceCache.ResourceCache.ID,
			"worker_base_resource_type_id": usedWorkerBaseResourceType.ID,
		}).
		RunWith(runner).
		QueryRow().
		Scan(&id)
	if err != nil {
		if err == sql.ErrNoRows {
			return 0, false, nil
		}

		return 0, false, err
	}

	return id, true, nil
}
<commit_msg>worker resource cache: don't retry on foreign key<commit_after>package db

import (
	"database/sql"
	"errors"

	sq "github.com/Masterminds/squirrel"
	"github.com/lib/pq"
)

type WorkerResourceCache struct {
	WorkerName    string
	ResourceCache *UsedResourceCache
}

type UsedWorkerResourceCache struct {
	ID int
}

var ErrWorkerBaseResourceTypeDisappeared = errors.New("worker base resource type disappeared")

func (workerResourceCache WorkerResourceCache) FindOrCreate(tx Tx) (*UsedWorkerResourceCache, error) {
	baseResourceType := workerResourceCache.ResourceCache.BaseResourceType()
	usedWorkerBaseResourceType, found, err := WorkerBaseResourceType{
		Name:       baseResourceType.Name,
		WorkerName: workerResourceCache.WorkerName,
	}.Find(tx)
	if err != nil {
		return nil, err
	}

	if !found {
		return nil, ErrWorkerBaseResourceTypeDisappeared
	}

	id, found, err := workerResourceCache.find(tx, usedWorkerBaseResourceType)
	if err != nil {
		return nil, err
	}

	if found {
		return &UsedWorkerResourceCache{
			ID: id,
		}, nil
	}

	err = psql.Insert("worker_resource_caches").
		Columns(
			"resource_cache_id",
			"worker_base_resource_type_id",
		).
		Values(
			workerResourceCache.ResourceCache.ID,
			usedWorkerBaseResourceType.ID,
		).
		Suffix("RETURNING id").
		RunWith(tx).
		QueryRow().
		Scan(&id)
	if err != nil {
		if pqErr, ok := err.(*pq.Error); ok && pqErr.Code.Name() == "unique_violation" {
			return nil, ErrSafeRetryFindOrCreate
		}

		return nil, err
	}

	return &UsedWorkerResourceCache{
		ID: id,
	}, nil
}

func (workerResourceCache WorkerResourceCache) Find(runner sq.Runner) (*UsedWorkerResourceCache, bool, error) {
	baseResourceType := workerResourceCache.ResourceCache.BaseResourceType()
	usedWorkerBaseResourceType, found, err := WorkerBaseResourceType{
		Name:       baseResourceType.Name,
		WorkerName: workerResourceCache.WorkerName,
	}.Find(runner)
	if err != nil {
		return nil, false, err
	}

	if !found {
		return nil, false, nil
	}

	id, found, err := workerResourceCache.find(runner, usedWorkerBaseResourceType)
	if err != nil {
		return nil, false, err
	}

	if found {
		return &UsedWorkerResourceCache{
			ID: id,
		}, true, nil
	}

	return nil, false, nil
}

func (workerResourceCache WorkerResourceCache) find(runner sq.Runner, usedWorkerBaseResourceType *UsedWorkerBaseResourceType) (int, bool, error) {
	var id int

	err := psql.Select("id").
		From("worker_resource_caches").
		Where(sq.Eq{
			"resource_cache_id":            workerResourceCache.ResourceCache.ID,
			"worker_base_resource_type_id": usedWorkerBaseResourceType.ID,
		}).
		RunWith(runner).
		QueryRow().
		Scan(&id)
	if err != nil {
		if err == sql.ErrNoRows {
			return 0, false, nil
		}

		return 0, false, err
	}

	return id, true, nil
}
<|endoftext|>


<commit_before>package steps

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"

	"github.com/cayleygraph/cayley/graph/memstore"
	"github.com/cayleygraph/cayley/query/linkedql"
	"github.com/cayleygraph/quad"
	"github.com/cayleygraph/quad/jsonld"
	"github.com/cayleygraph/quad/voc"
	"github.com/stretchr/testify/require"
)

type TestCase struct {
	Data    interface{} `json:"data"`
	Query   interface{} `json:"query"`
	Results interface{} `json:"results"`
}

func readData(data interface{}) ([]quad.Quad, error) {
	d, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	b := bytes.NewBuffer(d)
	reader := jsonld.NewReader(b)
	quads, err := quad.ReadAll(reader)
	if err != nil {
		return nil, err
	}
	return quads, nil
}

func readQuery(raw interface{}) (linkedql.Step, error) {
	d, err := json.Marshal(raw)
	if err != nil {
		return nil, err
	}
	q, err := linkedql.Unmarshal(d)
	if err != nil {
		return nil, err
	}
	query, ok := q.(linkedql.Step)
	if !ok {
		return nil, fmt.Errorf("Expected linkedql.Step")
	}
	return query, nil
}

func TestLinkedQL(t *testing.T) {
	// Using files
	direcotry := "test-cases"
	files, err := ioutil.ReadDir(direcotry)
	if err != nil {
		require.NoError(t, err)
	}
	for _, info := range files {
		fileName := info.Name()
		filePath := filepath.Join(direcotry, fileName)

		testName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
		t.Run(testName, func(t *testing.T) {
			file, err := ioutil.ReadFile(filePath)
			require.NoError(t, err)

			var c TestCase
			err = json.Unmarshal(file, &c)
			require.NoError(t, err)

			data, err := readData(c.Data)
			require.NoError(t, err, fileName)
			require.NotEmpty(t, data, fileName)

			query, err := readQuery(c.Query)
			require.NoError(t, err, fileName)
			require.NotNil(t, query, fileName)
			store := memstore.New(data...)
			voc := voc.Namespaces{}
			ctx := context.TODO()
			iterator, err := linkedql.BuildIterator(query, store, &voc)
			require.NoError(t, err)
			var results []interface{}
			for iterator.Next(ctx) {
				results = append(results, iterator.Result())
			}
			require.NoError(t, iterator.Err())
			require.Equal(t, nil, isomorphic(c.Results, results))
		})
	}
}
<commit_msg>Add a check to skip non-JSON files in the test-cases directory<commit_after>package steps

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"

	"github.com/cayleygraph/cayley/graph/memstore"
	"github.com/cayleygraph/cayley/query/linkedql"
	"github.com/cayleygraph/quad"
	"github.com/cayleygraph/quad/jsonld"
	"github.com/cayleygraph/quad/voc"
	"github.com/stretchr/testify/require"
)

type TestCase struct {
	Data    interface{} `json:"data"`
	Query   interface{} `json:"query"`
	Results interface{} `json:"results"`
}

func readData(data interface{}) ([]quad.Quad, error) {
	d, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	b := bytes.NewBuffer(d)
	reader := jsonld.NewReader(b)
	quads, err := quad.ReadAll(reader)
	if err != nil {
		return nil, err
	}
	return quads, nil
}

func readQuery(raw interface{}) (linkedql.Step, error) {
	d, err := json.Marshal(raw)
	if err != nil {
		return nil, err
	}
	q, err := linkedql.Unmarshal(d)
	if err != nil {
		return nil, err
	}
	query, ok := q.(linkedql.Step)
	if !ok {
		return nil, fmt.Errorf("Expected linkedql.Step")
	}
	return query, nil
}

func TestLinkedQL(t *testing.T) {
	// Using files
	directory := "test-cases"
	files, err := ioutil.ReadDir(directory)
	if err != nil {
		require.NoError(t, err)
	}
	for _, info := range files {
		fileName := info.Name()
		filePath := filepath.Join(directory, fileName)

		if !strings.HasSuffix(fileName, ".json") {
			// skip non-JSON files
			continue
		}

		testName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
		t.Run(testName, func(t *testing.T) {
			file, err := ioutil.ReadFile(filePath)
			require.NoError(t, err)

			var c TestCase
			err = json.Unmarshal(file, &c)
			require.NoError(t, err)

			data, err := readData(c.Data)
			require.NoError(t, err, fileName)
			require.NotEmpty(t, data, fileName)

			query, err := readQuery(c.Query)
			require.NoError(t, err, fileName)
			require.NotNil(t, query, fileName)
			store := memstore.New(data...)
			voc := voc.Namespaces{}
			ctx := context.TODO()
			iterator, err := linkedql.BuildIterator(query, store, &voc)
			require.NoError(t, err)
			var results []interface{}
			for iterator.Next(ctx) {
				results = append(results, iterator.Result())
			}
			require.NoError(t, iterator.Err())
			require.Equal(t, nil, isomorphic(c.Results, results))
		})
	}
}
<|endoftext|>


<commit_before>package torrentfs

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"testing"
	"time"

	"golang.org/x/net/context"

	"bitbucket.org/anacrolix/go.torrent"
	"bitbucket.org/anacrolix/go.torrent/data/mmap"
	"bitbucket.org/anacrolix/go.torrent/testutil"
	"bitbucket.org/anacrolix/go.torrent/util"

	"github.com/anacrolix/libtorgo/metainfo"

	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
)

func init() {
	go http.ListenAndServe(":6061", nil)
}

func TestTCPAddrString(t *testing.T) {
	l, err := net.Listen("tcp4", "localhost:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	c, err := net.Dial("tcp", l.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()
	ras := c.RemoteAddr().String()
	ta := &net.TCPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: util.AddrPort(l.Addr()),
	}
	s := ta.String()
	if ras != s {
		t.FailNow()
	}
}

type testLayout struct {
	BaseDir   string
	MountDir  string
	Completed string
	Metainfo  *metainfo.MetaInfo
}

func (me *testLayout) Destroy() error {
	return os.RemoveAll(me.BaseDir)
}

func newGreetingLayout() (tl testLayout, err error) {
	tl.BaseDir, err = ioutil.TempDir("", "torrentfs")
	if err != nil {
		return
	}
	tl.Completed = filepath.Join(tl.BaseDir, "completed")
	os.Mkdir(tl.Completed, 0777)
	tl.MountDir = filepath.Join(tl.BaseDir, "mnt")
	os.Mkdir(tl.MountDir, 0777)
	name := testutil.CreateDummyTorrentData(tl.Completed)
	metaInfoBuf := &bytes.Buffer{}
	testutil.CreateMetaInfo(name, metaInfoBuf)
	tl.Metainfo, err = metainfo.Load(metaInfoBuf)
	return
}

// Unmount without first killing the FUSE connection while there are FUSE
// operations blocked inside the filesystem code.
func TestUnmountWedged(t *testing.T) {
	layout, err := newGreetingLayout()
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		err := layout.Destroy()
		if err != nil {
			t.Log(err)
		}
	}()
	client, err := torrent.NewClient(&torrent.Config{
		DataDir:         filepath.Join(layout.BaseDir, "incomplete"),
		DisableTrackers: true,
		NoDHT:           true,

		NoDefaultBlocklist: true,
	})
	defer client.Stop()
	client.AddTorrent(layout.Metainfo)
	fs := New(client)
	fuseConn, err := fuse.Mount(layout.MountDir)
	if err != nil {
		if strings.Contains(err.Error(), "fuse") {
			t.Skip(err)
		}
		t.Fatal(err)
	}
	go func() {
		server := fusefs.Server{
			FS: fs,
			Debug: func(msg interface{}) {
				t.Log(msg)
			},
		}
		server.Serve(fuseConn)
	}()
	<-fuseConn.Ready
	if err := fuseConn.MountError; err != nil {
		t.Fatal(err)
	}
	// Read the greeting file, though it will never be available. This should
	// "wedge" FUSE, requiring the fs object to be forcibly destroyed. The
	// read call will return with a FS error.
	go func() {
		_, err := ioutil.ReadFile(filepath.Join(layout.MountDir, layout.Metainfo.Info.Name))
		if err == nil {
			t.Fatal("expected error reading greeting")
		}
	}()

	// Wait until the read has blocked inside the filesystem code.
	fs.mu.Lock()
	for fs.blockedReads != 1 {
		fs.event.Wait()
	}
	fs.mu.Unlock()

	fs.Destroy()

	for {
		err = fuse.Unmount(layout.MountDir)
		if err != nil {
			t.Logf("error unmounting: %s", err)
			time.Sleep(time.Millisecond)
		} else {
			break
		}
	}

	err = fuseConn.Close()
	if err != nil {
		t.Fatalf("error closing fuse conn: %s", err)
	}
}

func TestDownloadOnDemand(t *testing.T) {
	layout, err := newGreetingLayout()
	if err != nil {
		t.Fatal(err)
	}
	defer layout.Destroy()
	seeder, err := torrent.NewClient(&torrent.Config{
		DataDir:         layout.Completed,
		DisableTrackers: true,
		NoDHT:           true,
		ListenAddr:      ":0",

		NoDefaultBlocklist: true,
		// Ensure that the metainfo is obtained over the wire, since we added
		// the torrent to the seeder by magnet.
		DisableMetainfoCache: true,
	})
	if err != nil {
		t.Fatalf("error creating seeder client: %s", err)
	}
	seeder.SetIPBlockList(nil)
	defer seeder.Stop()
	http.HandleFunc("/seeder", func(w http.ResponseWriter, req *http.Request) {
		seeder.WriteStatus(w)
	})
	_, err = seeder.AddMagnet(fmt.Sprintf("magnet:?xt=urn:btih:%x", layout.Metainfo.Info.Hash))
	if err != nil {
		t.Fatal(err)
	}
	leecher, err := torrent.NewClient(&torrent.Config{
		DisableTrackers: true,
		NoDHT:           true,
		ListenAddr:      ":0",
		DisableTCP:      true,

		NoDefaultBlocklist: true,

		TorrentDataOpener: func(info *metainfo.Info) (torrent.TorrentData, error) {
			return mmap.TorrentData(info, filepath.Join(layout.BaseDir, "download"))
		},

		// This can be used to check if clients can connect to other clients
		// with the same ID.

		// PeerID: seeder.PeerID(),
	})
	leecher.SetIPBlockList(nil)
	http.HandleFunc("/leecher", func(w http.ResponseWriter, req *http.Request) {
		leecher.WriteStatus(w)
	})
	defer leecher.Stop()
	leecher.AddTorrent(layout.Metainfo)
	var ih torrent.InfoHash
	util.CopyExact(ih[:], layout.Metainfo.Info.Hash)
	leecher.AddPeers(ih, []torrent.Peer{func() torrent.Peer {
		_, port, err := net.SplitHostPort(seeder.ListenAddr().String())
		if err != nil {
			panic(err)
		}
		portInt64, err := strconv.ParseInt(port, 0, 0)
		if err != nil {
			panic(err)
		}
		return torrent.Peer{
			IP:   net.IPv6loopback,
			Port: int(portInt64),
		}
	}()})
	fs := New(leecher)
	defer fs.Destroy()
	root, _ := fs.Root()
	node, _ := root.(fusefs.NodeStringLookuper).Lookup(context.Background(), "greeting")
	size := int(node.Attr().Size)
	resp := &fuse.ReadResponse{
		Data: make([]byte, size),
	}
	node.(fusefs.HandleReader).Read(context.Background(), &fuse.ReadRequest{
		Size: size,
	}, resp)
	content := resp.Data
	if string(content) != testutil.GreetingFileContents {
		t.FailNow()
	}
}
<commit_msg>fs test: Resolve localhost, some platforms are funny about it<commit_after>package torrentfs

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	_ "net/http/pprof"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"testing"
	"time"

	"golang.org/x/net/context"

	"bitbucket.org/anacrolix/go.torrent"
	"bitbucket.org/anacrolix/go.torrent/data/mmap"
	"bitbucket.org/anacrolix/go.torrent/testutil"
	"bitbucket.org/anacrolix/go.torrent/util"

	"github.com/anacrolix/libtorgo/metainfo"

	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
)

func init() {
	go http.ListenAndServe(":6061", nil)
}

func TestTCPAddrString(t *testing.T) {
	l, err := net.Listen("tcp4", "localhost:0")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	c, err := net.Dial("tcp", l.Addr().String())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()
	ras := c.RemoteAddr().String()
	ta := &net.TCPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: util.AddrPort(l.Addr()),
	}
	s := ta.String()
	if ras != s {
		t.FailNow()
	}
}

type testLayout struct {
	BaseDir   string
	MountDir  string
	Completed string
	Metainfo  *metainfo.MetaInfo
}

func (me *testLayout) Destroy() error {
	return os.RemoveAll(me.BaseDir)
}

func newGreetingLayout() (tl testLayout, err error) {
	tl.BaseDir, err = ioutil.TempDir("", "torrentfs")
	if err != nil {
		return
	}
	tl.Completed = filepath.Join(tl.BaseDir, "completed")
	os.Mkdir(tl.Completed, 0777)
	tl.MountDir = filepath.Join(tl.BaseDir, "mnt")
	os.Mkdir(tl.MountDir, 0777)
	name := testutil.CreateDummyTorrentData(tl.Completed)
	metaInfoBuf := &bytes.Buffer{}
	testutil.CreateMetaInfo(name, metaInfoBuf)
	tl.Metainfo, err = metainfo.Load(metaInfoBuf)
	return
}

// Unmount without first killing the FUSE connection while there are FUSE
// operations blocked inside the filesystem code.
func TestUnmountWedged(t *testing.T) {
	layout, err := newGreetingLayout()
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		err := layout.Destroy()
		if err != nil {
			t.Log(err)
		}
	}()
	client, err := torrent.NewClient(&torrent.Config{
		DataDir:         filepath.Join(layout.BaseDir, "incomplete"),
		DisableTrackers: true,
		NoDHT:           true,

		NoDefaultBlocklist: true,
	})
	defer client.Stop()
	client.AddTorrent(layout.Metainfo)
	fs := New(client)
	fuseConn, err := fuse.Mount(layout.MountDir)
	if err != nil {
		if strings.Contains(err.Error(), "fuse") {
			t.Skip(err)
		}
		t.Fatal(err)
	}
	go func() {
		server := fusefs.Server{
			FS: fs,
			Debug: func(msg interface{}) {
				t.Log(msg)
			},
		}
		server.Serve(fuseConn)
	}()
	<-fuseConn.Ready
	if err := fuseConn.MountError; err != nil {
		t.Fatal(err)
	}
	// Read the greeting file, though it will never be available. This should
	// "wedge" FUSE, requiring the fs object to be forcibly destroyed. The
	// read call will return with a FS error.
	go func() {
		_, err := ioutil.ReadFile(filepath.Join(layout.MountDir, layout.Metainfo.Info.Name))
		if err == nil {
			t.Fatal("expected error reading greeting")
		}
	}()

	// Wait until the read has blocked inside the filesystem code.
	fs.mu.Lock()
	for fs.blockedReads != 1 {
		fs.event.Wait()
	}
	fs.mu.Unlock()

	fs.Destroy()

	for {
		err = fuse.Unmount(layout.MountDir)
		if err != nil {
			t.Logf("error unmounting: %s", err)
			time.Sleep(time.Millisecond)
		} else {
			break
		}
	}

	err = fuseConn.Close()
	if err != nil {
		t.Fatalf("error closing fuse conn: %s", err)
	}
}

func TestDownloadOnDemand(t *testing.T) {
	layout, err := newGreetingLayout()
	if err != nil {
		t.Fatal(err)
	}
	defer layout.Destroy()
	seeder, err := torrent.NewClient(&torrent.Config{
		DataDir:         layout.Completed,
		DisableTrackers: true,
		NoDHT:           true,
		ListenAddr:      ":0",

		NoDefaultBlocklist: true,
		// Ensure that the metainfo is obtained over the wire, since we added
		// the torrent to the seeder by magnet.
		DisableMetainfoCache: true,
	})
	if err != nil {
		t.Fatalf("error creating seeder client: %s", err)
	}
	seeder.SetIPBlockList(nil)
	defer seeder.Stop()
	http.HandleFunc("/seeder", func(w http.ResponseWriter, req *http.Request) {
		seeder.WriteStatus(w)
	})
	_, err = seeder.AddMagnet(fmt.Sprintf("magnet:?xt=urn:btih:%x", layout.Metainfo.Info.Hash))
	if err != nil {
		t.Fatal(err)
	}
	leecher, err := torrent.NewClient(&torrent.Config{
		DisableTrackers: true,
		NoDHT:           true,
		ListenAddr:      ":0",
		DisableTCP:      true,

		NoDefaultBlocklist: true,

		TorrentDataOpener: func(info *metainfo.Info) (torrent.TorrentData, error) {
			return mmap.TorrentData(info, filepath.Join(layout.BaseDir, "download"))
		},

		// This can be used to check if clients can connect to other clients
		// with the same ID.

		// PeerID: seeder.PeerID(),
	})
	leecher.SetIPBlockList(nil)
	http.HandleFunc("/leecher", func(w http.ResponseWriter, req *http.Request) {
		leecher.WriteStatus(w)
	})
	defer leecher.Stop()
	leecher.AddTorrent(layout.Metainfo)
	var ih torrent.InfoHash
	util.CopyExact(ih[:], layout.Metainfo.Info.Hash)
	leecher.AddPeers(ih, []torrent.Peer{func() torrent.Peer {
		_, port, err := net.SplitHostPort(seeder.ListenAddr().String())
		if err != nil {
			panic(err)
		}
		portInt64, err := strconv.ParseInt(port, 0, 0)
		if err != nil {
			panic(err)
		}
		return torrent.Peer{
			IP: func() net.IP {
				ret, _ := net.ResolveIPAddr("ip", "localhost")
				return ret.IP
			}(),
			Port: int(portInt64),
		}
	}()})
	fs := New(leecher)
	defer fs.Destroy()
	root, _ := fs.Root()
	node, _ := root.(fusefs.NodeStringLookuper).Lookup(context.Background(), "greeting")
	size := int(node.Attr().Size)
	resp := &fuse.ReadResponse{
		Data: make([]byte, size),
	}
	node.(fusefs.HandleReader).Read(context.Background(), &fuse.ReadRequest{
		Size: size,
	}, resp)
	content := resp.Data
	if string(content) != testutil.GreetingFileContents {
		t.FailNow()
	}
}
<|endoftext|>