imported svn:keywords properties
[public/netxms.git] / src / libnetxms / message.cpp
CommitLineData
8e76f8aa 1/* $Id$ */
563c2ae0
VK
2/*
3** NetXMS - Network Management System
4** NetXMS Foundation Library
5** Copyright (C) 2003, 2004, 2005, 2006 Victor Kirhenshtein
6**
7** This program is free software; you can redistribute it and/or modify
8** it under the terms of the GNU General Public License as published by
9** the Free Software Foundation; either version 2 of the License, or
10** (at your option) any later version.
11**
12** This program is distributed in the hope that it will be useful,
13** but WITHOUT ANY WARRANTY; without even the implied warranty of
14** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15** GNU General Public License for more details.
16**
17** You should have received a copy of the GNU General Public License
18** along with this program; if not, write to the Free Software
19** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20**
21** File: message.cpp
22**
23**/
24
25#include "libnetxms.h"
26
27
28//
29// Calculate variable size
30//
31
32static int VariableSize(CSCP_DF *pVar, BOOL bNetworkByteOrder)
33{
34 int nSize;
35
36 switch(pVar->bType)
37 {
38 case CSCP_DT_INTEGER:
39 nSize = 12;
40 break;
41 case CSCP_DT_INT64:
42 case CSCP_DT_FLOAT:
43 nSize = 16;
44 break;
45 case CSCP_DT_INT16:
46 nSize = 8;
47 break;
48 case CSCP_DT_STRING:
49 case CSCP_DT_BINARY:
50 if (bNetworkByteOrder)
51 nSize = ntohl(pVar->df_string.dwLen) + 12;
52 else
53 nSize = pVar->df_string.dwLen + 12;
54 break;
55 default:
56 nSize = 8;
57 break;
58 }
59 return nSize;
60}
61
62
63//
64// Default constructor for CSCPMessage class
65//
66
67CSCPMessage::CSCPMessage(int nVersion)
68{
69 m_wCode = 0;
70 m_dwId = 0;
71 m_dwNumVar = 0;
72 m_ppVarList = NULL;
73 m_wFlags = 0;
74 m_nVersion = nVersion;
75}
76
77
78//
79// Create a copy of prepared CSCP message
80//
81
82CSCPMessage::CSCPMessage(CSCPMessage *pMsg)
83{
84 DWORD i;
85
86 m_wCode = pMsg->m_wCode;
87 m_dwId = pMsg->m_dwId;
88 m_wFlags = pMsg->m_wFlags;
89 m_nVersion = pMsg->m_nVersion;
90 m_dwNumVar = pMsg->m_dwNumVar;
91 m_ppVarList = (CSCP_DF **)malloc(sizeof(CSCP_DF *) * m_dwNumVar);
92 for(i = 0; i < m_dwNumVar; i++)
93 {
94 m_ppVarList[i] = (CSCP_DF *)nx_memdup(pMsg->m_ppVarList[i],
95 VariableSize(pMsg->m_ppVarList[i], FALSE));
96 }
97}
98
99
100//
101// Create CSCPMessage object from received message
102//
103
104CSCPMessage::CSCPMessage(CSCP_MESSAGE *pMsg, int nVersion)
105{
106 DWORD i, dwPos, dwSize, dwVar;
107 CSCP_DF *pVar;
108 int iVarSize;
109
110 m_wFlags = ntohs(pMsg->wFlags);
111 m_wCode = ntohs(pMsg->wCode);
112 m_dwId = ntohl(pMsg->dwId);
113 dwSize = ntohl(pMsg->dwSize);
114 m_dwNumVar = ntohl(pMsg->dwNumVars);
115 m_ppVarList = (CSCP_DF **)malloc(sizeof(CSCP_DF *) * m_dwNumVar);
116 m_nVersion = nVersion;
117
118 // Parse data fields
119 for(dwPos = CSCP_HEADER_SIZE, dwVar = 0; dwVar < m_dwNumVar; dwVar++)
120 {
121 pVar = (CSCP_DF *)(((BYTE *)pMsg) + dwPos);
122
c5d159d9
VK
123 // Validate position inside message
124 if (dwPos > dwSize - 8)
125 break;
126 if ((dwPos > dwSize - 12) &&
127 ((pVar->bType == CSCP_DT_STRING) || (pVar->bType == CSCP_DT_BINARY)))
128 break;
129
130 // Calculate and validate variable size
563c2ae0 131 iVarSize = VariableSize(pVar, TRUE);
c5d159d9
VK
132 if (dwPos + iVarSize > dwSize)
133 break;
563c2ae0
VK
134
135 // Create new entry
136 m_ppVarList[dwVar] = (CSCP_DF *)malloc(iVarSize);
137 memcpy(m_ppVarList[dwVar], pVar, iVarSize);
138
139 // Convert numeric values to host format
140 m_ppVarList[dwVar]->dwVarId = ntohl(m_ppVarList[dwVar]->dwVarId);
141 switch(pVar->bType)
142 {
143 case CSCP_DT_INTEGER:
144 m_ppVarList[dwVar]->df_int32 = ntohl(m_ppVarList[dwVar]->df_int32);
145 break;
146 case CSCP_DT_INT64:
147 m_ppVarList[dwVar]->df_int64 = ntohq(m_ppVarList[dwVar]->df_int64);
148 break;
149 case CSCP_DT_INT16:
150 m_ppVarList[dwVar]->df_int16 = ntohs(m_ppVarList[dwVar]->df_int16);
151 break;
152 case CSCP_DT_FLOAT:
153 m_ppVarList[dwVar]->df_real = ntohd(m_ppVarList[dwVar]->df_real);
154 break;
155 case CSCP_DT_STRING:
156#if !(WORDS_BIGENDIAN)
157 m_ppVarList[dwVar]->df_string.dwLen = ntohl(m_ppVarList[dwVar]->df_string.dwLen);
158 for(i = 0; i < m_ppVarList[dwVar]->df_string.dwLen / 2; i++)
159 m_ppVarList[dwVar]->df_string.szValue[i] = ntohs(m_ppVarList[dwVar]->df_string.szValue[i]);
160#endif
161 break;
162 case CSCP_DT_BINARY:
163 m_ppVarList[dwVar]->df_string.dwLen = ntohl(m_ppVarList[dwVar]->df_string.dwLen);
164 break;
165 }
166
167 // Starting from version 2, all variables should be 8-byte aligned
168 if (m_nVersion >= 2)
169 dwPos += iVarSize + ((8 - (iVarSize % 8)) & 7);
170 else
171 dwPos += iVarSize;
172 }
c5d159d9
VK
173
174 // Cut unfilled variables, if any
175 m_dwNumVar = dwVar;
563c2ae0
VK
176}
177
178
179//
180// Destructor for CSCPMessage
181//
182
183CSCPMessage::~CSCPMessage()
184{
185 DeleteAllVariables();
186}
187
188
189//
190// Find variable by name
191//
192
193DWORD CSCPMessage::FindVariable(DWORD dwVarId)
194{
195 DWORD i;
196
197 for(i = 0; i < m_dwNumVar; i++)
c5d159d9
VK
198 if (m_ppVarList[i] != NULL)
199 if (m_ppVarList[i]->dwVarId == dwVarId)
200 return i;
563c2ae0
VK
201 return INVALID_INDEX;
202}
203
204
205//
206// Set variable
207// Argument dwSize (data size) is used only for DT_BINARY type
208//
209
76154a95 210void *CSCPMessage::Set(DWORD dwVarId, BYTE bType, const void *pValue, DWORD dwSize)
563c2ae0
VK
211{
212 DWORD dwIndex, dwLength;
213 CSCP_DF *pVar;
d6c7ce9a 214#if !defined(UNICODE_UCS2) || !defined(UNICODE)
8b86c5dc 215 UCS2CHAR *pBuffer;
563c2ae0
VK
216#endif
217
218 // Create CSCP_DF structure
219 switch(bType)
220 {
221 case CSCP_DT_INTEGER:
222 pVar = (CSCP_DF *)malloc(12);
76154a95 223 pVar->df_int32 = *((const DWORD *)pValue);
563c2ae0
VK
224 break;
225 case CSCP_DT_INT16:
226 pVar = (CSCP_DF *)malloc(8);
76154a95 227 pVar->df_int16 = *((const WORD *)pValue);
563c2ae0
VK
228 break;
229 case CSCP_DT_INT64:
230 pVar = (CSCP_DF *)malloc(16);
76154a95 231 pVar->df_int64 = *((const QWORD *)pValue);
563c2ae0
VK
232 break;
233 case CSCP_DT_FLOAT:
234 pVar = (CSCP_DF *)malloc(16);
76154a95 235 pVar->df_real = *((const double *)pValue);
563c2ae0
VK
236 break;
237 case CSCP_DT_STRING:
76154a95 238 dwLength = (DWORD)_tcslen((const TCHAR *)pValue);
563c2ae0
VK
239 pVar = (CSCP_DF *)malloc(12 + dwLength * 2);
240 pVar->df_string.dwLen = dwLength * 2;
8b86c5dc
VK
241#ifdef UNICODE
242#ifdef UNICODE_UCS2
563c2ae0 243 memcpy(pVar->df_string.szValue, pValue, pVar->df_string.dwLen);
8b86c5dc
VK
244#else /* assume UNICODE_UCS4 */
245 pBuffer = (UCS2CHAR *)malloc(dwLength * 2 + 2);
246 ucs4_to_ucs2((WCHAR *)pValue, dwLength, pBuffer, dwLength + 1);
247 memcpy(pVar->df_string.szValue, pBuffer, pVar->df_string.dwLen);
248 free(pBuffer);
249#endif
250#else /* not UNICODE */
251 pBuffer = (UCS2CHAR *)malloc(dwLength * 2 + 2);
252 mb_to_ucs2((const char *)pValue, dwLength, pBuffer, dwLength + 1);
563c2ae0
VK
253 memcpy(pVar->df_string.szValue, pBuffer, pVar->df_string.dwLen);
254 free(pBuffer);
255#endif
256 break;
257 case CSCP_DT_BINARY:
258 pVar = (CSCP_DF *)malloc(12 + dwSize);
259 pVar->df_string.dwLen = dwSize;
260 if ((pVar->df_string.dwLen > 0) && (pValue != NULL))
261 memcpy(pVar->df_string.szValue, pValue, pVar->df_string.dwLen);
262 break;
263 default:
264 return NULL; // Invalid data type, unable to handle
265 }
266 pVar->dwVarId = dwVarId;
267 pVar->bType = bType;
268
269 // Check if variable exists
270 dwIndex = FindVariable(pVar->dwVarId);
271 if (dwIndex == INVALID_INDEX) // Add new variable to list
272 {
273 m_ppVarList = (CSCP_DF **)realloc(m_ppVarList, sizeof(CSCP_DF *) * (m_dwNumVar + 1));
274 m_ppVarList[m_dwNumVar] = pVar;
275 m_dwNumVar++;
276 }
277 else // Replace existing variable
278 {
279 free(m_ppVarList[dwIndex]);
280 m_ppVarList[dwIndex] = pVar;
281 }
282
283 return (bType == CSCP_DT_INT16) ? ((void *)((BYTE *)pVar + 6)) : ((void *)((BYTE *)pVar + 8));
284}
285
286
287//
288// Get variable value
289//
290
291void *CSCPMessage::Get(DWORD dwVarId, BYTE bType)
292{
293 DWORD dwIndex;
294
295 // Find variable
296 dwIndex = FindVariable(dwVarId);
297 if (dwIndex == INVALID_INDEX)
298 return NULL; // No such variable
299
300 // Check data type
301 if (m_ppVarList[dwIndex]->bType != bType)
302 return NULL;
303
304 return (bType == CSCP_DT_INT16) ?
305 ((void *)((BYTE *)m_ppVarList[dwIndex] + 6)) :
306 ((void *)((BYTE *)m_ppVarList[dwIndex] + 8));
307}
308
309
310//
311// Get integer variable
312//
313
314DWORD CSCPMessage::GetVariableLong(DWORD dwVarId)
315{
316 char *pValue;
317
318 pValue = (char *)Get(dwVarId, CSCP_DT_INTEGER);
319 return pValue ? *((DWORD *)pValue) : 0;
320}
321
322
323//
324// Get 16-bit integer variable
325//
326
327WORD CSCPMessage::GetVariableShort(DWORD dwVarId)
328{
329 void *pValue;
330
331 pValue = Get(dwVarId, CSCP_DT_INT16);
332 return pValue ? *((WORD *)pValue) : 0;
333}
334
335
336//
337// Get 16-bit integer variable as signel 32-bit integer
338//
339
340LONG CSCPMessage::GetVariableShortAsInt32(DWORD dwVarId)
341{
342 void *pValue;
343
344 pValue = Get(dwVarId, CSCP_DT_INT16);
345 return pValue ? *((short *)pValue) : 0;
346}
347
348
349//
350// Get 64-bit integer variable
351//
352
353QWORD CSCPMessage::GetVariableInt64(DWORD dwVarId)
354{
355 char *pValue;
356
357 pValue = (char *)Get(dwVarId, CSCP_DT_INT64);
358 return pValue ? *((QWORD *)pValue) : 0;
359}
360
361
362//
363// Get 64-bit floating point variable
364//
365
366double CSCPMessage::GetVariableDouble(DWORD dwVarId)
367{
368 char *pValue;
369
370 pValue = (char *)Get(dwVarId, CSCP_DT_FLOAT);
371 return pValue ? *((double *)pValue) : 0;
372}
373
374
375//
376// Get string variable
377// If szBuffer is NULL, memory block of required size will be allocated
378// for result; if szBuffer is not NULL, entire result or part of it will
379// be placed to szBuffer and pointer to szBuffer will be returned.
380// Note: dwBufSize is buffer size in characters, not bytes!
381//
382
383TCHAR *CSCPMessage::GetVariableStr(DWORD dwVarId, TCHAR *pszBuffer, DWORD dwBufSize)
384{
385 void *pValue;
386 TCHAR *pStr = NULL;
387 DWORD dwLen;
388
389 if ((pszBuffer != NULL) && (dwBufSize == 0))
390 return NULL; // non-sense combination
391
392 pValue = Get(dwVarId, CSCP_DT_STRING);
393 if (pValue != NULL)
394 {
395 if (pszBuffer == NULL)
396 {
8b86c5dc
VK
397#if defined(UNICODE) && defined(UNICODE_UCS4)
398 pStr = (TCHAR *)malloc(*((DWORD *)pValue) * 2 + 4);
399#elif defined(UNICODE) && defined(UNICODE_UCS2)
563c2ae0
VK
400 pStr = (TCHAR *)malloc(*((DWORD *)pValue) + 2);
401#else
402 pStr = (TCHAR *)malloc(*((DWORD *)pValue) / 2 + 1);
403#endif
404 }
405 else
406 {
407 pStr = pszBuffer;
408 }
409
410 dwLen = (pszBuffer == NULL) ? (*((DWORD *)pValue) / 2) : min(*((DWORD *)pValue) / 2, dwBufSize - 1);
8b86c5dc 411#if defined(UNICODE) && defined(UNICODE_UCS4)
701b654b 412 ucs2_to_ucs4((UCS2CHAR *)((BYTE *)pValue + 4), dwLen, pStr, dwLen + 1);
8b86c5dc 413#elif defined(UNICODE) && defined(UNICODE_UCS2)
563c2ae0
VK
414 memcpy(pStr, (BYTE *)pValue + 4, dwLen * 2);
415#else
8b86c5dc 416 ucs2_to_mb((UCS2CHAR *)((BYTE *)pValue + 4), dwLen, pStr, dwLen + 1);
563c2ae0
VK
417#endif
418 pStr[dwLen] = 0;
419 }
420 else
421 {
422 if (pszBuffer != NULL)
423 {
424 pStr = pszBuffer;
425 pStr[0] = 0;
426 }
427 }
428 return pStr;
429}
430
431
432//
433// Get binary (byte array) variable
434// Result will be placed to the buffer provided (no more than dwBufSize bytes,
435// and actual size of data will be returned
436// If pBuffer is NULL, just actual data length is returned
437//
438
439DWORD CSCPMessage::GetVariableBinary(DWORD dwVarId, BYTE *pBuffer, DWORD dwBufSize)
440{
441 void *pValue;
442 DWORD dwSize;
443
444 pValue = Get(dwVarId, CSCP_DT_BINARY);
445 if (pValue != NULL)
446 {
447 dwSize = *((DWORD *)pValue);
448 if (pBuffer != NULL)
449 memcpy(pBuffer, (BYTE *)pValue + 4, min(dwBufSize, dwSize));
450 }
451 else
452 {
453 dwSize = 0;
454 }
455 return dwSize;
456}
457
458
459//
460// Build protocol message ready to be send over the wire
461//
462
463CSCP_MESSAGE *CSCPMessage::CreateMessage(void)
464{
465 DWORD dwSize;
466 int iVarSize;
467 DWORD i, j;
468 CSCP_MESSAGE *pMsg;
469 CSCP_DF *pVar;
470
471 // Calculate message size
472 for(i = 0, dwSize = CSCP_HEADER_SIZE; i < m_dwNumVar; i++)
473 {
474 iVarSize = VariableSize(m_ppVarList[i], FALSE);
475 if (m_nVersion >= 2)
476 dwSize += iVarSize + ((8 - (iVarSize % 8)) & 7);
477 else
478 dwSize += iVarSize;
479 }
480
481 // Message should be aligned to 8 bytes boundary
482 // This is always the case starting from version 2 because
483 // all variables padded to be _kratnimi_ 8 bytes
484 if (m_nVersion < 2)
485 dwSize += (8 - (dwSize % 8)) & 7;
486
487 // Create message
488 pMsg = (CSCP_MESSAGE *)malloc(dwSize);
489 pMsg->wCode = htons(m_wCode);
490 pMsg->wFlags = htons(m_wFlags);
491 pMsg->dwSize = htonl(dwSize);
492 pMsg->dwId = htonl(m_dwId);
493 pMsg->dwNumVars = htonl(m_dwNumVar);
494
495 // Fill data fields
496 for(i = 0, pVar = (CSCP_DF *)((char *)pMsg + CSCP_HEADER_SIZE); i < m_dwNumVar; i++)
497 {
498 iVarSize = VariableSize(m_ppVarList[i], FALSE);
499 memcpy(pVar, m_ppVarList[i], iVarSize);
500
501 // Convert numeric values to network format
502 pVar->dwVarId = htonl(pVar->dwVarId);
503 switch(pVar->bType)
504 {
505 case CSCP_DT_INTEGER:
506 pVar->df_int32 = htonl(pVar->df_int32);
507 break;
508 case CSCP_DT_INT64:
509 pVar->df_int64 = htonq(pVar->df_int64);
510 break;
511 case CSCP_DT_INT16:
512 pVar->df_int16 = htons(pVar->df_int16);
513 break;
514 case CSCP_DT_FLOAT:
515 pVar->df_real = htond(pVar->df_real);
516 break;
517 case CSCP_DT_STRING:
518#if !(WORDS_BIGENDIAN)
519 for(j = 0; j < pVar->df_string.dwLen / 2; j++)
520 pVar->df_string.szValue[j] = htons(pVar->df_string.szValue[j]);
521 pVar->df_string.dwLen = htonl(pVar->df_string.dwLen);
522#endif
523 break;
524 case CSCP_DT_BINARY:
525 pVar->df_string.dwLen = htonl(pVar->df_string.dwLen);
526 break;
527 }
528
529 if (m_nVersion >= 2)
530 pVar = (CSCP_DF *)((char *)pVar + iVarSize + ((8 - (iVarSize % 8)) & 7));
531 else
532 pVar = (CSCP_DF *)((char *)pVar + iVarSize);
533 }
534
535 return pMsg;
536}
537
538
539//
540// Delete all variables
541//
542
543void CSCPMessage::DeleteAllVariables(void)
544{
545 if (m_ppVarList != NULL)
546 {
547 DWORD i;
548
549 for(i = 0; i < m_dwNumVar; i++)
c5d159d9 550 safe_free(m_ppVarList[i]);
563c2ae0
VK
551 free(m_ppVarList);
552
553 m_ppVarList = NULL;
554 m_dwNumVar = 0;
555 }
556}
557
558
559//
560// Set binary variable to an array of DWORDs
561//
562
563void CSCPMessage::SetVariableToInt32Array(DWORD dwVarId, DWORD dwNumElements, DWORD *pdwData)
564{
565 DWORD i, *pdwBuffer;
566
567 pdwBuffer = (DWORD *)Set(dwVarId, CSCP_DT_BINARY, pdwData, dwNumElements * sizeof(DWORD));
568 if (pdwBuffer != NULL)
569 {
570 pdwBuffer++; // First DWORD is a length field
571 for(i = 0; i < dwNumElements; i++) // Convert DWORDs to network byte order
572 pdwBuffer[i] = htonl(pdwBuffer[i]);
573 }
574}
575
576
577//
578// Get binary variable as an array of DWORDs
579//
580
581DWORD CSCPMessage::GetVariableInt32Array(DWORD dwVarId, DWORD dwNumElements, DWORD *pdwBuffer)
582{
583 DWORD i, dwSize;
584
585 dwSize = GetVariableBinary(dwVarId, (BYTE *)pdwBuffer, dwNumElements * sizeof(DWORD));
586 dwSize /= sizeof(DWORD); // Convert bytes to elements
587 for(i = 0; i < dwSize; i++)
588 pdwBuffer[i] = ntohl(pdwBuffer[i]);
589 return dwSize;
590}
591
592
593//
594// Set binary variable from file
595//
596
3aa0061c 597BOOL CSCPMessage::SetVariableFromFile(DWORD dwVarId, const TCHAR *pszFileName)
563c2ae0
VK
598{
599 FILE *pFile;
600 BYTE *pBuffer;
601 DWORD dwSize;
602 BOOL bResult = FALSE;
603
604 dwSize = (DWORD)FileSize(pszFileName);
605 pFile = _tfopen(pszFileName, _T("rb"));
606 if (pFile != NULL)
607 {
608 pBuffer = (BYTE *)Set(dwVarId, CSCP_DT_BINARY, NULL, dwSize);
609 if (pBuffer != NULL)
610 {
611 if (fread(pBuffer + sizeof(DWORD), 1, dwSize, pFile) == dwSize)
612 bResult = TRUE;
613 }
614 fclose(pFile);
615 }
616 return bResult;
617}