<commit_before>package gopivo

import (
	"errors"
	"io"
	"net"
	"sync"
	"time"
)

const defaultJoinLimitRatePerSecond = 64
const defaultJoinLimitRateBurst = 32
const defaultJoinMaxQueueSize = 256

var (
	ErrJoinQueueIsFull  = errors.New("join queue is full")
	ErrNoSuchConnector  = errors.New("no such connector")
	ErrPortBufferIsFull = errors.New("port buffer is full")
)

type Connector interface {
	Error() error
	Initialize() (chan []byte, error)
	RemoteAddr() net.Addr

	Closer(error) error
	Receiver(io.ReadCloser) error
	Sender()
}

type Welcomer interface {
	Welcome() ([]byte, error)
}

type Hub struct {
	lock     *sync.Mutex
	ports    Port
	queue    chan chan bool
	throttle chan time.Time
}

type Port map[Connector]chan []byte

func NewHub() *Hub {
	h := &Hub{
		lock:     &sync.Mutex{},
		ports:    make(Port),
		queue:    make(chan chan bool, defaultJoinMaxQueueSize),
		throttle: make(chan time.Time, defaultJoinLimitRateBurst),
	}
	go h.run()
	return h
}

func (h Hub) run() {
	go h.ticker(defaultJoinLimitRatePerSecond)
	for waiter := range h.queue {
		<-h.throttle
		waiter <- true
	}
}

func (h Hub) ticker(rate time.Duration) {
	for ns := range time.Tick(time.Second / rate) {
		h.throttle <- ns
	}
}

func (h Hub) waitQueue() error {
	waiter := make(chan bool)
	defer close(waiter)

	select {
	case h.queue <- waiter:
	default:
		return ErrJoinQueueIsFull
	}
	<-waiter
	return nil
}

func (h Hub) Broadcast() chan []byte {
	messages := make(chan []byte)
	go func() {
		defer close(messages)
		for msg := range messages {
			h.lock.Lock()
			for c, port := range h.ports {
				select {
				case port <- msg:
				default:
					err := ErrPortBufferIsFull
					go h.Leave(c, err)
				}
			}
			h.lock.Unlock()
		}
	}()
	return messages
}

func (h Hub) Join(c Connector, r io.ReadCloser, w Welcomer) error {
	if err := h.waitQueue(); err != nil {
		c.Closer(err)
		return err
	}

	port, err := c.Initialize()
	if err != nil {
		c.Closer(err)
		return err
	}

	go c.Sender()
	if w != nil {
		msg, err := w.Welcome()
		if err != nil {
			c.Closer(err)
			return err
		} else if len(msg) > 0 {
			port <- msg
		}
	}

	h.lock.Lock()
	h.ports[c] = port
	h.lock.Unlock()
	go func() { h.Leave(c, c.Receiver(r)) }()
	return nil
}

func (h Hub) Leave(c Connector, reason error) error {
	h.lock.Lock()
	defer h.lock.Unlock()
	if _, ok := h.ports[c]; ok {
		delete(h.ports, c)
		return c.Closer(reason)
	}
	return ErrNoSuchConnector
}

/*
func (h Hub) Kill() (error, []error) {
	atomic.AddUint32(&h.state, 1)
	var errors []error
	for conn, _ := range h.ports {
		if err := conn.Closer(); err != nil {
			errors = append(errors, err)
		}
	}

	if len(errors) > 0 {
		return ErrHubKillWentBad, errors
	}
	return nil, nil
}
*/
<commit_msg>Broadcast() -> NewBroadcast()<commit_after>package gopivo

import (
	"errors"
	"io"
	"net"
	"sync"
	"time"
)

const defaultJoinLimitRatePerSecond = 64
const defaultJoinLimitRateBurst = 32
const defaultJoinMaxQueueSize = 256

var (
	ErrJoinQueueIsFull  = errors.New("join queue is full")
	ErrNoSuchConnector  = errors.New("no such connector")
	ErrPortBufferIsFull = errors.New("port buffer is full")
)

type Connector interface {
	Error() error
	Initialize() (chan []byte, error)
	RemoteAddr() net.Addr

	Closer(error) error
	Receiver(io.ReadCloser) error
	Sender()
}

type Welcomer interface {
	Welcome() ([]byte, error)
}

type Broadcast struct {
	C chan []byte
}

type Hub struct {
	lock     *sync.Mutex
	ports    Port
	queue    chan chan bool
	throttle chan time.Time
}

type Port map[Connector]chan []byte

func NewHub() *Hub {
	h := &Hub{
		lock:     &sync.Mutex{},
		ports:    make(Port),
		queue:    make(chan chan bool, defaultJoinMaxQueueSize),
		throttle: make(chan time.Time, defaultJoinLimitRateBurst),
	}
	go h.run()
	return h
}

func (b *Broadcast) broadcast(h *Hub) {
	for msg := range b.C {
		h.lock.Lock()
		for c, port := range h.ports {
			select {
			case port <- msg:
			default:
				err := ErrPortBufferIsFull
				go h.Leave(c, err)
			}
		}
		h.lock.Unlock()
	}
}

func (b *Broadcast) Close() {
	close(b.C)
}

func (h *Hub) run() {
	go h.ticker(defaultJoinLimitRatePerSecond)
	for waiter := range h.queue {
		<-h.throttle
		waiter <- true
	}
}

func (h *Hub) ticker(rate time.Duration) {
	for ns := range time.Tick(time.Second / rate) {
		h.throttle <- ns
	}
}

func (h *Hub) waitQueue() error {
	waiter := make(chan bool)
	defer close(waiter)

	select {
	case h.queue <- waiter:
	default:
		return ErrJoinQueueIsFull
	}
	<-waiter
	return nil
}

func (h *Hub) NewBroadcast() *Broadcast {
	bc := &Broadcast{C: make(chan []byte)}
	go bc.broadcast(h)
	return bc
}

func (h *Hub) Join(c Connector, r io.ReadCloser, w Welcomer) error {
	if err := h.waitQueue(); err != nil {
		c.Closer(err)
		return err
	}

	port, err := c.Initialize()
	if err != nil {
		c.Closer(err)
		return err
	}

	go c.Sender()
	if w != nil {
		msg, err := w.Welcome()
		if err != nil {
			c.Closer(err)
			return err
		} else if len(msg) > 0 {
			port <- msg
		}
	}

	h.lock.Lock()
	h.ports[c] = port
	h.lock.Unlock()
	go func() { h.Leave(c, c.Receiver(r)) }()
	return nil
}

func (h *Hub) Leave(c Connector, reason error) error {
	h.lock.Lock()
	defer h.lock.Unlock()
	if _, ok := h.ports[c]; ok {
		delete(h.ports, c)
		return c.Closer(reason)
	}
	return ErrNoSuchConnector
}

/*
func (h Hub) Kill() (error, []error) {
	atomic.AddUint32(&h.state, 1)
	var errors []error
	for conn, _ := range h.ports {
		if err := conn.Closer(); err != nil {
			errors = append(errors, err)
		}
	}

	if len(errors) > 0 {
		return ErrHubKillWentBad, errors
	}
	return nil, nil
}
*/
<|endoftext|>


<commit_before>/**
 * hub, core of chat application
 */

package tok

import (
	"expvar"
	"log"
)

var (
	expOnline = expvar.NewInt("tokOnline")
	expUp     = expvar.NewInt("tokUp")
	expDown   = expvar.NewInt("tokDown")
	expEnq    = expvar.NewInt("tokEnq")
	expDeq    = expvar.NewInt("tokDeq")
	expErr    = expvar.NewInt("tokErr")
)

type fatFrame struct {
	frame *frame //frame to be sent
	ttl   uint32
	chErr chan error //channel to read send result from
}

type frame struct {
	uid  interface{}
	data []byte
}

//Config to create new Hub
type HubConfig struct {
	Actor Actor //Actor implement dispatch logic
	Q     Queue //Message Queue, if nil, message to offline user will not be cached
	Sso   bool  //If it's true, new connection  with same uid will kick off old ones
}

//Dispatch message between connections
type Hub struct {
	sso           bool
	actor         Actor
	q             Queue
	cons          map[interface{}][]*connection //connection list
	chUp          chan *frame
	chDown        chan *fatFrame //for online user
	chDown2       chan *fatFrame //for all user
	chConState    chan *conState
	chReadSignal  chan interface{}
	chQueryOnline chan chan []interface{}
}

func createHub(actor Actor, q Queue, sso bool) *Hub {
	if READ_TIMEOUT > 0 {
		log.Println("[tok] read timeout is enabled, make sure it's greater than your client ping interval. otherwise you'll get read timeout err")
	} else {
		if actor.Ping() == nil {
			log.Fatalln("[tok] both read timeout and server ping have been disabled, server socket resource leak might happen")
		}
	}

	hub := &Hub{
		sso:           sso,
		actor:         actor,
		q:             q,
		cons:          make(map[interface{}][]*connection),
		chUp:          make(chan *frame),
		chDown:        make(chan *fatFrame),
		chDown2:       make(chan *fatFrame),
		chConState:    make(chan *conState),
		chReadSignal:  make(chan interface{}),
		chQueryOnline: make(chan chan []interface{}),
	}
	go hub.run()
	return hub
}

func (p *Hub) run() {
	for {
		select {
		case state := <-p.chConState:
			//			log.Printf("connection state change: %v, %v \n", state.online, &state.con)

			if state.online {
				p.goOnline(state.con)
			} else {
				p.goOffline(state.con)
			}
			count := int64(len(p.cons))
			expOnline.Set(count)
		case f := <-p.chUp:
			//			log.Println("up data")
			expUp.Add(1)
			go p.actor.OnReceive(f.uid, f.data)
		case ff := <-p.chDown:
			if len(p.cons[ff.frame.uid]) > 0 {
				go p.down(ff)
			} else {
				func() {
					defer func() {
						if err := recover(); err != nil {
							log.Println("bug:", err)
						}
					}()
					ff.chErr <- ErrOffline
					close(ff.chErr)
				}()
			}
		case ff := <-p.chDown2:
			if len(p.cons[ff.frame.uid]) > 0 {
				go p.down(ff)
			} else {
				go p.cache(ff)
			}

		case uid := <-p.chReadSignal:
			//only pop msg for online user
			if len(p.cons[uid]) > 0 {
				go p.popMsg(uid)
			}
		case chOnline := <-p.chQueryOnline:
			result := make([]interface{}, 0, len(p.cons))
			for uid := range p.cons {
				result = append(result, uid)
			}
			chOnline <- result
			close(chOnline)
		}
	}
}

func (p *Hub) popMsg(uid interface{}) {
	if p.q == nil {
		return
	}

	for {
		b, err := p.q.Deq(uid)
		if err != nil {
			log.Println("deq error", err)
			return
		}
		if len(b) == 0 {
			//no more data in queue
			return
		}
		expDeq.Add(1)
		if err := p.Send(uid, b, -1); err != nil {
			log.Println("send err after deq")
			return
		}
	}
}

//Send message to someone,
//ttl is expiry seconds. 0 means forever
//if ttl >= 0 and user is offline, message will be cached for ttl seconds
//if ttl < 0 and user is offline, ErrOffline will be returned
//if ttl >=0 and user is online, but error occurred during send, message will be cached
//if ttl < 0 and user is online, but error occurred during send, the error will be returned
func (p *Hub) Send(to interface{}, b []byte, ttl ...int) error {
	t := -1
	if len(ttl) > 0 {
		t = ttl[0]
	}

	ff := &fatFrame{frame: &frame{uid: to, data: b}, ttl: uint32(t), chErr: make(chan error)}
	if t > 0 {
		p.chDown2 <- ff
	} else {
		p.chDown <- ff
	}
	err := <-ff.chErr
	if err == nil {
		return nil
	}
	expErr.Add(1)
	if t < 0 {
		return err
	}

	ff = &fatFrame{frame: &frame{uid: to, data: b}, ttl: uint32(t), chErr: make(chan error)}
	go p.cache(ff)
	return <-ff.chErr
}

