<commit_before>package htm

import (
	//"math"
	"bytes"
)

//Entries are positions of non-zero values
type SparseEntry struct {
	Row int
	Col int
}

//Sparse binary matrix stores indexes of non-zero entries in matrix
//to conserve space
type SparseBinaryMatrix struct {
	Width             int
	Height            int
	TotalNonZeroCount int
	Entries           []SparseEntry
}

//Create new sparse binary matrix of specified size
func NewSparseBinaryMatrix(height, width int) *SparseBinaryMatrix {
	m := &SparseBinaryMatrix{}
	m.Height = height
	m.Width = width
	//Intialize with 70% sparsity
	//m.Entries = make([]SparseEntry, int(math.Ceil(width*height*0.3)))
	return m
}

//Create sparse binary matrix from specified dense matrix
func NewSparseBinaryMatrixFromDense(values [][]bool) *SparseBinaryMatrix {
	if len(values) < 1 {
		panic("No values specified.")
	}
	m := &SparseBinaryMatrix{}
	m.Height = len(values)
	m.Width = len(values[0])

	for r := 0; r < m.Height; r++ {
		m.SetRowFromDense(r, values[r])
	}

	return m
}

// Creates a sparse binary matrix from specified integer array
// (any values greater than 0 are true)
func NewSparseBinaryMatrixFromInts(values [][]int) *SparseBinaryMatrix {
	if len(values) < 1 {
		panic("No values specified.")
	}

	m := &SparseBinaryMatrix{}
	m.Height = len(values)
	m.Width = len(values[0])

	for r := 0; r < m.Height; r++ {
		for c := 0; c < m.Width; c++ {
			if values[r][c] > 0 {
				m.Set(r, c, true)
			}
		}
	}

	return m
}

// func NewRandSparseBinaryMatrix() *SparseBinaryMatrix {
// }

// func (sm *SparseBinaryMatrix) Resize(width int, height int) {
// }

//Returns flattend dense represenation
func (sm *SparseBinaryMatrix) Flatten() []bool {
	result := make([]bool, sm.Height*sm.Width)
	for _, val := range sm.Entries {
		result[(val.Row*sm.Width)+val.Col] = true
	}
	return result
}

//Get value at col,row position
func (sm *SparseBinaryMatrix) Get(row int, col int) bool {
	for _, val := range sm.Entries {
		if val.Row == row && val.Col == col {
			return true
		}
	}
	return false
}

func (sm *SparseBinaryMatrix) delete(row int, col int) {
	for idx, val := range sm.Entries {
		if val.Row == row && val.Col == col {
			sm.Entries = append(sm.Entries[:idx], sm.Entries[idx+1:]...)
			break
		}
	}

}

//Set value at row,col position
func (sm *SparseBinaryMatrix) Set(row int, col int, value bool) {
	if !value {
		sm.delete(row, col)
		return
	}

	if sm.Get(row, col) {
		return
	}

	newEntry := SparseEntry{}
	newEntry.Col = col
	newEntry.Row = row
	sm.Entries = append(sm.Entries, newEntry)

}

//Replaces specified row with values, assumes values is ordered
//correctly
func (sm *SparseBinaryMatrix) ReplaceRow(row int, values []bool) {
	sm.validateRowCol(row, len(values))

	for i := 0; i < sm.Width; i++ {
		sm.Set(row, i, values[i])
	}
}

//Replaces row with true values at specified indices
func (sm *SparseBinaryMatrix) ReplaceRowByIndices(row int, indices []int) {
	sm.validateRow(row)

	for i := 0; i < sm.Width; i++ {
		val := false
		for x := 0; x < len(indices); x++ {
			if i == indices[x] {
				val = true
				break
			}
		}
		sm.Set(row, i, val)
	}
}

//Returns dense row
func (sm *SparseBinaryMatrix) GetDenseRow(row int) []bool {
	sm.validateRow(row)
	result := make([]bool, sm.Width)

	for i := 0; i < len(sm.Entries); i++ {
		if sm.Entries[i].Row == row {
			result[sm.Entries[i].Col] = true
		}
	}

	return result
}

//Returns a rows "on" indices
func (sm *SparseBinaryMatrix) GetRowIndices(row int) []int {
	result := []int{}
	for i := 0; i < len(sm.Entries); i++ {
		if sm.Entries[i].Row == row {
			result = append(result, sm.Entries[i].Col)
		}
	}
	return result
}

//Sets a sparse row from dense representation
func (sm *SparseBinaryMatrix) SetRowFromDense(row int, denseRow []bool) {
	sm.validateRowCol(row, len(denseRow))
	for i := 0; i < sm.Width; i++ {
		sm.Set(row, i, denseRow[i])
	}
}

//In a normal matrix this would be multiplication in binary terms
//we just and then sum the true entries
func (sm *SparseBinaryMatrix) RowAndSum(row []bool) []int {
	sm.validateCol(len(row))
	result := make([]int, sm.Height)

	for _, val := range sm.Entries {
		if row[val.Col] {
			result[val.Row]++
		}
	}

	return result
}

//Returns # of rows with at least 1 true value
func (sm *SparseBinaryMatrix) TotatTrueRows() int {
	var hitRows []int
	for _, val := range sm.Entries {
		if !ContainsInt(val.Row, hitRows) {
			hitRows = append(hitRows, val.Row)
		}
	}
	return len(hitRows)
}

//Returns # of cols with at least 1 true value
func (sm *SparseBinaryMatrix) TotalTrueCols() int {
	var hitCols []int
	for _, val := range sm.Entries {
		if !ContainsInt(val.Col, hitCols) {
			hitCols = append(hitCols, val.Col)
		}
	}
	return len(hitCols)
}

// Ors 2 matrices
func (sm *SparseBinaryMatrix) Or(sm2 *SparseBinaryMatrix) *SparseBinaryMatrix {
	result := NewSparseBinaryMatrix(sm.Height, sm.Width)

	for _, val := range sm.Entries {
		result.Set(val.Row, val.Col, true)
	}

	for _, val := range sm2.Entries {
		result.Set(val.Row, val.Col, true)
	}

	return result
}

//Clears  all entries
func (sm *SparseBinaryMatrix) Clear() {
	sm.Entries = nil
}

//Copys a matrix
func (sm *SparseBinaryMatrix) Copy() *SparseBinaryMatrix {
	result := new(SparseBinaryMatrix)
	result.Width = sm.Width
	result.Height = sm.Height
	result.Entries = make([]SparseEntry, len(sm.Entries))
	for idx, val := range sm.Entries {
		result.Entries[idx] = val
	}

	return result
}

func (sm *SparseBinaryMatrix) ToString() string {
	var buffer bytes.Buffer

	for r := 0; r < sm.Height; r++ {
		for c := 0; c < sm.Width; c++ {
			if sm.Get(r, c) {
				buffer.WriteByte('1')
			} else {
				buffer.WriteByte('0')
			}
		}
		buffer.WriteByte('\n')
	}

	return buffer.String()
}

func (sm *SparseBinaryMatrix) validateCol(col int) {
	if col > sm.Width {
		panic("Specified row is wider than matrix.")
	}
}

func (sm *SparseBinaryMatrix) validateRow(row int) {
	if row > sm.Height {
		panic("Specified row is out of bounds.")
	}
}

func (sm *SparseBinaryMatrix) validateRowCol(row int, col int) {
	sm.validateCol(col)
	sm.validateRow(row)
}
<commit_msg>added fillrow function<commit_after>package htm

import (
	//"math"
	"bytes"
)

//Entries are positions of non-zero values
type SparseEntry struct {
	Row int
	Col int
}

//Sparse binary matrix stores indexes of non-zero entries in matrix
//to conserve space
type SparseBinaryMatrix struct {
	Width             int
	Height            int
	TotalNonZeroCount int
	Entries           []SparseEntry
}

//Create new sparse binary matrix of specified size
func NewSparseBinaryMatrix(height, width int) *SparseBinaryMatrix {
	m := &SparseBinaryMatrix{}
	m.Height = height
	m.Width = width
	//Intialize with 70% sparsity
	//m.Entries = make([]SparseEntry, int(math.Ceil(width*height*0.3)))
	return m
}

//Create sparse binary matrix from specified dense matrix
func NewSparseBinaryMatrixFromDense(values [][]bool) *SparseBinaryMatrix {
	if len(values) < 1 {
		panic("No values specified.")
	}
	m := &SparseBinaryMatrix{}
	m.Height = len(values)
	m.Width = len(values[0])

	for r := 0; r < m.Height; r++ {
		m.SetRowFromDense(r, values[r])
	}

	return m
}

// Creates a sparse binary matrix from specified integer array
// (any values greater than 0 are true)
func NewSparseBinaryMatrixFromInts(values [][]int) *SparseBinaryMatrix {
	if len(values) < 1 {
		panic("No values specified.")
	}

	m := &SparseBinaryMatrix{}
	m.Height = len(values)
	m.Width = len(values[0])

	for r := 0; r < m.Height; r++ {
		for c := 0; c < m.Width; c++ {
			if values[r][c] > 0 {
				m.Set(r, c, true)
			}
		}
	}

	return m
}

// func NewRandSparseBinaryMatrix() *SparseBinaryMatrix {
// }

// func (sm *SparseBinaryMatrix) Resize(width int, height int) {
// }

//Returns flattend dense represenation
func (sm *SparseBinaryMatrix) Flatten() []bool {
	result := make([]bool, sm.Height*sm.Width)
	for _, val := range sm.Entries {
		result[(val.Row*sm.Width)+val.Col] = true
	}
	return result
}

//Get value at col,row position
func (sm *SparseBinaryMatrix) Get(row int, col int) bool {
	for _, val := range sm.Entries {
		if val.Row == row && val.Col == col {
			return true
		}
	}
	return false
}

func (sm *SparseBinaryMatrix) delete(row int, col int) {
	for idx, val := range sm.Entries {
		if val.Row == row && val.Col == col {
			sm.Entries = append(sm.Entries[:idx], sm.Entries[idx+1:]...)
			break
		}
	}

}

//Set value at row,col position
func (sm *SparseBinaryMatrix) Set(row int, col int, value bool) {
	if !value {
		sm.delete(row, col)
		return
	}

	if sm.Get(row, col) {
		return
	}

	newEntry := SparseEntry{}
	newEntry.Col = col
	newEntry.Row = row
	sm.Entries = append(sm.Entries, newEntry)

}

//Replaces specified row with values, assumes values is ordered
//correctly
func (sm *SparseBinaryMatrix) ReplaceRow(row int, values []bool) {
	sm.validateRowCol(row, len(values))

	for i := 0; i < sm.Width; i++ {
		sm.Set(row, i, values[i])
	}
}

//Replaces row with true values at specified indices
func (sm *SparseBinaryMatrix) ReplaceRowByIndices(row int, indices []int) {
	sm.validateRow(row)

	for i := 0; i < sm.Width; i++ {
		val := false
		for x := 0; x < len(indices); x++ {
			if i == indices[x] {
				val = true
				break
			}
		}
		sm.Set(row, i, val)
	}
}

//Returns dense row
func (sm *SparseBinaryMatrix) GetDenseRow(row int) []bool {
	sm.validateRow(row)
	result := make([]bool, sm.Width)

	for i := 0; i < len(sm.Entries); i++ {
		if sm.Entries[i].Row == row {
			result[sm.Entries[i].Col] = true
		}
	}

	return result
}

//Returns a rows "on" indices
func (sm *SparseBinaryMatrix) GetRowIndices(row int) []int {
	result := []int{}
	for i := 0; i < len(sm.Entries); i++ {
		if sm.Entries[i].Row == row {
			result = append(result, sm.Entries[i].Col)
		}
	}
	return result
}

//Sets a sparse row from dense representation
func (sm *SparseBinaryMatrix) SetRowFromDense(row int, denseRow []bool) {
	sm.validateRowCol(row, len(denseRow))
	for i := 0; i < sm.Width; i++ {
		sm.Set(row, i, denseRow[i])
	}
}

//In a normal matrix this would be multiplication in binary terms
//we just and then sum the true entries
func (sm *SparseBinaryMatrix) RowAndSum(row []bool) []int {
	sm.validateCol(len(row))
	result := make([]int, sm.Height)

	for _, val := range sm.Entries {
		if row[val.Col] {
			result[val.Row]++
		}
	}

	return result
}

//Returns # of rows with at least 1 true value
func (sm *SparseBinaryMatrix) TotatTrueRows() int {
	var hitRows []int
	for _, val := range sm.Entries {
		if !ContainsInt(val.Row, hitRows) {
			hitRows = append(hitRows, val.Row)
		}
	}
	return len(hitRows)
}

//Returns # of cols with at least 1 true value
func (sm *SparseBinaryMatrix) TotalTrueCols() int {
	var hitCols []int
	for _, val := range sm.Entries {
		if !ContainsInt(val.Col, hitCols) {
			hitCols = append(hitCols, val.Col)
		}
	}
	return len(hitCols)
}

// Ors 2 matrices
func (sm *SparseBinaryMatrix) Or(sm2 *SparseBinaryMatrix) *SparseBinaryMatrix {
	result := NewSparseBinaryMatrix(sm.Height, sm.Width)

	for _, val := range sm.Entries {
		result.Set(val.Row, val.Col, true)
	}

	for _, val := range sm2.Entries {
		result.Set(val.Row, val.Col, true)
	}

	return result
}

