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