//Query online user list
func (p *Hub) Online() []interface{} {
	ch := make(chan []interface{})
	p.chQueryOnline <- ch
	return <-ch
}

func (p *Hub) cache(ff *fatFrame) {
	defer close(ff.chErr)
	expEnq.Add(1)
	if p.q == nil {
		ff.chErr <- ErrQueueRequired
		return
	}

	f := ff.frame
	if err := p.q.Enq(f.uid, f.data, ff.ttl); err != nil {
		ff.chErr <- err
	}

	go p.actor.OnCache(f.uid)
}

func (p *Hub) down(ff *fatFrame) {
	defer close(ff.chErr)
	expDown.Add(1)

	count := 0
	for _, con := range p.cons[ff.frame.uid] {
		if err := con.Write(ff.frame.data); err != nil {
			ff.chErr <- err
			return
		}
		count += 1
	}

	go p.actor.OnSent(ff.frame.uid, ff.frame.data, count)
}

func (p *Hub) goOffline(conn *connection) {
	l := p.cons[conn.uid]
	rest := connExclude(l, conn)

	//this connection has gotten offline, ignore
	if len(l) == len(rest) {
		return
	}

	if len(rest) == 0 {
		delete(p.cons, conn.uid)
	} else {
		p.cons[conn.uid] = rest
	}

	go func(active int) {
		conn.close()
		p.actor.OnClose(conn.uid, active)
	}(len(rest))
}

func (p *Hub) goOnline(conn *connection) {
	l := p.cons[conn.uid]
	if l == nil {
		l = []*connection{conn}
	} else {
		if p.sso {
			for _, old := range l {
				//				log.Printf("kick %v\n", old)
				//notify before close connection
				go func() {
					old.Write(p.actor.Bye("sso"))
					old.close()
					//after sso kick, only one connection keep active
					p.actor.OnClose(conn.uid, 1)
				}()
			}
			l = []*connection{conn}
		} else {
			exists := false
			for _, c := range l {
				if c == conn {
					log.Println("warning, repeat online: ", c)
					exists = true
					break
				}
			}
			if !exists {
				l = append(l, conn)
			}
		}
	}
	p.cons[conn.uid] = l
	go p.TryDeliver(conn.uid)
}

//try to deliver all messages, if uid is online
func (p *Hub) TryDeliver(uid interface{}) {
	p.chReadSignal <- uid
}

func (p *Hub) stateChange(conn *connection, online bool) {
	p.chConState <- &conState{conn, online}
}

//receive data from user
func (p *Hub) receive(uid interface{}, b []byte) {
	p.chUp <- &frame{uid: uid, data: b}
}

func connExclude(l []*connection, ex *connection) []*connection {
	rest := make([]*connection, 0, len(l))
	for _, c := range l {
		if c != ex {
			rest = append(rest, c)
		}
	}
	return rest
}
<commit_msg>nil protection<commit_after>/**
 * hub, core of chat application
 */

package tok

import (
	"expvar"
	"log"
)

var (
	expOnline = expvar.NewInt("tokOnline")
	expUp     = expvar.NewInt("tokUp")
	expDown   = expvar.NewInt("tokDown")
	expEnq    = expvar.NewInt("tokEnq")
	expDeq    = expvar.NewInt("tokDeq")
	expErr    = expvar.NewInt("tokErr")
)

type fatFrame struct {
	frame *frame //frame to be sent
	ttl   uint32
	chErr chan error //channel to read send result from
}

type frame struct {
	uid  interface{}
	data []byte
}

//Config to create new Hub
type HubConfig struct {
	Actor Actor //Actor implement dispatch logic
	Q     Queue //Message Queue, if nil, message to offline user will not be cached
	Sso   bool  //If it's true, new connection  with same uid will kick off old ones
}

//Dispatch message between connections
type Hub struct {
	sso           bool
	actor         Actor
	q             Queue
	cons          map[interface{}][]*connection //connection list
	chUp          chan *frame
	chDown        chan *fatFrame //for online user
	chDown2       chan *fatFrame //for all user
	chConState    chan *conState
	chReadSignal  chan interface{}
	chQueryOnline chan chan []interface{}
}

func createHub(actor Actor, q Queue, sso bool) *Hub {
	if READ_TIMEOUT > 0 {
		log.Println("[tok] read timeout is enabled, make sure it's greater than your client ping interval. otherwise you'll get read timeout err")
	} else {
		if actor.Ping() == nil {
			log.Fatalln("[tok] both read timeout and server ping have been disabled, server socket resource leak might happen")
		}
	}

	hub := &Hub{
		sso:           sso,
		actor:         actor,
		q:             q,
		cons:          make(map[interface{}][]*connection),
		chUp:          make(chan *frame),
		chDown:        make(chan *fatFrame),
		chDown2:       make(chan *fatFrame),
		chConState:    make(chan *conState),
		chReadSignal:  make(chan interface{}),
		chQueryOnline: make(chan chan []interface{}),
	}
	go hub.run()
	return hub
}

func (p *Hub) run() {
	for {
		select {
		case state := <-p.chConState:
			//			log.Printf("connection state change: %v, %v \n", state.online, &state.con)

			if state.online {
				p.goOnline(state.con)
			} else {
				p.goOffline(state.con)
			}
			count := int64(len(p.cons))
			expOnline.Set(count)
		case f := <-p.chUp:
			//			log.Println("up data")
			expUp.Add(1)
			go p.actor.OnReceive(f.uid, f.data)
		case ff := <-p.chDown:
			if len(p.cons[ff.frame.uid]) > 0 {
				go p.down(ff, p.cons[ff.frame.uid])
			} else {
				func() {
					defer func() {
						if err := recover(); err != nil {
							log.Println("bug:", err)
						}
					}()
					ff.chErr <- ErrOffline
					close(ff.chErr)
				}()
			}
		case ff := <-p.chDown2:
			if len(p.cons[ff.frame.uid]) > 0 {
				go p.down(ff, p.cons[ff.frame.uid])
			} else {
				go p.cache(ff)
			}

		case uid := <-p.chReadSignal:
			//only pop msg for online user
			if len(p.cons[uid]) > 0 {
				go p.popMsg(uid)
			}
		case chOnline := <-p.chQueryOnline:
			result := make([]interface{}, 0, len(p.cons))
			for uid := range p.cons {
				result = append(result, uid)
			}
			chOnline <- result
			close(chOnline)
		}
	}
}

func (p *Hub) popMsg(uid interface{}) {
	if p.q == nil {
		return
	}

	for {
		b, err := p.q.Deq(uid)
		if err != nil {
			log.Println("deq error", err)
			return
		}
		if len(b) == 0 {
			//no more data in queue
			return
		}
		expDeq.Add(1)
		if err := p.Send(uid, b, -1); err != nil {
			log.Println("send err after deq")
			return
		}
	}
}

//Send message to someone,
//ttl is expiry seconds. 0 means forever
//if ttl >= 0 and user is offline, message will be cached for ttl seconds
//if ttl < 0 and user is offline, ErrOffline will be returned
//if ttl >=0 and user is online, but error occurred during send, message will be cached
//if ttl < 0 and user is online, but error occurred during send, the error will be returned
func (p *Hub) Send(to interface{}, b []byte, ttl ...int) error {
	t := -1
	if len(ttl) > 0 {
		t = ttl[0]
	}

	ff := &fatFrame{frame: &frame{uid: to, data: b}, ttl: uint32(t), chErr: make(chan error)}
	if t > 0 {
		p.chDown2 <- ff
	} else {
		p.chDown <- ff
	}
	err := <-ff.chErr
	if err == nil {
		return nil
	}
	expErr.Add(1)
	if t < 0 {
		return err
	}

	ff = &fatFrame{frame: &frame{uid: to, data: b}, ttl: uint32(t), chErr: make(chan error)}
	go p.cache(ff)
	return <-ff.chErr
}

//Query online user list
func (p *Hub) Online() []interface{} {
	ch := make(chan []interface{})
	p.chQueryOnline <- ch
	return <-ch
}

func (p *Hub) cache(ff *fatFrame) {
	defer close(ff.chErr)
	expEnq.Add(1)
	if p.q == nil {
		ff.chErr <- ErrQueueRequired
		return
	}

	f := ff.frame
	if err := p.q.Enq(f.uid, f.data, ff.ttl); err != nil {
		ff.chErr <- err
	}

	go p.actor.OnCache(f.uid)
}

func (p *Hub) down(ff *fatFrame, conns []*connection) {
	defer func(){
		if ff == nil{
			log.Println("defer hub.down, ff is nil")
			return
		}
		close(ff.chErr)
	}()
	expDown.Add(1)

	if ff == nil{
		log.Println("hub.down, ff is nil")
		return
	}
	if ff.frame == nil{
		log.Println("hub.down, ff.frame is nil")
		return
	}

	count := 0
	for _, con := range conns {
		if con == nil{
			log.Println("hub.down, conn is nil")
			continue
		}
		if err := con.Write(ff.frame.data); err != nil {
			ff.chErr <- err
			return
		}
		count += 1
	}

	go p.actor.OnSent(ff.frame.uid, ff.frame.data, count)
}

func (p *Hub) goOffline(conn *connection) {
	l := p.cons[conn.uid]
	rest := connExclude(l, conn)

	//this connection has gotten offline, ignore
	if len(l) == len(rest) {
		return
	}

	if len(rest) == 0 {
		delete(p.cons, conn.uid)
	} else {
		p.cons[conn.uid] = rest
	}

	go func(active int) {
		conn.close()
		p.actor.OnClose(conn.uid, active)
	}(len(rest))
}

func (p *Hub) goOnline(conn *connection) {
	l := p.cons[conn.uid]
	if l == nil {
		l = []*connection{conn}
	} else {
		if p.sso {
			for _, old := range l {
				//				log.Printf("kick %v\n", old)
				//notify before close connection
				go func() {
					old.Write(p.actor.Bye("sso"))
					old.close()
					//after sso kick, only one connection keep active
					p.actor.OnClose(conn.uid, 1)
				}()
			}
			l = []*connection{conn}
		} else {
			exists := false
			for _, c := range l {
				if c == conn {
					log.Println("warning, repeat online: ", c)
					exists = true
					break
				}
			}
			if !exists {
				l = append(l, conn)
			}
		}
	}
	p.cons[conn.uid] = l
	go p.TryDeliver(conn.uid)
}

//try to deliver all messages, if uid is online
func (p *Hub) TryDeliver(uid interface{}) {
	p.chReadSignal <- uid
}

func (p *Hub) stateChange(conn *connection, online bool) {
	p.chConState <- &conState{conn, online}
}

//receive data from user
func (p *Hub) receive(uid interface{}, b []byte) {
	p.chUp <- &frame{uid: uid, data: b}
}

func connExclude(l []*connection, ex *connection) []*connection {
	rest := make([]*connection, 0, len(l))
	for _, c := range l {
		if c != ex {
			rest = append(rest, c)
		}
	}
	return rest
}
<|endoftext|>


<commit_before>// i2c libary for intel edison
// Taken for the embed project changes made for intel edison

// The MIT License (MIT)
// Copyright (c) 2015 NeuralSpaz
// 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.

package i2c

import (
	"fmt"
	"os"
	"reflect"
	"sync"
	"syscall"
	"time"
	"unsafe"
)