//Clears  all entries
func (sm *SparseBinaryMatrix) Clear() {
	sm.Entries = nil
}

//Fills specified row with specified value
func (sm *SparseBinaryMatrix) FillRow(row int, val bool) {
	for j := 0; j < sm.Width; j++ {
		sm.Set(row, j, val)
	}
}

//Copys a matrix
func (sm *SparseBinaryMatrix) Copy() *SparseBinaryMatrix {
	result := new(SparseBinaryMatrix)
	result.Width = sm.Width
	result.Height = sm.Height
	result.Entries = make([]SparseEntry, len(sm.Entries))
	for idx, val := range sm.Entries {
		result.Entries[idx] = val
	}

	return result
}

func (sm *SparseBinaryMatrix) ToString() string {
	var buffer bytes.Buffer

	for r := 0; r < sm.Height; r++ {
		for c := 0; c < sm.Width; c++ {
			if sm.Get(r, c) {
				buffer.WriteByte('1')
			} else {
				buffer.WriteByte('0')
			}
		}
		buffer.WriteByte('\n')
	}

	return buffer.String()
}

func (sm *SparseBinaryMatrix) validateCol(col int) {
	if col > sm.Width {
		panic("Specified row is wider than matrix.")
	}
}

func (sm *SparseBinaryMatrix) validateRow(row int) {
	if row > sm.Height {
		panic("Specified row is out of bounds.")
	}
}

func (sm *SparseBinaryMatrix) validateRowCol(row int, col int) {
	sm.validateCol(col)
	sm.validateRow(row)
}
<|endoftext|>


<commit_before>package pq

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"

	"github.com/oursky/ourd/oddb"
)

var subscribeListenOnce sync.Once
var appEventChannelsMap map[string][]chan oddb.RecordEvent

// Assume all app resist on one Database
func (c *conn) Subscribe(recordEventChan chan oddb.RecordEvent) error {
	channels := appEventChannelsMap[c.appName]
	appEventChannelsMap[c.appName] = append(channels, recordEventChan)

	// TODO(limouren): Seems a start-up time config would be better?
	subscribeListenOnce.Do(func() {
		go newRecordListener(c.option).Listen()
	})

	return nil
}

func emit(n *notification) {
	channels := appEventChannelsMap[n.AppName]
	for _, channel := range channels {
		go func(ch chan oddb.RecordEvent) {
			ch <- oddb.RecordEvent{
				Record: &n.Record,
				Event:  n.ChangeEvent,
			}
		}(channel)
	}
}

// the channel to listen for record changes
const recordChangeChannel = "record_change"

type notification struct {
	AppName     string
	ChangeEvent oddb.RecordHookEvent
	Record      oddb.Record
}

type rawNotification struct {
	AppName    string
	Op         string
	RecordType string
	Record     []byte
}

type recordListener struct {
	option string
	db     *sqlx.DB
}

func newRecordListener(option string) *recordListener {
	return &recordListener{
		option: option,
		db:     sqlx.MustOpen("postgres", option),
	}
}

func (l *recordListener) Listen() {
	eventCallback := func(event pq.ListenerEventType, err error) {
		if err != nil {
			log.WithField("err", err).Errorf("pq/listener: Received an error")
		} else {
			log.WithField("event", event).Infof("pq/listener: Received an event")
		}
	}

	listener := pq.NewListener(
		l.option,
		10*time.Second,
		time.Minute,
		eventCallback)

	if err := listener.Listen(recordChangeChannel); err != nil {
		log.WithFields(log.Fields{
			"channel": recordChangeChannel,
			"err":     err,
		}).Errorln("pq/listener: got an err while trying to listen")
		return
	}

	log.Infof("pq/listener: Listening to %s...", recordChangeChannel)

	for {
		select {
		case pqNotification := <-listener.Notify:
			log.WithField("pqNotification", pqNotification).Infoln("Received a notify")

			n := notification{}
			if err := l.fetchNotification(pqNotification.Extra, &n); err != nil {
				log.WithFields(log.Fields{
					"pqNotification": pqNotification,
					"err":            err,
				}).Errorln("pq/listener: failed to fetch notification")

				continue
			}

			emit(&n)

			l.deleteNotification(pqNotification.Extra)
		case <-time.After(60 * time.Second):
			go func() {
				if err := listener.Ping(); err != nil {
					log.WithField("err", err).Errorln("pq/listener: got an err while pinging connection")
				}
			}()
			log.Infoln("pq/listener: no notification for 60 seconds...")
		}
	}
}

// NOTE(limouren): pending_notification.id is integer in database.
func (l *recordListener) fetchNotification(notificationID string, n *notification) error {
	var rawNoti rawNotification
	err := l.db.QueryRowx("SELECT op, appname, recordtype, record FROM pending_notification WHERE id = $1", notificationID).
		StructScan(&rawNoti)
	if err != nil {
		log.WithFields(log.Fields{
			"notificationID": notificationID,
			"err":            err,
		}).Errorln("Failed to fetch pending notification")
		return err
	}

	if err := parseNotification(&rawNoti, n); err != nil {
		return err
	}

	return nil
}

func (l *recordListener) deleteNotification(notificationID string) {
	result, err := l.db.Exec("DELETE FROM pending_notification WHERE id = $1", notificationID)
	if err != nil {
		log.WithFields(log.Fields{
			"notificationID": notificationID,
			"err":            err,
		}).Errorln("Failed to delete notification")

		return
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		log.WithFields(log.Fields{
			"notificationID": notificationID,
			"err":            err,
			"rowsAffected":   rowsAffected,
		}).Errorln("More than one notification deleted")

		return
	}

	if rowsAffected != 1 {
		log.WithFields(log.Fields{
			"notificationID": notificationID,
			"rowsAffected":   rowsAffected,
		}).Errorln("Zero or more than one notification deleted")
	}
}

func parseNotification(raw *rawNotification, n *notification) error {
	if err := parseAppName(raw.AppName, &n.AppName); err != nil {
		return err
	}

	if err := parseChangeEvent(raw.Op, &n.ChangeEvent); err != nil {
		return err
	}

	if err := parseRecordData(raw.Record, &n.Record); err != nil {
		return err
	}
	n.Record.ID.Type = raw.RecordType

	return nil
}

func parseAppName(rawAppName string, appName *string) error {
	if !strings.HasPrefix(rawAppName, "app_") {
		return fmt.Errorf("Invalid AppName = %v", rawAppName)
	}
	*appName = rawAppName[4:]
	return nil
}

func parseChangeEvent(rawOp string, changeEvent *oddb.RecordHookEvent) error {
	switch rawOp {
	case "INSERT":
		*changeEvent = oddb.RecordCreated
	case "UPDATE":
		*changeEvent = oddb.RecordUpdated
	case "DELETE":
		*changeEvent = oddb.RecordDeleted
	default:
		return fmt.Errorf("Unrecongized Op = %v", rawOp)
	}

	return nil
}

func parseRecordData(data []byte, record *oddb.Record) error {
	recordData := map[string]interface{}{}
	if err := json.Unmarshal(data, &recordData); err != nil {
		return fmt.Errorf("invalid json: %v", err)
	}

	recordID, _ := recordData["_id"].(string)
	rawDatabaseID, _ := recordData["_database_id"].(string)
	rawOwnerID, _ := recordData["_owner_id"].(string)

	if recordID == "" || rawDatabaseID == "" || rawOwnerID == "" {
		return errors.New(`missing key "_id", "_database_id" or "_owner_id"`)
	}

	for key := range recordData {
		if key[0] == '_' {
			delete(recordData, key)
		}
	}

	record.ID.Key = recordID
	record.Data = recordData
	record.DatabaseID = rawDatabaseID
	record.OwnerID = rawOwnerID

	return nil
}

func init() {
	appEventChannelsMap = map[string][]chan oddb.RecordEvent{}
}
<commit_msg>Peace delivery by limouren, removing meaningless log<commit_after>package pq

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/jmoiron/sqlx"
	"github.com/lib/pq"

	"github.com/oursky/ourd/oddb"
)

var subscribeListenOnce sync.Once
var appEventChannelsMap map[string][]chan oddb.RecordEvent

// Assume all app resist on one Database
func (c *conn) Subscribe(recordEventChan chan oddb.RecordEvent) error {
	channels := appEventChannelsMap[c.appName]
	appEventChannelsMap[c.appName] = append(channels, recordEventChan)

	// TODO(limouren): Seems a start-up time config would be better?
	subscribeListenOnce.Do(func() {
		go newRecordListener(c.option).Listen()
	})

	return nil
}

func emit(n *notification) {
	channels := appEventChannelsMap[n.AppName]
	for _, channel := range channels {
		go func(ch chan oddb.RecordEvent) {
			ch <- oddb.RecordEvent{
				Record: &n.Record,
				Event:  n.ChangeEvent,
			}
		}(channel)
	}
}

// the channel to listen for record changes
const recordChangeChannel = "record_change"

type notification struct {
	AppName     string
	ChangeEvent oddb.RecordHookEvent
	Record      oddb.Record
}

type rawNotification struct {
	AppName    string
	Op         string
	RecordType string
	Record     []byte
}

type recordListener struct {
	option string
	db     *sqlx.DB
}

func newRecordListener(option string) *recordListener {
	return &recordListener{
		option: option,
		db:     sqlx.MustOpen("postgres", option),
	}
}

func (l *recordListener) Listen() {
	eventCallback := func(event pq.ListenerEventType, err error) {
		if err != nil {
			log.WithField("err", err).Errorf("pq/listener: Received an error")
		} else {
			log.WithField("event", event).Infof("pq/listener: Received an event")
		}
	}

	listener := pq.NewListener(
		l.option,
		10*time.Second,
		time.Minute,
		eventCallback)

	if err := listener.Listen(recordChangeChannel); err != nil {
		log.WithFields(log.Fields{
			"channel": recordChangeChannel,
			"err":     err,
		}).Errorln("pq/listener: got an err while trying to listen")
		return
	}

	log.Infof("pq/listener: Listening to %s...", recordChangeChannel)

	for {
		select {
		case pqNotification := <-listener.Notify:
			log.WithField("pqNotification", pqNotification).Infoln("Received a notify")

			n := notification{}
			if err := l.fetchNotification(pqNotification.Extra, &n); err != nil {
				log.WithFields(log.Fields{
					"pqNotification": pqNotification,
					"err":            err,
				}).Errorln("pq/listener: failed to fetch notification")

				continue
			}

			emit(&n)

			l.deleteNotification(pqNotification.Extra)
		case <-time.After(60 * time.Second):
			go func() {
				if err := listener.Ping(); err != nil {
					log.WithField("err", err).Errorln("pq/listener: got an err while pinging connection")
				}
			}()
		}
	}
}

// NOTE(limouren): pending_notification.id is integer in database.
func (l *recordListener) fetchNotification(notificationID string, n *notification) error {
	var rawNoti rawNotification
	err := l.db.QueryRowx("SELECT op, appname, recordtype, record FROM pending_notification WHERE id = $1", notificationID).
		StructScan(&rawNoti)
	if err != nil {
		log.WithFields(log.Fields{
			"notificationID": notificationID,
			"err":            err,
		}).Errorln("Failed to fetch pending notification")
		return err
	}

	if err := parseNotification(&rawNoti, n); err != nil {
		return err
	}

	return nil
}

func (l *recordListener) deleteNotification(notificationID string) {
	result, err := l.db.Exec("DELETE FROM pending_notification WHERE id = $1", notificationID)
	if err != nil {
		log.WithFields(log.Fields{
			"notificationID": notificationID,
			"err":            err,
		}).Errorln("Failed to delete notification")

		return
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		log.WithFields(log.Fields{
			"notificationID": notificationID,
			"err":            err,
			"rowsAffected":   rowsAffected,
		}).Errorln("More than one notification deleted")

		return
	}

	if rowsAffected != 1 {
		log.WithFields(log.Fields{
			"notificationID": notificationID,
			"rowsAffected":   rowsAffected,
		}).Errorln("Zero or more than one notification deleted")
	}
}

func parseNotification(raw *rawNotification, n *notification) error {
	if err := parseAppName(raw.AppName, &n.AppName); err != nil {
		return err
	}

	if err := parseChangeEvent(raw.Op, &n.ChangeEvent); err != nil {
		return err
	}

	if err := parseRecordData(raw.Record, &n.Record); err != nil {
		return err
	}
	n.Record.ID.Type = raw.RecordType

	return nil
}

func parseAppName(rawAppName string, appName *string) error {
	if !strings.HasPrefix(rawAppName, "app_") {
		return fmt.Errorf("Invalid AppName = %v", rawAppName)
	}
	*appName = rawAppName[4:]
	return nil
}

func parseChangeEvent(rawOp string, changeEvent *oddb.RecordHookEvent) error {
	switch rawOp {
	case "INSERT":
		*changeEvent = oddb.RecordCreated
	case "UPDATE":
		*changeEvent = oddb.RecordUpdated
	case "DELETE":
		*changeEvent = oddb.RecordDeleted
	default:
		return fmt.Errorf("Unrecongized Op = %v", rawOp)
	}

	return nil
}

