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