// S     (1 bit) : Start bit
// P     (1 bit) : Stop bit
// Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.
// A, NA (1 bit) : Accept and reverse accept bit.
// Addr  (7 bits): I2C 7 bit address. Note that this can be expanded as usual to
//                 get a 10 bit I2C address.
// Comm  (8 bits): Command byte, a data byte which often selects a register on
//                 the device.
// Data  (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh
//                 for 16 bit data.
// Count (8 bits): A data byte containing the length of a block operation.
// {..}: Data sent by I2C slave, as opposed to data sent by master.
type I2CBus interface {
	// ReadByte reads a byte from the given address.
	// S Addr Rd {A} {value} NA P
	ReadByte(addr byte) (value byte, err error)
	// WriteByte writes a byte to the given address.
	// S Addr Wr {A} value {A} P
	WriteByte(addr, value byte) error
	// WriteBytes writes a slice bytes to the given address.
	// S Addr Wr {A} value[0] {A} value[1] {A} ... {A} value[n] {A} NA P
	WriteBytes(addr byte, value []byte) error
	// ReadFromReg reads n (len(value)) bytes from the given address and register.
	ReadFromReg(addr, reg byte, value []byte) error
	// ReadByteFromReg reads a byte from the given address and register.
	ReadByteFromReg(addr, reg byte) (value byte, err error)
	// ReadU16FromReg reads a unsigned 16 bit integer from the given address and register.
	ReadWordFromReg(addr, reg byte) (value uint16, err error)
	// WriteToReg writes len(value) bytes to the given address and register.
	WriteToReg(addr, reg byte, value []byte) error
	// WriteByteToReg writes a byte to the given address and register.
	WriteByteToReg(addr, reg, value byte) error
	// WriteU16ToReg
	WriteWordToReg(addr, reg byte, value uint16) error
	// Close releases the resources associated with the bus.
	Close() error
}

const (
	delay    = 1      // delay in milliseconds
	slaveCmd = 0x0703 // Cmd to set slave address
	rdrwCmd  = 0x0707 // Cmd to read/write data together
	rd       = 0x0001
)

type i2c_msg struct {
	addr  uint16
	flags uint16
	len   uint16
	buf   uintptr
}

type i2c_rdwr_ioctl_data struct {
	msgs uintptr
	nmsg uint32
}

type i2cBus struct {
	l    byte
	file *os.File
	addr byte
	mu   sync.Mutex

	initialized bool
}

// Returns New i2c interfce on bus use i2cdetect to find out which bus you to use
func NewI2CBus(l byte) I2CBus {
	return &i2cBus{l: l}
}

func (b *i2cBus) init() error {
	if b.initialized {
		return nil
	}

	var err error
	if b.file, err = os.OpenFile(fmt.Sprintf("/dev/i2c-%v", b.l), os.O_RDWR, os.ModeExclusive); err != nil {
		return err
	}

	fmt.Println("i2c: bus %v initialized", b.l)

	b.initialized = true

	return nil
}

func (b *i2cBus) setAddress(addr byte) error {
	if addr != b.addr {
		fmt.Println("i2c: setting bus %v address to %#02x", b.l, addr)
		if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), slaveCmd, uintptr(addr)); errno != 0 {
			return syscall.Errno(errno)
		}

		b.addr = addr
	}

	return nil
}

// ReadByte reads a byte from the given address.
func (b *i2cBus) ReadByte(addr byte) (byte, error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return 0, err
	}

	if err := b.setAddress(addr); err != nil {
		return 0, err
	}

	bytes := make([]byte, 1)
	n, _ := b.file.Read(bytes)

	if n != 1 {
		return 0, fmt.Errorf("i2c: Unexpected number (%v) of bytes read", n)
	}

	return bytes[0], nil
}

// WriteByte writes a byte to the given address.
func (b *i2cBus) WriteByte(addr, value byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	n, err := b.file.Write([]byte{value})

	if n != 1 {
		err = fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteByte", n)
	}

	return err
}

// WriteBytes writes a slice bytes to the given address.
func (b *i2cBus) WriteBytes(addr byte, value []byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	for i := range value {
		n, err := b.file.Write([]byte{value[i]})

		if n != 1 {
			return fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteBytes", n)
		}
		if err != nil {
			return err
		}

		time.Sleep(delay * time.Millisecond)
	}

	return nil
}

// ReadFromReg reads n (len(value)) bytes from the given address and register.
func (b *i2cBus) ReadFromReg(addr, reg byte, value []byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&value))

	var messages [2]i2c_msg
	messages[0].addr = uint16(addr)
	messages[0].flags = 0
	messages[0].len = 1
	messages[0].buf = uintptr(unsafe.Pointer(&reg))

	messages[1].addr = uint16(addr)
	messages[1].flags = rd
	messages[1].len = uint16(len(value))
	messages[1].buf = uintptr(unsafe.Pointer(hdrp.Data))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&messages))
	packets.nmsg = 2

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// ReadByteFromReg reads a byte from the given address and register.
func (b *i2cBus) ReadByteFromReg(addr, reg byte) (byte, error) {
	buf := make([]byte, 1)
	if err := b.ReadFromReg(addr, reg, buf); err != nil {
		return 0, err
	}
	return buf[0], nil
}

// Read single word from register first byte is MSB
func (b *i2cBus) ReadWordFromReg(addr, reg byte) (uint16, error) {
	buf := make([]byte, 2)
	if err := b.ReadFromReg(addr, reg, buf); err != nil {
		return 0, err
	}
	return uint16((uint16(buf[0]) << 8) | uint16(buf[1])), nil
}

// Read single word from register first byte is LSB
func (b *i2cBus) ReadWordFromRegLSBF(addr, reg byte) (uint16, error) {
	buf := make([]byte, 2)
	if err := b.ReadFromReg(addr, reg, buf); err != nil {
		return 0, err
	}
	return uint16((uint16(buf[1]) << 8) | uint16(buf[0])), nil
}

//Write []byte word to resgister
func (b *i2cBus) WriteToReg(addr, reg byte, value []byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	outbuf := append([]byte{reg}, value...)

	hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&outbuf))

	var message i2c_msg
	message.addr = uint16(addr)
	message.flags = 0
	message.len = uint16(len(outbuf))
	message.buf = uintptr(unsafe.Pointer(&hdrp.Data))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&message))
	packets.nmsg = 1

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// Write single Byte to register
func (b *i2cBus) WriteByteToReg(addr, reg, value byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	outbuf := [...]byte{
		reg,
		value,
	}

	var message i2c_msg
	message.addr = uint16(addr)
	message.flags = 0
	message.len = uint16(len(outbuf))
	message.buf = uintptr(unsafe.Pointer(&outbuf))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&message))
	packets.nmsg = 1

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// Write Single Word to Register
func (b *i2cBus) WriteWordToReg(addr, reg byte, value uint16) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	outbuf := [...]byte{
		reg,
		byte(value >> 8),
		byte(value),
	}

	var messages i2c_msg
	messages.addr = uint16(addr)
	messages.flags = 0
	messages.len = uint16(len(outbuf))
	messages.buf = uintptr(unsafe.Pointer(&outbuf))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&messages))
	packets.nmsg = 1

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// Close i2c file
func (b *i2cBus) Close() error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if !b.initialized {
		return nil
	}

	return b.file.Close()
}
<commit_msg>LSBF word<commit_after>// i2c libary for intel edison
// Taken for the embed project changes made for intel edison

// The MIT License (MIT)
// Copyright (c) 2015 NeuralSpaz
// 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.

package i2c

import (
	"fmt"
	"os"
	"reflect"
	"sync"
	"syscall"
	"time"
	"unsafe"
)

// S     (1 bit) : Start bit
// P     (1 bit) : Stop bit
// Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0.
// A, NA (1 bit) : Accept and reverse accept bit.
// Addr  (7 bits): I2C 7 bit address. Note that this can be expanded as usual to
//                 get a 10 bit I2C address.
// Comm  (8 bits): Command byte, a data byte which often selects a register on
//                 the device.
// Data  (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh
//                 for 16 bit data.
// Count (8 bits): A data byte containing the length of a block operation.
// {..}: Data sent by I2C slave, as opposed to data sent by master.
type I2CBus interface {
	// ReadByte reads a byte from the given address.
	// S Addr Rd {A} {value} NA P
	ReadByte(addr byte) (value byte, err error)
	// WriteByte writes a byte to the given address.
	// S Addr Wr {A} value {A} P
	WriteByte(addr, value byte) error
	// WriteBytes writes a slice bytes to the given address.
	// S Addr Wr {A} value[0] {A} value[1] {A} ... {A} value[n] {A} NA P
	WriteBytes(addr byte, value []byte) error
	// ReadFromReg reads n (len(value)) bytes from the given address and register.
	ReadFromReg(addr, reg byte, value []byte) error
	// ReadByteFromReg reads a byte from the given address and register.
	ReadByteFromReg(addr, reg byte) (value byte, err error)
	// ReadU16FromReg reads a unsigned 16 bit integer from the given address and register.
	ReadWordFromReg(addr, reg byte) (value uint16, err error)
	ReadWordFromRegLSBF(addr, reg byte) (value uint16, err error)
	// WriteToReg writes len(value) bytes to the given address and register.
	WriteToReg(addr, reg byte, value []byte) error
	// WriteByteToReg writes a byte to the given address and register.
	WriteByteToReg(addr, reg, value byte) error
	// WriteU16ToReg
	WriteWordToReg(addr, reg byte, value uint16) error
	// Close releases the resources associated with the bus.
	Close() error
}

const (
	delay    = 1      // delay in milliseconds
	slaveCmd = 0x0703 // Cmd to set slave address
	rdrwCmd  = 0x0707 // Cmd to read/write data together
	rd       = 0x0001
)

type i2c_msg struct {
	addr  uint16
	flags uint16
	len   uint16
	buf   uintptr
}

type i2c_rdwr_ioctl_data struct {
	msgs uintptr
	nmsg uint32
}

type i2cBus struct {
	l    byte
	file *os.File
	addr byte
	mu   sync.Mutex

	initialized bool
}

// Returns New i2c interfce on bus use i2cdetect to find out which bus you to use
func NewI2CBus(l byte) I2CBus {
	return &i2cBus{l: l}
}

func (b *i2cBus) init() error {
	if b.initialized {
		return nil
	}

	var err error
	if b.file, err = os.OpenFile(fmt.Sprintf("/dev/i2c-%v", b.l), os.O_RDWR, os.ModeExclusive); err != nil {
		return err
	}

	fmt.Println("i2c: bus %v initialized", b.l)

	b.initialized = true

	return nil
}

func (b *i2cBus) setAddress(addr byte) error {
	if addr != b.addr {
		fmt.Println("i2c: setting bus %v address to %#02x", b.l, addr)
		if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), slaveCmd, uintptr(addr)); errno != 0 {
			return syscall.Errno(errno)
		}

		b.addr = addr
	}

	return nil
}

// ReadByte reads a byte from the given address.
func (b *i2cBus) ReadByte(addr byte) (byte, error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return 0, err
	}

	if err := b.setAddress(addr); err != nil {
		return 0, err
	}

	bytes := make([]byte, 1)
	n, _ := b.file.Read(bytes)

	if n != 1 {
		return 0, fmt.Errorf("i2c: Unexpected number (%v) of bytes read", n)
	}

	return bytes[0], nil
}

// WriteByte writes a byte to the given address.
func (b *i2cBus) WriteByte(addr, value byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	n, err := b.file.Write([]byte{value})

	if n != 1 {
		err = fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteByte", n)
	}

	return err
}

// WriteBytes writes a slice bytes to the given address.
func (b *i2cBus) WriteBytes(addr byte, value []byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	for i := range value {
		n, err := b.file.Write([]byte{value[i]})

		if n != 1 {
			return fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteBytes", n)
		}
		if err != nil {
			return err
		}

		time.Sleep(delay * time.Millisecond)
	}

	return nil
}

// ReadFromReg reads n (len(value)) bytes from the given address and register.
func (b *i2cBus) ReadFromReg(addr, reg byte, value []byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&value))

	var messages [2]i2c_msg
	messages[0].addr = uint16(addr)
	messages[0].flags = 0
	messages[0].len = 1
	messages[0].buf = uintptr(unsafe.Pointer(&reg))

	messages[1].addr = uint16(addr)
	messages[1].flags = rd
	messages[1].len = uint16(len(value))
	messages[1].buf = uintptr(unsafe.Pointer(hdrp.Data))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&messages))
	packets.nmsg = 2

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// ReadByteFromReg reads a byte from the given address and register.
func (b *i2cBus) ReadByteFromReg(addr, reg byte) (byte, error) {
	buf := make([]byte, 1)
	if err := b.ReadFromReg(addr, reg, buf); err != nil {
		return 0, err
	}
	return buf[0], nil
}

