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