<commit_before>package androidbinary

import (
	"encoding/binary"
	"io"
	"os"
)

type ResId uint32

type TableFile struct {
	stringPool    *ResStringPool
	tablePackages map[uint32]*TablePackage
}

type ResTableHeader struct {
	Header       ResChunkHeader
	PackageCount uint32
}

type ResTablePackage struct {
	Header         ResChunkHeader
	Id             uint32
	Name           [128]uint16
	TypeStrings    uint32
	LastPublicType uint32
	KeyStrings     uint32
	LastPublicKey  uint32
}

type TablePackage struct {
	Header      ResTablePackage
	TypeStrings *ResStringPool
	KeyStrings  *ResStringPool
	TableTypes  []*TableType
}

type ResTableType struct {
	Header       ResChunkHeader
	Id           uint8
	Res0         uint8
	Res1         uint16
	EntryCount   uint32
	EntriesStart uint32
	Config       ResTableConfig
}

type ResTableConfig struct {
	Size uint32
	// imsi
	Mcc uint16
	Mnc uint16

	// locale
	Language [2]uint8
	Country  [2]uint8

	// screen type
	Orientation uint8
	Touchscreen uint8
	Density     uint16

	// inout
	Keyboard   uint8
	Navigation uint8
	InputFlags uint8
	InputPad0  uint8

	// screen
	ScreenWidth  uint16
	ScreenHeight uint16

	// version
	SDKVersion   uint16
	MinorVersion uint16

	// screen config
	ScreenLayout     uint8
	UIMode           uint8
	ScreenConfigPad1 uint8
	ScreenConfigPad2 uint8
}

type TableType struct {
	Header  *ResTableType
	Entries []TableEntry
}

type ResTableEntry struct {
	Size  uint16
	Flags uint16
	Key   ResStringPoolRef
}

type TableEntry struct {
	Key   *ResTableEntry
	Value *ResValue
	Flags uint32
}

type ResTableTypeSpec struct {
	Header     ResChunkHeader
	Id         uint8
	Res0       uint8
	Res1       uint16
	EntryCount uint32
}

func (id ResId) Package() int {
	return int(id) >> 24
}

func (id ResId) Type() int {
	return (int(id) >> 16) & 0xFF
}

func (id ResId) Entry() int {
	return int(id) & 0xFFFF
}

func NewTableFile(r io.ReaderAt) (*TableFile, error) {
	f := new(TableFile)
	sr := io.NewSectionReader(r, 0, 1<<63-1)

	header := new(ResTableHeader)
	binary.Read(sr, binary.LittleEndian, header)
	f.tablePackages = make(map[uint32]*TablePackage)

	offset := int64(header.Header.HeaderSize)
	for offset < int64(header.Header.Size) {
		chunkHeader, err := f.readChunk(sr, offset)
		if err != nil {
			return nil, err
		}
		offset += int64(chunkHeader.Size)
	}
	return f, nil
}

func (f *TableFile) findPackage(id int) interface{} {
	return f.tablePackages[uint32(id)]
}

func (f *TableFile) readChunk(r io.ReaderAt, offset int64) (*ResChunkHeader, error) {
	sr := io.NewSectionReader(r, offset, 1<<63-1-offset)
	chunkHeader := &ResChunkHeader{}
	sr.Seek(0, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil {
		return nil, err
	}

	var err error
	sr.Seek(0, os.SEEK_SET)
	switch chunkHeader.Type {
	case RES_STRING_POOL_TYPE:
		f.stringPool, err = readStringPool(sr)
	case RES_TABLE_PACKAGE_TYPE:
		var tablePackage *TablePackage
		tablePackage, err = readTablePackage(sr)
		f.tablePackages[tablePackage.Header.Id] = tablePackage
	}
	if err != nil {
		return nil, err
	}

	return chunkHeader, nil
}

func readTablePackage(sr *io.SectionReader) (*TablePackage, error) {
	tablePackage := new(TablePackage)
	header := new(ResTablePackage)
	if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
		return nil, err
	}
	tablePackage.Header = *header

	srTypes := io.NewSectionReader(sr, int64(header.TypeStrings), int64(header.Header.Size-header.TypeStrings))
	if typeStrings, err := readStringPool(srTypes); err == nil {
		tablePackage.TypeStrings = typeStrings
	} else {
		return nil, err
	}

	srKeys := io.NewSectionReader(sr, int64(header.KeyStrings), int64(header.Header.Size-header.KeyStrings))
	if keyStrings, err := readStringPool(srKeys); err == nil {
		tablePackage.KeyStrings = keyStrings
	} else {
		return nil, err
	}

	offset := int64(header.Header.HeaderSize)
	for offset < int64(header.Header.Size) {
		chunkHeader := &ResChunkHeader{}
		sr.Seek(offset, os.SEEK_SET)
		if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil {
			return nil, err
		}

		var err error
		chunkReader := io.NewSectionReader(sr, offset, int64(chunkHeader.Size))
		sr.Seek(offset, os.SEEK_SET)
		switch chunkHeader.Type {
		case RES_TABLE_TYPE_TYPE:
			var tableType *TableType
			tableType, err = readTableType(chunkReader)
			tablePackage.TableTypes = append(tablePackage.TableTypes, tableType)
		case RES_TABLE_TYPE_SPEC_TYPE:
			_, err = readTableTypeSpec(chunkReader)
		}
		if err != nil {
			return nil, err
		}
		offset += int64(chunkHeader.Size)
	}

	return tablePackage, nil
}

func readTableType(sr *io.SectionReader) (*TableType, error) {
	header := new(ResTableType)
	if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
		return nil, err
	}

	entryIndexes := make([]uint32, header.EntryCount)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, entryIndexes); err != nil {
		return nil, err
	}

	entries := make([]TableEntry, header.EntryCount)
	for i, index := range entryIndexes {
		if index == 0xFFFFFFFF {
			continue
		}
		sr.Seek(int64(header.EntriesStart+index), os.SEEK_SET)
		var key ResTableEntry
		binary.Read(sr, binary.LittleEndian, &key)
		entries[i].Key = &key

		var val ResValue
		binary.Read(sr, binary.LittleEndian, &val)
		entries[i].Value = &val
	}
	return &TableType{
		header,
		entries,
	}, nil
}

func readTableTypeSpec(sr *io.SectionReader) ([]uint32, error) {
	header := new(ResTableTypeSpec)
	if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
		return nil, err
	}

	flags := make([]uint32, header.EntryCount)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, flags); err != nil {
		return nil, err
	}
	return flags, nil
}

func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool {
	// imsi
	if (c.Mcc != 0 && c.Mnc != 0) || (o.Mcc != 0 && o.Mnc != 0) {
		if c.Mcc != o.Mcc {
			if c.Mcc != 0 {
				return false
			}
			if o.Mnc != 0 {
				return true
			}
		}
		if c.Mnc != o.Mnc {
			if c.Mnc != 0 {
				return false
			}
			if o.Mnc != 0 {
				return true
			}
		}
	}

	// locale
	if (c.Language[0] != 0 && c.Country[0] != 0) || (o.Language[0] != 0 && o.Country[0] != 0) {
		if c.Language[0] != o.Language[0] {
			if c.Language[0] != 0 {
				return false
			}
			if o.Language[0] != 0 {
				return true
			}
		}
		if c.Country[0] != o.Country[0] {
			if c.Country[0] != 0 {
				return false
			}
			if o.Country[0] != 0 {
				return true
			}
		}
	}

	// orientation
	if c.Orientation != o.Orientation {
		if c.Orientation != 0 {
			return false
		}
		if o.Orientation != 0 {
			return true
		}
	}

	// TODO: uimode

	// touchscreen
	if c.Touchscreen != o.Touchscreen {
		if c.Touchscreen != 0 {
			return false
		}
		if o.Touchscreen != 0 {
			return true
		}
	}

	// TODO: input

	// screen size
	if (c.ScreenWidth != 0 && c.ScreenHeight != 0) || (o.ScreenWidth != 0 && o.ScreenHeight != 0) {
		if c.ScreenWidth != o.ScreenWidth {
			if c.ScreenWidth != 0 {
				return false
			}
			if o.ScreenWidth != 0 {
				return true
			}
		}
		if c.ScreenHeight != o.ScreenHeight {
			if c.ScreenHeight != 0 {
				return false
			}
			if o.ScreenHeight != 0 {
				return true
			}
		}
	}

	//version
	if (c.SDKVersion != 0 && c.SDKVersion != 0) || (o.MinorVersion != 0 && o.MinorVersion != 0) {
		if c.SDKVersion != o.SDKVersion {
			if c.SDKVersion != 0 {
				return false
			}
			if o.SDKVersion != 0 {
				return true
			}
		}
		if c.MinorVersion != o.MinorVersion {
			if c.MinorVersion != 0 {
				return false
			}
			if o.MinorVersion != 0 {
				return true
			}
		}
	}

	return false
}

func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool {
	if r == nil {
		return c.IsMoreSpecificThan(o)
	}

	// imsi
	if (c.Mcc != 0 && c.Mnc != 0) || (o.Mcc != 0 && o.Mnc != 0) {
		if c.Mcc != o.Mcc && r.Mcc != 0 {
			return c.Mcc != 0
		}
		if c.Mnc != o.Mnc && r.Mnc != 0 {
			return c.Mnc != 0
		}
	}

	// locale
	if (c.Language[0] != 0 && c.Country[0] != 0) || (o.Language[0] != 0 && o.Country[0] != 0) {
		if c.Language[0] != o.Language[0] && r.Language[0] != 0 {
			return c.Language[0] != 0
		}
		if c.Country[0] != o.Country[0] && r.Country[0] != 0 {
			return c.Country[0] != 0
		}
	}

	// TODO: screen layout

	// orientation
	if c.Orientation != o.Orientation && r.Orientation != 0 {
		return c.Orientation != 0
	}

	// TODO: uimode

	// TODO: screen type

	// TODO: input

	// screen size
	if (c.ScreenWidth != 0 && c.ScreenHeight != 0) || (o.ScreenWidth != 0 && o.ScreenHeight != 0) {
		if c.ScreenWidth != o.ScreenWidth && r.ScreenWidth != 0 {
			return c.ScreenWidth != 0
		}
		if c.ScreenHeight != o.ScreenHeight && r.ScreenHeight != 0 {
			return c.ScreenHeight != 0
		}
	}

	// version
	if (c.SDKVersion != 0 && c.SDKVersion != 0) || (o.MinorVersion != 0 && o.MinorVersion != 0) {
		if c.SDKVersion != o.SDKVersion && r.SDKVersion != 0 {
			return c.SDKVersion > o.SDKVersion
		}
		if c.MinorVersion != o.MinorVersion {
			return c.MinorVersion != 0
		}
	}

	return false
}

func (c *ResTableConfig) Match(settings *ResTableConfig) bool {
	// match imsi
	if c.Mcc != 0 && c.Mnc != 0 {
		if settings.Mcc == 0 {
			if c.Mcc != 0 {
				return false
			}
		} else {
			if c.Mcc != 0 && c.Mcc != settings.Mcc {
				return false
			}
		}
		if settings.Mnc == 0 {
			if c.Mnc != 0 {
				return false
			}
		} else {
			if c.Mnc != 0 && c.Mnc != settings.Mnc {
				return false
			}
		}
	}

	// match locale
	if c.Language[0] != 0 && c.Country[0] != 0 {
		if settings.Language[0] != 0 && c.Language[0] != 0 &&
			!(settings.Language[0] == c.Language[0] && settings.Language[1] == c.Language[1]) {
			return false
		}
		if settings.Country[0] != 0 && c.Country[0] != 0 &&
			!(settings.Country[0] == c.Country[0] && settings.Country[1] == c.Country[1]) {
			return false
		}
	}

	// TODO: screen config
	// TODO: screen type
	// TODO: input

	// screen size
	if c.ScreenWidth != 0 && c.ScreenHeight != 0 {
		if settings.ScreenWidth != 0 && c.ScreenWidth != 0 &&
			c.ScreenWidth != settings.ScreenWidth {
			return false
		}
		if settings.ScreenHeight != 0 && c.ScreenHeight != 0 &&
			c.ScreenHeight != settings.ScreenHeight {
			return false
		}
	}

	// version
	if c.SDKVersion != 0 && c.MinorVersion != 0 {
		if settings.SDKVersion != 0 && c.SDKVersion != 0 &&
			c.SDKVersion > settings.SDKVersion {
			return false
		}
		if settings.MinorVersion != 0 && c.MinorVersion != 0 &&
			c.MinorVersion != settings.MinorVersion {
			return false
		}
	}

	return true
}
<commit_msg>configの比較条件を修正<commit_after>package androidbinary

import (
	"encoding/binary"
	"io"
	"os"
)

type ResId uint32

type TableFile struct {
	stringPool    *ResStringPool
	tablePackages map[uint32]*TablePackage
}

type ResTableHeader struct {
	Header       ResChunkHeader
	PackageCount uint32
}

type ResTablePackage struct {
	Header         ResChunkHeader
	Id             uint32
	Name           [128]uint16
	TypeStrings    uint32
	LastPublicType uint32
	KeyStrings     uint32
	LastPublicKey  uint32
}

type TablePackage struct {
	Header      ResTablePackage
	TypeStrings *ResStringPool
	KeyStrings  *ResStringPool
	TableTypes  []*TableType
}

type ResTableType struct {
	Header       ResChunkHeader
	Id           uint8
	Res0         uint8
	Res1         uint16
	EntryCount   uint32
	EntriesStart uint32
	Config       ResTableConfig
}

type ResTableConfig struct {
	Size uint32
	// imsi
	Mcc uint16
	Mnc uint16

	// locale
	Language [2]uint8
	Country  [2]uint8

	// screen type
	Orientation uint8
	Touchscreen uint8
	Density     uint16

	// inout
	Keyboard   uint8
	Navigation uint8
	InputFlags uint8
	InputPad0  uint8

	// screen
	ScreenWidth  uint16
	ScreenHeight uint16

	// version
	SDKVersion   uint16
	MinorVersion uint16

	// screen config
	ScreenLayout     uint8
	UIMode           uint8
	ScreenConfigPad1 uint8
	ScreenConfigPad2 uint8
}