// Read single word from register first byte is MSB
func (b *i2cBus) ReadWordFromReg(addr, reg byte) (uint16, error) {
	buf := make([]byte, 2)
	if err := b.ReadFromReg(addr, reg, buf); err != nil {
		return 0, err
	}
	return uint16((uint16(buf[0]) << 8) | uint16(buf[1])), nil
}

// Read single word from register first byte is LSB
func (b *i2cBus) ReadWordFromRegLSBF(addr, reg byte) (uint16, error) {
	buf := make([]byte, 2)
	if err := b.ReadFromReg(addr, reg, buf); err != nil {
		return 0, err
	}
	return uint16((uint16(buf[1]) << 8) | uint16(buf[0])), nil
}

//Write []byte word to resgister
func (b *i2cBus) WriteToReg(addr, reg byte, value []byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	outbuf := append([]byte{reg}, value...)

	hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&outbuf))

	var message i2c_msg
	message.addr = uint16(addr)
	message.flags = 0
	message.len = uint16(len(outbuf))
	message.buf = uintptr(unsafe.Pointer(&hdrp.Data))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&message))
	packets.nmsg = 1

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// Write single Byte to register
func (b *i2cBus) WriteByteToReg(addr, reg, value byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	outbuf := [...]byte{
		reg,
		value,
	}

	var message i2c_msg
	message.addr = uint16(addr)
	message.flags = 0
	message.len = uint16(len(outbuf))
	message.buf = uintptr(unsafe.Pointer(&outbuf))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&message))
	packets.nmsg = 1

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// Write Single Word to Register
func (b *i2cBus) WriteWordToReg(addr, reg byte, value uint16) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.init(); err != nil {
		return err
	}

	if err := b.setAddress(addr); err != nil {
		return err
	}

	outbuf := [...]byte{
		reg,
		byte(value >> 8),
		byte(value),
	}

	var messages i2c_msg
	messages.addr = uint16(addr)
	messages.flags = 0
	messages.len = uint16(len(outbuf))
	messages.buf = uintptr(unsafe.Pointer(&outbuf))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&messages))
	packets.nmsg = 1

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// Close i2c file
func (b *i2cBus) Close() error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if !b.initialized {
		return nil
	}

	return b.file.Close()
}
<|endoftext|>


<commit_before>package sdp

import (
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/pions/webrtc/pkg/ice"
)

// ICECandidate is used to (un)marshal ICE candidates.
type ICECandidate struct {
	Foundation          string
	Component           uint16
	Priority            uint32
	IP                  string
	Protocol            string
	Port                uint16
	Typ                 string
	RelatedAddress      string
	RelatedPort         uint16
	ExtensionAttributes []ICECandidateAttribute
}

// ICECandidateAttribute represents an ICE candidate extension attribute
type ICECandidateAttribute struct {
	Key   string
	Value string
}

// https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-24#section-4.1
// candidate-attribute   = "candidate" ":" foundation SP component-id SP
//                            transport SP
//                            priority SP
//                            connection-address SP     ;from RFC 4566
//                            port         ;port from RFC 4566
//                            SP cand-type
//                            [SP rel-addr]
//                            [SP rel-port]
//                            *(SP extension-att-name SP
//                                 extension-att-value)

func (c ICECandidate) marshalString() string {
	val := fmt.Sprintf("%s %d %s %d %s %d typ %s",
		c.Foundation,
		c.Component,
		c.Protocol,
		c.Priority,
		c.IP,
		c.Port,
		c.Typ)

	if len(c.RelatedAddress) > 0 {
		val = fmt.Sprintf("%s raddr %s rport %d",
			val,
			c.RelatedAddress,
			c.RelatedPort)
	}

	for _, attr := range c.ExtensionAttributes {
		val = fmt.Sprintf("%s %s %s",
			val,
			attr.Key,
			attr.Value)
	}
	return val
}

func (c *ICECandidate) unmarshalString(raw string) error {
	split := strings.Fields(raw)
	if len(split) < 8 {
		return fmt.Errorf("attribute not long enough to be ICE candidate (%d)", len(split))
	}

	// Foundation
	c.Foundation = split[0]

	// Component
	component, err := strconv.ParseUint(split[1], 10, 16)
	if err != nil {
		return fmt.Errorf("could not parse component: %v", err)
	}
	c.Component = uint16(component)

	// Protocol
	c.Protocol = split[2]

	// Priority
	priority, err := strconv.ParseUint(split[3], 10, 32)
	if err != nil {
		return fmt.Errorf("could not parse priority: %v", err)
	}
	c.Priority = uint32(priority)

	// IP
	c.IP = split[4]

	// Port
	port, err := strconv.ParseUint(split[5], 10, 16)
	if err != nil {
		return fmt.Errorf("could not parse port: %v", err)
	}
	c.Port = uint16(port)

	c.Typ = split[7]

	if len(split) <= 8 {
		return nil
	}

	split = split[8:]

	if split[0] == "raddr" {
		if len(split) < 4 {
			return fmt.Errorf("could not parse related addresses: incorrect length")
		}

		// RelatedAddress
		c.RelatedAddress = split[1]

		// RelatedPort
		relatedPort, err := strconv.ParseUint(split[3], 10, 16)
		if err != nil {
			return fmt.Errorf("could not parse port: %v", err)
		}
		c.RelatedPort = uint16(relatedPort)

		if len(split) <= 4 {
			return nil
		}

		split = split[4:]
	}

	for i := 0; len(split) > i+1; i += 2 {
		c.ExtensionAttributes = append(c.ExtensionAttributes, ICECandidateAttribute{
			Key:   split[i],
			Value: split[i+1],
		})
	}

	return nil
}

// TODO: Remove the deprecated code below

// ICECandidateUnmarshal takes a candidate strings and returns a ice.Candidate or nil if it fails to parse
// TODO: return error if parsing fails
// Deprecated: use ICECandidate instead
func ICECandidateUnmarshal(raw string) (*ice.Candidate, error) {
	split := strings.Fields(raw)
	if len(split) < 8 {
		return nil, fmt.Errorf("attribute not long enough to be ICE candidate (%d) %s", len(split), raw)
	}

	getValue := func(key string) string {
		rtrnNext := false
		for _, i := range split {
			if rtrnNext {
				return i
			} else if i == key {
				rtrnNext = true
			}
		}
		return ""
	}

	port, err := strconv.Atoi(split[5])
	if err != nil {
		return nil, err
	}

	transport := split[2]

	// TODO verify valid address
	ip := net.ParseIP(split[4])
	if ip == nil {
		return nil, err
	}

	switch getValue("typ") {
	case "host":
		return ice.NewCandidateHost(transport, ip, port)
	case "srflx":
		return ice.NewCandidateServerReflexive(transport, ip, port, "", 0) // TODO: parse related address
	default:
		return nil, fmt.Errorf("Unhandled candidate typ %s", getValue("typ"))
	}
}

func iceCandidateString(c *ice.Candidate, component int) string {
	// TODO: calculate foundation
	switch c.Type {
	case ice.CandidateTypeHost:
		return fmt.Sprintf("foundation %d %s %d %s %d typ host generation 0",
			component, c.NetworkShort(), c.Priority(c.Type.Preference(), uint16(component)), c.IP, c.Port)

	case ice.CandidateTypeServerReflexive:
		return fmt.Sprintf("foundation %d %s %d %s %d typ srflx raddr %s rport %d generation 0",
			component, c.NetworkShort(), c.Priority(c.Type.Preference(), uint16(component)), c.IP, c.Port,
			c.RelatedAddress.Address, c.RelatedAddress.Port)
	}
	return ""
}

// ICECandidateMarshal takes a candidate and returns a string representation
// Deprecated: use ICECandidate instead
func ICECandidateMarshal(c *ice.Candidate) []string {
	out := make([]string, 0)

	out = append(out, iceCandidateString(c, 1))
	out = append(out, iceCandidateString(c, 2))

	return out
}
<commit_msg>Remove deprecated candidate serialization methods<commit_after>package sdp

import (
	"fmt"
	"strconv"
	"strings"
)

// ICECandidate is used to (un)marshal ICE candidates.
type ICECandidate struct {
	Foundation          string
	Component           uint16
	Priority            uint32
	IP                  string
	Protocol            string
	Port                uint16
	Typ                 string
	RelatedAddress      string
	RelatedPort         uint16
	ExtensionAttributes []ICECandidateAttribute
}

// ICECandidateAttribute represents an ICE candidate extension attribute
type ICECandidateAttribute struct {
	Key   string
	Value string
}

// https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-24#section-4.1
// candidate-attribute   = "candidate" ":" foundation SP component-id SP
//                            transport SP
//                            priority SP
//                            connection-address SP     ;from RFC 4566
//                            port         ;port from RFC 4566
//                            SP cand-type
//                            [SP rel-addr]
//                            [SP rel-port]
//                            *(SP extension-att-name SP
//                                 extension-att-value)

func (c ICECandidate) marshalString() string {
	val := fmt.Sprintf("%s %d %s %d %s %d typ %s",
		c.Foundation,
		c.Component,
		c.Protocol,
		c.Priority,
		c.IP,
		c.Port,
		c.Typ)

	if len(c.RelatedAddress) > 0 {
		val = fmt.Sprintf("%s raddr %s rport %d",
			val,
			c.RelatedAddress,
			c.RelatedPort)
	}

	for _, attr := range c.ExtensionAttributes {
		val = fmt.Sprintf("%s %s %s",
			val,
			attr.Key,
			attr.Value)
	}
	return val
}

