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