func (s *Squish) SetLast(l uint32) {
	if l == 0 {
		l = 1
	}
	r := s.indexStructure[l-1].MessageNum
	buf := new(bytes.Buffer)
	err := binary.Write(buf, binary.LittleEndian, r)
	if err != nil {
		log.Print(err)
	}
	err = ioutil.WriteFile(s.AreaPath+".sql", buf.Bytes(), 0644)
	if err != nil {
		log.Print(err)
	}
}
SetLast set last message number
package msgapi
import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/askovpen/gossiped/pkg/types"
	"github.com/askovpen/gossiped/pkg/utils"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"strings"
	"time"
)
type SquishAttrs uint32
const (
	SquishPRIVATE SquishAttrs = 0x0001
	SquishCRASH   SquishAttrs = 0x0002
	SquishREAD    SquishAttrs = 0x0004
	SquishSENT    SquishAttrs = 0x0008
	SquishFILE    SquishAttrs = 0x0010
	SquishFWD     SquishAttrs = 0x0020
	SquishORPHAN  SquishAttrs = 0x0040
	SquishKILL    SquishAttrs = 0x0080
	SquishLOCAL   SquishAttrs = 0x0100
	SquishHOLD    SquishAttrs = 0x0200
	SquishXX2     SquishAttrs = 0x0400
	SquishFRQ     SquishAttrs = 0x0800
	SquishRRQ     SquishAttrs = 0x1000
	SquishCPT     SquishAttrs = 0x2000
	SquishARQ     SquishAttrs = 0x4000
	SquishURQ     SquishAttrs = 0x8000
	SquishSCANNED SquishAttrs = 0x00010000
	SquishUID     SquishAttrs = 0x00020000
	SquishSEEN    SquishAttrs = 0x00080000
)
type Squish struct {
	AreaPath       string
	AreaName       string
	AreaType       EchoAreaType
	Chrs           string
	indexStructure []sqiS
	messages       []MessageListItem
}
type sqiS struct {
	Offset     uint32
	MessageNum uint32
	CRC        uint32
}
type sqdS struct {
	Len, Rsvd1                                                uint16
	NumMsg, HighMsg, SkipMsg, HighWater, UID                  uint32
	Base                                                      [80]byte
	BeginFrame, LastFrame, FreeFrame, LastFreeFrame, EndFrame uint32
	MaxMsg                                                    uint32
	KeepDays                                                  uint16
	SzSQHdr                                                   uint16
	Rsvd2                                                     [124]byte
}
type sqdH struct {
	ID, NextFrame, PrevFrame, FrameLength, MsgLength, CLen uint32
	FrameType, Rsvd                                        uint16
	Attr                                                   uint32
	From, To                                               [36]byte
	Subject                                                [72]byte
	FromZone, FromNet, FromNode, FromPoint                 uint16
	ToZone, ToNet, ToNode, ToPoint                         uint16
	DateWritten, DateArrived                               uint32
	Utc                                                    uint16
	ReplyTo                                                uint32
	Replies                                                [9]uint32
	UMsgID                                                 uint32
	Date                                                   [20]byte
}
func (s *Squish) getAttrs(a uint32) (attrs []string) {
	datr := []string{
		"Pvt", "", "Rcv", "Snt",
		"", "Trs", "", "K/s",
		"Loc", "", "", "",
		"Rrq", "", "Arq", "",
		"Scn", "", "", "",
		"", "", "", "",
		"", "", "", "",
		"", "", "", "",
	}
	i := 0
	for a > 0 {
		if a&1 > 0 {
			if datr[i] != "" {
				attrs = append(attrs, datr[i])
			}
		}
		i++
		a >>= 1
	}
	return
}
func (s *Squish) getOffsetByNum(num uint32) (offset uint32) {
	for i, is := range s.indexStructure {
		if is.MessageNum == num {
			return uint32(i) + 1
		}
	}
	return 0
}
func readSQDH(headerb *bytes.Buffer) (sqdH, error) {
	var sqdh sqdH
	if err := utils.ReadStructFromBuffer(headerb, &sqdh); err != nil {
		return sqdh, err
	}
	if sqdh.ID != 0xafae4453 {
		return sqdh, fmt.Errorf("wrong Squish header %08x", sqdh.ID)
	}
	return sqdh, nil
}
func (s *Squish) GetMsg(position uint32) (*Message, error) {
	if len(s.indexStructure) == 0 {
		return nil, nil
	}
	if position == 0 {
		position = 1
	}
	f, err := os.Open(s.AreaPath + ".sqd")
	if err != nil {
		return nil, err
	}
	defer f.Close()
	f.Seek(int64(s.indexStructure[position-1].Offset), 0)
	header := make([]byte, 266)
	f.Read(header)
	headerb := bytes.NewBuffer(header)
	sqdh, err := readSQDH(headerb)
	if err != nil {
		return nil, err
	}
	body := make([]byte, sqdh.MsgLength+28-266)
	f.Read(body)
	toHash := bufHash32(string(sqdh.To[:]))
	if sqdh.Attr&uint32(SquishREAD) > 0 {
		toHash |= 0x80000000
	}
	rm := &Message{Area: s.AreaName, MsgNum: position}
	if s.indexStructure[position-1].CRC != toHash {
		rm.Corrupted = true
	}
	rm.From = strings.Trim(string(sqdh.From[:]), "\x00")
	rm.To = strings.Trim(string(sqdh.To[:]), "\x00")
	rm.FromAddr = types.AddrFromNum(sqdh.FromZone, sqdh.FromNet, sqdh.FromNode, sqdh.FromPoint)
	if s.AreaType != EchoAreaTypeLocal && s.AreaType != EchoAreaTypeEcho {
		rm.ToAddr = types.AddrFromNum(sqdh.ToZone, sqdh.ToNet, sqdh.ToNode, sqdh.ToPoint)
	}
	rm.Subject = strings.Trim(string(sqdh.Subject[:]), "\x00")
	rm.Attrs = s.getAttrs(sqdh.Attr)
	rm.Body = string(body)
	rm.DateWritten = getTime(sqdh.DateWritten)
	rm.DateArrived = getTime(sqdh.DateArrived)
	if sqdh.ReplyTo > 0 {
		rm.ReplyTo = s.getOffsetByNum(sqdh.ReplyTo)  
	} else {
		rm.ReplyTo = 0
	}
	for _, reply := range sqdh.Replies {
		if reply > 0 {
			rm.Replies = append(rm.Replies, s.getOffsetByNum(reply))
		}
	}
	if sqdh.CLen > 0 {
		kla := strings.Split(rm.Body[1:sqdh.CLen], "\x01")
		for i := range kla {
			kla[i] = strings.Trim(kla[i], "\x00")
		}
		rm.Body = "\x01" + strings.Join(kla, "\x0d\x01") + "\x0d" + rm.Body[sqdh.CLen:]
	}
	if strings.Contains(rm.Body, "\x00") {
		rm.Body = rm.Body[0:strings.Index(rm.Body, "\x00")]
	}
	err = rm.ParseRaw()
	if err != nil {
		return nil, err
	}
	return rm, nil
}
func (s *Squish) readSQI() {
	if len(s.indexStructure) > 0 {
		return
	}
	file, err := os.Open(s.AreaPath + ".sqi")
	if err != nil {
		return
	}
	reader := bufio.NewReader(file)
	part := make([]byte, 12288)
	for {
		count, err := reader.Read(part)
		if err != nil {
			break
		}
		partb := bytes.NewBuffer(part[:count])
		for {
			var sqi sqiS
			if err = utils.ReadStructFromBuffer(partb, &sqi); err != nil {
				break
			}
			if sqi.Offset != 0 {
				s.indexStructure = append(s.indexStructure, sqi)
			}
		}
	}
	sort.Slice(s.indexStructure, func(i, j int) bool { return s.indexStructure[i].MessageNum < s.indexStructure[j].MessageNum })
}
func (s *Squish) GetLast() uint32 {
	s.readSQI()
	if len(s.indexStructure) == 0 {
		return 0
	}
	file, err := os.Open(s.AreaPath + ".sql")
	if err != nil {
		return 0
	}
	defer file.Close()
	var ret uint32
	err = binary.Read(file, binary.LittleEndian, &ret)
	if err != nil {
		return 0
	}
	for i, is := range s.indexStructure {
		if ret == is.MessageNum {
			return uint32(i + 1)
		}
	}
	if ret != 0 {
		return uint32(len(s.indexStructure))
	}
	return 0
}
func (s *Squish) GetCount() uint32 {
	s.readSQI()
	return uint32(len(s.indexStructure))
}
func (s *Squish) GetMsgType() EchoAreaMsgType {
	return EchoAreaMsgTypeSquish
}
func (s *Squish) GetType() EchoAreaType {
	return s.AreaType
}
func (s *Squish) Init() {
}
func (s *Squish) GetName() string {
	return s.AreaName
}
func getTime(t uint32) time.Time {
	return time.Date(
		int(t>>9&127)+1980,
		time.Month(int(t>>5&15)),
		int(t&31),
		int(t>>27&31),
		int(t>>21&63),
		int(t>>16&31)*2,
		0,
		time.Local)
}
func setTime(t time.Time) (rt uint32) {
	rt = 0
	rt |= uint32(t.Day() & 31)
	rt |= uint32(t.Month() & 15 << 5)
	rt |= uint32((t.Year() - 1980) & 127 << 9)
	rt |= uint32((t.Second() / 2) & 31 << 16)
	rt |= uint32(t.Minute() & 63 << 21)
	rt |= uint32(t.Hour() & 31 << 27)
	return
}
func bufHash32(str string) (h uint32) {
	strb := []byte(str)
	h = 0
	for i := range strb {
		if strb[i] == 0 {
			continue
		}
		strb[i] = tolower(strb[i])
		h = (h << 4) + uint32(strb[i])
		g := h & 0xF0000000
		if g != 0 {
			h |= g >> 24
			h |= g
		}
	}
	h &= 0x7fffffff
	return
}