func (c *ICECandidate) unmarshalString(raw string) error {
	split := strings.Fields(raw)
	if len(split) < 8 {
		return fmt.Errorf("attribute not long enough to be ICE candidate (%d)", len(split))
	}

	// Foundation
	c.Foundation = split[0]

	// Component
	component, err := strconv.ParseUint(split[1], 10, 16)
	if err != nil {
		return fmt.Errorf("could not parse component: %v", err)
	}
	c.Component = uint16(component)

	// Protocol
	c.Protocol = split[2]

	// Priority
	priority, err := strconv.ParseUint(split[3], 10, 32)
	if err != nil {
		return fmt.Errorf("could not parse priority: %v", err)
	}
	c.Priority = uint32(priority)

	// IP
	c.IP = split[4]

	// Port
	port, err := strconv.ParseUint(split[5], 10, 16)
	if err != nil {
		return fmt.Errorf("could not parse port: %v", err)
	}
	c.Port = uint16(port)

	c.Typ = split[7]

	if len(split) <= 8 {
		return nil
	}

	split = split[8:]

	if split[0] == "raddr" {
		if len(split) < 4 {
			return fmt.Errorf("could not parse related addresses: incorrect length")
		}

		// RelatedAddress
		c.RelatedAddress = split[1]

		// RelatedPort
		relatedPort, err := strconv.ParseUint(split[3], 10, 16)
		if err != nil {
			return fmt.Errorf("could not parse port: %v", err)
		}
		c.RelatedPort = uint16(relatedPort)

		if len(split) <= 4 {
			return nil
		}

		split = split[4:]
	}

	for i := 0; len(split) > i+1; i += 2 {
		c.ExtensionAttributes = append(c.ExtensionAttributes, ICECandidateAttribute{
			Key:   split[i],
			Value: split[i+1],
		})
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/thoj/go-ircevent"
	"math/rand"
	"os"
	"regexp"
	"strings"
	"time"
)

const (
	botName           = "meowkov"
	roomName          = "#meowkov"
	chainLength       = 2
	maxChainLength    = 30
	chainsToTry       = 30
	defaultChattiness = 0.01
	stop              = "\x01"
	separator         = "\x02"
	version           = botName + " v0.1"
	redisHost         = "localhost:6379"
)

var (
	ownMention   = regexp.MustCompile(botName + "_*[:,]*\\s*")
	otherMention = regexp.MustCompile("^\\S+[:,]+\\s+")
	Corpus       redis.Conn
)

func printErr(err error) {
	fmt.Fprintf(os.Stderr, "\n[redis error]: %v\n", err.Error())
}

func main() {

	rdb, err := redis.Dial("tcp", redisHost)
	if err != nil {
		print(err)
		os.Exit(1)
	}
	Corpus = rdb

	rand.Seed(time.Now().Unix())

	con := irc.IRC(botName, botName)
	con.UseTLS = true
	con.Debug = true
	con.Version = version
	con.Connect("chat.freenode.net:7000")

	con.AddCallback("001", func(e *irc.Event) {
		con.Join(roomName)
	})

	con.AddCallback("JOIN", func(e *irc.Event) {
		con.Privmsg(roomName, "kek")
	})

	con.AddCallback("PRIVMSG", func(e *irc.Event) {
		response, incentive := processInput(e.Message())
		if len(response) > 0 && response != stop && incentive > rand.Float64() {
			channel := e.Arguments[0]
			con.Privmsg(channel, response)
		}
	})

	con.Loop()
}

func processInput(message string) (string, float64) {

	words, chattiness := normalizeInput(message)
	groups := generateChainGroups(words)

	if chainLength < len(words) && len(words) <= maxChainLength {
		addToCorpus(groups)
	}

	response := generateResponse(groups)

	return response, chattiness
}

func normalizeInput(message string) ([]string, float64) {
	chattiness := defaultChattiness
	message = strings.ToLower(message)

	if ownMention.MatchString(message) {
		message = ownMention.ReplaceAllString(message, "")
		chattiness = 1.0
	}
	if otherMention.MatchString(message) {
		message = otherMention.ReplaceAllString(message, "")
	}

	tokens := strings.Split(message, " ")
	words := make([]string, 0)

	for _, token := range tokens {
		token = strings.TrimSpace(token)
		if len(token) > 0 {
			words = append(words, token)
		}
	}

	return append(words, stop), chattiness
}

func addToCorpus(groups [][]string) {
	for i, group := range groups {
		fmt.Println("group #" + fmt.Sprint(i) + ": " + dump(group))
		cut := len(group) - 1
		key := strings.Join(group[:cut], separator)
		value := group[cut:][0]
		_, err := Corpus.Do("SADD", key, value)
		if err != nil {
			printErr(err)
			continue
		}

		chainValues, err := redis.Strings(Corpus.Do("SMEMBERS", key))
		if err != nil {
			printErr(err)
		}
		fmt.Println("Corpus[" + key + "]=" + dump(chainValues))
	}
}

func generateChainGroups(words []string) [][]string {
	length := len(words)
	groups := make([][]string, 0)

	for i, _ := range words {
		end := i + chainLength + 1

		if end > length {
			end = length
		}
		if end-i <= chainLength {
			break
		}

		groups = append(groups, words[i:end])
	}

	return groups
}

func generateResponse(groups [][]string) string {
	responses := make([]string, 0)

	for _, group := range groups {
		best := ""
		for range [chainsToTry]struct{}{} {
			response := randomResponse(group)
			if len(response) > len(best) {
				best = response
			}
		}
		if len(best) > 2 && best != groups[0][0] {
			responses = append(responses, best)
		}
	}

	responses = deduplicate(responses)
	fmt.Print("responses: " + dump(responses))

	count := len(responses)
	if count > 0 {
		return responses[rand.Intn(count)]
	} else {
		return stop
	}

}

func dump(texts []string) string {
	var buffer bytes.Buffer

	buffer.WriteString("[")
	for i, text := range texts {
		if i > 0 {
			buffer.WriteString(", ")
		}
		buffer.WriteString("\"" + text + "\"")
	}
	buffer.WriteString("]")
	return buffer.String()
}

func randomResponse(words []string) string {

	chainKey := strings.Join(words[:chainLength], separator)
	response := []string{words[0]}

	//fmt.Print("rootKey: " + chainKey)

	for range [maxChainLength]struct{}{} {
		word := randomWord(chainKey)
		if len(word) > 0 && word != stop {
			words = append(words[1:], word)
			response = append(response, words[0])
			chainKey = strings.Join(words, separator)
			//fmt.Print(" | " + chainKey)
		} else {
			break
		}
	}

	//fmt.Println("\tresponse: " + dump(response))

	return strings.Join(response, " ")
}

func randomWord(key string) string {
	value, err := redis.String(Corpus.Do("SRANDMEMBER", key))
	if err == nil || err == redis.ErrNil {
		return value
	} else {
		printErr(err)
	}
	return stop

}

func deduplicate(col []string) []string {
	m := map[string]struct{}{}
	for _, v := range col {
		if _, ok := m[v]; !ok {
			m[v] = struct{}{}
		}
	}
	list := make([]string, len(m))

	i := 0
	for v := range m {
		list[i] = v
		i++
	}
	return list
}
<commit_msg>Append emotion to compensate intellectual disability<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/thoj/go-ircevent"
	"math/rand"
	"os"
	"regexp"
	"strings"
	"time"
)

const (
	botName           = "meowkov"
	roomName          = "#meowkov"
	chainLength       = 2
	maxChainLength    = 30
	chainsToTry       = 30
	defaultChattiness = 0.01
	stop              = "\x01"
	separator         = "\x02"
	always            = 1.0
	version           = botName + " v0.2"
	redisHost         = "localhost:6379"
	corpusPerChannel  = false
	smileyChance      = 0.5
	debug             = true
)

var (
	Corpus redis.Conn

	ownMention   = regexp.MustCompile(botName + "_*[:,]*\\s*")
	otherMention = regexp.MustCompile("^\\S+[:,]+\\s+")
	smileys      = []string{"8<", ":-<", ":'-<", ":(", ":<", ":'<", ":--<", ":[", ":/", ":S", "\\:</", "xD", "D:", ":|", "kek"}
)

func main() {

	rdb, err := redis.Dial("tcp", redisHost)
	if err != nil {
		printErr(err)
		os.Exit(1)
	}
	Corpus = rdb

	rand.Seed(time.Now().Unix())

	con := irc.IRC(botName, botName)
	con.UseTLS = true
	con.Debug = debug
	con.Version = version
	con.Connect("chat.freenode.net:7000")

	con.AddCallback("001", func(e *irc.Event) {
		con.Join(roomName)
	})

	con.AddCallback("JOIN", func(e *irc.Event) {
		con.Privmsg(roomName, randomSmiley())
	})

	con.AddCallback("PRIVMSG", func(e *irc.Event) {
		response, chattiness := processInput(e.Message())

		if !isEmpty(response) && chattiness > rand.Float64() {
			if chattiness == always {
				response = e.Nick + ": " + strings.TrimSpace(response)
			}
			channel := e.Arguments[0]
			con.Privmsg(channel, response)
		}
	})

	con.Loop()
}

func isEmpty(text string) bool {
	return len(text) == 0 || text == stop
}

func processInput(message string) (string, float64) {

	words, chattiness := parseInput(message)
	groups := generateChainGroups(words)

	if chainLength < len(words) && len(words) <= maxChainLength {
		addToCorpus(groups)
	}

	response := generateResponse(groups)

	if isEmpty(response) {
		response = randomSmiley()
	} else if smileyChance > rand.Float64() {
		response = response + " " + randomSmiley()
	}

	return response, chattiness
}

func parseInput(message string) ([]string, float64) {
	chattiness := defaultChattiness
	message = strings.ToLower(message)

	if ownMention.MatchString(message) {
		message = ownMention.ReplaceAllString(message, "")
		chattiness = always
	}
	if otherMention.MatchString(message) {
		message = otherMention.ReplaceAllString(message, "")
	}

	tokens := strings.Split(message, " ")
	words := make([]string, 0)

	for _, token := range tokens {
		token = strings.TrimSpace(token)
		if len(token) > 0 {
			words = append(words, token)
		}
	}

	return append(words, stop), chattiness
}

func addToCorpus(groups [][]string) {
	for i, group := range groups {

		if debug {
			fmt.Println("group  #" + fmt.Sprint(i) + ":\t" + dump(group))
		}

		cut := len(group) - 1
		key := corpusKey(strings.Join(group[:cut], separator))
		value := group[cut:][0]

		_, err := Corpus.Do("SADD", key, value)
		if err != nil {
			printErr(err)
			continue
		}

		chainValues, err := redis.Strings(Corpus.Do("SMEMBERS", key))
		if err != nil {
			printErr(err)
		}

		if debug {
			fmt.Println("corpus #" + fmt.Sprint(i) + ":\t" + dump(chainValues))
		}
	}
}

func corpusKey(key string) string {
	if corpusPerChannel {
		key = roomName + key
	}
	return key
}

func generateChainGroups(words []string) [][]string {
	length := len(words)
	groups := make([][]string, 0)

	for i, _ := range words {
		end := i + chainLength + 1

		if end > length {
			end = length
		}
		if end-i <= chainLength {
			break
		}

		groups = append(groups, words[i:end])
	}

	return groups
}

func generateResponse(groups [][]string) string {
	responses := make([]string, 0)

	for _, group := range groups {
		best := ""

		for range [chainsToTry]struct{}{} {
			response := randomChain(group)
			if len(response) > len(best) {
				best = response
			}
		}

		if len(best) > 2 && best != groups[0][0] {
			responses = append(responses, best)
		}
	}

	responses = deduplicate(responses)

	if debug {
		fmt.Println("responses:\t" + dump(responses))
	}

	count := len(responses)
	if count > 0 {
		return responses[rand.Intn(count)]
	} else {
		return stop
	}

}

func randomChain(words []string) string {

	chainKey := strings.Join(words[:chainLength], separator)
	response := []string{words[0]}

	for range [maxChainLength]struct{}{} {
		word := randomWord(chainKey)
		if len(word) > 0 && word != stop {
			words = append(words[1:], word)
			response = append(response, words[0])
			chainKey = strings.Join(words, separator)
		} else {
			break
		}
	}

	return strings.Join(response, " ")
}

func randomWord(key string) string {
	value, err := redis.String(Corpus.Do("SRANDMEMBER", corpusKey(key)))
	if err == nil || err == redis.ErrNil {
		return value
	} else {
		printErr(err)
	}
	return stop

}

func randomSmiley() string {
	return smileys[rand.Intn(len(smileys))]
}

func deduplicate(col []string) []string {
	m := map[string]struct{}{}
	for _, v := range col {
		if _, ok := m[v]; !ok {
			m[v] = struct{}{}
		}
	}
	list := make([]string, len(m))

	i := 0
	for v := range m {
		list[i] = v
		i++
	}
	return list
}

func dump(texts []string) string {
	var buffer bytes.Buffer

	buffer.WriteString("[")
	for i, text := range texts {
		if i > 0 {
			buffer.WriteString(", ")
		}
		buffer.WriteString("\"" + text + "\"")
	}
	buffer.WriteString("]")
	return buffer.String()
}

func printErr(err error) {
	fmt.Fprintf(os.Stderr, "\n[redis error]: %v\n", err.Error())
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	apns "github.com/anachronistic/apns"
	"log"
	"strings"
	"time"
)

type CommandMsg struct {
	Command map[string]string      `json:"command"`
	Message map[string]interface{} `json:"message,omitempty"`
}

type Message struct {
	Event string                 `json:"event"`
	Data  map[string]interface{} `json:"data"`
	Time  int64                  `json:"time"`
}

func (this *CommandMsg) FromSocket(sock *Socket) {
	command, ok := this.Command["command"]
	if !ok {
		return
	}

	if DEBUG {
		log.Printf("Handling socket message of type %s\n", command)
	}

	switch strings.ToLower(command) {
	case "message":
		if !CLIENT_BROAD {
			return
		}

		if sock.Server.Store.StorageType == "redis" {
			this.forwardToRedis(sock.Server)
			return
		}

		this.sendMessage(sock.Server)

	case "setpage":
		page, ok := this.Command["page"]
		if !ok || page == "" {
			return
		}

		if sock.Page != "" {
			sock.Server.Store.UnsetPage(sock) //remove old page if it exists
		}

		sock.Page = page
		sock.Server.Store.SetPage(sock) // set new page
	}
}