type TableType struct {
	Header  *ResTableType
	Entries []TableEntry
}

type ResTableEntry struct {
	Size  uint16
	Flags uint16
	Key   ResStringPoolRef
}

type TableEntry struct {
	Key   *ResTableEntry
	Value *ResValue
	Flags uint32
}

type ResTableTypeSpec struct {
	Header     ResChunkHeader
	Id         uint8
	Res0       uint8
	Res1       uint16
	EntryCount uint32
}

func (id ResId) Package() int {
	return int(id) >> 24
}

func (id ResId) Type() int {
	return (int(id) >> 16) & 0xFF
}

func (id ResId) Entry() int {
	return int(id) & 0xFFFF
}

func NewTableFile(r io.ReaderAt) (*TableFile, error) {
	f := new(TableFile)
	sr := io.NewSectionReader(r, 0, 1<<63-1)

	header := new(ResTableHeader)
	binary.Read(sr, binary.LittleEndian, header)
	f.tablePackages = make(map[uint32]*TablePackage)

	offset := int64(header.Header.HeaderSize)
	for offset < int64(header.Header.Size) {
		chunkHeader, err := f.readChunk(sr, offset)
		if err != nil {
			return nil, err
		}
		offset += int64(chunkHeader.Size)
	}
	return f, nil
}

func (f *TableFile) findPackage(id int) interface{} {
	return f.tablePackages[uint32(id)]
}

func (f *TableFile) readChunk(r io.ReaderAt, offset int64) (*ResChunkHeader, error) {
	sr := io.NewSectionReader(r, offset, 1<<63-1-offset)
	chunkHeader := &ResChunkHeader{}
	sr.Seek(0, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil {
		return nil, err
	}

	var err error
	sr.Seek(0, os.SEEK_SET)
	switch chunkHeader.Type {
	case RES_STRING_POOL_TYPE:
		f.stringPool, err = readStringPool(sr)
	case RES_TABLE_PACKAGE_TYPE:
		var tablePackage *TablePackage
		tablePackage, err = readTablePackage(sr)
		f.tablePackages[tablePackage.Header.Id] = tablePackage
	}
	if err != nil {
		return nil, err
	}

	return chunkHeader, nil
}

func readTablePackage(sr *io.SectionReader) (*TablePackage, error) {
	tablePackage := new(TablePackage)
	header := new(ResTablePackage)
	if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
		return nil, err
	}
	tablePackage.Header = *header

	srTypes := io.NewSectionReader(sr, int64(header.TypeStrings), int64(header.Header.Size-header.TypeStrings))
	if typeStrings, err := readStringPool(srTypes); err == nil {
		tablePackage.TypeStrings = typeStrings
	} else {
		return nil, err
	}

	srKeys := io.NewSectionReader(sr, int64(header.KeyStrings), int64(header.Header.Size-header.KeyStrings))
	if keyStrings, err := readStringPool(srKeys); err == nil {
		tablePackage.KeyStrings = keyStrings
	} else {
		return nil, err
	}

	offset := int64(header.Header.HeaderSize)
	for offset < int64(header.Header.Size) {
		chunkHeader := &ResChunkHeader{}
		sr.Seek(offset, os.SEEK_SET)
		if err := binary.Read(sr, binary.LittleEndian, chunkHeader); err != nil {
			return nil, err
		}

		var err error
		chunkReader := io.NewSectionReader(sr, offset, int64(chunkHeader.Size))
		sr.Seek(offset, os.SEEK_SET)
		switch chunkHeader.Type {
		case RES_TABLE_TYPE_TYPE:
			var tableType *TableType
			tableType, err = readTableType(chunkReader)
			tablePackage.TableTypes = append(tablePackage.TableTypes, tableType)
		case RES_TABLE_TYPE_SPEC_TYPE:
			_, err = readTableTypeSpec(chunkReader)
		}
		if err != nil {
			return nil, err
		}
		offset += int64(chunkHeader.Size)
	}

	return tablePackage, nil
}

func readTableType(sr *io.SectionReader) (*TableType, error) {
	header := new(ResTableType)
	if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
		return nil, err
	}

	entryIndexes := make([]uint32, header.EntryCount)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, entryIndexes); err != nil {
		return nil, err
	}

	entries := make([]TableEntry, header.EntryCount)
	for i, index := range entryIndexes {
		if index == 0xFFFFFFFF {
			continue
		}
		sr.Seek(int64(header.EntriesStart+index), os.SEEK_SET)
		var key ResTableEntry
		binary.Read(sr, binary.LittleEndian, &key)
		entries[i].Key = &key

		var val ResValue
		binary.Read(sr, binary.LittleEndian, &val)
		entries[i].Value = &val
	}
	return &TableType{
		header,
		entries,
	}, nil
}

func readTableTypeSpec(sr *io.SectionReader) ([]uint32, error) {
	header := new(ResTableTypeSpec)
	if err := binary.Read(sr, binary.LittleEndian, header); err != nil {
		return nil, err
	}

	flags := make([]uint32, header.EntryCount)
	sr.Seek(int64(header.Header.HeaderSize), os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, flags); err != nil {
		return nil, err
	}
	return flags, nil
}

func (c *ResTableConfig) IsMoreSpecificThan(o *ResTableConfig) bool {
	// imsi
	if c.Mcc != o.Mcc {
		if c.Mcc != 0 {
			return false
		}
		if o.Mnc != 0 {
			return true
		}
	}
	if c.Mnc != o.Mnc {
		if c.Mnc != 0 {
			return false
		}
		if o.Mnc != 0 {
			return true
		}
	}

	// locale
	if c.Language[0] != o.Language[0] {
		if c.Language[0] != 0 {
			return false
		}
		if o.Language[0] != 0 {
			return true
		}
	}
	if c.Country[0] != o.Country[0] {
		if c.Country[0] != 0 {
			return false
		}
		if o.Country[0] != 0 {
			return true
		}
	}

	// orientation
	if c.Orientation != o.Orientation {
		if c.Orientation != 0 {
			return false
		}
		if o.Orientation != 0 {
			return true
		}
	}

	// TODO: uimode

	// touchscreen
	if c.Touchscreen != o.Touchscreen {
		if c.Touchscreen != 0 {
			return false
		}
		if o.Touchscreen != 0 {
			return true
		}
	}

	// TODO: input

	// screen size
	if c.ScreenWidth != o.ScreenWidth {
		if c.ScreenWidth != 0 {
			return false
		}
		if o.ScreenWidth != 0 {
			return true
		}
	}
	if c.ScreenHeight != o.ScreenHeight {
		if c.ScreenHeight != 0 {
			return false
		}
		if o.ScreenHeight != 0 {
			return true
		}
	}

	//version
	if c.SDKVersion != o.SDKVersion {
		if c.SDKVersion != 0 {
			return false
		}
		if o.SDKVersion != 0 {
			return true
		}
	}
	if c.MinorVersion != o.MinorVersion {
		if c.MinorVersion != 0 {
			return false
		}
		if o.MinorVersion != 0 {
			return true
		}
	}

	return false
}

func (c *ResTableConfig) IsBetterThan(o *ResTableConfig, r *ResTableConfig) bool {
	if r == nil {
		return c.IsMoreSpecificThan(o)
	}

	// imsi
	if c.Mcc != 0 || c.Mnc != 0 || o.Mcc != 0 || o.Mnc != 0 {
		if c.Mcc != o.Mcc && r.Mcc != 0 {
			return c.Mcc != 0
		}
		if c.Mnc != o.Mnc && r.Mnc != 0 {
			return c.Mnc != 0
		}
	}

	// locale
	if c.Language[0] != 0 || c.Country[0] != 0 || o.Language[0] != 0 || o.Country[0] != 0 {
		if c.Language[0] != o.Language[0] && r.Language[0] != 0 {
			return c.Language[0] != 0
		}
		if c.Country[0] != o.Country[0] && r.Country[0] != 0 {
			return c.Country[0] != 0
		}
	}

	// TODO: screen layout

	// orientation
	if c.Orientation != o.Orientation || r.Orientation != 0 {
		return c.Orientation != 0
	}

	// TODO: uimode

	// TODO: screen type

	// TODO: input

	// screen size
	if c.ScreenWidth != 0 || c.ScreenHeight != 0 || o.ScreenWidth != 0 || o.ScreenHeight != 0 {
		if c.ScreenWidth != o.ScreenWidth && r.ScreenWidth != 0 {
			return c.ScreenWidth != 0
		}
		if c.ScreenHeight != o.ScreenHeight && r.ScreenHeight != 0 {
			return c.ScreenHeight != 0
		}
	}

	// version
	if c.SDKVersion != 0 || c.SDKVersion != 0 || o.MinorVersion != 0 || o.MinorVersion != 0 {
		if c.SDKVersion != o.SDKVersion && r.SDKVersion != 0 {
			return c.SDKVersion > o.SDKVersion
		}
		if c.MinorVersion != o.MinorVersion {
			return c.MinorVersion != 0
		}
	}

	return false
}

func (c *ResTableConfig) Match(settings *ResTableConfig) bool {
	// match imsi
	if settings.Mcc == 0 {
		if c.Mcc != 0 {
			return false
		}
	} else {
		if c.Mcc != 0 && c.Mcc != settings.Mcc {
			return false
		}
	}
	if settings.Mnc == 0 {
		if c.Mnc != 0 {
			return false
		}
	} else {
		if c.Mnc != 0 && c.Mnc != settings.Mnc {
			return false
		}
	}

	// match locale
	if settings.Language[0] != 0 && c.Language[0] != 0 &&
		!(settings.Language[0] == c.Language[0] && settings.Language[1] == c.Language[1]) {
		return false
	}
	if settings.Country[0] != 0 && c.Country[0] != 0 &&
		!(settings.Country[0] == c.Country[0] && settings.Country[1] == c.Country[1]) {
		return false
	}

	// TODO: screen config
	// TODO: screen type
	// TODO: input

	// screen size
	if settings.ScreenWidth != 0 && c.ScreenWidth != 0 &&
		c.ScreenWidth != settings.ScreenWidth {
		return false
	}
	if settings.ScreenHeight != 0 && c.ScreenHeight != 0 &&
		c.ScreenHeight != settings.ScreenHeight {
		return false
	}

	// version
	if settings.SDKVersion != 0 && c.SDKVersion != 0 &&
		c.SDKVersion > settings.SDKVersion {
		return false
	}
	if settings.MinorVersion != 0 && c.MinorVersion != 0 &&
		c.MinorVersion != settings.MinorVersion {
		return false
	}

	return true
}

func (c *ResTableConfig) Locale() string {
	if c.Language[0] == 0 {
		return ""
	}
	if c.Country[0] == 0 {
		return fmt.Sprintf("%c%c", c.Language[0], c.Language[1])
	}
	return fmt.Sprintf("%c%c-%c%c", c.Language[0], c.Language[1], c.Country[0], c.Country[1])
}
<|endoftext|>


<commit_before>package traceroute

import (
	"errors"
	"net"
	"syscall"
	"time"
)

func socketAddr() (addr [4]byte, err error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return
	}

	for _, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if len(ipnet.IP.To4()) == net.IPv4len {
				copy(addr[:], ipnet.IP.To4())
				return
			}
		}
	}
	err = errors.New("You do not appear to be connected to the Internet")
	return
}

func destAddr(dest string) (destAddr [4]byte, err error) {
	addrs, err := net.LookupHost(dest)
	if err != nil {
		return
	}
	addr := addrs[0]

	ipAddr, err := net.ResolveIPAddr("ip", addr)
	if err != nil {
		return
	}
	copy(destAddr[:], ipAddr.IP.To4())
	return
}

type TracerouteOptions struct {
	Port       int
	MaxHops    int
	TimeoutMs  int64
	Retries    int
	PacketSize int
}

type TracerouteHop struct {
	Address     [4]byte
	N           int
	ElapsedTime time.Duration
}

type TracerouteResult struct {
	Hops []TracerouteHop
}

func defaultOptions(options *TracerouteOptions) {
	if options.Port == 0 {
		options.Port = 33434
	}
	if options.MaxHops == 0 {
		options.MaxHops = 64
	}
	if options.TimeoutMs == 0 {
		options.TimeoutMs = 1000
	}
	if options.Retries == 0 {
		options.Retries = 3
	}
	if options.PacketSize == 0 {
		options.PacketSize = 52
	}
}

func Traceroute(dest string, options *TracerouteOptions) (result TracerouteResult, err error) {
	result.Hops = []TracerouteHop{}
	defaultOptions(options)
	destAddr, err := destAddr(dest)
	result.DestinationAddress = destAddr
	socketAddr, err := socketAddr()
	if err != nil {
		return
	}

	tv := syscall.NsecToTimeval(1000 * 1000 * options.TimeoutMs)

	ttl := 1
	retry := 0
	for {
		start := time.Now()

		recvSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
		if err != nil {
			return result, err
		}

		sendSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
		if err != nil {
			return result, err
		}
		syscall.SetsockoptInt(sendSocket, 0x0, syscall.IP_TTL, ttl)
		syscall.SetsockoptTimeval(recvSocket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv)

		defer syscall.Close(recvSocket)
		defer syscall.Close(sendSocket)

		syscall.Bind(recvSocket, &syscall.SockaddrInet4{Port: options.Port, Addr: socketAddr})
		syscall.Sendto(sendSocket, []byte{0x0}, 0, &syscall.SockaddrInet4{Port: options.Port, Addr: destAddr})

		var p = make([]byte, options.PacketSize)
		n, from, err := syscall.Recvfrom(recvSocket, p, 0)
		elapsed := time.Since(start)
		if err == nil {
			currAddr := from.(*syscall.SockaddrInet4).Addr
			result.Hops = append(result.Hops, TracerouteHop{Address: currAddr, N: n, ElapsedTime: elapsed})
			//log.Println("Received n=", n, ", from=", currAddr, ", t=", elapsed)

			ttl += 1
			retry = 0

			if ttl > options.MaxHops || currAddr == destAddr {
				return result, nil
			}
		} else {
			retry += 1
			//log.Print("* ")
			if retry > options.Retries {
				ttl += 1
				retry = 0
			}
		}

	}
}
<commit_msg>Include the destination address<commit_after>package traceroute