func parseRecordData(data []byte, record *oddb.Record) error {
	recordData := map[string]interface{}{}
	if err := json.Unmarshal(data, &recordData); err != nil {
		return fmt.Errorf("invalid json: %v", err)
	}

	recordID, _ := recordData["_id"].(string)
	rawDatabaseID, _ := recordData["_database_id"].(string)
	rawOwnerID, _ := recordData["_owner_id"].(string)

	if recordID == "" || rawDatabaseID == "" || rawOwnerID == "" {
		return errors.New(`missing key "_id", "_database_id" or "_owner_id"`)
	}

	for key := range recordData {
		if key[0] == '_' {
			delete(recordData, key)
		}
	}

	record.ID.Key = recordID
	record.Data = recordData
	record.DatabaseID = rawDatabaseID
	record.OwnerID = rawOwnerID

	return nil
}

func init() {
	appEventChannelsMap = map[string][]chan oddb.RecordEvent{}
}
<|endoftext|>


<commit_before>package logger

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

type Logger struct {
	name    string
	out     io.Writer
	outMu   sync.Mutex
	Enabled bool
}

var TimeFormat = "2006-01-02T15:04:05.000000Z07:00"

func NewLogger(name string, out io.Writer, enabled bool) *Logger {
	return &Logger{name: name, out: out, Enabled: enabled}
}

func (l *Logger) Printf(message string, values ...interface{}) {
	if l.Enabled {
		l.printf(message, values...)
	}
}

func (l *Logger) Println(values ...interface{}) {
	if l.Enabled {
		l.println(values...)
	}
}

func (l *Logger) printf(message string, values ...interface{}) {
	l.outMu.Lock()
	defer l.outMu.Unlock()
	date := time.Now().Format(TimeFormat)
	l.out.Write([]byte(date + fmt.Sprintf(" [%15s] ", l.name) + fmt.Sprintf(message+"\n", values...)))
}

func (l *Logger) println(values ...interface{}) {
	l.outMu.Lock()
	defer l.outMu.Unlock()
	date := time.Now().Format(TimeFormat)
	l.out.Write([]byte(date + fmt.Sprintf(" [%15s] ", l.name) + fmt.Sprintln(values...)))
}

type LoggerFactory struct {
	out            io.Writer
	loggers        map[string]*Logger
	defaultEnabled map[string]struct{}
	loggersMu      sync.Mutex
}

func NewLoggerFactory(out io.Writer, enabled map[string]struct{}) *LoggerFactory {
	return &LoggerFactory{
		out:            out,
		loggers:        map[string]*Logger{},
		defaultEnabled: enabled,
	}
}

func NewLoggerFactoryFromEnv(prefix string, out io.Writer) *LoggerFactory {
	log := os.Getenv(prefix + "LOG")
	if log == "" {
		log = "*"
	}
	enabled := strings.Split(log, ",")
	defaultEnabled := map[string]struct{}{}
	for _, name := range enabled {
		defaultEnabled[name] = struct{}{}
	}
	return NewLoggerFactory(out, defaultEnabled)
}

func (l *LoggerFactory) GetLogger(name string) *Logger {
	l.loggersMu.Lock()
	defer l.loggersMu.Unlock()
	logger, ok := l.loggers[name]
	if !ok {
		_, enabled := l.defaultEnabled[name]
		_, allEnabled := l.defaultEnabled["*"]
		logger = NewLogger(name, l.out, enabled || allEnabled)
		l.loggers[name] = logger
	}
	return logger
}

var GetLogger = NewLoggerFactoryFromEnv("", os.Stderr).GetLogger
<commit_msg>Add ability to disable specific loggers with -<commit_after>package logger

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

type Logger struct {
	name    string
	out     io.Writer
	outMu   sync.Mutex
	Enabled bool
}

var TimeFormat = "2006-01-02T15:04:05.000000Z07:00"

func NewLogger(name string, out io.Writer, enabled bool) *Logger {
	return &Logger{name: name, out: out, Enabled: enabled}
}

func (l *Logger) Printf(message string, values ...interface{}) {
	if l.Enabled {
		l.printf(message, values...)
	}
}

func (l *Logger) Println(values ...interface{}) {
	if l.Enabled {
		l.println(values...)
	}
}

func (l *Logger) printf(message string, values ...interface{}) {
	l.outMu.Lock()
	defer l.outMu.Unlock()
	date := time.Now().Format(TimeFormat)
	l.out.Write([]byte(date + fmt.Sprintf(" [%15s] ", l.name) + fmt.Sprintf(message+"\n", values...)))
}

func (l *Logger) println(values ...interface{}) {
	l.outMu.Lock()
	defer l.outMu.Unlock()
	date := time.Now().Format(TimeFormat)
	l.out.Write([]byte(date + fmt.Sprintf(" [%15s] ", l.name) + fmt.Sprintln(values...)))
}

type LoggerFactory struct {
	out            io.Writer
	loggers        map[string]*Logger
	defaultEnabled map[string]struct{}
	loggersMu      sync.Mutex
}

func NewLoggerFactory(out io.Writer, enabled map[string]struct{}) *LoggerFactory {
	return &LoggerFactory{
		out:            out,
		loggers:        map[string]*Logger{},
		defaultEnabled: enabled,
	}
}

func NewLoggerFactoryFromEnv(prefix string, out io.Writer) *LoggerFactory {
	log := os.Getenv(prefix + "LOG")
	if log == "" {
		log = "*"
	}
	enabled := strings.Split(log, ",")
	defaultEnabled := map[string]struct{}{}
	for _, name := range enabled {
		defaultEnabled[name] = struct{}{}
	}
	return NewLoggerFactory(out, defaultEnabled)
}

func (l *LoggerFactory) GetLogger(name string) *Logger {
	l.loggersMu.Lock()
	defer l.loggersMu.Unlock()
	logger, ok := l.loggers[name]
	if !ok {
		_, enabled := l.defaultEnabled[name]
		_, disabled := l.defaultEnabled["-"+name]
		_, allEnabled := l.defaultEnabled["*"]
		logger = NewLogger(name, l.out, enabled || allEnabled && !disabled)
		l.loggers[name] = logger
	}
	return logger
}

var GetLogger = NewLoggerFactoryFromEnv("PEERCALLS_", os.Stderr).GetLogger
<|endoftext|>


<commit_before>package openflow13

// This file has all group related defs

const (
    OFPG_MAX = 0xffffff00  /* Last usable group number. */
    /* Fake groups. */
    OFPG_ALL = 0xfffffffc /* Represents all groups for group delete commands. */
    OFPG_ANY = 0xffffffff /* Wildcard group used only for flow stats requests. Selects all flows regardless of group (including flows with no group).
    */
)

// FIXME: Add all the group constructs here
<commit_msg>Add group table management<commit_after>package openflow13

// This file has all group related defs

import (
    "log"
    "encoding/binary"

    "github.com/shaleman/libOpenflow/common"
)

const (
    OFPG_MAX = 0xffffff00  /* Last usable group number. */
    /* Fake groups. */
    OFPG_ALL = 0xfffffffc /* Represents all groups for group delete commands. */
    OFPG_ANY = 0xffffffff /* Wildcard group used only for flow stats requests. Selects all flows regardless of group (including flows with no group).
    */
)

const (
    OFPGC_ADD = 0       /* New group. */
    OFPGC_MODIFY = 1    /* Modify all matching groups. */
    OFPGC_DELETE = 2    /* Delete all matching groups. */
)

const (
    OFPGT_ALL = 0       /* All (multicast/broadcast) group. */
    OFPGT_SELECT = 1    /* Select group. */
    OFPGT_INDIRECT = 2  /* Indirect group. */
    OFPGT_FF = 3        /* Fast failover group. */
)

// GroupMod message
type GroupMod struct {
    common.Header
    Command     uint16           /* One of OFPGC_*. */
    Type        uint8            /* One of OFPGT_*. */
    pad         uint8            /* Pad to 64 bits. */
    GroupId     uint32           /* Group identifier. */
    Buckets     []Bucket         /* List of buckets */
}

// Create a new group mode message
func NewGroupMod() *GroupMod {
    g := new(GroupMod)
    g.Header = NewOfp13Header()
    g.Header.Type = Type_GroupMod

    g.Command = OFPGC_ADD
    g.Type = OFPGT_ALL
    g.GroupId = 0
    g.Buckets = make([]Bucket, 0)
    return g
}

// Add a bucket to group mod
func (g *GroupMod) AddBucket(bkt Bucket) {
    g.Buckets = append(g.Buckets, bkt)
}

func (g *GroupMod) Len() (n uint16) {
    n = g.Header.Len()
    n += 8
    if g.Command == OFPGC_DELETE {
        return
    }

    for _, b := range g.Buckets {
        n += b.Len()
    }

    return
}

func (g *GroupMod) MarshalBinary() (data []byte, err error) {
    g.Header.Length = g.Len()
    data, err = g.Header.MarshalBinary()

    bytes := make([]byte, 8)
    n := 0
    binary.BigEndian.PutUint16(bytes[n:], g.Command)
    n += 2
    bytes[n] = g.Type
    n += 1
    bytes[n] = g.pad
    n += 1
    binary.BigEndian.PutUint32(bytes[n:], g.GroupId)
    n += 4
    data = append(data, bytes...)

    for _, bkt := range g.Buckets {
        bytes, err = bkt.MarshalBinary()
        data = append(data, bytes...)
        log.Printf("Groupmod bucket: %v", bytes)
    }

    return
}

func (g *GroupMod) UnmarshalBinary(data []byte) error {
    n := 0
    g.Header.UnmarshalBinary(data[n:])
    n += int(g.Header.Len())

    g.Command = binary.BigEndian.Uint16(data[n:])
    n += 2
    g.Type = data[n]
    n += 1
    g.pad = data[n]
    n += 1
    g.GroupId = binary.BigEndian.Uint32(data[n:])
    n += 4

    for n < int(g.Header.Length) {
        bkt := new(Bucket)
        bkt.UnmarshalBinary(data[n:])
        g.Buckets = append(g.Buckets, *bkt)
        n += int(bkt.Len())
    }

    return nil
}

type Bucket struct {
    Length      uint16      /* Length the bucket in bytes, including this header and any padding to make it 64-bit aligned. */
    Weight      uint16      /* Relative weight of bucket. Only defined for select groups. */
    WatchPort   uint32      /* Used for FRR groups */
    WatchGroup  uint32      /* Used for FRR groups */
    pad         []byte      /* 4 bytes */
    Actions     []Action    /* zero or more actions */
}

// Create a new Bucket
func NewBucket() *Bucket {
    bkt := new(Bucket)

    bkt.Weight = 0
    bkt.pad = make([]byte, 4)
    bkt.Actions = make([]Action, 0)

    bkt.Length = bkt.Len()

    return bkt
}

// Add an action to the bucket
func (b *Bucket) AddAction(act Action) {
    b.Actions = append(b.Actions, act)
}

func (b *Bucket) Len() (n uint16) {
    n = 16

    for _, a := range b.Actions {
        n += a.Len()
    }

    // Round it to closest multiple of 8
    n = ((n + 7)/8)*8
    return
}

func (b *Bucket) MarshalBinary() (data []byte, err error) {
    bytes := make([]byte, 16)
    n := 0
    b.Length = b.Len()  // Calculate length first
    binary.BigEndian.PutUint16(bytes[n:], b.Length)
    n += 2
    binary.BigEndian.PutUint16(bytes[n:], b.Weight)
    n += 2
    binary.BigEndian.PutUint32(bytes[n:], b.WatchPort)
    n += 4
    binary.BigEndian.PutUint32(bytes[n:], b.WatchGroup)
    n += 4
    data = append(data, bytes...)

    for _, a := range b.Actions {
        bytes, err = a.MarshalBinary()
        data = append(data, bytes...)
    }

    return
}

func (b *Bucket) UnmarshalBinary(data []byte) error {
    n := 0
    b.Length = binary.BigEndian.Uint16(data[n:])
    n += 2
    b.Weight = binary.BigEndian.Uint16(data[n:])
    n += 2
    b.WatchPort = binary.BigEndian.Uint32(data[n:])
    n += 4
    b.WatchGroup = binary.BigEndian.Uint32(data[n:])
    n += 4
    n += 4 // for padding

    for n < int(b.Length) {
        a := DecodeAction(data[n:])
        b.Actions = append(b.Actions, a)
        n += int(a.Len())
    }

    return nil
}
<|endoftext|>


<commit_before>package luddite

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"

	"github.com/SpirentOrion/httprouter"
	"golang.org/x/net/context"
)

// Service is an interface that implements a standalone RESTful web service.
type Service interface {
	// AddHandler adds a context-aware middleware handler to the
	// middleware stack. All handlers must be added before Run is
	// called.
	AddHandler(h Handler)

	// AddSingletonResource registers a singleton-style resource
	// (supporting GET and PUT methods only).
	AddSingletonResource(itemPath string, r Resource)

	// AddCollectionResource registers a collection-style resource
	// (supporting GET, POST, PUT, and DELETE methods).
	AddCollectionResource(basePath string, r Resource)

	// Logger returns the service's log.Logger instance.
	Logger() *log.Logger

	// Run is a convenience function that runs the service as an
	// HTTP server. The address is taken from the ServiceConfig
	// passed to NewService.
	Run() error
}

