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