fixed Windows build errors
[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 m_flags = ntohs(msg->flags);
134 m_code = ntohs(msg->code);
135 m_id = ntohl(msg->id);
136 m_version = version;
137 m_fields = NULL;
138
139 // Parse data fields
140 if (m_flags & MF_BINARY)
141 {
142 m_dataSize = (size_t)ntohl(msg->numFields);
143 m_data = (BYTE *)nx_memdup(msg->fields, m_dataSize);
144 }
145 else
146 {
147 m_data = NULL;
148 m_dataSize = 0;
149
150 int fieldCount = (int)ntohl(msg->numFields);
151 size_t size = (size_t)ntohl(msg->size);
152 size_t pos = NXCP_HEADER_SIZE;
153 for(int f = 0; f < fieldCount; f++)
154 {
155 NXCP_MESSAGE_FIELD *field = (NXCP_MESSAGE_FIELD *)(((BYTE *)msg) + pos);
156
157 // Validate position inside message
158 if (pos > size - 8)
159 break;
160 if ((pos > size - 12) &&
161 ((field->type == NXCP_DT_STRING) || (field->type == NXCP_DT_BINARY)))
162 break;
163
164 // Calculate and validate variable size
165 size_t fieldSize = CalculateFieldSize(field, true);
166 if (pos + fieldSize > size)
167 break;
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);
176 switch(field->type)
177 {
178 case NXCP_DT_INT32:
179 entry->data.df_int32 = ntohl(entry->data.df_int32);
180 break;
181 case NXCP_DT_INT64:
182 entry->data.df_int64 = ntohq(entry->data.df_int64);
183 break;
184 case NXCP_DT_INT16:
185 entry->data.df_int16 = ntohs(entry->data.df_int16);
186 break;
187 case NXCP_DT_FLOAT:
188 entry->data.df_real = ntohd(entry->data.df_real);
189 break;
190 case NXCP_DT_STRING:
191 #if !(WORDS_BIGENDIAN)
192 entry->data.df_string.length = ntohl(entry->data.df_string.length);
193 bswap_array_16(entry->data.df_string.value, entry->data.df_string.length / 2);
194 #endif
195 break;
196 case NXCP_DT_BINARY:
197 entry->data.df_string.length = ntohl(entry->data.df_string.length);
198 break;
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;
205 }
206
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 }
215
216 }
217 }
218
219 /**
220 * Destructor for NXCPMessage
221 */
222 NXCPMessage::~NXCPMessage()
223 {
224 deleteAllFields();
225 safe_free(m_data);
226 }
227
228 /**
229 * Find field by ID
230 */
231 NXCP_MESSAGE_FIELD *NXCPMessage::find(UINT32 fieldId) const
232 {
233 MessageField *entry;
234 HASH_FIND_INT(m_fields, &fieldId, entry);
235 return (entry != NULL) ? &entry->data : NULL;
236 }
237
238 /**
239 * set variable
240 * Argument size (data size) contains data length in bytes for DT_BINARY type
241 * and maximum number of characters for DT_STRING type (0 means no limit)
242 */
243 void *NXCPMessage::set(UINT32 fieldId, BYTE type, const void *value, bool isSigned, size_t size)
244 {
245 if (m_flags & MF_BINARY)
246 return NULL;
247
248 size_t length;
249 #if defined(UNICODE_UCS2) && defined(UNICODE)
250 #define __buffer value
251 #else
252 UCS2CHAR *__buffer;
253 #endif
254
255 // Create entry
256 MessageField *entry;
257 switch(type)
258 {
259 case NXCP_DT_INT32:
260 entry = CreateMessageField(12);
261 entry->data.df_int32 = *((const UINT32 *)value);
262 break;
263 case NXCP_DT_INT16:
264 entry = CreateMessageField(8);
265 entry->data.df_int16 = *((const WORD *)value);
266 break;
267 case NXCP_DT_INT64:
268 entry = CreateMessageField(16);
269 entry->data.df_int64 = *((const UINT64 *)value);
270 break;
271 case NXCP_DT_FLOAT:
272 entry = CreateMessageField(16);
273 entry->data.df_real = *((const double *)value);
274 break;
275 case NXCP_DT_STRING:
276 #ifdef UNICODE
277 length = _tcslen((const TCHAR *)value);
278 if ((size > 0) && (length > size))
279 length = size;
280 #ifndef UNICODE_UCS2 /* assume UNICODE_UCS4 */
281 __buffer = (UCS2CHAR *)malloc(length * 2 + 2);
282 ucs4_to_ucs2((WCHAR *)value, length, __buffer, length + 1);
283 #endif
284 #else /* not UNICODE */
285 __buffer = UCS2StringFromMBString((const char *)value);
286 length = (UINT32)ucs2_strlen(__buffer);
287 if ((size > 0) && (length > size))
288 length = size;
289 #endif
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);
293 #if !defined(UNICODE_UCS2) || !defined(UNICODE)
294 free(__buffer);
295 #endif
296 break;
297 case NXCP_DT_BINARY:
298 entry = CreateMessageField(12 + size);
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);
302 break;
303 case NXCP_DT_INETADDR:
304 entry = CreateMessageField(32);
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);
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 }
313 else if (((InetAddress *)value)->getFamily() == AF_INET6)
314 {
315 memcpy(entry->data.df_inetaddr.addr.v6, ((InetAddress *)value)->getAddressV6(), 16);
316 }
317 break;
318 default:
319 return NULL; // Invalid data type, unable to handle
320 }
321 entry->id = fieldId;
322 entry->data.fieldId = fieldId;
323 entry->data.type = type;
324 if (isSigned)
325 entry->data.flags |= NXCP_MFF_SIGNED;
326
327 // add or replace field
328 MessageField *curr;
329 HASH_FIND_INT(m_fields, &fieldId, curr);
330 if (curr != NULL)
331 {
332 HASH_DEL(m_fields, curr);
333 free(curr);
334 }
335 HASH_ADD_INT(m_fields, id, entry);
336
337 return (type == NXCP_DT_INT16) ? ((void *)((BYTE *)&entry->data + 6)) : ((void *)((BYTE *)&entry->data + 8));
338 #undef __buffer
339 }
340
341 /**
342 * Get field value
343 */
344 void *NXCPMessage::get(UINT32 fieldId, BYTE requiredType, BYTE *fieldType) const
345 {
346 NXCP_MESSAGE_FIELD *field = find(fieldId);
347 if (field == NULL)
348 return NULL; // No such field
349
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
354 // Check data type
355 if ((requiredType != 0xFF) && (field->type != requiredType))
356 return NULL;
357
358 if (fieldType != NULL)
359 *fieldType = field->type;
360 return (field->type == NXCP_DT_INT16) ?
361 ((void *)((BYTE *)field + 6)) :
362 ((void *)((BYTE *)field + 8));
363 }
364
365 /**
366 * Get 16 bit field as boolean
367 */
368 bool NXCPMessage::getFieldAsBoolean(UINT32 fieldId) const
369 {
370 BYTE type;
371 void *value = (void *)get(fieldId, 0xFF, &type);
372 if (value == NULL)
373 return false;
374
375 switch(type)
376 {
377 case NXCP_DT_INT16:
378 return *((UINT16 *)value) ? true : false;
379 case NXCP_DT_INT32:
380 return *((UINT32 *)value) ? true : false;
381 case NXCP_DT_INT64:
382 return *((UINT64 *)value) ? true : false;
383 default:
384 return false;
385 }
386 }
387
388 /**
389 * Get data type of message field.
390 *
391 * @return field type or -1 if field with given ID does not exist
392 */
393 int NXCPMessage::getFieldType(UINT32 fieldId) const
394 {
395 NXCP_MESSAGE_FIELD *field = find(fieldId);
396 return (field != NULL) ? (int)field->type : -1;
397 }
398
399 /**
400 * get signed integer field
401 */
402 INT32 NXCPMessage::getFieldAsInt32(UINT32 fieldId) const
403 {
404 char *value = (char *)get(fieldId, NXCP_DT_INT32);
405 return (value != NULL) ? *((INT32 *)value) : 0;
406 }
407
408 /**
409 * get unsigned integer field
410 */
411 UINT32 NXCPMessage::getFieldAsUInt32(UINT32 fieldId) const
412 {
413 void *value = get(fieldId, NXCP_DT_INT32);
414 return (value != NULL) ? *((UINT32 *)value) : 0;
415 }
416
417 /**
418 * get signed 16-bit integer field
419 */
420 INT16 NXCPMessage::getFieldAsInt16(UINT32 fieldId) const
421 {
422 void *value = get(fieldId, NXCP_DT_INT16);
423 return (value != NULL) ? *((INT16 *)value) : 0;
424 }
425
426 /**
427 * get unsigned 16-bit integer variable
428 */
429 UINT16 NXCPMessage::getFieldAsUInt16(UINT32 fieldId) const
430 {
431 void *value = get(fieldId, NXCP_DT_INT16);
432 return value ? *((WORD *)value) : 0;
433 }
434
435 /**
436 * get signed 64-bit integer field
437 */
438 INT64 NXCPMessage::getFieldAsInt64(UINT32 fieldId) const
439 {
440 void *value = get(fieldId, NXCP_DT_INT64);
441 return (value != NULL) ? *((INT64 *)value) : 0;
442 }
443
444 /**
445 * get unsigned 64-bit integer field
446 */
447 UINT64 NXCPMessage::getFieldAsUInt64(UINT32 fieldId) const
448 {
449 void *value = get(fieldId, NXCP_DT_INT64);
450 return value ? *((UINT64 *)value) : 0;
451 }
452
453 /**
454 * get 64-bit floating point variable
455 */
456 double NXCPMessage::getFieldAsDouble(UINT32 fieldId) const
457 {
458 void *value = get(fieldId, NXCP_DT_FLOAT);
459 return (value != NULL) ? *((double *)value) : 0;
460 }
461
462 /**
463 * get time_t field
464 */
465 time_t NXCPMessage::getFieldAsTime(UINT32 fieldId) const
466 {
467 BYTE type;
468 void *value = (void *)get(fieldId, 0xFF, &type);
469 if (value == NULL)
470 return 0;
471
472 switch(type)
473 {
474 case NXCP_DT_INT32:
475 return (time_t)(*((UINT32 *)value));
476 case NXCP_DT_INT64:
477 return (time_t)(*((UINT64 *)value));
478 default:
479 return false;
480 }
481 }
482
483 /**
484 * Get field as inet address
485 */
486 InetAddress NXCPMessage::getFieldAsInetAddress(UINT32 fieldId) const
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) :
497 ((f->df_inetaddr.family == NXCP_AF_INET6) ? InetAddress(f->df_inetaddr.addr.v6) : InetAddress());
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.
513 * Note: bufferSize is buffer size in characters, not bytes!
514 */
515 TCHAR *NXCPMessage::getFieldAsString(UINT32 fieldId, TCHAR *buffer, size_t bufferSize) const
516 {
517 if ((buffer != NULL) && (bufferSize == 0))
518 return NULL; // non-sense combination
519
520 TCHAR *str = NULL;
521 void *value = get(fieldId, NXCP_DT_STRING);
522 if (value != NULL)
523 {
524 if (buffer == NULL)
525 {
526 #if defined(UNICODE) && defined(UNICODE_UCS4)
527 str = (TCHAR *)malloc(*((UINT32 *)value) * 2 + 4);
528 #elif defined(UNICODE) && defined(UNICODE_UCS2)
529 str = (TCHAR *)malloc(*((UINT32 *)value) + 2);
530 #else
531 str = (TCHAR *)malloc(*((UINT32 *)value) / 2 + 1);
532 #endif
533 }
534 else
535 {
536 str = buffer;
537 }
538
539 size_t length = (buffer == NULL) ? (*((UINT32 *)value) / 2) : min(*((UINT32 *)value) / 2, bufferSize - 1);
540 #if defined(UNICODE) && defined(UNICODE_UCS4)
541 ucs2_to_ucs4((UCS2CHAR *)((BYTE *)value + 4), length, str, length + 1);
542 #elif defined(UNICODE) && defined(UNICODE_UCS2)
543 memcpy(str, (BYTE *)value + 4, length * 2);
544 #else
545 ucs2_to_mb((UCS2CHAR *)((BYTE *)value + 4), length, str, length + 1);
546 #endif
547 str[length] = 0;
548 }
549 else
550 {
551 if (buffer != NULL)
552 {
553 str = buffer;
554 str[0] = 0;
555 }
556 }
557 return str;
558 }
559
560 #ifdef UNICODE
561
562 /**
563 * get variable as multibyte string
564 */
565 char *NXCPMessage::getFieldAsMBString(UINT32 fieldId, char *buffer, size_t bufferSize) const
566 {
567 if ((buffer != NULL) && (bufferSize == 0))
568 return NULL; // non-sense combination
569
570 char *str = NULL;
571 void *value = get(fieldId, NXCP_DT_STRING);
572 if (value != NULL)
573 {
574 if (buffer == NULL)
575 {
576 str = (char *)malloc(*((UINT32 *)value) / 2 + 1);
577 }
578 else
579 {
580 str = buffer;
581 }
582
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;
586 }
587 else
588 {
589 if (buffer != NULL)
590 {
591 str = buffer;
592 str[0] = 0;
593 }
594 }
595 return str;
596 }
597
598 #else
599
600 /**
601 * get field as multibyte string
602 */
603 char *NXCPMessage::getFieldAsMBString(UINT32 fieldId, char *buffer, size_t bufferSize) const
604 {
605 return getFieldAsString(fieldId, buffer, bufferSize);
606 }
607
608 #endif
609
610 /**
611 * get field as UTF-8 string
612 */
613 char *NXCPMessage::getFieldAsUtf8String(UINT32 fieldId, char *buffer, size_t bufferSize) const
614 {
615 if ((buffer != NULL) && (bufferSize == 0))
616 return NULL; // non-sense combination
617
618 char *str = NULL;
619 void *value = get(fieldId, NXCP_DT_STRING);
620 if (value != NULL)
621 {
622 int outSize;
623 if (buffer == NULL)
624 {
625 // Assume worst case scenario - 3 bytes per character
626 outSize = (int)(*((UINT32 *)value) + *((UINT32 *)value) / 2 + 1);
627 str = (char *)malloc(outSize);
628 }
629 else
630 {
631 outSize = (int)bufferSize;
632 str = buffer;
633 }
634
635 size_t length = *((UINT32 *)value) / 2;
636 #ifdef UNICODE_UCS2
637 int cc = WideCharToMultiByte(CP_UTF8, 0, (WCHAR *)((BYTE *)value + 4), (int)length, str, outSize - 1, NULL, NULL);
638 #else
639 int cc = ucs2_to_utf8((UCS2CHAR *)((BYTE *)value + 4), (int)length, str, outSize - 1);
640 #endif
641 str[cc] = 0;
642 }
643 else
644 {
645 if (buffer != NULL)
646 {
647 str = buffer;
648 str[0] = 0;
649 }
650 }
651 return str;
652 }
653
654 /**
655 * get binary (byte array) field
656 * Result will be placed to the buffer provided (no more than bufferSize bytes,
657 * and actual size of data will be returned
658 * If pBuffer is NULL, just actual data length is returned
659 */
660 UINT32 NXCPMessage::getFieldAsBinary(UINT32 fieldId, BYTE *pBuffer, size_t bufferSize) const
661 {
662 UINT32 size;
663 void *value = get(fieldId, NXCP_DT_BINARY);
664 if (value != NULL)
665 {
666 size = *((UINT32 *)value);
667 if (pBuffer != NULL)
668 memcpy(pBuffer, (BYTE *)value + 4, min(bufferSize, size));
669 }
670 else
671 {
672 size = 0;
673 }
674 return size;
675 }
676
677 /**
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 */
682 const BYTE *NXCPMessage::getBinaryFieldPtr(UINT32 fieldId, size_t *size) const
683 {
684 BYTE *data;
685 void *value = get(fieldId, NXCP_DT_BINARY);
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 /**
700 * Get field as GUID
701 * Returns NULL GUID on error
702 */
703 uuid NXCPMessage::getFieldAsGUID(UINT32 fieldId) const
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 /**
723 * Build protocol message ready to be send over the wire
724 */
725 NXCP_MESSAGE *NXCPMessage::createMessage() const
726 {
727 // Calculate message size
728 size_t size = NXCP_HEADER_SIZE;
729 UINT32 fieldCount = 0;
730 if (m_flags & MF_BINARY)
731 {
732 size += m_dataSize;
733 fieldCount = (UINT32)m_dataSize;
734 size += (8 - (size % 8)) & 7;
735 }
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 }
748
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 }
755
756 // Create message
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);
764
765 // Fill data fields
766 if (m_flags & MF_BINARY)
767 {
768 memcpy(msg->fields, m_data, m_dataSize);
769 }
770 else
771 {
772 NXCP_MESSAGE_FIELD *field = (NXCP_MESSAGE_FIELD *)((char *)msg + NXCP_HEADER_SIZE);
773 MessageField *entry, *tmp;
774 HASH_ITER(hh, m_fields, entry, tmp)
775 {
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);
781 switch(field->type)
782 {
783 case NXCP_DT_INT32:
784 field->df_int32 = htonl(field->df_int32);
785 break;
786 case NXCP_DT_INT64:
787 field->df_int64 = htonq(field->df_int64);
788 break;
789 case NXCP_DT_INT16:
790 field->df_int16 = htons(field->df_int16);
791 break;
792 case NXCP_DT_FLOAT:
793 field->df_real = htond(field->df_real);
794 break;
795 case NXCP_DT_STRING:
796 #if !(WORDS_BIGENDIAN)
797 {
798 bswap_array_16(field->df_string.value, field->df_string.length / 2);
799 field->df_string.length = htonl(field->df_string.length);
800 }
801 #endif
802 break;
803 case NXCP_DT_BINARY:
804 field->df_string.length = htonl(field->df_string.length);
805 break;
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;
812 }
813
814 if (m_version >= 2)
815 field = (NXCP_MESSAGE_FIELD *)((char *)field + fieldSize + ((8 - (fieldSize % 8)) & 7));
816 else
817 field = (NXCP_MESSAGE_FIELD *)((char *)field + fieldSize);
818 }
819 }
820 return msg;
821 }
822
823 /**
824 * Delete all variables
825 */
826 void NXCPMessage::deleteAllFields()
827 {
828 MessageField *entry, *tmp;
829 HASH_ITER(hh, m_fields, entry, tmp)
830 {
831 HASH_DEL(m_fields, entry);
832 free(entry);
833 }
834 }
835
836 #ifdef UNICODE
837
838 /**
839 * set variable from multibyte string
840 */
841 void NXCPMessage::setFieldFromMBString(UINT32 fieldId, const char *value)
842 {
843 WCHAR *wcValue = WideStringFromMBString(value);
844 set(fieldId, NXCP_DT_STRING, wcValue);
845 free(wcValue);
846 }
847
848 #endif
849
850 /**
851 * set binary field to an array of UINT32s
852 */
853 void NXCPMessage::setFieldFromInt32Array(UINT32 fieldId, size_t numElements, const UINT32 *elements)
854 {
855 UINT32 *pdwBuffer = (UINT32 *)set(fieldId, NXCP_DT_BINARY, elements, false, numElements * sizeof(UINT32));
856 if (pdwBuffer != NULL)
857 {
858 pdwBuffer++; // First UINT32 is a length field
859 for(size_t i = 0; i < numElements; i++) // Convert UINT32s to network byte order
860 pdwBuffer[i] = htonl(pdwBuffer[i]);
861 }
862 }
863
864 /**
865 * set binary field to an array of UINT32s
866 */
867 void NXCPMessage::setFieldFromInt32Array(UINT32 fieldId, const IntegerArray<UINT32> *data)
868 {
869 UINT32 *pdwBuffer = (UINT32 *)set(fieldId, NXCP_DT_BINARY, data->getBuffer(), false, data->size() * sizeof(UINT32));
870 if (pdwBuffer != NULL)
871 {
872 pdwBuffer++; // First UINT32 is a length field
873 for(int i = 0; i < data->size(); i++) // Convert UINT32s to network byte order
874 pdwBuffer[i] = htonl(pdwBuffer[i]);
875 }
876 }
877
878 /**
879 * get binary field as an array of 32 bit unsigned integers
880 */
881 UINT32 NXCPMessage::getFieldAsInt32Array(UINT32 fieldId, UINT32 numElements, UINT32 *buffer) const
882 {
883 UINT32 size = getFieldAsBinary(fieldId, (BYTE *)buffer, numElements * sizeof(UINT32));
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 }
889
890 /**
891 * get binary field as an array of 32 bit unsigned integers
892 */
893 UINT32 NXCPMessage::getFieldAsInt32Array(UINT32 fieldId, IntegerArray<UINT32> *data) const
894 {
895 data->clear();
896
897 UINT32 *value = (UINT32 *)get(fieldId, NXCP_DT_BINARY);
898 if (value != NULL)
899 {
900 UINT32 size = *value / sizeof(UINT32);
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();
909 }
910
911 /**
912 * set binary field from file
913 */
914 bool NXCPMessage::setFieldFromFile(UINT32 fieldId, const TCHAR *pszFileName)
915 {
916 FILE *pFile;
917 BYTE *pBuffer;
918 UINT32 size;
919 bool bResult = false;
920
921 size = (UINT32)FileSize(pszFileName);
922 pFile = _tfopen(pszFileName, _T("rb"));
923 if (pFile != NULL)
924 {
925 pBuffer = (BYTE *)set(fieldId, NXCP_DT_BINARY, NULL, false, size);
926 if (pBuffer != NULL)
927 {
928 if (fread(pBuffer + sizeof(UINT32), 1, size, pFile) == size)
929 bResult = true;
930 }
931 fclose(pFile);
932 }
933 return bResult;
934 }
935
936 /**
937 * Get string from field
938 */
939 static 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 */
963 String NXCPMessage::dump(const NXCP_MESSAGE *msg, int version)
964 {
965 String out;
966 int i;
967 TCHAR *str, buffer[128];
968
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);
974
975 // Dump raw message
976 for(i = 0; i < (int)size; i += 16)
977 {
978 BinToStr(((BYTE *)msg) + i, min(16, size - i), buffer);
979 out.appendFormattedString(_T(" ** %s\n"), buffer);
980 }
981
982 // header
983 out.appendFormattedString(_T(" ** code=0x%04X (%s) flags=0x%04X id=%d size=%d numFields=%d\n"),
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
992 size_t pos = NXCP_HEADER_SIZE;
993 for(int f = 0; f < numFields; f++)
994 {
995 NXCP_MESSAGE_FIELD *field = (NXCP_MESSAGE_FIELD *)(((BYTE *)msg) + pos);
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) &&
1004 ((field->type == NXCP_DT_STRING) || (field->type == NXCP_DT_BINARY)))
1005 {
1006 out.appendFormattedString(_T(" ** message format error (pos > size - 8 and field type %d)\n"), (int)field->type);
1007 break;
1008 }
1009
1010 // Calculate and validate field size
1011 size_t fieldSize = CalculateFieldSize(field, TRUE);
1012 if (pos + fieldSize > size)
1013 {
1014 out += _T(" ** message format error (invalid field size)\n");
1015 break;
1016 }
1017
1018 // Create new entry
1019 NXCP_MESSAGE_FIELD *convertedField = (NXCP_MESSAGE_FIELD *)malloc(fieldSize);
1020 memcpy(convertedField, field, fieldSize);
1021
1022 // Convert numeric values to host format
1023 convertedField->fieldId = ntohl(convertedField->fieldId);
1024 switch(field->type)
1025 {
1026 case NXCP_DT_INT32:
1027 convertedField->df_int32 = ntohl(convertedField->df_int32);
1028 out.appendFormattedString(_T(" ** [%6d] INT32 %d\n"), (int)convertedField->fieldId, convertedField->df_int32);
1029 break;
1030 case NXCP_DT_INT64:
1031 convertedField->df_int64 = ntohq(convertedField->df_int64);
1032 out.appendFormattedString(_T(" ** [%6d] INT64 ") INT64_FMT _T("\n"), (int)convertedField->fieldId, convertedField->df_int64);
1033 break;
1034 case NXCP_DT_INT16:
1035 convertedField->df_int16 = ntohs(convertedField->df_int16);
1036 out.appendFormattedString(_T(" ** [%6d] INT16 %d\n"), (int)convertedField->fieldId, (int)convertedField->df_int16);
1037 break;
1038 case NXCP_DT_FLOAT:
1039 convertedField->df_real = ntohd(convertedField->df_real);
1040 out.appendFormattedString(_T(" ** [%6d] FLOAT %f\n"), (int)convertedField->fieldId, convertedField->df_real);
1041 break;
1042 case NXCP_DT_STRING:
1043 #if !(WORDS_BIGENDIAN)
1044 convertedField->df_string.length = ntohl(convertedField->df_string.length);
1045 bswap_array_16(convertedField->df_string.value, (int)convertedField->df_string.length / 2);
1046 #endif
1047 str = GetStringFromField((BYTE *)convertedField + 8);
1048 out.appendFormattedString(_T(" ** [%6d] STRING \"%s\"\n"), (int)convertedField->fieldId, str);
1049 free(str);
1050 break;
1051 case NXCP_DT_BINARY:
1052 convertedField->df_string.length = ntohl(convertedField->df_string.length);
1053 out.appendFormattedString(_T(" ** [%6d] BINARY len=%d\n"), (int)convertedField->fieldId, (int)convertedField->df_string.length);
1054 break;
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;
1065 default:
1066 out.appendFormattedString(_T(" ** [%6d] unknown type %d\n"), (int)convertedField->fieldId, (int)field->type);
1067 break;
1068 }
1069 free(convertedField);
1070
1071 // Starting from version 2, all fields should be 8-byte aligned
1072 if (version >= 2)
1073 pos += fieldSize + ((8 - (fieldSize % 8)) & 7);
1074 else
1075 pos += fieldSize;
1076 }
1077
1078 return out;
1079 }