218 lines
5.2 KiB
Go
218 lines
5.2 KiB
Go
package packets
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"io"
|
|
"strings"
|
|
|
|
"badat.dev/maeqtt/v2/mqtt/properties"
|
|
"badat.dev/maeqtt/v2/mqtt/types"
|
|
)
|
|
|
|
type Topic struct {
|
|
Fields []string
|
|
}
|
|
|
|
var multiLevelWildcardNotLast = errors.New("Multi level wildcard isn't the field in a topic")
|
|
func parseTopic(topic_name string) (Topic, error) {
|
|
topic := Topic{}
|
|
fields := strings.Split(topic_name, "/")
|
|
for i, field := range fields {
|
|
if field == "#" && len(fields) > i+1 {
|
|
return topic, multiLevelWildcardNotLast
|
|
}
|
|
}
|
|
topic.Fields = fields
|
|
|
|
return topic, nil
|
|
}
|
|
|
|
type TopicFilter struct {
|
|
Topic Topic
|
|
MaxQoS uint
|
|
NoLocal bool
|
|
RetainAsPublished bool
|
|
RetainHandling uint
|
|
}
|
|
|
|
func parseTopicFilter(r *bufio.Reader) (TopicFilter, error) {
|
|
filter := TopicFilter{}
|
|
var err error
|
|
|
|
topic_str, err := types.DecodeUTF8String(r)
|
|
if err != nil {
|
|
return filter, err
|
|
}
|
|
|
|
filter.Topic, err = parseTopic(topic_str)
|
|
if err != nil {
|
|
return filter, err
|
|
}
|
|
|
|
options, err := types.DecodeBits(r)
|
|
if err != nil {
|
|
return filter, err
|
|
}
|
|
filter.MaxQoS = types.BoolsToUint(options[0], options[1])
|
|
filter.NoLocal = options[2]
|
|
filter.RetainAsPublished = options[3]
|
|
filter.RetainHandling = types.BoolsToUint(options[4], options[5])
|
|
return filter, nil
|
|
}
|
|
|
|
// Both sub and unsubscribe packets are identitcal so we can reuse the parsing logic
|
|
type SubscriptionPacket struct {
|
|
PacketId uint16
|
|
TopicFilters []TopicFilter
|
|
}
|
|
|
|
func parseSubscriptionPacket(control controlPacket, props []properties.Property) (SubscriptionPacket, error) {
|
|
var err error
|
|
r := bufio.NewReader(control.reader)
|
|
packet := SubscriptionPacket{}
|
|
|
|
if control.flags != 2 {
|
|
return packet, errors.New("Malformed subscription packet")
|
|
}
|
|
|
|
packet.PacketId, err = types.DecodeUint16(r)
|
|
if err != nil {
|
|
return packet, err
|
|
}
|
|
|
|
err = properties.ParseProperties(r, props)
|
|
if err != nil {
|
|
return packet, err
|
|
}
|
|
|
|
for err != io.EOF {
|
|
filter, err := parseTopicFilter(r)
|
|
packet.TopicFilters = append(packet.TopicFilters, filter)
|
|
if err != nil {
|
|
return packet, err
|
|
}
|
|
_, err = r.Peek(1)
|
|
if err != nil && err != io.EOF {
|
|
return packet, err
|
|
}
|
|
if err == io.EOF {
|
|
return packet, nil
|
|
}
|
|
}
|
|
println("A")
|
|
|
|
return packet, nil
|
|
}
|
|
|
|
type SubscribePacket struct {
|
|
*SubscriptionPacket
|
|
props properties.SubscribePacketProperties
|
|
}
|
|
|
|
/// CURRENTLY BROKEN
|
|
|
|
// TODO FIXME AAAAA
|
|
func parseSubscribePacket(control controlPacket) (SubscribePacket, error) {
|
|
if control.packetType != PacketTypeSubscribe {
|
|
panic("Wrong packet type for parseSubscribePacket")
|
|
}
|
|
|
|
pack := SubscribePacket{}
|
|
subscriptionPack, err := parseSubscriptionPacket(control, pack.props.ArrayOf())
|
|
if err != nil {
|
|
return pack, err
|
|
}
|
|
pack.SubscriptionPacket = &subscriptionPack
|
|
return pack, nil
|
|
}
|
|
|
|
func (p SubscribePacket) Visit(v PacketVisitor) {
|
|
v.VisitSubscribe(p)
|
|
}
|
|
|
|
type SubackReasonCode byte
|
|
|
|
const (
|
|
SubackReasonGrantedQoSZero PubackReasonCode = 0
|
|
SubackReasonGrantedQoSOne = 1
|
|
SubackReasonGrantedQoSTwo = 2
|
|
SubackReasonUnspecified = 128
|
|
SubackReasonImplSpecificError = 131
|
|
SubackReasonNotAuthorized = 135
|
|
SubackReasonTopicFilterInvalid = 143
|
|
SubackReasonPacketIDInUse = 145
|
|
SubackReasonQuotaExceeded = 151
|
|
SubackReasonSharedSubNotSupported = 151
|
|
SubackReasonSubIDUnsupported = 151
|
|
SubackReasonWildcardSubUnsupported = 151
|
|
)
|
|
|
|
type SubAckPacket struct {
|
|
PacketID uint16
|
|
Properties properties.SubackPacketProperties
|
|
Reason SubackReasonCode
|
|
}
|
|
|
|
|
|
func (p SubAckPacket) Write(w io.Writer) error {
|
|
resp := pubRespPacket{
|
|
PacketType: PacketTypeSuback,
|
|
PacketID: p.PacketID,
|
|
Properties: p.Properties.ArrayOf(),
|
|
Reason: byte(p.Reason),
|
|
}
|
|
return resp.Write(w)
|
|
}
|
|
|
|
type UnsubscribePacket struct {
|
|
*SubscriptionPacket
|
|
props properties.UnsubscribePacketProperties
|
|
}
|
|
|
|
func parseUnsubscribePacket(control controlPacket) (UnsubscribePacket, error) {
|
|
if control.packetType != PacketTypeUnsubscribe {
|
|
panic("Wrong packet type for parseSubscribePacket")
|
|
}
|
|
|
|
pack := UnsubscribePacket{}
|
|
subscriptionPack, err := parseSubscriptionPacket(control, pack.props.ArrayOf())
|
|
if err != nil {
|
|
return pack, err
|
|
}
|
|
pack.PacketId = subscriptionPack.PacketId
|
|
pack.TopicFilters = subscriptionPack.TopicFilters
|
|
return pack, nil
|
|
}
|
|
|
|
func (p UnsubscribePacket) Visit(v PacketVisitor) {
|
|
v.VisitUnsubscribe(p)
|
|
}
|
|
|
|
type UnsubackReasonCode byte
|
|
|
|
const (
|
|
UnsubackReasonSuccess PubackReasonCode = 0
|
|
UnSubackReasonUnspecified = 128
|
|
UnSubackReasonImplSpecificError = 131
|
|
UnSubackReasonNotAuthorized = 135
|
|
UnSubackReasonTopicFilterInvalid = 143
|
|
UnSubackReasonPacketIDInUse = 145
|
|
)
|
|
|
|
type UnsubAckPacket struct {
|
|
PacketID uint16
|
|
Properties properties.UnsubackPacketProperties
|
|
Reason UnsubackReasonCode
|
|
}
|
|
|
|
|
|
func (p UnsubAckPacket) Write(w io.Writer) error {
|
|
resp := pubRespPacket{
|
|
PacketType: PacketTypeUnsuback,
|
|
PacketID: p.PacketID,
|
|
Properties: p.Properties.ArrayOf(),
|
|
Reason: byte(p.Reason),
|
|
}
|
|
return resp.Write(w)
|
|
}
|