func (this *CommandMsg) FromRedis(server *Server) {
	command, ok := this.Command["command"]
	if !ok {
		return
	}

	if DEBUG {
		log.Printf("Handling redis message of type %s\n", command)
	}

	switch strings.ToLower(command) {

	case "message":
		this.sendMessage(server)
	}
}

func (this *CommandMsg) formatMessage() (*Message, error) {
	event, e_ok := this.Message["event"].(string)
	data, b_ok := this.Message["data"].(map[string]interface{})

	if !b_ok || !e_ok {
		return nil, errors.New("Could not format message")
	}

	msg := &Message{event, data, time.Now().UTC().Unix()}

	return msg, nil
}

func (this *CommandMsg) sendMessage(server *Server) {
	user, userok := this.Command["user"]
	page, pageok := this.Command["page"]
	deviceToken, deviceToken_ok := this.Command["device_token"]

	if userok {
		this.messageUser(user, page, server)
	} else if pageok {
		this.messagePage(page, server)
	} else if !deviceToken_ok {
		this.messageAll(server)
	}

	if deviceToken_ok {
		this.pushiOS(server, deviceToken)
	}
}

func (this *CommandMsg) pushiOS(server *Server, deviceToken string) {
	msg, err := this.formatMessage()
	if err != nil {
		log.Println("Could not format message")
		return
	}

	payload := apns.NewPayload()
	payload.Alert = msg.Data["message"]
	payload.Badge = int(msg.Data["count"])
	payload.Sound = "bingbong.aiff"

	pn := apns.NewPushNotification()
	pn.DeviceToken = deviceToken
	pn.AddPayload(payload)

	var apns_url string
	if DEBUG_PUSH {
		apns_url = server.Config.Get("apns_sandbox_url")
	} else {
		apns_url = server.Config.Get("apns_production_url")
	}

	client := apns.NewClient(apns_url, server.Config.Get("apns_cert"), server.Config.Get("apns_private_key"))
	resp := client.Send(pn)

	alert, _ := pn.PayloadString()
	log.Printf("Alert: %s\n", alert)
	log.Printf("Success: %s\n", resp.Success)
	log.Printf("Error: %s\n", resp.Error)
}

func (this *CommandMsg) messageUser(UID string, page string, server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	user, err := server.Store.Client(UID)
	if err != nil {
		return
	}

	for _, sock := range user {
		if page != "" && page != sock.Page {
			continue
		}

		if !sock.isClosed() {
			sock.buff <- msg
		}
	}
}

func (this *CommandMsg) messageAll(server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	clients := server.Store.Clients()

	for _, user := range clients {
		for _, sock := range user {
			if !sock.isClosed() {
				sock.buff <- msg
			}
		}
	}

	return
}

func (this *CommandMsg) messagePage(page string, server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	pageMap := server.Store.getPage(page)
	if pageMap == nil {
		return
	}

	for _, sock := range pageMap {
		if !sock.isClosed() {
			sock.buff <- msg
		}
	}

	return
}

func (this *CommandMsg) forwardToRedis(server *Server) {
	msg_str, _ := json.Marshal(this)
	server.Store.redis.Publish(server.Config.Get("redis_message_channel"), string(msg_str)) //pass the message into redis to send message across clusterpwn
}
<commit_msg>Int conversion<commit_after>package main

import (
	"encoding/json"
	"errors"
	apns "github.com/anachronistic/apns"
	"log"
	"strings"
	"time"
)

type CommandMsg struct {
	Command map[string]string      `json:"command"`
	Message map[string]interface{} `json:"message,omitempty"`
}

type Message struct {
	Event string                 `json:"event"`
	Data  map[string]interface{} `json:"data"`
	Time  int64                  `json:"time"`
}

func (this *CommandMsg) FromSocket(sock *Socket) {
	command, ok := this.Command["command"]
	if !ok {
		return
	}

	if DEBUG {
		log.Printf("Handling socket message of type %s\n", command)
	}

	switch strings.ToLower(command) {
	case "message":
		if !CLIENT_BROAD {
			return
		}

		if sock.Server.Store.StorageType == "redis" {
			this.forwardToRedis(sock.Server)
			return
		}

		this.sendMessage(sock.Server)

	case "setpage":
		page, ok := this.Command["page"]
		if !ok || page == "" {
			return
		}

		if sock.Page != "" {
			sock.Server.Store.UnsetPage(sock) //remove old page if it exists
		}

		sock.Page = page
		sock.Server.Store.SetPage(sock) // set new page
	}
}

func (this *CommandMsg) FromRedis(server *Server) {
	command, ok := this.Command["command"]
	if !ok {
		return
	}

	if DEBUG {
		log.Printf("Handling redis message of type %s\n", command)
	}

	switch strings.ToLower(command) {

	case "message":
		this.sendMessage(server)
	}
}

func (this *CommandMsg) formatMessage() (*Message, error) {
	event, e_ok := this.Message["event"].(string)
	data, b_ok := this.Message["data"].(map[string]interface{})

	if !b_ok || !e_ok {
		return nil, errors.New("Could not format message")
	}

	msg := &Message{event, data, time.Now().UTC().Unix()}

	return msg, nil
}

func (this *CommandMsg) sendMessage(server *Server) {
	user, userok := this.Command["user"]
	page, pageok := this.Command["page"]
	deviceToken, deviceToken_ok := this.Command["device_token"]

	if userok {
		this.messageUser(user, page, server)
	} else if pageok {
		this.messagePage(page, server)
	} else if !deviceToken_ok {
		this.messageAll(server)
	}

	if deviceToken_ok {
		this.pushiOS(server, deviceToken)
	}
}

func (this *CommandMsg) pushiOS(server *Server, deviceToken string) {
	msg, err := this.formatMessage()
	if err != nil {
		log.Println("Could not format message")
		return
	}

	payload := apns.NewPayload()
	payload.Alert = msg.Data["message"]
	payload.Badge = int(msg.Data["count"].(float64))
	payload.Sound = "bingbong.aiff"

	pn := apns.NewPushNotification()
	pn.DeviceToken = deviceToken
	pn.AddPayload(payload)

	var apns_url string
	if DEBUG_PUSH {
		apns_url = server.Config.Get("apns_sandbox_url")
	} else {
		apns_url = server.Config.Get("apns_production_url")
	}

	client := apns.NewClient(apns_url, server.Config.Get("apns_cert"), server.Config.Get("apns_private_key"))
	resp := client.Send(pn)

	alert, _ := pn.PayloadString()
	log.Printf("Alert: %s\n", alert)
	log.Printf("Success: %s\n", resp.Success)
	log.Printf("Error: %s\n", resp.Error)
}

func (this *CommandMsg) messageUser(UID string, page string, server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	user, err := server.Store.Client(UID)
	if err != nil {
		return
	}

	for _, sock := range user {
		if page != "" && page != sock.Page {
			continue
		}

		if !sock.isClosed() {
			sock.buff <- msg
		}
	}
}

func (this *CommandMsg) messageAll(server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	clients := server.Store.Clients()

	for _, user := range clients {
		for _, sock := range user {
			if !sock.isClosed() {
				sock.buff <- msg
			}
		}
	}

	return
}

func (this *CommandMsg) messagePage(page string, server *Server) {
	msg, err := this.formatMessage()
	if err != nil {
		return
	}

	pageMap := server.Store.getPage(page)
	if pageMap == nil {
		return
	}

	for _, sock := range pageMap {
		if !sock.isClosed() {
			sock.buff <- msg
		}
	}

	return
}

func (this *CommandMsg) forwardToRedis(server *Server) {
	msg_str, _ := json.Marshal(this)
	server.Store.redis.Publish(server.Config.Get("redis_message_channel"), string(msg_str)) //pass the message into redis to send message across clusterpwn
}
<|endoftext|>


<commit_before>package smtpd

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"mime"
	"mime/multipart"
	"mime/quotedprintable"
	"net/mail"
	"net/textproto"
	"strings"
	"sync"
	"time"
)

const idEntropy = 64
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
	letterIdxBits = 6                    // 6 bits to represent a letter index
	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
)

// Message is a nicely packaged representation of the
// recieved message
type Message struct {
	To      []*mail.Address
	From    *mail.Address
	Header  mail.Header
	Subject string
	RawBody []byte

	messageID    string
	genMessageID sync.Once
	rcpt         []*mail.Address

	// meta info
	Logger *log.Logger
}

// Part represents a single part of the message
type Part struct {
	Header   textproto.MIMEHeader
	part     *multipart.Part
	Body     []byte
	Children []*Part
}

// ID returns an identifier for this message, or generates one if none available using the masked string
// algorithm from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang
func (m *Message) ID() string {
	m.genMessageID.Do(func() {
		if m.messageID = m.Header.Get("Message-ID"); m.messageID != "" {
			return
		}
		var src = rand.NewSource(time.Now().UnixNano())

		b := make([]byte, idEntropy)
		// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
		for i, cache, remain := idEntropy-1, src.Int63(), letterIdxMax; i >= 0; {
			if remain == 0 {
				cache, remain = src.Int63(), letterIdxMax
			}
			if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
				b[i] = letterBytes[idx]
				i--
			}
			cache >>= letterIdxBits
			remain--
		}

		m.messageID = string(b)
	})
	return m.messageID
}

// BCC returns a list of addresses this message should be
func (m *Message) BCC() []*mail.Address {

	var inHeaders = make(map[string]struct{})
	for _, to := range m.To {
		inHeaders[to.Address] = struct{}{}
	}

	var bcc []*mail.Address
	for _, recipient := range m.rcpt {
		if _, ok := inHeaders[recipient.Address]; !ok {
			bcc = append(bcc, recipient)
		}
	}

	return bcc
}

// Plain returns the text/plain content of the message, if any
func (m *Message) Plain() ([]byte, error) {
	return m.FindBody("text/plain")
}

// HTML returns the text/html content of the message, if any
func (m *Message) HTML() ([]byte, error) {
	return m.FindBody("text/html")
}

func findTypeInParts(contentType string, parts []*Part) *Part {
	for _, p := range parts {
		mediaType, _, err := mime.ParseMediaType(p.Header.Get("Content-Type"))
		if err == nil && mediaType == contentType {
			return p
		}
	}
	return nil
}

// Attachments returns the list of attachments on this message
// XXX: this assumes that the only mimetype supporting attachments is multipart/mixed
// need to review https://en.wikipedia.org/wiki/MIME#Multipart_messages to ensure that is the case
func (m *Message) Attachments() ([]*Part, error) {
	mediaType, _, err := mime.ParseMediaType(m.Header.Get("Content-Type"))
	if err != nil {
		return nil, err
	}

	parts, err := m.Parts()
	if err != nil {
		return nil, err
	}

	var attachments []*Part
	if mediaType == "multipart/mixed" {
		for _, part := range parts {
			mediaType, _, err := mime.ParseMediaType(part.Header.Get("Content-Type"))
			if err != nil {
				return nil, err
			}
			if strings.HasPrefix(mediaType, "multipart/") {
				// XXX: any cases where this would still be an attachment?
				continue
			}
			attachments = append(attachments, part)
		}
	}
	return attachments, nil
}

