2 * Copyright 2001-2005 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * ddf.cpp - C++ DDF abstraction for interpretive RPC
20 * Created by: Scott Cantor and Tom Sanfilippo, OSU
26 # define _CRT_NONSTDC_NO_DEPRECATE 1
27 # define _CRT_SECURE_NO_DEPRECATE 1
28 # define SHIBTARGET_EXPORTS __declspec(dllexport)
29 # define snprintf _snprintf
32 #include <saml/saml.h>
33 #include <shib-target/ddf.h>
38 using namespace shibtarget;
41 // defensive string functions
43 size_t ddf_strlen(const char* s)
45 return s ? strlen(s) : 0;
48 char* ddf_strdup(const char* s)
50 return (s && *s) ? strdup(s) : NULL;
53 #define MAX_NAME_LEN 255
55 /* Parses '.' notation paths, where each component is at most MAX_NAME_LEN long.
56 path contains the address of a constant string which is the current path.
57 name points to a buffer in which to place the first path component.
58 After execution, the path pointer will be moved past the first dot.
59 The actual path string is never modified. Only name is written to.
60 The name buffer is returned from the function. */
61 char* ddf_token(const char** path, char* name)
63 const char* temp=NULL;
66 if (*path==NULL || **path=='\0')
69 temp=strchr(*path,'.');
77 strncpy(name,*path,temp-*path);
78 name[temp-*path]='\0';
86 // body implementation
88 struct shibtarget::ddf_body_t {
89 ddf_body_t() : name(NULL), parent(NULL), next(NULL), prev(NULL), type(DDF_EMPTY) {}
91 char* name; // name of node
92 ddf_body_t* parent; // parent node, if any
93 ddf_body_t* next; // next node, if any
94 ddf_body_t* prev; // previous node, if any
104 } type; // data type of node
117 } value; // value of node
120 // library implementation
122 DDF::DDF(const char* n)
124 m_handle=new(nothrow) ddf_body_t;
128 DDF::DDF(const char* n, const char* val)
130 m_handle=new(nothrow) ddf_body_t;
135 DDF::DDF(const char* n, long val)
137 m_handle=new(nothrow) ddf_body_t;
142 DDF::DDF(const char* n, double val)
144 m_handle=new(nothrow) ddf_body_t;
149 DDF::DDF(const char* n, void* val)
151 m_handle=new(nothrow) ddf_body_t;
158 remove().empty().name(NULL);
164 DDF DDF::copy() const
169 switch (m_handle->type) {
170 case ddf_body_t::DDF_EMPTY:
171 return DDF(m_handle->name);
172 case ddf_body_t::DDF_STRING:
173 return DDF(m_handle->name,m_handle->value.string);
174 case ddf_body_t::DDF_INT:
175 return DDF(m_handle->name,m_handle->value.integer);
176 case ddf_body_t::DDF_FLOAT:
177 return DDF(m_handle->name,m_handle->value.floating);
178 case ddf_body_t::DDF_POINTER:
179 return DDF(m_handle->name,m_handle->value.pointer);
180 case ddf_body_t::DDF_STRUCT:
181 case ddf_body_t::DDF_LIST:
183 DDF copy(m_handle->name), temp;
184 if (m_handle->type==ddf_body_t::DDF_STRUCT)
188 ddf_body_t* child=m_handle->value.children.first;
191 DDF temp2=temp.copy();
193 if (copy.m_handle==NULL)
195 if (m_handle->value.children.current==child)
196 copy.m_handle->value.children.current=copy.m_handle->value.children.last;
205 const char* DDF::name() const
207 return (m_handle) ? m_handle->name : NULL;
210 DDF& DDF::name(const char* name)
212 char trunc_name[MAX_NAME_LEN+1]="";
216 free(m_handle->name);
218 strncpy(trunc_name,name,MAX_NAME_LEN);
219 trunc_name[MAX_NAME_LEN]='\0';
220 m_handle->name=ddf_strdup(trunc_name);
230 bool DDF::isnull() const
232 return m_handle ? false : true;
235 bool DDF::isempty() const
237 return m_handle ? (m_handle->type==ddf_body_t::DDF_EMPTY) : false;
240 bool DDF::isstring() const
242 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING) : false;
245 bool DDF::isint() const
247 return m_handle ? (m_handle->type==ddf_body_t::DDF_INT) : false;
250 bool DDF::isfloat() const
252 return m_handle ? (m_handle->type==ddf_body_t::DDF_FLOAT) : false;
255 bool DDF::isstruct() const
257 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRUCT) : false;
260 bool DDF::islist() const
262 return m_handle ? (m_handle->type==ddf_body_t::DDF_LIST) : false;
265 bool DDF::ispointer() const
267 return m_handle ? (m_handle->type==ddf_body_t::DDF_POINTER) : false;
270 const char* DDF::string() const
272 return isstring() ? m_handle->value.string : NULL;
275 long DDF::integer() const
278 switch(m_handle->type) {
279 case ddf_body_t::DDF_INT:
280 return m_handle->value.integer;
281 case ddf_body_t::DDF_FLOAT:
282 return static_cast<long>(m_handle->value.floating);
283 case ddf_body_t::DDF_STRING:
284 return m_handle->value.string ? atol(m_handle->value.string) : 0;
285 case ddf_body_t::DDF_STRUCT:
286 case ddf_body_t::DDF_LIST:
287 return m_handle->value.children.count;
288 case ddf_body_t::DDF_POINTER:
289 return reinterpret_cast<long>(m_handle->value.pointer);
295 double DDF::floating() const
298 switch(m_handle->type) {
299 case ddf_body_t::DDF_INT:
300 return m_handle->value.integer;
301 case ddf_body_t::DDF_FLOAT:
302 return m_handle->value.floating;
303 case ddf_body_t::DDF_STRING:
304 return m_handle->value.string ? atof(m_handle->value.string) : 0;
305 case ddf_body_t::DDF_STRUCT:
306 case ddf_body_t::DDF_LIST:
307 return m_handle->value.children.count;
313 void* DDF::pointer() const
315 return ispointer() ? m_handle->value.pointer : NULL;
318 size_t DDF::strlen() const
320 return ddf_strlen(string());
323 bool DDF::operator==(const char* s) const
325 if (string()==NULL || s==NULL)
326 return (string()==NULL && s==NULL);
328 return (::strcmp(string(),s)==0);
334 switch (m_handle->type) {
335 case ddf_body_t::DDF_STRING:
336 if (m_handle->value.string)
337 free(m_handle->value.string);
339 case ddf_body_t::DDF_LIST:
340 case ddf_body_t::DDF_STRUCT:
343 while (m_handle->value.children.first)
345 temp.m_handle=m_handle->value.children.first;
350 m_handle->type=ddf_body_t::DDF_EMPTY;
355 DDF& DDF::string(const char* val)
357 if (empty().m_handle) {
358 m_handle->value.string=ddf_strdup(val);
359 if (!m_handle->value.string && val && *val)
361 m_handle->type=ddf_body_t::DDF_STRING;
366 DDF& DDF::string(long val)
370 sprintf(buf,"%ld",val);
374 DDF& DDF::string(double val)
378 snprintf(buf,39,"%f",val);
382 DDF& DDF::integer(long val)
384 if (empty().m_handle) {
385 m_handle->value.integer=val;
386 m_handle->type=ddf_body_t::DDF_INT;
391 DDF& DDF::integer(const char* val)
393 if (empty().m_handle) {
394 m_handle->value.integer=(val ? atol(val) : 0);
395 m_handle->type=ddf_body_t::DDF_INT;
400 DDF& DDF::floating(double val)
402 if (empty().m_handle) {
403 m_handle->value.floating=val;
404 m_handle->type=ddf_body_t::DDF_FLOAT;
409 DDF& DDF::floating(const char* val)
411 if (empty().m_handle) {
412 m_handle->value.floating=(val ? atof(val) : 0);
413 m_handle->type=ddf_body_t::DDF_FLOAT;
418 DDF& DDF::structure()
420 if (empty().m_handle) {
421 m_handle->type=ddf_body_t::DDF_STRUCT;
422 m_handle->value.children.first=NULL;
423 m_handle->value.children.last=NULL;
424 m_handle->value.children.current=NULL;
425 m_handle->value.children.count=0;
432 if (empty().m_handle) {
433 m_handle->type=ddf_body_t::DDF_LIST;
434 m_handle->value.children.first=NULL;
435 m_handle->value.children.last=NULL;
436 m_handle->value.children.current=NULL;
437 m_handle->value.children.count=0;
442 DDF& DDF::pointer(void* val)
444 if (empty().m_handle) {
445 m_handle->value.pointer=val;
446 m_handle->type=ddf_body_t::DDF_POINTER;
451 DDF& DDF::add(DDF& child)
453 if ((!isstruct() && !islist()) || !child.m_handle)
456 if (m_handle==child.m_handle->parent)
462 getmember(child.name()).destroy();
466 if (!m_handle->value.children.first)
467 m_handle->value.children.first=child.m_handle;
469 m_handle->value.children.last->next=child.m_handle;
470 child.m_handle->prev=m_handle->value.children.last;
472 m_handle->value.children.last=child.m_handle;
473 child.m_handle->parent=m_handle;
474 m_handle->value.children.count++;
478 DDF& DDF::addbefore(DDF& child, DDF& before)
480 if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
484 if (m_handle->value.children.first==before.m_handle)
485 m_handle->value.children.first=child.m_handle;
486 child.m_handle->prev=before.m_handle->prev;
487 if (child.m_handle->prev)
488 child.m_handle->prev->next=child.m_handle;
489 before.m_handle->prev=child.m_handle;
490 child.m_handle->next=before.m_handle;
491 child.m_handle->parent=m_handle;
492 m_handle->value.children.count++;
496 DDF& DDF::addafter(DDF& child, DDF& after)
498 if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
502 if (m_handle->value.children.last==after.m_handle)
503 m_handle->value.children.last=child.m_handle;
504 child.m_handle->next=after.m_handle->next;
505 if (child.m_handle->next)
506 child.m_handle->next->prev=child.m_handle;
507 after.m_handle->next=child.m_handle;
508 child.m_handle->prev=after.m_handle;
509 child.m_handle->parent=m_handle;
510 m_handle->value.children.count++;
514 void DDF::swap(DDF& arg)
516 ddf_body_t* temp=arg.m_handle;
517 arg.m_handle=m_handle;
523 if (!m_handle || !m_handle->parent)
527 m_handle->next->prev=m_handle->prev;
530 m_handle->prev->next=m_handle->next;
532 if (m_handle->parent->value.children.first==m_handle)
533 m_handle->parent->value.children.first=m_handle->next;
535 if (m_handle->parent->value.children.last==m_handle)
536 m_handle->parent->value.children.last=m_handle->prev;
538 if (m_handle->parent->value.children.current==m_handle)
539 m_handle->parent->value.children.current=m_handle->prev;
541 m_handle->parent->value.children.count--;
542 m_handle->parent=NULL;
548 DDF DDF::parent() const
552 p.m_handle=(m_handle ? m_handle->parent : NULL);
560 if (islist() || isstruct())
561 f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
569 if ((islist() || isstruct()) && m_handle->value.children.current!=m_handle->value.children.last) {
570 if (!m_handle->value.children.current)
571 n.m_handle=m_handle->value.children.current=m_handle->value.children.first;
573 n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
582 if ((islist() || isstruct()) && m_handle->value.children.last) {
583 m_handle->value.children.current=m_handle->value.children.last->prev;
584 l.m_handle=m_handle->value.children.last;
593 if (islist() || isstruct()) {
594 p.m_handle=m_handle->value.children.current;
596 m_handle->value.children.current=m_handle->value.children.current->prev;
601 DDF DDF::operator[](unsigned long index) const
605 if (islist() && index<m_handle->value.children.count) {
606 for (d.m_handle=m_handle->value.children.first; index; index--)
607 d.m_handle=d.m_handle->next;
610 throw range_error("DDF object not a list with >=index+1 elements");
614 DDF DDF::addmember(const char* path)
616 char name[MAX_NAME_LEN+1];
617 const char* path_ptr=path;
619 if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
623 DDF new_member=getmember(name);
624 if (!new_member.m_handle) {
626 new_member=add(temp);
629 if (new_member.m_handle) {
630 if (ddf_strlen(path_ptr)>0) {
631 DDF last_member=new_member.addmember(path_ptr);
632 if (!last_member.m_handle)
633 return new_member.destroy();
644 DDF DDF::getmember(const char* path) const
646 char name[MAX_NAME_LEN+1];
647 const char* path_ptr=path;
650 if (isstruct() && ddf_strlen(ddf_token(&path_ptr,name))>0) {
651 current.m_handle=m_handle->value.children.first;
652 while (current.m_handle && strcmp(current.m_handle->name,name)!=0)
653 current.m_handle=current.m_handle->next;
655 if (current.m_handle && ddf_strlen(path_ptr)>0)
656 current=current.getmember(path_ptr);
662 void ddf_print_indent(FILE* f, int indent)
664 for (; indent>0; indent--)
668 void DDF::dump(FILE* f, int indent) const
673 ddf_print_indent(f,indent);
675 switch (m_handle->type) {
677 case ddf_body_t::DDF_EMPTY:
680 fprintf(f," %s",m_handle->name);
683 case ddf_body_t::DDF_STRING:
685 fprintf(f,"char* %s = ",m_handle->name);
687 fprintf(f,"char* = ");
688 if (const char* chptr=m_handle->value.string) {
698 case ddf_body_t::DDF_INT:
700 fprintf(f,"long %s = ",m_handle->name);
702 fprintf(f,"long = ");
703 fprintf(f,"%ld",m_handle->value.integer);
706 case ddf_body_t::DDF_FLOAT:
708 fprintf(f,"double %s = ",m_handle->name);
710 fprintf(f,"double = ");
711 fprintf(f,"%.15f",m_handle->value.floating);
714 case ddf_body_t::DDF_STRUCT:
715 fprintf(f,"struct ");
717 fprintf(f,"%s ",m_handle->name);
719 if (m_handle->value.children.count) {
722 child.m_handle=m_handle->value.children.first;
723 while (child.m_handle) {
724 child.dump(f,indent+2);
725 child.m_handle=child.m_handle->next;
728 ddf_print_indent(f,indent);
732 case ddf_body_t::DDF_LIST:
735 fprintf(f," %s",m_handle->name);
736 fprintf(f,"[%lu] {",m_handle->value.children.count);
737 if (m_handle->value.children.count) {
740 child.m_handle=m_handle->value.children.first;
741 while (child.m_handle) {
742 child.dump(f,indent+2);
743 child.m_handle=child.m_handle->next;
746 ddf_print_indent(f,indent);
750 case ddf_body_t::DDF_POINTER:
752 fprintf(f,"void* %s = ",m_handle->name);
754 fprintf(f,"void* = ");
755 if (m_handle->value.pointer)
756 fprintf(f,"%p",m_handle->value.pointer);
762 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
770 // Serialization is fairly easy. We have to walk the DDF and hand-generate a
771 // wddxPacket XML fragment, with some simple extensions. We escape the four major
772 // special characters, which requires that we output strings one char at a time.
774 void xml_encode(ostream& os, const char* start)
776 while (start && *start) {
778 case '\'': os << "'"; break;
779 case '<': os << "<"; break;
780 case '>': os << ">"; break;
781 case '&': os << "&"; break;
782 default: os << *start;
788 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
793 case ddf_body_t::DDF_STRING:
795 if (name_attr && p->name) {
797 xml_encode(os,p->name);
800 if (p->value.string) {
802 xml_encode(os,p->value.string);
809 case ddf_body_t::DDF_INT:
811 if (name_attr && p->name) {
813 xml_encode(os,p->name);
816 os << '>' << p->value.integer << "</number>\n";
819 case ddf_body_t::DDF_FLOAT:
821 if (name_attr && p->name) {
823 xml_encode(os,p->name);
826 os << '>' << fixed << p->value.floating << dec << "</number>\n";
829 case ddf_body_t::DDF_STRUCT:
832 if (name_attr && p->name) {
834 xml_encode(os,p->name);
838 ddf_body_t* child=p->value.children.first;
841 xml_encode(os,child->name);
843 serialize(child,os,false);
851 case ddf_body_t::DDF_LIST:
853 os << "<array length='" << p->value.children.count << '\'';
854 if (name_attr && p->name) {
856 xml_encode(os,p->name);
860 ddf_body_t* child=p->value.children.first;
869 case ddf_body_t::DDF_EMPTY:
870 case ddf_body_t::DDF_POINTER:
873 if (name_attr && p->name) {
875 xml_encode(os,p->name);
886 // The stream insertion will work for any ostream-based object.
888 SHIBTARGET_EXPORTS ostream& shibtarget::operator<<(ostream& os, const DDF& obj)
891 os << "<wddxPacket version=\"1.0\" lowercase=\"no\">\n<header/>\n<data>\n";
892 serialize(obj.m_handle,os);
893 os << "</data>\n</wddxPacket>\n";
897 // This is a DTD internal subset based on a compatible permutation of the WDDX spec, with the
898 // extension of a name attribute on all the typed elements, which DDF has, but WDDX does not.
900 static const char* g_DocType=
902 <!DOCTYPE wddxPacket [\n\
903 <!ELEMENT wddxPacket (header, data)>\n\
904 <!ATTLIST wddxPacket version CDATA #FIXED \"1.0\" lowercase (yes|no) \"yes\">\n\
905 <!ELEMENT header (comment?)>\n\
906 <!ELEMENT comment (#PCDATA)>\n\
907 <!ELEMENT data (null | number | string | array | struct)>\n\
908 <!ELEMENT null EMPTY>\n\
909 <!ATTLIST null name CDATA #IMPLIED type CDATA #IMPLIED>\n\
910 <!ELEMENT string (#PCDATA | char)*>\n\
911 <!ATTLIST string name CDATA #IMPLIED type CDATA #IMPLIED>\n\
912 <!ELEMENT char EMPTY>\n\
913 <!ATTLIST char code CDATA #REQUIRED>\n\
914 <!ELEMENT number (#PCDATA)>\n\
915 <!ATTLIST number name CDATA #IMPLIED type CDATA #IMPLIED>\n\
916 <!ELEMENT array (null | number | string | array | struct)*>\n\
917 <!ATTLIST array length CDATA #REQUIRED name CDATA #IMPLIED type CDATA #IMPLIED>\n\
918 <!ELEMENT struct (var*)>\n\
919 <!ATTLIST struct name CDATA #IMPLIED type CDATA #IMPLIED>\n\
920 <!ELEMENT var (null | number | string | array | struct)>\n\
921 <!ATTLIST var name CDATA #REQUIRED>\n\
924 // This function constructs a DDF object equivalent to the wddx data element rooted
927 static const XMLCh _no[] = { chLatin_n, chLatin_o, chNull };
928 static const XMLCh _name[] = { chLatin_n, chLatin_a, chLatin_m, chLatin_e, chNull };
929 static const XMLCh _var[] = { chLatin_v, chLatin_a, chLatin_r, chNull };
930 static const XMLCh _string[] = { chLatin_s, chLatin_t, chLatin_r, chLatin_i, chLatin_n, chLatin_g, chNull };
931 static const XMLCh _number[] = { chLatin_n, chLatin_u, chLatin_m, chLatin_b, chLatin_e, chLatin_r, chNull };
932 static const XMLCh _array[] = { chLatin_a, chLatin_r, chLatin_r, chLatin_a, chLatin_y, chNull };
933 static const XMLCh _struct[] = { chLatin_s, chLatin_t, chLatin_r, chLatin_u, chLatin_c, chLatin_t, chNull };
934 static const XMLCh _lowercase[] =
935 { chLatin_l, chLatin_o, chLatin_w, chLatin_e, chLatin_r, chLatin_c, chLatin_a, chLatin_s, chLatin_e, chNull };
937 DDF deserialize(DOMElement* root, bool lowercase)
940 auto_ptr_char name_val(root->getAttribute(_name));
941 if (name_val.get() && *name_val.get()) {
943 for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
944 obj.name(name_val.get());
947 const XMLCh* tag=root->getTagName();
948 if (!XMLString::compareString(tag,_var)) {
949 root=saml::XML::getFirstChildElement(root);
950 tag=(root ? root->getTagName() : &chNull);
953 if (!XMLString::compareString(tag,_string)) {
954 DOMNode* child=root->getFirstChild();
955 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
956 auto_ptr_char val(child->getNodeValue());
957 obj.string(val.get());
960 else if (!XMLString::compareString(tag,_number)) {
961 DOMNode* child=root->getFirstChild();
962 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
963 auto_ptr_char val(child->getNodeValue());
964 if (val.get() && strchr(val.get(),'.'))
965 obj.floating(val.get());
967 obj.integer(val.get());
970 else if (!XMLString::compareString(tag,_array)) {
972 DOMNodeList* children=root->getChildNodes();
973 for (unsigned int i=0; children && i<children->getLength(); i++)
974 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
975 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
979 else if (!XMLString::compareString(tag,_struct)) {
981 DOMNodeList* children=root->getChildNodes();
982 for (unsigned int i=0; children && i<children->getLength(); i++)
983 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
984 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
992 SHIBTARGET_EXPORTS istream& shibtarget::operator>>(istream& is, DDF& obj)
994 // Parse the input stream into a DOM tree and construct the equivalent DDF.
995 DOMDocument* doc=NULL;
997 XML::StreamInputSource src(is);
998 Wrapper4InputSource dsrc(&src,false);
999 saml::XML::Parser parser(false); // non-validating
1000 doc=parser.parse(dsrc);
1001 const XMLCh* lowercase=doc->getDocumentElement()->getAttribute(_lowercase);
1002 DOMElement* first=saml::XML::getFirstChildElement(saml::XML::getLastChildElement(doc->getDocumentElement()));
1004 obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);