type service struct {
	config     *ServiceConfig
	logger     *log.Logger
	router     *httprouter.Router
	handlers   []Handler
	middleware *middleware
	schema     *SchemaHandler
}

func NewService(config *ServiceConfig) (Service, error) {
	// Create service
	s := &service{
		config:   config,
		logger:   log.New(os.Stderr, config.Log.Prefix, log.LstdFlags),
		router:   httprouter.New(),
		handlers: []Handler{},
	}

	s.router.NotFound = func(_ context.Context, rw http.ResponseWriter, _ *http.Request) {
		rw.WriteHeader(http.StatusNotFound)
	}

	s.router.MethodNotAllowed = func(_ context.Context, rw http.ResponseWriter, _ *http.Request) {
		rw.WriteHeader(http.StatusMethodNotAllowed)
	}

	// Create default middleware handlers
	// NB: failures to initialize/configure tracing should not fail the service startup
	h, err := s.newBottomHandler()
	if err != nil {
		return nil, err
	}
	s.handlers = append(s.handlers, h)

	h, err = s.newNegotiatorHandler()
	if err != nil {
		return nil, err
	}
	s.handlers = append(s.handlers, h)

	h, err = s.newContextHandler()
	if err != nil {
		return nil, err
	}
	s.handlers = append(s.handlers, h)

	// Build middleware stack
	s.middleware = buildMiddleware(s.handlers)

	// Install default http handlers
	if config.Schema.Enabled {
		s.addSchemaRoutes()
	}

	return s, nil
}

func (s *service) AddHandler(h Handler) {
	s.handlers = append(s.handlers, h)
	s.middleware = buildMiddleware(s.handlers)
}

func (s *service) AddSingletonResource(basePath string, r Resource) {
	// GET /basePath
	addGetRoute(s.router, basePath, false, r)

	// PUT /basePath
	addUpdateRoute(s.router, basePath, false, r)

	// POST /basePath/{action}
	addActionRoute(s.router, basePath, false, r)
}

func (s *service) AddCollectionResource(basePath string, r Resource) {
	// GET /basePath
	addListRoute(s.router, basePath, r)

	// GET /basePath/{id}
	addGetRoute(s.router, basePath, true, r)

	// POST /basePath
	addCreateRoute(s.router, basePath, r)

	// PUT /basePath/{id}
	addUpdateRoute(s.router, basePath, true, r)

	// DELETE /basePath
	addDeleteRoute(s.router, basePath, false, r)

	// DELETE /basePath/{id}
	addDeleteRoute(s.router, basePath, true, r)

	// POST /basePath/{id}/{action}
	addActionRoute(s.router, basePath, true, r)
}

func (s *service) Logger() *log.Logger {
	return s.logger
}

func (s *service) Run() error {
	// Add the router as the final middleware handler
	h, err := s.newRouterHandler()
	if err != nil {
		return err
	}
	s.AddHandler(h)

	// Serve
	s.logger.Printf("listening on %s", s.config.Addr)
	return http.ListenAndServe(s.config.Addr, s.middleware)
}

func (s *service) newBottomHandler() (Handler, error) {
	return NewBottom(s.config, s.logger), nil
}

func (s *service) newNegotiatorHandler() (Handler, error) {
	return NewNegotiator([]string{ContentTypeJson, ContentTypeXml, ContentTypeHtml}), nil
}

func (s *service) newContextHandler() (Handler, error) {
	if s.config.Version.Min < 1 {
		return nil, errors.New("service's minimum API version must be greater than zero")
	}
	if s.config.Version.Max < 1 {
		return nil, errors.New("service's maximum API version must be greater than zero")
	}

	return NewContext(s, s.config.Version.Min, s.config.Version.Max), nil
}

func (s *service) newRouterHandler() (Handler, error) {
	return HandlerFunc(func(ctx context.Context, rw http.ResponseWriter, r *http.Request, _ ContextHandlerFunc) {
		// No more middleware handlers: further dispatch happens via httprouter
		s.router.HandleHTTP(ctx, rw, r)
	}), nil
}

func (s *service) addSchemaRoutes() {
	config := s.config

	// Serve the various schemas, e.g. /schema/v1, /schema/v2, etc.
	s.schema = NewSchemaHandler(config.Schema.FilePath, config.Schema.FilePattern)
	s.router.GET(path.Join(config.Schema.UriPath, "/v:version"), s.schema.ServeHTTP)

	// Temporarily redirect (307) the base schema path to the default schema, e.g. /schema -> /schema/v2
	defaultSchemaPath := path.Join(config.Schema.UriPath, fmt.Sprintf("v%d", config.Version.Max))
	s.router.GET(config.Schema.UriPath, func(_ context.Context, rw http.ResponseWriter, r *http.Request) {
		http.Redirect(rw, r, defaultSchemaPath, http.StatusTemporaryRedirect)
	})

	// Optionally temporarily redirect (307) the root to the base schema path, e.g. / -> /schema
	if config.Schema.RootRedirect {
		s.router.GET("/", func(_ context.Context, rw http.ResponseWriter, r *http.Request) {
			http.Redirect(rw, r, config.Schema.UriPath, http.StatusTemporaryRedirect)
		})
	}
}
<commit_msg>Expose services' router instance.  Necessary when services need to register non-REST handlers, e.g. for OAuth2.<commit_after>package luddite

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"

	"github.com/SpirentOrion/httprouter"
	"golang.org/x/net/context"
)

// Service is an interface that implements a standalone RESTful web service.
type Service interface {
	// AddHandler adds a context-aware middleware handler to the
	// middleware stack. All handlers must be added before Run is
	// called.
	AddHandler(h Handler)

	// AddSingletonResource registers a singleton-style resource
	// (supporting GET and PUT methods only).
	AddSingletonResource(itemPath string, r Resource)

	// AddCollectionResource registers a collection-style resource
	// (supporting GET, POST, PUT, and DELETE methods).
	AddCollectionResource(basePath string, r Resource)

	// Logger returns the service's log.Logger instance.
	Logger() *log.Logger

	// Router returns the services' httprouter.Router instance.
	Router() *httprouter.Router

	// Run is a convenience function that runs the service as an
	// HTTP server. The address is taken from the ServiceConfig
	// passed to NewService.
	Run() error
}

type service struct {
	config     *ServiceConfig
	logger     *log.Logger
	router     *httprouter.Router
	handlers   []Handler
	middleware *middleware
	schema     *SchemaHandler
}

func NewService(config *ServiceConfig) (Service, error) {
	// Create service
	s := &service{
		config:   config,
		logger:   log.New(os.Stderr, config.Log.Prefix, log.LstdFlags),
		router:   httprouter.New(),
		handlers: []Handler{},
	}

	s.router.NotFound = func(_ context.Context, rw http.ResponseWriter, _ *http.Request) {
		rw.WriteHeader(http.StatusNotFound)
	}

	s.router.MethodNotAllowed = func(_ context.Context, rw http.ResponseWriter, _ *http.Request) {
		rw.WriteHeader(http.StatusMethodNotAllowed)
	}

	// Create default middleware handlers
	// NB: failures to initialize/configure tracing should not fail the service startup
	h, err := s.newBottomHandler()
	if err != nil {
		return nil, err
	}
	s.handlers = append(s.handlers, h)

	h, err = s.newNegotiatorHandler()
	if err != nil {
		return nil, err
	}
	s.handlers = append(s.handlers, h)

	h, err = s.newContextHandler()
	if err != nil {
		return nil, err
	}
	s.handlers = append(s.handlers, h)

	// Build middleware stack
	s.middleware = buildMiddleware(s.handlers)

	// Install default http handlers
	if config.Schema.Enabled {
		s.addSchemaRoutes()
	}

	return s, nil
}

func (s *service) AddHandler(h Handler) {
	s.handlers = append(s.handlers, h)
	s.middleware = buildMiddleware(s.handlers)
}

func (s *service) AddSingletonResource(basePath string, r Resource) {
	// GET /basePath
	addGetRoute(s.router, basePath, false, r)

	// PUT /basePath
	addUpdateRoute(s.router, basePath, false, r)

	// POST /basePath/{action}
	addActionRoute(s.router, basePath, false, r)
}

func (s *service) AddCollectionResource(basePath string, r Resource) {
	// GET /basePath
	addListRoute(s.router, basePath, r)

	// GET /basePath/{id}
	addGetRoute(s.router, basePath, true, r)

	// POST /basePath
	addCreateRoute(s.router, basePath, r)

	// PUT /basePath/{id}
	addUpdateRoute(s.router, basePath, true, r)

	// DELETE /basePath
	addDeleteRoute(s.router, basePath, false, r)

	// DELETE /basePath/{id}
	addDeleteRoute(s.router, basePath, true, r)

	// POST /basePath/{id}/{action}
	addActionRoute(s.router, basePath, true, r)
}

func (s *service) Logger() *log.Logger {
	return s.logger
}

func (s *service) Router() *httprouter.Router {
	return s.router
}

func (s *service) Run() error {
	// Add the router as the final middleware handler
	h, err := s.newRouterHandler()
	if err != nil {
		return err
	}
	s.AddHandler(h)

	// Serve
	s.logger.Printf("listening on %s", s.config.Addr)
	return http.ListenAndServe(s.config.Addr, s.middleware)
}

func (s *service) newBottomHandler() (Handler, error) {
	return NewBottom(s.config, s.logger), nil
}

func (s *service) newNegotiatorHandler() (Handler, error) {
	return NewNegotiator([]string{ContentTypeJson, ContentTypeXml, ContentTypeHtml}), nil
}

func (s *service) newContextHandler() (Handler, error) {
	if s.config.Version.Min < 1 {
		return nil, errors.New("service's minimum API version must be greater than zero")
	}
	if s.config.Version.Max < 1 {
		return nil, errors.New("service's maximum API version must be greater than zero")
	}

	return NewContext(s, s.config.Version.Min, s.config.Version.Max), nil
}

func (s *service) newRouterHandler() (Handler, error) {
	return HandlerFunc(func(ctx context.Context, rw http.ResponseWriter, r *http.Request, _ ContextHandlerFunc) {
		// No more middleware handlers: further dispatch happens via httprouter
		s.router.HandleHTTP(ctx, rw, r)
	}), nil
}

func (s *service) addSchemaRoutes() {
	config := s.config

	// Serve the various schemas, e.g. /schema/v1, /schema/v2, etc.
	s.schema = NewSchemaHandler(config.Schema.FilePath, config.Schema.FilePattern)
	s.router.GET(path.Join(config.Schema.UriPath, "/v:version"), s.schema.ServeHTTP)

	// Temporarily redirect (307) the base schema path to the default schema, e.g. /schema -> /schema/v2
	defaultSchemaPath := path.Join(config.Schema.UriPath, fmt.Sprintf("v%d", config.Version.Max))
	s.router.GET(config.Schema.UriPath, func(_ context.Context, rw http.ResponseWriter, r *http.Request) {
		http.Redirect(rw, r, defaultSchemaPath, http.StatusTemporaryRedirect)
	})

	// Optionally temporarily redirect (307) the root to the base schema path, e.g. / -> /schema
	if config.Schema.RootRedirect {
		s.router.GET("/", func(_ context.Context, rw http.ResponseWriter, r *http.Request) {
			http.Redirect(rw, r, config.Schema.UriPath, http.StatusTemporaryRedirect)
		})
	}
}
<|endoftext|>


<commit_before>package anonuuid

import (
	"fmt"
	"testing"
)

func ExampleNew() {
	anonuuid := New()
	fmt.Println(anonuuid)
	// Output:
	// &{map[]}
}

func ExampleSanitize() {
	anonuuid := New()
	input := `VOLUMES_0_SERVER_ID=15573749-c89d-41dd-a655-16e79bed52e0
VOLUMES_0_SERVER_NAME=hello
VOLUMES_0_ID=c245c3cb-3336-4567-ada1-70cb1fe4eefe
VOLUMES_0_SIZE=50000000000
ORGANIZATION=fe1e54e8-d69d-4f7c-a9f1-42069e03da31
TEST=15573749-c89d-41dd-a655-16e79bed52e0`

	fmt.Println(anonuuid.Sanitize(input))
	// Output:
	// VOLUMES_0_SERVER_ID=00000000-0000-0000-0000-000000000000
	// VOLUMES_0_SERVER_NAME=hello
	// VOLUMES_0_ID=11111111-1111-1111-1111-111111111111
	// VOLUMES_0_SIZE=50000000000
	// ORGANIZATION=22222222-2222-2222-2222-222222222222
	// TEST=00000000-0000-0000-0000-000000000000
}

func ExampleFakeUUID() {
	anonuuid := New()
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("c245c3cb-3336-4567-ada1-70cb1fe4eefe"))
	fmt.Println(anonuuid.FakeUUID("c245c3cb-3336-4567-ada1-70cb1fe4eefe"))
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("fe1e54e8-d69d-4f7c-a9f1-42069e03da31"))
	// Output:
	// 00000000-0000-0000-0000-000000000000
	// 00000000-0000-0000-0000-000000000000
	// 11111111-1111-1111-1111-111111111111
	// 11111111-1111-1111-1111-111111111111
	// 00000000-0000-0000-0000-000000000000
	// 22222222-2222-2222-2222-222222222222
}

