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