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