import (
	"errors"
	"net"
	"syscall"
	"time"
)

func socketAddr() (addr [4]byte, err error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return
	}

	for _, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if len(ipnet.IP.To4()) == net.IPv4len {
				copy(addr[:], ipnet.IP.To4())
				return
			}
		}
	}
	err = errors.New("You do not appear to be connected to the Internet")
	return
}

func destAddr(dest string) (destAddr [4]byte, err error) {
	addrs, err := net.LookupHost(dest)
	if err != nil {
		return
	}
	addr := addrs[0]

	ipAddr, err := net.ResolveIPAddr("ip", addr)
	if err != nil {
		return
	}
	copy(destAddr[:], ipAddr.IP.To4())
	return
}

type TracerouteOptions struct {
	Port       int
	MaxHops    int
	TimeoutMs  int64
	Retries    int
	PacketSize int
}

type TracerouteHop struct {
	Address     [4]byte
	N           int
	ElapsedTime time.Duration
}

type TracerouteResult struct {
	DestinationAddress [4]byte
	Hops               []TracerouteHop
}

func defaultOptions(options *TracerouteOptions) {
	if options.Port == 0 {
		options.Port = 33434
	}
	if options.MaxHops == 0 {
		options.MaxHops = 64
	}
	if options.TimeoutMs == 0 {
		options.TimeoutMs = 1000
	}
	if options.Retries == 0 {
		options.Retries = 3
	}
	if options.PacketSize == 0 {
		options.PacketSize = 52
	}
}

func Traceroute(dest string, options *TracerouteOptions) (result TracerouteResult, err error) {
	result.Hops = []TracerouteHop{}
	defaultOptions(options)
	destAddr, err := destAddr(dest)
	result.DestinationAddress = destAddr
	socketAddr, err := socketAddr()
	if err != nil {
		return
	}

	tv := syscall.NsecToTimeval(1000 * 1000 * options.TimeoutMs)

	ttl := 1
	retry := 0
	for {
		start := time.Now()

		recvSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
		if err != nil {
			return result, err
		}

		sendSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
		if err != nil {
			return result, err
		}
		syscall.SetsockoptInt(sendSocket, 0x0, syscall.IP_TTL, ttl)
		syscall.SetsockoptTimeval(recvSocket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv)

		defer syscall.Close(recvSocket)
		defer syscall.Close(sendSocket)

		syscall.Bind(recvSocket, &syscall.SockaddrInet4{Port: options.Port, Addr: socketAddr})
		syscall.Sendto(sendSocket, []byte{0x0}, 0, &syscall.SockaddrInet4{Port: options.Port, Addr: destAddr})

		var p = make([]byte, options.PacketSize)
		n, from, err := syscall.Recvfrom(recvSocket, p, 0)
		elapsed := time.Since(start)
		if err == nil {
			currAddr := from.(*syscall.SockaddrInet4).Addr
			result.Hops = append(result.Hops, TracerouteHop{Address: currAddr, N: n, ElapsedTime: elapsed})
			//log.Println("Received n=", n, ", from=", currAddr, ", t=", elapsed)

			ttl += 1
			retry = 0

			if ttl > options.MaxHops || currAddr == destAddr {
				return result, nil
			}
		} else {
			retry += 1
			//log.Print("* ")
			if retry > options.Retries {
				ttl += 1
				retry = 0
			}
		}

	}
}
<|endoftext|>


<commit_before><commit_msg>Close #4 fix error output of scanner<commit_after><|endoftext|>


<commit_before>package loader

import (
	"testing"
)

func TestSourceParse(t *testing.T) {
	err := LoadDefaultSources()
	if err != nil {
		t.Error(err)
	}
	if len(sources) < 1 {
		t.Error("No Sources parsed!")
	}

	// Check the status Source
	status := sources[0]
	if status.Name != "status" {
		t.Error("First view is not named `status`")
	}
}

func TestGetSource(t *testing.T) {
	source, err := GetSource("status")
	if err != nil {
		t.Error(err)
	}
	if source.Name != "status" {
		t.Errorf("Unexpected status name: %s", source.Name)
	}
	if source.Description != "Mysql server global status counters" {
		t.Errorf("Unexpected status description: %s", source.Description)
	}
}
<commit_msg>Test GetSource Errors<commit_after>package loader

import (
	"testing"
)

func TestSourceParse(t *testing.T) {
	err := LoadDefaultSources()
	if err != nil {
		t.Error(err)
	}
	if len(sources) < 1 {
		t.Error("No Sources parsed!")
	}

	// Check the status Source
	status := sources[0]
	if status.Name != "status" {
		t.Error("First view is not named `status`")
	}
}

func TestGetSource(t *testing.T) {
	source, err := GetSource("status")
	if err != nil {
		t.Error(err)
	}
	if source.Name != "status" {
		t.Errorf("Unexpected status name: %s", source.Name)
	}
	if source.Description != "Mysql server global status counters" {
		t.Errorf("Unexpected status description: %s", source.Description)
	}
}

