1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.codec.mqtt;
18
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.handler.codec.DecoderException;
21 import io.netty.util.Attribute;
22 import io.netty.util.AttributeKey;
23
24 import static io.netty.handler.codec.mqtt.MqttConstant.MIN_CLIENT_ID_LENGTH;
25
26 final class MqttCodecUtil {
27
28 static final AttributeKey<MqttVersion> MQTT_VERSION_KEY = AttributeKey.valueOf("NETTY_CODEC_MQTT_VERSION");
29
30 static MqttVersion getMqttVersion(ChannelHandlerContext ctx) {
31 Attribute<MqttVersion> attr = ctx.channel().attr(MQTT_VERSION_KEY);
32 MqttVersion version = attr.get();
33 if (version == null) {
34 return MqttVersion.MQTT_3_1_1;
35 }
36 return version;
37 }
38
39 static void setMqttVersion(ChannelHandlerContext ctx, MqttVersion version) {
40 Attribute<MqttVersion> attr = ctx.channel().attr(MQTT_VERSION_KEY);
41 attr.set(version);
42 }
43
44 static boolean isValidPublishTopicName(String topicName) {
45 if (topicName == null) {
46 return false;
47 }
48
49 for (int i = 0; i < topicName.length(); i++) {
50 char c = topicName.charAt(i);
51 if (c == '#' || c == '+' || c == '\0') {
52 return false;
53 }
54 }
55 return true;
56 }
57
58 static boolean isValidMessageId(int messageId) {
59 return messageId != 0;
60 }
61
62 static boolean isValidUserName(String userName) {
63 return userName == null || userName.indexOf('\0') == -1;
64 }
65
66
67
68
69
70
71
72
73
74
75 static boolean isValidClientId(MqttVersion mqttVersion, int maxClientIdLength, String clientId,
76 boolean acceptNulBytes) {
77 if (clientId == null || (!acceptNulBytes && clientId.indexOf('\0') != -1)) {
78 return false;
79 }
80 if (mqttVersion == MqttVersion.MQTT_3_1) {
81 return clientId.length() >= MIN_CLIENT_ID_LENGTH && clientId.length() <= maxClientIdLength;
82 }
83 if (mqttVersion == MqttVersion.MQTT_3_1_1 || mqttVersion == MqttVersion.MQTT_5) {
84
85
86 return true;
87 }
88 throw new IllegalArgumentException(mqttVersion + " is unknown mqtt version");
89 }
90
91 static MqttFixedHeader validateFixedHeader(ChannelHandlerContext ctx, MqttFixedHeader mqttFixedHeader) {
92 switch (mqttFixedHeader.messageType()) {
93 case PUBREL:
94 case SUBSCRIBE:
95 case UNSUBSCRIBE:
96 if (mqttFixedHeader.qosLevel() != MqttQoS.AT_LEAST_ONCE) {
97 throw new DecoderException(mqttFixedHeader.messageType().name() + " message must have QoS 1");
98 }
99 return mqttFixedHeader;
100 case AUTH:
101 if (MqttCodecUtil.getMqttVersion(ctx) != MqttVersion.MQTT_5) {
102 throw new DecoderException("AUTH message requires at least MQTT 5");
103 }
104 return mqttFixedHeader;
105 default:
106 return mqttFixedHeader;
107 }
108 }
109
110 static MqttFixedHeader resetUnusedFields(MqttFixedHeader mqttFixedHeader) {
111 switch (mqttFixedHeader.messageType()) {
112 case CONNECT:
113 case CONNACK:
114 case PUBACK:
115 case PUBREC:
116 case PUBCOMP:
117 case SUBACK:
118 case UNSUBACK:
119 case PINGREQ:
120 case PINGRESP:
121 case DISCONNECT:
122 if (mqttFixedHeader.isDup() ||
123 mqttFixedHeader.qosLevel() != MqttQoS.AT_MOST_ONCE ||
124 mqttFixedHeader.isRetain()) {
125 return new MqttFixedHeader(
126 mqttFixedHeader.messageType(),
127 false,
128 MqttQoS.AT_MOST_ONCE,
129 false,
130 mqttFixedHeader.remainingLength());
131 }
132 return mqttFixedHeader;
133 case PUBREL:
134 case SUBSCRIBE:
135 case UNSUBSCRIBE:
136 if (mqttFixedHeader.isRetain()) {
137 return new MqttFixedHeader(
138 mqttFixedHeader.messageType(),
139 mqttFixedHeader.isDup(),
140 mqttFixedHeader.qosLevel(),
141 false,
142 mqttFixedHeader.remainingLength());
143 }
144 return mqttFixedHeader;
145 default:
146 return mqttFixedHeader;
147 }
148 }
149
150 private MqttCodecUtil() { }
151 }