| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 | package libboximport (	"bufio"	"bytes"	"compress/gzip"	"encoding/binary"	E "github.com/sagernet/sing/common/exceptions"	"github.com/sagernet/sing/common/varbin")func EncodeChunkedMessage(data []byte) []byte {	var buffer bytes.Buffer	binary.Write(&buffer, binary.BigEndian, uint16(len(data)))	buffer.Write(data)	return buffer.Bytes()}func DecodeLengthChunk(data []byte) int32 {	return int32(binary.BigEndian.Uint16(data))}const (	MessageTypeError = iota	MessageTypeProfileList	MessageTypeProfileContentRequest	MessageTypeProfileContent)type ErrorMessage struct {	Message string}func (e *ErrorMessage) Encode() []byte {	var buffer bytes.Buffer	buffer.WriteByte(MessageTypeError)	varbin.Write(&buffer, binary.BigEndian, e.Message)	return buffer.Bytes()}func DecodeErrorMessage(data []byte) (*ErrorMessage, error) {	reader := bytes.NewReader(data)	messageType, err := reader.ReadByte()	if err != nil {		return nil, err	}	if messageType != MessageTypeError {		return nil, E.New("invalid message")	}	var message ErrorMessage	message.Message, err = varbin.ReadValue[string](reader, binary.BigEndian)	if err != nil {		return nil, err	}	return &message, nil}const (	ProfileTypeLocal int32 = iota	ProfileTypeiCloud	ProfileTypeRemote)type ProfilePreview struct {	ProfileID int64	Name      string	Type      int32}type ProfilePreviewIterator interface {	Next() *ProfilePreview	HasNext() bool}type ProfileEncoder struct {	profiles []ProfilePreview}func (e *ProfileEncoder) Append(profile *ProfilePreview) {	e.profiles = append(e.profiles, *profile)}func (e *ProfileEncoder) Encode() []byte {	var buffer bytes.Buffer	buffer.WriteByte(MessageTypeProfileList)	binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles)))	for _, preview := range e.profiles {		binary.Write(&buffer, binary.BigEndian, preview.ProfileID)		varbin.Write(&buffer, binary.BigEndian, preview.Name)		binary.Write(&buffer, binary.BigEndian, preview.Type)	}	return buffer.Bytes()}type ProfileDecoder struct {	profiles []*ProfilePreview}func (d *ProfileDecoder) Decode(data []byte) error {	reader := bytes.NewReader(data)	messageType, err := reader.ReadByte()	if err != nil {		return err	}	if messageType != MessageTypeProfileList {		return E.New("invalid message")	}	var profileCount uint16	err = binary.Read(reader, binary.BigEndian, &profileCount)	if err != nil {		return err	}	for i := 0; i < int(profileCount); i++ {		var profile ProfilePreview		err = binary.Read(reader, binary.BigEndian, &profile.ProfileID)		if err != nil {			return err		}		profile.Name, err = varbin.ReadValue[string](reader, binary.BigEndian)		if err != nil {			return err		}		err = binary.Read(reader, binary.BigEndian, &profile.Type)		if err != nil {			return err		}		d.profiles = append(d.profiles, &profile)	}	return nil}func (d *ProfileDecoder) Iterator() ProfilePreviewIterator {	return newIterator(d.profiles)}type ProfileContentRequest struct {	ProfileID int64}func (r *ProfileContentRequest) Encode() []byte {	var buffer bytes.Buffer	buffer.WriteByte(MessageTypeProfileContentRequest)	binary.Write(&buffer, binary.BigEndian, r.ProfileID)	return buffer.Bytes()}func DecodeProfileContentRequest(data []byte) (*ProfileContentRequest, error) {	reader := bytes.NewReader(data)	messageType, err := reader.ReadByte()	if err != nil {		return nil, err	}	if messageType != MessageTypeProfileContentRequest {		return nil, E.New("invalid message")	}	var request ProfileContentRequest	err = binary.Read(reader, binary.BigEndian, &request.ProfileID)	if err != nil {		return nil, err	}	return &request, nil}type ProfileContent struct {	Name               string	Type               int32	Config             string	RemotePath         string	AutoUpdate         bool	AutoUpdateInterval int32	LastUpdated        int64}func (c *ProfileContent) Encode() []byte {	buffer := new(bytes.Buffer)	buffer.WriteByte(MessageTypeProfileContent)	buffer.WriteByte(1)	gWriter := gzip.NewWriter(buffer)	writer := bufio.NewWriter(gWriter)	varbin.Write(writer, binary.BigEndian, c.Name)	binary.Write(writer, binary.BigEndian, c.Type)	varbin.Write(writer, binary.BigEndian, c.Config)	if c.Type != ProfileTypeLocal {		varbin.Write(writer, binary.BigEndian, c.RemotePath)	}	if c.Type == ProfileTypeRemote {		binary.Write(writer, binary.BigEndian, c.AutoUpdate)		binary.Write(writer, binary.BigEndian, c.AutoUpdateInterval)		binary.Write(writer, binary.BigEndian, c.LastUpdated)	}	writer.Flush()	gWriter.Flush()	gWriter.Close()	return buffer.Bytes()}func DecodeProfileContent(data []byte) (*ProfileContent, error) {	reader := bytes.NewReader(data)	messageType, err := reader.ReadByte()	if err != nil {		return nil, err	}	if messageType != MessageTypeProfileContent {		return nil, E.New("invalid message")	}	version, err := reader.ReadByte()	if err != nil {		return nil, err	}	gReader, err := gzip.NewReader(reader)	if err != nil {		return nil, E.Cause(err, "unsupported profile")	}	bReader := varbin.StubReader(gReader)	var content ProfileContent	content.Name, err = varbin.ReadValue[string](bReader, binary.BigEndian)	if err != nil {		return nil, err	}	err = binary.Read(bReader, binary.BigEndian, &content.Type)	if err != nil {		return nil, err	}	content.Config, err = varbin.ReadValue[string](bReader, binary.BigEndian)	if err != nil {		return nil, err	}	if content.Type != ProfileTypeLocal {		content.RemotePath, err = varbin.ReadValue[string](bReader, binary.BigEndian)		if err != nil {			return nil, err		}	}	if content.Type == ProfileTypeRemote || (version == 0 && content.Type != ProfileTypeLocal) {		err = binary.Read(bReader, binary.BigEndian, &content.AutoUpdate)		if err != nil {			return nil, err		}		if version >= 1 {			err = binary.Read(bReader, binary.BigEndian, &content.AutoUpdateInterval)			if err != nil {				return nil, err			}		}		err = binary.Read(bReader, binary.BigEndian, &content.LastUpdated)		if err != nil {			return nil, err		}	}	return &content, nil}
 |