1package packets
2
3import (
4	"bytes"
5	"fmt"
6	"io"
7)
8
9//ConnectPacket is an internal representation of the fields of the
10//Connect MQTT packet
11type ConnectPacket struct {
12	FixedHeader
13	ProtocolName    string
14	ProtocolVersion byte
15	CleanSession    bool
16	WillFlag        bool
17	WillQos         byte
18	WillRetain      bool
19	UsernameFlag    bool
20	PasswordFlag    bool
21	ReservedBit     byte
22	Keepalive       uint16
23
24	ClientIdentifier string
25	WillTopic        string
26	WillMessage      []byte
27	Username         string
28	Password         []byte
29}
30
31func (c *ConnectPacket) String() string {
32	str := fmt.Sprintf("%s\n", c.FixedHeader)
33	str += fmt.Sprintf("protocolversion: %d protocolname: %s cleansession: %t willflag: %t WillQos: %d WillRetain: %t Usernameflag: %t Passwordflag: %t keepalive: %d\nclientId: %s\nwilltopic: %s\nwillmessage: %s\nUsername: %s\nPassword: %s\n", c.ProtocolVersion, c.ProtocolName, c.CleanSession, c.WillFlag, c.WillQos, c.WillRetain, c.UsernameFlag, c.PasswordFlag, c.Keepalive, c.ClientIdentifier, c.WillTopic, c.WillMessage, c.Username, c.Password)
34	return str
35}
36
37func (c *ConnectPacket) Write(w io.Writer) error {
38	var body bytes.Buffer
39	var err error
40
41	body.Write(encodeString(c.ProtocolName))
42	body.WriteByte(c.ProtocolVersion)
43	body.WriteByte(boolToByte(c.CleanSession)<<1 | boolToByte(c.WillFlag)<<2 | c.WillQos<<3 | boolToByte(c.WillRetain)<<5 | boolToByte(c.PasswordFlag)<<6 | boolToByte(c.UsernameFlag)<<7)
44	body.Write(encodeUint16(c.Keepalive))
45	body.Write(encodeString(c.ClientIdentifier))
46	if c.WillFlag {
47		body.Write(encodeString(c.WillTopic))
48		body.Write(encodeBytes(c.WillMessage))
49	}
50	if c.UsernameFlag {
51		body.Write(encodeString(c.Username))
52	}
53	if c.PasswordFlag {
54		body.Write(encodeBytes(c.Password))
55	}
56	c.FixedHeader.RemainingLength = body.Len()
57	packet := c.FixedHeader.pack()
58	packet.Write(body.Bytes())
59	_, err = packet.WriteTo(w)
60
61	return err
62}
63
64//Unpack decodes the details of a ControlPacket after the fixed
65//header has been read
66func (c *ConnectPacket) Unpack(b io.Reader) error {
67	c.ProtocolName = decodeString(b)
68	c.ProtocolVersion = decodeByte(b)
69	options := decodeByte(b)
70	c.ReservedBit = 1 & options
71	c.CleanSession = 1&(options>>1) > 0
72	c.WillFlag = 1&(options>>2) > 0
73	c.WillQos = 3 & (options >> 3)
74	c.WillRetain = 1&(options>>5) > 0
75	c.PasswordFlag = 1&(options>>6) > 0
76	c.UsernameFlag = 1&(options>>7) > 0
77	c.Keepalive = decodeUint16(b)
78	c.ClientIdentifier = decodeString(b)
79	if c.WillFlag {
80		c.WillTopic = decodeString(b)
81		c.WillMessage = decodeBytes(b)
82	}
83	if c.UsernameFlag {
84		c.Username = decodeString(b)
85	}
86	if c.PasswordFlag {
87		c.Password = decodeBytes(b)
88	}
89
90	return nil
91}
92
93//Validate performs validation of the fields of a Connect packet
94func (c *ConnectPacket) Validate() byte {
95	if c.PasswordFlag && !c.UsernameFlag {
96		return ErrRefusedBadUsernameOrPassword
97	}
98	if c.ReservedBit != 0 {
99		//Bad reserved bit
100		return ErrProtocolViolation
101	}
102	if (c.ProtocolName == "MQIsdp" && c.ProtocolVersion != 3) || (c.ProtocolName == "MQTT" && c.ProtocolVersion != 4) {
103		//Mismatched or unsupported protocol version
104		return ErrRefusedBadProtocolVersion
105	}
106	if c.ProtocolName != "MQIsdp" && c.ProtocolName != "MQTT" {
107		//Bad protocol name
108		return ErrProtocolViolation
109	}
110	if len(c.ClientIdentifier) > 65535 || len(c.Username) > 65535 || len(c.Password) > 65535 {
111		//Bad size field
112		return ErrProtocolViolation
113	}
114	return Accepted
115}
116
117//Details returns a Details struct containing the Qos and
118//MessageID of this ControlPacket
119func (c *ConnectPacket) Details() Details {
120	return Details{Qos: 0, MessageID: 0}
121}
122