<commit_before>/* Operations with Trello cards */
package trello

import (
  . "github.com/ErintLabs/trellohub/genapi"
  "github.com/ErintLabs/trellohub/github"
  "net/url"
  "log"
  "strconv"
  "regexp"
)

type Card struct {
  // Object TODO cascading
  Id          string      `json:"id"`
  Name        string      `json:"name"`
  ListId      string      `json:"idList"`
  Desc        string      `json:"desc"`
  trello      *Trello
  Issue       *github.Issue `json:"-"`
  checklist   *Checklist
  Members     Set         `json:"-"`
}

/* Places the card in the cache */
func (card *Card) cache() {
  card.trello.cardById[card.Id] = card
  if card.Issue != nil {
    issuestr := card.Issue.String()
    if card.trello.cardByIssue[issuestr] != card {
      log.Printf("Card %s registered for issue %s", card.Id, issuestr)
      card.trello.cardByIssue[issuestr] = card
    }
  }
}

/* Updates card data from the server */
func (card *Card) update() {
  GenGET(card.trello, "/cards/" + card.Id, card)
  // TODO if error

  /* We don't really care to hold attachments array, just check if there is something to link */
  var data []Object
  GenGET(card.trello, "/cards/" + card.Id + "/attachments/", &data)
  issuesFound := 0
  for _, v := range data {
    re := regexp.MustCompile(REGEX_GH_ISSUE)
    if res := re.FindStringSubmatch(v.Name); res != nil {
      issuesFound ++;
      if issuesFound > 1 {
        log.Printf("WARNING: Duplicate issue attachments found on card #%s.", card.Id)
      } else {
        issueno, _ := strconv.Atoi(res[2])
        card.LinkIssue(card.trello.github.GetIssue(res[1], issueno))
      }
    }
  }

  card.updateMembers()

  // REFACTOR: checklist
}

/* Adds a card to the list with a given name and returns the card id */
func (trello *Trello) AddCard(listid string, name string, desc string) *Card {
  data := Card{ trello: trello }
  GenPOSTForm(trello, "/cards/", &data, url.Values{
    "name": { name },
    "idList": { listid },
    "desc": { desc },
    "pos": { "top" } })

  data.cache()
  // TODO if error

  return &data
}

/* Retrieves the card from the server */
func (trello *Trello) GetCard(cardid string) *Card {
  if card := trello.cardById[cardid]; card == nil {
    data := Card{ trello: trello, Id: cardid, Members: NewSet() }
    data.update()
    data.cache()
    return &data
  } else {
    return card
  }
}

/* Attach issues, PRs and commits to the card */
func (card *Card) attachURL(addr string) {
  GenPOSTForm(card.trello, "/cards/" + card.Id + "/attachments", nil, url.Values{ "url": { addr } })
  // TODO if error
}

func (card *Card) AttachIssue(issue *github.Issue) {
  card.attachURL(issue.IssueURL())
}

/* Move a card to the different list */
func (card *Card) Move(listid string) {
  log.Printf("Moving card %s to list %s.", card.Id, listid)
  GenPUT(card.trello, "/cards/" + card.Id + "/idList?value=" + listid)
}

/* Find card by Issue. Assuming only one such card exists. */
func (trello *Trello) FindCard(issue string) *Card {
  return trello.cardByIssue[issue]
}

/* Fetch all cards from the board and [re-]initialise caches */
func (trello *Trello) makeCardCache() {
  var data []Card
  GenGET(trello, "/boards/" + trello.BoardId + "/cards/", &data)

  for _, card := range data {
    card.trello = trello
    card.update()
    card.cache()
  }
}

/* Attach an Issue link */
func (card *Card) LinkIssue(issue *github.Issue) {
  if (card.Issue != issue) {
    card.trello.cardByIssue[issue.String()] = card
    card.Issue = issue
    card.cache()
  }
}

/* TODO handlers:
 - card created
 - card links updated
 */
<commit_msg>Ok now it doesn't crash for real<commit_after>/* Operations with Trello cards */
package trello

import (
  . "github.com/ErintLabs/trellohub/genapi"
  "github.com/ErintLabs/trellohub/github"
  "net/url"
  "log"
  "strconv"
  "regexp"
)

type Card struct {
  // Object TODO cascading
  Id          string      `json:"id"`
  Name        string      `json:"name"`
  ListId      string      `json:"idList"`
  Desc        string      `json:"desc"`
  trello      *Trello
  Issue       *github.Issue `json:"-"`
  checklist   *Checklist
  Members     Set         `json:"-"`
}

/* Places the card in the cache */
func (card *Card) cache() {
  card.trello.cardById[card.Id] = card
  if card.Issue != nil {
    issuestr := card.Issue.String()
    if card.trello.cardByIssue[issuestr] != card {
      log.Printf("Card %s registered for issue %s", card.Id, issuestr)
      card.trello.cardByIssue[issuestr] = card
    }
  }
}

/* Updates card data from the server */
func (card *Card) update() {
  GenGET(card.trello, "/cards/" + card.Id, card)
  // TODO if error

  /* We don't really care to hold attachments array, just check if there is something to link */
  var data []Object
  GenGET(card.trello, "/cards/" + card.Id + "/attachments/", &data)
  issuesFound := 0
  for _, v := range data {
    re := regexp.MustCompile(REGEX_GH_ISSUE)
    if res := re.FindStringSubmatch(v.Name); res != nil {
      issuesFound ++;
      if issuesFound > 1 {
        log.Printf("WARNING: Duplicate issue attachments found on card #%s.", card.Id)
      } else {
        issueno, _ := strconv.Atoi(res[2])
        card.LinkIssue(card.trello.github.GetIssue(res[1], issueno))
      }
    }
  }

  card.updateMembers()

  // REFACTOR: checklist
}

/* Adds a card to the list with a given name and returns the card id */
func (trello *Trello) AddCard(listid string, name string, desc string) *Card {
  data := Card{ trello: trello, Members: NewSet() }
  GenPOSTForm(trello, "/cards/", &data, url.Values{
    "name": { name },
    "idList": { listid },
    "desc": { desc },
    "pos": { "top" } })

  data.cache()
  // TODO if error

  return &data
}

/* Retrieves the card from the server */
func (trello *Trello) GetCard(cardid string) *Card {
  if card := trello.cardById[cardid]; card == nil {
    data := Card{ trello: trello, Id: cardid, Members: NewSet() }
    data.update()
    data.cache()
    return &data
  } else {
    return card
  }
}

/* Attach issues, PRs and commits to the card */
func (card *Card) attachURL(addr string) {
  GenPOSTForm(card.trello, "/cards/" + card.Id + "/attachments", nil, url.Values{ "url": { addr } })
  // TODO if error
}

func (card *Card) AttachIssue(issue *github.Issue) {
  card.attachURL(issue.IssueURL())
}

/* Move a card to the different list */
func (card *Card) Move(listid string) {
  log.Printf("Moving card %s to list %s.", card.Id, listid)
  GenPUT(card.trello, "/cards/" + card.Id + "/idList?value=" + listid)
}

/* Find card by Issue. Assuming only one such card exists. */
func (trello *Trello) FindCard(issue string) *Card {
  return trello.cardByIssue[issue]
}

/* Fetch all cards from the board and [re-]initialise caches */
func (trello *Trello) makeCardCache() {
  var data []Card
  GenGET(trello, "/boards/" + trello.BoardId + "/cards/", &data)

  for _, card := range data {
    card.trello = trello
    card.update()
    card.cache()
  }
}

/* Attach an Issue link */
func (card *Card) LinkIssue(issue *github.Issue) {
  if (card.Issue != issue) {
    card.trello.cardByIssue[issue.String()] = card
    card.Issue = issue
    card.cache()
  }
}

/* TODO handlers:
 - card created
 - card links updated
 */
<|endoftext|>


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

package pilot

import (
	"fmt"
	"testing"

	"istio.io/istio/pkg/log"
)