// FindBody finds the first part of the message with the specified Content-Type
func (m *Message) FindBody(contentType string) ([]byte, error) {

	mediaType, _, err := mime.ParseMediaType(m.Header.Get("Content-Type"))
	if err != nil {
		return nil, err
	}

	parts, err := m.Parts()
	if err != nil {
		return nil, err
	}

	var alternatives []*Part
	switch mediaType {
	case contentType:
		if len(parts) > 0 {
			return parts[0].Body, nil
		}
		return nil, fmt.Errorf("%v found, but no data in body", contentType)
	case "multipart/alternative":
		alternatives = parts
	default:
		if alt := findTypeInParts("multipart/alternative", parts); alt != nil {
			alternatives = alt.Children
		}
	}

	if len(alternatives) == 0 {
		return nil, fmt.Errorf("No multipart/alternative section found, can't find %v", contentType)
	}

	part := findTypeInParts(contentType, alternatives)
	if part == nil {
		return nil, fmt.Errorf("No %v content found in multipart/alternative section", contentType)
	}

	return part.Body, nil
}

func readToPart(header textproto.MIMEHeader, content io.Reader) (*Part, error) {
	cte := strings.ToLower(header.Get("Content-Transfer-Encoding"))

	if cte == "quoted-printable" {
		content = quotedprintable.NewReader(content)
	}

	slurp, err := ioutil.ReadAll(content)
	if err != nil {
		return nil, err
	}

	if cte == "base64" {
		dst := make([]byte, base64.StdEncoding.DecodedLen(len(slurp)))
		decodedLen, err := base64.StdEncoding.Decode(dst, slurp)
		if err != nil {
			return nil, err
		}

		slurp = dst[:decodedLen]
	}
	return &Part{
		Header: header,
		Body:   slurp,
	}, nil
}

func parseContent(header textproto.MIMEHeader, content io.Reader) ([]*Part, error) {

	mediaType, params, err := mime.ParseMediaType(header.Get("Content-Type"))
	if err != nil && err.Error() == "mime: no media type" {
		mediaType = "application/octet-stream"
	} else if err != nil {
		return nil, fmt.Errorf("Media Type error: %v", err)
	}

	var parts []*Part

	if strings.HasPrefix(mediaType, "multipart/") {

		mr := multipart.NewReader(content, params["boundary"])
		for {
			p, err := mr.NextPart()
			if err == io.EOF {
				break
			} else if err != nil {
				return nil, fmt.Errorf("MIME error: %v", err)
			}

			part, err := readToPart(p.Header, p)

			// XXX: maybe want to implement a less strict mode that gets what it can out of the message
			// instead of erroring out on individual sections?
			partType, _, err := mime.ParseMediaType(p.Header.Get("Content-Type"))
			if err != nil {
				return nil, err
			}
			if strings.HasPrefix(partType, "multipart/") {
				subParts, err := parseContent(p.Header, bytes.NewBuffer(part.Body))
				if err != nil {
					return nil, err
				}
				part.Children = subParts
			}
			parts = append(parts, part)
		}
	} else {
		part, err := readToPart(header, content)
		if err != nil {
			return nil, err
		}
		parts = append(parts, part)
	}

	return parts, nil
}

// Parts breaks a message body into its mime parts
func (m *Message) Parts() ([]*Part, error) {
	parts, err := parseContent(textproto.MIMEHeader(m.Header), bytes.NewBuffer(m.RawBody))
	if err != nil {
		return nil, err
	}

	return parts, nil
}

// NewMessage creates a Message from a data blob and a recipients list
func NewMessage(data []byte, rcpt []*mail.Address, logger *log.Logger) (*Message, error) {
	m, err := mail.ReadMessage(bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}

	// TODO: This isn't accurate, the To field should be all the values from RCPT TO:
	to, err := m.Header.AddressList("To")
	if err != nil {
		return nil, err
	}

	from, err := m.Header.AddressList("From")
	if err != nil {
		return nil, err
	}

	header := make(map[string]string)

	for k, v := range m.Header {
		if len(v) == 1 {
			header[k] = v[0]
		}
	}

	raw, err := ioutil.ReadAll(m.Body)
	if err != nil && err != io.EOF {
		return nil, err
	}

	return &Message{
		rcpt:    rcpt,
		To:      to,
		From:    from[0],
		Header:  m.Header,
		Subject: m.Header.Get("subject"),
		RawBody: raw,
		Logger:  logger,
	}, nil

}
<commit_msg>keep the source around<commit_after>package smtpd

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"mime"
	"mime/multipart"
	"mime/quotedprintable"
	"net/mail"
	"net/textproto"
	"strings"
	"sync"
	"time"
)

const idEntropy = 64
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
	letterIdxBits = 6                    // 6 bits to represent a letter index
	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
)

// Message is a nicely packaged representation of the
// recieved message
type Message struct {
	To      []*mail.Address
	From    *mail.Address
	Header  mail.Header
	Subject string
	RawBody []byte
	Source  []byte

	messageID    string
	genMessageID sync.Once
	rcpt         []*mail.Address

	// meta info
	Logger *log.Logger
}

// Part represents a single part of the message
type Part struct {
	Header   textproto.MIMEHeader
	part     *multipart.Part
	Body     []byte
	Children []*Part
}

// ID returns an identifier for this message, or generates one if none available using the masked string
// algorithm from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang
func (m *Message) ID() string {
	m.genMessageID.Do(func() {
		if m.messageID = m.Header.Get("Message-ID"); m.messageID != "" {
			return
		}
		var src = rand.NewSource(time.Now().UnixNano())

		b := make([]byte, idEntropy)
		// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
		for i, cache, remain := idEntropy-1, src.Int63(), letterIdxMax; i >= 0; {
			if remain == 0 {
				cache, remain = src.Int63(), letterIdxMax
			}
			if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
				b[i] = letterBytes[idx]
				i--
			}
			cache >>= letterIdxBits
			remain--
		}

		m.messageID = string(b)
	})
	return m.messageID
}

// BCC returns a list of addresses this message should be
func (m *Message) BCC() []*mail.Address {

	var inHeaders = make(map[string]struct{})
	for _, to := range m.To {
		inHeaders[to.Address] = struct{}{}
	}

	var bcc []*mail.Address
	for _, recipient := range m.rcpt {
		if _, ok := inHeaders[recipient.Address]; !ok {
			bcc = append(bcc, recipient)
		}
	}

	return bcc
}

// Plain returns the text/plain content of the message, if any
func (m *Message) Plain() ([]byte, error) {
	return m.FindBody("text/plain")
}

// HTML returns the text/html content of the message, if any
func (m *Message) HTML() ([]byte, error) {
	return m.FindBody("text/html")
}

func findTypeInParts(contentType string, parts []*Part) *Part {
	for _, p := range parts {
		mediaType, _, err := mime.ParseMediaType(p.Header.Get("Content-Type"))
		if err == nil && mediaType == contentType {
			return p
		}
	}
	return nil
}

// Attachments returns the list of attachments on this message
// XXX: this assumes that the only mimetype supporting attachments is multipart/mixed
// need to review https://en.wikipedia.org/wiki/MIME#Multipart_messages to ensure that is the case
func (m *Message) Attachments() ([]*Part, error) {
	mediaType, _, err := mime.ParseMediaType(m.Header.Get("Content-Type"))
	if err != nil {
		return nil, err
	}

	parts, err := m.Parts()
	if err != nil {
		return nil, err
	}

	var attachments []*Part
	if mediaType == "multipart/mixed" {
		for _, part := range parts {
			mediaType, _, err := mime.ParseMediaType(part.Header.Get("Content-Type"))
			if err != nil {
				return nil, err
			}
			if strings.HasPrefix(mediaType, "multipart/") {
				// XXX: any cases where this would still be an attachment?
				continue
			}
			attachments = append(attachments, part)
		}
	}
	return attachments, nil
}

// FindBody finds the first part of the message with the specified Content-Type
func (m *Message) FindBody(contentType string) ([]byte, error) {

	mediaType, _, err := mime.ParseMediaType(m.Header.Get("Content-Type"))
	if err != nil {
		return nil, err
	}

	parts, err := m.Parts()
	if err != nil {
		return nil, err
	}

	var alternatives []*Part
	switch mediaType {
	case contentType:
		if len(parts) > 0 {
			return parts[0].Body, nil
		}
		return nil, fmt.Errorf("%v found, but no data in body", contentType)
	case "multipart/alternative":
		alternatives = parts
	default:
		if alt := findTypeInParts("multipart/alternative", parts); alt != nil {
			alternatives = alt.Children
		}
	}

	if len(alternatives) == 0 {
		return nil, fmt.Errorf("No multipart/alternative section found, can't find %v", contentType)
	}

	part := findTypeInParts(contentType, alternatives)
	if part == nil {
		return nil, fmt.Errorf("No %v content found in multipart/alternative section", contentType)
	}

	return part.Body, nil
}

func readToPart(header textproto.MIMEHeader, content io.Reader) (*Part, error) {
	cte := strings.ToLower(header.Get("Content-Transfer-Encoding"))

	if cte == "quoted-printable" {
		content = quotedprintable.NewReader(content)
	}

	slurp, err := ioutil.ReadAll(content)
	if err != nil {
		return nil, err
	}

	if cte == "base64" {
		dst := make([]byte, base64.StdEncoding.DecodedLen(len(slurp)))
		decodedLen, err := base64.StdEncoding.Decode(dst, slurp)
		if err != nil {
			return nil, err
		}

		slurp = dst[:decodedLen]
	}
	return &Part{
		Header: header,
		Body:   slurp,
	}, nil
}

func parseContent(header textproto.MIMEHeader, content io.Reader) ([]*Part, error) {

	mediaType, params, err := mime.ParseMediaType(header.Get("Content-Type"))
	if err != nil && err.Error() == "mime: no media type" {
		mediaType = "application/octet-stream"
	} else if err != nil {
		return nil, fmt.Errorf("Media Type error: %v", err)
	}

	var parts []*Part

	if strings.HasPrefix(mediaType, "multipart/") {

		mr := multipart.NewReader(content, params["boundary"])
		for {
			p, err := mr.NextPart()
			if err == io.EOF {
				break
			} else if err != nil {
				return nil, fmt.Errorf("MIME error: %v", err)
			}

			part, err := readToPart(p.Header, p)

			// XXX: maybe want to implement a less strict mode that gets what it can out of the message
			// instead of erroring out on individual sections?
			partType, _, err := mime.ParseMediaType(p.Header.Get("Content-Type"))
			if err != nil {
				return nil, err
			}
			if strings.HasPrefix(partType, "multipart/") {
				subParts, err := parseContent(p.Header, bytes.NewBuffer(part.Body))
				if err != nil {
					return nil, err
				}
				part.Children = subParts
			}
			parts = append(parts, part)
		}
	} else {
		part, err := readToPart(header, content)
		if err != nil {
			return nil, err
		}
		parts = append(parts, part)
	}

	return parts, nil
}

// Parts breaks a message body into its mime parts
func (m *Message) Parts() ([]*Part, error) {
	parts, err := parseContent(textproto.MIMEHeader(m.Header), bytes.NewBuffer(m.RawBody))
	if err != nil {
		return nil, err
	}

	return parts, nil
}

// NewMessage creates a Message from a data blob and a recipients list
func NewMessage(data []byte, rcpt []*mail.Address, logger *log.Logger) (*Message, error) {
	m, err := mail.ReadMessage(bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}

	// TODO: This isn't accurate, the To field should be all the values from RCPT TO:
	to, err := m.Header.AddressList("To")
	if err != nil {
		return nil, err
	}

	from, err := m.Header.AddressList("From")
	if err != nil {
		return nil, err
	}

	header := make(map[string]string)

	for k, v := range m.Header {
		if len(v) == 1 {
			header[k] = v[0]
		}
	}

	raw, err := ioutil.ReadAll(m.Body)
	if err != nil && err != io.EOF {
		return nil, err
	}

	return &Message{
		rcpt:    rcpt,
		To:      to,
		From:    from[0],
		Header:  m.Header,
		Subject: m.Header.Get("subject"),
		RawBody: raw,
		Source:  data,
		Logger:  logger,
	}, nil

}
<|endoftext|>


<commit_before>package data

import (
	"crypto/rand"
	"encoding/base64"
	"log"
	"mime"
	"strings"
	"time"
)

