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