Preparation for UNICODE support on UNIX
[public/netxms.git] / src / libnetxms / message.cpp
1 /* $Id: message.cpp,v 1.6 2008-01-28 18:09:38 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_UCS2
215 UCS2CHAR *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 #ifdef UNICODE_UCS2
243 memcpy(pVar->df_string.szValue, pValue, pVar->df_string.dwLen);
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);
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
291 void *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
314 DWORD 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
327 WORD 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
340 LONG 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
353 QWORD 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
366 double 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
383 TCHAR *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 {
397 #if defined(UNICODE) && defined(UNICODE_UCS4)
398 pStr = (TCHAR *)malloc(*((DWORD *)pValue) * 2 + 4);
399 #elif defined(UNICODE) && defined(UNICODE_UCS2)
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);
411 #if defined(UNICODE) && defined(UNICODE_UCS4)
412 ucs2_to_ucs4((UCS2CHAR *)pValue + 4, dwLen, pStr, dwLen + 1);
413 #elif defined(UNICODE) && defined(UNICODE_UCS2)
414 memcpy(pStr, (BYTE *)pValue + 4, dwLen * 2);
415 #else
416 ucs2_to_mb((UCS2CHAR *)((BYTE *)pValue + 4), dwLen, pStr, dwLen + 1);
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
439 DWORD 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
463 CSCP_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
543 void CSCPMessage::DeleteAllVariables(void)
544 {
545 if (m_ppVarList != NULL)
546 {
547 DWORD i;
548
549 for(i = 0; i < m_dwNumVar; i++)
550 safe_free(m_ppVarList[i]);
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
563 void 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
581 DWORD 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
597 BOOL CSCPMessage::SetVariableFromFile(DWORD dwVarId, const TCHAR *pszFileName)
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 }