Updated version of MQTT Arduino ported to mbed, with additional functionality for lower barrier of entry
Fork of MQTT by
PubSubClient.cpp@2:52a50084cec3, 2014-05-10 (annotated)
- Committer:
- gkutsy
- Date:
- Sat May 10 19:36:14 2014 +0000
- Revision:
- 2:52a50084cec3
- Parent:
- 1:f794fdfd3ecf
asdf
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
jwende | 0:ca855d29545b | 1 | /* |
jwende | 0:ca855d29545b | 2 | PubSubClient.cpp - A simple client for MQTT. |
jwende | 0:ca855d29545b | 3 | Nicholas O'Leary |
jwende | 0:ca855d29545b | 4 | http://knolleary.net |
jwende | 0:ca855d29545b | 5 | |
jwende | 0:ca855d29545b | 6 | initial port for mbed |
jwende | 0:ca855d29545b | 7 | Joerg Wende |
jwende | 0:ca855d29545b | 8 | https://twitter.com/joerg_wende |
gkutsy | 1:f794fdfd3ecf | 9 | |
gkutsy | 1:f794fdfd3ecf | 10 | update, including more lib functionality reducing barrier of entry MQTT |
gkutsy | 1:f794fdfd3ecf | 11 | for mbed client users |
gkutsy | 1:f794fdfd3ecf | 12 | German Kutsy, Litmus Automation LLC. (2014) |
jwende | 0:ca855d29545b | 13 | */ |
jwende | 0:ca855d29545b | 14 | |
jwende | 0:ca855d29545b | 15 | #include "PubSubClient.h" |
jwende | 0:ca855d29545b | 16 | |
jwende | 0:ca855d29545b | 17 | Timer t; |
jwende | 0:ca855d29545b | 18 | //Serial pc1(USBTX, USBRX); |
jwende | 0:ca855d29545b | 19 | |
jwende | 0:ca855d29545b | 20 | |
jwende | 0:ca855d29545b | 21 | int millis() |
jwende | 0:ca855d29545b | 22 | { |
jwende | 0:ca855d29545b | 23 | return t.read_ms(); |
jwende | 0:ca855d29545b | 24 | } |
jwende | 0:ca855d29545b | 25 | |
jwende | 0:ca855d29545b | 26 | PubSubClient::PubSubClient() |
jwende | 0:ca855d29545b | 27 | { |
jwende | 0:ca855d29545b | 28 | } |
jwende | 0:ca855d29545b | 29 | |
jwende | 0:ca855d29545b | 30 | PubSubClient::PubSubClient(char *ip, int port, void (*callback)(char*,char*,unsigned int)) |
jwende | 0:ca855d29545b | 31 | { |
jwende | 0:ca855d29545b | 32 | this->callback = callback; |
jwende | 0:ca855d29545b | 33 | this->ip = ip; |
jwende | 0:ca855d29545b | 34 | this->port = port; |
jwende | 0:ca855d29545b | 35 | t.start(); |
jwende | 0:ca855d29545b | 36 | } |
jwende | 0:ca855d29545b | 37 | |
gkutsy | 2:52a50084cec3 | 38 | PubSubClient::PubSubClient(char *ip, uint16_t port, |
gkutsy | 2:52a50084cec3 | 39 | |
jwende | 0:ca855d29545b | 40 | |
jwende | 0:ca855d29545b | 41 | bool PubSubClient::connect(char *id) |
jwende | 0:ca855d29545b | 42 | { |
jwende | 0:ca855d29545b | 43 | return connect(id,NULL,NULL,0,0,0,0); |
jwende | 0:ca855d29545b | 44 | } |
jwende | 0:ca855d29545b | 45 | |
jwende | 0:ca855d29545b | 46 | bool PubSubClient::connect(char *id, char *user, char *pass) |
jwende | 0:ca855d29545b | 47 | { |
jwende | 0:ca855d29545b | 48 | return connect(id,user,pass,0,0,0,0); |
jwende | 0:ca855d29545b | 49 | } |
jwende | 0:ca855d29545b | 50 | |
jwende | 0:ca855d29545b | 51 | bool PubSubClient::connect(char *id, char* willTopic, short willQos, short willRetain, char* willMessage) |
jwende | 0:ca855d29545b | 52 | { |
jwende | 0:ca855d29545b | 53 | return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage); |
jwende | 0:ca855d29545b | 54 | } |
jwende | 0:ca855d29545b | 55 | |
jwende | 0:ca855d29545b | 56 | bool PubSubClient::connect(char *id, char *user, char *pass, char* willTopic, short willQos, short willRetain, char* willMessage) |
jwende | 0:ca855d29545b | 57 | { |
jwende | 0:ca855d29545b | 58 | if (!connected()) { |
jwende | 0:ca855d29545b | 59 | int result = 0; |
jwende | 0:ca855d29545b | 60 | result = _client.connect(this->ip, this->port); |
jwende | 0:ca855d29545b | 61 | _client.set_blocking(false, 1); |
jwende | 0:ca855d29545b | 62 | //pc1.printf("IP: %s\r\n",this->ip); |
jwende | 0:ca855d29545b | 63 | //pc1.printf("Port: %i\r\n",this->port); |
jwende | 0:ca855d29545b | 64 | //pc1.printf("Result: %i \r\n", result); |
jwende | 0:ca855d29545b | 65 | |
jwende | 0:ca855d29545b | 66 | if (result==0) { |
jwende | 0:ca855d29545b | 67 | nextMsgId = 1; |
jwende | 0:ca855d29545b | 68 | char d[9] = {0x00,0x06,'M','Q','I','s','d','p',MQTTPROTOCOLVERSION}; |
jwende | 0:ca855d29545b | 69 | // Leave room in the buffer for header and variable length field |
jwende | 0:ca855d29545b | 70 | int length = 5; |
jwende | 0:ca855d29545b | 71 | unsigned int j; |
jwende | 0:ca855d29545b | 72 | for (j = 0; j<9; j++) { |
jwende | 0:ca855d29545b | 73 | buffer[length++] = d[j]; |
jwende | 0:ca855d29545b | 74 | } |
jwende | 0:ca855d29545b | 75 | |
jwende | 0:ca855d29545b | 76 | char v; |
jwende | 0:ca855d29545b | 77 | if (willTopic) { |
jwende | 0:ca855d29545b | 78 | v = 0x06|(willQos<<3)|(willRetain<<5); |
jwende | 0:ca855d29545b | 79 | } else { |
jwende | 0:ca855d29545b | 80 | v = 0x02; |
jwende | 0:ca855d29545b | 81 | } |
jwende | 0:ca855d29545b | 82 | |
jwende | 0:ca855d29545b | 83 | if(user != NULL) { |
jwende | 0:ca855d29545b | 84 | v = v|0x80; |
jwende | 0:ca855d29545b | 85 | |
jwende | 0:ca855d29545b | 86 | if(pass != NULL) { |
jwende | 0:ca855d29545b | 87 | v = v|(0x80>>1); |
jwende | 0:ca855d29545b | 88 | } |
jwende | 0:ca855d29545b | 89 | } |
jwende | 0:ca855d29545b | 90 | |
jwende | 0:ca855d29545b | 91 | buffer[length++] = v; |
jwende | 0:ca855d29545b | 92 | |
jwende | 0:ca855d29545b | 93 | buffer[length++] = ((MQTT_KEEPALIVE) >> 8); |
jwende | 0:ca855d29545b | 94 | buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); |
jwende | 0:ca855d29545b | 95 | length = writeString(id,buffer,length); |
jwende | 0:ca855d29545b | 96 | if (willTopic) { |
jwende | 0:ca855d29545b | 97 | length = writeString(willTopic,buffer,length); |
jwende | 0:ca855d29545b | 98 | length = writeString(willMessage,buffer,length); |
jwende | 0:ca855d29545b | 99 | } |
jwende | 0:ca855d29545b | 100 | |
jwende | 0:ca855d29545b | 101 | if(user != NULL) { |
jwende | 0:ca855d29545b | 102 | length = writeString(user,buffer,length); |
jwende | 0:ca855d29545b | 103 | if(pass != NULL) { |
jwende | 0:ca855d29545b | 104 | length = writeString(pass,buffer,length); |
jwende | 0:ca855d29545b | 105 | } |
jwende | 0:ca855d29545b | 106 | } |
jwende | 0:ca855d29545b | 107 | //pc1.printf("Before MQTT Connect ... \r\n"); |
jwende | 0:ca855d29545b | 108 | write(MQTTCONNECT,buffer,length-5); |
jwende | 0:ca855d29545b | 109 | |
jwende | 0:ca855d29545b | 110 | lastInActivity = lastOutActivity = millis(); |
jwende | 0:ca855d29545b | 111 | |
jwende | 0:ca855d29545b | 112 | int llen=128; |
jwende | 0:ca855d29545b | 113 | int len =0; |
jwende | 0:ca855d29545b | 114 | |
jwende | 0:ca855d29545b | 115 | while ((len=readPacket(llen))==0) { |
jwende | 0:ca855d29545b | 116 | unsigned long t = millis(); |
jwende | 0:ca855d29545b | 117 | if (t-lastInActivity > MQTT_KEEPALIVE*1000UL) { |
jwende | 0:ca855d29545b | 118 | _client.close(true); |
jwende | 0:ca855d29545b | 119 | return false; |
jwende | 0:ca855d29545b | 120 | } |
jwende | 0:ca855d29545b | 121 | } |
jwende | 0:ca855d29545b | 122 | //pc1.printf("after MQTT Connect ... %i\r\n",len); |
jwende | 0:ca855d29545b | 123 | if (len == 4 && buffer[3] == 0) { |
jwende | 0:ca855d29545b | 124 | lastInActivity = millis(); |
jwende | 0:ca855d29545b | 125 | pingOutstanding = false; |
jwende | 0:ca855d29545b | 126 | return true; |
jwende | 0:ca855d29545b | 127 | } |
jwende | 0:ca855d29545b | 128 | } |
jwende | 0:ca855d29545b | 129 | _client.close(true); |
jwende | 0:ca855d29545b | 130 | } |
jwende | 0:ca855d29545b | 131 | return false; |
jwende | 0:ca855d29545b | 132 | } |
jwende | 0:ca855d29545b | 133 | |
jwende | 0:ca855d29545b | 134 | |
jwende | 0:ca855d29545b | 135 | int PubSubClient::readPacket(int lengthLength) |
jwende | 0:ca855d29545b | 136 | { |
jwende | 0:ca855d29545b | 137 | int len = 0; |
jwende | 0:ca855d29545b | 138 | len = _client.receive_all(buffer,lengthLength); |
jwende | 0:ca855d29545b | 139 | return len; |
jwende | 0:ca855d29545b | 140 | } |
jwende | 0:ca855d29545b | 141 | |
jwende | 0:ca855d29545b | 142 | bool PubSubClient::loop() |
jwende | 0:ca855d29545b | 143 | { |
jwende | 0:ca855d29545b | 144 | if (connected()) { |
jwende | 0:ca855d29545b | 145 | unsigned long t = millis(); |
jwende | 0:ca855d29545b | 146 | if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) { |
jwende | 0:ca855d29545b | 147 | if (pingOutstanding) { |
jwende | 0:ca855d29545b | 148 | _client.close(true); |
jwende | 0:ca855d29545b | 149 | return false; |
jwende | 0:ca855d29545b | 150 | } else { |
jwende | 0:ca855d29545b | 151 | buffer[0] = MQTTPINGREQ; |
jwende | 0:ca855d29545b | 152 | buffer[1] = 0; |
jwende | 0:ca855d29545b | 153 | _client.send(buffer,2); |
jwende | 0:ca855d29545b | 154 | lastOutActivity = t; |
jwende | 0:ca855d29545b | 155 | lastInActivity = t; |
jwende | 0:ca855d29545b | 156 | pingOutstanding = true; |
jwende | 0:ca855d29545b | 157 | } |
jwende | 0:ca855d29545b | 158 | } |
jwende | 0:ca855d29545b | 159 | int len; |
jwende | 0:ca855d29545b | 160 | int llen= 128; |
jwende | 0:ca855d29545b | 161 | if (!((len=readPacket(llen))==0)) { |
jwende | 0:ca855d29545b | 162 | if (len > 0) { |
jwende | 0:ca855d29545b | 163 | lastInActivity = t; |
jwende | 0:ca855d29545b | 164 | char type = buffer[0]&0xF0; |
jwende | 0:ca855d29545b | 165 | if (type == MQTTPUBLISH) { |
jwende | 0:ca855d29545b | 166 | if (callback) { |
jwende | 0:ca855d29545b | 167 | //pc1.printf("MQTTPUBLISH received ... %i\r\n",len); |
jwende | 0:ca855d29545b | 168 | int tl = (buffer[2]<<8)+buffer[3]; |
jwende | 0:ca855d29545b | 169 | //pc1.printf("t1 ... %i\r\n",tl); |
jwende | 0:ca855d29545b | 170 | char topic[tl+1]; |
jwende | 0:ca855d29545b | 171 | for (int i=0; i<tl; i++) { |
jwende | 0:ca855d29545b | 172 | topic[i] = buffer[4+i]; |
jwende | 0:ca855d29545b | 173 | } |
jwende | 0:ca855d29545b | 174 | topic[tl] = 0; |
jwende | 0:ca855d29545b | 175 | //pc1.printf("MQTTPUBLISH Topic ... %s\r\n",topic); |
jwende | 0:ca855d29545b | 176 | // ignore msgID - only support QoS 0 subs |
jwende | 0:ca855d29545b | 177 | int t2 = len-4-tl; |
jwende | 0:ca855d29545b | 178 | //pc1.printf("t2 ... %i\r\n",t2); |
jwende | 0:ca855d29545b | 179 | char payload[t2+1]; |
jwende | 0:ca855d29545b | 180 | for (int i=0; i<t2; i++) { |
jwende | 0:ca855d29545b | 181 | payload[i] = buffer[4+i+tl]; |
jwende | 0:ca855d29545b | 182 | } |
jwende | 0:ca855d29545b | 183 | payload[t2] = 0; |
jwende | 0:ca855d29545b | 184 | //pc1.printf("MQTTPUBLISH Payload ... %s\r\n",payload); |
jwende | 0:ca855d29545b | 185 | callback(topic,payload,t2); |
jwende | 0:ca855d29545b | 186 | } |
jwende | 0:ca855d29545b | 187 | } else if (type == MQTTPINGREQ) { |
jwende | 0:ca855d29545b | 188 | buffer[0] = MQTTPINGRESP; |
jwende | 0:ca855d29545b | 189 | buffer[1] = 0; |
jwende | 0:ca855d29545b | 190 | _client.send(buffer,2); |
jwende | 0:ca855d29545b | 191 | } else if (type == MQTTPINGRESP) { |
jwende | 0:ca855d29545b | 192 | pingOutstanding = false; |
jwende | 0:ca855d29545b | 193 | } |
jwende | 0:ca855d29545b | 194 | } |
jwende | 0:ca855d29545b | 195 | } |
jwende | 0:ca855d29545b | 196 | return true; |
jwende | 0:ca855d29545b | 197 | } |
jwende | 0:ca855d29545b | 198 | return false; |
jwende | 0:ca855d29545b | 199 | } |
jwende | 0:ca855d29545b | 200 | |
jwende | 0:ca855d29545b | 201 | bool PubSubClient::publish(char* topic, char* payload) |
jwende | 0:ca855d29545b | 202 | { |
jwende | 0:ca855d29545b | 203 | return publish(topic,payload,strlen(payload),false); |
jwende | 0:ca855d29545b | 204 | } |
jwende | 0:ca855d29545b | 205 | |
jwende | 0:ca855d29545b | 206 | bool PubSubClient::publish(char* topic, char* payload, unsigned int plength) |
jwende | 0:ca855d29545b | 207 | { |
jwende | 0:ca855d29545b | 208 | return publish(topic, payload, plength, false); |
jwende | 0:ca855d29545b | 209 | } |
jwende | 0:ca855d29545b | 210 | |
jwende | 0:ca855d29545b | 211 | bool PubSubClient::publish(char* topic, char* payload, unsigned int plength, bool retained) |
jwende | 0:ca855d29545b | 212 | { |
jwende | 0:ca855d29545b | 213 | if (connected()) { |
jwende | 0:ca855d29545b | 214 | // Leave room in the buffer for header and variable length field |
jwende | 0:ca855d29545b | 215 | //pc1.printf("in publish ... %s\r\n",topic); |
jwende | 0:ca855d29545b | 216 | //pc1.printf("in publish ... %s\r\n",payload); |
jwende | 0:ca855d29545b | 217 | int length = 5; |
jwende | 0:ca855d29545b | 218 | length = writeString(topic,buffer,length); |
jwende | 0:ca855d29545b | 219 | int i; |
jwende | 0:ca855d29545b | 220 | for (i=0; i<plength; i++) { |
jwende | 0:ca855d29545b | 221 | buffer[length++] = payload[i]; |
jwende | 0:ca855d29545b | 222 | } |
jwende | 0:ca855d29545b | 223 | short header = MQTTPUBLISH; |
jwende | 0:ca855d29545b | 224 | if (retained) { |
jwende | 0:ca855d29545b | 225 | header |= 1; |
jwende | 0:ca855d29545b | 226 | } |
jwende | 0:ca855d29545b | 227 | return write(header,buffer,length-5); |
jwende | 0:ca855d29545b | 228 | } |
jwende | 0:ca855d29545b | 229 | return false; |
jwende | 0:ca855d29545b | 230 | } |
jwende | 0:ca855d29545b | 231 | |
jwende | 0:ca855d29545b | 232 | |
jwende | 0:ca855d29545b | 233 | |
jwende | 0:ca855d29545b | 234 | bool PubSubClient::write(short header, char* buf, int length) |
jwende | 0:ca855d29545b | 235 | { |
jwende | 0:ca855d29545b | 236 | short lenBuf[4]; |
jwende | 0:ca855d29545b | 237 | short llen = 0; |
jwende | 0:ca855d29545b | 238 | short digit; |
jwende | 0:ca855d29545b | 239 | short pos = 0; |
jwende | 0:ca855d29545b | 240 | short rc; |
jwende | 0:ca855d29545b | 241 | short len = length; |
jwende | 0:ca855d29545b | 242 | //pc1.printf("in write ... %d\r\n",length); |
jwende | 0:ca855d29545b | 243 | //pc1.printf("in write ... %s\r\n",buf); |
jwende | 0:ca855d29545b | 244 | do { |
jwende | 0:ca855d29545b | 245 | digit = len % 128; |
jwende | 0:ca855d29545b | 246 | len = len / 128; |
jwende | 0:ca855d29545b | 247 | if (len > 0) { |
jwende | 0:ca855d29545b | 248 | digit |= 0x80; |
jwende | 0:ca855d29545b | 249 | } |
jwende | 0:ca855d29545b | 250 | lenBuf[pos++] = digit; |
jwende | 0:ca855d29545b | 251 | llen++; |
jwende | 0:ca855d29545b | 252 | } while(len>0); |
jwende | 0:ca855d29545b | 253 | |
jwende | 0:ca855d29545b | 254 | buf[4-llen] = header; |
jwende | 0:ca855d29545b | 255 | for (int i=0; i<llen; i++) { |
jwende | 0:ca855d29545b | 256 | buf[5-llen+i] = lenBuf[i]; |
jwende | 0:ca855d29545b | 257 | } |
jwende | 0:ca855d29545b | 258 | rc = _client.send(buf+(4-llen),length+1+llen); |
jwende | 0:ca855d29545b | 259 | |
jwende | 0:ca855d29545b | 260 | lastOutActivity = millis(); |
jwende | 0:ca855d29545b | 261 | return (rc == 1+llen+length); |
jwende | 0:ca855d29545b | 262 | } |
jwende | 0:ca855d29545b | 263 | |
jwende | 0:ca855d29545b | 264 | bool PubSubClient::subscribe(char* topic) |
jwende | 0:ca855d29545b | 265 | { |
jwende | 0:ca855d29545b | 266 | //pc1.printf("in subscribe ... %s\r\n",topic); |
jwende | 0:ca855d29545b | 267 | if (connected()) { |
jwende | 0:ca855d29545b | 268 | // Leave room in the buffer for header and variable length field |
jwende | 0:ca855d29545b | 269 | int length = 5; |
jwende | 0:ca855d29545b | 270 | nextMsgId++; |
jwende | 0:ca855d29545b | 271 | if (nextMsgId == 0) { |
jwende | 0:ca855d29545b | 272 | nextMsgId = 1; |
jwende | 0:ca855d29545b | 273 | } |
jwende | 0:ca855d29545b | 274 | buffer[length++] = (nextMsgId >> 8); |
jwende | 0:ca855d29545b | 275 | buffer[length++] = (nextMsgId & 0xFF); |
jwende | 0:ca855d29545b | 276 | length = writeString(topic, buffer,length); |
jwende | 0:ca855d29545b | 277 | buffer[length++] = 0; // Only do QoS 0 subs |
jwende | 0:ca855d29545b | 278 | return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5); |
jwende | 0:ca855d29545b | 279 | } |
jwende | 0:ca855d29545b | 280 | return false; |
jwende | 0:ca855d29545b | 281 | } |
jwende | 0:ca855d29545b | 282 | |
jwende | 0:ca855d29545b | 283 | bool PubSubClient::unsubscribe(char* topic) |
jwende | 0:ca855d29545b | 284 | { |
jwende | 0:ca855d29545b | 285 | if (connected()) { |
jwende | 0:ca855d29545b | 286 | int length = 5; |
jwende | 0:ca855d29545b | 287 | nextMsgId++; |
jwende | 0:ca855d29545b | 288 | if (nextMsgId == 0) { |
jwende | 0:ca855d29545b | 289 | nextMsgId = 1; |
jwende | 0:ca855d29545b | 290 | } |
jwende | 0:ca855d29545b | 291 | buffer[length++] = (nextMsgId >> 8); |
jwende | 0:ca855d29545b | 292 | buffer[length++] = (nextMsgId & 0xFF); |
jwende | 0:ca855d29545b | 293 | length = writeString(topic, buffer,length); |
jwende | 0:ca855d29545b | 294 | return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5); |
jwende | 0:ca855d29545b | 295 | } |
jwende | 0:ca855d29545b | 296 | return false; |
jwende | 0:ca855d29545b | 297 | } |
jwende | 0:ca855d29545b | 298 | |
jwende | 0:ca855d29545b | 299 | void PubSubClient::disconnect() |
jwende | 0:ca855d29545b | 300 | { |
jwende | 0:ca855d29545b | 301 | buffer[0] = MQTTDISCONNECT; |
jwende | 0:ca855d29545b | 302 | buffer[1] = 0; |
jwende | 0:ca855d29545b | 303 | _client.send(buffer,2); |
jwende | 0:ca855d29545b | 304 | _client.close(true); |
jwende | 0:ca855d29545b | 305 | lastInActivity = lastOutActivity = millis(); |
jwende | 0:ca855d29545b | 306 | } |
jwende | 0:ca855d29545b | 307 | |
jwende | 0:ca855d29545b | 308 | int PubSubClient::writeString(char* string, char* buf, int pos) |
jwende | 0:ca855d29545b | 309 | { |
jwende | 0:ca855d29545b | 310 | char* idp = string; |
jwende | 0:ca855d29545b | 311 | int i = 0; |
jwende | 0:ca855d29545b | 312 | pos += 2; |
jwende | 0:ca855d29545b | 313 | while (*idp) { |
jwende | 0:ca855d29545b | 314 | buf[pos++] = *idp++; |
jwende | 0:ca855d29545b | 315 | i++; |
jwende | 0:ca855d29545b | 316 | } |
jwende | 0:ca855d29545b | 317 | buf[pos-i-2] = (i >> 8); |
jwende | 0:ca855d29545b | 318 | buf[pos-i-1] = (i & 0xFF); |
jwende | 0:ca855d29545b | 319 | return pos; |
jwende | 0:ca855d29545b | 320 | } |
jwende | 0:ca855d29545b | 321 | |
jwende | 0:ca855d29545b | 322 | |
jwende | 0:ca855d29545b | 323 | bool PubSubClient::connected() |
jwende | 0:ca855d29545b | 324 | { |
jwende | 0:ca855d29545b | 325 | bool rc; |
jwende | 0:ca855d29545b | 326 | rc = (int)_client.is_connected(); |
jwende | 0:ca855d29545b | 327 | if (!rc) _client.close(true); |
jwende | 0:ca855d29545b | 328 | return rc; |
jwende | 0:ca855d29545b | 329 | } |