func BenchmarkNew(b *testing.B) {
	for i := 0; i < b.N; i++ {
		New()
	}
}

func BenchmarkFakeUUID(b *testing.B) {
	anonuuid := New()
	for i := 0; i < b.N; i++ {
		anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0")
	}
}

func BenchmarkSanitize(b *testing.B) {
	anonuuid := New()
	for i := 0; i < b.N; i++ {
		anonuuid.Sanitize("A: 15573749-c89d-41dd-a655-16e79bed52e0, B: c245c3cb-3336-4567-ada1-70cb1fe4eefe, A: 15573749-c89d-41dd-a655-16e79bed52e0")
	}
}
<commit_msg>Updated test function names<commit_after>package anonuuid

import (
	"fmt"
	"testing"
)

func ExampleNew() {
	anonuuid := New()
	fmt.Println(anonuuid)
	// Output:
	// &{map[]}
}

func ExampleAnonUUIDSanitize() {
	anonuuid := New()
	input := `VOLUMES_0_SERVER_ID=15573749-c89d-41dd-a655-16e79bed52e0
VOLUMES_0_SERVER_NAME=hello
VOLUMES_0_ID=c245c3cb-3336-4567-ada1-70cb1fe4eefe
VOLUMES_0_SIZE=50000000000
ORGANIZATION=fe1e54e8-d69d-4f7c-a9f1-42069e03da31
TEST=15573749-c89d-41dd-a655-16e79bed52e0`

	fmt.Println(anonuuid.Sanitize(input))
	// Output:
	// VOLUMES_0_SERVER_ID=00000000-0000-0000-0000-000000000000
	// VOLUMES_0_SERVER_NAME=hello
	// VOLUMES_0_ID=11111111-1111-1111-1111-111111111111
	// VOLUMES_0_SIZE=50000000000
	// ORGANIZATION=22222222-2222-2222-2222-222222222222
	// TEST=00000000-0000-0000-0000-000000000000
}

func ExampleAnonUUIDFakeUUID() {
	anonuuid := New()
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("c245c3cb-3336-4567-ada1-70cb1fe4eefe"))
	fmt.Println(anonuuid.FakeUUID("c245c3cb-3336-4567-ada1-70cb1fe4eefe"))
	fmt.Println(anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0"))
	fmt.Println(anonuuid.FakeUUID("fe1e54e8-d69d-4f7c-a9f1-42069e03da31"))
	// Output:
	// 00000000-0000-0000-0000-000000000000
	// 00000000-0000-0000-0000-000000000000
	// 11111111-1111-1111-1111-111111111111
	// 11111111-1111-1111-1111-111111111111
	// 00000000-0000-0000-0000-000000000000
	// 22222222-2222-2222-2222-222222222222
}

func BenchmarkNew(b *testing.B) {
	for i := 0; i < b.N; i++ {
		New()
	}
}

func BenchmarkAnonUUIDFakeUUID(b *testing.B) {
	anonuuid := New()
	for i := 0; i < b.N; i++ {
		anonuuid.FakeUUID("15573749-c89d-41dd-a655-16e79bed52e0")
	}
}

func BenchmarkAnonUUIDSanitize(b *testing.B) {
	anonuuid := New()
	for i := 0; i < b.N; i++ {
		anonuuid.Sanitize("A: 15573749-c89d-41dd-a655-16e79bed52e0, B: c245c3cb-3336-4567-ada1-70cb1fe4eefe, A: 15573749-c89d-41dd-a655-16e79bed52e0")
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2019 Andrew Ayer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Except as contained in this notice, the name(s) of the above copyright
 * holders shall not be used in advertising or otherwise to promote the
 * sale, use or other dealings in this Software without prior written
 * authorization.
 */

package smsxmpp

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"log"
	"strings"
	"time"

	"src.agwa.name/sms-over-xmpp/config"
	"src.agwa.name/go-xmpp/component"
	"src.agwa.name/go-xmpp"
)

type user struct {
	phoneNumber string // e.g. "+19255551212"
	provider    Provider
}

type Service struct {
	defaultPrefix   string // prepended to phone numbers that don't start with +
	publicURL       string
	users           map[xmpp.Address]user // Map from bare JID -> user
	providers       map[string]Provider
	xmppParams      component.Params
	xmppSendChan    chan interface{}
}

func NewService(config *config.Config) (*Service, error) {
	if config.DefaultPrefix != "" {
		if err := validatePhoneNumber(config.DefaultPrefix); err != nil {
			return nil, fmt.Errorf("default_prefix option is invalid: %s", err)
		}
	}
	service := &Service{
		defaultPrefix: config.DefaultPrefix,
		publicURL:   config.PublicURL,
		users:       make(map[xmpp.Address]user),
		providers:   make(map[string]Provider),
		xmppParams:  component.Params{
			Domain: config.XMPPDomain,
			Secret: config.XMPPSecret,
			Server: config.XMPPServer,
			Logger: log.New(os.Stderr, "", log.Ldate | log.Ltime | log.Lmicroseconds),
		},
		xmppSendChan: make(chan interface{}),
	}

	for providerName, providerConfig := range config.Providers {
		provider, err := MakeProvider(providerConfig.Type, service, providerConfig.Params)
		if err != nil {
			return nil, fmt.Errorf("Provider %s: %s", providerName, err)
		}
		service.providers[providerName] = provider
	}

	for userJID, userConfig := range config.Users {
		userAddress, err := xmpp.ParseAddress(userJID)
		if err != nil {
			return nil, fmt.Errorf("User %s has malformed JID: %s", userJID, err)
		}
		userProvider, providerExists := service.providers[userConfig.Provider]
		if !providerExists {
			return nil, fmt.Errorf("User %s refers to non-existent provider %s", userJID, userConfig.Provider)
		}
		service.users[userAddress] = user{
			phoneNumber: userConfig.PhoneNumber,
			provider: userProvider,
		}
	}
	return service, nil
}

func (service *Service) sendWithin(timeout time.Duration, v interface{}) bool {
	timer := time.NewTimer(timeout)
	defer timer.Stop()

	select {
	case service.xmppSendChan <- v:
		return true
	case <-timer.C:
		return false
	}
}

func (service *Service) defaultHTTPHandler(w http.ResponseWriter, req *http.Request) {
	if req.URL.Path == "/" {
		http.Error(w, "You have successfully reached sms-over-xmpp.", 200)
	} else {
		http.Error(w, "You have reached sms-over-xmpp, but the provider indicated in the URL is not known.", 404)
	}
}

func (service *Service) HTTPHandler() http.Handler {
	mux := http.NewServeMux()
	for name, provider := range service.providers {
		if providerHandler := provider.HTTPHandler(); providerHandler != nil {
			mux.Handle("/" + name + "/", http.StripPrefix("/" + name, providerHandler))
		}
	}
	mux.HandleFunc("/", service.defaultHTTPHandler)
	return mux
}

func (service *Service) RunXMPPComponent(ctx context.Context) error {
	callbacks := component.Callbacks{
		Message: service.receiveXMPPMessage,
		Presence: service.receiveXMPPPresence,
	}

	return component.Run(ctx, service.xmppParams, callbacks, service.xmppSendChan)
}

func (service *Service) Receive(message *Message) error {
	address, known := service.addressForPhoneNumber(message.To)
	if !known {
		return errors.New("Unknown phone number " + message.To)
	}
	from := xmpp.Address{service.friendlyPhoneNumber(message.From), service.xmppParams.Domain, ""}

	if err := service.sendXMPPChat(from, address, message.Body); err != nil {
		return err
	}

	for _, mediaURL := range message.MediaURLs {
		if err := service.sendXMPPMediaURL(from, address, mediaURL); err != nil {
			return err
		}
	}

	return nil
}

func (service *Service) sendXMPPChat(from xmpp.Address, to xmpp.Address, body string) error {
	xmppMessage := xmpp.Message{
		Header: xmpp.Header{
			From: &from,
			To:   &to,
		},
		Body: body,
		Type: xmpp.CHAT,
	}

	if !service.sendWithin(5*time.Second, xmppMessage) {
		return errors.New("Timed out when sending XMPP message")
	}
	return nil
}

func (service *Service) sendXMPPMediaURL(from xmpp.Address, to xmpp.Address, mediaURL string) error {
	xmppMessage := xmpp.Message{
		Header: xmpp.Header{
			From: &from,
			To:   &to,
		},
		Body: mediaURL,
		Type: xmpp.CHAT,
		OutOfBandData: &xmpp.OutOfBandData{URL: mediaURL},
	}

	if !service.sendWithin(5*time.Second, xmppMessage) {
		return errors.New("Timed out when sending XMPP message with out-of-band data")
	}
	return nil
}

func shouldForwardMessageType(t xmpp.MessageType) bool {
	return t == "" || t == xmpp.CHAT || t == xmpp.NORMAL
}

func messageHasContent(message *xmpp.Message) bool {
	// This function filters out "$user is typing" messages
	return message.Body != "" || message.OutOfBandData != nil
}

func shouldForwardMessage(message *xmpp.Message) bool {
	return shouldForwardMessageType(message.Type) && messageHasContent(message)
}

func (service *Service) receiveXMPPMessage(ctx context.Context, xmppMessage *xmpp.Message) error {
	if xmppMessage.From == nil || xmppMessage.To == nil {
		return errors.New("Received malformed XMPP message: From and To not set")
	}
	if !shouldForwardMessage(xmppMessage) {
		return nil
	}
	user, userExists := service.users[*xmppMessage.From.Bare()]
	if !userExists {
		return service.sendXMPPError(xmppMessage.To, xmppMessage.From, xmppMessage.From.Bare().String() + " is not a known user; please add them to sms-over-xmpp's users file")
	}

	toPhoneNumber, err := service.canonPhoneNumber(xmppMessage.To.LocalPart)
	if err != nil {
		return service.sendXMPPError(xmppMessage.To, xmppMessage.From, fmt.Sprintf("Invalid phone number '%s': %s (example: +12125551212)", xmppMessage.To.LocalPart, err))
	}

	message := &Message{
		From: user.phoneNumber,
		To:   toPhoneNumber,
	}
	if xmppMessage.OutOfBandData != nil {
		message.MediaURLs = append(message.MediaURLs, xmppMessage.OutOfBandData.URL)
	} else {
		message.Body = xmppMessage.Body
	}

	go func() {
		err := user.provider.Send(message)
		if err != nil {
			// TODO: if sendXMPPError fails, log the error
			service.sendXMPPError(xmppMessage.To, xmppMessage.From, "Sending SMS failed: " + err.Error())
		}
	}()

	return nil
}

func (service *Service) receiveXMPPPresence(ctx context.Context, presence *xmpp.Presence) error {
	if presence.From == nil || presence.To == nil {
		return errors.New("Received malformed XMPP presence: From and To not set")
	}

	if _, userExists := service.users[*presence.From.Bare()]; !userExists {
		return nil
	}

	if presence.Type == xmpp.SUBSCRIBE {
		if err := service.sendXMPPPresence(presence.To, presence.From, xmpp.SUBSCRIBED, ""); err != nil {
			return err
		}
	}

	if presence.Type == xmpp.SUBSCRIBE || presence.Type == xmpp.PROBE {
		var presenceType string
		var status string

		if _, err := service.canonPhoneNumber(presence.To.LocalPart); err != nil {
			presenceType = "error"
			status = "Invalid phone number: " + err.Error()
		}

		if err := service.sendXMPPPresence(presence.To, presence.From, presenceType, status); err != nil {
			return err
		}
	}

	return nil
}

func (service *Service) sendXMPPError(from *xmpp.Address, to *xmpp.Address, message string) error {
	xmppMessage := &xmpp.Message{
		Header: xmpp.Header{
			From: from,
			To:   to,
		},
		Type: xmpp.ERROR,
		Body: message,
	}
	if !service.sendWithin(5*time.Second, xmppMessage) {
		return errors.New("Timed out when sending XMPP error message")
	}
	return nil
}

func (service *Service) sendXMPPPresence(from *xmpp.Address, to *xmpp.Address, presenceType string, status string) error {
	xmppPresence := &xmpp.Presence{
		Header: xmpp.Header{
			From: from,
			To:   to,
		},
		Type: presenceType,
		Status: status,
	}
	if !service.sendWithin(5*time.Second, xmppPresence) {
		return errors.New("Timed out when sending XMPP presence")
	}
	return nil
}

func (service *Service) addressForPhoneNumber(phoneNumber string) (xmpp.Address, bool) {
	for address, user := range service.users {
		if user.phoneNumber == phoneNumber {
			return address, true
		}
	}
	return xmpp.Address{}, false
}

func (service *Service) canonPhoneNumber(phoneNumber string) (string, error) {
	if !strings.HasPrefix(phoneNumber, "+") {
		if service.defaultPrefix == "" {
			return "", errors.New("does not start with + (please prefix number with + and a country code, or configure the default_prefix option)")
		}
		phoneNumber = service.defaultPrefix + phoneNumber
	}
	return phoneNumber, validatePhoneNumber(phoneNumber)
}

func (service *Service) friendlyPhoneNumber(phoneNumber string) string {
	return strings.TrimPrefix(phoneNumber, service.defaultPrefix)
}
<commit_msg>Add initial support for managing rosters<commit_after>/*
 * Copyright (c) 2019 Andrew Ayer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Except as contained in this notice, the name(s) of the above copyright
 * holders shall not be used in advertising or otherwise to promote the
 * sale, use or other dealings in this Software without prior written
 * authorization.
 */

package smsxmpp

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"log"
	"strings"
	"sync"
	"time"

	"src.agwa.name/sms-over-xmpp/config"
	"src.agwa.name/go-xmpp/component"
	"src.agwa.name/go-xmpp"
)

