eefc4122f98cbf367ab32037c0d321f82da5bc17
[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 = (((InetAddress *)value)->getFamily() == AF_INET) ? NXCP_AF_INET : NXCP_AF_INET6;
309 entry->data.df_inetaddr.maskBits = (BYTE)((InetAddress *)value)->getMaskBits();
310 if (((InetAddress *)value)->getFamily() == AF_INET)
311 {
312 entry->data.df_inetaddr.addr.v4 = ((InetAddress *)value)->getAddressV4();
313 }
314 else
315 {
316 memcpy(entry->data.df_inetaddr.addr.v6, ((InetAddress *)value)->getAddressV6(), 16);
317 }
318 break;
319 default:
320 return NULL; // Invalid data type, unable to handle
321 }
322 entry->id = fieldId;
323 entry->data.fieldId = fieldId;
324 entry->data.type = type;
325 if (isSigned)
326 entry->data.flags |= NXCP_MFF_SIGNED;
327
328 // add or replace field
329 MessageField *curr;
330 HASH_FIND_INT(m_fields, &fieldId, curr);
331 if (curr != NULL)
332 {
333 HASH_DEL(m_fields, curr);
334 free(curr);
335 }
336 HASH_ADD_INT(m_fields, id, entry);
337
338 return (type == NXCP_DT_INT16) ? ((void *)((BYTE *)&entry->data + 6)) : ((void *)((BYTE *)&entry->data + 8));
339 #undef __buffer
340 }
341
342 /**
343 * Get field value
344 */
345 void *NXCPMessage::get(UINT32 fieldId, BYTE requiredType, BYTE *fieldType) const
346 {
347 NXCP_MESSAGE_FIELD *field = find(fieldId);
348 if (field == NULL)
349 return NULL; // No such field
350
351 // Data type check exception - return IPv4 address as INT32 if requested
352 if ((requiredType == NXCP_DT_INT32) && (field->type == NXCP_DT_INETADDR) && (field->df_inetaddr.family == NXCP_AF_INET))
353 return &field->df_inetaddr.addr.v4;
354
355 // Check data type
356 if ((requiredType != 0xFF) && (field->type != requiredType))
357 return NULL;
358
359 if (fieldType != NULL)
360 *fieldType = field->type;
361 return (field->type == NXCP_DT_INT16) ?
362 ((void *)((BYTE *)field + 6)) :
363 ((void *)((BYTE *)field + 8));
364 }
365
366 /**
367 * Get 16 bit field as boolean
368 */
369 bool NXCPMessage::getFieldAsBoolean(UINT32 fieldId) const
370 {
371 BYTE type;
372 void *value = (void *)get(fieldId, 0xFF, &type);
373 if (value == NULL)
374 return false;
375
376 switch(type)
377 {
378 case NXCP_DT_INT16:
379 return *((UINT16 *)value) ? true : false;
380 case NXCP_DT_INT32:
381 return *((UINT32 *)value) ? true : false;
382 case NXCP_DT_INT64:
383 return *((UINT64 *)value) ? true : false;
384 default:
385 return false;
386 }
387 }
388
389 /**
390 * Get data type of message field.
391 *
392 * @return field type or -1 if field with given ID does not exist
393 */
394 int NXCPMessage::getFieldType(UINT32 fieldId) const
395 {
396 NXCP_MESSAGE_FIELD *field = find(fieldId);
397 return (field != NULL) ? (int)field->type : -1;
398 }
399
400 /**
401 * get signed integer field
402 */
403 INT32 NXCPMessage::getFieldAsInt32(UINT32 fieldId) const
404 {
405 char *value = (char *)get(fieldId, NXCP_DT_INT32);
406 return (value != NULL) ? *((INT32 *)value) : 0;
407 }
408
409 /**
410 * get unsigned integer field
411 */
412 UINT32 NXCPMessage::getFieldAsUInt32(UINT32 fieldId) const
413 {
414 void *value = get(fieldId, NXCP_DT_INT32);
415 return (value != NULL) ? *((UINT32 *)value) : 0;
416 }
417
418 /**
419 * get signed 16-bit integer field
420 */
421 INT16 NXCPMessage::getFieldAsInt16(UINT32 fieldId) const
422 {
423 void *value = get(fieldId, NXCP_DT_INT16);
424 return (value != NULL) ? *((INT16 *)value) : 0;
425 }
426
427 /**
428 * get unsigned 16-bit integer variable
429 */
430 UINT16 NXCPMessage::getFieldAsUInt16(UINT32 fieldId) const
431 {
432 void *value = get(fieldId, NXCP_DT_INT16);
433 return value ? *((WORD *)value) : 0;
434 }
435
436 /**
437 * get signed 64-bit integer field
438 */
439 INT64 NXCPMessage::getFieldAsInt64(UINT32 fieldId) const
440 {
441 void *value = get(fieldId, NXCP_DT_INT64);
442 return (value != NULL) ? *((INT64 *)value) : 0;
443 }
444
445 /**
446 * get unsigned 64-bit integer field
447 */
448 UINT64 NXCPMessage::getFieldAsUInt64(UINT32 fieldId) const
449 {
450 void *value = get(fieldId, NXCP_DT_INT64);
451 return value ? *((UINT64 *)value) : 0;
452 }
453
454 /**
455 * get 64-bit floating point variable
456 */
457 double NXCPMessage::getFieldAsDouble(UINT32 fieldId) const
458 {
459 void *value = get(fieldId, NXCP_DT_FLOAT);
460 return (value != NULL) ? *((double *)value) : 0;
461 }
462
463 /**
464 * get time_t field
465 */
466 time_t NXCPMessage::getFieldAsTime(UINT32 fieldId) const
467 {
468 BYTE type;
469 void *value = (void *)get(fieldId, 0xFF, &type);
470 if (value == NULL)
471 return 0;
472
473 switch(type)
474 {
475 case NXCP_DT_INT32:
476 return (time_t)(*((UINT32 *)value));
477 case NXCP_DT_INT64:
478 return (time_t)(*((UINT64 *)value));
479 default:
480 return false;
481 }
482 }
483
484 /**
485 * Get field as inet address
486 */
487 InetAddress NXCPMessage::getFieldAsInetAddress(UINT32 fieldId) const
488 {
489 NXCP_MESSAGE_FIELD *f = find(fieldId);
490 if (f == NULL)
491 return InetAddress();
492
493 if (f->type == NXCP_DT_INETADDR)
494 {
495 InetAddress a =
496 (f->df_inetaddr.family == NXCP_AF_INET) ?
497 InetAddress(f->df_inetaddr.addr.v4) :
498 InetAddress(f->df_inetaddr.addr.v6);
499 a.setMaskBits(f->df_inetaddr.maskBits);
500 return a;
501 }
502 else if (f->type == NXCP_DT_INT32)
503 {
504 return InetAddress(f->df_uint32);
505 }
506 return InetAddress();
507 }
508
509 /**
510 * Get string field
511 * If buffer is NULL, memory block of required size will be allocated
512 * for result; if buffer is not NULL, entire result or part of it will
513 * be placed to buffer and pointer to buffer will be returned.
514 * Note: bufferSize is buffer size in characters, not bytes!
515 */
516 TCHAR *NXCPMessage::getFieldAsString(UINT32 fieldId, TCHAR *buffer, size_t bufferSize) const
517 {
518 if ((buffer != NULL) && (bufferSize == 0))
519 return NULL; // non-sense combination
520
521 TCHAR *str = NULL;
522 void *value = get(fieldId, NXCP_DT_STRING);
523 if (value != NULL)
524 {
525 if (buffer == NULL)
526 {
527 #if defined(UNICODE) && defined(UNICODE_UCS4)
528 str = (TCHAR *)malloc(*((UINT32 *)value) * 2 + 4);
529 #elif defined(UNICODE) && defined(UNICODE_UCS2)
530 str = (TCHAR *)malloc(*((UINT32 *)value) + 2);
531 #else
532 str = (TCHAR *)malloc(*((UINT32 *)value) / 2 + 1);
533 #endif
534 }
535 else
536 {
537 str = buffer;
538 }
539
540 size_t length = (buffer == NULL) ? (*((UINT32 *)value) / 2) : min(*((UINT32 *)value) / 2, bufferSize - 1);
541 #if defined(UNICODE) && defined(UNICODE_UCS4)
542 ucs2_to_ucs4((UCS2CHAR *)((BYTE *)value + 4), length, str, length + 1);
543 #elif defined(UNICODE) && defined(UNICODE_UCS2)
544 memcpy(str, (BYTE *)value + 4, length * 2);
545 #else
546 ucs2_to_mb((UCS2CHAR *)((BYTE *)value + 4), length, str, length + 1);
547 #endif
548 str[length] = 0;
549 }
550 else
551 {
552 if (buffer != NULL)
553 {
554 str = buffer;
555 str[0] = 0;
556 }
557 }
558 return str;
559 }
560
561 #ifdef UNICODE
562
563 /**
564 * get variable as multibyte string
565 */
566 char *NXCPMessage::getFieldAsMBString(UINT32 fieldId, char *buffer, size_t bufferSize) const
567 {
568 if ((buffer != NULL) && (bufferSize == 0))
569 return NULL; // non-sense combination
570
571 char *str = NULL;
572 void *value = get(fieldId, NXCP_DT_STRING);
573 if (value != NULL)
574 {
575 if (buffer == NULL)
576 {
577 str = (char *)malloc(*((UINT32 *)value) / 2 + 1);
578 }
579 else
580 {
581 str = buffer;
582 }
583
584 size_t length = (buffer == NULL) ? (*((UINT32 *)value) / 2) : min(*((UINT32 *)value) / 2, bufferSize - 1);
585 ucs2_to_mb((UCS2CHAR *)((BYTE *)value + 4), (int)length, str, (int)length + 1);
586 str[length] = 0;
587 }
588 else
589 {
590 if (buffer != NULL)
591 {
592 str = buffer;
593 str[0] = 0;
594 }
595 }
596 return str;
597 }
598
599 #else
600
601 /**
602 * get field as multibyte string
603 */
604 char *NXCPMessage::getFieldAsMBString(UINT32 fieldId, char *buffer, size_t bufferSize) const
605 {
606 return getFieldAsString(fieldId, buffer, bufferSize);
607 }
608
609 #endif
610
611 /**
612 * get field as UTF-8 string
613 */
614 char *NXCPMessage::getFieldAsUtf8String(UINT32 fieldId, char *buffer, size_t bufferSize) const
615 {
616 if ((buffer != NULL) && (bufferSize == 0))
617 return NULL; // non-sense combination
618
619 char *str = NULL;
620 void *value = get(fieldId, NXCP_DT_STRING);
621 if (value != NULL)
622 {
623 int outSize;
624 if (buffer == NULL)
625 {
626 // Assume worst case scenario - 3 bytes per character
627 outSize = (int)(*((UINT32 *)value) + *((UINT32 *)value) / 2 + 1);
628 str = (char *)malloc(outSize);
629 }
630 else
631 {
632 outSize = (int)bufferSize;
633 str = buffer;
634 }
635
636 size_t length = *((UINT32 *)value) / 2;
637 #ifdef UNICODE_UCS2
638 int cc = WideCharToMultiByte(CP_UTF8, 0, (WCHAR *)((BYTE *)value + 4), (int)length, str, outSize - 1, NULL, NULL);
639 #else
640 int cc = ucs2_to_utf8((UCS2CHAR *)((BYTE *)value + 4), (int)length, str, outSize - 1);
641 #endif
642 str[cc] = 0;
643 }
644 else
645 {
646 if (buffer != NULL)
647 {
648 str = buffer;
649 str[0] = 0;
650 }
651 }
652 return str;
653 }
654
655 /**
656 * get binary (byte array) field
657 * Result will be placed to the buffer provided (no more than bufferSize bytes,
658 * and actual size of data will be returned
659 * If pBuffer is NULL, just actual data length is returned
660 */
661 UINT32 NXCPMessage::getFieldAsBinary(UINT32 fieldId, BYTE *pBuffer, size_t bufferSize) const
662 {
663 UINT32 size;
664 void *value = get(fieldId, NXCP_DT_BINARY);
665 if (value != NULL)
666 {
667 size = *((UINT32 *)value);
668 if (pBuffer != NULL)
669 memcpy(pBuffer, (BYTE *)value + 4, min(bufferSize, size));
670 }
671 else
672 {
673 size = 0;
674 }
675 return size;
676 }
677
678 /**
679 * get binary (byte array) field
680 * Returns pointer to internal buffer or NULL if field not found
681 * Data length set in size parameter.
682 */
683 const BYTE *NXCPMessage::getBinaryFieldPtr(UINT32 fieldId, size_t *size) const
684 {
685 BYTE *data;
686 void *value = get(fieldId, NXCP_DT_BINARY);
687 if (value != NULL)
688 {
689 *size = (size_t)(*((UINT32 *)value));
690 data = (BYTE *)value + 4;
691 }
692 else
693 {
694 *size = 0;
695 data = NULL;
696 }
697 return data;
698 }
699
700 /**
701 * Get field as GUID
702 * Returns NULL GUID on error
703 */
704 uuid NXCPMessage::getFieldAsGUID(UINT32 fieldId) const
705 {
706 NXCP_MESSAGE_FIELD *f = find(fieldId);
707 if (f == NULL)
708 return uuid::NULL_UUID;
709
710 if ((f->type == NXCP_DT_BINARY) && (f->df_binary.length == UUID_LENGTH))
711 {
712 return uuid(f->df_binary.value);
713 }
714 else if (f->type == NXCP_DT_STRING)
715 {
716 TCHAR buffer[64] = _T("");
717 getFieldAsString(fieldId, buffer, 64);
718 return uuid::parse(buffer);
719 }
720 return uuid::NULL_UUID;
721 }
722
723 /**
724 * Build protocol message ready to be send over the wire
725 */
726 NXCP_MESSAGE *NXCPMessage::createMessage()
727 {
728 // Calculate message size
729 size_t size = NXCP_HEADER_SIZE;
730 UINT32 fieldCount = 0;
731 if (m_flags & MF_BINARY)
732 {
733 size += m_dataSize;
734 fieldCount = (UINT32)m_dataSize;
735 size += (8 - (size % 8)) & 7;
736 }
737 else
738 {
739 MessageField *entry, *tmp;
740 HASH_ITER(hh, m_fields, entry, tmp)
741 {
742 size_t fieldSize = CalculateFieldSize(&entry->data, false);
743 if (m_version >= 2)
744 size += fieldSize + ((8 - (fieldSize % 8)) & 7);
745 else
746 size += fieldSize;
747 fieldCount++;
748 }
749
750 // Message should be aligned to 8 bytes boundary
751 // This is always the case starting from version 2 because
752 // all fields are padded to 8 bytes boundary
753 if (m_version < 2)
754 size += (8 - (size % 8)) & 7;
755 }
756
757 // Create message
758 NXCP_MESSAGE *msg = (NXCP_MESSAGE *)malloc(size);
759 memset(msg, 0, size);
760 msg->code = htons(m_code);
761 msg->flags = htons(m_flags);
762 msg->size = htonl((UINT32)size);
763 msg->id = htonl(m_id);
764 msg->numFields = htonl(fieldCount);
765
766 // Fill data fields
767 if (m_flags & MF_BINARY)
768 {
769 memcpy(msg->fields, m_data, m_dataSize);
770 }
771 else
772 {
773 NXCP_MESSAGE_FIELD *field = (NXCP_MESSAGE_FIELD *)((char *)msg + NXCP_HEADER_SIZE);
774 MessageField *entry, *tmp;
775 HASH_ITER(hh, m_fields, entry, tmp)
776 {
777 size_t fieldSize = CalculateFieldSize(&entry->data, false);
778 memcpy(field, &entry->data, fieldSize);
779
780 // Convert numeric values to network format
781 field->fieldId = htonl(field->fieldId);
782 switch(field->type)
783 {
784 case NXCP_DT_INT32:
785 field->df_int32 = htonl(field->df_int32);
786 break;
787 case NXCP_DT_INT64:
788 field->df_int64 = htonq(field->df_int64);
789 break;
790 case NXCP_DT_INT16:
791 field->df_int16 = htons(field->df_int16);
792 break;
793 case NXCP_DT_FLOAT:
794 field->df_real = htond(field->df_real);
795 break;
796 case NXCP_DT_STRING:
797 #if !(WORDS_BIGENDIAN)
798 {
799 for(UINT32 i = 0; i < field->df_string.length / 2; i++)
800 field->df_string.value[i] = htons(field->df_string.value[i]);
801 field->df_string.length = htonl(field->df_string.length);
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 for(i = 0; i < (int)convertedField->df_string.length / 2; i++)
1048 convertedField->df_string.value[i] = ntohs(convertedField->df_string.value[i]);
1049 #endif
1050 str = GetStringFromField((BYTE *)convertedField + 8);
1051 out.appendFormattedString(_T(" ** [%6d] STRING \"%s\"\n"), (int)convertedField->fieldId, str);
1052 free(str);
1053 break;
1054 case NXCP_DT_BINARY:
1055 convertedField->df_string.length = ntohl(convertedField->df_string.length);
1056 out.appendFormattedString(_T(" ** [%6d] BINARY len=%d\n"), (int)convertedField->fieldId, (int)convertedField->df_string.length);
1057 break;
1058 case NXCP_DT_INETADDR:
1059 {
1060 InetAddress a =
1061 (convertedField->df_inetaddr.family == NXCP_AF_INET) ?
1062 InetAddress(ntohl(convertedField->df_inetaddr.addr.v4)) :
1063 InetAddress(convertedField->df_inetaddr.addr.v6);
1064 a.setMaskBits(convertedField->df_inetaddr.maskBits);
1065 out.appendFormattedString(_T(" ** [%6d] INETADDR %s\n"), (int)convertedField->fieldId, (const TCHAR *)a.toString());
1066 }
1067 break;
1068 default:
1069 out.appendFormattedString(_T(" ** [%6d] unknown type %d\n"), (int)convertedField->fieldId, (int)field->type);
1070 break;
1071 }
1072 free(convertedField);
1073
1074 // Starting from version 2, all fields should be 8-byte aligned
1075 if (version >= 2)
1076 pos += fieldSize + ((8 - (fieldSize % 8)) & 7);
1077 else
1078 pos += fieldSize;
1079 }
1080
1081 return out;
1082 }