// To route all external traffic via Istio Egress gateway
// 1. Add external services
// 2. Add egress gateway
// 3. Add virtual service for each external service such that
//    3.a. Traffic from all sidecars (i.e. mesh gateway) goes to egress gateway svc
//    3.b. Traffic from egress gateway goes to actual destination (in our case, its t)
// The tests will only check for requests from a->t with host matching ext service
func TestEgressGateway(t *testing.T) {
	if !tc.V1alpha3 {
		t.Skipf("Skipping %s: v1alpha3=false", t.Name())
	}

	cfgs := &deployableConfig{
		Namespace: tc.Kube.Namespace,
		YamlFiles: []string{
			"testdata/v1alpha3/egressgateway.yaml",
			"testdata/v1alpha3/external-service.yaml",
			"testdata/v1alpha3/rule-route-via-egressgateway.yaml"},
	}
	if err := cfgs.Setup(); err != nil {
		t.Fatal(err)
	}
	defer cfgs.Teardown()

	runRetriableTest(t, "RouteViaEgressGateway", defaultRetryBudget, func() error {
		// We use an arbitrary IP to ensure that the test fails if networking logic is implemented incorrectly
		reqURL := fmt.Sprintf("http://1.1.1.1/bookinfo")
		resp := ClientRequest("a", reqURL, 100, "-key Host -val eu.bookinfo.com")
		count := make(map[string]int)
		for _, elt := range resp.Host {
			count[elt] = count[elt] + 1
		}
		log.Infof("request counts %v", count)
		if count["eu.bookinfo.com"] >= 95 {
			return nil
		}
		return errAgain
	})
}
<commit_msg>verify 200 status code in addition to header value (#5163)<commit_after>// Copyright 2017 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pilot

import (
	"fmt"
	"testing"

	"istio.io/istio/pkg/log"
)

// To route all external traffic via Istio Egress gateway
// 1. Add external services
// 2. Add egress gateway
// 3. Add virtual service for each external service such that
//    3.a. Traffic from all sidecars (i.e. mesh gateway) goes to egress gateway svc
//    3.b. Traffic from egress gateway goes to actual destination (in our case, its t)
// The tests will only check for requests from a->t with host matching ext service
func TestEgressGateway(t *testing.T) {
	if !tc.V1alpha3 {
		t.Skipf("Skipping %s: v1alpha3=false", t.Name())
	}

	cfgs := &deployableConfig{
		Namespace: tc.Kube.Namespace,
		YamlFiles: []string{
			"testdata/v1alpha3/egressgateway.yaml",
			"testdata/v1alpha3/external-service.yaml",
			"testdata/v1alpha3/rule-route-via-egressgateway.yaml"},
	}
	if err := cfgs.Setup(); err != nil {
		t.Fatal(err)
	}
	defer cfgs.Teardown()

	runRetriableTest(t, "RouteViaEgressGateway", defaultRetryBudget, func() error {
		// We use an arbitrary IP to ensure that the test fails if networking logic is implemented incorrectly
		reqURL := fmt.Sprintf("http://1.1.1.1/bookinfo")
		resp := ClientRequest("a", reqURL, 100, "-key Host -val eu.bookinfo.com")
		count := make(map[string]int)
		for _, elt := range resp.Host {
			count[elt] = count[elt] + 1
		}
		for _, elt := range resp.Code {
			count[elt] = count[elt] + 1
		}
		log.Infof("request counts %v", count)
		if count["eu.bookinfo.com"] >= 95 && count[httpOK] >= 95 {
			return nil
		}
		return errAgain
	})
}
<|endoftext|>


<commit_before>// Copyright 2015 The Vanadium 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 leveldb provides a LevelDB-based implementation of store.Store.
package leveldb

// #cgo LDFLAGS: -lleveldb
// #include <stdlib.h>
// #include "leveldb/c.h"
// #include "syncbase_leveldb.h"
import "C"
import (
	"sync"
	"unsafe"

	"v.io/syncbase/x/ref/services/syncbase/store"
)

// db is a wrapper around LevelDB that implements the store.Store interface.
// TODO(rogulenko): ensure thread safety.
type db struct {
	cDb *C.leveldb_t
	// Default read/write options.
	readOptions  *C.leveldb_readoptions_t
	writeOptions *C.leveldb_writeoptions_t
	// Used to prevent concurrent transactions.
	// TODO(rogulenko): improve concurrency.
	mu sync.Mutex
}

var _ store.Store = (*db)(nil)

// Open opens the database located at the given path, creating it if it doesn't
// exist.
func Open(path string) (store.Store, error) {
	var cError *C.char
	cPath := C.CString(path)
	defer C.free(unsafe.Pointer(cPath))

	cOpts := C.leveldb_options_create()
	C.leveldb_options_set_create_if_missing(cOpts, 1)
	C.leveldb_options_set_paranoid_checks(cOpts, 1)
	defer C.leveldb_options_destroy(cOpts)

	cDb := C.leveldb_open(cOpts, cPath, &cError)
	if err := goError(cError); err != nil {
		return nil, err
	}
	readOptions := C.leveldb_readoptions_create()
	C.leveldb_readoptions_set_verify_checksums(readOptions, 1)
	return &db{
		cDb:          cDb,
		readOptions:  readOptions,
		writeOptions: C.leveldb_writeoptions_create(),
	}, nil
}

// Close implements the store.Store interface.
func (d *db) Close() error {
	C.leveldb_close(d.cDb)
	C.leveldb_readoptions_destroy(d.readOptions)
	C.leveldb_writeoptions_destroy(d.writeOptions)
	return nil
}

// Destroy removes all physical data of the database located at the given path.
func Destroy(path string) error {
	var cError *C.char
	cPath := C.CString(path)
	defer C.free(unsafe.Pointer(cPath))
	cOpts := C.leveldb_options_create()
	defer C.leveldb_options_destroy(cOpts)
	C.leveldb_destroy_db(cOpts, cPath, &cError)
	return goError(cError)
}

// Get implements the store.StoreReader interface.
func (d *db) Get(key, valbuf []byte) ([]byte, error) {
	return d.getWithOpts(key, valbuf, d.readOptions)
}

// Scan implements the store.StoreReader interface.
func (d *db) Scan(start, end []byte) (store.Stream, error) {
	return newStream(d, start, end, d.readOptions), nil
}

// Put implements the store.StoreWriter interface.
func (d *db) Put(key, value []byte) error {
	// TODO(rogulenko): improve performance.
	return store.RunInTransaction(d, func(st store.StoreReadWriter) error {
		return st.Put(key, value)
	})
}

// Delete implements the store.StoreWriter interface.
func (d *db) Delete(key []byte) error {
	// TODO(rogulenko): improve performance.
	return store.RunInTransaction(d, func(st store.StoreReadWriter) error {
		return st.Delete(key)
	})
}

// NewTransaction implements the store.Store interface.
func (d *db) NewTransaction() store.Transaction {
	return newTransaction(d)
}

// NewSnapshot implements the store.Store interface.
func (d *db) NewSnapshot() store.Snapshot {
	return newSnapshot(d)
}

// getWithOpts returns the value for the given key.
// cOpts may contain a pointer to a snapshot.
func (d *db) getWithOpts(key, valbuf []byte, cOpts *C.leveldb_readoptions_t) ([]byte, error) {
	var cError *C.char
	var valLen C.size_t
	cStr, cLen := cSlice(key)
	val := C.leveldb_get(d.cDb, cOpts, cStr, cLen, &valLen, &cError)
	if err := goError(cError); err != nil {
		return nil, err
	}
	if val == nil {
		return nil, &store.ErrUnknownKey{Key: string(key)}
	}
	defer C.leveldb_free(unsafe.Pointer(val))
	return store.CopyBytes(valbuf, goBytes(val, valLen)), nil
}
<commit_msg>syncbase profile: add Snappy<commit_after>// Copyright 2015 The Vanadium 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 leveldb provides a LevelDB-based implementation of store.Store.
package leveldb

// #cgo LDFLAGS: -lleveldb -lsnappy
// #include <stdlib.h>
// #include "leveldb/c.h"
// #include "syncbase_leveldb.h"
import "C"
import (
	"sync"
	"unsafe"

	"v.io/syncbase/x/ref/services/syncbase/store"
)

// db is a wrapper around LevelDB that implements the store.Store interface.
// TODO(rogulenko): ensure thread safety.
type db struct {
	cDb *C.leveldb_t
	// Default read/write options.
	readOptions  *C.leveldb_readoptions_t
	writeOptions *C.leveldb_writeoptions_t
	// Used to prevent concurrent transactions.
	// TODO(rogulenko): improve concurrency.
	mu sync.Mutex
}

var _ store.Store = (*db)(nil)

// Open opens the database located at the given path, creating it if it doesn't
// exist.
func Open(path string) (store.Store, error) {
	var cError *C.char
	cPath := C.CString(path)
	defer C.free(unsafe.Pointer(cPath))

	cOpts := C.leveldb_options_create()
	C.leveldb_options_set_create_if_missing(cOpts, 1)
	C.leveldb_options_set_paranoid_checks(cOpts, 1)
	defer C.leveldb_options_destroy(cOpts)

	cDb := C.leveldb_open(cOpts, cPath, &cError)
	if err := goError(cError); err != nil {
		return nil, err
	}
	readOptions := C.leveldb_readoptions_create()
	C.leveldb_readoptions_set_verify_checksums(readOptions, 1)
	return &db{
		cDb:          cDb,
		readOptions:  readOptions,
		writeOptions: C.leveldb_writeoptions_create(),
	}, nil
}

// Close implements the store.Store interface.
func (d *db) Close() error {
	C.leveldb_close(d.cDb)
	C.leveldb_readoptions_destroy(d.readOptions)
	C.leveldb_writeoptions_destroy(d.writeOptions)
	return nil
}

// Destroy removes all physical data of the database located at the given path.
func Destroy(path string) error {
	var cError *C.char
	cPath := C.CString(path)
	defer C.free(unsafe.Pointer(cPath))
	cOpts := C.leveldb_options_create()
	defer C.leveldb_options_destroy(cOpts)
	C.leveldb_destroy_db(cOpts, cPath, &cError)
	return goError(cError)
}

// Get implements the store.StoreReader interface.
func (d *db) Get(key, valbuf []byte) ([]byte, error) {
	return d.getWithOpts(key, valbuf, d.readOptions)
}

// Scan implements the store.StoreReader interface.
func (d *db) Scan(start, end []byte) (store.Stream, error) {
	return newStream(d, start, end, d.readOptions), nil
}

// Put implements the store.StoreWriter interface.
func (d *db) Put(key, value []byte) error {
	// TODO(rogulenko): improve performance.
	return store.RunInTransaction(d, func(st store.StoreReadWriter) error {
		return st.Put(key, value)
	})
}

// Delete implements the store.StoreWriter interface.
func (d *db) Delete(key []byte) error {
	// TODO(rogulenko): improve performance.
	return store.RunInTransaction(d, func(st store.StoreReadWriter) error {
		return st.Delete(key)
	})
}

// NewTransaction implements the store.Store interface.
func (d *db) NewTransaction() store.Transaction {
	return newTransaction(d)
}

// NewSnapshot implements the store.Store interface.
func (d *db) NewSnapshot() store.Snapshot {
	return newSnapshot(d)
}

// getWithOpts returns the value for the given key.
// cOpts may contain a pointer to a snapshot.
func (d *db) getWithOpts(key, valbuf []byte, cOpts *C.leveldb_readoptions_t) ([]byte, error) {
	var cError *C.char
	var valLen C.size_t
	cStr, cLen := cSlice(key)
	val := C.leveldb_get(d.cDb, cOpts, cStr, cLen, &valLen, &cError)
	if err := goError(cError); err != nil {
		return nil, err
	}
	if val == nil {
		return nil, &store.ErrUnknownKey{Key: string(key)}
	}
	defer C.leveldb_free(unsafe.Pointer(val))
	return store.CopyBytes(valbuf, goBytes(val, valLen)), nil
}
<|endoftext|>


<commit_before>package sessmemory

import (
	"errors"
	"github.com/gabstv/goboots"
	"sync"
	"time"
)

type MemoryDbSession struct {
	sessions      map[string]*goboots.Session
	app           *goboots.App
	sessions_lock sync.Mutex
}

func (m *MemoryDbSession) SetApp(app *goboots.App) {
	m.app = app
}

func (m *MemoryDbSession) GetSession(sid string) (*goboots.Session, error) {
	m.sessions_lock.Lock()
	defer m.sessions_lock.Unlock()

	if m.sessions == nil {
		return nil, errors.New("not found")
	}

	if sessfile, ok := m.sessions[sid]; ok {
		sessfile.Updated = time.Now()
		sessfile.Flush()
		return sessfile, nil
	}

	return nil, errors.New("not found")
}

func (m *MemoryDbSession) PutSession(session *goboots.Session) error {
	if session == nil {
		return errors.New("session is nil")
	}
	m.sessions_lock.Lock()
	defer m.sessions_lock.Unlock()

	if m.sessions == nil {
		m.sessions = make(map[string]*goboots.Session)
	}

	m.sessions[session.SID] = session

	return nil
}

func (m *MemoryDbSession) NewSession(session *goboots.Session) error {
	return m.PutSession(session)
}

func (m *MemoryDbSession) RemoveSession(session *goboots.Session) error {
	if session == nil || m.sessions == nil {
		return nil
	}

	m.sessions_lock.Lock()
	defer m.sessions_lock.Unlock()

	delete(m.sessions, session.SID)

	return nil
}

func (m *MemoryDbSession) Cleanup(minTime time.Time) {
	m.sessions_lock.Lock()
	defer m.sessions_lock.Unlock()

	if m.sessions == nil {
		return
	}
	//TODO: implement a faster cleanup method
	delList := make([]string, 0, len(m.sessions))
	for k, v := range m.sessions {
		if minTime.After(v.Updated) {
			delList = append(delList, k)
		}
	}
	for _, v := range delList {
		delete(m.sessions, v)
	}
	m.app.Logger.Println("MemoryDbSession::Cleanup ok", len(delList), "entries removed")
}

func (m *MemoryDbSession) Close() {

}

func init() {
	goboots.RegisterSessionStorageDriver("sessmemory", &MemoryDbSession{})
}
<commit_msg>fixed mutex lock<commit_after>package sessmemory

import (
	"errors"
	"github.com/gabstv/goboots"
	"sync"
	"time"
)

type MemoryDbSession struct {
	sessions      map[string]*goboots.Session
	app           *goboots.App
	sessions_lock sync.Mutex
}

func (m *MemoryDbSession) SetApp(app *goboots.App) {
	m.app = app
}

func (m *MemoryDbSession) GetSession(sid string) (*goboots.Session, error) {
	m.sessions_lock.Lock()
	defer m.sessions_lock.Unlock()

	if m.sessions == nil {
		return nil, errors.New("not found")
	}

	if sessfile := m.sessions[sid]; sessfile != nil {
		sessfile.Updated = time.Now()
		// sessfile.Flush() calls PutSession which will be locked by now so we use a goroutine for this!
		go sessfile.Flush()
		return sessfile, nil
	}

	return nil, errors.New("not found")
}

func (m *MemoryDbSession) PutSession(session *goboots.Session) error {
	if session == nil {
		return errors.New("session is nil")
	}

	m.sessions_lock.Lock()
	defer m.sessions_lock.Unlock()

	if m.sessions == nil {
		m.sessions = make(map[string]*goboots.Session)
	}

	m.sessions[session.SID] = session

	return nil
}

func (m *MemoryDbSession) NewSession(session *goboots.Session) error {
	return m.PutSession(session)
}

func (m *MemoryDbSession) RemoveSession(session *goboots.Session) error {
	m.sessions_lock.Lock()
	defer m.sessions_lock.Unlock()

	if session == nil || m.sessions == nil {
		return nil
	}

	delete(m.sessions, session.SID)

	return nil
}

func (m *MemoryDbSession) Cleanup(minTime time.Time) {
	m.app.Logger.Println("MemoryDbSession::Cleanup start")
	m.sessions_lock.Lock()
	defer m.sessions_lock.Unlock()

	if m.sessions == nil {
		return
	}
	//TODO: implement a faster cleanup method
	delList := make([]string, 0, len(m.sessions))
	for k, v := range m.sessions {
		if minTime.After(v.Updated) {
			delList = append(delList, k)
		}
	}
	for _, v := range delList {
		delete(m.sessions, v)
	}
	m.app.Logger.Println("MemoryDbSession::Cleanup ok", len(delList), "entries removed")
}

func (m *MemoryDbSession) Close() {

}

func init() {
	mmm := &MemoryDbSession{}
	mmm.sessions = make(map[string]*goboots.Session)
	goboots.RegisterSessionStorageDriver("sessmemory", mmm)
}
<|endoftext|>


<commit_before>package twilio

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/itsabot/abot/core"
	"github.com/itsabot/abot/shared/interface/sms"
	"github.com/itsabot/abot/shared/log"
	"github.com/jmoiron/sqlx"
	"github.com/labstack/echo"
)

var e *echo.Echo
var db *sqlx.DB
var ner core.Classifier
var offensive map[string]struct{}

func TestMain(m *testing.M) {
	if len(os.Getenv("TWILIO_TEST_ACCOUNT_SID")) == 0 ||
		len(os.Getenv("TWILIO_TEST_AUTH_TOKEN")) == 0 {
		log.Info("must set TWILIO_TEST_ACCOUNT_SID and TWILIO_TEST_AUTH_TOKEN env vars")
		os.Exit(1)
	}
	var err error
	e, err = core.NewServer()
	if err != nil {
		log.Info("couldnt boot server", err)
		os.Exit(1)
	}
	os.Exit(m.Run())
}

func TestOpenAndSend(t *testing.T) {
	auth := os.Getenv("TWILIO_TEST_ACCOUNT_SID") + ":" +
		os.Getenv("TWILIO_TEST_AUTH_TOKEN")
	conn, err := sms.Open("twilio", auth, e)
	if err != nil {
		log.Info("opening conn")
		t.Fatal(err)
	}
	err = conn.Send("15005550006", "15005550005", "test message")
	if err != nil {
		log.Info("sending msg")
		t.Fatal(err)
	}
}

func TestHandler(t *testing.T) {
	u := fmt.Sprintf("http://localhost:%s/twilio", os.Getenv("PORT"))
	data := []byte(`{ "Body": "Test message", "From": "+15005551234" }`)
	c, _ := request("POST", u, data, e)
	if c != 200 {
		t.Fatal("expected 200. got", c)
	}
}