type RosterItem struct {
	Name   string
	Groups []string
}

func (item RosterItem) Equal(other RosterItem) bool {
	if item.Name != other.Name {
		return false
	}
	if len(item.Groups) != len(other.Groups) {
		return false
	}
	for i := range item.Groups {
		if item.Groups[i] != other.Groups[i] {
			return false
		}
	}
	return true
}

type Roster map[xmpp.Address]RosterItem

var ErrRosterNotIntialized = errors.New("the roster for this user has not been initialized yet")

type userState struct {
	rosterMu sync.Mutex
	roster   Roster
}

type user struct {
	phoneNumber string // e.g. "+19255551212"
	provider    Provider

	state *userState
}

type Service struct {
	defaultPrefix   string // prepended to phone numbers that don't start with +
	publicURL       string
	users           map[xmpp.Address]user // Map from bare JID -> user
	providers       map[string]Provider
	xmppParams      component.Params
	xmppSendChan    chan interface{}
}

func NewService(config *config.Config) (*Service, error) {
	if config.DefaultPrefix != "" {
		if err := validatePhoneNumber(config.DefaultPrefix); err != nil {
			return nil, fmt.Errorf("default_prefix option is invalid: %s", err)
		}
	}
	service := &Service{
		defaultPrefix: config.DefaultPrefix,
		publicURL:   config.PublicURL,
		users:       make(map[xmpp.Address]user),
		providers:   make(map[string]Provider),
		xmppParams:  component.Params{
			Domain: config.XMPPDomain,
			Secret: config.XMPPSecret,
			Server: config.XMPPServer,
			Logger: log.New(os.Stderr, "", log.Ldate | log.Ltime | log.Lmicroseconds),
		},
		xmppSendChan: make(chan interface{}),
	}

	for providerName, providerConfig := range config.Providers {
		provider, err := MakeProvider(providerConfig.Type, service, providerConfig.Params)
		if err != nil {
			return nil, fmt.Errorf("Provider %s: %s", providerName, err)
		}
		service.providers[providerName] = provider
	}

	for userJID, userConfig := range config.Users {
		userAddress, err := xmpp.ParseAddress(userJID)
		if err != nil {
			return nil, fmt.Errorf("User %s has malformed JID: %s", userJID, err)
		}
		userProvider, providerExists := service.providers[userConfig.Provider]
		if !providerExists {
			return nil, fmt.Errorf("User %s refers to non-existent provider %s", userJID, userConfig.Provider)
		}
		service.users[userAddress] = user{
			phoneNumber: userConfig.PhoneNumber,
			provider: userProvider,
			state: new(userState),
		}
	}
	return service, nil
}

func (service *Service) sendWithin(timeout time.Duration, v interface{}) bool {
	timer := time.NewTimer(timeout)
	defer timer.Stop()

	select {
	case service.xmppSendChan <- v:
		return true
	case <-timer.C:
		return false
	}
}

func (service *Service) defaultHTTPHandler(w http.ResponseWriter, req *http.Request) {
	if req.URL.Path == "/" {
		http.Error(w, "You have successfully reached sms-over-xmpp.", 200)
	} else {
		http.Error(w, "You have reached sms-over-xmpp, but the provider indicated in the URL is not known.", 404)
	}
}

func (service *Service) HTTPHandler() http.Handler {
	mux := http.NewServeMux()
	for name, provider := range service.providers {
		if providerHandler := provider.HTTPHandler(); providerHandler != nil {
			mux.Handle("/" + name + "/", http.StripPrefix("/" + name, providerHandler))
		}
	}
	mux.HandleFunc("/", service.defaultHTTPHandler)
	return mux
}

func (service *Service) RunXMPPComponent(ctx context.Context) error {
	callbacks := component.Callbacks{
		Message: service.receiveXMPPMessage,
		Presence: service.receiveXMPPPresence,
		Iq: service.receiveXMPPIq,
	}

	return component.Run(ctx, service.xmppParams, callbacks, service.xmppSendChan)
}

func (service *Service) Receive(message *Message) error {
	address, known := service.addressForPhoneNumber(message.To)
	if !known {
		return errors.New("Unknown phone number " + message.To)
	}
	from := xmpp.Address{service.friendlyPhoneNumber(message.From), service.xmppParams.Domain, ""}

	if err := service.sendXMPPChat(from, address, message.Body); err != nil {
		return err
	}

	for _, mediaURL := range message.MediaURLs {
		if err := service.sendXMPPMediaURL(from, address, mediaURL); err != nil {
			return err
		}
	}

	return nil
}

func (service *Service) sendXMPPChat(from xmpp.Address, to xmpp.Address, body string) error {
	xmppMessage := xmpp.Message{
		Header: xmpp.Header{
			From: &from,
			To:   &to,
		},
		Body: body,
		Type: xmpp.CHAT,
	}

	if !service.sendWithin(5*time.Second, xmppMessage) {
		return errors.New("Timed out when sending XMPP message")
	}
	return nil
}

func (service *Service) sendXMPPMediaURL(from xmpp.Address, to xmpp.Address, mediaURL string) error {
	xmppMessage := xmpp.Message{
		Header: xmpp.Header{
			From: &from,
			To:   &to,
		},
		Body: mediaURL,
		Type: xmpp.CHAT,
		OutOfBandData: &xmpp.OutOfBandData{URL: mediaURL},
	}

	if !service.sendWithin(5*time.Second, xmppMessage) {
		return errors.New("Timed out when sending XMPP message with out-of-band data")
	}
	return nil
}

func shouldForwardMessageType(t xmpp.MessageType) bool {
	return t == "" || t == xmpp.CHAT || t == xmpp.NORMAL
}

func messageHasContent(message *xmpp.Message) bool {
	// This function filters out "$user is typing" messages
	return message.Body != "" || message.OutOfBandData != nil
}

func shouldForwardMessage(message *xmpp.Message) bool {
	return shouldForwardMessageType(message.Type) && messageHasContent(message)
}

func (service *Service) receiveXMPPMessage(ctx context.Context, xmppMessage *xmpp.Message) error {
	if xmppMessage.From == nil || xmppMessage.To == nil {
		return errors.New("Received malformed XMPP message: From and To not set")
	}
	if !shouldForwardMessage(xmppMessage) {
		return nil
	}
	user, userExists := service.users[*xmppMessage.From.Bare()]
	if !userExists {
		return service.sendXMPPError(xmppMessage.To, xmppMessage.From, xmppMessage.From.Bare().String() + " is not a known user; please add them to sms-over-xmpp's users file")
	}

	toPhoneNumber, err := service.canonPhoneNumber(xmppMessage.To.LocalPart)
	if err != nil {
		return service.sendXMPPError(xmppMessage.To, xmppMessage.From, fmt.Sprintf("Invalid phone number '%s': %s (example: +12125551212)", xmppMessage.To.LocalPart, err))
	}

	message := &Message{
		From: user.phoneNumber,
		To:   toPhoneNumber,
	}
	if xmppMessage.OutOfBandData != nil {
		message.MediaURLs = append(message.MediaURLs, xmppMessage.OutOfBandData.URL)
	} else {
		message.Body = xmppMessage.Body
	}

	go func() {
		err := user.provider.Send(message)
		if err != nil {
			// TODO: if sendXMPPError fails, log the error
			service.sendXMPPError(xmppMessage.To, xmppMessage.From, "Sending SMS failed: " + err.Error())
		}
	}()

	return nil
}

func (service *Service) receiveXMPPPresence(ctx context.Context, presence *xmpp.Presence) error {
	if presence.From == nil || presence.To == nil {
		return errors.New("Received malformed XMPP presence: From and To not set")
	}

	if _, userExists := service.users[*presence.From.Bare()]; !userExists {
		return nil
	}

	if presence.Type == xmpp.SUBSCRIBE {
		if err := service.sendXMPPPresence(presence.To, presence.From, xmpp.SUBSCRIBED, ""); err != nil {
			return err
		}
	}

	if presence.Type == xmpp.SUBSCRIBE || presence.Type == xmpp.PROBE {
		var presenceType string
		var status string

		if _, err := service.canonPhoneNumber(presence.To.LocalPart); err != nil {
			presenceType = "error"
			status = "Invalid phone number: " + err.Error()
		}

		if err := service.sendXMPPPresence(presence.To, presence.From, presenceType, status); err != nil {
			return err
		}
	}

	return nil
}

func (service *Service) receiveXMPPIq(ctx context.Context, iq *xmpp.Iq) error {
	switch {
	case iq.RosterQuery != nil:
		return service.receiveXMPPRosterQuery(ctx, iq)
	default:
		return nil
	}
}

func (service *Service) receiveXMPPRosterQuery(ctx context.Context, iq *xmpp.Iq) error {
	if iq.From == nil || iq.To == nil {
		return errors.New("Received malformed XMPP iq: From and To not set")
	}

	user, userExists := service.users[*iq.From.Bare()]
	if !userExists {
		return nil
	}

	switch iq.Type {
	case "set":
		return service.receiveXMPPRosterSet(ctx, user.state, iq.RosterQuery)
	case "result":
		return service.receiveXMPPRosterResult(ctx, user.state, iq.RosterQuery)
	default:
		return nil
	}
}

func (service *Service) receiveXMPPRosterSet(ctx context.Context, user *userState, query *xmpp.RosterQuery) error {
	user.rosterMu.Lock()
	defer user.rosterMu.Unlock()

	if user.roster == nil {
		return nil
	}
	if len(query.Items) != 1 {
		return nil
	}
	item := query.Items[0]
	if item.Subscription == "remove" {
		delete(user.roster, item.JID)
	} else {
		user.roster[item.JID] = RosterItem{
			Name:   item.Name,
			Groups: item.Groups,
		}
	}

	return nil
}

func (service *Service) receiveXMPPRosterResult(ctx context.Context, user *userState, query *xmpp.RosterQuery) error {
	user.rosterMu.Lock()
	defer user.rosterMu.Unlock()

	user.roster = make(Roster)
	for _, item := range query.Items {
		if item.Subscription == "remove" {
			continue
		}
		user.roster[item.JID] = RosterItem{
			Name:   item.Name,
			Groups: item.Groups,
		}
	}

	return nil
}

func replaceRoster(user *userState, newRoster Roster) ([]xmpp.RosterItem, error) {
	user.rosterMu.Lock()
	defer user.rosterMu.Unlock()
	if user.roster == nil {
		return nil, ErrRosterNotIntialized
	}

	changes := []xmpp.RosterItem{}

	for jid, newItem := range newRoster {
		curItem, exists := user.roster[jid]
		if !exists || !curItem.Equal(newItem) {
			user.roster[jid] = newItem
			changes = append(changes, xmpp.RosterItem{
				JID:          jid,
				Name:         newItem.Name,
				Subscription: "both",
				Groups:       newItem.Groups,
			})
		}
	}
	for jid := range user.roster {
		_, exists := newRoster[jid]
		if !exists {
			delete(user.roster, jid)
			changes = append(changes, xmpp.RosterItem{
				JID:          jid,
				Subscription: "remove",
			})
		}
	}

	return changes, nil
}

func (service *Service) SetRoster(ctx context.Context, userJID xmpp.Address, newRoster Roster) error {
	user, userExists := service.users[userJID]
	if !userExists {
		return errors.New("no such user")
	}

	changes, err := replaceRoster(user.state, newRoster)
	if err != nil {
		return err
	}

	for _, changedItem := range changes {
		query := xmpp.RosterQuery{
			Items: []xmpp.RosterItem{changedItem},
		}
		if err := service.sendXMPPRosterQuery(xmpp.RandomID(), userJID, "set", query); err != nil {
			return err
		}
	}
	return nil
}

func (service *Service) sendXMPPRosterQuery(id string, to xmpp.Address, iqType string, query xmpp.RosterQuery) error {
	iq := &xmpp.Iq{
		Header: xmpp.Header{
			ID: id,
			To: &to,
		},
		Type: iqType,
		RosterQuery: &query,
	}
	if !service.sendWithin(5*time.Second, iq) {
		return errors.New("Timed out when sending XMPP iq message")
	}
	return nil
}

func (service *Service) sendXMPPError(from *xmpp.Address, to *xmpp.Address, message string) error {
	xmppMessage := &xmpp.Message{
		Header: xmpp.Header{
			From: from,
			To:   to,
		},
		Type: xmpp.ERROR,
		Body: message,
	}
	if !service.sendWithin(5*time.Second, xmppMessage) {
		return errors.New("Timed out when sending XMPP error message")
	}
	return nil
}

