7245fc27fa910a7be00589aa39550520b0ecfaf6
[public/netxms.git] / src / libnetxms / message.cpp
1 /* $Id$ */
2 /*
3 ** NetXMS - Network Management System
4 ** NetXMS Foundation Library
5 ** Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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 #include <expat.h>
27
28
29 //
30 // Parser state for creating CSCPMessage object from XML
31 //
32
33 #define XML_STATE_INIT -1
34 #define XML_STATE_END -2
35 #define XML_STATE_ERROR -255
36 #define XML_STATE_NXCP 0
37 #define XML_STATE_MESSAGE 1
38 #define XML_STATE_VARIABLE 2
39 #define XML_STATE_VALUE 3
40
41 typedef struct
42 {
43 CSCPMessage *msg;
44 int state;
45 int valueLen;
46 char *value;
47 int varType;
48 DWORD varId;
49 } XML_PARSER_STATE;
50
51
52 //
53 // Calculate variable size
54 //
55
56 static int VariableSize(CSCP_DF *pVar, BOOL bNetworkByteOrder)
57 {
58 int nSize;
59
60 switch(pVar->bType)
61 {
62 case CSCP_DT_INTEGER:
63 nSize = 12;
64 break;
65 case CSCP_DT_INT64:
66 case CSCP_DT_FLOAT:
67 nSize = 16;
68 break;
69 case CSCP_DT_INT16:
70 nSize = 8;
71 break;
72 case CSCP_DT_STRING:
73 case CSCP_DT_BINARY:
74 if (bNetworkByteOrder)
75 nSize = ntohl(pVar->df_string.dwLen) + 12;
76 else
77 nSize = pVar->df_string.dwLen + 12;
78 break;
79 default:
80 nSize = 8;
81 break;
82 }
83 return nSize;
84 }
85
86
87 //
88 // Default constructor for CSCPMessage class
89 //
90
91 CSCPMessage::CSCPMessage(int nVersion)
92 {
93 m_wCode = 0;
94 m_dwId = 0;
95 m_dwNumVar = 0;
96 m_ppVarList = NULL;
97 m_wFlags = 0;
98 m_nVersion = nVersion;
99 }
100
101
102 //
103 // Create a copy of prepared CSCP message
104 //
105
106 CSCPMessage::CSCPMessage(CSCPMessage *pMsg)
107 {
108 DWORD i;
109
110 m_wCode = pMsg->m_wCode;
111 m_dwId = pMsg->m_dwId;
112 m_wFlags = pMsg->m_wFlags;
113 m_nVersion = pMsg->m_nVersion;
114 m_dwNumVar = pMsg->m_dwNumVar;
115 m_ppVarList = (CSCP_DF **)malloc(sizeof(CSCP_DF *) * m_dwNumVar);
116 for(i = 0; i < m_dwNumVar; i++)
117 {
118 m_ppVarList[i] = (CSCP_DF *)nx_memdup(pMsg->m_ppVarList[i],
119 VariableSize(pMsg->m_ppVarList[i], FALSE));
120 }
121 }
122
123
124 //
125 // Create CSCPMessage object from received message
126 //
127
128 CSCPMessage::CSCPMessage(CSCP_MESSAGE *pMsg, int nVersion)
129 {
130 DWORD i, dwPos, dwSize, dwVar;
131 CSCP_DF *pVar;
132 int iVarSize;
133
134 m_wFlags = ntohs(pMsg->wFlags);
135 m_wCode = ntohs(pMsg->wCode);
136 m_dwId = ntohl(pMsg->dwId);
137 dwSize = ntohl(pMsg->dwSize);
138 m_dwNumVar = ntohl(pMsg->dwNumVars);
139 m_ppVarList = (CSCP_DF **)malloc(sizeof(CSCP_DF *) * m_dwNumVar);
140 m_nVersion = nVersion;
141
142 // Parse data fields
143 for(dwPos = CSCP_HEADER_SIZE, dwVar = 0; dwVar < m_dwNumVar; dwVar++)
144 {
145 pVar = (CSCP_DF *)(((BYTE *)pMsg) + dwPos);
146
147 // Validate position inside message
148 if (dwPos > dwSize - 8)
149 break;
150 if ((dwPos > dwSize - 12) &&
151 ((pVar->bType == CSCP_DT_STRING) || (pVar->bType == CSCP_DT_BINARY)))
152 break;
153
154 // Calculate and validate variable size
155 iVarSize = VariableSize(pVar, TRUE);
156 if (dwPos + iVarSize > dwSize)
157 break;
158
159 // Create new entry
160 m_ppVarList[dwVar] = (CSCP_DF *)malloc(iVarSize);
161 memcpy(m_ppVarList[dwVar], pVar, iVarSize);
162
163 // Convert numeric values to host format
164 m_ppVarList[dwVar]->dwVarId = ntohl(m_ppVarList[dwVar]->dwVarId);
165 switch(pVar->bType)
166 {
167 case CSCP_DT_INTEGER:
168 m_ppVarList[dwVar]->df_int32 = ntohl(m_ppVarList[dwVar]->df_int32);
169 break;
170 case CSCP_DT_INT64:
171 m_ppVarList[dwVar]->df_int64 = ntohq(m_ppVarList[dwVar]->df_int64);
172 break;
173 case CSCP_DT_INT16:
174 m_ppVarList[dwVar]->df_int16 = ntohs(m_ppVarList[dwVar]->df_int16);
175 break;
176 case CSCP_DT_FLOAT:
177 m_ppVarList[dwVar]->df_real = ntohd(m_ppVarList[dwVar]->df_real);
178 break;
179 case CSCP_DT_STRING:
180 #if !(WORDS_BIGENDIAN)
181 m_ppVarList[dwVar]->df_string.dwLen = ntohl(m_ppVarList[dwVar]->df_string.dwLen);
182 for(i = 0; i < m_ppVarList[dwVar]->df_string.dwLen / 2; i++)
183 m_ppVarList[dwVar]->df_string.szValue[i] = ntohs(m_ppVarList[dwVar]->df_string.szValue[i]);
184 #endif
185 break;
186 case CSCP_DT_BINARY:
187 m_ppVarList[dwVar]->df_string.dwLen = ntohl(m_ppVarList[dwVar]->df_string.dwLen);
188 break;
189 }
190
191 // Starting from version 2, all variables should be 8-byte aligned
192 if (m_nVersion >= 2)
193 dwPos += iVarSize + ((8 - (iVarSize % 8)) & 7);
194 else
195 dwPos += iVarSize;
196 }
197
198 // Cut unfilled variables, if any
199 m_dwNumVar = dwVar;
200 }
201
202
203 //
204 // Create CSCPMessage object from XML document
205 //
206
207 static void StartElement(void *userData, const char *name, const char **attrs)
208 {
209 if (!strcmp(name, "nxcp"))
210 {
211 ((XML_PARSER_STATE *)userData)->state = XML_STATE_NXCP;
212 }
213 else if (!strcmp(name, "message"))
214 {
215 ((XML_PARSER_STATE *)userData)->state = XML_STATE_MESSAGE;
216 }
217 else if (!strcmp(name, "variable"))
218 {
219 ((XML_PARSER_STATE *)userData)->state = XML_STATE_VARIABLE;
220 }
221 else if (!strcmp(name, "value"))
222 {
223 ((XML_PARSER_STATE *)userData)->valueLen = 1;
224 ((XML_PARSER_STATE *)userData)->value = NULL;
225 ((XML_PARSER_STATE *)userData)->state = XML_STATE_VALUE;
226 }
227 else
228 {
229 ((XML_PARSER_STATE *)userData)->state = XML_STATE_ERROR;
230 }
231 if (((XML_PARSER_STATE *)userData)->state != XML_STATE_ERROR)
232 ((XML_PARSER_STATE *)userData)->msg->ProcessXMLToken(userData, attrs);
233 }
234
235 static void EndElement(void *userData, const char *name)
236 {
237 if (!strcmp(name, "nxcp"))
238 {
239 ((XML_PARSER_STATE *)userData)->state = XML_STATE_END;
240 }
241 else if (!strcmp(name, "message"))
242 {
243 ((XML_PARSER_STATE *)userData)->state = XML_STATE_NXCP;
244 }
245 else if (!strcmp(name, "variable"))
246 {
247 ((XML_PARSER_STATE *)userData)->state = XML_STATE_MESSAGE;
248 }
249 else if (!strcmp(name, "value"))
250 {
251 ((XML_PARSER_STATE *)userData)->msg->ProcessXMLData(userData);
252 safe_free(((XML_PARSER_STATE *)userData)->value);
253 ((XML_PARSER_STATE *)userData)->state = XML_STATE_VARIABLE;
254 }
255 }
256
257 static void CharData(void *userData, const XML_Char *s, int len)
258 {
259 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
260
261 if (ps->state != XML_STATE_VALUE)
262 return;
263
264 ps->value = (char *)realloc(ps->value, ps->valueLen + len);
265 memcpy(&ps->value[ps->valueLen - 1], s, len);
266 ps->valueLen += len;
267 ps->value[ps->valueLen - 1] = 0;
268 }
269
270 CSCPMessage::CSCPMessage(const char *xml)
271 {
272 XML_Parser parser = XML_ParserCreate(NULL);
273 XML_PARSER_STATE state;
274
275 // Default values
276 m_wCode = 0;
277 m_dwId = 0;
278 m_dwNumVar = 0;
279 m_ppVarList = NULL;
280 m_wFlags = 0;
281 m_nVersion = NXCP_VERSION;
282
283 // Parse XML
284 state.msg = this;
285 state.state = -1;
286 XML_SetUserData(parser, &state);
287 XML_SetElementHandler(parser, StartElement, EndElement);
288 XML_SetCharacterDataHandler(parser, CharData);
289 if (XML_Parse(parser, xml, (int)strlen(xml), TRUE) == XML_STATUS_ERROR)
290 {
291 /*fprintf(stderr,
292 "%s at line %d\n",
293 XML_ErrorString(XML_GetErrorCode(parser)),
294 XML_GetCurrentLineNumber(parser));*/
295 }
296 XML_ParserFree(parser);
297 }
298
299 void CSCPMessage::ProcessXMLToken(void *state, const char **attrs)
300 {
301 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)state;
302 const char *type;
303 static const char *types[] = { "int32", "string", "int64", "int16", "binary", "float", NULL };
304
305 switch(ps->state)
306 {
307 case XML_STATE_NXCP:
308 m_nVersion = XMLGetAttrInt(attrs, "version", m_nVersion);
309 break;
310 case XML_STATE_MESSAGE:
311 m_dwId = XMLGetAttrDWORD(attrs, "id", m_dwId);
312 m_wCode = (WORD)XMLGetAttrDWORD(attrs, "code", m_wCode);
313 break;
314 case XML_STATE_VARIABLE:
315 ps->varId = XMLGetAttrDWORD(attrs, "id", 0);
316 type = XMLGetAttr(attrs, "type");
317 if (type != NULL)
318 {
319 int i;
320
321 for(i = 0; types[i] != NULL; i++)
322 if (!stricmp(types[i], type))
323 {
324 ps->varType = i;
325 break;
326 }
327 }
328 break;
329 default:
330 break;
331 }
332 }
333
334 void CSCPMessage::ProcessXMLData(void *state)
335 {
336 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)state;
337 char *binData;
338 size_t binLen;
339 #ifdef UNICODE
340 WCHAR *temp;
341 #endif
342
343 if (ps->value == NULL)
344 return;
345
346 switch(ps->varType)
347 {
348 case CSCP_DT_INTEGER:
349 SetVariable(ps->varId, (DWORD)strtoul(ps->value, NULL, 0));
350 break;
351 case CSCP_DT_INT16:
352 SetVariable(ps->varId, (WORD)strtoul(ps->value, NULL, 0));
353 break;
354 case CSCP_DT_INT64:
355 SetVariable(ps->varId, (QWORD)strtoull(ps->value, NULL, 0));
356 break;
357 case CSCP_DT_FLOAT:
358 SetVariable(ps->varId, strtod(ps->value, NULL));
359 break;
360 case CSCP_DT_STRING:
361 #ifdef UNICODE
362 temp = WideStringFromUTF8String(ps->value);
363 SetVariable(ps->varId, temp);
364 free(temp);
365 #else
366 SetVariable(ps->varId, ps->value);
367 #endif
368 break;
369 case CSCP_DT_BINARY:
370 if (base64_decode_alloc(ps->value, ps->valueLen, &binData, &binLen))
371 {
372 if (binData != NULL)
373 {
374 SetVariable(ps->varId, (BYTE *)binData, (DWORD)binLen);
375 free(binData);
376 }
377 }
378 break;
379 }
380 }
381
382
383 //
384 // Destructor for CSCPMessage
385 //
386
387 CSCPMessage::~CSCPMessage()
388 {
389 DeleteAllVariables();
390 }
391
392
393 //
394 // Find variable by name
395 //
396
397 DWORD CSCPMessage::FindVariable(DWORD dwVarId)
398 {
399 DWORD i;
400
401 for(i = 0; i < m_dwNumVar; i++)
402 if (m_ppVarList[i] != NULL)
403 if (m_ppVarList[i]->dwVarId == dwVarId)
404 return i;
405 return INVALID_INDEX;
406 }
407
408
409 //
410 // Set variable
411 // Argument dwSize (data size) is used only for DT_BINARY type
412 //
413
414 void *CSCPMessage::Set(DWORD dwVarId, BYTE bType, const void *pValue, DWORD dwSize)
415 {
416 DWORD dwIndex, dwLength;
417 CSCP_DF *pVar;
418 #if !defined(UNICODE_UCS2) || !defined(UNICODE)
419 UCS2CHAR *pBuffer;
420 #endif
421
422 // Create CSCP_DF structure
423 switch(bType)
424 {
425 case CSCP_DT_INTEGER:
426 pVar = (CSCP_DF *)malloc(12);
427 pVar->df_int32 = *((const DWORD *)pValue);
428 break;
429 case CSCP_DT_INT16:
430 pVar = (CSCP_DF *)malloc(8);
431 pVar->df_int16 = *((const WORD *)pValue);
432 break;
433 case CSCP_DT_INT64:
434 pVar = (CSCP_DF *)malloc(16);
435 pVar->df_int64 = *((const QWORD *)pValue);
436 break;
437 case CSCP_DT_FLOAT:
438 pVar = (CSCP_DF *)malloc(16);
439 pVar->df_real = *((const double *)pValue);
440 break;
441 case CSCP_DT_STRING:
442 dwLength = (DWORD)_tcslen((const TCHAR *)pValue);
443 pVar = (CSCP_DF *)malloc(12 + dwLength * 2);
444 pVar->df_string.dwLen = dwLength * 2;
445 #ifdef UNICODE
446 #ifdef UNICODE_UCS2
447 memcpy(pVar->df_string.szValue, pValue, pVar->df_string.dwLen);
448 #else /* assume UNICODE_UCS4 */
449 pBuffer = (UCS2CHAR *)malloc(dwLength * 2 + 2);
450 ucs4_to_ucs2((WCHAR *)pValue, dwLength, pBuffer, dwLength + 1);
451 memcpy(pVar->df_string.szValue, pBuffer, pVar->df_string.dwLen);
452 free(pBuffer);
453 #endif
454 #else /* not UNICODE */
455 pBuffer = (UCS2CHAR *)malloc(dwLength * 2 + 2);
456 mb_to_ucs2((const char *)pValue, dwLength, pBuffer, dwLength + 1);
457 memcpy(pVar->df_string.szValue, pBuffer, pVar->df_string.dwLen);
458 free(pBuffer);
459 #endif
460 break;
461 case CSCP_DT_BINARY:
462 pVar = (CSCP_DF *)malloc(12 + dwSize);
463 pVar->df_string.dwLen = dwSize;
464 if ((pVar->df_string.dwLen > 0) && (pValue != NULL))
465 memcpy(pVar->df_string.szValue, pValue, pVar->df_string.dwLen);
466 break;
467 default:
468 return NULL; // Invalid data type, unable to handle
469 }
470 pVar->dwVarId = dwVarId;
471 pVar->bType = bType;
472
473 // Check if variable exists
474 dwIndex = FindVariable(pVar->dwVarId);
475 if (dwIndex == INVALID_INDEX) // Add new variable to list
476 {
477 m_ppVarList = (CSCP_DF **)realloc(m_ppVarList, sizeof(CSCP_DF *) * (m_dwNumVar + 1));
478 m_ppVarList[m_dwNumVar] = pVar;
479 m_dwNumVar++;
480 }
481 else // Replace existing variable
482 {
483 free(m_ppVarList[dwIndex]);
484 m_ppVarList[dwIndex] = pVar;
485 }
486
487 return (bType == CSCP_DT_INT16) ? ((void *)((BYTE *)pVar + 6)) : ((void *)((BYTE *)pVar + 8));
488 }
489
490
491 //
492 // Get variable value
493 //
494
495 void *CSCPMessage::Get(DWORD dwVarId, BYTE bType)
496 {
497 DWORD dwIndex;
498
499 // Find variable
500 dwIndex = FindVariable(dwVarId);
501 if (dwIndex == INVALID_INDEX)
502 return NULL; // No such variable
503
504 // Check data type
505 if (m_ppVarList[dwIndex]->bType != bType)
506 return NULL;
507
508 return (bType == CSCP_DT_INT16) ?
509 ((void *)((BYTE *)m_ppVarList[dwIndex] + 6)) :
510 ((void *)((BYTE *)m_ppVarList[dwIndex] + 8));
511 }
512
513
514 //
515 // Get integer variable
516 //
517
518 DWORD CSCPMessage::GetVariableLong(DWORD dwVarId)
519 {
520 char *pValue;
521
522 pValue = (char *)Get(dwVarId, CSCP_DT_INTEGER);
523 return pValue ? *((DWORD *)pValue) : 0;
524 }
525
526
527 //
528 // Get 16-bit integer variable
529 //
530
531 WORD CSCPMessage::GetVariableShort(DWORD dwVarId)
532 {
533 void *pValue;
534
535 pValue = Get(dwVarId, CSCP_DT_INT16);
536 return pValue ? *((WORD *)pValue) : 0;
537 }
538
539
540 //
541 // Get 16-bit integer variable as signel 32-bit integer
542 //
543
544 LONG CSCPMessage::GetVariableShortAsInt32(DWORD dwVarId)
545 {
546 void *pValue;
547
548 pValue = Get(dwVarId, CSCP_DT_INT16);
549 return pValue ? *((short *)pValue) : 0;
550 }
551
552
553 //
554 // Get 64-bit integer variable
555 //
556
557 QWORD CSCPMessage::GetVariableInt64(DWORD dwVarId)
558 {
559 char *pValue;
560
561 pValue = (char *)Get(dwVarId, CSCP_DT_INT64);
562 return pValue ? *((QWORD *)pValue) : 0;
563 }
564
565
566 //
567 // Get 64-bit floating point variable
568 //
569
570 double CSCPMessage::GetVariableDouble(DWORD dwVarId)
571 {
572 char *pValue;
573
574 pValue = (char *)Get(dwVarId, CSCP_DT_FLOAT);
575 return pValue ? *((double *)pValue) : 0;
576 }
577
578
579 //
580 // Get string variable
581 // If szBuffer is NULL, memory block of required size will be allocated
582 // for result; if szBuffer is not NULL, entire result or part of it will
583 // be placed to szBuffer and pointer to szBuffer will be returned.
584 // Note: dwBufSize is buffer size in characters, not bytes!
585 //
586
587 TCHAR *CSCPMessage::GetVariableStr(DWORD dwVarId, TCHAR *pszBuffer, DWORD dwBufSize)
588 {
589 void *pValue;
590 TCHAR *pStr = NULL;
591 DWORD dwLen;
592
593 if ((pszBuffer != NULL) && (dwBufSize == 0))
594 return NULL; // non-sense combination
595
596 pValue = Get(dwVarId, CSCP_DT_STRING);
597 if (pValue != NULL)
598 {
599 if (pszBuffer == NULL)
600 {
601 #if defined(UNICODE) && defined(UNICODE_UCS4)
602 pStr = (TCHAR *)malloc(*((DWORD *)pValue) * 2 + 4);
603 #elif defined(UNICODE) && defined(UNICODE_UCS2)
604 pStr = (TCHAR *)malloc(*((DWORD *)pValue) + 2);
605 #else
606 pStr = (TCHAR *)malloc(*((DWORD *)pValue) / 2 + 1);
607 #endif
608 }
609 else
610 {
611 pStr = pszBuffer;
612 }
613
614 dwLen = (pszBuffer == NULL) ? (*((DWORD *)pValue) / 2) : min(*((DWORD *)pValue) / 2, dwBufSize - 1);
615 #if defined(UNICODE) && defined(UNICODE_UCS4)
616 ucs2_to_ucs4((UCS2CHAR *)((BYTE *)pValue + 4), dwLen, pStr, dwLen + 1);
617 #elif defined(UNICODE) && defined(UNICODE_UCS2)
618 memcpy(pStr, (BYTE *)pValue + 4, dwLen * 2);
619 #else
620 ucs2_to_mb((UCS2CHAR *)((BYTE *)pValue + 4), dwLen, pStr, dwLen + 1);
621 #endif
622 pStr[dwLen] = 0;
623 }
624 else
625 {
626 if (pszBuffer != NULL)
627 {
628 pStr = pszBuffer;
629 pStr[0] = 0;
630 }
631 }
632 return pStr;
633 }
634
635
636 //
637 // Get binary (byte array) variable
638 // Result will be placed to the buffer provided (no more than dwBufSize bytes,
639 // and actual size of data will be returned
640 // If pBuffer is NULL, just actual data length is returned
641 //
642
643 DWORD CSCPMessage::GetVariableBinary(DWORD dwVarId, BYTE *pBuffer, DWORD dwBufSize)
644 {
645 void *pValue;
646 DWORD dwSize;
647
648 pValue = Get(dwVarId, CSCP_DT_BINARY);
649 if (pValue != NULL)
650 {
651 dwSize = *((DWORD *)pValue);
652 if (pBuffer != NULL)
653 memcpy(pBuffer, (BYTE *)pValue + 4, min(dwBufSize, dwSize));
654 }
655 else
656 {
657 dwSize = 0;
658 }
659 return dwSize;
660 }
661
662
663 //
664 // Build protocol message ready to be send over the wire
665 //
666
667 CSCP_MESSAGE *CSCPMessage::CreateMessage(void)
668 {
669 DWORD dwSize;
670 int iVarSize;
671 DWORD i, j;
672 CSCP_MESSAGE *pMsg;
673 CSCP_DF *pVar;
674
675 // Calculate message size
676 for(i = 0, dwSize = CSCP_HEADER_SIZE; i < m_dwNumVar; i++)
677 {
678 iVarSize = VariableSize(m_ppVarList[i], FALSE);
679 if (m_nVersion >= 2)
680 dwSize += iVarSize + ((8 - (iVarSize % 8)) & 7);
681 else
682 dwSize += iVarSize;
683 }
684
685 // Message should be aligned to 8 bytes boundary
686 // This is always the case starting from version 2 because
687 // all variables padded to be _kratnimi_ 8 bytes
688 if (m_nVersion < 2)
689 dwSize += (8 - (dwSize % 8)) & 7;
690
691 // Create message
692 pMsg = (CSCP_MESSAGE *)malloc(dwSize);
693 pMsg->wCode = htons(m_wCode);
694 pMsg->wFlags = htons(m_wFlags);
695 pMsg->dwSize = htonl(dwSize);
696 pMsg->dwId = htonl(m_dwId);
697 pMsg->dwNumVars = htonl(m_dwNumVar);
698
699 // Fill data fields
700 for(i = 0, pVar = (CSCP_DF *)((char *)pMsg + CSCP_HEADER_SIZE); i < m_dwNumVar; i++)
701 {
702 iVarSize = VariableSize(m_ppVarList[i], FALSE);
703 memcpy(pVar, m_ppVarList[i], iVarSize);
704
705 // Convert numeric values to network format
706 pVar->dwVarId = htonl(pVar->dwVarId);
707 switch(pVar->bType)
708 {
709 case CSCP_DT_INTEGER:
710 pVar->df_int32 = htonl(pVar->df_int32);
711 break;
712 case CSCP_DT_INT64:
713 pVar->df_int64 = htonq(pVar->df_int64);
714 break;
715 case CSCP_DT_INT16:
716 pVar->df_int16 = htons(pVar->df_int16);
717 break;
718 case CSCP_DT_FLOAT:
719 pVar->df_real = htond(pVar->df_real);
720 break;
721 case CSCP_DT_STRING:
722 #if !(WORDS_BIGENDIAN)
723 for(j = 0; j < pVar->df_string.dwLen / 2; j++)
724 pVar->df_string.szValue[j] = htons(pVar->df_string.szValue[j]);
725 pVar->df_string.dwLen = htonl(pVar->df_string.dwLen);
726 #endif
727 break;
728 case CSCP_DT_BINARY:
729 pVar->df_string.dwLen = htonl(pVar->df_string.dwLen);
730 break;
731 }
732
733 if (m_nVersion >= 2)
734 pVar = (CSCP_DF *)((char *)pVar + iVarSize + ((8 - (iVarSize % 8)) & 7));
735 else
736 pVar = (CSCP_DF *)((char *)pVar + iVarSize);
737 }
738
739 return pMsg;
740 }
741
742
743 //
744 // Delete all variables
745 //
746
747 void CSCPMessage::DeleteAllVariables(void)
748 {
749 if (m_ppVarList != NULL)
750 {
751 DWORD i;
752
753 for(i = 0; i < m_dwNumVar; i++)
754 safe_free(m_ppVarList[i]);
755 free(m_ppVarList);
756
757 m_ppVarList = NULL;
758 m_dwNumVar = 0;
759 }
760 }
761
762
763 //
764 // Set binary variable to an array of DWORDs
765 //
766
767 void CSCPMessage::SetVariableToInt32Array(DWORD dwVarId, DWORD dwNumElements, DWORD *pdwData)
768 {
769 DWORD i, *pdwBuffer;
770
771 pdwBuffer = (DWORD *)Set(dwVarId, CSCP_DT_BINARY, pdwData, dwNumElements * sizeof(DWORD));
772 if (pdwBuffer != NULL)
773 {
774 pdwBuffer++; // First DWORD is a length field
775 for(i = 0; i < dwNumElements; i++) // Convert DWORDs to network byte order
776 pdwBuffer[i] = htonl(pdwBuffer[i]);
777 }
778 }
779
780
781 //
782 // Get binary variable as an array of DWORDs
783 //
784
785 DWORD CSCPMessage::GetVariableInt32Array(DWORD dwVarId, DWORD dwNumElements, DWORD *pdwBuffer)
786 {
787 DWORD i, dwSize;
788
789 dwSize = GetVariableBinary(dwVarId, (BYTE *)pdwBuffer, dwNumElements * sizeof(DWORD));
790 dwSize /= sizeof(DWORD); // Convert bytes to elements
791 for(i = 0; i < dwSize; i++)
792 pdwBuffer[i] = ntohl(pdwBuffer[i]);
793 return dwSize;
794 }
795
796
797 //
798 // Set binary variable from file
799 //
800
801 BOOL CSCPMessage::SetVariableFromFile(DWORD dwVarId, const TCHAR *pszFileName)
802 {
803 FILE *pFile;
804 BYTE *pBuffer;
805 DWORD dwSize;
806 BOOL bResult = FALSE;
807
808 dwSize = (DWORD)FileSize(pszFileName);
809 pFile = _tfopen(pszFileName, _T("rb"));
810 if (pFile != NULL)
811 {
812 pBuffer = (BYTE *)Set(dwVarId, CSCP_DT_BINARY, NULL, dwSize);
813 if (pBuffer != NULL)
814 {
815 if (fread(pBuffer + sizeof(DWORD), 1, dwSize, pFile) == dwSize)
816 bResult = TRUE;
817 }
818 fclose(pFile);
819 }
820 return bResult;
821 }
822
823
824 //
825 // Create XML document
826 //
827
828 char *CSCPMessage::CreateXML(void)
829 {
830 String xml;
831 DWORD i;
832 char *out, *bdata;
833 size_t blen;
834 TCHAR *tempStr;
835 #if !defined(UNICODE) || defined(UNICODE_UCS4)
836 int bytes;
837 #endif
838 static const TCHAR *dtString[] = { _T("int32"), _T("string"), _T("int64"), _T("int16"), _T("binary"), _T("float") };
839
840 xml.addFormattedString(_T("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<nxcp version=\"%d\">\r\n <message code=\"%d\" id=\"%d\">\r\n"), m_nVersion, m_wCode, m_dwId);
841 for(i = 0; i < m_dwNumVar; i++)
842 {
843 xml.addFormattedString(_T(" <variable id=\"%d\" type=\"%s\">\r\n <value>"),
844 m_ppVarList[i]->dwVarId, dtString[m_ppVarList[i]->bType]);
845 switch(m_ppVarList[i]->bType)
846 {
847 case CSCP_DT_INTEGER:
848 xml.addFormattedString(_T("%d"), m_ppVarList[i]->data.dwInteger);
849 break;
850 case CSCP_DT_INT16:
851 xml.addFormattedString(_T("%d"), m_ppVarList[i]->wInt16);
852 break;
853 case CSCP_DT_INT64:
854 xml.addFormattedString(INT64_FMT, m_ppVarList[i]->data.qwInt64);
855 break;
856 case CSCP_DT_STRING:
857 #ifdef UNICODE
858 #ifdef UNICODE_UCS2
859 xml.addDynamicString(EscapeStringForXML((TCHAR *)m_ppVarList[i]->data.string.szValue, m_ppVarList[i]->data.string.dwLen / 2));
860 #else
861 tempStr = (WCHAR *)malloc(m_ppVarList[i]->data.string.dwLen * 2);
862 bytes = ucs2_to_ucs4(m_ppVarList[i]->data.string.szValue, m_ppVarList[i]->data.string.dwLen / 2, tempStr, m_ppVarList[i]->data.string.dwLen / 2);
863 xml.addDynamicString(EscapeStringForXML(tempStr, bytes));
864 free(tempStr);
865 #endif
866 #else /* not UNICODE */
867 #ifdef UNICODE_UCS2
868 bytes = WideCharToMultiByte(CP_UTF8, 0, (UCS2CHAR *)m_ppVarList[i]->data.string.szValue,
869 m_ppVarList[i]->data.string.dwLen / 2, NULL, 0, NULL, NULL);
870 tempStr = (char *)malloc(bytes + 1);
871 bytes = WideCharToMultiByte(CP_UTF8, 0, (UCS2CHAR *)m_ppVarList[i]->data.string.szValue,
872 m_ppVarList[i]->data.string.dwLen / 2, tempStr, bytes + 1, NULL, NULL);
873 xml.addDynamicString(EscapeStringForXML(tempStr, bytes));
874 free(tempStr);
875 #else
876 tempStr = (char *)malloc(m_ppVarList[i]->data.string.dwLen);
877 bytes = ucs2_to_utf8(m_ppVarList[i]->data.string.szValue, m_ppVarList[i]->data.string.dwLen / 2, tempStr, m_ppVarList[i]->data.string.dwLen);
878 xml.addDynamicString(EscapeStringForXML(tempStr, bytes));
879 free(tempStr);
880 #endif
881 #endif /* UNICODE */
882 break;
883 case CSCP_DT_BINARY:
884 blen = base64_encode_alloc((char *)m_ppVarList[i]->data.string.szValue,
885 m_ppVarList[i]->data.string.dwLen, &bdata);
886 if ((blen != 0) && (bdata != NULL))
887 {
888 #ifdef UNICODE
889 tempStr = (WCHAR *)malloc((blen + 1) * sizeof(WCHAR));
890 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, bdata, (int)blen, tempStr, (int)blen);
891 tempStr[blen] = 0;
892 xml.addDynamicString(tempStr);
893 #else
894 xml.addString(bdata, (DWORD)blen);
895 #endif
896 }
897 safe_free(bdata);
898 break;
899 default:
900 break;
901 }
902 xml += _T("</value>\r\n </variable>\r\n");
903 }
904 xml += _T(" </message>\r\n</nxcp>\r\n");
905
906 #ifdef UNICODE
907 out = UTF8StringFromWideString(xml);
908 #else
909 out = strdup(xml);
910 #endif
911 return out;
912 }