func request(method, path string, data []byte, e *echo.Echo) (int, string) {
	r, err := http.NewRequest(method, path, bytes.NewBuffer(data))
	if err != nil {
		return 0, "err completing request: " + err.Error()
	}
	w := httptest.NewRecorder()
	e.ServeHTTP(w, r)
	return w.Code, w.Body.String()
}
<commit_msg>Improve tests<commit_after>package twilio

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/itsabot/abot/core"
	"github.com/itsabot/abot/shared/interface/sms"
	"github.com/itsabot/abot/shared/log"
	"github.com/labstack/echo"
)

var e *echo.Echo

func TestMain(m *testing.M) {
	if len(os.Getenv("TWILIO_TEST_ACCOUNT_SID")) == 0 ||
		len(os.Getenv("TWILIO_TEST_AUTH_TOKEN")) == 0 {
		log.Info("must set TWILIO_TEST_ACCOUNT_SID and TWILIO_TEST_AUTH_TOKEN env vars")
		os.Exit(1)
	}
	var err error
	if err = os.Setenv("ABOT_ENV", "test"); err != nil {
		log.Info("couldn't set ABOT_ENV=test", err)
	}
	e, err = core.NewServer()
	if err != nil {
		log.Info("couldn't boot server", err)
		os.Exit(1)
	}
	os.Exit(m.Run())
}

func TestOpenAndSend(t *testing.T) {
	auth := os.Getenv("TWILIO_TEST_ACCOUNT_SID") + ":" +
		os.Getenv("TWILIO_TEST_AUTH_TOKEN")
	conn, err := sms.Open("twilio", auth, e)
	if err != nil {
		log.Info("opening conn")
		t.Fatal(err)
	}
	err = conn.Send("15005550006", "15005550005", "test message")
	if err != nil {
		log.Info("sending msg")
		t.Fatal(err)
	}
}

func TestHandler(t *testing.T) {
	u := fmt.Sprintf("http://localhost:%s/twilio", os.Getenv("PORT"))
	data := []byte(`{ "Body": "Test message", "From": "+15005551234" }`)
	c, _ := request("POST", u, data)
	if c != 200 {
		t.Fatal("expected 200. got", c)
	}
}

func request(method, path string, data []byte) (int, string) {
	r, err := http.NewRequest(method, path, bytes.NewBuffer(data))
	if err != nil {
		return 0, "err completing request: " + err.Error()
	}
	w := httptest.NewRecorder()
	e.ServeHTTP(w, r)
	return w.Code, w.Body.String()
}
<|endoftext|>


<commit_before>package p2p

import (
	"errors"
	"fmt"
	"net"
	"strconv"
	"time"

	"github.com/tendermint/tendermint/Godeps/_workspace/src/github.com/tendermint/log15"
	acm "github.com/tendermint/tendermint/account"
	. "github.com/tendermint/tendermint/common"
	"github.com/tendermint/tendermint/types"
)

type Reactor interface {
	Service // Start, Stop

	SetSwitch(*Switch)
	GetChannels() []*ChannelDescriptor
	AddPeer(peer *Peer)
	RemovePeer(peer *Peer, reason interface{})
	Receive(chId byte, peer *Peer, msgBytes []byte)
}

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

type BaseReactor struct {
	QuitService // Provides Start, Stop, .Quit
	Switch      *Switch
}

func NewBaseReactor(log log15.Logger, name string, impl Reactor) *BaseReactor {
	return &BaseReactor{
		QuitService: *NewQuitService(log, name, impl),
		Switch:      nil,
	}
}

func (br *BaseReactor) SetSwitch(sw *Switch) {
	br.Switch = sw
}
func (_ *BaseReactor) GetChannels() []*ChannelDescriptor              { return nil }
func (_ *BaseReactor) AddPeer(peer *Peer)                             {}
func (_ *BaseReactor) RemovePeer(peer *Peer, reason interface{})      {}
func (_ *BaseReactor) Receive(chId byte, peer *Peer, msgBytes []byte) {}

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

/*
The `Switch` handles peer connections and exposes an API to receive incoming messages
on `Reactors`.  Each `Reactor` is responsible for handling incoming messages of one
or more `Channels`.  So while sending outgoing messages is typically performed on the peer,
incoming messages are received on the reactor.
*/
type Switch struct {
	BaseService

	listeners    []Listener
	reactors     map[string]Reactor
	chDescs      []*ChannelDescriptor
	reactorsByCh map[byte]Reactor
	peers        *PeerSet
	dialing      *CMap
	nodeInfo     *types.NodeInfo    // our node info
	nodePrivKey  acm.PrivKeyEd25519 // our node privkey
}

var (
	ErrSwitchDuplicatePeer      = errors.New("Duplicate peer")
	ErrSwitchMaxPeersPerIPRange = errors.New("IP range has too many peers")
)

const (
	peerDialTimeoutSeconds  = 3  // TODO make this configurable
	handshakeTimeoutSeconds = 20 // TODO make this configurable
	maxNumPeers             = 50 // TODO make this configurable
)

func NewSwitch() *Switch {
	sw := &Switch{
		reactors:     make(map[string]Reactor),
		chDescs:      make([]*ChannelDescriptor, 0),
		reactorsByCh: make(map[byte]Reactor),
		peers:        NewPeerSet(),
		dialing:      NewCMap(),
		nodeInfo:     nil,
	}
	sw.BaseService = *NewBaseService(log, "P2P Switch", sw)
	return sw
}

// Not goroutine safe.
func (sw *Switch) AddReactor(name string, reactor Reactor) Reactor {
	// Validate the reactor.
	// No two reactors can share the same channel.
	reactorChannels := reactor.GetChannels()
	for _, chDesc := range reactorChannels {
		chId := chDesc.Id
		if sw.reactorsByCh[chId] != nil {
			panic(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chId, sw.reactorsByCh[chId], reactor))
		}
		sw.chDescs = append(sw.chDescs, chDesc)
		sw.reactorsByCh[chId] = reactor
	}
	sw.reactors[name] = reactor
	reactor.SetSwitch(sw)
	return reactor
}

// Not goroutine safe.
func (sw *Switch) Reactors() map[string]Reactor {
	return sw.reactors
}

// Not goroutine safe.
func (sw *Switch) Reactor(name string) Reactor {
	return sw.reactors[name]
}

// Not goroutine safe.
func (sw *Switch) AddListener(l Listener) {
	sw.listeners = append(sw.listeners, l)
}

// Not goroutine safe.
func (sw *Switch) Listeners() []Listener {
	return sw.listeners
}

// Not goroutine safe.
func (sw *Switch) IsListening() bool {
	return len(sw.listeners) > 0
}

// Not goroutine safe.
func (sw *Switch) SetNodeInfo(nodeInfo *types.NodeInfo) {
	sw.nodeInfo = nodeInfo
}

// Not goroutine safe.
func (sw *Switch) NodeInfo() *types.NodeInfo {
	return sw.nodeInfo
}

// Not goroutine safe.
// NOTE: Overwrites sw.nodeInfo.PubKey
func (sw *Switch) SetNodePrivKey(nodePrivKey acm.PrivKeyEd25519) {
	sw.nodePrivKey = nodePrivKey
	if sw.nodeInfo != nil {
		sw.nodeInfo.PubKey = nodePrivKey.PubKey().(acm.PubKeyEd25519)
	}
}

// Switch.Start() starts all the reactors, peers, and listeners.
func (sw *Switch) AfterStart() {
	// Start reactors
	for _, reactor := range sw.reactors {
		reactor.Start()
	}
	// Start peers
	for _, peer := range sw.peers.List() {
		sw.startInitPeer(peer)
	}
	// Start listeners
	for _, listener := range sw.listeners {
		go sw.listenerRoutine(listener)
	}
}

func (sw *Switch) AfterStop() {
	// Stop listeners
	for _, listener := range sw.listeners {
		listener.Stop()
	}
	sw.listeners = nil
	// Stop peers
	for _, peer := range sw.peers.List() {
		peer.Stop()
	}
	sw.peers = NewPeerSet()
	// Stop reactors
	for _, reactor := range sw.reactors {
		reactor.Stop()
	}
}

// NOTE: This performs a blocking handshake before the peer is added.
// CONTRACT: Iff error is returned, peer is nil, and conn is immediately closed.
func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, error) {
	// Set deadline for handshake so we don't block forever on conn.ReadFull
	conn.SetDeadline(time.Now().Add(handshakeTimeoutSeconds * time.Second))

	// First, encrypt the connection.
	sconn, err := MakeSecretConnection(conn, sw.nodePrivKey)
	if err != nil {
		conn.Close()
		return nil, err
	}
	// Then, perform node handshake
	peerNodeInfo, err := peerHandshake(sconn, sw.nodeInfo)
	if err != nil {
		sconn.Close()
		return nil, err
	}
	// Check that the professed PubKey matches the sconn's.
	if !peerNodeInfo.PubKey.Equals(sconn.RemotePubKey()) {
		sconn.Close()
		return nil, fmt.Errorf("Ignoring connection with unmatching pubkey: %v vs %v",
			peerNodeInfo.PubKey, sconn.RemotePubKey())
	}
	// Avoid self
	if peerNodeInfo.PubKey.Equals(sw.nodeInfo.PubKey) {
		sconn.Close()
		return nil, fmt.Errorf("Ignoring connection from self")
	}
	// Check version, chain id
	if err := sw.nodeInfo.CompatibleWith(peerNodeInfo); err != nil {
		sconn.Close()
		return nil, err
	}

	// The peerNodeInfo is not verified, so overwrite
	// the IP, and the port too if we dialed out
	// Everything else we just have to trust
	ip, port, _ := net.SplitHostPort(sconn.RemoteAddr().String())
	peerNodeInfo.Host = ip
	if outbound {
		porti, _ := strconv.Atoi(port)
		peerNodeInfo.P2PPort = uint16(porti)
	}
	peer := newPeer(sconn, peerNodeInfo, outbound, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError)

	// Add the peer to .peers
	// ignore if duplicate or if we already have too many for that IP range
	if err := sw.peers.Add(peer); err != nil {
		log.Notice("Ignoring peer", "error", err, "peer", peer)
		peer.mconn.Stop()
		return nil, err
	}

	// remove deadline and start peer
	conn.SetDeadline(time.Time{})
	if sw.IsRunning() {
		sw.startInitPeer(peer)
	}

	log.Notice("Added peer", "peer", peer)
	return peer, nil
}

func (sw *Switch) startInitPeer(peer *Peer) {
	peer.Start()               // spawn send/recv routines
	sw.addPeerToReactors(peer) // run AddPeer on each reactor
}

func (sw *Switch) DialPeerWithAddress(addr *NetAddress) (*Peer, error) {
	log.Info("Dialing address", "address", addr)
	sw.dialing.Set(addr.IP.String(), addr)
	conn, err := addr.DialTimeout(peerDialTimeoutSeconds * time.Second)
	sw.dialing.Delete(addr.IP.String())
	if err != nil {
		log.Info("Failed dialing address", "address", addr, "error", err)
		return nil, err
	}
	peer, err := sw.AddPeerWithConnection(conn, true)
	if err != nil {
		log.Info("Failed adding peer", "address", addr, "conn", conn, "error", err)
		return nil, err
	}
	log.Notice("Dialed and added peer", "address", addr, "peer", peer)
	return peer, nil
}

func (sw *Switch) IsDialing(addr *NetAddress) bool {
	return sw.dialing.Has(addr.IP.String())
}

// Broadcast runs a go routine for each attempted send, which will block
// trying to send for defaultSendTimeoutSeconds. Returns a channel
// which receives success values for each attempted send (false if times out)
func (sw *Switch) Broadcast(chId byte, msg interface{}) chan bool {
	successChan := make(chan bool, len(sw.peers.List()))
	log.Info("Broadcast", "channel", chId, "msg", msg)
	for _, peer := range sw.peers.List() {
		go func(peer *Peer) {
			success := peer.Send(chId, msg)
			successChan <- success
		}(peer)
	}
	return successChan

}

// Returns the count of outbound/inbound and outbound-dialing peers.
func (sw *Switch) NumPeers() (outbound, inbound, dialing int) {
	peers := sw.peers.List()
	for _, peer := range peers {
		if peer.outbound {
			outbound++
		} else {
			inbound++
		}
	}
	dialing = sw.dialing.Size()
	return
}

func (sw *Switch) Peers() IPeerSet {
	return sw.peers
}

// Disconnect from a peer due to external error.
// TODO: make record depending on reason.
func (sw *Switch) StopPeerForError(peer *Peer, reason interface{}) {
	log.Notice("Stopping peer for error", "peer", peer, "error", reason)
	sw.peers.Remove(peer)
	peer.Stop()
	sw.removePeerFromReactors(peer, reason)
}

// Disconnect from a peer gracefully.
// TODO: handle graceful disconnects.
func (sw *Switch) StopPeerGracefully(peer *Peer) {
	log.Notice("Stopping peer gracefully")
	sw.peers.Remove(peer)
	peer.Stop()
	sw.removePeerFromReactors(peer, nil)
}

