mibor refactoring
[public/netxms.git] / src / libnetxms / message.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
3** NetXMS Foundation Library
ca0a165b 4** Copyright (C) 2003-2016 Victor Kirhenshtein
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
68f384ea
VK
7** it under the terms of the GNU Lesser General Public License as published
8** by the Free Software Foundation; either version 3 of the License, or
5039dede
AK
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
68f384ea 16** You should have received a copy of the GNU Lesser General Public License
5039dede
AK
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** File: message.cpp
21**
22**/
5039dede 23#include "libnetxms.h"
5c44534b 24#include <uthash.h>
5039dede 25
934f53da
VK
26/**
27 * Calculate field size
28 */
b368969c 29static size_t CalculateFieldSize(NXCP_MESSAGE_FIELD *field, bool networkByteOrder)
5039dede 30{
818ebb6a 31 size_t nSize;
5039dede 32
b368969c 33 switch(field->type)
5039dede 34 {
b368969c 35 case NXCP_DT_INT32:
5039dede
AK
36 nSize = 12;
37 break;
b368969c
VK
38 case NXCP_DT_INT64:
39 case NXCP_DT_FLOAT:
5039dede
AK
40 nSize = 16;
41 break;
b368969c 42 case NXCP_DT_INT16:
5039dede
AK
43 nSize = 8;
44 break;
712b2760
VK
45 case NXCP_DT_INETADDR:
46 nSize = 32;
47 break;
b368969c
VK
48 case NXCP_DT_STRING:
49 case NXCP_DT_BINARY:
5c44534b 50 if (networkByteOrder)
b368969c 51 nSize = ntohl(field->df_string.length) + 12;
5039dede 52 else
b368969c 53 nSize = field->df_string.length + 12;
5039dede
AK
54 break;
55 default:
56 nSize = 8;
57 break;
58 }
59 return nSize;
60}
61
5c44534b
VK
62/**
63 * Field hash map entry
64 */
65struct MessageField
66{
67 UT_hash_handle hh;
68 UINT32 id;
818ebb6a 69 size_t size;
b368969c 70 NXCP_MESSAGE_FIELD data;
5c44534b
VK
71};
72
73/**
74 * Create new hash entry wth given field size
75 */
818ebb6a 76inline MessageField *CreateMessageField(size_t fieldSize)
5c44534b 77{
b368969c 78 size_t entrySize = sizeof(MessageField) - sizeof(NXCP_MESSAGE_FIELD) + fieldSize;
818ebb6a 79 MessageField *entry = (MessageField *)calloc(1, entrySize);
5c44534b
VK
80 entry->size = entrySize;
81 return entry;
82}
83
934f53da 84/**
b368969c 85 * Default constructor for NXCPMessage class
934f53da 86 */
b368969c 87NXCPMessage::NXCPMessage(int version)
5039dede 88{
5c44534b
VK
89 m_code = 0;
90 m_id = 0;
91 m_fields = NULL;
92 m_flags = 0;
93 m_version = version;
6be0a20b
VK
94 m_data = NULL;
95 m_dataSize = 0;
5039dede
AK
96}
97
56dd6325
VK
98/**
99 * Create a copy of prepared CSCP message
100 */
b368969c 101NXCPMessage::NXCPMessage(NXCPMessage *msg)
5039dede 102{
b368969c
VK
103 m_code = msg->m_code;
104 m_id = msg->m_id;
105 m_flags = msg->m_flags;
106 m_version = msg->m_version;
5c44534b 107 m_fields = NULL;
5039dede 108
6be0a20b 109 if (m_flags & MF_BINARY)
5039dede 110 {
279d8695 111 m_dataSize = msg->m_dataSize;
b368969c 112 m_data = (BYTE *)nx_memdup(msg->m_data, m_dataSize);
6be0a20b
VK
113 }
114 else
115 {
279d8695
VK
116 m_data = NULL;
117 m_dataSize = 0;
118
6be0a20b 119 MessageField *entry, *tmp;
b368969c 120 HASH_ITER(hh, msg->m_fields, entry, tmp)
6be0a20b
VK
121 {
122 MessageField *f = (MessageField *)nx_memdup(entry, entry->size);
123 HASH_ADD_INT(m_fields, id, f);
124 }
5039dede
AK
125 }
126}
127
56dd6325 128/**
b368969c 129 * Create NXCPMessage object from received message
56dd6325 130 */
b368969c 131NXCPMessage::NXCPMessage(NXCP_MESSAGE *msg, int version)
5039dede 132{
5c44534b
VK
133 UINT32 i;
134
b368969c
VK
135 m_flags = ntohs(msg->flags);
136 m_code = ntohs(msg->code);
137 m_id = ntohl(msg->id);
5c44534b
VK
138 m_version = version;
139 m_fields = NULL;
140
5039dede 141 // Parse data fields
6be0a20b 142 if (m_flags & MF_BINARY)
5039dede 143 {
b368969c
VK
144 m_dataSize = (size_t)ntohl(msg->numFields);
145 m_data = (BYTE *)nx_memdup(msg->fields, m_dataSize);
6be0a20b
VK
146 }
147 else
148 {
149 m_data = NULL;
150 m_dataSize = 0;
5039dede 151
b368969c
VK
152 int fieldCount = (int)ntohl(msg->numFields);
153 size_t size = (size_t)ntohl(msg->size);
6be0a20b
VK
154 size_t pos = NXCP_HEADER_SIZE;
155 for(int f = 0; f < fieldCount; f++)
5039dede 156 {
b368969c 157 NXCP_MESSAGE_FIELD *field = (NXCP_MESSAGE_FIELD *)(((BYTE *)msg) + pos);
6be0a20b
VK
158
159 // Validate position inside message
b368969c 160 if (pos > size - 8)
5039dede 161 break;
b368969c
VK
162 if ((pos > size - 12) &&
163 ((field->type == NXCP_DT_STRING) || (field->type == NXCP_DT_BINARY)))
5039dede 164 break;
6be0a20b
VK
165
166 // Calculate and validate variable size
167 size_t fieldSize = CalculateFieldSize(field, true);
b368969c 168 if (pos + fieldSize > size)
5039dede 169 break;
6be0a20b
VK
170
171 // Create new entry
172 MessageField *entry = CreateMessageField(fieldSize);
173 entry->id = ntohl(field->fieldId);
174 memcpy(&entry->data, field, fieldSize);
175
176 // Convert values to host format
177 entry->data.fieldId = ntohl(entry->data.fieldId);
b368969c 178 switch(field->type)
6be0a20b 179 {
b368969c 180 case NXCP_DT_INT32:
6be0a20b
VK
181 entry->data.df_int32 = ntohl(entry->data.df_int32);
182 break;
b368969c 183 case NXCP_DT_INT64:
6be0a20b
VK
184 entry->data.df_int64 = ntohq(entry->data.df_int64);
185 break;
b368969c 186 case NXCP_DT_INT16:
6be0a20b
VK
187 entry->data.df_int16 = ntohs(entry->data.df_int16);
188 break;
b368969c 189 case NXCP_DT_FLOAT:
6be0a20b
VK
190 entry->data.df_real = ntohd(entry->data.df_real);
191 break;
b368969c 192 case NXCP_DT_STRING:
5039dede 193#if !(WORDS_BIGENDIAN)
b368969c
VK
194 entry->data.df_string.length = ntohl(entry->data.df_string.length);
195 for(i = 0; i < entry->data.df_string.length / 2; i++)
196 entry->data.df_string.value[i] = ntohs(entry->data.df_string.value[i]);
5039dede 197#endif
6be0a20b 198 break;
b368969c
VK
199 case NXCP_DT_BINARY:
200 entry->data.df_string.length = ntohl(entry->data.df_string.length);
6be0a20b 201 break;
712b2760
VK
202 case NXCP_DT_INETADDR:
203 if (entry->data.df_inetaddr.family == NXCP_AF_INET)
204 {
205 entry->data.df_inetaddr.addr.v4 = ntohl(entry->data.df_inetaddr.addr.v4);
206 }
207 break;
6be0a20b 208 }
5039dede 209
6be0a20b
VK
210 HASH_ADD_INT(m_fields, id, entry);
211
212 // Starting from version 2, all variables should be 8-byte aligned
213 if (m_version >= 2)
214 pos += fieldSize + ((8 - (fieldSize % 8)) & 7);
215 else
216 pos += fieldSize;
217 }
5c44534b 218
5039dede 219 }
5039dede
AK
220}
221
56dd6325 222/**
b368969c 223 * Destructor for NXCPMessage
56dd6325 224 */
b368969c 225NXCPMessage::~NXCPMessage()
5039dede 226{
b368969c 227 deleteAllFields();
6be0a20b 228 safe_free(m_data);
5039dede
AK
229}
230
56dd6325 231/**
5c44534b 232 * Find field by ID
56dd6325 233 */
ca0a165b 234NXCP_MESSAGE_FIELD *NXCPMessage::find(UINT32 fieldId) const
5039dede 235{
5c44534b
VK
236 MessageField *entry;
237 HASH_FIND_INT(m_fields, &fieldId, entry);
238 return (entry != NULL) ? &entry->data : NULL;
5039dede
AK
239}
240
fb9441ee 241/**
4af351c7 242 * set variable
b368969c 243 * Argument size (data size) contains data length in bytes for DT_BINARY type
fb9441ee
VK
244 * and maximum number of characters for DT_STRING type (0 means no limit)
245 */
712b2760 246void *NXCPMessage::set(UINT32 fieldId, BYTE type, const void *value, bool isSigned, size_t size)
5039dede 247{
6be0a20b
VK
248 if (m_flags & MF_BINARY)
249 return NULL;
250
b368969c 251 size_t length;
8756ff63 252#if defined(UNICODE_UCS2) && defined(UNICODE)
b368969c 253#define __buffer value
8756ff63
VK
254#else
255 UCS2CHAR *__buffer;
5039dede
AK
256#endif
257
5c44534b
VK
258 // Create entry
259 MessageField *entry;
b368969c 260 switch(type)
5039dede 261 {
b368969c 262 case NXCP_DT_INT32:
5c44534b 263 entry = CreateMessageField(12);
b368969c 264 entry->data.df_int32 = *((const UINT32 *)value);
5039dede 265 break;
b368969c 266 case NXCP_DT_INT16:
5c44534b 267 entry = CreateMessageField(8);
b368969c 268 entry->data.df_int16 = *((const WORD *)value);
5039dede 269 break;
b368969c 270 case NXCP_DT_INT64:
5c44534b 271 entry = CreateMessageField(16);
b368969c 272 entry->data.df_int64 = *((const UINT64 *)value);
5039dede 273 break;
b368969c 274 case NXCP_DT_FLOAT:
5c44534b 275 entry = CreateMessageField(16);
b368969c 276 entry->data.df_real = *((const double *)value);
5039dede 277 break;
b368969c 278 case NXCP_DT_STRING:
5039dede 279#ifdef UNICODE
b368969c
VK
280 length = _tcslen((const TCHAR *)value);
281 if ((size > 0) && (length > size))
282 length = size;
8756ff63 283#ifndef UNICODE_UCS2 /* assume UNICODE_UCS4 */
b368969c
VK
284 __buffer = (UCS2CHAR *)malloc(length * 2 + 2);
285 ucs4_to_ucs2((WCHAR *)value, length, __buffer, length + 1);
5039dede
AK
286#endif
287#else /* not UNICODE */
b368969c
VK
288 __buffer = UCS2StringFromMBString((const char *)value);
289 length = (UINT32)ucs2_strlen(__buffer);
290 if ((size > 0) && (length > size))
291 length = size;
8756ff63 292#endif
b368969c
VK
293 entry = CreateMessageField(12 + length * 2);
294 entry->data.df_string.length = (UINT32)(length * 2);
295 memcpy(entry->data.df_string.value, __buffer, entry->data.df_string.length);
8756ff63
VK
296#if !defined(UNICODE_UCS2) || !defined(UNICODE)
297 free(__buffer);
5039dede
AK
298#endif
299 break;
b368969c
VK
300 case NXCP_DT_BINARY:
301 entry = CreateMessageField(12 + size);
de4af576
VK
302 entry->data.df_binary.length = (UINT32)size;
303 if ((entry->data.df_binary.length > 0) && (value != NULL))
304 memcpy(entry->data.df_binary.value, value, entry->data.df_binary.length);
5039dede 305 break;
712b2760
VK
306 case NXCP_DT_INETADDR:
307 entry = CreateMessageField(32);
308 entry->data.df_inetaddr.family = (((InetAddress *)value)->getFamily() == AF_INET) ? NXCP_AF_INET : NXCP_AF_INET6;
309 entry->data.df_inetaddr.maskBits = (BYTE)((InetAddress *)value)->getMaskBits();
310 if (((InetAddress *)value)->getFamily() == AF_INET)
311 {
312 entry->data.df_inetaddr.addr.v4 = ((InetAddress *)value)->getAddressV4();
313 }
314 else
315 {
316 memcpy(entry->data.df_inetaddr.addr.v6, ((InetAddress *)value)->getAddressV6(), 16);
317 }
318 break;
5039dede
AK
319 default:
320 return NULL; // Invalid data type, unable to handle
321 }
8f238fd7
VK
322 entry->id = fieldId;
323 entry->data.fieldId = fieldId;
b368969c 324 entry->data.type = type;
712b2760
VK
325 if (isSigned)
326 entry->data.flags |= NXCP_MFF_SIGNED;
5c44534b
VK
327
328 // add or replace field
329 MessageField *curr;
8f238fd7 330 HASH_FIND_INT(m_fields, &fieldId, curr);
5c44534b 331 if (curr != NULL)
5039dede 332 {
5c44534b
VK
333 HASH_DEL(m_fields, curr);
334 free(curr);
5039dede 335 }
5c44534b 336 HASH_ADD_INT(m_fields, id, entry);
5039dede 337
b368969c 338 return (type == NXCP_DT_INT16) ? ((void *)((BYTE *)&entry->data + 6)) : ((void *)((BYTE *)&entry->data + 8));
8756ff63 339#undef __buffer
5039dede
AK
340}
341
56dd6325 342/**
712b2760 343 * Get field value
56dd6325 344 */
ca0a165b 345void *NXCPMessage::get(UINT32 fieldId, BYTE requiredType, BYTE *fieldType) const
5039dede 346{
b368969c 347 NXCP_MESSAGE_FIELD *field = find(fieldId);
5c44534b
VK
348 if (field == NULL)
349 return NULL; // No such field
5039dede 350
712b2760
VK
351 // Data type check exception - return IPv4 address as INT32 if requested
352 if ((requiredType == NXCP_DT_INT32) && (field->type == NXCP_DT_INETADDR) && (field->df_inetaddr.family == NXCP_AF_INET))
353 return &field->df_inetaddr.addr.v4;
354
5039dede 355 // Check data type
b368969c 356 if ((requiredType != 0xFF) && (field->type != requiredType))
5039dede
AK
357 return NULL;
358
8f238fd7 359 if (fieldType != NULL)
b368969c
VK
360 *fieldType = field->type;
361 return (field->type == NXCP_DT_INT16) ?
5c44534b
VK
362 ((void *)((BYTE *)field + 6)) :
363 ((void *)((BYTE *)field + 8));
5039dede
AK
364}
365
d6bbfa4e 366/**
712b2760 367 * Get 16 bit field as boolean
d6bbfa4e 368 */
ca0a165b 369bool NXCPMessage::getFieldAsBoolean(UINT32 fieldId) const
d6bbfa4e 370{
8f238fd7
VK
371 BYTE type;
372 void *value = (void *)get(fieldId, 0xFF, &type);
373 if (value == NULL)
374 return false;
375
376 switch(type)
377 {
b368969c 378 case NXCP_DT_INT16:
8f238fd7 379 return *((UINT16 *)value) ? true : false;
b368969c 380 case NXCP_DT_INT32:
8f238fd7 381 return *((UINT32 *)value) ? true : false;
b368969c 382 case NXCP_DT_INT64:
8f238fd7
VK
383 return *((UINT64 *)value) ? true : false;
384 default:
385 return false;
386 }
d6bbfa4e
VK
387}
388
56dd6325 389/**
4101571e
VK
390 * Get data type of message field.
391 *
392 * @return field type or -1 if field with given ID does not exist
393 */
ca0a165b 394int NXCPMessage::getFieldType(UINT32 fieldId) const
4101571e 395{
b368969c
VK
396 NXCP_MESSAGE_FIELD *field = find(fieldId);
397 return (field != NULL) ? (int)field->type : -1;
4101571e
VK
398}
399
400/**
401 * get signed integer field
402 */
ca0a165b 403INT32 NXCPMessage::getFieldAsInt32(UINT32 fieldId) const
4101571e 404{
b368969c 405 char *value = (char *)get(fieldId, NXCP_DT_INT32);
4101571e
VK
406 return (value != NULL) ? *((INT32 *)value) : 0;
407}
408
409/**
410 * get unsigned integer field
56dd6325 411 */
ca0a165b 412UINT32 NXCPMessage::getFieldAsUInt32(UINT32 fieldId) const
5039dede 413{
b368969c 414 void *value = get(fieldId, NXCP_DT_INT32);
8f238fd7 415 return (value != NULL) ? *((UINT32 *)value) : 0;
5039dede
AK
416}
417
967893bb 418/**
4101571e
VK
419 * get signed 16-bit integer field
420 */
ca0a165b 421INT16 NXCPMessage::getFieldAsInt16(UINT32 fieldId) const
4101571e 422{
b368969c 423 void *value = get(fieldId, NXCP_DT_INT16);
4101571e
VK
424 return (value != NULL) ? *((INT16 *)value) : 0;
425}
426
427/**
428 * get unsigned 16-bit integer variable
967893bb 429 */
ca0a165b 430UINT16 NXCPMessage::getFieldAsUInt16(UINT32 fieldId) const
5039dede 431{
b368969c
VK
432 void *value = get(fieldId, NXCP_DT_INT16);
433 return value ? *((WORD *)value) : 0;
5039dede
AK
434}
435
967893bb 436/**
4101571e 437 * get signed 64-bit integer field
967893bb 438 */
ca0a165b 439INT64 NXCPMessage::getFieldAsInt64(UINT32 fieldId) const
5039dede 440{
b368969c 441 void *value = get(fieldId, NXCP_DT_INT64);
4101571e 442 return (value != NULL) ? *((INT64 *)value) : 0;
5039dede
AK
443}
444
967893bb 445/**
4101571e 446 * get unsigned 64-bit integer field
967893bb 447 */
ca0a165b 448UINT64 NXCPMessage::getFieldAsUInt64(UINT32 fieldId) const
5039dede 449{
b368969c
VK
450 void *value = get(fieldId, NXCP_DT_INT64);
451 return value ? *((UINT64 *)value) : 0;
5039dede
AK
452}
453
56dd6325 454/**
4af351c7 455 * get 64-bit floating point variable
56dd6325 456 */
ca0a165b 457double NXCPMessage::getFieldAsDouble(UINT32 fieldId) const
5039dede 458{
b368969c 459 void *value = get(fieldId, NXCP_DT_FLOAT);
4101571e 460 return (value != NULL) ? *((double *)value) : 0;
5039dede
AK
461}
462
8f238fd7
VK
463/**
464 * get time_t field
465 */
ca0a165b 466time_t NXCPMessage::getFieldAsTime(UINT32 fieldId) const
8f238fd7
VK
467{
468 BYTE type;
469 void *value = (void *)get(fieldId, 0xFF, &type);
470 if (value == NULL)
471 return 0;
472
473 switch(type)
474 {
b368969c 475 case NXCP_DT_INT32:
8f238fd7 476 return (time_t)(*((UINT32 *)value));
b368969c 477 case NXCP_DT_INT64:
8f238fd7
VK
478 return (time_t)(*((UINT64 *)value));
479 default:
480 return false;
481 }
482}
483
56dd6325 484/**
712b2760
VK
485 * Get field as inet address
486 */
ca0a165b 487InetAddress NXCPMessage::getFieldAsInetAddress(UINT32 fieldId) const
712b2760
VK
488{
489 NXCP_MESSAGE_FIELD *f = find(fieldId);
490 if (f == NULL)
491 return InetAddress();
492
493 if (f->type == NXCP_DT_INETADDR)
494 {
495 InetAddress a =
496 (f->df_inetaddr.family == NXCP_AF_INET) ?
497 InetAddress(f->df_inetaddr.addr.v4) :
498 InetAddress(f->df_inetaddr.addr.v6);
499 a.setMaskBits(f->df_inetaddr.maskBits);
500 return a;
501 }
502 else if (f->type == NXCP_DT_INT32)
503 {
504 return InetAddress(f->df_uint32);
505 }
506 return InetAddress();
507}
508
509/**
510 * Get string field
511 * If buffer is NULL, memory block of required size will be allocated
512 * for result; if buffer is not NULL, entire result or part of it will
513 * be placed to buffer and pointer to buffer will be returned.
b368969c 514 * Note: bufferSize is buffer size in characters, not bytes!
56dd6325 515 */
ca0a165b 516TCHAR *NXCPMessage::getFieldAsString(UINT32 fieldId, TCHAR *buffer, size_t bufferSize) const
5039dede 517{
b368969c 518 if ((buffer != NULL) && (bufferSize == 0))
5039dede
AK
519 return NULL; // non-sense combination
520
b368969c
VK
521 TCHAR *str = NULL;
522 void *value = get(fieldId, NXCP_DT_STRING);
523 if (value != NULL)
5039dede 524 {
b368969c 525 if (buffer == NULL)
5039dede
AK
526 {
527#if defined(UNICODE) && defined(UNICODE_UCS4)
b368969c 528 str = (TCHAR *)malloc(*((UINT32 *)value) * 2 + 4);
5039dede 529#elif defined(UNICODE) && defined(UNICODE_UCS2)
b368969c 530 str = (TCHAR *)malloc(*((UINT32 *)value) + 2);
5039dede 531#else
b368969c 532 str = (TCHAR *)malloc(*((UINT32 *)value) / 2 + 1);
5039dede
AK
533#endif
534 }
535 else
536 {
b368969c 537 str = buffer;
5039dede
AK
538 }
539
b368969c 540 size_t length = (buffer == NULL) ? (*((UINT32 *)value) / 2) : min(*((UINT32 *)value) / 2, bufferSize - 1);
5039dede 541#if defined(UNICODE) && defined(UNICODE_UCS4)
b368969c 542 ucs2_to_ucs4((UCS2CHAR *)((BYTE *)value + 4), length, str, length + 1);
5039dede 543#elif defined(UNICODE) && defined(UNICODE_UCS2)
b368969c 544 memcpy(str, (BYTE *)value + 4, length * 2);
5039dede 545#else
b368969c 546 ucs2_to_mb((UCS2CHAR *)((BYTE *)value + 4), length, str, length + 1);
5039dede 547#endif
b368969c 548 str[length] = 0;
5039dede
AK
549 }
550 else
551 {
b368969c 552 if (buffer != NULL)
5039dede 553 {
b368969c 554 str = buffer;
934f53da 555 str[0] = 0;
5039dede
AK
556 }
557 }
934f53da 558 return str;
5039dede
AK
559}
560
35f836fe
VK
561#ifdef UNICODE
562
56dd6325 563/**
4af351c7 564 * get variable as multibyte string
56dd6325 565 */
ca0a165b 566char *NXCPMessage::getFieldAsMBString(UINT32 fieldId, char *buffer, size_t bufferSize) const
35f836fe 567{
b368969c 568 if ((buffer != NULL) && (bufferSize == 0))
35f836fe
VK
569 return NULL; // non-sense combination
570
b368969c
VK
571 char *str = NULL;
572 void *value = get(fieldId, NXCP_DT_STRING);
573 if (value != NULL)
35f836fe 574 {
b368969c 575 if (buffer == NULL)
35f836fe 576 {
b368969c 577 str = (char *)malloc(*((UINT32 *)value) / 2 + 1);
35f836fe
VK
578 }
579 else
580 {
b368969c 581 str = buffer;
35f836fe
VK
582 }
583
b368969c
VK
584 size_t length = (buffer == NULL) ? (*((UINT32 *)value) / 2) : min(*((UINT32 *)value) / 2, bufferSize - 1);
585 ucs2_to_mb((UCS2CHAR *)((BYTE *)value + 4), (int)length, str, (int)length + 1);
586 str[length] = 0;
35f836fe
VK
587 }
588 else
589 {
b368969c 590 if (buffer != NULL)
35f836fe 591 {
b368969c 592 str = buffer;
934f53da 593 str[0] = 0;
35f836fe
VK
594 }
595 }
934f53da 596 return str;
35f836fe
VK
597}
598
599#else
600
56dd6325 601/**
5c44534b 602 * get field as multibyte string
56dd6325 603 */
ca0a165b 604char *NXCPMessage::getFieldAsMBString(UINT32 fieldId, char *buffer, size_t bufferSize) const
35f836fe 605{
b368969c 606 return getFieldAsString(fieldId, buffer, bufferSize);
35f836fe
VK
607}
608
609#endif
610
56dd6325 611/**
5c44534b 612 * get field as UTF-8 string
56dd6325 613 */
ca0a165b 614char *NXCPMessage::getFieldAsUtf8String(UINT32 fieldId, char *buffer, size_t bufferSize) const
35f836fe 615{
b368969c 616 if ((buffer != NULL) && (bufferSize == 0))
35f836fe
VK
617 return NULL; // non-sense combination
618
b368969c
VK
619 char *str = NULL;
620 void *value = get(fieldId, NXCP_DT_STRING);
621 if (value != NULL)
35f836fe 622 {
b368969c
VK
623 int outSize;
624 if (buffer == NULL)
35f836fe
VK
625 {
626 // Assume worst case scenario - 3 bytes per character
b368969c
VK
627 outSize = (int)(*((UINT32 *)value) + *((UINT32 *)value) / 2 + 1);
628 str = (char *)malloc(outSize);
35f836fe
VK
629 }
630 else
631 {
b368969c
VK
632 outSize = (int)bufferSize;
633 str = buffer;
35f836fe
VK
634 }
635
b368969c 636 size_t length = *((UINT32 *)value) / 2;
35f836fe 637#ifdef UNICODE_UCS2
b368969c 638 int cc = WideCharToMultiByte(CP_UTF8, 0, (WCHAR *)((BYTE *)value + 4), (int)length, str, outSize - 1, NULL, NULL);
35f836fe 639#else
b368969c 640 int cc = ucs2_to_utf8((UCS2CHAR *)((BYTE *)value + 4), (int)length, str, outSize - 1);
35f836fe 641#endif
934f53da 642 str[cc] = 0;
35f836fe
VK
643 }
644 else
645 {
b368969c 646 if (buffer != NULL)
35f836fe 647 {
b368969c 648 str = buffer;
934f53da 649 str[0] = 0;
35f836fe
VK
650 }
651 }
934f53da 652 return str;
35f836fe
VK
653}
654
5c44534b
VK
655/**
656 * get binary (byte array) field
b368969c 657 * Result will be placed to the buffer provided (no more than bufferSize bytes,
5c44534b
VK
658 * and actual size of data will be returned
659 * If pBuffer is NULL, just actual data length is returned
660 */
ca0a165b 661UINT32 NXCPMessage::getFieldAsBinary(UINT32 fieldId, BYTE *pBuffer, size_t bufferSize) const
5039dede 662{
9a68ca24 663 UINT32 size;
b368969c 664 void *value = get(fieldId, NXCP_DT_BINARY);
9a68ca24 665 if (value != NULL)
5039dede 666 {
9a68ca24 667 size = *((UINT32 *)value);
5039dede 668 if (pBuffer != NULL)
b368969c 669 memcpy(pBuffer, (BYTE *)value + 4, min(bufferSize, size));
5039dede
AK
670 }
671 else
672 {
9a68ca24 673 size = 0;
5039dede 674 }
9a68ca24 675 return size;
5039dede
AK
676}
677
ac14e3e6
VK
678/**
679 * get binary (byte array) field
680 * Returns pointer to internal buffer or NULL if field not found
681 * Data length set in size parameter.
682 */
ca0a165b 683const BYTE *NXCPMessage::getBinaryFieldPtr(UINT32 fieldId, size_t *size) const
ac14e3e6
VK
684{
685 BYTE *data;
b368969c 686 void *value = get(fieldId, NXCP_DT_BINARY);
ac14e3e6
VK
687 if (value != NULL)
688 {
689 *size = (size_t)(*((UINT32 *)value));
690 data = (BYTE *)value + 4;
691 }
692 else
693 {
694 *size = 0;
695 data = NULL;
696 }
697 return data;
698}
699
de4af576
VK
700/**
701 * Get field as GUID
702 * Returns NULL GUID on error
703 */
ca0a165b 704uuid NXCPMessage::getFieldAsGUID(UINT32 fieldId) const
de4af576
VK
705{
706 NXCP_MESSAGE_FIELD *f = find(fieldId);
707 if (f == NULL)
708 return uuid::NULL_UUID;
709
710 if ((f->type == NXCP_DT_BINARY) && (f->df_binary.length == UUID_LENGTH))
711 {
712 return uuid(f->df_binary.value);
713 }
714 else if (f->type == NXCP_DT_STRING)
715 {
716 TCHAR buffer[64] = _T("");
717 getFieldAsString(fieldId, buffer, 64);
718 return uuid::parse(buffer);
719 }
720 return uuid::NULL_UUID;
721}
722
5c44534b
VK
723/**
724 * Build protocol message ready to be send over the wire
725 */
c4b35dc7 726NXCP_MESSAGE *NXCPMessage::createMessage() const
5039dede 727{
5039dede 728 // Calculate message size
6be0a20b 729 size_t size = NXCP_HEADER_SIZE;
5c44534b 730 UINT32 fieldCount = 0;
6be0a20b 731 if (m_flags & MF_BINARY)
5039dede 732 {
6be0a20b
VK
733 size += m_dataSize;
734 fieldCount = (UINT32)m_dataSize;
735 size += (8 - (size % 8)) & 7;
5039dede 736 }
6be0a20b
VK
737 else
738 {
739 MessageField *entry, *tmp;
740 HASH_ITER(hh, m_fields, entry, tmp)
741 {
742 size_t fieldSize = CalculateFieldSize(&entry->data, false);
743 if (m_version >= 2)
744 size += fieldSize + ((8 - (fieldSize % 8)) & 7);
745 else
746 size += fieldSize;
747 fieldCount++;
748 }
5039dede 749
6be0a20b
VK
750 // Message should be aligned to 8 bytes boundary
751 // This is always the case starting from version 2 because
752 // all fields are padded to 8 bytes boundary
753 if (m_version < 2)
754 size += (8 - (size % 8)) & 7;
755 }
5039dede
AK
756
757 // Create message
b368969c
VK
758 NXCP_MESSAGE *msg = (NXCP_MESSAGE *)malloc(size);
759 memset(msg, 0, size);
760 msg->code = htons(m_code);
761 msg->flags = htons(m_flags);
762 msg->size = htonl((UINT32)size);
763 msg->id = htonl(m_id);
764 msg->numFields = htonl(fieldCount);
5039dede
AK
765
766 // Fill data fields
6be0a20b 767 if (m_flags & MF_BINARY)
5039dede 768 {
b368969c 769 memcpy(msg->fields, m_data, m_dataSize);
6be0a20b
VK
770 }
771 else
772 {
b368969c 773 NXCP_MESSAGE_FIELD *field = (NXCP_MESSAGE_FIELD *)((char *)msg + NXCP_HEADER_SIZE);
6be0a20b
VK
774 MessageField *entry, *tmp;
775 HASH_ITER(hh, m_fields, entry, tmp)
5039dede 776 {
6be0a20b
VK
777 size_t fieldSize = CalculateFieldSize(&entry->data, false);
778 memcpy(field, &entry->data, fieldSize);
779
780 // Convert numeric values to network format
781 field->fieldId = htonl(field->fieldId);
b368969c 782 switch(field->type)
6be0a20b 783 {
b368969c 784 case NXCP_DT_INT32:
6be0a20b
VK
785 field->df_int32 = htonl(field->df_int32);
786 break;
b368969c 787 case NXCP_DT_INT64:
6be0a20b
VK
788 field->df_int64 = htonq(field->df_int64);
789 break;
b368969c 790 case NXCP_DT_INT16:
6be0a20b
VK
791 field->df_int16 = htons(field->df_int16);
792 break;
b368969c 793 case NXCP_DT_FLOAT:
6be0a20b
VK
794 field->df_real = htond(field->df_real);
795 break;
b368969c 796 case NXCP_DT_STRING:
5039dede 797#if !(WORDS_BIGENDIAN)
6be0a20b 798 {
b368969c
VK
799 for(UINT32 i = 0; i < field->df_string.length / 2; i++)
800 field->df_string.value[i] = htons(field->df_string.value[i]);
801 field->df_string.length = htonl(field->df_string.length);
6be0a20b 802 }
5039dede 803#endif
6be0a20b 804 break;
b368969c
VK
805 case NXCP_DT_BINARY:
806 field->df_string.length = htonl(field->df_string.length);
6be0a20b 807 break;
712b2760
VK
808 case NXCP_DT_INETADDR:
809 if (field->df_inetaddr.family == NXCP_AF_INET)
810 {
811 field->df_inetaddr.addr.v4 = htonl(field->df_inetaddr.addr.v4);
812 }
813 break;
6be0a20b 814 }
5039dede 815
6be0a20b 816 if (m_version >= 2)
b368969c 817 field = (NXCP_MESSAGE_FIELD *)((char *)field + fieldSize + ((8 - (fieldSize % 8)) & 7));
6be0a20b 818 else
b368969c 819 field = (NXCP_MESSAGE_FIELD *)((char *)field + fieldSize);
6be0a20b 820 }
5039dede 821 }
b368969c 822 return msg;
5039dede
AK
823}
824
4af351c7
VK
825/**
826 * Delete all variables
827 */
b368969c 828void NXCPMessage::deleteAllFields()
5039dede 829{
5c44534b
VK
830 MessageField *entry, *tmp;
831 HASH_ITER(hh, m_fields, entry, tmp)
5039dede 832 {
5c44534b
VK
833 HASH_DEL(m_fields, entry);
834 free(entry);
5039dede
AK
835 }
836}
837
35f836fe
VK
838#ifdef UNICODE
839
4af351c7
VK
840/**
841 * set variable from multibyte string
842 */
b368969c 843void NXCPMessage::setFieldFromMBString(UINT32 fieldId, const char *value)
35f836fe 844{
b368969c
VK
845 WCHAR *wcValue = WideStringFromMBString(value);
846 set(fieldId, NXCP_DT_STRING, wcValue);
35f836fe
VK
847 free(wcValue);
848}
849
850#endif
851
fb9441ee 852/**
9a68ca24 853 * set binary field to an array of UINT32s
fb9441ee 854 */
14149881 855void NXCPMessage::setFieldFromInt32Array(UINT32 fieldId, size_t numElements, const UINT32 *elements)
5039dede 856{
14149881 857 UINT32 *pdwBuffer = (UINT32 *)set(fieldId, NXCP_DT_BINARY, elements, false, numElements * sizeof(UINT32));
9a68ca24
VK
858 if (pdwBuffer != NULL)
859 {
860 pdwBuffer++; // First UINT32 is a length field
14149881 861 for(size_t i = 0; i < numElements; i++) // Convert UINT32s to network byte order
9a68ca24
VK
862 pdwBuffer[i] = htonl(pdwBuffer[i]);
863 }
864}
5039dede 865
9a68ca24
VK
866/**
867 * set binary field to an array of UINT32s
868 */
b368969c 869void NXCPMessage::setFieldFromInt32Array(UINT32 fieldId, IntegerArray<UINT32> *data)
9a68ca24 870{
712b2760 871 UINT32 *pdwBuffer = (UINT32 *)set(fieldId, NXCP_DT_BINARY, data->getBuffer(), false, data->size() * sizeof(UINT32));
5039dede
AK
872 if (pdwBuffer != NULL)
873 {
967893bb 874 pdwBuffer++; // First UINT32 is a length field
9a68ca24 875 for(int i = 0; i < data->size(); i++) // Convert UINT32s to network byte order
5039dede
AK
876 pdwBuffer[i] = htonl(pdwBuffer[i]);
877 }
878}
879
fb9441ee 880/**
9a68ca24 881 * get binary field as an array of 32 bit unsigned integers
fb9441ee 882 */
ca0a165b 883UINT32 NXCPMessage::getFieldAsInt32Array(UINT32 fieldId, UINT32 numElements, UINT32 *buffer) const
5039dede 884{
b368969c 885 UINT32 size = getFieldAsBinary(fieldId, (BYTE *)buffer, numElements * sizeof(UINT32));
9a68ca24
VK
886 size /= sizeof(UINT32); // Convert bytes to elements
887 for(UINT32 i = 0; i < size; i++)
888 buffer[i] = ntohl(buffer[i]);
889 return size;
890}
5039dede 891
9a68ca24
VK
892/**
893 * get binary field as an array of 32 bit unsigned integers
894 */
ca0a165b 895UINT32 NXCPMessage::getFieldAsInt32Array(UINT32 fieldId, IntegerArray<UINT32> *data) const
9a68ca24
VK
896{
897 data->clear();
898
b368969c 899 UINT32 *value = (UINT32 *)get(fieldId, NXCP_DT_BINARY);
9a68ca24
VK
900 if (value != NULL)
901 {
ed83fef5 902 UINT32 size = *value / sizeof(UINT32);
9a68ca24
VK
903 value++;
904 for(UINT32 i = 0; i < size; i++)
905 {
906 data->add(ntohl(*value));
907 value++;
908 }
909 }
910 return (UINT32)data->size();
5039dede
AK
911}
912
5c44534b
VK
913/**
914 * set binary field from file
915 */
b368969c 916bool NXCPMessage::setFieldFromFile(UINT32 fieldId, const TCHAR *pszFileName)
5039dede
AK
917{
918 FILE *pFile;
919 BYTE *pBuffer;
b368969c 920 UINT32 size;
5944946e 921 bool bResult = false;
5039dede 922
b368969c 923 size = (UINT32)FileSize(pszFileName);
5039dede
AK
924 pFile = _tfopen(pszFileName, _T("rb"));
925 if (pFile != NULL)
926 {
712b2760 927 pBuffer = (BYTE *)set(fieldId, NXCP_DT_BINARY, NULL, false, size);
5039dede
AK
928 if (pBuffer != NULL)
929 {
b368969c 930 if (fread(pBuffer + sizeof(UINT32), 1, size, pFile) == size)
5944946e 931 bResult = true;
5039dede
AK
932 }
933 fclose(pFile);
934 }
935 return bResult;
936}
937
934f53da
VK
938/**
939 * Get string from field
940 */
941static TCHAR *GetStringFromField(void *df)
942{
943#if defined(UNICODE) && defined(UNICODE_UCS4)
944 TCHAR *str = (TCHAR *)malloc(*((UINT32 *)df) * 2 + 4);
945#elif defined(UNICODE) && defined(UNICODE_UCS2)
946 TCHAR *str = (TCHAR *)malloc(*((UINT32 *)df) + 2);
947#else
948 TCHAR *str = (TCHAR *)malloc(*((UINT32 *)df) / 2 + 1);
949#endif
950 int len = (int)(*((UINT32 *)df) / 2);
951#if defined(UNICODE) && defined(UNICODE_UCS4)
952 ucs2_to_ucs4((UCS2CHAR *)((BYTE *)df + 4), len, str, len + 1);
953#elif defined(UNICODE) && defined(UNICODE_UCS2)
954 memcpy(str, (BYTE *)df + 4, len * 2);
955#else
956 ucs2_to_mb((UCS2CHAR *)((BYTE *)df + 4), len, str, len + 1);
957#endif
958 str[len] = 0;
959 return str;
960}
961
962/**
963 * Dump NXCP message
964 */
ca0a165b 965String NXCPMessage::dump(const NXCP_MESSAGE *msg, int version)
934f53da
VK
966{
967 String out;
968 int i;
969 TCHAR *str, buffer[128];
970
b368969c
VK
971 WORD flags = ntohs(msg->flags);
972 WORD code = ntohs(msg->code);
973 UINT32 id = ntohl(msg->id);
974 UINT32 size = ntohl(msg->size);
975 int numFields = (int)ntohl(msg->numFields);
934f53da
VK
976
977 // Dump raw message
978 for(i = 0; i < (int)size; i += 16)
979 {
b368969c 980 BinToStr(((BYTE *)msg) + i, min(16, size - i), buffer);
4e0e77e6 981 out.appendFormattedString(_T(" ** %s\n"), buffer);
934f53da
VK
982 }
983
984 // header
4e0e77e6 985 out.appendFormattedString(_T(" ** code=0x%04X (%s) flags=0x%04X id=%d size=%d numFields=%d\n"),
934f53da
VK
986 code, NXCPMessageCodeName(code, buffer), flags, id, size, numFields);
987 if (flags & MF_BINARY)
988 {
989 out += _T(" ** binary message\n");
990 return out;
991 }
992
993 // Parse data fields
6be0a20b 994 size_t pos = NXCP_HEADER_SIZE;
934f53da
VK
995 for(int f = 0; f < numFields; f++)
996 {
b368969c 997 NXCP_MESSAGE_FIELD *field = (NXCP_MESSAGE_FIELD *)(((BYTE *)msg) + pos);
934f53da
VK
998
999 // Validate position inside message
1000 if (pos > size - 8)
1001 {
1002 out += _T(" ** message format error (pos > size - 8)\n");
1003 break;
1004 }
1005 if ((pos > size - 12) &&
b368969c 1006 ((field->type == NXCP_DT_STRING) || (field->type == NXCP_DT_BINARY)))
934f53da 1007 {
4e0e77e6 1008 out.appendFormattedString(_T(" ** message format error (pos > size - 8 and field type %d)\n"), (int)field->type);
934f53da
VK
1009 break;
1010 }
1011
1012 // Calculate and validate field size
818ebb6a 1013 size_t fieldSize = CalculateFieldSize(field, TRUE);
934f53da
VK
1014 if (pos + fieldSize > size)
1015 {
1016 out += _T(" ** message format error (invalid field size)\n");
1017 break;
1018 }
1019
1020 // Create new entry
b368969c 1021 NXCP_MESSAGE_FIELD *convertedField = (NXCP_MESSAGE_FIELD *)malloc(fieldSize);
934f53da
VK
1022 memcpy(convertedField, field, fieldSize);
1023
1024 // Convert numeric values to host format
8f238fd7 1025 convertedField->fieldId = ntohl(convertedField->fieldId);
b368969c 1026 switch(field->type)
934f53da 1027 {
b368969c 1028 case NXCP_DT_INT32:
934f53da 1029 convertedField->df_int32 = ntohl(convertedField->df_int32);
845303de 1030 out.appendFormattedString(_T(" ** [%6d] INT32 %d\n"), (int)convertedField->fieldId, convertedField->df_int32);
934f53da 1031 break;
b368969c 1032 case NXCP_DT_INT64:
934f53da 1033 convertedField->df_int64 = ntohq(convertedField->df_int64);
845303de 1034 out.appendFormattedString(_T(" ** [%6d] INT64 ") INT64_FMT _T("\n"), (int)convertedField->fieldId, convertedField->df_int64);
934f53da 1035 break;
b368969c 1036 case NXCP_DT_INT16:
934f53da 1037 convertedField->df_int16 = ntohs(convertedField->df_int16);
845303de 1038 out.appendFormattedString(_T(" ** [%6d] INT16 %d\n"), (int)convertedField->fieldId, (int)convertedField->df_int16);
934f53da 1039 break;
b368969c 1040 case NXCP_DT_FLOAT:
934f53da 1041 convertedField->df_real = ntohd(convertedField->df_real);
845303de 1042 out.appendFormattedString(_T(" ** [%6d] FLOAT %f\n"), (int)convertedField->fieldId, convertedField->df_real);
934f53da 1043 break;
b368969c 1044 case NXCP_DT_STRING:
934f53da 1045#if !(WORDS_BIGENDIAN)
b368969c
VK
1046 convertedField->df_string.length = ntohl(convertedField->df_string.length);
1047 for(i = 0; i < (int)convertedField->df_string.length / 2; i++)
1048 convertedField->df_string.value[i] = ntohs(convertedField->df_string.value[i]);
934f53da
VK
1049#endif
1050 str = GetStringFromField((BYTE *)convertedField + 8);
845303de 1051 out.appendFormattedString(_T(" ** [%6d] STRING \"%s\"\n"), (int)convertedField->fieldId, str);
934f53da
VK
1052 free(str);
1053 break;
b368969c
VK
1054 case NXCP_DT_BINARY:
1055 convertedField->df_string.length = ntohl(convertedField->df_string.length);
845303de 1056 out.appendFormattedString(_T(" ** [%6d] BINARY len=%d\n"), (int)convertedField->fieldId, (int)convertedField->df_string.length);
934f53da 1057 break;
cb33b0e2
VK
1058 case NXCP_DT_INETADDR:
1059 {
1060 InetAddress a =
1061 (convertedField->df_inetaddr.family == NXCP_AF_INET) ?
1062 InetAddress(ntohl(convertedField->df_inetaddr.addr.v4)) :
1063 InetAddress(convertedField->df_inetaddr.addr.v6);
1064 a.setMaskBits(convertedField->df_inetaddr.maskBits);
1065 out.appendFormattedString(_T(" ** [%6d] INETADDR %s\n"), (int)convertedField->fieldId, (const TCHAR *)a.toString());
1066 }
1067 break;
934f53da 1068 default:
4e0e77e6 1069 out.appendFormattedString(_T(" ** [%6d] unknown type %d\n"), (int)convertedField->fieldId, (int)field->type);
934f53da
VK
1070 break;
1071 }
1072 free(convertedField);
1073
6be0a20b 1074 // Starting from version 2, all fields should be 8-byte aligned
934f53da
VK
1075 if (version >= 2)
1076 pos += fieldSize + ((8 - (fieldSize % 8)) & 7);
1077 else
1078 pos += fieldSize;
1079 }
1080
1081 return out;
1082}