func TestGetSourceErr(t *testing.T) {
	_, err := GetSource("fooey")
	if err == nil {
		t.Error("Expected error!")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"log"
)

type Client struct {
	writer http.ResponseWriter
	channel chan <- string
}

func handleMessages(messageChan <- chan string, addChan <- chan Client, removeChan <- chan Client) {
	clients := make(map[http.ResponseWriter] chan <- string)

	for {
		select {
		case message := <- messageChan:
			log.Print("New message: ", message)
			for _, channel := range clients {
				// TODO: use buffered channel?
				go func () {
					channel <- message
				}()
			}
		case client := <- addChan:
			log.Print("Client connected: ", client)
			clients[client.writer] = client.channel
		case client := <- removeChan:
			log.Print("Client disconnected: ", client)
			delete(clients, client.writer)
		}
	}
}

func handleStream(messageChan chan <- string, addChan chan <- Client, removeChan chan <- Client, writer http.ResponseWriter, request *http.Request) {
	writer.Header().Set("Content-Type", "text/event-stream")
	writer.Header().Set("Access-Control-Allow-Origin", "*")
	writer.WriteHeader(200)

	channel := make(chan string)
	client  := Client{writer, channel}

	addChan <- client

	for {
		message := <- channel
		if _, error := writer.Write([]byte(message + "\r\n")); error != nil {
			log.Print("Write: ", error)
			break
		}
		writer.(http.Flusher).Flush()
	}

	removeChan <- client
}

func handleMessage(messageChan chan <- string, writer http.ResponseWriter, request *http.Request) {
	request.ParseForm()
	message := request.FormValue("message")
	messageChan <- message
	writer.WriteHeader(200)
}

func main() {
	messagesChan := make(chan string)
	addChan      := make(chan Client)
	removeChan   := make(chan Client)

	go handleMessages(messagesChan, addChan, removeChan)

	http.HandleFunc("/", func (writer http.ResponseWriter, request *http.Request) {
		http.ServeFile(writer, request, "static/index.html")
	})
	http.HandleFunc("/static/", func (writer http.ResponseWriter, request *http.Request) {
		http.ServeFile(writer, request, request.URL.Path[1:])
	})
	http.HandleFunc("/stream", func (writer http.ResponseWriter, request *http.Request) {
		handleStream(messagesChan, addChan, removeChan, writer, request)
	})
	http.HandleFunc("/messages", func (writer http.ResponseWriter, request *http.Request) {
		handleMessage(messagesChan, writer, request)
	})

	log.Print("Starting server on :8080")

	http.ListenAndServe(":8080", nil)
}
<commit_msg>working fanout<commit_after>package main

import (
	"net/http"
	"log"
)

type Client struct {
	channel chan <- string
}

func handleMessages(messageChan <- chan string, addChan <- chan Client, removeChan <- chan Client) {
	channels := make(map[Client] chan <- string)

	for {
		select {
		case message := <- messageChan:
			log.Print("New message: ", message)
			for _, channel := range channels {
				go func (c chan <- string) {
					c <- message
				}(channel)
			}
		case client := <- addChan:
			log.Print("Client connected: ", client)
			channels[client] = client.channel
		case client := <- removeChan:
			log.Print("Client disconnected: ", client)
			delete(channels, client)
		}
	}
}

func handleStream(messageChan chan <- string, addChan chan <- Client, removeChan chan <- Client, writer http.ResponseWriter, request *http.Request) {
	writer.Header().Set("Content-Type", "text/event-stream")
	writer.Header().Set("Access-Control-Allow-Origin", "*")
	writer.WriteHeader(200)

	channel := make(chan string)
	client  := Client{channel}

	addChan <- client

	for {
		message := <- channel
		if _, error := writer.Write([]byte(message + "\r\n")); error != nil {
			log.Print("Write: ", error)
			break
		}
		writer.(http.Flusher).Flush()
	}

	removeChan <- client
}

func handleMessage(messageChan chan <- string, writer http.ResponseWriter, request *http.Request) {
	request.ParseForm()
	message := request.FormValue("message")
	messageChan <- message
	writer.WriteHeader(200)
}

func main() {
	messagesChan := make(chan string)
	addChan      := make(chan Client)
	removeChan   := make(chan Client)

	go handleMessages(messagesChan, addChan, removeChan)

	http.HandleFunc("/", func (writer http.ResponseWriter, request *http.Request) {
		http.ServeFile(writer, request, "static/index.html")
	})
	http.HandleFunc("/static/", func (writer http.ResponseWriter, request *http.Request) {
		http.ServeFile(writer, request, request.URL.Path[1:])
	})
	http.HandleFunc("/stream", func (writer http.ResponseWriter, request *http.Request) {
		handleStream(messagesChan, addChan, removeChan, writer, request)
	})
	http.HandleFunc("/messages", func (writer http.ResponseWriter, request *http.Request) {
		handleMessage(messagesChan, writer, request)
	})

	log.Print("Starting server on :8080")

	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before><commit_msg>Improved tests<commit_after><|endoftext|>


<commit_before>package main

import (
	"bufio"
	"github.com/Toorop/tmail/config"
	//"github.com/Toorop/tmail/deliverd"
	"fmt"
	"github.com/Toorop/tmail/logger"
	s "github.com/Toorop/tmail/scope"
	"github.com/Toorop/tmail/smtpd"
	"github.com/bitly/nsq/nsqd"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const (
	// TMAIL_VERSION version of tmail
	TMAIL_VERSION = "0.0.1"
)

var (
	cfg   *config.Config
	scope *s.Scope
)

func init() {
	var err error
	// Init config
	cfg, err = config.Init("tmail")
	if err != nil {
		log.Fatalln(err)
	}

	// Init DB
	DB, err := gorm.Open(cfg.GetDbDriver(), cfg.GetDbSource())
	if err != nil {
		log.Fatalln("Database initialisation failed", err)
	}
	DB.LogMode(cfg.GetDebugEnabled())

	// ping
	if DB.DB().Ping() != nil {
		log.Fatalln("I could not access to database", cfg.GetDbDriver(), cfg.GetDbSource(), err)
	}
	if !dbIsOk(DB) {
		var r []byte
		for {
			fmt.Print(fmt.Sprintf("Database 'driver: %s, source: %s' misses some tables.\r\nShould i create them ? (y/n):", cfg.GetDbDriver(), cfg.GetDbSource()))
			r, _, _ = bufio.NewReader(os.Stdin).ReadLine()
			if r[0] == 110 || r[0] == 121 {
				break
			}
		}
		if r[0] == 121 {
			if err = initDB(DB); err != nil {
				log.Fatalln(err)
			}
		} else {
			log.Fatalln("See you soon...")
		}
	}

	// Init scope
	scope = s.New(cfg, DB)
}

// MAIN
func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	l := logger.New(cfg.GetDebugEnabled())
	// if there nothing to do do nothing
	if !cfg.GetLaunchDeliverd() && !cfg.GetLaunchSmtpd() {
		log.Fatalln("I have nothing to do, so i do nothing. Bye.")
	}

	// Synch tables to structs
	if err := autoMigrateDB(scope.DB); err != nil {
		log.Fatalln(err)
	}

	// Loop
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Chanel to comunicate between all elements
	//daChan := make(chan string)

	// nsqd
	opts := nsqd.NewNSQDOptions()
	opts.Verbose = cfg.GetDebugEnabled()
	nsqd := nsqd.NewNSQD(opts)

	nsqd.LoadMetadata()
	err := nsqd.PersistMetadata()
	if err != nil {
		log.Fatalf("ERROR: failed to persist metadata - %s", err.Error())
	}
	nsqd.Main()

	// smtpd
	if cfg.GetLaunchSmtpd() {
		smtpdDsns, err := smtpd.GetDsnsFromString(cfg.GetSmtpdDsns())
		if err != nil {
			log.Fatalln("unable to parse smtpd dsn -", err)
		}
		for _, dsn := range smtpdDsns {
			go smtpd.New(scope, dsn).ListenAndServe()
			l.Info("smtpd " + dsn.String() + " launched.")
		}
	}

	// deliverd
	/*d, err := deliverd.New()
	if err != nil {
		log.Fatalln(err)
	}
	log.Println(d.Config)
	//go d.Run()*/

	<-sigChan
	l.Info("Exiting...")
	/*for {
		fromSmtpChan = <-smtpChan
		TRACE.Println(fromSmtpChan)
	}*/
}

/*var (
	me string // my hostname
	//distPath string // Path where the dist is
	confPath string // Path where are located config files

	// Config store config
	Config *MergedConfig

	// defaults Loggers - TODO usefull ?
	TRACE = log.New(ioutil.Discard, "TRACE -", log.Ldate|log.Ltime|log.Lshortfile)
	INFO  = log.New(ioutil.Discard, "INFO  -", log.Ldate|log.Ltime)
	WARN  = log.New(ioutil.Discard, "WARN  -", log.Ldate|log.Ltime)
	ERROR = log.New(os.Stderr, "ERROR -", log.Ldate|log.Ltime|log.Lshortfile)

	// SMTP server DSNs
	smtpDsn []dsn

	// mgo session
	mgoSession *mgo.Session

	// database
	db gorm.DB

	//  store
	queueStore store.Storer

	// Queue
	queue *mailsQueue

	// Global countDeliveries
	countDeliveries int // number of deliveries in progress
)*/

// INIT
/*func init() {
	var err error

	log.SetFlags(ERROR.Flags()) // default

	// Dist path
	distPath, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatalln("Enable to get distribution path")
	}
	// ConfPath
	confPath = path.Join(distPath, "conf")
	// Exists ?
	if _, err := os.Stat(confPath); os.IsNotExist(err) {
		fmt.Print(fmt.Sprintf("Creating config path %s...", confPath))
		err = os.Mkdir(confPath, 0700)
		if err != nil {
			log.Fatalln("Unable to create config path ", confPath, ". Check permissions.")
		}
		fmt.Println("Done.")
	}

	// tmail.conf exists ?
	if _, err := os.Stat(path.Join(confPath, "tmail.conf")); os.IsNotExist(err) {
		log.Fatalln("Main configuration file", path.Join(confPath, "tmail.conf"), "does not exists !")
	}

	// load config tmail.conf
	Config, err = LoadConfig("tmail.conf")
	if err != nil || Config == nil {
		log.Fatalln("Fail to load main configuration file", path.Join(confPath, "tmail.conf"), err)
	}
	// use default section (TODO : dev section)
	Config.SetSection(config.DEFAULT_SECTION)
	//Config.SetSection("prod")
	me = Config.StringDefault("me", "localhost")

	// Init log
	TRACE = getLogger("trace")
	INFO = getLogger("info")
	WARN = getLogger("warn")
	ERROR = getLogger("error")

	// Initialize Database
	dbDriver, found := Config.String("db.driver")
	if !found {
		ERROR.Fatalln("No db.driver found in your config file")
	}
	dbDsn, found := Config.String("db.dsn")
	if !found {
		ERROR.Fatalln("No db.dsn found in your config file")
	}
	db, err = gorm.Open(dbDriver, dbDsn)
	if err != nil {
		ERROR.Fatalln("Database initialisation failed", err)
	}
	db.LogMode(Config.BoolDefault("db.debug", false))
	err = db.DB().Ping()
	if err != nil {
		ERROR.Fatalln(fmt.Sprintf("I could not access to database 'driver: %s, dns: %s - '", dbDriver, dbDsn), err)
	}
	if !dbIsOk() {
		var r []byte
		for {
			fmt.Print(fmt.Sprintf("Database 'driver: %s, dns: %s' misses some tables.\r\nShould i create them ? (y/n):", dbDriver, dbDsn))
			r, _, _ = bufio.NewReader(os.Stdin).ReadLine()
			if r[0] == 110 || r[0] == 121 {
				break
			}
		}
		if r[0] == 121 {
			if err = initDB(); err != nil {
				ERROR.Fatalln(err)
			}
		} else {
			INFO.Fatalln("See you soon...")
		}
	}

	// Synch tables to structs
	if err = autoMigrateDB(); err != nil {
		ERROR.Fatalln(err)
	}

	// DSN for SMTP server
	//var found bool
	strSmtpDsn, found := Config.String("smtp.dsn")
	if !found {
		INFO.Println("No smtp.dsn found in config file (tmail.conf). Listening on 0.0.0.0:25 with no SSL nor TLS extension")
		strSmtpDsn = "0.0.0.0:25:none"
	}
	// Are dsn OK ? We just validate entry, no check on IP/Port, they will be done with listen & serve
	smtpDsn, err = getDsnsFromString(strSmtpDsn)
	if err != nil {
		ERROR.Fatalln(err)
	}

	// Load plugins smtpIn_helo_01_monplugin

	// Init stores
	// queueStore
	switch Config.StringDefault("queue.store.type", "disk") {
	case "disk":
		queuePath, found := Config.String("queue.store.diskpath")
		if !found {
			queuePath = path.Join(distPath, "queue")
		}
		queueStore, err = store.NewDiskStore(queuePath)
		if err != nil {
			ERROR.Fatalln("Unable to get queueStore -", err)
		}
	}

	// Queue
	queue = &mailsQueue{}

	// init some globals
	countDeliveries = 0

	// Done
	INFO.Println("Init sequence done")

}
*/
<commit_msg>check paths on init<commit_after>package main

import (
	"bufio"
	"github.com/Toorop/tmail/config"
	//"github.com/Toorop/tmail/deliverd"
	"fmt"
	"github.com/Toorop/tmail/logger"
	s "github.com/Toorop/tmail/scope"
	"github.com/Toorop/tmail/smtpd"
	"github.com/Toorop/tmail/util"
	"github.com/bitly/nsq/nsqd"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"path"
	"syscall"
	"time"
)

const (
	// TMAIL_VERSION version of tmail
	TMAIL_VERSION = "0.0.1"
)

var (
	cfg   *config.Config
	scope *s.Scope
)

func init() {
	var err error
	// Init config
	cfg, err = config.Init("tmail")
	if err != nil {
		log.Fatalln(err)
	}

	// Check base path structure
	requiredPaths := []string{"db", "nsq"}
	for _, p := range requiredPaths {
		if err = os.MkdirAll(path.Join(util.GetBasePath(), p), 0700); err != nil {
			log.Fatalln("Unable to create path "+path.Join(util.GetBasePath(), p), " - ", err.Error())
		}
	}

	// if clusterMode check if nsqlookupd is available
	// Todo

	// Init DB
	DB, err := gorm.Open(cfg.GetDbDriver(), cfg.GetDbSource())
	if err != nil {
		log.Fatalln("Database initialisation failed", err)
	}
	DB.LogMode(cfg.GetDebugEnabled())

	// ping
	if DB.DB().Ping() != nil {
		log.Fatalln("I could not access to database", cfg.GetDbDriver(), cfg.GetDbSource(), err)
	}
	if !dbIsOk(DB) {
		var r []byte
		for {
			fmt.Print(fmt.Sprintf("Database 'driver: %s, source: %s' misses some tables.\r\nShould i create them ? (y/n):", cfg.GetDbDriver(), cfg.GetDbSource()))
			r, _, _ = bufio.NewReader(os.Stdin).ReadLine()
			if r[0] == 110 || r[0] == 121 {
				break
			}
		}
		if r[0] == 121 {
			if err = initDB(DB); err != nil {
				log.Fatalln(err)
			}
		} else {
			log.Fatalln("See you soon...")
		}
	}

	// Init scope
	scope = s.New(cfg, DB)
}

// MAIN
func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	l := logger.New(cfg.GetDebugEnabled())
	// if there nothing to do do nothing
	if !cfg.GetLaunchDeliverd() && !cfg.GetLaunchSmtpd() {
		log.Fatalln("I have nothing to do, so i do nothing. Bye.")
	}

	// Synch tables to structs
	if err := autoMigrateDB(scope.DB); err != nil {
		log.Fatalln(err)
	}

	// Loop
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Chanel to comunicate between all elements
	//daChan := make(chan string)

	// nsqd
	opts := nsqd.NewNSQDOptions()
	opts.Verbose = cfg.GetDebugEnabled() // verbosity
	opts.DataPath = util.GetBasePath() + "/nsq"

	nsqd := nsqd.NewNSQD(opts)

	nsqd.LoadMetadata()
	err := nsqd.PersistMetadata()
	if err != nil {
		log.Fatalf("ERROR: failed to persist metadata - %s", err.Error())
	}
	nsqd.Main()

	// smtpd
	if cfg.GetLaunchSmtpd() {
		smtpdDsns, err := smtpd.GetDsnsFromString(cfg.GetSmtpdDsns())
		if err != nil {
			log.Fatalln("unable to parse smtpd dsn -", err)
		}
		for _, dsn := range smtpdDsns {
			go smtpd.New(scope, dsn).ListenAndServe()
			l.Info("smtpd " + dsn.String() + " launched.")
		}
	}

	// deliverd
	/*d, err := deliverd.New()
	if err != nil {
		log.Fatalln(err)
	}
	log.Println(d.Config)
	//go d.Run()*/

	<-sigChan
	l.Info("Exiting...")
	/*for {
		fromSmtpChan = <-smtpChan
		TRACE.Println(fromSmtpChan)
	}*/
}

/*var (
	me string // my hostname
	//distPath string // Path where the dist is
	confPath string // Path where are located config files

	// Config store config
	Config *MergedConfig

	// defaults Loggers - TODO usefull ?
	TRACE = log.New(ioutil.Discard, "TRACE -", log.Ldate|log.Ltime|log.Lshortfile)
	INFO  = log.New(ioutil.Discard, "INFO  -", log.Ldate|log.Ltime)
	WARN  = log.New(ioutil.Discard, "WARN  -", log.Ldate|log.Ltime)
	ERROR = log.New(os.Stderr, "ERROR -", log.Ldate|log.Ltime|log.Lshortfile)

	// SMTP server DSNs
	smtpDsn []dsn

	// mgo session
	mgoSession *mgo.Session

	// database
	db gorm.DB

	//  store
	queueStore store.Storer

	// Queue
	queue *mailsQueue

	// Global countDeliveries
	countDeliveries int // number of deliveries in progress
)*/

// INIT
/*func init() {
	var err error

	log.SetFlags(ERROR.Flags()) // default

	// Dist path
	distPath, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatalln("Enable to get distribution path")
	}
	// ConfPath
	confPath = path.Join(distPath, "conf")
	// Exists ?
	if _, err := os.Stat(confPath); os.IsNotExist(err) {
		fmt.Print(fmt.Sprintf("Creating config path %s...", confPath))
		err = os.Mkdir(confPath, 0700)
		if err != nil {
			log.Fatalln("Unable to create config path ", confPath, ". Check permissions.")
		}
		fmt.Println("Done.")
	}

	// tmail.conf exists ?
	if _, err := os.Stat(path.Join(confPath, "tmail.conf")); os.IsNotExist(err) {
		log.Fatalln("Main configuration file", path.Join(confPath, "tmail.conf"), "does not exists !")
	}

	// load config tmail.conf
	Config, err = LoadConfig("tmail.conf")
	if err != nil || Config == nil {
		log.Fatalln("Fail to load main configuration file", path.Join(confPath, "tmail.conf"), err)
	}
	// use default section (TODO : dev section)
	Config.SetSection(config.DEFAULT_SECTION)
	//Config.SetSection("prod")
	me = Config.StringDefault("me", "localhost")

	// Init log
	TRACE = getLogger("trace")
	INFO = getLogger("info")
	WARN = getLogger("warn")
	ERROR = getLogger("error")

	// Initialize Database
	dbDriver, found := Config.String("db.driver")
	if !found {
		ERROR.Fatalln("No db.driver found in your config file")
	}
	dbDsn, found := Config.String("db.dsn")
	if !found {
		ERROR.Fatalln("No db.dsn found in your config file")
	}
	db, err = gorm.Open(dbDriver, dbDsn)
	if err != nil {
		ERROR.Fatalln("Database initialisation failed", err)
	}
	db.LogMode(Config.BoolDefault("db.debug", false))
	err = db.DB().Ping()
	if err != nil {
		ERROR.Fatalln(fmt.Sprintf("I could not access to database 'driver: %s, dns: %s - '", dbDriver, dbDsn), err)
	}
	if !dbIsOk() {
		var r []byte
		for {
			fmt.Print(fmt.Sprintf("Database 'driver: %s, dns: %s' misses some tables.\r\nShould i create them ? (y/n):", dbDriver, dbDsn))
			r, _, _ = bufio.NewReader(os.Stdin).ReadLine()
			if r[0] == 110 || r[0] == 121 {
				break
			}
		}
		if r[0] == 121 {
			if err = initDB(); err != nil {
				ERROR.Fatalln(err)
			}
		} else {
			INFO.Fatalln("See you soon...")
		}
	}

	// Synch tables to structs
	if err = autoMigrateDB(); err != nil {
		ERROR.Fatalln(err)
	}

	// DSN for SMTP server
	//var found bool
	strSmtpDsn, found := Config.String("smtp.dsn")
	if !found {
		INFO.Println("No smtp.dsn found in config file (tmail.conf). Listening on 0.0.0.0:25 with no SSL nor TLS extension")
		strSmtpDsn = "0.0.0.0:25:none"
	}
	// Are dsn OK ? We just validate entry, no check on IP/Port, they will be done with listen & serve
	smtpDsn, err = getDsnsFromString(strSmtpDsn)
	if err != nil {
		ERROR.Fatalln(err)
	}

	// Load plugins smtpIn_helo_01_monplugin

	// Init stores
	// queueStore
	switch Config.StringDefault("queue.store.type", "disk") {
	case "disk":
		queuePath, found := Config.String("queue.store.diskpath")
		if !found {
			queuePath = path.Join(distPath, "queue")
		}
		queueStore, err = store.NewDiskStore(queuePath)
		if err != nil {
			ERROR.Fatalln("Unable to get queueStore -", err)
		}
	}

	// Queue
	queue = &mailsQueue{}

	// init some globals
	countDeliveries = 0

	// Done
	INFO.Println("Init sequence done")

}
*/
<|endoftext|>


<commit_before>// Package trace defines common-use Dapper-style tracing APIs for the Go programming language.
//
// Package trace provides a backend-agnostic APIs and various tracing providers
// can be used with the package by importing various implementations of the Client interface.
package trace

import (
	"context"
	"errors"
	"net/http"
	"time"
)

var client Client

// TODO(jbd): annotate with labels that can propagate, similar to OpenTracing baggage.

// TODO(jbd): should we support a span to have multiple parents?

// TODO(jbd): set error/state on finish.

// TODO(jbd): A big TODO, we probably don't want to set a global client.
func Configure(c Client) {
	client = c
}

// Span represents a work.
type Span struct {
	ID          []byte            // represents the global identifier of the span.
	Annotations map[string][]byte // annotations set on this span.
}

// Annotate allows you to attach data to a span. Key-value pairs are
// arbitary information you want to collect in the lifetime of a span.
func (s *Span) Annotate(key string, val []byte) {
	s.Annotations[key] = val
}

// Child creates a child span from s with the given name.
// Created child span needs to be finished by calling
// the finishing function.
func (s *Span) Child(name string, linked ...*Span) (*Span, FinishFunc) {
	child := &Span{
		ID:          client.NewSpan(s.ID),
		Annotations: make(map[string][]byte),
	}
	start := time.Now()
	fn := func() error {
		return client.Finish(child.ID, name, spanIDs(linked), child.Annotations, start, time.Now())
	}
	return child, fn
}

// ToHTTPReq injects the span information in the given request
// and returns the modified request.
//
// If the current client is not supporting HTTP propagation,
// an error is returned.
func (s *Span) ToHTTPReq(req *http.Request) (*http.Request, error) {
	hc, ok := client.(HTTPCarrier)
	if !ok {
		return req, errors.New("not supported")
	}
	err := hc.ToHTTPReq(req, s.ID)
	if err != nil {
		return nil, err
	}
	return req, nil
}

// FromHTTPReq creates a *Span from an incoming request.
//
// An error will be returned if the current tracing client is
// not supporting propagation via HTTP.
func FromHTTPReq(req *http.Request) (*Span, error) {
	hc, ok := client.(HTTPCarrier)
	if !ok {
		return nil, errors.New("not supported")
	}
	id, err := hc.FromHTTPReq(req)
	if err != nil {
		return nil, err
	}
	return &Span{ID: id}, nil
}

// HTTPCarrier represents a mechanism that can attach the tracing
// information into an HTTP request or extract it from one.
type HTTPCarrier interface {
	FromHTTPReq(req *http.Request) (id []byte, err error)
	ToHTTPReq(req *http.Request, id []byte) error
}

// Client represents a client communicates with a tracing backend.
// Tracing backends are supposed to implement the interface in order to
// provide Go support.
//
// A Client is an HTTPCarrier if it can propagate the tracing
// information via an HTTP request.
//
// If you are not a tracing provider, you will never have to interact with
// this interface directly.
type Client interface {
	NewSpan(parent []byte) (id []byte)
	Finish(id []byte, name string, linked [][]byte, annotations map[string][]byte, start, end time.Time) error
}

// NewSpan creates a new root-level span.
//
// The span must be finished when the job it represents it is finished.
func NewSpan(name string, linked ...*Span) (*Span, FinishFunc) {
	span := &Span{
		ID:          client.NewSpan(nil),
		Annotations: make(map[string][]byte),
	}
	start := time.Now()
	fn := func() error {
		return client.Finish(span.ID, name, spanIDs(linked), span.Annotations, start, time.Now())
	}
	return span, fn
}

// NewContext returns a context with the span in.
func NewContext(ctx context.Context, span *Span) context.Context {
	return context.WithValue(ctx, spanKey, span)
}

// FromContext returns a span from the given context.
func FromContext(ctx context.Context) *Span {
	return ctx.Value(spanKey).(*Span)
}

func spanIDs(spans []*Span) [][]byte {
	var links [][]byte
	for _, s := range spans {
		links = append(links, s.ID)
	}
	return links
}

// FinishFunc finalizes its span.
type FinishFunc func() error

type contextKey struct{}

var spanKey = contextKey{}
<commit_msg>simplify HTTPCarrier methods<commit_after>// Package trace defines common-use Dapper-style tracing APIs for the Go programming language.
//
// Package trace provides a backend-agnostic APIs and various tracing providers
// can be used with the package by importing various implementations of the Client interface.
package trace

import (
	"context"
	"errors"
	"net/http"
	"time"
)

var client Client

// TODO(jbd): annotate with labels that can propagate, similar to OpenTracing baggage.

// TODO(jbd): should we support a span to have multiple parents?

// TODO(jbd): set error/state on finish.

// TODO(jbd): A big TODO, we probably don't want to set a global client.
func Configure(c Client) {
	client = c
}

// Span represents a work.
type Span struct {
	ID          []byte            // represents the global identifier of the span.
	Annotations map[string][]byte // annotations set on this span.
}

// Annotate allows you to attach data to a span. Key-value pairs are
// arbitary information you want to collect in the lifetime of a span.
func (s *Span) Annotate(key string, val []byte) {
	s.Annotations[key] = val
}

// Child creates a child span from s with the given name.
// Created child span needs to be finished by calling
// the finishing function.
func (s *Span) Child(name string, linked ...*Span) (*Span, FinishFunc) {
	child := &Span{
		ID:          client.NewSpan(s.ID),
		Annotations: make(map[string][]byte),
	}
	start := time.Now()
	fn := func() error {
		return client.Finish(child.ID, name, spanIDs(linked), child.Annotations, start, time.Now())
	}
	return child, fn
}

// ToHTTPReq injects the span information in the given request
// and returns the modified request.
//
// If the current client is not supporting HTTP propagation,
// an error is returned.
func (s *Span) ToHTTPReq(req *http.Request) (*http.Request, error) {
	hc, ok := client.(HTTPCarrier)
	if !ok {
		return req, errors.New("not supported")
	}
	err := hc.ToReq(req, s.ID)
	if err != nil {
		return nil, err
	}
	return req, nil
}

// FromHTTPReq creates a *Span from an incoming request.
//
// An error will be returned if the current tracing client is
// not supporting propagation via HTTP.
func FromHTTPReq(req *http.Request) (*Span, error) {
	hc, ok := client.(HTTPCarrier)
	if !ok {
		return nil, errors.New("not supported")
	}
	id, err := hc.FromReq(req)
	if err != nil {
		return nil, err
	}
	return &Span{ID: id}, nil
}

// HTTPCarrier represents a mechanism that can attach the tracing
// information into an HTTP request or extract it from one.
type HTTPCarrier interface {
	FromReq(req *http.Request) (id []byte, err error)
	ToReq(req *http.Request, id []byte) error
}

// Client represents a client communicates with a tracing backend.
// Tracing backends are supposed to implement the interface in order to
// provide Go support.
//
// A Client is an HTTPCarrier if it can propagate the tracing
// information via an HTTP request.
//
// If you are not a tracing provider, you will never have to interact with
// this interface directly.
type Client interface {
	NewSpan(parent []byte) (id []byte)
	Finish(id []byte, name string, linked [][]byte, annotations map[string][]byte, start, end time.Time) error
}

// NewSpan creates a new root-level span.
//
// The span must be finished when the job it represents it is finished.
func NewSpan(name string, linked ...*Span) (*Span, FinishFunc) {
	span := &Span{
		ID:          client.NewSpan(nil),
		Annotations: make(map[string][]byte),
	}
	start := time.Now()
	fn := func() error {
		return client.Finish(span.ID, name, spanIDs(linked), span.Annotations, start, time.Now())
	}
	return span, fn
}

// NewContext returns a context with the span in.
func NewContext(ctx context.Context, span *Span) context.Context {
	return context.WithValue(ctx, spanKey, span)
}

// FromContext returns a span from the given context.
func FromContext(ctx context.Context) *Span {
	return ctx.Value(spanKey).(*Span)
}

func spanIDs(spans []*Span) [][]byte {
	var links [][]byte
	for _, s := range spans {
		links = append(links, s.ID)
	}
	return links
}

// FinishFunc finalizes its span.
type FinishFunc func() error

type contextKey struct{}

var spanKey = contextKey{}
<|endoftext|>


<commit_before>package loggingWriter

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

	"github.com/Sirupsen/logrus"
)