// LogHandler is called for each log message. If nil, log messages will
// be output using log.Printf instead.
var LogHandler func(message string, args ...interface{})

func logf(message string, args ...interface{}) {
	if LogHandler != nil {
		LogHandler(message, args...)
	} else {
		log.Printf(message, args...)
	}
}

// MessageID represents the ID of an SMTP message including the hostname part
type MessageID string

// NewMessageID generates a new message ID
func NewMessageID(hostname string) (MessageID, error) {
	size := 32

	rb := make([]byte, size)
	_, err := rand.Read(rb)

	if err != nil {
		return MessageID(""), err
	}

	rs := base64.URLEncoding.EncodeToString(rb)

	return MessageID(rs + "@" + hostname), nil
}

// Messages represents an array of Messages
// - TODO is this even required?
type Messages []Message

// Message represents a parsed SMTP message
type Message struct {
	ID      MessageID
	From    *Path
	To      []*Path
	Content *Content
	Created time.Time
	MIME    *MIMEBody // FIXME refactor to use Content.MIME
	Raw     *SMTPMessage
}

// Path represents an SMTP forward-path or return-path
type Path struct {
	Relays  []string
	Mailbox string
	Domain  string
	Params  string
}

// Content represents the body content of an SMTP message
type Content struct {
	Headers map[string][]string
	Body    string
	Size    int
	MIME    *MIMEBody
}

// SMTPMessage represents a raw SMTP message
type SMTPMessage struct {
	From string
	To   []string
	Data string
	Helo string
}

// MIMEBody represents a collection of MIME parts
type MIMEBody struct {
	Parts []*Content
}

// Parse converts a raw SMTP message to a parsed MIME message
func (m *SMTPMessage) Parse(hostname string) *Message {
	var arr []*Path
	for _, path := range m.To {
		arr = append(arr, PathFromString(path))
	}

	id, _ := NewMessageID(hostname)
	msg := &Message{
		ID:      id,
		From:    PathFromString(m.From),
		To:      arr,
		Content: ContentFromString(m.Data),
		Created: time.Now(),
		Raw:     m,
	}

	if msg.Content.IsMIME() {
		logf("Parsing MIME body")
		msg.MIME = msg.Content.ParseMIMEBody()
	}

	// FIXME shouldn't be setting Message-ID, its a client thing
	msg.Content.Headers["Message-ID"] = []string{string(id)}
	msg.Content.Headers["Received"] = []string{"from " + m.Helo + " by " + hostname + " (Go-MailHog)\r\n          id " + string(id) + "; " + time.Now().Format(time.RFC1123Z)}
	msg.Content.Headers["Return-Path"] = []string{"<" + m.From + ">"}
	return msg
}

// IsMIME detects a valid MIME header
func (content *Content) IsMIME() bool {
	header, ok := content.Headers["Content-Type"]
	if !ok {
		return false
	}
	return strings.HasPrefix(header[0], "multipart/")
}

// ParseMIMEBody parses SMTP message content into multiple MIME parts
func (content *Content) ParseMIMEBody() *MIMEBody {
	var parts []*Content

	if hdr, ok := content.Headers["Content-Type"]; ok {
		if len(hdr) > 0 {
			boundary := extractBoundary(hdr[0])
			var p []string
			if len(boundary) > 0 {
				p = strings.Split(content.Body, "--"+boundary)
				logf("Got boundary: %s", boundary)
			} else {
				logf("Boundary not found: %s", hdr[0])
			}

			for _, s := range p {
				if len(s) > 0 {
					part := ContentFromString(strings.Trim(s, "\r\n"))
					if part.IsMIME() {
						logf("Parsing inner MIME body")
						part.MIME = part.ParseMIMEBody()
					}
					parts = append(parts, part)
				}
			}
		}
	}

	return &MIMEBody{
		Parts: parts,
	}
}

// PathFromString parses a forward-path or reverse-path into its parts
func PathFromString(path string) *Path {
	var relays []string
	email := path
	if strings.Contains(path, ":") {
		x := strings.SplitN(path, ":", 2)
		r, e := x[0], x[1]
		email = e
		relays = strings.Split(r, ",")
	}
	mailbox, domain := "", ""
	if strings.Contains(email, "@") {
		x := strings.SplitN(email, "@", 2)
		mailbox, domain = x[0], x[1]
	} else {
		mailbox = email
	}

	return &Path{
		Relays:  relays,
		Mailbox: mailbox,
		Domain:  domain,
		Params:  "", // FIXME?
	}
}

// ContentFromString parses SMTP content into separate headers and body
func ContentFromString(data string) *Content {
	logf("Parsing Content from string: '%s'", data)
	x := strings.SplitN(data, "\r\n\r\n", 2)
	h := make(map[string][]string, 0)

	if len(x) == 2 {
		headers, body := x[0], x[1]
		hdrs := strings.Split(headers, "\r\n")
		var lastHdr = ""
		for _, hdr := range hdrs {
			if lastHdr != "" && (strings.HasPrefix(hdr, " ") || strings.HasPrefix(hdr, "\t")) {
				h[lastHdr][len(h[lastHdr])-1] = h[lastHdr][len(h[lastHdr])-1] + hdr
			} else if strings.Contains(hdr, ": ") {
				y := strings.SplitN(hdr, ": ", 2)
				key, value := y[0], y[1]
				// TODO multiple header fields
				h[key] = []string{value}
				lastHdr = key
			} else {
				logf("Found invalid header: '%s'", hdr)
			}
		}
		return &Content{
			Size:    len(data),
			Headers: h,
			Body:    body,
		}
	}
	return &Content{
		Size:    len(data),
		Headers: h,
		Body:    x[0],
	}
}

// extractBoundary extract boundary string in contentType.
// It returns empty string if no valid boundary found
func extractBoundary(contentType string) string {
	_, params, err := mime.ParseMediaType(contentType)
	if err == nil {
		return params["boundary"]
	}
	return ""
}
<commit_msg>add fixme<commit_after>package data

import (
	"crypto/rand"
	"encoding/base64"
	"log"
	"mime"
	"strings"
	"time"
)

// LogHandler is called for each log message. If nil, log messages will
// be output using log.Printf instead.
var LogHandler func(message string, args ...interface{})

func logf(message string, args ...interface{}) {
	if LogHandler != nil {
		LogHandler(message, args...)
	} else {
		log.Printf(message, args...)
	}
}

// MessageID represents the ID of an SMTP message including the hostname part
type MessageID string

// NewMessageID generates a new message ID
func NewMessageID(hostname string) (MessageID, error) {
	size := 32

	rb := make([]byte, size)
	_, err := rand.Read(rb)

	if err != nil {
		return MessageID(""), err
	}

	rs := base64.URLEncoding.EncodeToString(rb)

	return MessageID(rs + "@" + hostname), nil
}

// Messages represents an array of Messages
// - TODO is this even required?
type Messages []Message

// Message represents a parsed SMTP message
type Message struct {
	ID      MessageID
	From    *Path
	To      []*Path
	Content *Content
	Created time.Time
	MIME    *MIMEBody // FIXME refactor to use Content.MIME
	Raw     *SMTPMessage
}

// Path represents an SMTP forward-path or return-path
type Path struct {
	Relays  []string
	Mailbox string
	Domain  string
	Params  string
}

// Content represents the body content of an SMTP message
type Content struct {
	Headers map[string][]string
	Body    string
	Size    int
	MIME    *MIMEBody
}

// SMTPMessage represents a raw SMTP message
type SMTPMessage struct {
	From string
	To   []string
	Data string
	Helo string
}

// MIMEBody represents a collection of MIME parts
type MIMEBody struct {
	Parts []*Content
}

// Parse converts a raw SMTP message to a parsed MIME message
func (m *SMTPMessage) Parse(hostname string) *Message {
	var arr []*Path
	for _, path := range m.To {
		arr = append(arr, PathFromString(path))
	}

	id, _ := NewMessageID(hostname)
	msg := &Message{
		ID:      id,
		From:    PathFromString(m.From),
		To:      arr,
		Content: ContentFromString(m.Data),
		Created: time.Now(),
		Raw:     m,
	}

	if msg.Content.IsMIME() {
		logf("Parsing MIME body")
		msg.MIME = msg.Content.ParseMIMEBody()
	}

	// FIXME shouldn't be setting Message-ID, its a client thing
	msg.Content.Headers["Message-ID"] = []string{string(id)}
	msg.Content.Headers["Received"] = []string{"from " + m.Helo + " by " + hostname + " (Go-MailHog)\r\n          id " + string(id) + "; " + time.Now().Format(time.RFC1123Z)}
	msg.Content.Headers["Return-Path"] = []string{"<" + m.From + ">"}
	return msg
}

// IsMIME detects a valid MIME header
func (content *Content) IsMIME() bool {
	header, ok := content.Headers["Content-Type"]
	if !ok {
		return false
	}
	return strings.HasPrefix(header[0], "multipart/")
}

// ParseMIMEBody parses SMTP message content into multiple MIME parts
func (content *Content) ParseMIMEBody() *MIMEBody {
	var parts []*Content

	if hdr, ok := content.Headers["Content-Type"]; ok {
		if len(hdr) > 0 {
			boundary := extractBoundary(hdr[0])
			var p []string
			if len(boundary) > 0 {
				p = strings.Split(content.Body, "--"+boundary)
				logf("Got boundary: %s", boundary)
			} else {
				logf("Boundary not found: %s", hdr[0])
			}

			for _, s := range p {
				if len(s) > 0 {
					part := ContentFromString(strings.Trim(s, "\r\n"))
					if part.IsMIME() {
						logf("Parsing inner MIME body")
						part.MIME = part.ParseMIMEBody()
					}
					parts = append(parts, part)
				}
			}
		}
	}

	return &MIMEBody{
		Parts: parts,
	}
}

// PathFromString parses a forward-path or reverse-path into its parts
func PathFromString(path string) *Path {
	var relays []string
	email := path
	if strings.Contains(path, ":") {
		x := strings.SplitN(path, ":", 2)
		r, e := x[0], x[1]
		email = e
		relays = strings.Split(r, ",")
	}
	mailbox, domain := "", ""
	if strings.Contains(email, "@") {
		x := strings.SplitN(email, "@", 2)
		mailbox, domain = x[0], x[1]
	} else {
		mailbox = email
	}

	return &Path{
		Relays:  relays,
		Mailbox: mailbox,
		Domain:  domain,
		Params:  "", // FIXME?
	}
}

// ContentFromString parses SMTP content into separate headers and body
func ContentFromString(data string) *Content {
	logf("Parsing Content from string: '%s'", data)
	x := strings.SplitN(data, "\r\n\r\n", 2)
	h := make(map[string][]string, 0)

	// FIXME this fails if the message content has no headers - specifically,
	// if it doesn't contain \r\n\r\n

	if len(x) == 2 {
		headers, body := x[0], x[1]
		hdrs := strings.Split(headers, "\r\n")
		var lastHdr = ""
		for _, hdr := range hdrs {
			if lastHdr != "" && (strings.HasPrefix(hdr, " ") || strings.HasPrefix(hdr, "\t")) {
				h[lastHdr][len(h[lastHdr])-1] = h[lastHdr][len(h[lastHdr])-1] + hdr
			} else if strings.Contains(hdr, ": ") {
				y := strings.SplitN(hdr, ": ", 2)
				key, value := y[0], y[1]
				// TODO multiple header fields
				h[key] = []string{value}
				lastHdr = key
			} else {
				logf("Found invalid header: '%s'", hdr)
			}
		}
		return &Content{
			Size:    len(data),
			Headers: h,
			Body:    body,
		}
	}
	return &Content{
		Size:    len(data),
		Headers: h,
		Body:    x[0],
	}
}

// extractBoundary extract boundary string in contentType.
// It returns empty string if no valid boundary found
func extractBoundary(contentType string) string {
	_, params, err := mime.ParseMediaType(contentType)
	if err == nil {
		return params["boundary"]
	}
	return ""
}
<|endoftext|>