func (sw *Switch) addPeerToReactors(peer *Peer) {
	for _, reactor := range sw.reactors {
		reactor.AddPeer(peer)
	}
}

func (sw *Switch) removePeerFromReactors(peer *Peer, reason interface{}) {
	for _, reactor := range sw.reactors {
		reactor.RemovePeer(peer, reason)
	}
}

func (sw *Switch) listenerRoutine(l Listener) {
	for {
		inConn, ok := <-l.Connections()
		if !ok {
			break
		}

		// ignore connection if we already have enough
		if maxNumPeers <= sw.peers.Size() {
			log.Info("Ignoring inbound connection: already have enough peers", "address", inConn.RemoteAddr().String(), "numPeers", sw.peers.Size(), "max", maxNumPeers)
			continue
		}

		// Ignore connections from IP ranges for which we have too many
		if sw.peers.HasMaxForIPRange(inConn) {
			log.Info("Ignoring inbound connection: already have enough peers for that IP range", "address", inConn.RemoteAddr().String())
			continue
		}

		// New inbound connection!
		_, err := sw.AddPeerWithConnection(inConn, false)
		if err != nil {
			log.Notice("Ignoring inbound connection: error on AddPeerWithConnection", "address", inConn.RemoteAddr().String(), "error", err)
			continue
		}

		// NOTE: We don't yet have the listening port of the
		// remote (if they have a listener at all).
		// The peerHandshake will handle that
	}

	// cleanup
}

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

type SwitchEventNewPeer struct {
	Peer *Peer
}

type SwitchEventDonePeer struct {
	Peer  *Peer
	Error interface{}
}
<commit_msg>Stop the peer to stop the mconn<commit_after>package p2p

import (
	"errors"
	"fmt"
	"net"
	"strconv"
	"time"

	"github.com/tendermint/tendermint/Godeps/_workspace/src/github.com/tendermint/log15"
	acm "github.com/tendermint/tendermint/account"
	. "github.com/tendermint/tendermint/common"
	"github.com/tendermint/tendermint/types"
)

type Reactor interface {
	Service // Start, Stop

	SetSwitch(*Switch)
	GetChannels() []*ChannelDescriptor
	AddPeer(peer *Peer)
	RemovePeer(peer *Peer, reason interface{})
	Receive(chId byte, peer *Peer, msgBytes []byte)
}

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

type BaseReactor struct {
	QuitService // Provides Start, Stop, .Quit
	Switch      *Switch
}

func NewBaseReactor(log log15.Logger, name string, impl Reactor) *BaseReactor {
	return &BaseReactor{
		QuitService: *NewQuitService(log, name, impl),
		Switch:      nil,
	}
}

func (br *BaseReactor) SetSwitch(sw *Switch) {
	br.Switch = sw
}
func (_ *BaseReactor) GetChannels() []*ChannelDescriptor              { return nil }
func (_ *BaseReactor) AddPeer(peer *Peer)                             {}
func (_ *BaseReactor) RemovePeer(peer *Peer, reason interface{})      {}
func (_ *BaseReactor) Receive(chId byte, peer *Peer, msgBytes []byte) {}

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

/*
The `Switch` handles peer connections and exposes an API to receive incoming messages
on `Reactors`.  Each `Reactor` is responsible for handling incoming messages of one
or more `Channels`.  So while sending outgoing messages is typically performed on the peer,
incoming messages are received on the reactor.
*/
type Switch struct {
	BaseService

	listeners    []Listener
	reactors     map[string]Reactor
	chDescs      []*ChannelDescriptor
	reactorsByCh map[byte]Reactor
	peers        *PeerSet
	dialing      *CMap
	nodeInfo     *types.NodeInfo    // our node info
	nodePrivKey  acm.PrivKeyEd25519 // our node privkey
}

var (
	ErrSwitchDuplicatePeer      = errors.New("Duplicate peer")
	ErrSwitchMaxPeersPerIPRange = errors.New("IP range has too many peers")
)

const (
	peerDialTimeoutSeconds  = 3  // TODO make this configurable
	handshakeTimeoutSeconds = 20 // TODO make this configurable
	maxNumPeers             = 50 // TODO make this configurable
)

func NewSwitch() *Switch {
	sw := &Switch{
		reactors:     make(map[string]Reactor),
		chDescs:      make([]*ChannelDescriptor, 0),
		reactorsByCh: make(map[byte]Reactor),
		peers:        NewPeerSet(),
		dialing:      NewCMap(),
		nodeInfo:     nil,
	}
	sw.BaseService = *NewBaseService(log, "P2P Switch", sw)
	return sw
}

// Not goroutine safe.
func (sw *Switch) AddReactor(name string, reactor Reactor) Reactor {
	// Validate the reactor.
	// No two reactors can share the same channel.
	reactorChannels := reactor.GetChannels()
	for _, chDesc := range reactorChannels {
		chId := chDesc.Id
		if sw.reactorsByCh[chId] != nil {
			panic(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chId, sw.reactorsByCh[chId], reactor))
		}
		sw.chDescs = append(sw.chDescs, chDesc)
		sw.reactorsByCh[chId] = reactor
	}
	sw.reactors[name] = reactor
	reactor.SetSwitch(sw)
	return reactor
}

// Not goroutine safe.
func (sw *Switch) Reactors() map[string]Reactor {
	return sw.reactors
}

// Not goroutine safe.
func (sw *Switch) Reactor(name string) Reactor {
	return sw.reactors[name]
}

// Not goroutine safe.
func (sw *Switch) AddListener(l Listener) {
	sw.listeners = append(sw.listeners, l)
}

// Not goroutine safe.
func (sw *Switch) Listeners() []Listener {
	return sw.listeners
}

// Not goroutine safe.
func (sw *Switch) IsListening() bool {
	return len(sw.listeners) > 0
}

// Not goroutine safe.
func (sw *Switch) SetNodeInfo(nodeInfo *types.NodeInfo) {
	sw.nodeInfo = nodeInfo
}

// Not goroutine safe.
func (sw *Switch) NodeInfo() *types.NodeInfo {
	return sw.nodeInfo
}

// Not goroutine safe.
// NOTE: Overwrites sw.nodeInfo.PubKey
func (sw *Switch) SetNodePrivKey(nodePrivKey acm.PrivKeyEd25519) {
	sw.nodePrivKey = nodePrivKey
	if sw.nodeInfo != nil {
		sw.nodeInfo.PubKey = nodePrivKey.PubKey().(acm.PubKeyEd25519)
	}
}

// Switch.Start() starts all the reactors, peers, and listeners.
func (sw *Switch) AfterStart() {
	// Start reactors
	for _, reactor := range sw.reactors {
		reactor.Start()
	}
	// Start peers
	for _, peer := range sw.peers.List() {
		sw.startInitPeer(peer)
	}
	// Start listeners
	for _, listener := range sw.listeners {
		go sw.listenerRoutine(listener)
	}
}

func (sw *Switch) AfterStop() {
	// Stop listeners
	for _, listener := range sw.listeners {
		listener.Stop()
	}
	sw.listeners = nil
	// Stop peers
	for _, peer := range sw.peers.List() {
		peer.Stop()
	}
	sw.peers = NewPeerSet()
	// Stop reactors
	for _, reactor := range sw.reactors {
		reactor.Stop()
	}
}

// NOTE: This performs a blocking handshake before the peer is added.
// CONTRACT: Iff error is returned, peer is nil, and conn is immediately closed.
func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, error) {
	// Set deadline for handshake so we don't block forever on conn.ReadFull
	conn.SetDeadline(time.Now().Add(handshakeTimeoutSeconds * time.Second))

	// First, encrypt the connection.
	sconn, err := MakeSecretConnection(conn, sw.nodePrivKey)
	if err != nil {
		conn.Close()
		return nil, err
	}
	// Then, perform node handshake
	peerNodeInfo, err := peerHandshake(sconn, sw.nodeInfo)
	if err != nil {
		sconn.Close()
		return nil, err
	}
	// Check that the professed PubKey matches the sconn's.
	if !peerNodeInfo.PubKey.Equals(sconn.RemotePubKey()) {
		sconn.Close()
		return nil, fmt.Errorf("Ignoring connection with unmatching pubkey: %v vs %v",
			peerNodeInfo.PubKey, sconn.RemotePubKey())
	}
	// Avoid self
	if peerNodeInfo.PubKey.Equals(sw.nodeInfo.PubKey) {
		sconn.Close()
		return nil, fmt.Errorf("Ignoring connection from self")
	}
	// Check version, chain id
	if err := sw.nodeInfo.CompatibleWith(peerNodeInfo); err != nil {
		sconn.Close()
		return nil, err
	}

	// The peerNodeInfo is not verified, so overwrite
	// the IP, and the port too if we dialed out
	// Everything else we just have to trust
	ip, port, _ := net.SplitHostPort(sconn.RemoteAddr().String())
	peerNodeInfo.Host = ip
	if outbound {
		porti, _ := strconv.Atoi(port)
		peerNodeInfo.P2PPort = uint16(porti)
	}
	peer := newPeer(sconn, peerNodeInfo, outbound, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError)

	// Add the peer to .peers
	// ignore if duplicate or if we already have too many for that IP range
	if err := sw.peers.Add(peer); err != nil {
		log.Notice("Ignoring peer", "error", err, "peer", peer)
		peer.Stop()
		return nil, err
	}

	// remove deadline and start peer
	conn.SetDeadline(time.Time{})
	if sw.IsRunning() {
		sw.startInitPeer(peer)
	}

	log.Notice("Added peer", "peer", peer)
	return peer, nil
}

func (sw *Switch) startInitPeer(peer *Peer) {
	peer.Start()               // spawn send/recv routines
	sw.addPeerToReactors(peer) // run AddPeer on each reactor
}

func (sw *Switch) DialPeerWithAddress(addr *NetAddress) (*Peer, error) {
	log.Info("Dialing address", "address", addr)
	sw.dialing.Set(addr.IP.String(), addr)
	conn, err := addr.DialTimeout(peerDialTimeoutSeconds * time.Second)
	sw.dialing.Delete(addr.IP.String())
	if err != nil {
		log.Info("Failed dialing address", "address", addr, "error", err)
		return nil, err
	}
	peer, err := sw.AddPeerWithConnection(conn, true)
	if err != nil {
		log.Info("Failed adding peer", "address", addr, "conn", conn, "error", err)
		return nil, err
	}
	log.Notice("Dialed and added peer", "address", addr, "peer", peer)
	return peer, nil
}

func (sw *Switch) IsDialing(addr *NetAddress) bool {
	return sw.dialing.Has(addr.IP.String())
}

// Broadcast runs a go routine for each attempted send, which will block
// trying to send for defaultSendTimeoutSeconds. Returns a channel
// which receives success values for each attempted send (false if times out)
func (sw *Switch) Broadcast(chId byte, msg interface{}) chan bool {
	successChan := make(chan bool, len(sw.peers.List()))
	log.Info("Broadcast", "channel", chId, "msg", msg)
	for _, peer := range sw.peers.List() {
		go func(peer *Peer) {
			success := peer.Send(chId, msg)
			successChan <- success
		}(peer)
	}
	return successChan

}

// Returns the count of outbound/inbound and outbound-dialing peers.
func (sw *Switch) NumPeers() (outbound, inbound, dialing int) {
	peers := sw.peers.List()
	for _, peer := range peers {
		if peer.outbound {
			outbound++
		} else {
			inbound++
		}
	}
	dialing = sw.dialing.Size()
	return
}

func (sw *Switch) Peers() IPeerSet {
	return sw.peers
}

// Disconnect from a peer due to external error.
// TODO: make record depending on reason.
func (sw *Switch) StopPeerForError(peer *Peer, reason interface{}) {
	log.Notice("Stopping peer for error", "peer", peer, "error", reason)
	sw.peers.Remove(peer)
	peer.Stop()
	sw.removePeerFromReactors(peer, reason)
}

// Disconnect from a peer gracefully.
// TODO: handle graceful disconnects.
func (sw *Switch) StopPeerGracefully(peer *Peer) {
	log.Notice("Stopping peer gracefully")
	sw.peers.Remove(peer)
	peer.Stop()
	sw.removePeerFromReactors(peer, nil)
}

func (sw *Switch) addPeerToReactors(peer *Peer) {
	for _, reactor := range sw.reactors {
		reactor.AddPeer(peer)
	}
}

func (sw *Switch) removePeerFromReactors(peer *Peer, reason interface{}) {
	for _, reactor := range sw.reactors {
		reactor.RemovePeer(peer, reason)
	}
}

func (sw *Switch) listenerRoutine(l Listener) {
	for {
		inConn, ok := <-l.Connections()
		if !ok {
			break
		}

		// ignore connection if we already have enough
		if maxNumPeers <= sw.peers.Size() {
			log.Info("Ignoring inbound connection: already have enough peers", "address", inConn.RemoteAddr().String(), "numPeers", sw.peers.Size(), "max", maxNumPeers)
			continue
		}

		// Ignore connections from IP ranges for which we have too many
		if sw.peers.HasMaxForIPRange(inConn) {
			log.Info("Ignoring inbound connection: already have enough peers for that IP range", "address", inConn.RemoteAddr().String())
			continue
		}

		// New inbound connection!
		_, err := sw.AddPeerWithConnection(inConn, false)
		if err != nil {
			log.Notice("Ignoring inbound connection: error on AddPeerWithConnection", "address", inConn.RemoteAddr().String(), "error", err)
			continue
		}

		// NOTE: We don't yet have the listening port of the
		// remote (if they have a listener at all).
		// The peerHandshake will handle that
	}

	// cleanup
}

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