type loggingWriter struct {
	w             http.ResponseWriter
	statusCode    int
	contentLength int
}

func NewLoggingWriter(w http.ResponseWriter) *loggingWriter {
	return &loggingWriter{w: w}
}

func (l *loggingWriter) Header() http.Header {
	return l.w.Header()
}

func (l *loggingWriter) Write(data []byte) (int, error) {
	l.contentLength += len(data)
	return l.w.Write(data)
}

func (l *loggingWriter) WriteHeader(status int) {
	l.statusCode = status
	l.w.WriteHeader(status)
}

func (l *loggingWriter) Length() int {
	return l.contentLength
}

func (l *loggingWriter) StatusCode() int {

	// if nobody set the status, but data has been written
	// then all must be well.
	if l.statusCode == 0 && l.contentLength > 0 {
		return http.StatusOK
	}

	return l.statusCode
}

// httpLogger provides per request log statements (ala Apache httpd)
func HttpApacheLogger(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		lw := NewLoggingWriter(w)
		defer func() {
			end := time.Now()
			duration := end.Sub(start)
			log.Printf("host: %s; uri: %s; CorrelationID: %s; remoteAddress: %s; method:  %s; proto: %s; status: %d, contentLength: %d, duration: %.3f; ua: %s",
				r.Host,
				r.RequestURI,
				lw.Header().Get("X-Correlation-ID"),
				r.RemoteAddr,
				r.Method,
				r.Proto,
				lw.StatusCode(),
				lw.Length(),
				duration.Seconds()*1000,
				r.UserAgent())
		}()

		h.ServeHTTP(lw, r)

	})
}

func getRequestURIFromRaw(rawURI string) string {
	if ! strings.Contains(rawURI, "?") {
		return rawURI
	}

	i := strings.Index(rawURI, "?")

	return rawURI[:i]
}

func HTTPLogrusLogger(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		lw := NewLoggingWriter(w)

		// save some values, in case the handler changes 'em
		host := r.Host
		url := getRequestURIFromRaw(r.RequestURI)
		remoteAddr := r.RemoteAddr
		method := r.Method
		proto := r.Proto

		defer func() {
			end := time.Now()
			duration := end.Sub(start)

			fields := logrus.Fields{}
			for key := range r.Header {
				fields[key] = r.Header.Get(key)
			}
			fields["Host"] = host
			fields["URL"] = url
			fields["remoteIP"] = remoteAddr
			fields["method"] = method
			fields["proto"] = proto
			fields["status"] = lw.StatusCode()
			fields["length"] = lw.Length()
			fields["duration"] = duration.Seconds() * 1000

			logrus.WithFields(fields).Info("")
		}()

		h.ServeHTTP(lw, r)

	})
}
<commit_msg>add time of receipt to log message<commit_after>package loggingWriter

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

	"github.com/Sirupsen/logrus"
)

type loggingWriter struct {
	w             http.ResponseWriter
	statusCode    int
	contentLength int
}

func NewLoggingWriter(w http.ResponseWriter) *loggingWriter {
	return &loggingWriter{w: w}
}

func (l *loggingWriter) Header() http.Header {
	return l.w.Header()
}

func (l *loggingWriter) Write(data []byte) (int, error) {
	l.contentLength += len(data)
	return l.w.Write(data)
}

func (l *loggingWriter) WriteHeader(status int) {
	l.statusCode = status
	l.w.WriteHeader(status)
}

func (l *loggingWriter) Length() int {
	return l.contentLength
}

func (l *loggingWriter) StatusCode() int {

	// if nobody set the status, but data has been written
	// then all must be well.
	if l.statusCode == 0 && l.contentLength > 0 {
		return http.StatusOK
	}

	return l.statusCode
}

// httpLogger provides per request log statements (ala Apache httpd)
func HttpApacheLogger(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		lw := NewLoggingWriter(w)
		defer func() {
			end := time.Now()
			duration := end.Sub(start)
			log.Printf("host: %s; uri: %s; CorrelationID: %s; remoteAddress: %s; method:  %s; proto: %s; status: %d, contentLength: %d, duration: %.3f; ua: %s",
				r.Host,
				r.RequestURI,
				lw.Header().Get("X-Correlation-ID"),
				r.RemoteAddr,
				r.Method,
				r.Proto,
				lw.StatusCode(),
				lw.Length(),
				duration.Seconds()*1000,
				r.UserAgent())
		}()

		h.ServeHTTP(lw, r)

	})
}

func getRequestURIFromRaw(rawURI string) string {
	if !strings.Contains(rawURI, "?") {
		return rawURI
	}

	i := strings.Index(rawURI, "?")

	return rawURI[:i]
}

func HTTPLogrusLogger(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		lw := NewLoggingWriter(w)

		// save some values, in case the handler changes 'em
		host := r.Host
		url := getRequestURIFromRaw(r.RequestURI)
		remoteAddr := r.RemoteAddr
		method := r.Method
		proto := r.Proto

		defer func() {
			fields := logrus.Fields{}
			for key := range r.Header {
				fields[key] = r.Header.Get(key)
			}
			fields["Host"] = host
			fields["URL"] = url
			fields["remoteIP"] = remoteAddr
			fields["method"] = method
			fields["proto"] = proto
			fields["status"] = lw.StatusCode()
			fields["length"] = lw.Length()

			end := time.Now()
			duration := end.Sub(start)

			fields["duration"] = duration.Seconds() * 1000
			fields["time"] = start.Format("20060102030405.000000")

			logrus.WithFields(fields).Info("")
		}()

		h.ServeHTTP(lw, r)

	})
}
<|endoftext|>


<commit_before>package edn

import (
	"reflect"
	"sync"
)

type Eq interface {
	Equal(other interface{}) bool
}

type Named interface {
	Ns() string
	Name() string
}

type Hasher interface {
	Hash() int32
	Hasheq(other Hasher) bool
}

type Counted interface {
	Count() int
}

type Seq interface {
	First() interface{}
	Next() Seq
	More() Seq
	Cons(interface{}) Seq
}

type Stack interface {
	Peek() interface{}
	Pop() Stack
}

type List interface {
	Seq
	Eq
	Stack
	Contains(o interface{}) bool
	//ContainsAll need to work out signature
	Index(o interface{}) int
	LastIndex(o interface{}) int
	EmptyP() bool
}

type Symbol struct {
	ns, name string
	hash int
	_str string // for caching the value of
}

func NewSym(ns, name string) Symbol {
	var str string
	if ns != "" {
		str = ns+"/"+name
	}else{
		str = name
	}
	return Symbol{ //todo: implement hashing
		ns:		ns,
		name:	name,
		_str:		str
	}
}