func (service *Service) sendXMPPPresence(from *xmpp.Address, to *xmpp.Address, presenceType string, status string) error {
	xmppPresence := &xmpp.Presence{
		Header: xmpp.Header{
			From: from,
			To:   to,
		},
		Type: presenceType,
		Status: status,
	}
	if !service.sendWithin(5*time.Second, xmppPresence) {
		return errors.New("Timed out when sending XMPP presence")
	}
	return nil
}

func (service *Service) addressForPhoneNumber(phoneNumber string) (xmpp.Address, bool) {
	for address, user := range service.users {
		if user.phoneNumber == phoneNumber {
			return address, true
		}
	}
	return xmpp.Address{}, false
}

func (service *Service) canonPhoneNumber(phoneNumber string) (string, error) {
	if !strings.HasPrefix(phoneNumber, "+") {
		if service.defaultPrefix == "" {
			return "", errors.New("does not start with + (please prefix number with + and a country code, or configure the default_prefix option)")
		}
		phoneNumber = service.defaultPrefix + phoneNumber
	}
	return phoneNumber, validatePhoneNumber(phoneNumber)
}

func (service *Service) friendlyPhoneNumber(phoneNumber string) string {
	return strings.TrimPrefix(phoneNumber, service.defaultPrefix)
}
<|endoftext|>


<commit_before>package ses

import (
	"net/mail"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/ses"
)

// New to new a ses.SES
func New(AWSID, AWSKEY string) *ses.SES {
	var config = aws.DefaultConfig
	config.Region = "us-east-1"
	config.Credentials = credentials.NewStaticCredentials(AWSID, AWSKEY, "")
	return ses.New(config)
}

// Message to render a ses.SendEmailInput
func Message(ToUsers []*mail.Address, Sender *mail.Address, Subject, Content string) *ses.SendEmailInput {
	var toUsers []*string
	var mailCharset = aws.String("UTF-8")
	var mailContent = aws.String(Content)
	var mailSubject = aws.String(Subject)

	toUsers = make([]*string, len(ToUsers))
	for i, v := range ToUsers {
		toUsers[i] = aws.String(v.String())
	}

	return &ses.SendEmailInput{
		Destination: &ses.Destination{
			ToAddresses: toUsers,
		},
		Message: &ses.Message{
			Body: &ses.Body{
				HTML: &ses.Content{
					Charset: mailCharset,
					Data:    mailContent,
				},
			},
			Subject: &ses.Content{
				Charset: mailCharset,
				Data:    mailSubject,
			},
		},
		Source: aws.String(Sender.String()),
	}
}
<commit_msg>Tiny changed.<commit_after>package ses

import (
	"net/mail"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/ses"
)

// New to new a ses.SES
func New(AWSID, AWSKEY string) *ses.SES {
	var config = aws.DefaultConfig
	config.Region = "us-east-1"
	config.Credentials = credentials.NewStaticCredentials(AWSID, AWSKEY, "")
	return ses.New(config)
}

// Message to render a ses.SendEmailInput
func Message(ToUsers []*mail.Address, Sender *mail.Address,
	Subject, Content string) *ses.SendEmailInput {

	var mailCharset = aws.String("UTF-8")
	var toUsers []*string

	toUsers = make([]*string, len(ToUsers))
	for i, v := range ToUsers {
		toUsers[i] = aws.String(v.String())
	}

	return &ses.SendEmailInput{
		Destination: &ses.Destination{
			ToAddresses: toUsers,
		},
		Message: &ses.Message{
			Body: &ses.Body{
				HTML: &ses.Content{
					Charset: mailCharset,
					Data:    aws.String(Content),
				},
			},
			Subject: &ses.Content{
				Charset: mailCharset,
				Data:    aws.String(Subject),
			},
		},
		Source: aws.String(Sender.String()),
	}
}
<|endoftext|>


<commit_before>package statistic

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

var Endpoint string
var Username string
var Password string

type StatisticProduct struct {
	Id         int    `json:"id"`
	Name       string `json:"name"`
	Ean        string `json:"product_ean"`
	Brand      string `json:"product_brand"`
	ShopCode   string `json:"shop_code"`
	MaxCPO     int    `json:"max_cpo"`
	Category   string `json:"product_category_name"`
	Price      int    `json:"product_price"`
	Statistics []*DailyStatistic
}

func (statsProduct *StatisticProduct) TotalCosts() float64 {
	var total float64
	for _, stat := range statsProduct.Statistics {
		total += stat.Costs
	}
	return total
}

// Totals up the traffic from the daily stats record and
// converts to int cause traffic would be a whole number
func (statsProduct *StatisticProduct) TotalTraffic() int {
	var total int
	for _, stat := range statsProduct.Statistics {
		total += int(stat.Traffic)
	}
	return total
}
func (statsProduct *StatisticProduct) TotalProfit() float64 {
	var total float64
	for _, stat := range statsProduct.Statistics {
		total += stat.Profit
	}
	return total
}

type DailyStatistic struct {
	BounceRate              float64 `json:"bounce_rate"`
	CCPO                    float64 `json:"ccpo"`
	CEXAmount               float64 `json:"cex_amount"`
	ContributedProfit       float64 `json:"contributed_profit"`
	Contribution            float64 `json:"contribution"`
	Conversion              float64 `json:"conversion"`
	Costs                   float64 `json:"costs"`
	CPO                     float64 `json:"cpo"`
	CROAS                   float64 `json:"croas"`
	CROI                    float64 `json:"croi"`
	ECPC                    float64 `json:"ecpc"`
	OrderAmountExcludingTax float64 `json:"order_amount_excluding_tax"`
	OrderAmountIncludingTax float64 `json:"order_amount_including_tax"`
	Orders                  float64 `json:"orders"`
	Quantity                float64 `json:"quantity"`
	Roas                    float64 `json:"roas"`
	Profit                  float64 `json:"profit"`
	Traffic                 float64 `json:"traffic"`
	Tos                     float64 `json:"tos"`
	Contributed             float64 `json:"contributed"`
	Views                   float64 `json:"views"`
	Assists                 float64 `json:"assists"`
	AssistRatio             float64 `json:"assist_ratio"`
	TimeId                  string  `json:"time_id"`
}

type DailyProductsQuery struct {
	ShopId      int
	PublisherId int
	StartDate   time.Time
	StopDate    time.Time
	ShopCodes   *[]string
	Limit       *int
	Skip        *int
}

func (productsQuery *DailyProductsQuery) RawQuery() string {
	query := url.Values{}

	query.Add("time_id", fmt.Sprintf("%s:%s", DailyTimeId(productsQuery.StartDate), DailyTimeId(productsQuery.StopDate)))

	if productsQuery.ShopCodes != nil {
		for _, shopCode := range *productsQuery.ShopCodes {
			query.Add("shop_codes[]", shopCode)
		}
	}

	if productsQuery.Limit != nil {
		query.Add("limit", strconv.Itoa(*productsQuery.Limit))
	}
	if productsQuery.Skip != nil {
		query.Add("skip", strconv.Itoa(*productsQuery.Skip))
	}

	return query.Encode()
}

type DailyProductFinder func(*DailyProductsQuery) ([]*StatisticProduct, error)

func FindDailyProduct(query *DailyProductsQuery) ([]*StatisticProduct, error) {
	url, err := getStatsUrl(query)
	if err != nil {
		return nil, err
	}

	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	request.SetBasicAuth(Username, Password)
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		return nil, err
	}
	stats := []*StatisticProduct{}
	err = json.NewDecoder(response.Body).Decode(&stats)
	if err != nil {
		return nil, err
	}
	return stats, nil
}

func getStatsUrl(query *DailyProductsQuery) (string, error) {
	statsUrl, err := url.Parse(fmt.Sprintf("%s/api/v1/shops/%d/publishers/%d/shop_products/statistics.json", Endpoint, query.ShopId, query.PublisherId))
	if err != nil {
		return "", err
	}
	// query := statsUrl.Query()
	// query.Add("time_id", fmt.Sprintf("%s:%s", DailyTimeId(startDate), DailyTimeId(stopDate)))
	//
	// for _, shopCode := range shopCodes {
	// 	query.Add("shop_codes[]", shopCode)
	// }
	statsUrl.RawQuery = query.RawQuery()
	return statsUrl.String(), nil
}
<commit_msg>Removing commented code<commit_after>package statistic

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

var Endpoint string
var Username string
var Password string

type StatisticProduct struct {
	Id         int    `json:"id"`
	Name       string `json:"name"`
	Ean        string `json:"product_ean"`
	Brand      string `json:"product_brand"`
	ShopCode   string `json:"shop_code"`
	MaxCPO     int    `json:"max_cpo"`
	Category   string `json:"product_category_name"`
	Price      int    `json:"product_price"`
	Statistics []*DailyStatistic
}

func (statsProduct *StatisticProduct) TotalCosts() float64 {
	var total float64
	for _, stat := range statsProduct.Statistics {
		total += stat.Costs
	}
	return total
}

// Totals up the traffic from the daily stats record and
// converts to int cause traffic would be a whole number
func (statsProduct *StatisticProduct) TotalTraffic() int {
	var total int
	for _, stat := range statsProduct.Statistics {
		total += int(stat.Traffic)
	}
	return total
}
func (statsProduct *StatisticProduct) TotalProfit() float64 {
	var total float64
	for _, stat := range statsProduct.Statistics {
		total += stat.Profit
	}
	return total
}

type DailyStatistic struct {
	BounceRate              float64 `json:"bounce_rate"`
	CCPO                    float64 `json:"ccpo"`
	CEXAmount               float64 `json:"cex_amount"`
	ContributedProfit       float64 `json:"contributed_profit"`
	Contribution            float64 `json:"contribution"`
	Conversion              float64 `json:"conversion"`
	Costs                   float64 `json:"costs"`
	CPO                     float64 `json:"cpo"`
	CROAS                   float64 `json:"croas"`
	CROI                    float64 `json:"croi"`
	ECPC                    float64 `json:"ecpc"`
	OrderAmountExcludingTax float64 `json:"order_amount_excluding_tax"`
	OrderAmountIncludingTax float64 `json:"order_amount_including_tax"`
	Orders                  float64 `json:"orders"`
	Quantity                float64 `json:"quantity"`
	Roas                    float64 `json:"roas"`
	Profit                  float64 `json:"profit"`
	Traffic                 float64 `json:"traffic"`
	Tos                     float64 `json:"tos"`
	Contributed             float64 `json:"contributed"`
	Views                   float64 `json:"views"`
	Assists                 float64 `json:"assists"`
	AssistRatio             float64 `json:"assist_ratio"`
	TimeId                  string  `json:"time_id"`
}

type DailyProductsQuery struct {
	ShopId      int
	PublisherId int
	StartDate   time.Time
	StopDate    time.Time
	ShopCodes   *[]string
	Limit       *int
	Skip        *int
}

func (productsQuery *DailyProductsQuery) RawQuery() string {
	query := url.Values{}

	query.Add("time_id", fmt.Sprintf("%s:%s", DailyTimeId(productsQuery.StartDate), DailyTimeId(productsQuery.StopDate)))

	if productsQuery.ShopCodes != nil {
		for _, shopCode := range *productsQuery.ShopCodes {
			query.Add("shop_codes[]", shopCode)
		}
	}

	if productsQuery.Limit != nil {
		query.Add("limit", strconv.Itoa(*productsQuery.Limit))
	}
	if productsQuery.Skip != nil {
		query.Add("skip", strconv.Itoa(*productsQuery.Skip))
	}

	return query.Encode()
}

type DailyProductFinder func(*DailyProductsQuery) ([]*StatisticProduct, error)

func FindDailyProduct(query *DailyProductsQuery) ([]*StatisticProduct, error) {
	url, err := getStatsUrl(query)
	if err != nil {
		return nil, err
	}

	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	request.SetBasicAuth(Username, Password)
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		return nil, err
	}
	stats := []*StatisticProduct{}
	err = json.NewDecoder(response.Body).Decode(&stats)
	if err != nil {
		return nil, err
	}
	return stats, nil
}

func getStatsUrl(query *DailyProductsQuery) (string, error) {
	statsUrl, err := url.Parse(fmt.Sprintf("%s/api/v1/shops/%d/publishers/%d/shop_products/statistics.json", Endpoint, query.ShopId, query.PublisherId))
	if err != nil {
		return "", err
	}
	statsUrl.RawQuery = query.RawQuery()
	return statsUrl.String(), nil
}
<|endoftext|>


<commit_before>package frame

import (
	_ "github.com/as/etch"
	"image"
)