type SwitchEventNewPeer struct {
	Peer *Peer
}

type SwitchEventDonePeer struct {
	Peer  *Peer
	Error interface{}
}
<|endoftext|>


<commit_before>package migrations

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"

	"github.com/BurntSushi/migration"
)

func AddBuildEvents(tx migration.LimitedTx) error {
	_, err := tx.Exec(`
    CREATE TABLE build_events (
      id serial PRIMARY KEY,
      build_id integer REFERENCES builds (id),
			type varchar(32) NOT NULL,
      payload text NOT NULL
    )
  `)
	if err != nil {
		return err
	}

	cursor := 0

	for {
		var id int
		var buildLog sql.NullString

		err := tx.QueryRow(`
			SELECT id, log
			FROM builds
			WHERE id > $1
			LIMIT 1
		`, cursor).Scan(&id, &buildLog)
		if err != nil {
			if err == sql.ErrNoRows {
				break
			}

			return err
		}

		cursor = id

		if !buildLog.Valid {
			continue
		}

		logBuf := bytes.NewBufferString(buildLog.String)
		decoder := json.NewDecoder(logBuf)

		for {
			var entry logEntry

			err := decoder.Decode(&entry)
			if err != nil {
				if err != io.EOF {
					// non-JSON log; assume v0.0

					_, err = tx.Exec(`
						INSERT INTO build_events (build_id, type, payload)
						VALUES ($1, $2, $3)
					`, id, "version", "0.0")
					if err != nil {
						return err
					}

					_, err = tx.Exec(`
							INSERT INTO build_events (build_id, type, payload)
							VALUES ($1, $2, $3)
						`, id, "log", buildLog.String)
					if err != nil {
						return err
					}
				}

				break
			}

			if entry.Type != "" && entry.EventPayload != nil {
				_, err = tx.Exec(`
						INSERT INTO build_events (build_id, type, payload)
						VALUES ($1, $2, $3)
					`, id, entry.Type, []byte(*entry.EventPayload))
				if err != nil {
					return err
				}

				continue
			}

			if entry.Version != "" {
				versionEnc, err := json.Marshal(entry.Version)
				if err != nil {
					return err
				}

				_, err = tx.Exec(`
					INSERT INTO build_events (build_id, type, payload)
					VALUES ($1, $2, $3)
				`, id, "version", versionEnc)
				if err != nil {
					return err
				}

				continue
			}

			return fmt.Errorf("malformed event stream; got stuck at %s", logBuf.String())
		}
	}

	_, err = tx.Exec(`
		ALTER TABLE builds
		DROP COLUMN log
	`)
	if err != nil {
		return err
	}

	return nil
}

type logEntry struct {
	// either an event...
	Type         string           `json:"type"`
	EventPayload *json.RawMessage `json:"event"`

	// ...or a version
	Version string `json:"version"`
}
<commit_msg>correct build events migration cursor use<commit_after>package migrations

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"

	"github.com/BurntSushi/migration"
)

func AddBuildEvents(tx migration.LimitedTx) error {
	_, err := tx.Exec(`
    CREATE TABLE build_events (
      id serial PRIMARY KEY,
      build_id integer REFERENCES builds (id),
			type varchar(32) NOT NULL,
      payload text NOT NULL
    )
  `)
	if err != nil {
		return err
	}

	cursor := 0

	for {
		var id int
		var buildLog sql.NullString

		err := tx.QueryRow(`
			SELECT id, log
			FROM builds
			WHERE id > $1
			ORDER BY id ASC
			LIMIT 1
		`, cursor).Scan(&id, &buildLog)
		if err != nil {
			if err == sql.ErrNoRows {
				break
			}

			return err
		}

		cursor = id

		if !buildLog.Valid {
			continue
		}

		logBuf := bytes.NewBufferString(buildLog.String)
		decoder := json.NewDecoder(logBuf)

		for {
			var entry logEntry

			err := decoder.Decode(&entry)
			if err != nil {
				if err != io.EOF {
					// non-JSON log; assume v0.0

					_, err = tx.Exec(`
						INSERT INTO build_events (build_id, type, payload)
						VALUES ($1, $2, $3)
					`, id, "version", "0.0")
					if err != nil {
						return err
					}

					_, err = tx.Exec(`
							INSERT INTO build_events (build_id, type, payload)
							VALUES ($1, $2, $3)
						`, id, "log", buildLog.String)
					if err != nil {
						return err
					}
				}

				break
			}

			if entry.Type != "" && entry.EventPayload != nil {
				_, err = tx.Exec(`
						INSERT INTO build_events (build_id, type, payload)
						VALUES ($1, $2, $3)
					`, id, entry.Type, []byte(*entry.EventPayload))
				if err != nil {
					return err
				}

				continue
			}

			if entry.Version != "" {
				versionEnc, err := json.Marshal(entry.Version)
				if err != nil {
					return err
				}

				_, err = tx.Exec(`
					INSERT INTO build_events (build_id, type, payload)
					VALUES ($1, $2, $3)
				`, id, "version", versionEnc)
				if err != nil {
					return err
				}

				continue
			}

			return fmt.Errorf("malformed event stream; got stuck at %s", logBuf.String())
		}
	}

	_, err = tx.Exec(`
		ALTER TABLE builds
		DROP COLUMN log
	`)
	if err != nil {
		return err
	}

	return nil
}

type logEntry struct {
	// either an event...
	Type         string           `json:"type"`
	EventPayload *json.RawMessage `json:"event"`

	// ...or a version
	Version string `json:"version"`
}
<|endoftext|>


<commit_before>package ui

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

	"google.golang.org/appengine"
	"google.golang.org/appengine/urlfetch"
	//"golang.org/x/net/context"

	"github.com/skypies/geo/sfo"
	"github.com/skypies/util/widget"

	fdb "github.com/skypies/flightdb2"
	"github.com/skypies/flightdb2/fgae"
	"github.com/skypies/flightdb2/fpdf"
	"github.com/skypies/flightdb2/metar"
	"github.com/skypies/flightdb2/ref"
)

func init() {
	http.HandleFunc("/fdb/approach", approachHandler)
	http.HandleFunc("/fdb/descent",  descentHandler)
}

// {{{ approachHandler

// ?idspec=XX,YY,...    (or ?idspec=XX&idspec=YYY&...)
//  &colorby=delta      (delta groundspeed, instead of groundspeed)

func approachHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	db := fgae.FlightDB{C:c}

	// This whole Airframe cache thing should be automatic, and upstream from here.
	airframes := ref.NewAirframeCache(c)

	idspecs,err := FormValueIdSpecs(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	flights := []*fdb.Flight{}
	for _,idspec := range idspecs {
		f,err := db.LookupMostRecent(db.NewQuery().ByIdSpec(idspec))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		} else if f == nil {
			http.Error(w, fmt.Sprintf("idspec %s not found", idspec), http.StatusInternalServerError)
			return
		}
		if af := airframes.Get(f.IcaoId); af != nil { f.Airframe = *af }
		flights = append(flights, f)
	}

	OutputApproachesAsPDF(w,r,flights)
}

// }}}
// {{{ descentHandler

// ?idspec=XX,YY,...    (or ?idspec=XX&idspec=YYY&...)
//  &sample=N           (sample the track every N seconds)
//  &averagingwindow=2m (duration to average over)
//  &alt=30000          (max altitude for graph)
//  &length=80          (max distance from origin; in nautical miles)
//  &dist=from          (for distance axis, use dist from airport; by default, uses dist along path)
//  &colorby=delta      (delta groundspeed, instead of groundspeed)

func descentHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	db := fgae.FlightDB{C:c}

	// This whole Airframe cache thing should be automatic, and upstream from here.
	airframes := ref.NewAirframeCache(c)

	idspecs,err := FormValueIdSpecs(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	dp := DescentPDFInit(w, r, len(idspecs))

	if len(idspecs) > 10 {
		dp.LineThickness = 0.1
		dp.LineOpacity = 0.25
	}

	for _,idspec := range idspecs {
		f,err := db.LookupMostRecent(db.NewQuery().ByIdSpec(idspec))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		} else if f == nil {
			http.Error(w, fmt.Sprintf("idspec %s not found", idspec), http.StatusBadRequest)
			return
		}
		if af := airframes.Get(f.IcaoId); af != nil { f.Airframe = *af }

		if err := DescentPDFAddFlight(r, dp, f); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	DescentPDFFinalize(w,r,dp)
}

// }}}

// {{{ OutputApproachesAsPDF

// This is the old handler, useful for Class B
func OutputApproachesAsPDF(w http.ResponseWriter, r *http.Request, flights []*fdb.Flight) {
	colorscheme := FormValuePDFColorScheme(r)

	s,e,_ := widget.FormValueDateRange(r)

	// Default to the time range of the flights
	if time.Since(e) > time.Hour*24*365 {
		// assume undef
		s = time.Now().Add(30*24*time.Hour) // Implausibly in the future
		for _,f := range flights {
			fs,fe := f.Times()
			if fs.Before(s) { s = fs }
			if fe.After(e) { e = fe }
		}
		s = s.Add(-24*time.Hour)
		e = s.Add(24*time.Hour)
	}

	c := appengine.NewContext(r)
	metars,err := metar.FetchFromNOAA(urlfetch.Client(c), "KSFO",s.AddDate(0,0,-1), e.AddDate(0,0,1))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	pdf := fpdf.NewApproachPdf(colorscheme)
	fStrs := []string{}
//outerLoop:
	for _,f := range flights {
		fStrs = append(fStrs, f.String())

		trackType,track := f.PreferredTrack([]string{"FOIA", "ADSB"})
		if track == nil || trackType == "" {
			continue
		}

		track.PostProcess() // Calculate groundspeed data for FOIA data

		if trackType == "ADSB" {
			track.AdjustAltitudes(metars)
		} else {
			for i,_ := range track {
				track[i].IndicatedAltitude = track[i].Altitude
			}
		}

		fpdf.DrawTrack(pdf, track, colorscheme)
	}

	if len(flights) == 1 {
		fpdf.DrawTitle(pdf, fmt.Sprintf("%s", fStrs[0]))
	}

	w.Header().Set("Content-Type", "application/pdf")
	if err := pdf.Output(w); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// }}}

// {{{ DescentPDFInit

func DescentPDFInit(w http.ResponseWriter, r *http.Request, numFlights int) *fpdf.DescentPdf {
	colorscheme := FormValuePDFColorScheme(r)
	colorscheme = fpdf.ByPlotKind

	lengthNM := 80
	if length := widget.FormValueInt64(r, "length"); length > 0 {
		lengthNM = int(length)
	}
	altitudeMax := 30000
	if alt := widget.FormValueInt64(r, "alt"); alt > 0 {
		altitudeMax = int(alt)
	}

	dp := fpdf.DescentPdf{
		ColorScheme: colorscheme,
		OriginPoint: sfo.KLatlongSFO,
		AltitudeMax: float64(altitudeMax),
		LengthNM:    float64(lengthNM),
		ToShow:      map[string]bool{"altitude":true, "groundspeed":true, "verticalspeed":true},
		ShowDebug:  (r.FormValue("debug") != ""),
		AveragingWindow: widget.FormValueDuration(r, "averagingwindow"),
	}

	if originPos,err := FormValueAirportLocation(r, "airportdest"); err == nil {
		dp.OriginPoint = originPos
		dp.OriginLabel = r.FormValue("airportdest")
	}

	if widget.FormValueCheckbox(r, "showaccelerations") {
		dp.ToShow["groundacceleration"],dp.ToShow["verticalacceleration"] = true,true
	}
	if widget.FormValueCheckbox(r, "showangleofinclination") {
		dp.ToShow["angleofinclination"] = true
	}

	dp.Init()

	if r.FormValue("asdepartures") != "" {
		dp.ReconfigureForDepartures()
	}

	dp.DrawFrames()

	if rep,err := getReport(r); err==nil && rep!=nil {
		dp.Caption += fmt.Sprintf("%d flights, %s\n", numFlights, rep.DescriptionText())
	}

	return &dp
}

// }}}
// {{{ DescentPDFAddFlight

func DescentPDFAddFlight(r *http.Request, dp *fpdf.DescentPdf, f *fdb.Flight) error {
	if t,err := flightToDescentTrack(r, f); err != nil {
		return err
	} else {
		if r.FormValue("dist") == "from" {
			dp.DrawTrackAsDistanceFromOrigin(t)
		} else if r.FormValue("asdepartures") != "" {
			dp.DrawTrackAsDistanceTravelledAlongPath(t)
		} else {
			dp.DrawTrackAsDistanceRemainingAlongPath(t)
		}

		if strings.Count(dp.Caption, "\n") < 4 {
			dp.Caption += fmt.Sprintf("%s %s\n", f.IdentString(), t.MediumString())
		}
	}

	return nil
}

// }}}
// {{{ DescentPDFFinalize

func DescentPDFFinalize(w http.ResponseWriter, r *http.Request, dp *fpdf.DescentPdf) {
	dp.DrawCaption()

	w.Header().Set("Content-Type", "application/pdf")
	if err := dp.Output(w); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// }}}

// {{{ flightToDescentTrack

// Resamples the track; does full post-processing; attempts altitude correction
// Extracts a bunch of args from the request (sample, DateRange widget)

func flightToDescentTrack(r *http.Request, f *fdb.Flight) (fdb.Track, error) {
	trackKeyName,track := f.PreferredTrack([]string{"ADSB", "MLAT", "FOIA", "FA", "fr24"})
	if track == nil {
		return nil, fmt.Errorf("no track found (saw %q)", f.ListTracks())
	}

	sampleRate := widget.FormValueDuration(r, "sample")
	if sampleRate == 0 { sampleRate = 15 * time.Second }
	track = track.SampleEvery(sampleRate, false)
	track.PostProcess()

	if trackKeyName == "FOIA" {
		track.AdjustAltitudes(nil) // FOIA track altitudes are already pressure-corrected

	} else {
		c := appengine.NewContext(r)

		// Default to the time range of the flights
		s,e,_ := widget.FormValueDateRange(r)
		if time.Since(e) > time.Hour*24*365 {
			s,e = f.Times()
			s = s.Add(-24*time.Hour)
			e = s.Add(24*time.Hour)
		}

		m,err := metar.FetchFromNOAA(urlfetch.Client(c), "KSFO",s.AddDate(0,0,-1), e.AddDate(0,0,1))
		if err != nil {
			return nil, err
		}
		track.AdjustAltitudes(m)
	}

	return track, nil
}

// }}}

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

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

// }}}
<commit_msg>Stop Metar derailing things<commit_after>package ui

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

	"google.golang.org/appengine"
	"google.golang.org/appengine/urlfetch"
	//"golang.org/x/net/context"

	"github.com/skypies/geo/sfo"
	"github.com/skypies/util/widget"

	fdb "github.com/skypies/flightdb2"
	"github.com/skypies/flightdb2/fgae"
	"github.com/skypies/flightdb2/fpdf"
	"github.com/skypies/flightdb2/metar"
	"github.com/skypies/flightdb2/ref"
)

func init() {
	http.HandleFunc("/fdb/approach", approachHandler)
	http.HandleFunc("/fdb/descent",  descentHandler)
}

// {{{ approachHandler

// ?idspec=XX,YY,...    (or ?idspec=XX&idspec=YYY&...)
//  &colorby=delta      (delta groundspeed, instead of groundspeed)

func approachHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	db := fgae.FlightDB{C:c}

	// This whole Airframe cache thing should be automatic, and upstream from here.
	airframes := ref.NewAirframeCache(c)

	idspecs,err := FormValueIdSpecs(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	flights := []*fdb.Flight{}
	for _,idspec := range idspecs {
		f,err := db.LookupMostRecent(db.NewQuery().ByIdSpec(idspec))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		} else if f == nil {
			http.Error(w, fmt.Sprintf("idspec %s not found", idspec), http.StatusInternalServerError)
			return
		}
		if af := airframes.Get(f.IcaoId); af != nil { f.Airframe = *af }
		flights = append(flights, f)
	}

	OutputApproachesAsPDF(w,r,flights)
}