func (sym Symobl) String() string { return sym._str }
func (sym Symbol) Name() string { return sym.name }
func (sym Symbol) Ns() string { return sym.ns }

func (sym Symbol) Equal(other interface{}) bool {
	so, ok := other.(Symbol)
	return ok && so.Name() == sym.name && so.Ns() == sym.ns
}

// Keyword implementation
var (
	keyTable = make(map[Symbol]*Keyword) //cache of Keywords
	keyLock sync.Mutex // lock ^
)

type Keyword struct {
	Symbol
	hash int
	_str string
}

func NewKwd(ns, name string) *Keyword {
	keyLock.Lock()
	defer keyLock.Unlock()
	sym := NewSym(ns,name)
	k, ok := keyTable[sym]
	if !ok { // todo: implement hashing
		k = &Keyword{Symbol:sym,_str:":"+sym.String()}
	}
	return k
}

func (k *Keyword) String() string { return k._str }
func (k *Keyword) Equal(other interface{}) bool {
	ko, ok := other.(*Keyword)
	return ok && ko == k
	// reference equality deliberate: cached in keyTable
}

// Persistent List

type PList struct {
	first interface{}
	next *PList
	count int
}

func NewList1(fst interface{}) *PList {
	return &PList{first: fst, next: nil, count: 1}
}

func NewList(fst interface{}, rst *PList, count int) *PList {
	return &PList{first: fst, rest: rst, count: count}
}

var emptyList = NewList(nil,nil,0)

func (p *PList) Count() int {
	return p.count
}

func (p *PList) FIrst() interface{} {
	return p.first
}

func (p *PList) Next() Seq {
	if p.Count() == 1 { return nil }
	return p.next
}

func (p *PList) More() Seq {
	s := p.Next()
	if s == nil { s = emptyList }
	return s
}

func (p *PList) Cons(o interface{}) Seq {
	return NewList(o, p, p.count+1)
}

func (p *PList) Peek() interface{} { return p.first }

func (p *PList) Pop() Stack {
	if p.rest == nil {
		return emptyList
	}
	return p.rest
}

func (p *PList) Empty() *PList { return emptyList }

func (p *PList) EmptyP() bool {
	return p.first == nil && p.next == nil
}