// Delete deletes the text range represented by [p0,p1) and
// returns the number of text glyphs deleted.
func (f *Frame) Deletex(p0, p1 int64) int {
	if p0 >= f.Nchars || p0 == p1 || f.b == nil {
		return 0
	}
	if p1 > f.Nchars {
		p1 = f.Nchars
	}
	if f.p0 == f.p1 {
		f.tickat(f.PointOf(int64(f.p0)), false)
	}
	f.modified = true

	// Advance forward, copying the first un-deleted box
	// on the right all the way to the left, splitting them
	// when necessary to fit on a wrapped line. A bit of draw
	// computation is saved by keeping track of the selection
	// and interpolating its drawing routine into the same
	// loop.
	//
	// Might have to rethink this when adding support for
	// multiple selections.
	//
	// pt0/pt1: deletion start/stop
	// n0/n1: deleted box/first surviving box
	// cn1: char index of the surviving box

	n0 := f.Find(0, 0, p0)
	n1 := f.Find(n0, p0, p1)
	cn1 := int64(p1)
	nn0 := n0
	f.Free(n0, n1-1)

	pt0 := f.ptOfCharNBox(p0, n0)
	pt1 := f.PointOf(p1)
	ppt0 := pt0

	h := f.Font.Dy()
	pt0, pt1, n0, n1, cn1 = f.delete(pt0, pt1, n0, n1, cn1)

	// Delete more than a line. All the boxes have been shifted
	// but the bitmap might still have a copy of them down below
	if pt1.Y != pt0.Y {
		pt0, pt1, n1 = f.fixTrailer(pt0, pt1, n1)
	}

	f.Run.Close(n0, n1-1)
	if nn0 > 0 && f.Box[nn0-1].Nrune >= 0 && ppt0.X-f.Box[nn0-1].Width >= f.r.Min.X {
		nn0--
		ppt0.X -= f.Box[nn0].Width
	}

	if n0 < f.Nbox-1 {
		n0++
	}
	f.clean(ppt0, nn0, n0)

	if f.p1 > p1 {
		f.p1 -= p1 - p0
	} else if f.p1 > p0 {
		f.p1 = p0
	}
	if f.p0 > p1 {
		f.p0 -= p1 - p0
	} else if f.p0 > p0 {
		f.p0 = p0
	}

	f.Nchars -= p1 - p0
	if f.p0 == f.p1 {
		f.tickat(f.PointOf(f.p0), true)
	}
	pt0 = f.PointOf(f.Nchars)
	extra := 0
	if pt0.X > f.r.Min.X {
		extra = 1
	}
	f.Nlines = (pt0.Y-f.r.Min.Y)/h + extra
	if ForceElasticTabstopExperiment {
		// Just to see if the algorithm works not ideal to sift through all of
		// the boxes per insertion, although surprisingly faster than expected
		// to the point of where its almost unnoticable without the print
		// statements
		f.Stretch(0)
		f.Refresh() // must do this until line mapper is fixed
	}
	return int(p1 - p0) //n - f.Nlines
}
func (f *Frame) Delete(p0, p1 int64) int {

	if p0 >= f.Nchars || p0 == p1 || f.b == nil {
		return 0
	}

	if p1 > f.Nchars {
		p1 = f.Nchars
	}
	if f.p0 == f.p1 {
		f.tickat(f.PointOf(int64(f.p0)), false)
	}
	n0 := f.Find(0, 0, p0)
	nn0 := n0
	n1 := f.Find(n0, p0, p1)
	pt0 := f.ptOfCharNBox(p0, n0)
	ppt0 := pt0
	pt1 := f.PointOf(p1)
	f.Free(n0, n1-1)
	f.modified = true

	cn1 := int64(p1)

	pt0, pt1, n0, n1, cn1 = f.delete(pt0, pt1, n0, n1, cn1)

	if n1 == f.Nbox && pt0.X != pt1.X {
		f.Paint(pt0, pt1, f.Color.Back)
	}

	if pt1.Y != pt0.Y {
		pt0, pt1, n1 = f.fixTrailer(pt0, pt1, n1)
	}

	f.Run.Close(n0, n1-1)
	if nn0 > 0 && f.Box[nn0-1].Nrune >= 0 && ppt0.X-f.Box[nn0-1].Width >= f.r.Min.X {
		nn0--
		ppt0.X -= f.Box[nn0].Width
	}

	if n0 < f.Nbox-1 {
		n0++
	}
	f.clean(ppt0, nn0, n0)

	if f.p1 > p1 {
		f.p1 -= p1 - p0
	} else if f.p1 > p0 {
		f.p1 = p0
	}
	if f.p0 > p1 {
		f.p0 -= p1 - p0
	} else if f.p0 > p0 {
		f.p0 = p0
	}

	f.Nchars -= p1 - p0
	if f.p0 == f.p1 {
		f.tickat(f.PointOf(f.p0), true)
	}
	pt0 = f.PointOf(f.Nchars)
	extra := 0
	if pt0.X > f.r.Min.X {
		extra = 1
	}
	h := f.Font.Dy()
	f.Nlines = (pt0.Y-f.r.Min.Y)/h + extra
	if ForceElasticTabstopExperiment {
		// Just to see if the algorithm works not ideal to sift through all of
		// the boxes per insertion, although surprisingly faster than expected
		// to the point of where its almost unnoticable without the print
		// statements
		f.Stretch(0)
		f.Refresh() // must do this until line mapper is fixed
	}
	return int(p1 - p0) //n - f.Nlines
}
func (f *Frame) delete(pt0, pt1 image.Point, n0, n1 int, cn1 int64) (image.Point, image.Point, int, int, int64) {
	h := f.Font.Dy()
	for pt1.X != pt0.X && n1 < f.Nbox {
		b := &f.Box[n1]
		pt0 = f.lineWrap0(pt0, b)
		pt1 = f.lineWrap(pt1, b)
		r := image.Rectangle{pt0, pt0}
		r.Max.Y += h

		if b.Nrune > 0 { // non-newline
			n := f.canFit(pt0, b)
			if n != b.Nrune {
				f.Split(n1, n)
				b = &f.Box[n1]
			}
			r.Max.X += b.Width
			f.Draw(f.b, r, f.b, pt1, f.op)
			//drawBorder(f.b, r.Inset(-4), Green, image.ZP, 8)
			cn1 += int64(b.Nrune)
		} else {
			r.Max.X = min(r.Max.X+f.newWid0(pt0, b), f.r.Max.X)
			_, col := f.pick(cn1, f.p0, f.p1)
			f.Draw(f.b, r, col, pt0, f.op)
			cn1++
		}
		pt1 = f.advance(pt1, b)
		pt0.X += f.newWid(pt0, b)
		f.Box[n0] = f.Box[n1]
		n0++
		n1++
	}
	return pt0, pt1, n0, n1, cn1
}
func (f *Frame) fixTrailer(pt0, pt1 image.Point, n1 int) (image.Point, image.Point, int) {
	if n1 == f.Nbox && pt0.X != pt1.X {
		f.Paint(pt0, pt1, f.Color.Back)
	}
	h := f.Font.Dy()
	pt2 := f.ptOfCharPtBox(32768, pt1, n1)
	if pt2.Y > f.r.Max.Y {
		pt2.Y = f.r.Max.Y - h
	}
	if n1 < f.Nbox {
		q0 := pt0.Y + h
		q1 := pt1.Y + h
		q2 := pt2.Y + h
		if q2 > f.r.Max.Y {
			q2 = f.r.Max.Y
		}
		f.Draw(f.b, image.Rect(pt0.X, pt0.Y, pt0.X+(f.r.Max.X-pt1.X), q0), f.b, pt1, f.op)
		f.Draw(f.b, image.Rect(f.r.Min.X, q0, f.r.Max.X, q0+(q2-q1)), f.b, image.Pt(f.r.Min.X, q1), f.op)
		f.Paint(image.Pt(pt2.X, pt2.Y-(pt1.Y-pt0.Y)), pt2, f.Color.Back)
	} else {
		f.Paint(pt0, pt2, f.Color.Back)
	}
	return pt0, pt1, n1
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func max(a, b int) int {
	if a < b {
		return a
	}
	return b
}
<commit_msg>remove extra method<commit_after>package frame

import (
	_ "github.com/as/etch"
	"image"
)

// Delete deletes the text range represented by [p0,p1) and
// returns the number of text glyphs deleted.
func (f *Frame) Deletex(p0, p1 int64) int {}
func (f *Frame) Delete(p0, p1 int64) int {

	if p0 >= f.Nchars || p0 == p1 || f.b == nil {
		return 0
	}

	if p1 > f.Nchars {
		p1 = f.Nchars
	}
	if f.p0 == f.p1 {
		f.tickat(f.PointOf(int64(f.p0)), false)
	}

	n0 := f.Find(0, 0, p0)
	nn0 := n0
	n1 := f.Find(n0, p0, p1)
	pt0 := f.ptOfCharNBox(p0, n0)
	ppt0 := pt0
	pt1 := f.PointOf(p1)
	f.Free(n0, n1-1)
	f.modified = true
	cn1 := int64(p1)

	// Advance forward, copying the first un-deleted box
	// on the right all the way to the left, splitting them
	// when necessary to fit on a wrapped line. A bit of draw
	// computation is saved by keeping track of the selection
	// and interpolating its drawing routine into the same
	// loop.
	//
	// Might have to rethink this when adding support for
	// multiple selections.
	//
	// pt0/pt1: deletion start/stop
	// n0/n1: deleted box/first surviving box
	// cn1: char index of the surviving box

	pt0, pt1, n0, n1, cn1 = f.delete(pt0, pt1, n0, n1, cn1)

	if n1 == f.Nbox && pt0.X != pt1.X {
		f.Paint(pt0, pt1, f.Color.Back)
	}

	// Delete more than a line. All the boxes have been shifted
	// but the bitmap might still have a copy of them down below
	if pt1.Y != pt0.Y {
		pt0, pt1, n1 = f.fixTrailer(pt0, pt1, n1)
	}

	f.Run.Close(n0, n1-1)
	if nn0 > 0 && f.Box[nn0-1].Nrune >= 0 && ppt0.X-f.Box[nn0-1].Width >= f.r.Min.X {
		nn0--
		ppt0.X -= f.Box[nn0].Width
	}

	if n0 < f.Nbox-1 {
		n0++
	}
	f.clean(ppt0, nn0, n0)

	if f.p1 > p1 {
		f.p1 -= p1 - p0
	} else if f.p1 > p0 {
		f.p1 = p0
	}
	if f.p0 > p1 {
		f.p0 -= p1 - p0
	} else if f.p0 > p0 {
		f.p0 = p0
	}

	f.Nchars -= p1 - p0
	if f.p0 == f.p1 {
		f.tickat(f.PointOf(f.p0), true)
	}
	pt0 = f.PointOf(f.Nchars)
	extra := 0
	if pt0.X > f.r.Min.X {
		extra = 1
	}
	h := f.Font.Dy()
	f.Nlines = (pt0.Y-f.r.Min.Y)/h + extra
	if ForceElasticTabstopExperiment {
		// Just to see if the algorithm works not ideal to sift through all of
		// the boxes per insertion, although surprisingly faster than expected
		// to the point of where its almost unnoticable without the print
		// statements
		f.Stretch(0)
		f.Refresh() // must do this until line mapper is fixed
	}
	return int(p1 - p0) //n - f.Nlines
}
func (f *Frame) delete(pt0, pt1 image.Point, n0, n1 int, cn1 int64) (image.Point, image.Point, int, int, int64) {
	h := f.Font.Dy()
	for pt1.X != pt0.X && n1 < f.Nbox {
		b := &f.Box[n1]
		pt0 = f.lineWrap0(pt0, b)
		pt1 = f.lineWrap(pt1, b)
		r := image.Rectangle{pt0, pt0}
		r.Max.Y += h

		if b.Nrune > 0 { // non-newline
			n := f.canFit(pt0, b)
			if n != b.Nrune {
				f.Split(n1, n)
				b = &f.Box[n1]
			}
			r.Max.X += b.Width
			f.Draw(f.b, r, f.b, pt1, f.op)
			//drawBorder(f.b, r.Inset(-4), Green, image.ZP, 8)
			cn1 += int64(b.Nrune)
		} else {
			r.Max.X = min(r.Max.X+f.newWid0(pt0, b), f.r.Max.X)
			_, col := f.pick(cn1, f.p0, f.p1)
			f.Draw(f.b, r, col, pt0, f.op)
			cn1++
		}
		pt1 = f.advance(pt1, b)
		pt0.X += f.newWid(pt0, b)
		f.Box[n0] = f.Box[n1]
		n0++
		n1++
	}
	return pt0, pt1, n0, n1, cn1
}
func (f *Frame) fixTrailer(pt0, pt1 image.Point, n1 int) (image.Point, image.Point, int) {
	if n1 == f.Nbox && pt0.X != pt1.X {
		f.Paint(pt0, pt1, f.Color.Back)
	}
	h := f.Font.Dy()
	pt2 := f.ptOfCharPtBox(32768, pt1, n1)
	if pt2.Y > f.r.Max.Y {
		pt2.Y = f.r.Max.Y - h
	}
	if n1 < f.Nbox {
		q0 := pt0.Y + h
		q1 := pt1.Y + h
		q2 := pt2.Y + h
		if q2 > f.r.Max.Y {
			q2 = f.r.Max.Y
		}
		f.Draw(f.b, image.Rect(pt0.X, pt0.Y, pt0.X+(f.r.Max.X-pt1.X), q0), f.b, pt1, f.op)
		f.Draw(f.b, image.Rect(f.r.Min.X, q0, f.r.Max.X, q0+(q2-q1)), f.b, image.Pt(f.r.Min.X, q1), f.op)
		f.Paint(image.Pt(pt2.X, pt2.Y-(pt1.Y-pt0.Y)), pt2, f.Color.Back)
	} else {
		f.Paint(pt0, pt2, f.Color.Back)
	}
	return pt0, pt1, n1
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func max(a, b int) int {
	if a < b {
		return a
	}
	return b
}
<|endoftext|>