// }}}
// {{{ descentHandler

// ?idspec=XX,YY,...    (or ?idspec=XX&idspec=YYY&...)
//  &sample=N           (sample the track every N seconds)
//  &averagingwindow=2m (duration to average over)
//  &alt=30000          (max altitude for graph)
//  &length=80          (max distance from origin; in nautical miles)
//  &dist=from          (for distance axis, use dist from airport; by default, uses dist along path)
//  &colorby=delta      (delta groundspeed, instead of groundspeed)

func descentHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	db := fgae.FlightDB{C:c}

	// This whole Airframe cache thing should be automatic, and upstream from here.
	airframes := ref.NewAirframeCache(c)

	idspecs,err := FormValueIdSpecs(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	dp := DescentPDFInit(w, r, len(idspecs))

	if len(idspecs) > 10 {
		dp.LineThickness = 0.1
		dp.LineOpacity = 0.25
	}

	for _,idspec := range idspecs {
		f,err := db.LookupMostRecent(db.NewQuery().ByIdSpec(idspec))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		} else if f == nil {
			http.Error(w, fmt.Sprintf("idspec %s not found", idspec), http.StatusBadRequest)
			return
		}
		if af := airframes.Get(f.IcaoId); af != nil { f.Airframe = *af }

		if err := DescentPDFAddFlight(r, dp, f); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}

	DescentPDFFinalize(w,r,dp)
}

// }}}

// {{{ OutputApproachesAsPDF

// This is the old handler, useful for Class B
func OutputApproachesAsPDF(w http.ResponseWriter, r *http.Request, flights []*fdb.Flight) {
	colorscheme := FormValuePDFColorScheme(r)

	s,e,_ := widget.FormValueDateRange(r)

	// Default to the time range of the flights
	if time.Since(e) > time.Hour*24*365 {
		// assume undef
		s = time.Now().Add(30*24*time.Hour) // Implausibly in the future
		for _,f := range flights {
			fs,fe := f.Times()
			if fs.Before(s) { s = fs }
			if fe.After(e) { e = fe }
		}
		s = s.Add(-24*time.Hour)
		e = s.Add(24*time.Hour)
	}

	c := appengine.NewContext(r)
	metars,err := metar.FetchFromNOAA(urlfetch.Client(c), "KSFO",s.AddDate(0,0,-1), e.AddDate(0,0,1))
	if err != nil {
		// we don't need metar for FOIA data anyhow, so stop noaa derailing things
		// http.Error(w, err.Error(), http.StatusInternalServerError)
		// return
	}

	pdf := fpdf.NewApproachPdf(colorscheme)
	fStrs := []string{}
//outerLoop:
	for _,f := range flights {
		fStrs = append(fStrs, f.String())

		trackType,track := f.PreferredTrack([]string{"FOIA", "ADSB"})
		if track == nil || trackType == "" {
			continue
		}

		track.PostProcess() // Calculate groundspeed data for FOIA data

		if trackType == "ADSB" && metars != nil {
			track.AdjustAltitudes(metars)
		} else {
			for i,_ := range track {
				track[i].IndicatedAltitude = track[i].Altitude
			}
		}

		fpdf.DrawTrack(pdf, track, colorscheme)
	}

	if len(flights) == 1 {
		fpdf.DrawTitle(pdf, fmt.Sprintf("%s", fStrs[0]))
	}

	w.Header().Set("Content-Type", "application/pdf")
	if err := pdf.Output(w); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// }}}

// {{{ DescentPDFInit

func DescentPDFInit(w http.ResponseWriter, r *http.Request, numFlights int) *fpdf.DescentPdf {
	colorscheme := FormValuePDFColorScheme(r)
	colorscheme = fpdf.ByPlotKind

	lengthNM := 80
	if length := widget.FormValueInt64(r, "length"); length > 0 {
		lengthNM = int(length)
	}
	altitudeMax := 30000
	if alt := widget.FormValueInt64(r, "alt"); alt > 0 {
		altitudeMax = int(alt)
	}

	dp := fpdf.DescentPdf{
		ColorScheme: colorscheme,
		OriginPoint: sfo.KLatlongSFO,
		AltitudeMax: float64(altitudeMax),
		LengthNM:    float64(lengthNM),
		ToShow:      map[string]bool{"altitude":true, "groundspeed":true, "verticalspeed":true},
		ShowDebug:  (r.FormValue("debug") != ""),
		AveragingWindow: widget.FormValueDuration(r, "averagingwindow"),
	}

	if originPos,err := FormValueAirportLocation(r, "airportdest"); err == nil {
		dp.OriginPoint = originPos
		dp.OriginLabel = r.FormValue("airportdest")
	}

	if widget.FormValueCheckbox(r, "showaccelerations") {
		dp.ToShow["groundacceleration"],dp.ToShow["verticalacceleration"] = true,true
	}
	if widget.FormValueCheckbox(r, "showangleofinclination") {
		dp.ToShow["angleofinclination"] = true
	}

	dp.Init()

	if r.FormValue("asdepartures") != "" {
		dp.ReconfigureForDepartures()
	}

	dp.DrawFrames()

	if rep,err := getReport(r); err==nil && rep!=nil {
		dp.Caption += fmt.Sprintf("%d flights, %s\n", numFlights, rep.DescriptionText())
	}

	return &dp
}

// }}}
// {{{ DescentPDFAddFlight

func DescentPDFAddFlight(r *http.Request, dp *fpdf.DescentPdf, f *fdb.Flight) error {
	if t,err := flightToDescentTrack(r, f); err != nil {
		return err
	} else {
		if r.FormValue("dist") == "from" {
			dp.DrawTrackAsDistanceFromOrigin(t)
		} else if r.FormValue("asdepartures") != "" {
			dp.DrawTrackAsDistanceTravelledAlongPath(t)
		} else {
			dp.DrawTrackAsDistanceRemainingAlongPath(t)
		}

		if strings.Count(dp.Caption, "\n") < 4 {
			dp.Caption += fmt.Sprintf("%s %s\n", f.IdentString(), t.MediumString())
		}
	}

	return nil
}

// }}}
// {{{ DescentPDFFinalize

func DescentPDFFinalize(w http.ResponseWriter, r *http.Request, dp *fpdf.DescentPdf) {
	dp.DrawCaption()

	w.Header().Set("Content-Type", "application/pdf")
	if err := dp.Output(w); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// }}}

// {{{ flightToDescentTrack

// Resamples the track; does full post-processing; attempts altitude correction
// Extracts a bunch of args from the request (sample, DateRange widget)

func flightToDescentTrack(r *http.Request, f *fdb.Flight) (fdb.Track, error) {
	trackKeyName,track := f.PreferredTrack([]string{"ADSB", "MLAT", "FOIA", "FA", "fr24"})
	if track == nil {
		return nil, fmt.Errorf("no track found (saw %q)", f.ListTracks())
	}

	sampleRate := widget.FormValueDuration(r, "sample")
	if sampleRate == 0 { sampleRate = 15 * time.Second }
	track = track.SampleEvery(sampleRate, false)
	track.PostProcess()

	if trackKeyName == "FOIA" {
		track.AdjustAltitudes(nil) // FOIA track altitudes are already pressure-corrected

	} else {
		c := appengine.NewContext(r)

		// Default to the time range of the flights
		s,e,_ := widget.FormValueDateRange(r)
		if time.Since(e) > time.Hour*24*365 {
			s,e = f.Times()
			s = s.Add(-24*time.Hour)
			e = s.Add(24*time.Hour)
		}

		m,err := metar.FetchFromNOAA(urlfetch.Client(c), "KSFO",s.AddDate(0,0,-1), e.AddDate(0,0,1))
		if err != nil {
			return nil, err
		}
		track.AdjustAltitudes(m)
	}

	return track, nil
}

// }}}

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

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

// }}}
<|endoftext|>


<commit_before>package routes

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/takonews/takonews-api/app/controllers"
	"github.com/takonews/takonews-api/config"
)

func Router() *http.ServeMux {
	router := gin.Default()

	users := config.Config.Secret.Users
	fmt.Println(users)
	accounts := make(map[string]string)
	for _, v := range users {
		fmt.Println(v["name"])
		accounts[v["name"]] = v["password"]
	}
	fmt.Println(accounts)
	authorized := router.Group("/")
	authorized.Use(gin.BasicAuth(accounts))
	authorized.GET("/api/v2/articles", controllers.ArticleIndex)
	authorized.GET("/api/v2/articles/:articles_id", controllers.ArticleShow)

	var mux = http.NewServeMux()
	mux.Handle("/", router)

	return mux
}
<commit_msg>Update routes.go<commit_after>package routes

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/takonews/takonews-api/app/controllers"
	"github.com/takonews/takonews-api/config"
)

func Router() *http.ServeMux {
	router := gin.Default()

	users := config.Config.Secret.Users
	accounts := make(map[string]string)
	for _, v := range users {
		accounts[v["name"]] = v["password"]
	}
	authorized := router.Group("/")
	authorized.Use(gin.BasicAuth(accounts))
	authorized.GET("/api/v2/articles", controllers.ArticleIndex)
	authorized.GET("/api/v2/articles/:articles_id", controllers.ArticleShow)

	var mux = http.NewServeMux()
	mux.Handle("/", router)

	return mux
}
<|endoftext|>


<commit_before>package main

import (
	"time"
	"fmt"
)

// START_1 OMIT
type NodeOfInt struct {
	in    chan int                                 // Input channel
	cin   chan chan int                            // can be exchanged.

	f     func(int) int                            // Function
	cf    chan func(int) int                       // can be exchanged.

	out   chan int                                 // Output channel
	cout  chan chan int                            // can be exchanged.

	metain chan string
	cmetain chan chan string

	methods map[string]func() string

	metaout chan string
	cmetaout chan chan string

	close chan bool // OMIT
}
// END_1 OMIT

