6735b13cb9faeee19f0d586e847dc5b3539e2919
[public/netxms.git] / src / libnetxms / message.cpp
1 /* $Id: message.cpp,v 1.5 2007-09-19 16:57:40 victor Exp $ */
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
32 static 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
67 CSCPMessage::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
82 CSCPMessage::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
104 CSCPMessage::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
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
131 iVarSize = VariableSize(pVar, TRUE);
132 if (dwPos + iVarSize > dwSize)
133 break;
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 }
173
174 // Cut unfilled variables, if any
175 m_dwNumVar = dwVar;
176 }
177
178
179 //
180 // Destructor for CSCPMessage
181 //
182
183 CSCPMessage::~CSCPMessage()
184 {
185 DeleteAllVariables();
186 }
187
188
189 //
190 // Find variable by name
191 //
192
193 DWORD CSCPMessage::FindVariable(DWORD dwVarId)
194 {
195 DWORD i;
196
197 for(i = 0; i < m_dwNumVar; i++)
198 if (m_ppVarList[i] != NULL)
199 if (m_ppVarList[i]->dwVarId == dwVarId)
200 return i;
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
210 void *CSCPMessage::Set(DWORD dwVarId, BYTE bType, const void *pValue, DWORD dwSize)
211 {
212 DWORD dwIndex, dwLength;
213 CSCP_DF *pVar;
214 #ifndef UNICODE
215 WCHAR *pBuffer;
216 #endif
217
218 // Create CSCP_DF structure
219 switch(bType)
220 {
221 case CSCP_DT_INTEGER:
222 pVar = (CSCP_DF *)malloc(12);
223 pVar->df_int32 = *((const DWORD *)pValue);
224 break;
225 case CSCP_DT_INT16:
226 pVar = (CSCP_DF *)malloc(8);
227 pVar->df_int16 = *((const WORD *)pValue);
228 break;
229 case CSCP_DT_INT64:
230 pVar = (CSCP_DF *)malloc(16);
231 pVar->df_int64 = *((const QWORD *)pValue);
232 break;
233 case CSCP_DT_FLOAT:
234 pVar = (CSCP_DF *)malloc(16);
235 pVar->df_real = *((const double *)pValue);
236 break;
237 case CSCP_DT_STRING:
238 dwLength = (DWORD)_tcslen((const TCHAR *)pValue);
239 pVar = (CSCP_DF *)malloc(12 + dwLength * 2);
240 pVar->df_string.dwLen = dwLength * 2;
241 #ifdef UNICODE
242 memcpy(pVar->df_string.szValue, pValue, pVar->df_string.dwLen);
243 #else
244 pBuffer = (WCHAR *)malloc(dwLength * 2 + 2);
245 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (const char *)pValue, dwLength, pBuffer, dwLength + 1);
246 memcpy(pVar->df_string.szValue, pBuffer, pVar->df_string.dwLen);
247 free(pBuffer);
248 #endif
249 break;
250 case CSCP_DT_BINARY:
251 pVar = (CSCP_DF *)malloc(12 + dwSize);
252 pVar->df_string.dwLen = dwSize;
253 if ((pVar->df_string.dwLen > 0) && (pValue != NULL))
254 memcpy(pVar->df_string.szValue, pValue, pVar->df_string.dwLen);
255 break;
256 default:
257 return NULL; // Invalid data type, unable to handle
258 }
259 pVar->dwVarId = dwVarId;
260 pVar->bType = bType;
261
262 // Check if variable exists
263 dwIndex = FindVariable(pVar->dwVarId);
264 if (dwIndex == INVALID_INDEX) // Add new variable to list
265 {
266 m_ppVarList = (CSCP_DF **)realloc(m_ppVarList, sizeof(CSCP_DF *) * (m_dwNumVar + 1));
267 m_ppVarList[m_dwNumVar] = pVar;
268 m_dwNumVar++;
269 }
270 else // Replace existing variable
271 {
272 free(m_ppVarList[dwIndex]);
273 m_ppVarList[dwIndex] = pVar;
274 }
275
276 return (bType == CSCP_DT_INT16) ? ((void *)((BYTE *)pVar + 6)) : ((void *)((BYTE *)pVar + 8));
277 }
278
279
280 //
281 // Get variable value
282 //
283
284 void *CSCPMessage::Get(DWORD dwVarId, BYTE bType)
285 {
286 DWORD dwIndex;
287
288 // Find variable
289 dwIndex = FindVariable(dwVarId);
290 if (dwIndex == INVALID_INDEX)
291 return NULL; // No such variable
292
293 // Check data type
294 if (m_ppVarList[dwIndex]->bType != bType)
295 return NULL;
296
297 return (bType == CSCP_DT_INT16) ?
298 ((void *)((BYTE *)m_ppVarList[dwIndex] + 6)) :
299 ((void *)((BYTE *)m_ppVarList[dwIndex] + 8));
300 }
301
302
303 //
304 // Get integer variable
305 //
306
307 DWORD CSCPMessage::GetVariableLong(DWORD dwVarId)
308 {
309 char *pValue;
310
311 pValue = (char *)Get(dwVarId, CSCP_DT_INTEGER);
312 return pValue ? *((DWORD *)pValue) : 0;
313 }
314
315
316 //
317 // Get 16-bit integer variable
318 //
319
320 WORD CSCPMessage::GetVariableShort(DWORD dwVarId)
321 {
322 void *pValue;
323
324 pValue = Get(dwVarId, CSCP_DT_INT16);
325 return pValue ? *((WORD *)pValue) : 0;
326 }
327
328
329 //
330 // Get 16-bit integer variable as signel 32-bit integer
331 //
332
333 LONG CSCPMessage::GetVariableShortAsInt32(DWORD dwVarId)
334 {
335 void *pValue;
336
337 pValue = Get(dwVarId, CSCP_DT_INT16);
338 return pValue ? *((short *)pValue) : 0;
339 }
340
341
342 //
343 // Get 64-bit integer variable
344 //
345
346 QWORD CSCPMessage::GetVariableInt64(DWORD dwVarId)
347 {
348 char *pValue;
349
350 pValue = (char *)Get(dwVarId, CSCP_DT_INT64);
351 return pValue ? *((QWORD *)pValue) : 0;
352 }
353
354
355 //
356 // Get 64-bit floating point variable
357 //
358
359 double CSCPMessage::GetVariableDouble(DWORD dwVarId)
360 {
361 char *pValue;
362
363 pValue = (char *)Get(dwVarId, CSCP_DT_FLOAT);
364 return pValue ? *((double *)pValue) : 0;
365 }
366
367
368 //
369 // Get string variable
370 // If szBuffer is NULL, memory block of required size will be allocated
371 // for result; if szBuffer is not NULL, entire result or part of it will
372 // be placed to szBuffer and pointer to szBuffer will be returned.
373 // Note: dwBufSize is buffer size in characters, not bytes!
374 //
375
376 TCHAR *CSCPMessage::GetVariableStr(DWORD dwVarId, TCHAR *pszBuffer, DWORD dwBufSize)
377 {
378 void *pValue;
379 TCHAR *pStr = NULL;
380 DWORD dwLen;
381
382 if ((pszBuffer != NULL) && (dwBufSize == 0))
383 return NULL; // non-sense combination
384
385 pValue = Get(dwVarId, CSCP_DT_STRING);
386 if (pValue != NULL)
387 {
388 if (pszBuffer == NULL)
389 {
390 #ifdef UNICODE
391 pStr = (TCHAR *)malloc(*((DWORD *)pValue) + 2);
392 #else
393 pStr = (TCHAR *)malloc(*((DWORD *)pValue) / 2 + 1);
394 #endif
395 }
396 else
397 {
398 pStr = pszBuffer;
399 }
400
401 dwLen = (pszBuffer == NULL) ? (*((DWORD *)pValue) / 2) : min(*((DWORD *)pValue) / 2, dwBufSize - 1);
402 #ifdef UNICODE
403 memcpy(pStr, (BYTE *)pValue + 4, dwLen * 2);
404 #else
405 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
406 (WCHAR *)((BYTE *)pValue + 4), dwLen, pStr, dwLen + 1, NULL, NULL);
407 #endif
408 pStr[dwLen] = 0;
409 }
410 else
411 {
412 if (pszBuffer != NULL)
413 {
414 pStr = pszBuffer;
415 pStr[0] = 0;
416 }
417 }
418 return pStr;
419 }
420
421
422 //
423 // Get binary (byte array) variable
424 // Result will be placed to the buffer provided (no more than dwBufSize bytes,
425 // and actual size of data will be returned
426 // If pBuffer is NULL, just actual data length is returned
427 //
428
429 DWORD CSCPMessage::GetVariableBinary(DWORD dwVarId, BYTE *pBuffer, DWORD dwBufSize)
430 {
431 void *pValue;
432 DWORD dwSize;
433
434 pValue = Get(dwVarId, CSCP_DT_BINARY);
435 if (pValue != NULL)
436 {
437 dwSize = *((DWORD *)pValue);
438 if (pBuffer != NULL)
439 memcpy(pBuffer, (BYTE *)pValue + 4, min(dwBufSize, dwSize));
440 }
441 else
442 {
443 dwSize = 0;
444 }
445 return dwSize;
446 }
447
448
449 //
450 // Build protocol message ready to be send over the wire
451 //
452
453 CSCP_MESSAGE *CSCPMessage::CreateMessage(void)
454 {
455 DWORD dwSize;
456 int iVarSize;
457 DWORD i, j;
458 CSCP_MESSAGE *pMsg;
459 CSCP_DF *pVar;
460
461 // Calculate message size
462 for(i = 0, dwSize = CSCP_HEADER_SIZE; i < m_dwNumVar; i++)
463 {
464 iVarSize = VariableSize(m_ppVarList[i], FALSE);
465 if (m_nVersion >= 2)
466 dwSize += iVarSize + ((8 - (iVarSize % 8)) & 7);
467 else
468 dwSize += iVarSize;
469 }
470
471 // Message should be aligned to 8 bytes boundary
472 // This is always the case starting from version 2 because
473 // all variables padded to be _kratnimi_ 8 bytes
474 if (m_nVersion < 2)
475 dwSize += (8 - (dwSize % 8)) & 7;
476
477 // Create message
478 pMsg = (CSCP_MESSAGE *)malloc(dwSize);
479 pMsg->wCode = htons(m_wCode);
480 pMsg->wFlags = htons(m_wFlags);
481 pMsg->dwSize = htonl(dwSize);
482 pMsg->dwId = htonl(m_dwId);
483 pMsg->dwNumVars = htonl(m_dwNumVar);
484
485 // Fill data fields
486 for(i = 0, pVar = (CSCP_DF *)((char *)pMsg + CSCP_HEADER_SIZE); i < m_dwNumVar; i++)
487 {
488 iVarSize = VariableSize(m_ppVarList[i], FALSE);
489 memcpy(pVar, m_ppVarList[i], iVarSize);
490
491 // Convert numeric values to network format
492 pVar->dwVarId = htonl(pVar->dwVarId);
493 switch(pVar->bType)
494 {
495 case CSCP_DT_INTEGER:
496 pVar->df_int32 = htonl(pVar->df_int32);
497 break;
498 case CSCP_DT_INT64:
499 pVar->df_int64 = htonq(pVar->df_int64);
500 break;
501 case CSCP_DT_INT16:
502 pVar->df_int16 = htons(pVar->df_int16);
503 break;
504 case CSCP_DT_FLOAT:
505 pVar->df_real = htond(pVar->df_real);
506 break;
507 case CSCP_DT_STRING:
508 #if !(WORDS_BIGENDIAN)
509 for(j = 0; j < pVar->df_string.dwLen / 2; j++)
510 pVar->df_string.szValue[j] = htons(pVar->df_string.szValue[j]);
511 pVar->df_string.dwLen = htonl(pVar->df_string.dwLen);
512 #endif
513 break;
514 case CSCP_DT_BINARY:
515 pVar->df_string.dwLen = htonl(pVar->df_string.dwLen);
516 break;
517 }
518
519 if (m_nVersion >= 2)
520 pVar = (CSCP_DF *)((char *)pVar + iVarSize + ((8 - (iVarSize % 8)) & 7));
521 else
522 pVar = (CSCP_DF *)((char *)pVar + iVarSize);
523 }
524
525 return pMsg;
526 }
527
528
529 //
530 // Delete all variables
531 //
532
533 void CSCPMessage::DeleteAllVariables(void)
534 {
535 if (m_ppVarList != NULL)
536 {
537 DWORD i;
538
539 for(i = 0; i < m_dwNumVar; i++)
540 safe_free(m_ppVarList[i]);
541 free(m_ppVarList);
542
543 m_ppVarList = NULL;
544 m_dwNumVar = 0;
545 }
546 }
547
548
549 //
550 // Set binary variable to an array of DWORDs
551 //
552
553 void CSCPMessage::SetVariableToInt32Array(DWORD dwVarId, DWORD dwNumElements, DWORD *pdwData)
554 {
555 DWORD i, *pdwBuffer;
556
557 pdwBuffer = (DWORD *)Set(dwVarId, CSCP_DT_BINARY, pdwData, dwNumElements * sizeof(DWORD));
558 if (pdwBuffer != NULL)
559 {
560 pdwBuffer++; // First DWORD is a length field
561 for(i = 0; i < dwNumElements; i++) // Convert DWORDs to network byte order
562 pdwBuffer[i] = htonl(pdwBuffer[i]);
563 }
564 }
565
566
567 //
568 // Get binary variable as an array of DWORDs
569 //
570
571 DWORD CSCPMessage::GetVariableInt32Array(DWORD dwVarId, DWORD dwNumElements, DWORD *pdwBuffer)
572 {
573 DWORD i, dwSize;
574
575 dwSize = GetVariableBinary(dwVarId, (BYTE *)pdwBuffer, dwNumElements * sizeof(DWORD));
576 dwSize /= sizeof(DWORD); // Convert bytes to elements
577 for(i = 0; i < dwSize; i++)
578 pdwBuffer[i] = ntohl(pdwBuffer[i]);
579 return dwSize;
580 }
581
582
583 //
584 // Set binary variable from file
585 //
586
587 BOOL CSCPMessage::SetVariableFromFile(DWORD dwVarId, const TCHAR *pszFileName)
588 {
589 FILE *pFile;
590 BYTE *pBuffer;
591 DWORD dwSize;
592 BOOL bResult = FALSE;
593
594 dwSize = (DWORD)FileSize(pszFileName);
595 pFile = _tfopen(pszFileName, _T("rb"));
596 if (pFile != NULL)
597 {
598 pBuffer = (BYTE *)Set(dwVarId, CSCP_DT_BINARY, NULL, dwSize);
599 if (pBuffer != NULL)
600 {
601 if (fread(pBuffer + sizeof(DWORD), 1, dwSize, pFile) == dwSize)
602 bResult = TRUE;
603 }
604 fclose(pFile);
605 }
606 return bResult;
607 }