1""" 2******************************************************************* 3 Copyright (c) 2017, 2019 IBM Corp. 4 5 All rights reserved. This program and the accompanying materials 6 are made available under the terms of the Eclipse Public License v2.0 7 and Eclipse Distribution License v1.0 which accompany this distribution. 8 9 The Eclipse Public License is available at 10 http://www.eclipse.org/legal/epl-v10.html 11 and the Eclipse Distribution License is available at 12 http://www.eclipse.org/org/documents/edl-v10.php. 13 14 Contributors: 15 Ian Craggs - initial implementation and/or documentation 16******************************************************************* 17""" 18 19import sys 20 21from .packettypes import PacketTypes 22 23 24class ReasonCodes: 25 """MQTT version 5.0 reason codes class. 26 27 See ReasonCodes.names for a list of possible numeric values along with their 28 names and the packets to which they apply. 29 30 """ 31 32 def __init__(self, packetType, aName="Success", identifier=-1): 33 """ 34 packetType: the type of the packet, such as PacketTypes.CONNECT that 35 this reason code will be used with. Some reason codes have different 36 names for the same identifier when used a different packet type. 37 38 aName: the String name of the reason code to be created. Ignored 39 if the identifier is set. 40 41 identifier: an integer value of the reason code to be created. 42 43 """ 44 45 self.packetType = packetType 46 self.names = { 47 0: {"Success": [PacketTypes.CONNACK, PacketTypes.PUBACK, 48 PacketTypes.PUBREC, PacketTypes.PUBREL, PacketTypes.PUBCOMP, 49 PacketTypes.UNSUBACK, PacketTypes.AUTH], 50 "Normal disconnection": [PacketTypes.DISCONNECT], 51 "Granted QoS 0": [PacketTypes.SUBACK]}, 52 1: {"Granted QoS 1": [PacketTypes.SUBACK]}, 53 2: {"Granted QoS 2": [PacketTypes.SUBACK]}, 54 4: {"Disconnect with will message": [PacketTypes.DISCONNECT]}, 55 16: {"No matching subscribers": 56 [PacketTypes.PUBACK, PacketTypes.PUBREC]}, 57 17: {"No subscription found": [PacketTypes.UNSUBACK]}, 58 24: {"Continue authentication": [PacketTypes.AUTH]}, 59 25: {"Re-authenticate": [PacketTypes.AUTH]}, 60 128: {"Unspecified error": [PacketTypes.CONNACK, PacketTypes.PUBACK, 61 PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, 62 PacketTypes.DISCONNECT], }, 63 129: {"Malformed packet": 64 [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 65 130: {"Protocol error": 66 [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 67 131: {"Implementation specific error": [PacketTypes.CONNACK, 68 PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.SUBACK, 69 PacketTypes.UNSUBACK, PacketTypes.DISCONNECT], }, 70 132: {"Unsupported protocol version": [PacketTypes.CONNACK]}, 71 133: {"Client identifier not valid": [PacketTypes.CONNACK]}, 72 134: {"Bad user name or password": [PacketTypes.CONNACK]}, 73 135: {"Not authorized": [PacketTypes.CONNACK, PacketTypes.PUBACK, 74 PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.UNSUBACK, 75 PacketTypes.DISCONNECT], }, 76 136: {"Server unavailable": [PacketTypes.CONNACK]}, 77 137: {"Server busy": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 78 138: {"Banned": [PacketTypes.CONNACK]}, 79 139: {"Server shutting down": [PacketTypes.DISCONNECT]}, 80 140: {"Bad authentication method": 81 [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 82 141: {"Keep alive timeout": [PacketTypes.DISCONNECT]}, 83 142: {"Session taken over": [PacketTypes.DISCONNECT]}, 84 143: {"Topic filter invalid": 85 [PacketTypes.SUBACK, PacketTypes.UNSUBACK, PacketTypes.DISCONNECT]}, 86 144: {"Topic name invalid": 87 [PacketTypes.CONNACK, PacketTypes.PUBACK, 88 PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, 89 145: {"Packet identifier in use": 90 [PacketTypes.PUBACK, PacketTypes.PUBREC, 91 PacketTypes.SUBACK, PacketTypes.UNSUBACK]}, 92 146: {"Packet identifier not found": 93 [PacketTypes.PUBREL, PacketTypes.PUBCOMP]}, 94 147: {"Receive maximum exceeded": [PacketTypes.DISCONNECT]}, 95 148: {"Topic alias invalid": [PacketTypes.DISCONNECT]}, 96 149: {"Packet too large": [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 97 150: {"Message rate too high": [PacketTypes.DISCONNECT]}, 98 151: {"Quota exceeded": [PacketTypes.CONNACK, PacketTypes.PUBACK, 99 PacketTypes.PUBREC, PacketTypes.SUBACK, PacketTypes.DISCONNECT], }, 100 152: {"Administrative action": [PacketTypes.DISCONNECT]}, 101 153: {"Payload format invalid": 102 [PacketTypes.PUBACK, PacketTypes.PUBREC, PacketTypes.DISCONNECT]}, 103 154: {"Retain not supported": 104 [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 105 155: {"QoS not supported": 106 [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 107 156: {"Use another server": 108 [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 109 157: {"Server moved": 110 [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 111 158: {"Shared subscription not supported": 112 [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, 113 159: {"Connection rate exceeded": 114 [PacketTypes.CONNACK, PacketTypes.DISCONNECT]}, 115 160: {"Maximum connect time": 116 [PacketTypes.DISCONNECT]}, 117 161: {"Subscription identifiers not supported": 118 [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, 119 162: {"Wildcard subscription not supported": 120 [PacketTypes.SUBACK, PacketTypes.DISCONNECT]}, 121 } 122 if identifier == -1: 123 if packetType == PacketTypes.DISCONNECT and aName == "Success": 124 aName = "Normal disconnection" 125 self.set(aName) 126 else: 127 self.value = identifier 128 self.getName() # check it's good 129 130 def __getName__(self, packetType, identifier): 131 """ 132 Get the reason code string name for a specific identifier. 133 The name can vary by packet type for the same identifier, which 134 is why the packet type is also required. 135 136 Used when displaying the reason code. 137 """ 138 assert identifier in self.names.keys(), identifier 139 names = self.names[identifier] 140 namelist = [name for name in names.keys() if packetType in names[name]] 141 assert len(namelist) == 1 142 return namelist[0] 143 144 def getId(self, name): 145 """ 146 Get the numeric id corresponding to a reason code name. 147 148 Used when setting the reason code for a packetType 149 check that only valid codes for the packet are set. 150 """ 151 identifier = None 152 for code in self.names.keys(): 153 if name in self.names[code].keys(): 154 if self.packetType in self.names[code][name]: 155 identifier = code 156 break 157 assert identifier is not None, name 158 return identifier 159 160 def set(self, name): 161 self.value = self.getId(name) 162 163 def unpack(self, buffer): 164 c = buffer[0] 165 if sys.version_info[0] < 3: 166 c = ord(c) 167 name = self.__getName__(self.packetType, c) 168 self.value = self.getId(name) 169 return 1 170 171 def getName(self): 172 """Returns the reason code name corresponding to the numeric value which is set. 173 """ 174 return self.__getName__(self.packetType, self.value) 175 176 def __eq__(self, other): 177 if isinstance(other, int): 178 return self.value == other 179 if isinstance(other, str): 180 return self.value == str(self) 181 if isinstance(other, ReasonCodes): 182 return self.value == other.value 183 return False 184 185 def __str__(self): 186 return self.getName() 187 188 def json(self): 189 return self.getName() 190 191 def pack(self): 192 return bytearray([self.value]) 193