// START_2 OMIT
func (node *NodeOfInt) Start() {
	go func() {
		for { select {

		case in := <-node.in: node.out <- node.f(in) // Handle data (DEADLOCKS!) // HL

		case node.in = <-node.cin:   	            // Change input channel
		case node.f = <-node.cf: 		            // Change function
		case node.out = <-node.cout: 	            // Change output channel

		case methodName := <-node.metain:
			if fmeta, ok := node.methods[methodName]; ok {
				reply := fmeta()
				fmt.Printf("method: %v: %v\n", methodName, reply)
				node.metaout <-reply
			} else {
				fmt.Printf("Unknown method: %v\n", methodName)
			}

		case node.metain = <-node.cmetain: 	            // Change metain channel
		case node.metaout = <-node.cmetaout: 	            // Change metain channel

		case str := <-node.metaout:
			fmt.Printf("%v ", str)

		case <-node.close: return // OMIT
		}
		}}()
}
// END_2 OMIT

// START_3 OMIT
func NewNodeOfInt() *NodeOfInt {
	node := NodeOfInt{}
	node.in = make(chan int)
	node.cin = make(chan chan int)
	node.f = func(in int) int { return in }        // Default function returns input value
	node.cf = make(chan func(int) int)
	node.out = make(chan int)
	node.cout = make(chan chan int)

	node.metain = make(chan string)
	node.cmetain = make(chan chan string)

	node.methods = make(map[string]func() string)
	node.methods["print"] = func() string { return "123" }

	node.metaout = make(chan string)
	node.cmetaout = make(chan chan string)

	node.close = make(chan bool) // OMIT
	node.Start()
	return &node
}
// END_3 OMIT

// START_5 OMIT
func (node *NodeOfInt) Connect(nextNode *NodeOfInt) *NodeOfInt {
	node.cout <- nextNode.in
	node.cmetaout <- nextNode.metain
	return nextNode
}
// END_5 OMIT

// START_NODE_SETFUNC OMIT
func (node *NodeOfInt) SetFunc(f func(int) int) { node.cf <- f }
// END_NODE_SETFUNC OMIT

// START_PRINTF OMIT
func (node *NodeOfInt) Printf(format string) {
	go func() { for { select {
	case in := <-node.out: fmt.Printf(format, in)		// HL
	}}}()
}
// END_PRINTF OMIT

// START_ProduceAtMs OMIT
func (node *NodeOfInt) ProduceAtMs(n time.Duration) *NodeOfInt {
	go func() { for { select {
	default: node.in <- 0 }	               // Trigger permanently // HL
		time.Sleep(time.Millisecond * n)	      // with delay in ms // HL
	}}()
	return node
}
// END_ProduceAtMs OMIT

func main() {
	node_1, node_2 := NewNodeOfInt(), NewNodeOfInt()            // nodes' creation // HL
	var i int                                                   //
	node_1.SetFunc(func(in int) int { i++; return in + i })     //
	node_2.SetFunc(func(in int) int { return in * 2 })          //

	node_2.metain <- "print"

	node_1.Connect(node_2).Printf("%v ")                        // stream configuration // HL
	node_1.ProduceAtMs(50)                                      // sending data  // HL
	time.Sleep(time.Second)
	fmt.Println()

	node_2.SetFunc(func(in int) int { return in * 10 })         // change function // HL
	time.Sleep(time.Second)
}<commit_msg>CmdExec PRINT/LOG<commit_after>package main

import (
	"fmt"
	"strings"
	"time"
)

// START_1 OMIT
type NodeOfInt struct {
	in  chan int      // Input channel
	cin chan chan int // can be exchanged.

	f  func(int) int      // Function
	cf chan func(int) int // can be exchanged.

	out  chan int      // Output channel
	cout chan chan int // can be exchanged.

	name    string
	metain  chan string
	cmetain chan chan string

	methods map[string]func(string) string

	metaout  chan string
	cmetaout chan chan string

	close chan bool // OMIT
}

// END_1 OMIT

// START_2 OMIT
func (node *NodeOfInt) Start() {
	go func() {
		for {
			select {

			case in := <-node.in:
				node.out <- node.f(in) // Handle data (DEADLOCKS!) // HL

			case node.in = <-node.cin: // Change input channel
			case node.f = <-node.cf: // Change function
			case node.out = <-node.cout: // Change output channel

			case methodName := <-node.metain:
				cmdAndString := strings.SplitAfterN(methodName, " ", 2)
				/*
					fmt.Printf("methodName: %v\n", methodName)
					fmt.Printf("cmdAndString: %v\n", cmdAndString)
					fmt.Printf("len(cmdAndString): %v\n", len(cmdAndString))
					fmt.Printf("cmdAndString[0]: %v\n", cmdAndString[0])
					fmt.Printf("cmdAndString[1]: %v\n", cmdAndString[1])
				*/

				if fmeta, ok := node.methods[strings.Trim(cmdAndString[0], " ")]; ok {
					reply := fmeta(cmdAndString[1])
					//fmt.Printf("cmdAndString[0]: %v\ncmdAndString[1]: %v\n", cmdAndString[0], cmdAndString[1])
					node.metaout <- reply
				} else {
					fmt.Printf("UNKNOWN METHOD: %v%v\n", cmdAndString[0], cmdAndString[1])
				}

			case node.metain = <-node.cmetain: // Change metain channel
			case node.metaout = <-node.cmetaout: // Change metaout channel

			case str := <-node.metaout:
				fmt.Printf("%v ", str)

			case <-node.close:
				return // OMIT
			}
		}
	}()
}

// END_2 OMIT

// START_3 OMIT
func NewNodeOfInt(name string) *NodeOfInt {
	node := NodeOfInt{}
	node.in = make(chan int)
	node.cin = make(chan chan int)
	node.f = func(in int) int { return in } // Default function returns input value
	node.cf = make(chan func(int) int)
	node.out = make(chan int)
	node.cout = make(chan chan int)

	node.name = name
	node.metain = make(chan string)
	node.cmetain = make(chan chan string)

	node.methods = make(map[string]func(string) string)
	node.methods["print"] = func(str string) string {
		return fmt.Sprintf("print %v\nPRINT: (%T) \"%v\"", str, node, node.name)
	}
	node.methods["log"] = func(str string) string {
		return fmt.Sprintf("log %v\nLOG: (%T) \"%v\"", str, node, node.name)
	}

	node.metaout = make(chan string)
	node.cmetaout = make(chan chan string)

	node.close = make(chan bool) // OMIT
	node.Start()
	return &node
}

// END_3 OMIT

// START_5 OMIT
func (node *NodeOfInt) Connect(nextNode *NodeOfInt) *NodeOfInt {
	node.cout <- nextNode.in
	node.cmetaout <- nextNode.metain
	return nextNode
}

// END_5 OMIT

// START_NODE_SETFUNC OMIT
func (node *NodeOfInt) SetFunc(f func(int) int) { node.cf <- f }

// END_NODE_SETFUNC OMIT

// START_PRINTF OMIT
func (node *NodeOfInt) Printf(format string) {
	go func() {
		for {
			select {
			case in := <-node.out:
				fmt.Printf(format, in) // HL
			}
		}
	}()
}

// END_PRINTF OMIT

// START_PRINTF OMIT
func (node *NodeOfInt) MetaPrintf(format string) {
	go func() {
		for {
			select {
			case metaout := <-node.metaout:
				fmt.Printf(format, strings.SplitAfterN(metaout, "\n", 2)[1]) // HL
			}
		}
	}()
}

// END_PRINTF OMIT

// START_ProduceAtMs OMIT
func (node *NodeOfInt) ProduceAtMs(n time.Duration) *NodeOfInt {
	go func() {
		for {
			select {
			default:
				node.in <- 0
			} // Trigger permanently // HL
			time.Sleep(time.Millisecond * n) // with delay in ms // HL
		}
	}()
	return node
}

// END_ProduceAtMs OMIT

// START_NODE_SETFUNC OMIT
func (node *NodeOfInt) ExecCmd(cmd string) { node.metain <- cmd + " " }

// END_NODE_SETFUNC OMIT

func main() {
	node_1, node_2, node_3 := NewNodeOfInt("node_1"), NewNodeOfInt("node_2"), NewNodeOfInt("node_3") // nodes' creation // HL
	var i int                                                                                //
	node_1.SetFunc(func(in int) int { i++; return in + i })                                          //
	node_2.SetFunc(func(in int) int { return in * 2 })                                               //

	node_1.Connect(node_2).Connect(node_3).Printf("%v ") // stream configuration // HL
	node_1.ProduceAtMs(50)                               // sending data  // HL

	node_3.MetaPrintf("\n%v\n")

	node_1.ExecCmd("print")
	node_2.ExecCmd("log")

	time.Sleep(time.Second)
	fmt.Println()

	node_2.SetFunc(func(in int) int { return in * 10 }) // change function // HL
	time.Sleep(time.Second)
}
<|endoftext|>


<commit_before>// unico - Send Google+ activities to other networks
//
// Copyright 2011 The Unico 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 unico

import (
	"http"
	"io/ioutil"
	"json"
	"template"
	"tweetlib"
	"time"
	plus "google-api-go-client.googlecode.com/hg/plus/v1"
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"appengine/urlfetch"

	appengineSessions "gorilla.googlecode.com/hg/gorilla/appengine/sessions"
	"gorilla.googlecode.com/hg/gorilla/sessions"
)

var appConfig struct {
	FacebookAppId         string
	FacebookAppSecret     string
	GoogleClientId        string
	GoogleClientSecret    string
	TwitterConsumerKey    string
	TwitterConsumerSecret string
	AppHost               string
	AppDomain             string
	SessionStoreKey       string
}

var (
	templates = template.SetMust(template.ParseSetFiles(
		"404.html",
		"home.html",
		"header.html",
		"footer.html",
		"error.html"))
)

func init() {
	// Read configuration file
	content, err := ioutil.ReadFile("config.json")
	if err == nil {
		err = json.Unmarshal(content, &appConfig)
	}
	if err != nil {
		panic("Can't load configuration")
	}
	if appConfig.FacebookAppId == "" || appConfig.FacebookAppSecret == "" ||
		appConfig.GoogleClientId == "" || appConfig.GoogleClientSecret == "" ||
		appConfig.TwitterConsumerKey == "" || appConfig.TwitterConsumerSecret == "" ||
		appConfig.AppHost == "" || appConfig.AppDomain == "" ||
		appConfig.SessionStoreKey == "" {
		panic("Invalid configuration")
	}

	// Register the datastore and memcache session stores.
	sessions.SetStore("datastore", new(appengineSessions.DatastoreSessionStore))
	sessions.SetStore("memcache", new(appengineSessions.MemcacheSessionStore))

	// Set secret keys for the session stores.
	sessions.SetStoreKeys("datastore", []byte(appConfig.SessionStoreKey))
	sessions.SetStoreKeys("memcache", []byte(appConfig.SessionStoreKey))

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/twitter", twitterHandler)
	http.HandleFunc("/loginGoogle", loginGoogle)
	http.HandleFunc("/oauth2callback", googleCallbackHandler)
	http.HandleFunc("/fb", fbHandler)
	http.HandleFunc("/sync", syncHandler)
	http.HandleFunc("/deleteAccount", deleteAccountHandler)
	http.HandleFunc("/deleteFacebook", deleteFacebookHandler)
	http.HandleFunc("/deleteTwitter", deleteTwitterHandler)

}

// Displays the home page.
func homeHandler(w http.ResponseWriter, r *http.Request) {
	if appConfig.AppHost == "" {
		appConfig.AppHost = r.Host
	}
	c := appengine.NewContext(r)
	if r.Method != "GET" || r.URL.Path != "/" {
		serve404(w)
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	params := make(map[string]string)

	// Look for a browser cookie containing the user id
	// We can use this to load the user information
	userCookie, err := r.Cookie("userId")
	var user User
	if err == nil {
		user = loadUser(r, userCookie.Value)
	}
	c.Debugf("loadUser: %v\n", user)
	if user.Id != "" {
		if session, err := sessions.Session(r, "", "datastore"); err == nil {
			session["userID"] = user.Id
			f := sessions.Save(r, w)
			c.Debugf("saveSession: %v\n", f)
		}

		if user.TwitterId != "" {

			item := new(memcache.Item)
			item, err := memcache.Get(c, "pic"+user.Id)

			if err != nil {
				// get the user profile pic
				conf := &tweetlib.Config{
					ConsumerKey:    appConfig.TwitterConsumerKey,
					ConsumerSecret: appConfig.TwitterConsumerSecret}
				tok := &tweetlib.Token{
					OAuthSecret: user.TwitterOAuthSecret,
					OAuthToken:  user.TwitterOAuthToken}
				tr := &tweetlib.Transport{Config: conf,
					Token:     tok,
					Transport: &urlfetch.Transport{Context: c}}

				tl, _ := tweetlib.New(tr.Client())
				u, err := tl.Users.Show().UserId(user.TwitterId).Do()
				if err == nil {
					params["pic"] = u.ProfileImageUrl
					memcache.Add(c, &memcache.Item{Key: "pic" + user.Id, Value: []byte(u.ProfileImageUrl)})
				}

			} else {
				params["pic"] = string(item.Value)
			}

		}
		params["twitterid"] = user.TwitterId
		params["twittername"] = user.TwitterScreenName
		params["googleid"] = user.Id
		params["fbid"] = user.FBId
		params["fbname"] = user.FBName

		mu := memUser(c, user.Id)
		if mu.Name == "" {
			tr := transport(user)
			tr.Transport = &urlfetch.Transport{Context: c}
			p, _ := plus.New(tr.Client())
			person, err := p.People.Get(user.Id).Do()
			c.Debugf("Home people get: %v,(%v)\n", person, err)
			if err == nil {
				mu.Image = person.Image.Url
				mu.Name = person.DisplayName
				memUserSave(c, user.Id, mu)
			}

		}
		params["googleimg"] = mu.Image
		params["googlename"] = mu.Name

	}

	if err := templates.Execute(w, "home", params); err != nil {
		serveError(c, w, err)
		c.Errorf("%v", err)
		return
	}

}

func syncHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	q := datastore.NewQuery("User").
		Filter("Active=", true)

	for t := q.Run(c); ; {
		var u User
		_, err := t.Next(&u)
		if err == datastore.Done {
			break
		}
		if err != nil {
			serveError(c, w, err)
			return
		}

		syncStream(w, r, &u)
	}
	// schedule next run
}