func (p *PList) reify() []interface{} {
	np := p
	ret := make(<commit_msg>More progress on PList, still need to implement Equal and ContainsAll (which needs a signature).<commit_after>package edn

import (
	"reflect"
	"sync"
)

type Eq interface {
	Equal(other interface{}) bool
}

type Named interface {
	Ns() string
	Name() string
}

type Hasher interface {
	Hash() int32
	Hasheq(other Hasher) bool
}

type Counted interface {
	Count() int
}

type Seq interface {
	First() interface{}
	Next() Seq
	More() Seq
	Cons(interface{}) Seq
}

type Stack interface {
	Peek() interface{}
	Pop() Stack
}

type List interface {
	Seq
	// todo: formalize equality
	//Eq
	Stack
	Contains(o interface{}) bool
	// todo: work out func signature, maybe Coll inter?
	//ContainsAll
	Index(interface{}) (int, bool)
	LastIndex(interface{}) (int, bool)
	EmptyP() bool
	SubList(int, int) List
}

type Symbol struct {
	ns, name string
	hash int
	_str string // for caching the value of
}

func NewSym(ns, name string) Symbol {
	var str string
	if ns != "" {
		str = ns+"/"+name
	}else{
		str = name
	}
	return Symbol{ //todo: implement hashing
		ns:		ns,
		name:	name,
		_str:		str
	}
}

func (sym Symobl) String() string { return sym._str }
func (sym Symbol) Name() string { return sym.name }
func (sym Symbol) Ns() string { return sym.ns }

func (sym Symbol) Equal(other interface{}) bool {
	so, ok := other.(Symbol)
	return ok && so.Name() == sym.name && so.Ns() == sym.ns
}

// Keyword implementation
var (
	keyTable = make(map[Symbol]*Keyword) //cache of Keywords
	keyLock sync.Mutex // lock ^
)

type Keyword struct {
	Symbol
	hash int
	_str string
}

func NewKwd(ns, name string) *Keyword {
	keyLock.Lock()
	defer keyLock.Unlock()
	sym := NewSym(ns,name)
	k, ok := keyTable[sym]
	if !ok { // todo: implement hashing
		k = &Keyword{Symbol:sym,_str:":"+sym.String()}
	}
	return k
}

func (k *Keyword) String() string { return k._str }
func (k *Keyword) Equal(other interface{}) bool {
	ko, ok := other.(*Keyword)
	return ok && ko == k
	// reference equality deliberate: cached in keyTable
}

// Persistent List

type PList struct {
	first interface{}
	next *PList
	count int
}

func NewList1(fst interface{}) *PList {
	return &PList{first: fst, next: nil, count: 1}
}

func NewList(fst interface{}, rst *PList, count int) *PList {
	return &PList{first: fst, rest: rst, count: count}
}

var emptyList = NewList(nil,nil,0)

func (p *PList) Count() int {
	return p.count
}

func (p *PList) FIrst() interface{} {
	return p.first
}

func (p *PList) Next() Seq {
	if p.count == 1 { return nil }
	return p.next
}

func (p *PList) More() Seq {
	s := p.Next()
	if s == nil { s = emptyList }
	return s
}

func (p *PList) Cons(o interface{}) Seq {
	return NewList(o, p, p.count+1)
}

func (p *PList) Peek() interface{} { return p.first }

func (p *PList) Pop() Stack {
	if p.rest == nil {
		return emptyList
	}
	return p.rest
}

func (p *PList) Empty() *PList { return emptyList }

func (p *PList) EmptyP() bool {
	return p.first == nil && p.next == nil
}

func (p *PList) reify() []interface{} {
	np := p
	ret := make([]interface{}, np.count)
	for i := range ret {
		ret[i] = np.first
		np = np,Next()
		if np == nil {
			break
		}
	}
	return ret
}

func sliceToList(all ...interface{}) *PList {
	p := emptyList
	for _,v := range all {
		p = p.Cons(v)
	}
	return p
}

func (p *PList) SubList(from, to int) List {
	return sliceToList(p.reify()[from:to]...)
}

func (p *PList) Index(o interface{}) (int, bool) {
	np := p
	for i := 0; np != nil; i++ {
		// todo: *might* need to fix this to enable nesting
		if reflect.DeepEqual(np.first, o) {
			return i, true
		}
		np := np.Next()
	}
	return -1, false // <-- indicating not found
}

func (p *PList) LastIndex(o interface{}) (int, bool) {
	r := p.reify()
	for i := len(r); i > 0; i-- {
		// todo: ref Index ^
		if reflect.DeepEqual(r[i], o) {
			return i, true
		}
	}
	return -1, false
}

func (p *PList) Contains(o interface{}) bool {
	for np := p; np != nil; np = np.Next() {
		if reflect.DeepEqual(np.First(), o) {
			return true
		}
	}
	return false
}<|endoftext|>


<commit_before><commit_msg>Added ui module to support screen and keyboard.<commit_after><|endoftext|>


<commit_before><commit_msg>Introduce parseTime()<commit_after><|endoftext|>


<commit_before><commit_msg>Added NormalizeExpression() function<commit_after><|endoftext|>


<commit_before><commit_msg>Minor internal refactor<commit_after><|endoftext|>


<commit_before>package lib

import (
	"errors"
	"fmt"
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
)

const reminder_tag = remind_cmd
const said_tag = say_cmd
const chat_tag = chat_cmd

type (
	Note struct {
		WhoId       int
		AddedOn     time.Time
		RemindNext  time.Time
		CompletedOn time.Time
		Tag         string
		Text        string
	}

	Notes []*Note
)

func (this *Note) RemindToday() bool {
	today := time.Now()

	if this.RemindNext.Before(today) == false {
		return this.RemindNext.Year() == today.Year() &&
			this.RemindNext.YearDay() == today.YearDay()
	}
	return true
}

func (this *Note) IsReminder() bool {
	return strings.Contains(this.Tag, reminder_tag)
}

func (this *Note) IsCurrentReminder() bool {
	return this.IsReminder() && this.IsCurrent()
}

func (this *Note) IsCurrent() bool {
	return this.CompletedOn.IsZero()
}

func (this *Note) Complete() {
	this.CompletedOn = time.Now()
	return
}

func (this *Note) UpdateRemindNext() {
	today := time.Now()
	this.RemindNext = today.AddDate(0, 0, 7)
	return
}

func (this *Note) ToString() string {
	return fmt.Sprintf("On %s you said `%s`", this.AddedOn.Format("Mon Jan 2 03:04PM"), this.Text)
}

func (this Notes) FilterReminders() Notes {
	var r Notes
	for i := range this {
		if this[i].CompletedOn.IsZero() && strings.Contains(this[i].Tag, reminder_tag) {
			r = append(r, this[i])
		}
	}
	return r
}

func (this Notes) FilterNotes() Notes {
	var n Notes
	for i := range this {
		if this[i].CompletedOn.IsZero() && strings.Contains(this[i].Tag, said_tag) {
			n = append(n, this[i])
		}
	}
	return n
}

func (this Notes) FilterChat(topic string) *Note {
	for i := range this {
		if this[i].CompletedOn.IsZero() && strings.Contains(this[i].Tag, chat_tag) && strings.Contains(this[i].Tag, topic) {
			return this[i]
		}
	}

	return nil
}

func (this Notes) ForToday() Notes {
	var r Notes
	for i := range this {
		if this[i].RemindToday() {
			r = append(r, this[i])
		}
	}
	return r
}

func (this Notes) ForNextDays(days int) Notes {
	var r Notes
	t := time.Now()

	t.AddDate(0, 0, days)

	log.Println("getting all before ", t.Format(time.Stamp))

	for i := range this {
		if this[i].RemindNext.Before(t) {
			r = append(r, this[i])
		}
	}
	return r
}

func NewNote(msg telebot.Message, tags ...string) Note {

	txt := msg.Text

	//e.g. remove '/say' from the message
	if strings.Contains(txt, tags[0]) {
		txt = strings.TrimSpace(strings.Split(txt, tags[0])[1])
	}

	return Note{
		WhoId:      msg.Sender.ID,
		AddedOn:    msg.Time(),
		Text:       txt,
		Tag:        strings.Join(tags, ","),
		RemindNext: time.Now().AddDate(0, 0, 7)}
}

func NewReminderNote(msg telebot.Message) (Note, error) {

	const remind_pos, time_pos, msg_pos = 0, 1, 2
	words := strings.Fields(msg.Text)

	days, err := strconv.Atoi(words[time_pos])

	if err != nil {
		return Note{}, errors.New("format is " + remind_cmd_hint)
	}

	what := strings.SplitAfterN(msg.Text, words[time_pos], 2)[1]

	if what == "" {
		return Note{}, errors.New("format is " + remind_cmd_hint)
	}

	return Note{
		WhoId:      msg.Sender.ID,
		AddedOn:    msg.Time(),
		Text:       strings.TrimSpace(what),
		Tag:        reminder_tag,
		RemindNext: time.Now().AddDate(0, 0, days)}, nil
}
<commit_msg>formatting<commit_after>package lib

import (
	"errors"
	"fmt"
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
)

const reminder_tag = remind_cmd
const said_tag = say_cmd
const chat_tag = chat_cmd

type (
	Note struct {
		WhoId       int
		AddedOn     time.Time
		RemindNext  time.Time
		CompletedOn time.Time
		Tag         string
		Text        string
	}

	Notes []*Note
)

func (this *Note) RemindToday() bool {
	today := time.Now()

	if this.RemindNext.Before(today) == false {
		return this.RemindNext.Year() == today.Year() &&
			this.RemindNext.YearDay() == today.YearDay()
	}
	return true
}

func (this *Note) IsReminder() bool {
	return strings.Contains(this.Tag, reminder_tag)
}

func (this *Note) IsCurrentReminder() bool {
	return this.IsReminder() && this.IsCurrent()
}

func (this *Note) IsCurrent() bool {
	return this.CompletedOn.IsZero()
}

func (this *Note) Complete() {
	this.CompletedOn = time.Now()
	return
}

func (this *Note) UpdateRemindNext() {
	today := time.Now()
	this.RemindNext = today.AddDate(0, 0, 7)
	return
}

func (this *Note) ToString() string {
	return fmt.Sprintf("On %s you said '%s'", this.AddedOn.Format("Mon Jan 2 03:04pm"), this.Text)
}

func (this Notes) FilterReminders() Notes {
	var r Notes
	for i := range this {
		if this[i].CompletedOn.IsZero() && strings.Contains(this[i].Tag, reminder_tag) {
			r = append(r, this[i])
		}
	}
	return r
}

func (this Notes) FilterNotes() Notes {
	var n Notes
	for i := range this {
		if this[i].CompletedOn.IsZero() && strings.Contains(this[i].Tag, said_tag) {
			n = append(n, this[i])
		}
	}
	return n
}

func (this Notes) FilterChat(topic string) *Note {
	for i := range this {
		if this[i].CompletedOn.IsZero() && strings.Contains(this[i].Tag, chat_tag) && strings.Contains(this[i].Tag, topic) {
			return this[i]
		}
	}

	return nil
}

func (this Notes) ForToday() Notes {
	var r Notes
	for i := range this {
		if this[i].RemindToday() {
			r = append(r, this[i])
		}
	}
	return r
}

func (this Notes) ForNextDays(days int) Notes {
	var r Notes
	t := time.Now()

	t.AddDate(0, 0, days)

	log.Println("getting all before ", t.Format(time.Stamp))

	for i := range this {
		if this[i].RemindNext.Before(t) {
			r = append(r, this[i])
		}
	}
	return r
}

func NewNote(msg telebot.Message, tags ...string) Note {

	txt := msg.Text

	//e.g. remove '/say' from the message
	if strings.Contains(txt, tags[0]) {
		txt = strings.TrimSpace(strings.Split(txt, tags[0])[1])
	}

	return Note{
		WhoId:      msg.Sender.ID,
		AddedOn:    msg.Time(),
		Text:       txt,
		Tag:        strings.Join(tags, ","),
		RemindNext: time.Now().AddDate(0, 0, 7)}
}

func NewReminderNote(msg telebot.Message) (Note, error) {

	const remind_pos, time_pos, msg_pos = 0, 1, 2
	words := strings.Fields(msg.Text)

	days, err := strconv.Atoi(words[time_pos])

	if err != nil {
		return Note{}, errors.New("format is " + remind_cmd_hint)
	}

	what := strings.SplitAfterN(msg.Text, words[time_pos], 2)[1]

	if what == "" {
		return Note{}, errors.New("format is " + remind_cmd_hint)
	}

	return Note{
		WhoId:      msg.Sender.ID,
		AddedOn:    msg.Time(),
		Text:       strings.TrimSpace(what),
		Tag:        reminder_tag,
		RemindNext: time.Now().AddDate(0, 0, days)}, nil
}
<|endoftext|>


<commit_before>package gokalman

import (
	"errors"
	"fmt"
	"math"

	"github.com/gonum/matrix/mat64"
)

// NewHybridKF returns a new hybrid Kalman Filter which can be used both as a CKF and EKF.
// Warning: there is a failsafe preventing any update prior to updating the matrices.
// Usage:
// ```
/// kf.Prepare(Φ, Htilde)
//  estimate, err := kf.Update(realObs, computedObs)
/// ```
// Parameters:
// - x0: initial state estimate
// - P0: initial covariance symmetric matrix
// - noise: Noise
// - measSize: number of rows of the measurement vector (not actually important)
func NewHybridKF(x0 *mat64.Vector, P0 mat64.Symmetric, noise Noise, measSize int) (*HybridKF, *HybridKFEstimate, error) {
	// Let's check the dimensions of everything here to return an error ASAP.
	if err := checkMatDims(x0, P0, "x0", "Covar0", rows2cols); err != nil {
		return nil, nil, err
	}

	// Populate with the initial values.
	cr, _ := P0.Dims()
	predCovar := mat64.NewSymDense(cr, nil)
	est0 := HybridKFEstimate{x0, mat64.NewVector(measSize, nil), mat64.NewVector(measSize, nil), mat64.NewVector(measSize, nil), P0, predCovar, nil}
	return &HybridKF{nil, nil, nil, noise, est0, false, true, false, 0}, &est0, nil
}

// HybridKF defines a vanilla kalman filter. Use NewVanilla to initialize.
type HybridKF struct {
	Φ, Htilde, Γ *mat64.Dense
	Noise        Noise
	prevEst      HybridKFEstimate
	ekfMode      bool // Allows switching between CKF and EKF.
	locked       bool // Locks the KF to ensure Prepare is called.
	sncEnabled   bool // Stores whether we should enable or disable the state noise compensation.
	step         int
}

// EKFEnabled returns whether the KF is in EKF mode.
func (kf *HybridKF) EKFEnabled() bool {
	return kf.ekfMode
}

// EnableEKF switches this to an EKF mode.
func (kf *HybridKF) EnableEKF() {
	kf.ekfMode = true
}

// DisableEKF switches this back to a CKF mode.
func (kf *HybridKF) DisableEKF() {
	kf.ekfMode = false
}

func (kf *HybridKF) String() string {
	return fmt.Sprintf("HybridKF [k=%d]\n%s", kf.step, kf.Noise)
}

// SetNoise updates the Noise.
func (kf *HybridKF) SetNoise(n Noise) {
	kf.Noise = n
}

// GetNoise updates the F matrix.
func (kf *HybridKF) GetNoise() Noise {
	return kf.Noise
}

// Prepare unlocks the KF ready for the next Update call.
func (kf *HybridKF) Prepare(Φ, Htilde *mat64.Dense) {
	kf.Φ = Φ
	kf.Htilde = Htilde
	kf.locked = false
}

// PreparePNT prepares the process noise transition matrix and enabled the SNC
// for the next update. WARNING: If not called, the SNC *will not* be included.
func (kf *HybridKF) PreparePNT(Γ *mat64.Dense) {
	kf.Γ = Γ
	kf.sncEnabled = true
}

// Update implements the HybridKalmanFilter interface to compute an update.
// Will return an error if the KF is locked (call Prepare to unlock)
func (kf *HybridKF) Update(realObservation, computedObservation *mat64.Vector) (est *HybridKFEstimate, err error) {
	if kf.locked {
		return nil, errors.New("kf is locked (call Prepare() first)")
	}
	if err = checkMatDims(realObservation, computedObservation, "real observation", "computed observation", rowsAndcols); err != nil {
		return nil, err
	}

	// XXX: No future proofing so I'm not using the process noise just yet.
	// PBar
	var PBar, ΦP mat64.Dense
	ΦP.Mul(kf.Φ, kf.prevEst.Covariance())
	PBar.Mul(&ΦP, kf.Φ.T())
	if kf.sncEnabled {
		// Add the process noise
		var ΓQΓt, ΓQ mat64.Dense
		ΓQ.Mul(kf.Γ, kf.Noise.ProcessMatrix())
		ΓQΓt.Mul(&ΓQ, kf.Γ.T())
		PBar.Add(&PBar, &ΓQΓt)
	}

	// Kalman gain
	var PHt, HPHt, K mat64.Dense
	PHt.Mul(&PBar, kf.Htilde.T())
	HPHt.Mul(kf.Htilde, &PHt)
	HPHt.Add(&HPHt, kf.Noise.MeasurementMatrix())
	if ierr := HPHt.Inverse(&HPHt); ierr != nil {
		return nil, fmt.Errorf("could not invert `H*P_kp1_minus*H' + R` at k=%d: %s", kf.step, ierr)
	}
	K.Mul(&PHt, &HPHt)

	// Compute observation deviation y
	var y mat64.Vector
	y.SubVec(realObservation, computedObservation)

	var xBar mat64.Vector
	var innov, xHat mat64.Vector
	if kf.ekfMode {
		xHat.MulVec(&K, &y)
	} else {
		// Prediction step.
		xBar.MulVec(kf.Φ, kf.prevEst.State())
		// Measurement update
		var Hx mat64.Vector
		Hx.MulVec(kf.Htilde, &xBar) // Predicted measurement
		innov.SubVec(&y, &Hx)       // Innovation vector
		// XXX: Does not support scalar measurements.
		xHat.MulVec(&K, &innov)
		xHat.AddVec(&xBar, &xHat)
	}
	var P, Ptmp1, IKH, KR, KRKt mat64.Dense
	IKH.Mul(&K, kf.Htilde)
	n, _ := IKH.Dims()
	IKH.Sub(Identity(n), &IKH)
	Ptmp1.Mul(&IKH, &PBar)
	P.Mul(&Ptmp1, IKH.T())
	KR.Mul(&K, kf.Noise.MeasurementMatrix())
	KRKt.Mul(&KR, K.T())
	P.Add(&P, &KRKt)

	PBarSym, err := AsSymDense(&PBar)
	if err != nil {
		return nil, err
	}

	PSym, err := AsSymDense(&P)
	if err != nil {
		return nil, err
	}
	est = &HybridKFEstimate{&xHat, realObservation, &innov, &y, PSym, PBarSym, &K}
	kf.prevEst = *est
	kf.step++
	kf.locked = true
	return
}

// HybridKFEstimate is the output of each update state of the Vanilla KF.
// It implements the Estimate interface.
type HybridKFEstimate struct {
	state, meas, innov, Δobs *mat64.Vector
	covar, predCovar         mat64.Symmetric
	gain                     mat64.Matrix
}

// IsWithinNσ returns whether the estimation is within the 2σ bounds.
func (e HybridKFEstimate) IsWithinNσ(N float64) bool {
	for i := 0; i < e.state.Len(); i++ {
		nσ := N * math.Sqrt(e.covar.At(i, i))
		if e.state.At(i, 0) > nσ || e.state.At(i, 0) < -nσ {
			return false
		}
	}
	return true
}

// IsWithin2σ returns whether the estimation is within the 2σ bounds.
func (e HybridKFEstimate) IsWithin2σ() bool {
	return e.IsWithinNσ(2)
}

// State implements the Estimate interface.
func (e HybridKFEstimate) State() *mat64.Vector {
	return e.state
}

// Measurement implements the Estimate interface.
func (e HybridKFEstimate) Measurement() *mat64.Vector {
	return e.meas
}

// Innovation implements the Estimate interface.
func (e HybridKFEstimate) Innovation() *mat64.Vector {
	return e.innov
}

// ObservationDev returns the observation deviation.
func (e HybridKFEstimate) ObservationDev() *mat64.Vector {
	return e.Δobs
}

// Covariance implements the Estimate interface.
func (e HybridKFEstimate) Covariance() mat64.Symmetric {
	return e.covar
}

// PredCovariance implements the Estimate interface.
func (e HybridKFEstimate) PredCovariance() mat64.Symmetric {
	return e.predCovar
}

// Gain the Estimate interface.
func (e HybridKFEstimate) Gain() mat64.Matrix {
	return e.gain
}

func (e HybridKFEstimate) String() string {
	state := mat64.Formatted(e.State(), mat64.Prefix("  "))
	meas := mat64.Formatted(e.Measurement(), mat64.Prefix("  "))
	covar := mat64.Formatted(e.Covariance(), mat64.Prefix("  "))
	gain := mat64.Formatted(e.Gain(), mat64.Prefix("  "))
	innov := mat64.Formatted(e.Innovation(), mat64.Prefix("  "))
	predp := mat64.Formatted(e.PredCovariance(), mat64.Prefix("   "))
	return fmt.Sprintf("{\ns=%v\ny=%v\nP=%v\nK=%v\nP-=%v\ni=%v\n}", state, meas, covar, gain, predp, innov)
}
<commit_msg>Additional changes to Hybrid<commit_after>package gokalman

import (
	"errors"
	"fmt"
	"math"

	"github.com/gonum/matrix/mat64"
)

// NewHybridKF returns a new hybrid Kalman Filter which can be used both as a CKF and EKF.
// Warning: there is a failsafe preventing any update prior to updating the matrices.
// Usage:
// ```
/// kf.Prepare(Φ, Htilde)
//  estimate, err := kf.Update(realObs, computedObs)
/// ```
// Parameters:
// - x0: initial state estimate
// - P0: initial covariance symmetric matrix
// - noise: Noise
// - measSize: number of rows of the measurement vector (not actually important)
func NewHybridKF(x0 *mat64.Vector, P0 mat64.Symmetric, noise Noise, measSize int) (*HybridKF, *HybridKFEstimate, error) {
	// Let's check the dimensions of everything here to return an error ASAP.
	if err := checkMatDims(x0, P0, "x0", "Covar0", rows2cols); err != nil {
		return nil, nil, err
	}

	// Populate with the initial values.
	cr, _ := P0.Dims()
	predCovar := mat64.NewSymDense(cr, nil)
	est0 := HybridKFEstimate{x0, mat64.NewVector(measSize, nil), mat64.NewVector(measSize, nil), mat64.NewVector(measSize, nil), P0, predCovar, nil}
	return &HybridKF{nil, nil, nil, noise, est0, false, true, false, 0}, &est0, nil
}

// HybridKF defines a vanilla kalman filter. Use NewVanilla to initialize.
type HybridKF struct {
	Φ, Htilde, Γ *mat64.Dense
	Noise        Noise
	prevEst      HybridKFEstimate
	ekfMode      bool // Allows switching between CKF and EKF.
	locked       bool // Locks the KF to ensure Prepare is called.
	sncEnabled   bool // Stores whether we should enable or disable the state noise compensation.
	step         int
}

// EKFEnabled returns whether the KF is in EKF mode.
func (kf *HybridKF) EKFEnabled() bool {
	return kf.ekfMode
}

// EnableEKF switches this to an EKF mode.
func (kf *HybridKF) EnableEKF() {
	kf.ekfMode = true
}

// DisableEKF switches this back to a CKF mode.
func (kf *HybridKF) DisableEKF() {
	kf.ekfMode = false
}

func (kf *HybridKF) String() string {
	return fmt.Sprintf("HybridKF [k=%d]\n%s", kf.step, kf.Noise)
}

// SetNoise updates the Noise.
func (kf *HybridKF) SetNoise(n Noise) {
	kf.Noise = n
}

// GetNoise updates the F matrix.
func (kf *HybridKF) GetNoise() Noise {
	return kf.Noise
}

// Prepare unlocks the KF ready for the next Update call.
func (kf *HybridKF) Prepare(Φ, Htilde *mat64.Dense) {
	kf.Φ = Φ
	kf.Htilde = Htilde
	kf.locked = false
}

// PreparePNT prepares the process noise transition matrix and enabled the SNC
// for the next update. WARNING: If not called, the SNC *will not* be included.
func (kf *HybridKF) PreparePNT(Γ *mat64.Dense) {
	kf.Γ = Γ
	kf.sncEnabled = true
}

// Update implements the HybridKalmanFilter interface to compute an update.
// Will return an error if the KF is locked (call Prepare to unlock)
func (kf *HybridKF) Update(realObservation, computedObservation *mat64.Vector) (est *HybridKFEstimate, err error) {
	if kf.locked {
		return nil, errors.New("kf is locked (call Prepare() first)")
	}
	if err = checkMatDims(realObservation, computedObservation, "real observation", "computed observation", rowsAndcols); err != nil {
		return nil, err
	}

	// XXX: No future proofing so I'm not using the process noise just yet.
	// PBar
	var PBar, ΦP mat64.Dense
	ΦP.Mul(kf.Φ, kf.prevEst.Covariance())
	PBar.Mul(&ΦP, kf.Φ.T())
	if kf.sncEnabled {
		// Add the process noise
		var ΓQΓt, ΓQ mat64.Dense
		ΓQ.Mul(kf.Γ, kf.Noise.ProcessMatrix())
		ΓQΓt.Mul(&ΓQ, kf.Γ.T())
		PBar.Add(&PBar, &ΓQΓt)
	}

	// Kalman gain
	var PHt, HPHt, K mat64.Dense
	PHt.Mul(&PBar, kf.Htilde.T())
	HPHt.Mul(kf.Htilde, &PHt)
	HPHt.Add(&HPHt, kf.Noise.MeasurementMatrix())
	if ierr := HPHt.Inverse(&HPHt); ierr != nil {
		return nil, fmt.Errorf("could not invert `H*P_kp1_minus*H' + R` at k=%d: %s", kf.step, ierr)
	}
	K.Mul(&PHt, &HPHt)

	// Compute observation deviation y
	var y mat64.Vector
	y.SubVec(realObservation, computedObservation)

	var innov, xHat mat64.Vector
	if kf.ekfMode {
		xHat.MulVec(&K, &y)
	} else {
		// Prediction step.
		var xBar mat64.Vector
		xBar.MulVec(kf.Φ, kf.prevEst.State())
		// Measurement update
		var Hx mat64.Vector
		Hx.MulVec(kf.Htilde, &xBar) // Predicted measurement
		innov.SubVec(&y, &Hx)       // Innovation vector
		// XXX: Does not support scalar measurements.
		xHat.MulVec(&K, &innov)
		xHat.AddVec(&xBar, &xHat)
	}
	var P, Ptmp1, IKH, KR, KRKt mat64.Dense
	IKH.Mul(&K, kf.Htilde)
	n, _ := IKH.Dims()
	IKH.Sub(Identity(n), &IKH)
	Ptmp1.Mul(&IKH, &PBar)
	P.Mul(&Ptmp1, IKH.T())
	KR.Mul(&K, kf.Noise.MeasurementMatrix())
	KRKt.Mul(&KR, K.T())
	P.Add(&P, &KRKt)

	PBarSym, err := AsSymDense(&PBar)
	if err != nil {
		return nil, err
	}

	PSym, err := AsSymDense(&P)
	if err != nil {
		return nil, err
	}
	est = &HybridKFEstimate{&xHat, realObservation, &innov, &y, PSym, PBarSym, &K}
	kf.prevEst = *est
	kf.step++
	kf.sncEnabled = false
	kf.locked = true
	return
}

// HybridKFEstimate is the output of each update state of the Vanilla KF.
// It implements the Estimate interface.
type HybridKFEstimate struct {
	state, meas, innov, Δobs *mat64.Vector
	covar, predCovar         mat64.Symmetric
	gain                     mat64.Matrix
}

// IsWithinNσ returns whether the estimation is within the 2σ bounds.
func (e HybridKFEstimate) IsWithinNσ(N float64) bool {
	for i := 0; i < e.state.Len(); i++ {
		nσ := N * math.Sqrt(e.covar.At(i, i))
		if e.state.At(i, 0) > nσ || e.state.At(i, 0) < -nσ {
			return false
		}
	}
	return true
}

// IsWithin2σ returns whether the estimation is within the 2σ bounds.
func (e HybridKFEstimate) IsWithin2σ() bool {
	return e.IsWithinNσ(2)
}

// State implements the Estimate interface.
func (e HybridKFEstimate) State() *mat64.Vector {
	return e.state
}

// Measurement implements the Estimate interface.
func (e HybridKFEstimate) Measurement() *mat64.Vector {
	return e.meas
}

// Innovation implements the Estimate interface.
func (e HybridKFEstimate) Innovation() *mat64.Vector {
	return e.innov
}

// ObservationDev returns the observation deviation.
func (e HybridKFEstimate) ObservationDev() *mat64.Vector {
	return e.Δobs
}

// Covariance implements the Estimate interface.
func (e HybridKFEstimate) Covariance() mat64.Symmetric {
	return e.covar
}

// PredCovariance implements the Estimate interface.
func (e HybridKFEstimate) PredCovariance() mat64.Symmetric {
	return e.predCovar
}

// Gain the Estimate interface.
func (e HybridKFEstimate) Gain() mat64.Matrix {
	return e.gain
}

func (e HybridKFEstimate) String() string {
	state := mat64.Formatted(e.State(), mat64.Prefix("  "))
	meas := mat64.Formatted(e.Measurement(), mat64.Prefix("  "))
	covar := mat64.Formatted(e.Covariance(), mat64.Prefix("  "))
	gain := mat64.Formatted(e.Gain(), mat64.Prefix("  "))
	innov := mat64.Formatted(e.Innovation(), mat64.Prefix("  "))
	predp := mat64.Formatted(e.PredCovariance(), mat64.Prefix("   "))
	return fmt.Sprintf("{\ns=%v\ny=%v\nP=%v\nK=%v\nP-=%v\ni=%v\n}", state, meas, covar, gain, predp, innov)
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"strings"
	"time"

	"code.google.com/p/go.net/ipv6"
	"github.com/golang/glog"

	"./icmpv6"
)

func (s *Server) ListenAndServeICMPv6() {
	ipAddr := &net.IPAddr{IP: net.IPv6linklocalallrouters, Zone: "tap" + s.name}
	conn, err := net.ListenIP("ip6:58", ipAddr)
	if err != nil {
		glog.Errorf(err.Error())
		return
	}
	if err = bindToDevice(conn, "tap"+s.name); err != nil {
		glog.Errorf(err.Error())
		return
	}
	s.ipv6conn = ipv6.NewPacketConn(conn)

	if err = s.ipv6conn.SetControlMessage(ipv6.FlagDst, true); err != nil {
		glog.Warningf(err.Error())
		return
	}

	buffer := make([]byte, 1500)

	go s.Unsolicitated()

	for {
		//		s.RLock()
		if s.shutdown {
			//		s.RUnlock()
			return
		}
		//s.RUnlock()

		s.ipv6conn.SetReadDeadline(time.Now().Add(time.Second))
		_, _, _, err := s.ipv6conn.ReadFrom(buffer)
		if err != nil {
			continue
		}

		req := &icmpv6.ICMPv6{}
		err = req.Unmarshal(buffer)
		if err != nil {
			glog.Infof(err.Error())
			continue
		}

		if req.Type == uint8(ipv6.ICMPTypeRouterSolicitation) {
			s.sendRA()
		}
	}
}

func (s *Server) Unsolicitated() {
	ticker := time.NewTicker(9000 * time.Second)
	quit := make(chan struct{})

	time.Sleep(5 * time.Second)
	s.sendRA()

	for {
		select {
		case <-quit:
			ticker.Stop()
			return
		case <-ticker.C:
			s.sendRA()
			/*
				default:
					if s.shutdown {
						ticker.Stop()
						return
					}
			*/
		}
	}
}

func (s *Server) sendRA() {
	var srcIP net.IP
	var ipAddr *net.IPAddr

	iface, err := net.InterfaceByName("tap" + s.name)
	if err != nil {
		glog.Infof("can't find iface %s: %s\n", "tap"+s.name, err.Error())
		return
	}
	addrs, err := iface.Addrs()
	if err != nil {
		glog.Infof("can't get addresses from %s: %s\n", iface.Name, err.Error())
		return
	}

	for _, addr := range addrs {
		ip, ipnet, err := net.ParseCIDR(addr.String())
		_ = ipnet
		if err != nil {
			glog.Infof(err.Error())
			continue
		}
		if ip.To4() == nil && strings.HasPrefix(addr.String(), "fe80") {
			srcIP = ip
			ipAddr = &net.IPAddr{IP: ip, Zone: "tap" + s.name}
			break
		}
	}
	if ipAddr == nil || srcIP == nil {
		glog.Infof("ipv6 add missing for tap%s %s", s.name, srcIP)
		return
	}
	res, err := s.ServeICMPv6(srcIP, &icmpv6.ICMPv6{Type: uint8(ipv6.ICMPTypeRouterSolicitation)})
	if err != nil {
		glog.Infof(err.Error())
		return
	}

	for _, msg := range res {
		buf := make([]byte, msg.Len())
		buf, err = msg.Marshal()
		if err != nil {
			glog.Infof("%s err: %s", s.name, err.Error())
			continue
		}

		wcm := ipv6.ControlMessage{HopLimit: 255}
		wcm.Dst = net.IPv6linklocalallnodes
		wcm.IfIndex = iface.Index
		_, err = s.ipv6conn.WriteTo(buf, &wcm, ipAddr)
		if err != nil {
			glog.Infof("%s err: %s", s.name, err.Error())
			continue
		}
	}
}

func (s *Server) ServeICMPv6(src net.IP, req *icmpv6.ICMPv6) ([]*icmpv6.ICMPv6, error) {
	var res []*icmpv6.ICMPv6
	switch req.ICMPType() {
	case ipv6.ICMPTypeRouterSolicitation:
		for _, addr := range s.metadata.Network.IP {
			// TODO fix ipv6 addr
			if addr.Family == "ipv6" && addr.Host == "true" {
				ra := icmpv6.NewRouterAdvertisement(src, net.IPv6linklocalallnodes, addr.Address, addr.Prefix)
				buf, err := ra.Marshal()
				if err != nil {
					return nil, err
				}
				res = append(res, &icmpv6.ICMPv6{Type: ra.Type, Code: ra.Code, Checksum: ra.Checksum, Data: buf[4:]})
			}
		}
	}
	return res, nil
}
<commit_msg>fix ipv6 ra msg<commit_after>package main

import (
	"net"
	"strings"
	"time"

	"code.google.com/p/go.net/ipv6"
	"github.com/golang/glog"

	"./icmpv6"
)

func (s *Server) ListenAndServeICMPv6() {
	ipAddr := &net.IPAddr{IP: net.IPv6linklocalallrouters, Zone: "tap" + s.name}
	conn, err := net.ListenIP("ip6:58", ipAddr)
	if err != nil {
		glog.Errorf(err.Error())
		return
	}
	if err = bindToDevice(conn, "tap"+s.name); err != nil {
		glog.Errorf(err.Error())
		return
	}
	s.ipv6conn = ipv6.NewPacketConn(conn)

	if err = s.ipv6conn.SetControlMessage(ipv6.FlagDst, true); err != nil {
		glog.Warningf(err.Error())
		return
	}

	buffer := make([]byte, 1500)

	go s.Unsolicitated()

	for {
		//		s.RLock()
		if s.shutdown {
			//		s.RUnlock()
			return
		}
		//s.RUnlock()

		s.ipv6conn.SetReadDeadline(time.Now().Add(time.Second))
		_, _, src, err := s.ipv6conn.ReadFrom(buffer)
		if err != nil {
			continue
		}

		req := &icmpv6.ICMPv6{}
		err = req.Unmarshal(buffer)
		if err != nil {
			glog.Infof(err.Error())
			continue
		}
		if req.Type == uint8(ipv6.ICMPTypeRouterSolicitation) {
			s.sendRA(src)
		}
	}
}

func (s *Server) Unsolicitated() {
	ticker := time.NewTicker(10 * time.Second)
	quit := make(chan struct{})

	time.Sleep(5 * time.Second)
	s.sendRA(nil)

	for {
		select {
		case <-quit:
			ticker.Stop()
			return
		case <-ticker.C:
			s.sendRA(nil)
			/*
				default:
					if s.shutdown {
						ticker.Stop()
						return
					}
			*/
		}
	}
}

func (s *Server) sendRA(src net.Addr) {
	var srcIP net.IP
	var ipAddr net.Addr

	iface, err := net.InterfaceByName("tap" + s.name)
	if err != nil {
		glog.Infof("can't find iface %s: %s\n", "tap"+s.name, err.Error())
		return
	}
	addrs, err := iface.Addrs()
	if err != nil {
		glog.Infof("can't get addresses from %s: %s\n", iface.Name, err.Error())
		return
	}
	for _, addr := range addrs {
		ip, ipnet, err := net.ParseCIDR(addr.String())
		_ = ipnet
		if err != nil {
			glog.Infof(err.Error())
			continue
		}
		if ip.To4() == nil && strings.HasPrefix(addr.String(), "fe80") {
			srcIP = ip
			if src == nil {
				ipAddr = net.Addr(&net.IPAddr{IP: net.IPv6linklocalallnodes, Zone: "tap" + s.name})
			} else {
				ipAddr = src
			}
			break
		}
	}
	/*
		if src == nil {
			log.Printf("unsolicitated %+v\n", ipAddr)
		} else {
			log.Printf("solicitated %+v\n", ipAddr)
		}
	*/
	if ipAddr == nil || srcIP == nil {
		glog.Infof("ipv6 add missing for tap%s %s", s.name, srcIP)
		return
	}
	res, err := s.ServeICMPv6(srcIP, &icmpv6.ICMPv6{Type: uint8(ipv6.ICMPTypeRouterSolicitation)})
	if err != nil {
		glog.Infof(err.Error())
		return
	}

	for _, msg := range res {
		buf := make([]byte, msg.Len())
		buf, err = msg.Marshal()
		if err != nil {
			glog.Infof("%s err: %s", s.name, err.Error())
			continue
		}

		wcm := ipv6.ControlMessage{HopLimit: 255}
		wcm.Dst = net.IPv6linklocalallnodes
		wcm.IfIndex = iface.Index
		_, err = s.ipv6conn.WriteTo(buf, &wcm, ipAddr)
		if err != nil {
			glog.Infof("%s err: %s", s.name, err.Error())
			continue
		}
	}
}

func (s *Server) ServeICMPv6(src net.IP, req *icmpv6.ICMPv6) ([]*icmpv6.ICMPv6, error) {
	var res []*icmpv6.ICMPv6
	switch req.ICMPType() {
	case ipv6.ICMPTypeRouterSolicitation:
		for _, addr := range s.metadata.Network.IP {
			// TODO fix ipv6 addr
			if addr.Family == "ipv6" && addr.Host == "true" {
				ra := icmpv6.NewRouterAdvertisement(src, net.IPv6linklocalallnodes, addr.Address, addr.Prefix)
				buf, err := ra.Marshal()
				if err != nil {
					return nil, err
				}
				res = append(res, &icmpv6.ICMPv6{Type: ra.Type, Code: ra.Code, Checksum: ra.Checksum, Data: buf[4:]})
			}
		}
	}
	return res, nil
}
<|endoftext|>