1 mike 1.1 //BEGIN_LICENSE
2 //
3 // Copyright (c) 2000 The Open Group, BMC Software, Tivoli Systems, IBM
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
15 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18 // DEALINGS IN THE SOFTWARE.
19 //
20 //END_LICENSE
21 //BEGIN_HISTORY
22 mike 1.1 //
23 // Author:
24 //
25 // $Log$
26 //
27 //END_HISTORY
28
29 #include <cstdlib>
30 #include <cstdio>
31 #include "ClassDecl.h"
32 #include "QualifierDecl.h"
33 #include "XmlWriter.h"
34 #include "XmlParser.h"
35
36 PEGASUS_NAMESPACE_BEGIN
37
38 inline void AppendChar(Array<Sint8>& out, Char16 c)
39 {
40 out.append(Sint8(c));
41 }
42
43 mike 1.1 inline void AppendSpecialChar(Array<Sint8>& out, Char16 c)
44 {
45 // ATTN-B: Only UTF-8 handled for now.
46
47 switch (c)
48 {
49 case '&':
50 out.append("&", 5);
51 break;
52
53 case '<':
54 out.append("<", 4);
55 break;
56
57 case '>':
58 out.append(">", 4);
59 break;
60
61 case '"':
62 out.append(""", 6);
63 break;
64 mike 1.1
65 case '\'':
66 out.append("'", 6);
67 break;
68
69 default:
70 out.append(Sint8(c));
71 }
72 }
73
74 void XmlWriter::append(Array<Sint8>& out, Char16 x)
75 {
76 AppendChar(out, x);
77 }
78
79 void XmlWriter::append(Array<Sint8>& out, Uint32 x)
80 {
81 char buffer[32];
82 sprintf(buffer, "%d", x);
83 append(out, buffer);
84 }
85 mike 1.1
86 void XmlWriter::append(Array<Sint8>& out, const char* str)
87 {
88 while (*str)
89 AppendChar(out, *str++);
90 }
91
92 void XmlWriter::appendSpecial(Array<Sint8>& out, Char16 x)
93 {
94 AppendSpecialChar(out, x);
95 }
96
97 void XmlWriter::appendSpecial(Array<Sint8>& out, char x)
98 {
99 AppendSpecialChar(out, Char16(x));
100 }
101
102 void XmlWriter::appendSpecial(Array<Sint8>& out, const char* str)
103 {
104 while (*str)
105 AppendSpecialChar(out, *str++);
106 mike 1.1 }
107
108 void XmlWriter::appendSpecial(Array<Sint8>& out, const String& str)
109 {
110 const Char16* tmp = str.getData();
111
112 while (*tmp)
113 AppendSpecialChar(out, *tmp++);
114 }
115
116 void XmlWriter::append(Array<Sint8>& out, const String& str)
117 {
118 const Char16* tmp = str.getData();
119
120 while (*tmp)
121 AppendChar(out, *tmp++);
122 }
123
124 void XmlWriter::append(Array<Sint8>& out, const Indentor& x)
125 {
126 for (Uint32 i = 0; i < 4 * x.getLevel(); i++)
127 mike 1.1 out.append(' ');
128 }
129
130 void XmlWriter::appendLocalNameSpaceElement(
131 Array<Sint8>& out,
132 const String& nameSpace)
133 {
134 out << "<LOCALNAMESPACEPATH>\n";
135
136 char* tmp = nameSpace.allocateCString();
137
138 for (char* p = strtok(tmp, "/"); p; p = strtok(NULL, "/"))
139 {
140 out << "<NAMESPACE NAME=\"" << p << "\"/>\n";
141 }
142
143 out << "</LOCALNAMESPACEPATH>\n";
144 }
145
146 static inline void AppendSpecialChar(std::ostream& os, char c)
147 {
148 mike 1.1 switch (c)
149 {
150 case '&':
151 os << "&";
152 break;
153
154 case '<':
155 os << "<";
156 break;
157
158 case '>':
159 os << ">";
160 break;
161
162 case '"':
163 os << """;
164 break;
165
166 case '\'':
167 os << "'";
168 break;
169 mike 1.1
170 default:
171 os << c;
172 }
173 }
174
175 static inline void AppendSpecial(std::ostream& os, const char* str)
176 {
177 while (*str)
178 AppendSpecialChar(os, *str++);
179 }
180
181 //------------------------------------------------------------------------------
182 //
183 // formatGetHeader()
184 //
185 //------------------------------------------------------------------------------
186
187 Array<Sint8> XmlWriter::formatGetHeader(
188 const char* documentPath)
189 {
190 mike 1.1 Array<Sint8> out;
191 return out << "GET " << documentPath << "HTTP/1.0\r\n\r\n";
192 }
193
194 //------------------------------------------------------------------------------
195 //
196 // formatMPostHeader()
197 //
198 // Build HTTP request header.
199 //
200 //------------------------------------------------------------------------------
201
202 Array<Sint8> XmlWriter::formatMPostHeader(
203 const char* host,
204 const char* cimOperation,
205 const char* cimMethod,
206 const String& cimObject,
207 const Array<Sint8>& content)
208 {
209 Array<Sint8> out;
210 out.reserve(1024);
211 mike 1.1 char nn[] = { '0' + (rand() % 10), '0' + (rand() % 10), '\0' };
212
213 out << "M-POST /cimom HTTP/1.1\r\n";
214 out << "HOST: " << host << "\r\n";
215 out << "Content-Type: application/xml; charset=\"utf-8\"\r\n";
216 out << "Content-Length: " << content.getSize() << "\r\n";
217 out << "Man: http://www.dmtf.org/cim/mapping/http/v1.0; ns=";
218 out << nn <<"\r\n";
219 out << nn << "-CIMOperation: " << cimOperation << "\r\n";
220 out << nn << "-CIMMethod: " << cimMethod << "\r\n";
221 out << nn << "-CIMObject: " << cimObject << "\r\n\r\n";
222 out << content;
223 return out;
224 }
225
226 //------------------------------------------------------------------------------
227 //
228 // formatMethodResponseHeader()
229 //
230 // Build HTTP response header.
231 //
232 mike 1.1 //------------------------------------------------------------------------------
233
234 Array<Sint8> XmlWriter::formatMethodResponseHeader(
235 const Array<Sint8>& content)
236 {
237 Array<Sint8> out;
238 out.reserve(1024);
239 char nn[] = { '0' + (rand() % 10), '0' + (rand() % 10), '\0' };
240
241 out << "HTTP/1.1 200 OK\r\n";
242 out << "Content-Type: application/xml; charset=\"utf-8\"\r\n";
243 out << "Content-Length: " << content.getSize() << "\r\n";
244 out << "Ext:\r\n";
245 out << "Cache-Control: no-cache\r\n";
246 out << "Man: http://www.dmtf.org/cim/mapping/http/v1.0; ns=";
247 out << nn <<"\r\n";
248 out << nn << "-CIMOperation: MethodResponse\r\n\r\n";
249 out << content;
250 return out;
251 }
252
253 mike 1.1 //------------------------------------------------------------------------------
254 //
255 // formatMessageElement()
256 //
257 // <!ELEMENT MESSAGE (SIMPLEREQ|MULTIREQ|SIMPLERSP|MULTIRSP)>
258 // <!ATTLIST MESSAGE
259 // ID CDATA #REQUIRED
260 // PROTOCOLVERSION CDATA #REQUIRED>
261 //
262 //------------------------------------------------------------------------------
263
264 Array<Sint8> XmlWriter::formatMessageElement(
265 Uint32 messageId,
266 const Array<Sint8>& body)
267 {
268 Array<Sint8> out;
269 out.reserve(1024);
270
271 out << "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
272 out << "<CIM CIMVERSION=\"2.0\" DTDVERSION=\"2.0\">\n";
273 out << "<MESSAGE ID=\"" << messageId << "\" PROTOCOLVERSION=\"1.0\">\n";
274 mike 1.1 out << body;
275 out << "</MESSAGE>\n";
276 out << "</CIM>\n";
277 return out;
278 }
279
280 //------------------------------------------------------------------------------
281 //
282 // formatSimpleReqElement()
283 //
284 // <!ELEMENT SIMPLEREQ (IMETHODCALL|METHODCALL)>
285 //
286 //------------------------------------------------------------------------------
287
288 Array<Sint8> XmlWriter::formatSimpleReqElement(
289 const Array<Sint8>& body)
290 {
291 Array<Sint8> out;
292 return out << "<SIMPLEREQ>\n" << body << "</SIMPLEREQ>\n";
293 }
294
295 mike 1.1 //------------------------------------------------------------------------------
296 //
297 // formatSimpleRspElement()
298 //
299 // <!ELEMENT SIMPLERSP (METHODRESPONSE|IMETHODRESPONSE)>
300 //
301 //------------------------------------------------------------------------------
302
303 Array<Sint8> XmlWriter::formatSimpleRspElement(
304 const Array<Sint8>& body)
305 {
306 Array<Sint8> out;
307 return out << "<SIMPLERSP>\n" << body << "</SIMPLERSP>\n";
308 }
309
310 //------------------------------------------------------------------------------
311 //
312 // formatIMethodCallElement()
313 //
314 // <!ELEMENT IMETHODCALL (LOCALNAMESPACEPATH,IPARAMVALUE*)>
315 // <!ATTLIST IMETHODCALL %CIMName;>
316 mike 1.1 //
317 //------------------------------------------------------------------------------
318
319 Array<Sint8> XmlWriter::formatIMethodCallElement(
320 const char* name,
321 const String& nameSpace,
322 const Array<Sint8>& iParamValues)
323 {
324 Array<Sint8> out;
325 out << "<IMETHODCALL NAME=\"" << name << "\">\n";
326 XmlWriter::appendLocalNameSpaceElement(out, nameSpace);
327 out << iParamValues;
328 out << "</IMETHODCALL>\n";
329 return out;
330 }
331
332 //------------------------------------------------------------------------------
333 //
334 // formatIMethodResponseElement()
335 //
336 // <!ELEMENT IMETHODRESPONSE (ERROR|IRETURNVALUE?)>
337 mike 1.1 // <!ATTLIST IMETHODRESPONSE %CIMName;>
338 //
339 //------------------------------------------------------------------------------
340
341 Array<Sint8> XmlWriter::formatIMethodResponseElement(
342 const char* name,
343 const Array<Sint8>& body)
344 {
345 Array<Sint8> out;
346 out << "<IMETHODRESPONSE NAME=\"" << name << "\">\n";
347 out << body;
348 out << "</IMETHODRESPONSE>\n";
349 return out;
350 }
351
352 //------------------------------------------------------------------------------
353 //
354 // formatIReturnValueElement()
355 //
356 // <!ELEMENT IRETURNVALUE (CLASSNAME*|INSTANCENAME*|VALUE*|
357 // VALUE.OBJECTWITHPATH*|VALUE.OBJECTWITHLOCALPATH*|VALUE.OBJECT*|
358 mike 1.1 // OBJECTPATH*|QUALIFIER.DECLARATION*|VALUE.ARRAY?|VALUE.REFERENCE?|
359 // CLASS*|INSTANCE*|VALUE.NAMEDINSTANCE*)>
360 //
361 //------------------------------------------------------------------------------
362
363 Array<Sint8> XmlWriter::formatIReturnValueElement(
364 const Array<Sint8>& body)
365 {
366 Array<Sint8> out;
367 return out << "<IRETURNVALUE>\n" << body << "</IRETURNVALUE>\n";
368 }
369
370 //------------------------------------------------------------------------------
371 //
372 // formatIParamValueElement()
373 //
374 // <!ELEMENT IPARAMVALUE (VALUE|VALUE.ARRAY|VALUE.REFERENCE
375 // |INSTANCENAME|CLASSNAME|QUALIFIER.DECLARATION
376 // |CLASS|INSTANCE|VALUE.NAMEDINSTANCE)?>
377 // <!ATTLIST IPARAMVALUE %CIMName;>
378 //
379 mike 1.1 //------------------------------------------------------------------------------
380
381 Array<Sint8>& XmlWriter::formatIParamValueElement(
382 Array<Sint8>& out,
383 const char* name,
384 const Array<Sint8>& body)
385 {
386 out << "<IPARAMVALUE NAME=\"" << name << "\">\n";
387 out << body;
388 out << "</IPARAMVALUE>\n";
389 return out;
390 }
391
392 //------------------------------------------------------------------------------
393 //
394 // formatErrorElement()
395 //
396 //------------------------------------------------------------------------------
397
398 Array<Sint8> XmlWriter::formatErrorElement(
399 CimException::Code code,
400 mike 1.1 const char* description)
401 {
402 Array<Sint8> out;
403 out << "<ERROR";
404 out << " CODE=\"" << Uint32(code) << "\"";
405 out << " DESCRIPTION=\"";
406 appendSpecial(out, description);
407 out << "\"/>";
408 return out;
409 }
410
411 //------------------------------------------------------------------------------
412 //
413 // appendBooleanParameter()
414 //
415 //------------------------------------------------------------------------------
416
417 Array<Sint8>& XmlWriter::appendBooleanParameter(
418 Array<Sint8>& out,
419 const char* name,
420 Boolean flag)
421 mike 1.1 {
422 Array<Sint8> tmp;
423 tmp << "<VALUE>" << (flag ? "TRUE" : "FALSE") << "</VALUE>\n";
424 return formatIParamValueElement(out, name, tmp);
425 }
426
427 //------------------------------------------------------------------------------
428 //
429 // appendClassNameParameter()
430 //
431 //------------------------------------------------------------------------------
432
433 Array<Sint8>& XmlWriter::appendClassNameParameter(
434 Array<Sint8>& out,
435 const char* name,
436 const String& className)
437 {
438 Array<Sint8> tmp;
439 appendClassNameElement(tmp, className);
440 return formatIParamValueElement(out, name, tmp);
441 }
442 mike 1.1
443 //------------------------------------------------------------------------------
444 //
445 // appendQualifierNameParameter()
446 //
447 //------------------------------------------------------------------------------
448
449 Array<Sint8>& XmlWriter::appendQualifierNameParameter(
450 Array<Sint8>& out,
451 const char* name,
452 const String& qualifierName)
453 {
454 // <!ELEMENT IPARAMVALUE (VALUE|VALUE.ARRAY|VALUE.REFERENCE
455 // |INSTANCENAME|CLASSNAME|QUALIFIER.DECLARATION
456 // |CLASS|INSTANCE|VALUE.NAMEDINSTANCE)?>
457 //
458 // ATTN: notice that there is really no way to pass a qualifier name
459 // as an IPARAMVALUE element according to the spec (look above). So we
460 // just pass it as a class name. An answer must be obtained later.
461
462 Array<Sint8> tmp;
463 mike 1.1 appendClassNameElement(tmp, qualifierName);
464 return formatIParamValueElement(out, name, tmp);
465 }
466
467 //------------------------------------------------------------------------------
468 //
469 // appendClassParameter()
470 //
471 //------------------------------------------------------------------------------
472
473 Array<Sint8>& XmlWriter::appendClassParameter(
474 Array<Sint8>& out,
475 const char* parameterName,
476 const ConstClassDecl& classDecl)
477 {
478 Array<Sint8> tmp;
479 classDecl.toXml(tmp);
480 return formatIParamValueElement(out, parameterName, tmp);
481 }
482
483 //------------------------------------------------------------------------------
484 mike 1.1 //
485 // appendClassParameter()
486 //
487 //------------------------------------------------------------------------------
488
489 Array<Sint8>& XmlWriter::appendQualifierDeclarationParameter(
490 Array<Sint8>& out,
491 const char* parameterName,
492 const ConstQualifierDecl& qualifierDecl)
493 {
494 Array<Sint8> tmp;
495 qualifierDecl.toXml(tmp);
496 return formatIParamValueElement(out, parameterName, tmp);
497 }
498
499 //------------------------------------------------------------------------------
500 //
501 // appendClassNameElement()
502 //
503 //------------------------------------------------------------------------------
504
505 mike 1.1 Array<Sint8>& XmlWriter::appendClassNameElement(
506 Array<Sint8>& out,
507 const String& className)
508 {
509 return out << "<CLASSNAME NAME=\"" << className << "\"/>\n";
510 }
511
512 //------------------------------------------------------------------------------
513 //
514 // _printAttributes()
515 //
516 //------------------------------------------------------------------------------
517
518 static void _printAttributes(
519 std::ostream& os,
520 const XmlAttribute* attributes,
521 Uint32 attributeCount)
522 {
523 for (Uint32 i = 0; i < attributeCount; i++)
524 {
525 os << attributes[i].name << "=";
526 mike 1.1
527 os << '"';
528 AppendSpecial(os, attributes[i].value);
529 os << '"';
530
531 if (i + 1 != attributeCount)
532 os << ' ';
533 }
534 }
535
536 //------------------------------------------------------------------------------
537 //
538 // _indent()
539 //
540 //------------------------------------------------------------------------------
541
542 static void _indent(std::ostream& os, Uint32 level, Uint32 indentChars)
543 {
544 Uint32 n = level * indentChars;
545
546 for (Uint32 i = 0; i < n; i++)
547 mike 1.1 os << ' ';
548 }
549
550 //------------------------------------------------------------------------------
551 //
552 // indentedPrint()
553 //
554 //------------------------------------------------------------------------------
555
556 void XmlWriter::indentedPrint(
557 std::ostream& os,
558 const char* text,
559 Uint32 indentChars)
560 {
561 char* tmp = strcpy(new char[strlen(text) + 1], text);
562
563 XmlParser parser(tmp);
564 XmlEntry entry;
565 Stack<const char*> stack;
566
567 while (parser.next(entry))
568 mike 1.1 {
569 switch (entry.type)
570 {
571 case XmlEntry::XML_DECLARATION:
572 {
573 _indent(os, stack.getSize(), indentChars);
574
575 os << "<?" << entry.text << " ";
576 _printAttributes(os, entry.attributes, entry.attributeCount);
577 os << "?>";
578 break;
579 }
580
581 case XmlEntry::START_TAG:
582 {
583 _indent(os, stack.getSize(), indentChars);
584
585 os << "<" << entry.text;
586
587 if (entry.attributeCount)
588 os << ' ';
589 mike 1.1
590 _printAttributes(os, entry.attributes, entry.attributeCount);
591 os << ">";
592 stack.push(entry.text);
593 break;
594 }
595
596 case XmlEntry::EMPTY_TAG:
597 {
598 _indent(os, stack.getSize(), indentChars);
599
600 os << "<" << entry.text << " ";
601 _printAttributes(os, entry.attributes, entry.attributeCount);
602 os << "/>";
603 break;
604 }
605
606 case XmlEntry::END_TAG:
607 {
608 if (!stack.isEmpty() && strcmp(stack.top(), entry.text) == 0)
609 stack.pop();
610 mike 1.1
611 _indent(os, stack.getSize(), indentChars);
612
613 os << "</" << entry.text << ">";
614 break;
615 }
616
617 case XmlEntry::COMMENT:
618 {
619
620 _indent(os, stack.getSize(), indentChars);
621 os << "<!--";
622 AppendSpecial(os, entry.text);
623 os << "-->";
624 break;
625 }
626
627 case XmlEntry::CONTENT:
628 {
629 _indent(os, stack.getSize(), indentChars);
630 AppendSpecial(os, entry.text);
631 mike 1.1 break;
632 }
633
634 case XmlEntry::CDATA:
635 {
636 _indent(os, stack.getSize(), indentChars);
637 os << "<![CDATA[...]]>";
638 break;
639 }
640
641 case XmlEntry::DOCTYPE:
642 {
643 _indent(os, stack.getSize(), indentChars);
644 os << "<!DOCTYPE...>";
645 break;
646 }
647 }
648
649 os << std::endl;
650 }
651
652 mike 1.1 delete [] tmp;
653 }
654
655 //------------------------------------------------------------------------------
656 //
657 // XmlWriter::getNextMessageId()
658 //
659 //------------------------------------------------------------------------------
660
661 Uint32 XmlWriter::getNextMessageId()
662 {
663 // ATTN: make thread-safe:
664 static Uint32 messageId = 1000;
665
666 messageId++;
667
668 if (messageId < 1000)
669 messageId = 1001;
670
671 return messageId;
672 }
673 mike 1.1
674 //------------------------------------------------------------------------------
675 //
676 // XmlWriter::formatSimpleReqMessage()
677 //
678 //------------------------------------------------------------------------------
679
680 Array<Sint8> XmlWriter::formatSimpleReqMessage(
681 const char* host,
682 const String& nameSpace,
683 const char* iMethodName,
684 const Array<Sint8>& body)
685 {
686 return XmlWriter::formatMPostHeader(
687 host,
688 "MethodCall",
689 iMethodName,
690 nameSpace,
691 XmlWriter::formatMessageElement(
692 XmlWriter::getNextMessageId(),
693 XmlWriter::formatSimpleReqElement(
694 mike 1.1 XmlWriter::formatIMethodCallElement(
695 iMethodName,
696 nameSpace,
697 body))));
698 }
699
700 Array<Sint8> XmlWriter::formatSimpleRspMessage(
701 const char* iMethodName,
702 const Array<Sint8>& body)
703 {
704 return XmlWriter::formatMethodResponseHeader(
705 XmlWriter::formatMessageElement(
706 XmlWriter::getNextMessageId(),
707 XmlWriter::formatSimpleRspElement(
708 XmlWriter::formatIMethodResponseElement(
709 iMethodName,
710 XmlWriter::formatIReturnValueElement(body)))));
711 }
712
713 PEGASUS_NAMESPACE_END
|