func syncStream(w http.ResponseWriter, r *http.Request, user *User) {
	c := appengine.NewContext(r)
	tr := transport(*user)
	tr.Transport = &urlfetch.Transport{Context: c}

	httpClient := tr.Client()
	p, err := plus.New(httpClient)
	if err != nil {
		serveError(c, w, err)
		return
	}

	latest := user.GoogleLatest
	c.Debugf("syncStream: fetching for %s\n", user.Id)
	activityFeed, err := p.Activities.List(user.Id, "public").MaxResults(5).Do()
	if err != nil {
		c.Debugf("syncStream: activity fetch failed for %s. Err: %v\n", user.Id, err)
		return
	}

	for _, act := range activityFeed.Items {
		published, _ := time.Parse(time.RFC3339, act.Published)
		nPub := published.Nanoseconds()

		c.Debugf("syncStream: user: %s, nPub: %v, Latest: %v\n", user.Id, nPub, user.GoogleLatest)

		if nPub > user.GoogleLatest {
			if user.HasFacebook() {
				publishActivityToFacebook(w, r, act, user)
			}
			if user.HasTwitter() {
				publishActivityToTwitter(w, r, act, user)
			}
		}
		if nPub > latest {
			latest = nPub
		}
	}
	if latest > user.GoogleLatest {
		user.GoogleLatest = latest
		saveUser(r, user)
	}
}

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

	id := ""
	session, err := sessions.Session(r, "", "datastore")
	c1:= appengine.NewContext(r)
	c1.Debugf("deleteAccount: id=%v, session=%v, err=%v\n", session["userID"], session, err)
	if err == nil {
		if session["userID"] != nil {
		id = session["userID"].(string)
		}
	}
	if id != "" {
		user := loadUser(r, id)
		if user.Id != "" {
			c := appengine.NewContext(r)
			key := datastore.NewKey(c, "User", user.Id, 0, nil)
			datastore.Delete(c, key)
			session["userID"] = ""
			sessions.Save(r, w)
			memUserDelete(c, user.Id)
			memcache.Delete(c, "user" + user.Id)
			http.SetCookie(w, &http.Cookie{Name: "userId", Value: "", Domain: appConfig.AppDomain, Path: "/", MaxAge: -1})
		}
	}
	http.Redirect(w, r, "/", http.StatusFound)
}

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

	id := ""
	session, err := sessions.Session(r, "", "datastore")
	if err == nil {
		c := appengine.NewContext(r)
		c.Debugf("session: %v\n",  session)
		id = session["userID"].(string)
	}
	if id != "" {
		user := loadUser(r, id)
		if user.Id != "" {
			user.DisableTwitter()
			saveUser(r, &user)
		}
	}
	http.Redirect(w, r, "/", http.StatusFound)
}

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

	id := ""
	session, err := sessions.Session(r, "", "datastore")
	if err == nil {
		id = session["userID"].(string)
	}
	if id != "" {
		user := loadUser(r, id)
		if user.Id != "" {
			user.DisableFacebook()
			saveUser(r, &user)
		}
	}
	http.Redirect(w, r, "/", http.StatusFound)
}
<commit_msg>Allow AppDomain to be null (for Chrome testing)<commit_after>// unico - Send Google+ activities to other networks
//
// Copyright 2011 The Unico 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 unico

import (
	"http"
	"io/ioutil"
	"json"
	"template"
	"tweetlib"
	"time"
	plus "google-api-go-client.googlecode.com/hg/plus/v1"
	"appengine"
	"appengine/datastore"
	"appengine/memcache"
	"appengine/urlfetch"

	appengineSessions "gorilla.googlecode.com/hg/gorilla/appengine/sessions"
	"gorilla.googlecode.com/hg/gorilla/sessions"
)

var appConfig struct {
	FacebookAppId         string
	FacebookAppSecret     string
	GoogleClientId        string
	GoogleClientSecret    string
	TwitterConsumerKey    string
	TwitterConsumerSecret string
	AppHost               string
	AppDomain             string
	SessionStoreKey       string
}

var (
	templates = template.SetMust(template.ParseSetFiles(
		"404.html",
		"home.html",
		"header.html",
		"footer.html",
		"error.html"))
)

func init() {
	// Read configuration file
	content, err := ioutil.ReadFile("config.json")
	if err == nil {
		err = json.Unmarshal(content, &appConfig)
	}
	if err != nil {
		panic("Can't load configuration")
	}

	// Make sure every conf option has been completed, except
	// for AppDomain, because it is useful to test the app with
	// localhost but some browsers require localhost cookies
	// to have Domain as ""
	if appConfig.FacebookAppId == "" || appConfig.FacebookAppSecret == "" ||
		appConfig.GoogleClientId == "" || appConfig.GoogleClientSecret == "" ||
		appConfig.TwitterConsumerKey == "" || appConfig.TwitterConsumerSecret == "" ||
		appConfig.AppHost == "" ||
		appConfig.SessionStoreKey == "" {
		panic("Invalid configuration")
	}

	// Register the datastore and memcache session stores.
	sessions.SetStore("datastore", new(appengineSessions.DatastoreSessionStore))
	sessions.SetStore("memcache", new(appengineSessions.MemcacheSessionStore))

	// Set secret keys for the session stores.
	sessions.SetStoreKeys("datastore", []byte(appConfig.SessionStoreKey))
	sessions.SetStoreKeys("memcache", []byte(appConfig.SessionStoreKey))

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/twitter", twitterHandler)
	http.HandleFunc("/loginGoogle", loginGoogle)
	http.HandleFunc("/oauth2callback", googleCallbackHandler)
	http.HandleFunc("/fb", fbHandler)
	http.HandleFunc("/sync", syncHandler)
	http.HandleFunc("/deleteAccount", deleteAccountHandler)
	http.HandleFunc("/deleteFacebook", deleteFacebookHandler)
	http.HandleFunc("/deleteTwitter", deleteTwitterHandler)

}

// Displays the home page.
func homeHandler(w http.ResponseWriter, r *http.Request) {
	if appConfig.AppHost == "" {
		appConfig.AppHost = r.Host
	}
	c := appengine.NewContext(r)
	if r.Method != "GET" || r.URL.Path != "/" {
		serve404(w)
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	params := make(map[string]string)

	// Look for a browser cookie containing the user id
	// We can use this to load the user information
	userCookie, err := r.Cookie("userId")
	var user User
	if err == nil {
		user = loadUser(r, userCookie.Value)
	}
	c.Debugf("loadUser: %v\n", user)
	if user.Id != "" {
		if session, err := sessions.Session(r, "", "datastore"); err == nil {
			session["userID"] = user.Id
			f := sessions.Save(r, w)
			c.Debugf("saveSession: %v\n", f)
		}

		if user.TwitterId != "" {

			item := new(memcache.Item)
			item, err := memcache.Get(c, "pic"+user.Id)

			if err != nil {
				// get the user profile pic
				conf := &tweetlib.Config{
					ConsumerKey:    appConfig.TwitterConsumerKey,
					ConsumerSecret: appConfig.TwitterConsumerSecret}
				tok := &tweetlib.Token{
					OAuthSecret: user.TwitterOAuthSecret,
					OAuthToken:  user.TwitterOAuthToken}
				tr := &tweetlib.Transport{Config: conf,
					Token:     tok,
					Transport: &urlfetch.Transport{Context: c}}

				tl, _ := tweetlib.New(tr.Client())
				u, err := tl.Users.Show().UserId(user.TwitterId).Do()
				if err == nil {
					params["pic"] = u.ProfileImageUrl
					memcache.Add(c, &memcache.Item{Key: "pic" + user.Id, Value: []byte(u.ProfileImageUrl)})
				}

			} else {
				params["pic"] = string(item.Value)
			}

		}
		params["twitterid"] = user.TwitterId
		params["twittername"] = user.TwitterScreenName
		params["googleid"] = user.Id
		params["fbid"] = user.FBId
		params["fbname"] = user.FBName

		mu := memUser(c, user.Id)
		if mu.Name == "" {
			tr := transport(user)
			tr.Transport = &urlfetch.Transport{Context: c}
			p, _ := plus.New(tr.Client())
			person, err := p.People.Get(user.Id).Do()
			c.Debugf("Home people get: %v,(%v)\n", person, err)
			if err == nil {
				mu.Image = person.Image.Url
				mu.Name = person.DisplayName
				memUserSave(c, user.Id, mu)
			}

		}
		params["googleimg"] = mu.Image
		params["googlename"] = mu.Name

	}

	if err := templates.Execute(w, "home", params); err != nil {
		serveError(c, w, err)
		c.Errorf("%v", err)
		return
	}

}

func syncHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	q := datastore.NewQuery("User").
		Filter("Active=", true)

	for t := q.Run(c); ; {
		var u User
		_, err := t.Next(&u)
		if err == datastore.Done {
			break
		}
		if err != nil {
			serveError(c, w, err)
			return
		}

		syncStream(w, r, &u)
	}
	// schedule next run
}

func syncStream(w http.ResponseWriter, r *http.Request, user *User) {
	c := appengine.NewContext(r)
	tr := transport(*user)
	tr.Transport = &urlfetch.Transport{Context: c}

	httpClient := tr.Client()
	p, err := plus.New(httpClient)
	if err != nil {
		serveError(c, w, err)
		return
	}

	latest := user.GoogleLatest
	c.Debugf("syncStream: fetching for %s\n", user.Id)
	activityFeed, err := p.Activities.List(user.Id, "public").MaxResults(5).Do()
	if err != nil {
		c.Debugf("syncStream: activity fetch failed for %s. Err: %v\n", user.Id, err)
		return
	}

	for _, act := range activityFeed.Items {
		published, _ := time.Parse(time.RFC3339, act.Published)
		nPub := published.Nanoseconds()

		c.Debugf("syncStream: user: %s, nPub: %v, Latest: %v\n", user.Id, nPub, user.GoogleLatest)

		if nPub > user.GoogleLatest {
			if user.HasFacebook() {
				publishActivityToFacebook(w, r, act, user)
			}
			if user.HasTwitter() {
				publishActivityToTwitter(w, r, act, user)
			}
		}
		if nPub > latest {
			latest = nPub
		}
	}
	if latest > user.GoogleLatest {
		user.GoogleLatest = latest
		saveUser(r, user)
	}
}

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

	id := ""
	session, err := sessions.Session(r, "", "datastore")
	c1:= appengine.NewContext(r)
	c1.Debugf("deleteAccount: id=%v, session=%v, err=%v\n", session["userID"], session, err)
	if err == nil {
		if session["userID"] != nil {
		id = session["userID"].(string)
		}
	}
	if id != "" {
		user := loadUser(r, id)
		if user.Id != "" {
			c := appengine.NewContext(r)
			key := datastore.NewKey(c, "User", user.Id, 0, nil)
			datastore.Delete(c, key)
			session["userID"] = ""
			sessions.Save(r, w)
			memUserDelete(c, user.Id)
			memcache.Delete(c, "user" + user.Id)
			http.SetCookie(w, &http.Cookie{Name: "userId", Value: "", Domain: appConfig.AppDomain, Path: "/", MaxAge: -1})
		}
	}
	http.Redirect(w, r, "/", http.StatusFound)
}

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

	id := ""
	session, err := sessions.Session(r, "", "datastore")
	if err == nil {
		c := appengine.NewContext(r)
		c.Debugf("session: %v\n",  session)
		id = session["userID"].(string)
	}
	if id != "" {
		user := loadUser(r, id)
		if user.Id != "" {
			user.DisableTwitter()
			saveUser(r, &user)
		}
	}
	http.Redirect(w, r, "/", http.StatusFound)
}

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

	id := ""
	session, err := sessions.Session(r, "", "datastore")
	if err == nil {
		id = session["userID"].(string)
	}
	if id != "" {
		user := loadUser(r, id)
		if user.Id != "" {
			user.DisableFacebook()
			saveUser(r, &user)
		}
	}
	http.Redirect(w, r, "/", http.StatusFound)
}
<|endoftext|>