1 /*
2  * Collie - An asynchronous event-driven network framework using Dlang development
3  *
4  * Copyright (C) 2015-2017  Shanghai Putao Technology Co., Ltd 
5  *
6  * Developer: putao's Dlang team
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 module collie.codec.mqtt.mqttcodecutil;
12 
13 import kiss.log;
14 import std.conv;
15 import std.stdio;
16 import std..string;
17 import std.array;
18 import collie.codec.mqtt.mqttversion;
19 import collie.codec.mqtt.mqttfixedheader;
20 import collie.codec.mqtt.mqttmsgtype;
21 import collie.codec.mqtt.mqttqos;
22 
23 class MqttCodecUtil
24 {
25 	static bool isValidPublishTopicName(string topicName) {
26 		// publish topic name must not contain any wildcard
27 		foreach (char c ; TOPIC_WILDCARDS) {
28 			if (indexOf(topicName,c) >= 0) {
29 				return false;
30 			}
31 		}
32 		return true;
33 	}
34 	
35 	static bool isValidMessageId(int messageId) {
36 		return messageId != 0;
37 	}
38 
39 	static bool isValidClientId(MqttVersion mqttVersion, string clientId) {
40 		if (mqttVersion.protocolName() == "MQIsdp") {
41 			return clientId != null && clientId.length >= MIN_CLIENT_ID_LENGTH &&
42 				clientId.length <= MAX_CLIENT_ID_LENGTH;
43 		}
44 		if (mqttVersion.protocolName() == "MQTT") {
45 			// In 3.1.3.1 Client Identifier of MQTT 3.1.1 specification, The Server MAY allow ClientId’s
46 			// that contain more than 23 encoded bytes. And, The Server MAY allow zero-length ClientId.
47 			return clientId != null;
48 		}
49 
50 		logDebug(to!string(mqttVersion) ~ " is unknown mqtt version");
51 		return false;
52 	}
53 	
54 	static MqttFixedHeader validateFixedHeader(MqttFixedHeader mqttFixedHeader) {
55 		switch (mqttFixedHeader.messageType()) {
56 			case MqttMsgType.PUBREL:
57 			case MqttMsgType.SUBSCRIBE:
58 			case MqttMsgType.UNSUBSCRIBE:
59 				if (mqttFixedHeader.qosLevel() != MqttQoS.AT_LEAST_ONCE) {
60 					
61 					throw new Exception(to!string(mqttFixedHeader.messageType()) ~ " message must have QoS 1");
62 				}
63 				return mqttFixedHeader;
64 			default:
65 				return mqttFixedHeader;
66 		}
67 	}
68 	
69 	static MqttFixedHeader resetUnusedFields(MqttFixedHeader mqttFixedHeader) {
70 		switch (mqttFixedHeader.messageType()) {
71 			case MqttMsgType.CONNECT:
72 			case MqttMsgType.CONNACK:
73 			case MqttMsgType.PUBACK:
74 			case MqttMsgType.PUBREC:
75 			case MqttMsgType.PUBCOMP:
76 			case MqttMsgType.SUBACK:
77 			case MqttMsgType.UNSUBACK:
78 			case MqttMsgType.PINGREQ:
79 			case MqttMsgType.PINGRESP:
80 			case MqttMsgType.DISCONNECT:
81 				if (mqttFixedHeader.isDup() ||
82 					mqttFixedHeader.qosLevel() != MqttQoS.AT_MOST_ONCE ||
83 					mqttFixedHeader.isRetain()) {
84 					return new MqttFixedHeader(
85 						mqttFixedHeader.messageType(),
86 						false,
87 						MqttQoS.AT_MOST_ONCE,
88 						false,
89 						mqttFixedHeader.remainingLength());
90 				}
91 				return mqttFixedHeader;
92 			case MqttMsgType.PUBREL:
93 			case MqttMsgType.SUBSCRIBE:
94 			case MqttMsgType.UNSUBSCRIBE:
95 				if (mqttFixedHeader.isRetain()) {
96 					return new MqttFixedHeader(
97 						mqttFixedHeader.messageType(),
98 						mqttFixedHeader.isDup(),
99 						mqttFixedHeader.qosLevel(),
100 						false,
101 						mqttFixedHeader.remainingLength());
102 				}
103 				return mqttFixedHeader;
104 			default:
105 				return mqttFixedHeader;
106 		}
107 	}
108 
109 private:
110 	this() { }
111 private:
112 	 static  char[] TOPIC_WILDCARDS = array(['#', '+'][]);
113 	 static  int MIN_CLIENT_ID_LENGTH = 1;
114 	 static  int MAX_CLIENT_ID_LENGTH = 23;
115 }
116