Use shibboleth-sp as package name for compatibility.
[shibboleth/cpp-sp.git] / shibsp / remoting / impl / ddf.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * ddf.cpp
23  *
24  * C++ DDF abstraction for interpretive RPC
25  */
26
27 #include "internal.h"
28 #include "remoting/ddf.h"
29
30 #include <stdexcept>
31 #include <boost/lexical_cast.hpp>
32 #include <xercesc/dom/DOM.hpp>
33 #include <xercesc/util/XMLUniDefs.hpp>
34 #include <xmltooling/XMLToolingConfig.h>
35 #include <xmltooling/util/ParserPool.h>
36 #include <xmltooling/util/URLEncoder.h>
37 #include <xmltooling/util/XMLHelper.h>
38
39 using namespace shibsp;
40 using namespace xmltooling;
41 using namespace xercesc;
42 using namespace std;
43
44 // defensive string functions
45
46 size_t ddf_strlen(const char* s)
47 {
48     return s ? strlen(s) : 0;
49 }
50
51 char* ddf_strdup(const char* s)
52 {
53     return (s && *s) ? strdup(s) : nullptr;
54 }
55
56 #define MAX_NAME_LEN 255
57
58 /* Parses '.' notation paths, where each component is at most MAX_NAME_LEN long.
59    path contains the address of a constant string which is the current path.
60    name points to a buffer in which to place the first path component.
61    After execution, the path pointer will be moved past the first dot.
62    The actual path string is never modified. Only name is written to.
63    The name buffer is returned from the function. */
64 char* ddf_token(const char** path, char* name)
65 {
66     *name=0;
67     if (*path==nullptr || **path==0)
68         return name;
69
70     const char* temp=strchr(*path,'.');
71     if (temp==nullptr) {
72         strncpy(name,*path,MAX_NAME_LEN);
73         name[MAX_NAME_LEN]=0;
74         *path=nullptr;
75     }
76     else if (temp>*path) {
77         strncpy(name,*path,temp-*path);
78         name[temp-*path]=0;
79         *path=temp+1;
80     }
81     else
82         *path=temp+1;
83     return name;
84 }
85
86 // body implementation
87
88 struct shibsp::ddf_body_t {
89     ddf_body_t() : name(nullptr), parent(nullptr), next(nullptr), prev(nullptr), type(DDF_EMPTY) {}
90
91     char* name;                     // name of node
92     ddf_body_t* parent;             // parent node, if any
93     ddf_body_t* next;               // next node, if any
94     ddf_body_t* prev;               // previous node, if any
95
96     enum {
97             DDF_EMPTY,
98             DDF_STRING,
99             DDF_INT,
100         DDF_FLOAT,
101             DDF_STRUCT,
102         DDF_LIST,
103             DDF_POINTER,
104         DDF_STRING_UNSAFE
105     } type;                         // data type of node
106
107     union {
108         char* string;
109         long integer;
110         double floating;
111         void* pointer;
112         struct {
113                 ddf_body_t* first;
114                 ddf_body_t* last;
115                 ddf_body_t* current;
116                 unsigned long count;
117         } children;
118     } value;                        // value of node
119 };
120
121 // library implementation
122
123 DDF::DDF() : m_handle(nullptr)
124 {
125 }
126
127 DDF::DDF(const char* n)
128 {
129     m_handle=new(nothrow) ddf_body_t;
130     name(n);
131 }
132
133 DDF::DDF(const char* n, const char* val, bool safe)
134 {
135     m_handle=new(nothrow) ddf_body_t;
136     name(n);
137     string(const_cast<char*>(val), true, safe);
138 }
139
140 DDF::DDF(const char* n, long val)
141 {
142     m_handle=new(nothrow) ddf_body_t;
143     name(n);
144     integer(val);
145 }
146
147 DDF::DDF(const char* n, double val)
148 {
149     m_handle=new(nothrow) ddf_body_t;
150     name(n);
151     floating(val);
152 }
153
154 DDF::DDF(const char* n, void* val)
155 {
156     m_handle=new(nothrow) ddf_body_t;
157     name(n);
158     pointer(val);
159 }
160
161 DDF& DDF::destroy()
162 {
163     remove().empty().name(nullptr);
164     delete m_handle;
165     m_handle=nullptr;
166     return *this;
167 }
168
169 DDF DDF::copy() const
170 {
171     if (m_handle==nullptr)
172         return DDF();
173
174     switch (m_handle->type) {
175         case ddf_body_t::DDF_EMPTY:
176             return DDF(m_handle->name);
177         case ddf_body_t::DDF_STRING:
178         case ddf_body_t::DDF_STRING_UNSAFE:
179             return DDF(m_handle->name,m_handle->value.string,(m_handle->type==ddf_body_t::DDF_STRING));
180         case ddf_body_t::DDF_INT:
181             return DDF(m_handle->name,m_handle->value.integer);
182         case ddf_body_t::DDF_FLOAT:
183             return DDF(m_handle->name,m_handle->value.floating);
184         case ddf_body_t::DDF_POINTER:
185             return DDF(m_handle->name,m_handle->value.pointer);
186         case ddf_body_t::DDF_STRUCT:
187         case ddf_body_t::DDF_LIST:
188         {
189             DDF copy(m_handle->name), temp;
190             if (m_handle->type==ddf_body_t::DDF_STRUCT)
191                 copy.structure();
192             else
193                 copy.list();
194             ddf_body_t* child=m_handle->value.children.first;
195             while (child) {
196                 temp.m_handle=child;
197                 DDF temp2=temp.copy();
198                 copy.add(temp2);
199                 if (copy.m_handle==nullptr)
200                     return copy;
201                 if (m_handle->value.children.current==child)
202                     copy.m_handle->value.children.current=copy.m_handle->value.children.last;
203                 child=child->next;
204             }
205             return copy;
206         }
207     }
208     return DDF();
209 }
210
211 const char* DDF::name() const
212 {
213     return (m_handle) ? m_handle->name : nullptr;
214 }
215
216 DDF& DDF::name(const char* name)
217 {
218     char trunc_name[MAX_NAME_LEN+1]="";
219
220     if (m_handle) {
221         if (m_handle->name)
222             free(m_handle->name);
223         if (name && *name) {
224             strncpy(trunc_name,name,MAX_NAME_LEN);
225             trunc_name[MAX_NAME_LEN]='\0';
226             m_handle->name=ddf_strdup(trunc_name);
227             if (!m_handle->name)
228                 destroy();
229         }
230         else
231             m_handle->name=nullptr;
232     }
233     return *this;
234 }
235
236 bool DDF::isnull() const
237 {
238     return m_handle ? false : true;
239 }
240
241 bool DDF::isempty() const
242 {
243     return m_handle ? (m_handle->type==ddf_body_t::DDF_EMPTY) : false;
244 }
245
246 bool DDF::isstring() const
247 {
248     return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING || m_handle->type==ddf_body_t::DDF_STRING_UNSAFE) : false;
249 }
250
251 bool DDF::isint() const
252 {
253     return m_handle ? (m_handle->type==ddf_body_t::DDF_INT) : false;
254 }
255
256 bool DDF::isfloat() const
257 {
258     return m_handle ? (m_handle->type==ddf_body_t::DDF_FLOAT) : false;
259 }
260
261 bool DDF::isstruct() const
262 {
263     return m_handle ? (m_handle->type==ddf_body_t::DDF_STRUCT) : false;
264 }
265
266 bool DDF::islist() const
267 {
268     return m_handle ? (m_handle->type==ddf_body_t::DDF_LIST) : false;
269 }
270
271 bool DDF::ispointer() const
272 {
273     return m_handle ? (m_handle->type==ddf_body_t::DDF_POINTER) : false;
274 }
275
276 const char* DDF::string() const
277 {
278     return isstring() ? m_handle->value.string : nullptr;
279 }
280
281 long DDF::integer() const
282 {
283     if (m_handle) {
284         switch(m_handle->type) {
285             case ddf_body_t::DDF_INT:
286                 return m_handle->value.integer;
287             case ddf_body_t::DDF_FLOAT:
288                 return static_cast<long>(m_handle->value.floating);
289             case ddf_body_t::DDF_STRING:
290             case ddf_body_t::DDF_STRING_UNSAFE:
291                 return m_handle->value.string ? atol(m_handle->value.string) : 0;
292             case ddf_body_t::DDF_STRUCT:
293             case ddf_body_t::DDF_LIST:
294                 return m_handle->value.children.count;
295         }
296     }
297     return 0;
298 }
299
300 double DDF::floating() const
301 {
302     if (m_handle) {
303         switch(m_handle->type) {
304             case ddf_body_t::DDF_INT:
305                 return m_handle->value.integer;
306             case ddf_body_t::DDF_FLOAT:
307                 return m_handle->value.floating;
308             case ddf_body_t::DDF_STRING:
309             case ddf_body_t::DDF_STRING_UNSAFE:
310                 return m_handle->value.string ? atof(m_handle->value.string) : 0;
311             case ddf_body_t::DDF_STRUCT:
312             case ddf_body_t::DDF_LIST:
313                 return m_handle->value.children.count;
314         }
315     }
316     return 0;
317 }
318
319 void* DDF::pointer() const
320 {
321     return ispointer() ? m_handle->value.pointer : nullptr;
322 }
323
324 size_t DDF::strlen() const
325 {
326     return ddf_strlen(string());
327 }
328
329 bool DDF::operator==(const char* s) const
330 {
331     if (string()==nullptr || s==nullptr)
332         return (string()==nullptr && s==nullptr);
333     else
334         return (::strcmp(string(),s)==0);
335 }
336
337 DDF& DDF::empty()
338 {
339     if (m_handle) {
340         switch (m_handle->type) {
341             case ddf_body_t::DDF_STRING:
342             case ddf_body_t::DDF_STRING_UNSAFE:
343                 if (m_handle->value.string)
344                     free(m_handle->value.string);
345                 break;
346             case ddf_body_t::DDF_LIST:
347             case ddf_body_t::DDF_STRUCT:
348             {
349                 DDF temp;
350                 while (m_handle->value.children.first)
351                 {
352                     temp.m_handle=m_handle->value.children.first;
353                     temp.destroy();
354                 }
355             }
356         }
357         m_handle->type=ddf_body_t::DDF_EMPTY;
358     }
359     return *this;
360 }
361
362 DDF& DDF::string(char* val, bool copyit, bool safe)
363 {
364     if (empty().m_handle) {
365         m_handle->value.string = copyit ? ddf_strdup(val) : val;
366         if (!m_handle->value.string && val && *val)
367             return destroy();
368         m_handle->type=(safe ? ddf_body_t::DDF_STRING : ddf_body_t::DDF_STRING_UNSAFE);
369     }
370     return *this;
371 }
372
373 DDF& DDF::string(const char* val)
374 {
375     return string(const_cast<char*>(val), true);
376 }
377
378 DDF& DDF::unsafe_string(const char* val)
379 {
380     return string(const_cast<char*>(val), true, false);
381 }
382
383 DDF& DDF::string(long val)
384 {
385     std::string buf = boost::lexical_cast<std::string>(val);
386     return string(buf.c_str());
387 }
388
389 DDF& DDF::string(double val)
390 {
391     std::string buf = boost::lexical_cast<std::string>(val);
392     return string(buf.c_str());
393 }
394
395 DDF& DDF::integer(long val)
396 {
397     if (empty().m_handle) {
398         m_handle->value.integer=val;
399         m_handle->type=ddf_body_t::DDF_INT;
400     }
401     return *this;
402 }
403
404 DDF& DDF::integer(const char* val)
405 {
406     if (empty().m_handle) {
407         m_handle->value.integer=(val ? atol(val) : 0);
408         m_handle->type=ddf_body_t::DDF_INT;
409     }
410     return *this;
411 }
412
413 DDF& DDF::floating(double val)
414 {
415     if (empty().m_handle) {
416         m_handle->value.floating=val;
417         m_handle->type=ddf_body_t::DDF_FLOAT;
418     }
419     return *this;
420 }
421
422 DDF& DDF::floating(const char* val)
423 {
424     if (empty().m_handle) {
425         m_handle->value.floating=(val ? atof(val) : 0);
426         m_handle->type=ddf_body_t::DDF_FLOAT;
427     }
428     return *this;
429 }
430
431 DDF& DDF::structure()
432 {
433     if (empty().m_handle) {
434         m_handle->type=ddf_body_t::DDF_STRUCT;
435         m_handle->value.children.first=nullptr;
436         m_handle->value.children.last=nullptr;
437         m_handle->value.children.current=nullptr;
438         m_handle->value.children.count=0;
439     }
440     return *this;
441 }
442
443 DDF& DDF::list()
444 {
445     if (empty().m_handle) {
446         m_handle->type=ddf_body_t::DDF_LIST;
447         m_handle->value.children.first=nullptr;
448         m_handle->value.children.last=nullptr;
449         m_handle->value.children.current=nullptr;
450         m_handle->value.children.count=0;
451     }
452     return *this;
453 }
454
455 DDF& DDF::pointer(void* val)
456 {
457     if (empty().m_handle) {
458         m_handle->value.pointer=val;
459         m_handle->type=ddf_body_t::DDF_POINTER;
460     }
461     return *this;
462 }
463
464 DDF& DDF::add(DDF& child)
465 {
466     if ((!isstruct() && !islist()) || !child.m_handle)
467         return child;
468
469     if (m_handle==child.m_handle->parent)
470         return child;
471
472     if (isstruct()) {
473         if (!child.name())
474             return child;
475         getmember(child.name()).destroy();
476     }
477
478     child.remove();
479     if (!m_handle->value.children.first)
480         m_handle->value.children.first=child.m_handle;
481     else {
482         m_handle->value.children.last->next=child.m_handle;
483         child.m_handle->prev=m_handle->value.children.last;
484     }
485     m_handle->value.children.last=child.m_handle;
486     child.m_handle->parent=m_handle;
487     m_handle->value.children.count++;
488     return child;
489 }
490
491 DDF& DDF::addbefore(DDF& child, DDF& before)
492 {
493     if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
494         return child;
495
496     child.remove();
497     if (m_handle->value.children.first==before.m_handle)
498         m_handle->value.children.first=child.m_handle;
499     child.m_handle->prev=before.m_handle->prev;
500     if (child.m_handle->prev)
501         child.m_handle->prev->next=child.m_handle;
502     before.m_handle->prev=child.m_handle;
503     child.m_handle->next=before.m_handle;
504     child.m_handle->parent=m_handle;
505     m_handle->value.children.count++;
506     return child;
507 }
508
509 DDF& DDF::addafter(DDF& child, DDF& after)
510 {
511     if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
512         return child;
513
514     child.remove();
515     if (m_handle->value.children.last==after.m_handle)
516         m_handle->value.children.last=child.m_handle;
517     child.m_handle->next=after.m_handle->next;
518     if (child.m_handle->next)
519         child.m_handle->next->prev=child.m_handle;
520     after.m_handle->next=child.m_handle;
521     child.m_handle->prev=after.m_handle;
522     child.m_handle->parent=m_handle;
523     m_handle->value.children.count++;
524     return child;
525 }
526
527 void DDF::swap(DDF& arg)
528 {
529     ddf_body_t* temp=arg.m_handle;
530     arg.m_handle=m_handle;
531     m_handle=temp;
532 }
533
534 DDF& DDF::remove()
535 {
536     if (!m_handle || !m_handle->parent)
537         return *this;
538
539     if (m_handle->next)
540         m_handle->next->prev=m_handle->prev;
541
542     if (m_handle->prev)
543         m_handle->prev->next=m_handle->next;
544
545     if (m_handle->parent->value.children.first==m_handle)
546         m_handle->parent->value.children.first=m_handle->next;
547
548     if (m_handle->parent->value.children.last==m_handle)
549         m_handle->parent->value.children.last=m_handle->prev;
550
551     if (m_handle->parent->value.children.current==m_handle)
552         m_handle->parent->value.children.current=m_handle->prev;
553
554     m_handle->parent->value.children.count--;
555     m_handle->parent=nullptr;
556     m_handle->next=nullptr;
557     m_handle->prev=nullptr;
558     return *this;
559 }
560
561 DDF DDF::parent() const
562 {
563     DDF p;
564
565     p.m_handle=(m_handle ? m_handle->parent : nullptr);
566     return p;
567 }
568
569 DDF DDF::first()
570 {
571     DDF f;
572
573     if (islist() || isstruct())
574         f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
575     return f;
576 }
577
578 DDF DDF::next()
579 {
580     DDF n;
581
582     if ((islist() || isstruct()) && m_handle->value.children.current!=m_handle->value.children.last) {
583         if (!m_handle->value.children.current)
584             n.m_handle=m_handle->value.children.current=m_handle->value.children.first;
585         else
586             n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
587     }
588     return n;
589 }
590
591 DDF DDF::last()
592 {
593     DDF l;
594
595     if ((islist() || isstruct()) && m_handle->value.children.last) {
596         m_handle->value.children.current=m_handle->value.children.last->prev;
597         l.m_handle=m_handle->value.children.last;
598     }
599     return l;
600 }
601
602 DDF DDF::previous()
603 {
604     DDF p;
605
606     if (islist() || isstruct()) {
607         p.m_handle=m_handle->value.children.current;
608         if (p.m_handle)
609             m_handle->value.children.current=m_handle->value.children.current->prev;
610     }
611     return p;
612 }
613
614 DDF DDF::operator[](const char* path) const
615 {
616     return getmember(path);
617 }
618
619 DDF DDF::operator[](unsigned long index) const
620 {
621     DDF d;
622
623     if (islist() && index<m_handle->value.children.count) {
624         for (d.m_handle=m_handle->value.children.first; index; index--)
625             d.m_handle=d.m_handle->next;
626     }
627     else
628         throw range_error("DDF object not a list with >=index+1 elements");
629     return d;
630 }
631
632 DDF DDF::addmember(const char* path)
633 {
634     char name[MAX_NAME_LEN+1];
635     const char* path_ptr=path;
636
637     if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
638         if (!isstruct())
639             structure();
640
641         DDF new_member=getmember(name);
642         if (!new_member.m_handle) {
643             DDF temp(name);
644             new_member=add(temp);
645         }
646
647         if (new_member.m_handle) {
648             if (ddf_strlen(path_ptr)>0) {
649                 DDF last_member=new_member.addmember(path_ptr);
650                 if (!last_member.m_handle)
651                     return new_member.destroy();
652                 else
653                     return last_member;
654             }
655             return new_member;
656         }
657         return new_member;
658     }
659     return DDF();
660 }
661
662 DDF DDF::getmember(const char* path) const
663 {
664     DDF current;
665     char name[MAX_NAME_LEN+1];
666     const char* path_ptr=path;
667
668     ddf_token(&path_ptr, name);
669     if (*name == 0)
670         return current;
671     else if (*name == '[') {
672         unsigned long i = strtoul(name+1, nullptr, 10);
673         if (islist() && i < m_handle->value.children.count)
674             current=operator[](i);
675         else if (i == 0)
676             current = *this;
677     }
678     else if (isstruct()) {
679         current.m_handle = m_handle->value.children.first;
680         while (current.m_handle && strcmp(current.m_handle->name,name) != 0)
681             current.m_handle = current.m_handle->next;
682     }
683     else if (islist()) {
684         current.m_handle = m_handle->value.children.first;
685         return current.getmember(path);
686     }
687
688     if (current.m_handle && path_ptr && *path_ptr)
689         current = current.getmember(path_ptr);
690     return current;
691 }
692
693
694 void ddf_print_indent(FILE* f, int indent)
695 {
696     for (; indent>0; indent--)
697         putc(' ',f);
698 }
699
700 void DDF::dump(FILE* f, int indent) const
701 {
702     if (!f)
703         f=stderr;
704
705     ddf_print_indent(f,indent);
706     if (m_handle) {
707         switch (m_handle->type) {
708
709             case ddf_body_t::DDF_EMPTY:
710                 fprintf(f,"empty");
711                 if (m_handle->name)
712                     fprintf(f," %s",m_handle->name);
713                 break;
714
715             case ddf_body_t::DDF_STRING:
716             case ddf_body_t::DDF_STRING_UNSAFE:
717                 if (m_handle->name)
718                     fprintf(f,"char* %s = ",m_handle->name);
719                 else
720                     fprintf(f,"char* = ");
721                 if (const char* chptr=m_handle->value.string) {
722                     putc('"',f);
723                     while (*chptr)
724                         fputc(*chptr++,f);
725                     putc('"',f);
726                 }
727                 else
728                     fprintf(f,"nullptr");
729                 break;
730
731             case ddf_body_t::DDF_INT:
732                 if (m_handle->name)
733                     fprintf(f,"long %s = ",m_handle->name);
734                 else
735                     fprintf(f,"long = ");
736                 fprintf(f,"%ld",m_handle->value.integer);
737                 break;
738
739             case ddf_body_t::DDF_FLOAT:
740                 if (m_handle->name)
741                     fprintf(f,"double %s = ",m_handle->name);
742                 else
743                     fprintf(f,"double = ");
744                 fprintf(f,"%.15f",m_handle->value.floating);
745                 break;
746
747             case ddf_body_t::DDF_STRUCT:
748                 fprintf(f,"struct ");
749                 if (m_handle->name)
750                     fprintf(f,"%s ",m_handle->name);
751                 putc('{',f);
752                 if (m_handle->value.children.count) {
753                     putc('\n',f);
754                     DDF child;
755                     child.m_handle=m_handle->value.children.first;
756                     while (child.m_handle) {
757                         child.dump(f,indent+2);
758                         child.m_handle=child.m_handle->next;
759                     }
760                 }
761                 ddf_print_indent(f,indent);
762                 putc('}',f);
763                 break;
764
765             case ddf_body_t::DDF_LIST:
766                 fprintf(f,"list");
767                 if (m_handle->name)
768                     fprintf(f," %s",m_handle->name);
769                 fprintf(f,"[%lu] {",m_handle->value.children.count);
770                 if (m_handle->value.children.count) {
771                     putc('\n',f);
772                     DDF child;
773                     child.m_handle=m_handle->value.children.first;
774                     while (child.m_handle) {
775                         child.dump(f,indent+2);
776                         child.m_handle=child.m_handle->next;
777                     }
778                 }
779                 ddf_print_indent(f,indent);
780                 putc('}',f);
781                 break;
782
783             case ddf_body_t::DDF_POINTER:
784                 if (m_handle->name)
785                     fprintf(f,"void* %s = ",m_handle->name);
786                 else
787                     fprintf(f,"void* = ");
788                 if (m_handle->value.pointer)
789                     fprintf(f,"%p",m_handle->value.pointer);
790                 else
791                     fprintf(f,"nullptr");
792                 break;
793
794             default:
795                 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
796         }
797     }
798     else
799         fprintf(f,"nullptr");
800     fprintf(f,";\n");
801 }
802
803 // Serialization is fairly easy. We have to walk the DDF and hand-generate a
804 // wddxPacket XML fragment, with some simple extensions. We escape the four major
805 // special characters, which requires that we output strings one char at a time.
806
807 void xml_encode(ostream& os, const char* start)
808 {
809     size_t pos;
810     while (start && *start) {
811         pos = strcspn(start, "\"<>&");
812         if (pos > 0) {
813             os.write(start,pos);
814             start += pos;
815         }
816         else {
817             switch (*start) {
818                 case '"':   os << "&quot;";     break;
819                 case '<':   os << "&lt;";       break;
820                 case '>':   os << "&gt;";       break;
821                 case '&':   os << "&amp;";      break;
822                 default:    os << *start;
823             }
824             start++;
825         }
826     }
827 }
828
829 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
830 {
831     if (p) {
832         switch (p->type) {
833
834             case ddf_body_t::DDF_STRING:
835             case ddf_body_t::DDF_STRING_UNSAFE:
836                 os << "<string";
837                 if (name_attr && p->name) {
838                     os << " name=\"";
839                     xml_encode(os,p->name);
840                     os << '"';
841                 }
842                 if (p->value.string) {
843                     if (p->type == ddf_body_t::DDF_STRING) {
844                         os << '>';
845                         xml_encode(os,p->value.string);
846                     }
847                     else {
848                         os << " unsafe=\"1\">";
849                         xml_encode(os,XMLToolingConfig::getConfig().getURLEncoder()->encode(p->value.string).c_str());
850                     }
851                     os << "</string>";
852                 }
853                 else
854                     os << "/>";
855                 break;
856
857             case ddf_body_t::DDF_INT:
858                 os << "<number";
859                 if (name_attr && p->name) {
860                     os << " name=\"";
861                     xml_encode(os,p->name);
862                     os << '"';
863                 }
864                 os << '>' << p->value.integer << "</number>";
865                 break;
866
867             case ddf_body_t::DDF_FLOAT:
868                 os << "<number";
869                 if (name_attr && p->name) {
870                     os << " name=\"";
871                     xml_encode(os,p->name);
872                     os << '"';
873                 }
874                 os << '>' << fixed << p->value.floating << dec << "</number>";
875                 break;
876
877             case ddf_body_t::DDF_STRUCT:
878             {
879                 os << "<struct";
880                 if (name_attr && p->name) {
881                     os << " name=\"";
882                     xml_encode(os,p->name);
883                     os << '"';
884                 }
885                 os << '>';
886                 ddf_body_t* child=p->value.children.first;
887                 while (child) {
888                     os << "<var name=\"";
889                     xml_encode(os,child->name);
890                     os << "\">";
891                     serialize(child,os,false);
892                     os << "</var>";
893                     child=child->next;
894                 }
895                 os << "</struct>";
896                 break;
897             }
898
899             case ddf_body_t::DDF_LIST:
900             {
901                 os << "<array length=\"" << p->value.children.count << '"';
902                 if (name_attr && p->name) {
903                     os << " name=\"";
904                     xml_encode(os,p->name);
905                     os << '"';
906                 }
907                 os << '>';
908                 ddf_body_t* child=p->value.children.first;
909                 while (child) {
910                     serialize(child,os);
911                     child=child->next;
912                 }
913                 os << "</array>";
914                 break;
915             }
916
917             case ddf_body_t::DDF_EMPTY:
918             case ddf_body_t::DDF_POINTER:
919             default:
920                 os << "<null";
921                 if (name_attr && p->name) {
922                     os << " name=\"";
923                     xml_encode(os,p->name);
924                     os << '"';
925                 }
926                 os << "/>";
927                 break;
928         }
929     }
930     else
931         os << "<null/>";
932 }
933
934 // The stream insertion will work for any ostream-based object.
935
936 SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
937 {
938     os.precision(15);
939     os << "<wddxPacket version=\"1.0\" lowercase=\"no\"><header/><data>";
940     serialize(obj.m_handle,os);
941     os << "</data></wddxPacket>";
942     return os;
943 }
944
945 // This is a DTD internal subset based on a compatible permutation of the WDDX spec, with the
946 // extension of a name attribute on all the typed elements, which DDF has, but WDDX does not.
947
948 /*
949 static const char* g_DocType=
950 "\
951 <!DOCTYPE wddxPacket [\n\
952 <!ELEMENT wddxPacket (header, data)>\n\
953 <!ATTLIST wddxPacket version CDATA #FIXED \"1.0\" lowercase (yes|no) \"yes\">\n\
954 <!ELEMENT header (comment?)>\n\
955 <!ELEMENT comment (#PCDATA)>\n\
956 <!ELEMENT data (null | number | string | array | struct)>\n\
957 <!ELEMENT null EMPTY>\n\
958 <!ATTLIST null name CDATA #IMPLIED type CDATA #IMPLIED>\n\
959 <!ELEMENT string (#PCDATA | char)*>\n\
960 <!ATTLIST string name CDATA #IMPLIED type CDATA #IMPLIED>\n\
961 <!ELEMENT char EMPTY>\n\
962 <!ATTLIST char code CDATA #REQUIRED>\n\
963 <!ELEMENT number (#PCDATA)>\n\
964 <!ATTLIST number name CDATA #IMPLIED type CDATA #IMPLIED>\n\
965 <!ELEMENT array (null | number | string | array | struct)*>\n\
966 <!ATTLIST array length CDATA #REQUIRED name CDATA #IMPLIED type CDATA #IMPLIED>\n\
967 <!ELEMENT struct (var*)>\n\
968 <!ATTLIST struct name CDATA #IMPLIED type CDATA #IMPLIED>\n\
969 <!ELEMENT var (null | number | string | array | struct)>\n\
970 <!ATTLIST var name CDATA #REQUIRED>\n\
971 ]>\n";
972 */
973
974 // This function constructs a DDF object equivalent to the wddx data element rooted
975 // by the input.
976
977 static const XMLCh _no[] =      UNICODE_LITERAL_2(n,o);
978 static const XMLCh _name[] =    UNICODE_LITERAL_4(n,a,m,e);
979 static const XMLCh _var[] =     UNICODE_LITERAL_3(v,a,r);
980 static const XMLCh _string[] =  UNICODE_LITERAL_6(s,t,r,i,n,g);
981 static const XMLCh _number[] =  UNICODE_LITERAL_6(n,u,m,b,e,r);
982 static const XMLCh _array[] =   UNICODE_LITERAL_5(a,r,r,a,y);
983 static const XMLCh _struct[] =  UNICODE_LITERAL_6(s,t,r,u,c,t);
984 static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e);
985 static const XMLCh _unsafe[] =  UNICODE_LITERAL_6(u,n,s,a,f,e);
986
987 DDF deserialize(DOMElement* root, bool lowercase)
988 {
989     DDF obj(nullptr);
990     auto_ptr_char name_val(root->getAttributeNS(nullptr, _name));
991     if (name_val.get() && *name_val.get()) {
992         if (lowercase)
993             for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
994         obj.name(name_val.get());
995     }
996
997     const XMLCh* tag=root->getTagName();
998     if (XMLString::equals(tag,_var)) {
999         root=XMLHelper::getFirstChildElement(root);
1000         tag=(root ? root->getTagName() : &chNull);
1001     }
1002
1003     if (XMLString::equals(tag,_string)) {
1004         DOMNode* child=root->getFirstChild();
1005         if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
1006             const XMLCh* unsafe = root->getAttributeNS(nullptr, _unsafe);
1007             if (unsafe && *unsafe==chDigit_1) {
1008                 // If it's unsafe, it's not UTF-8 data, so we have to convert to ASCII and decode it.
1009                 char* encoded = XMLString::transcode(child->getNodeValue());
1010                 XMLToolingConfig::getConfig().getURLEncoder()->decode(encoded);
1011                 obj.string(encoded, true, false); // re-copy into free-able buffer, plus mark unsafe
1012                 XMLString::release(&encoded);
1013             }
1014             else {
1015                 char* val = toUTF8(child->getNodeValue(), true);    // use malloc
1016                 obj.string(val, false); // don't re-copy the string
1017             }
1018         }
1019     }
1020     else if (XMLString::equals(tag,_number)) {
1021         DOMNode* child=root->getFirstChild();
1022         if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
1023             auto_ptr_char val(child->getNodeValue());
1024             if (val.get() && strchr(val.get(),'.'))
1025                 obj.floating(val.get());
1026             else
1027                 obj.integer(val.get());
1028         }
1029     }
1030     else if (XMLString::equals(tag,_array)) {
1031         obj.list();
1032         DOMNodeList* children=root->getChildNodes();
1033         for (unsigned int i=0; children && i<children->getLength(); i++)
1034             if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
1035                 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
1036                 obj.add(temp);
1037             }
1038     }
1039     else if (XMLString::equals(tag,_struct)) {
1040         obj.structure();
1041         DOMNodeList* children=root->getChildNodes();
1042         for (unsigned int i=0; children && i<children->getLength(); i++)
1043             if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
1044                 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
1045                 obj.add(temp);
1046             }
1047     }
1048
1049     return obj;
1050 }
1051
1052 SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
1053 {
1054     // Parse the input stream into a DOM tree and construct the equivalent DDF.
1055     DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(is);
1056     XercesJanitor<DOMDocument> docj(doc);
1057     const XMLCh* lowercase=doc->getDocumentElement()->getAttribute(_lowercase);
1058     DOMElement* first=XMLHelper::getFirstChildElement(XMLHelper::getLastChildElement(doc->getDocumentElement()));
1059     obj.destroy();
1060     obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);
1061     return is;
1062 }