2 * Copyright 2001-2009 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.
20 * C++ DDF abstraction for interpretive RPC
24 #include "remoting/ddf.h"
27 # define snprintf _snprintf
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>
38 using namespace shibsp;
39 using namespace xmltooling;
40 using namespace xercesc;
43 // defensive string functions
45 size_t ddf_strlen(const char* s)
47 return s ? strlen(s) : 0;
50 char* ddf_strdup(const char* s)
52 return (s && *s) ? strdup(s) : NULL;
55 #define MAX_NAME_LEN 255
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)
65 const char* temp=NULL;
68 if (*path==NULL || **path=='\0')
71 temp=strchr(*path,'.');
79 strncpy(name,*path,temp-*path);
80 name[temp-*path]='\0';
88 // body implementation
90 struct shibsp::ddf_body_t {
91 ddf_body_t() : name(NULL), parent(NULL), next(NULL), prev(NULL), type(DDF_EMPTY) {}
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
107 } type; // data type of node
120 } value; // value of node
123 // library implementation
125 DDF::DDF(const char* n)
127 m_handle=new(nothrow) ddf_body_t;
131 DDF::DDF(const char* n, const char* val, bool safe)
133 m_handle=new(nothrow) ddf_body_t;
135 string(const_cast<char*>(val), true, safe);
138 DDF::DDF(const char* n, long val)
140 m_handle=new(nothrow) ddf_body_t;
145 DDF::DDF(const char* n, double val)
147 m_handle=new(nothrow) ddf_body_t;
152 DDF::DDF(const char* n, void* val)
154 m_handle=new(nothrow) ddf_body_t;
161 remove().empty().name(NULL);
167 DDF DDF::copy() const
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:
187 DDF copy(m_handle->name), temp;
188 if (m_handle->type==ddf_body_t::DDF_STRUCT)
192 ddf_body_t* child=m_handle->value.children.first;
195 DDF temp2=temp.copy();
197 if (copy.m_handle==NULL)
199 if (m_handle->value.children.current==child)
200 copy.m_handle->value.children.current=copy.m_handle->value.children.last;
209 const char* DDF::name() const
211 return (m_handle) ? m_handle->name : NULL;
214 DDF& DDF::name(const char* name)
216 char trunc_name[MAX_NAME_LEN+1]="";
220 free(m_handle->name);
222 strncpy(trunc_name,name,MAX_NAME_LEN);
223 trunc_name[MAX_NAME_LEN]='\0';
224 m_handle->name=ddf_strdup(trunc_name);
234 bool DDF::isnull() const
236 return m_handle ? false : true;
239 bool DDF::isempty() const
241 return m_handle ? (m_handle->type==ddf_body_t::DDF_EMPTY) : false;
244 bool DDF::isstring() const
246 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING || m_handle->type==ddf_body_t::DDF_STRING_UNSAFE) : false;
249 bool DDF::isint() const
251 return m_handle ? (m_handle->type==ddf_body_t::DDF_INT) : false;
254 bool DDF::isfloat() const
256 return m_handle ? (m_handle->type==ddf_body_t::DDF_FLOAT) : false;
259 bool DDF::isstruct() const
261 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRUCT) : false;
264 bool DDF::islist() const
266 return m_handle ? (m_handle->type==ddf_body_t::DDF_LIST) : false;
269 bool DDF::ispointer() const
271 return m_handle ? (m_handle->type==ddf_body_t::DDF_POINTER) : false;
274 const char* DDF::string() const
276 return isstring() ? m_handle->value.string : NULL;
279 long DDF::integer() const
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;
298 double DDF::floating() const
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;
317 void* DDF::pointer() const
319 return ispointer() ? m_handle->value.pointer : NULL;
322 size_t DDF::strlen() const
324 return ddf_strlen(string());
327 bool DDF::operator==(const char* s) const
329 if (string()==NULL || s==NULL)
330 return (string()==NULL && s==NULL);
332 return (::strcmp(string(),s)==0);
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);
344 case ddf_body_t::DDF_LIST:
345 case ddf_body_t::DDF_STRUCT:
348 while (m_handle->value.children.first)
350 temp.m_handle=m_handle->value.children.first;
355 m_handle->type=ddf_body_t::DDF_EMPTY;
360 DDF& DDF::string(char* val, bool copyit, bool safe)
362 if (empty().m_handle) {
363 m_handle->value.string = copyit ? ddf_strdup(val) : val;
364 if (!m_handle->value.string && val && *val)
366 m_handle->type=(safe ? ddf_body_t::DDF_STRING : ddf_body_t::DDF_STRING_UNSAFE);
371 DDF& DDF::string(long val)
375 sprintf(buf,"%ld",val);
379 DDF& DDF::string(double val)
383 snprintf(buf,39,"%f",val);
387 DDF& DDF::integer(long val)
389 if (empty().m_handle) {
390 m_handle->value.integer=val;
391 m_handle->type=ddf_body_t::DDF_INT;
396 DDF& DDF::integer(const char* val)
398 if (empty().m_handle) {
399 m_handle->value.integer=(val ? atol(val) : 0);
400 m_handle->type=ddf_body_t::DDF_INT;
405 DDF& DDF::floating(double val)
407 if (empty().m_handle) {
408 m_handle->value.floating=val;
409 m_handle->type=ddf_body_t::DDF_FLOAT;
414 DDF& DDF::floating(const char* val)
416 if (empty().m_handle) {
417 m_handle->value.floating=(val ? atof(val) : 0);
418 m_handle->type=ddf_body_t::DDF_FLOAT;
423 DDF& DDF::structure()
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;
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;
447 DDF& DDF::pointer(void* val)
449 if (empty().m_handle) {
450 m_handle->value.pointer=val;
451 m_handle->type=ddf_body_t::DDF_POINTER;
456 DDF& DDF::add(DDF& child)
458 if ((!isstruct() && !islist()) || !child.m_handle)
461 if (m_handle==child.m_handle->parent)
467 getmember(child.name()).destroy();
471 if (!m_handle->value.children.first)
472 m_handle->value.children.first=child.m_handle;
474 m_handle->value.children.last->next=child.m_handle;
475 child.m_handle->prev=m_handle->value.children.last;
477 m_handle->value.children.last=child.m_handle;
478 child.m_handle->parent=m_handle;
479 m_handle->value.children.count++;
483 DDF& DDF::addbefore(DDF& child, DDF& before)
485 if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
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++;
501 DDF& DDF::addafter(DDF& child, DDF& after)
503 if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
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++;
519 void DDF::swap(DDF& arg)
521 ddf_body_t* temp=arg.m_handle;
522 arg.m_handle=m_handle;
528 if (!m_handle || !m_handle->parent)
532 m_handle->next->prev=m_handle->prev;
535 m_handle->prev->next=m_handle->next;
537 if (m_handle->parent->value.children.first==m_handle)
538 m_handle->parent->value.children.first=m_handle->next;
540 if (m_handle->parent->value.children.last==m_handle)
541 m_handle->parent->value.children.last=m_handle->prev;
543 if (m_handle->parent->value.children.current==m_handle)
544 m_handle->parent->value.children.current=m_handle->prev;
546 m_handle->parent->value.children.count--;
547 m_handle->parent=NULL;
553 DDF DDF::parent() const
557 p.m_handle=(m_handle ? m_handle->parent : NULL);
565 if (islist() || isstruct())
566 f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
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;
578 n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
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;
598 if (islist() || isstruct()) {
599 p.m_handle=m_handle->value.children.current;
601 m_handle->value.children.current=m_handle->value.children.current->prev;
606 DDF DDF::operator[](unsigned long index) const
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;
615 throw range_error("DDF object not a list with >=index+1 elements");
619 DDF DDF::addmember(const char* path)
621 char name[MAX_NAME_LEN+1];
622 const char* path_ptr=path;
624 if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
628 DDF new_member=getmember(name);
629 if (!new_member.m_handle) {
631 new_member=add(temp);
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();
649 DDF DDF::getmember(const char* path) const
651 char name[MAX_NAME_LEN+1];
652 const char* path_ptr=path;
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;
660 if (current.m_handle && ddf_strlen(path_ptr)>0)
661 current=current.getmember(path_ptr);
667 void ddf_print_indent(FILE* f, int indent)
669 for (; indent>0; indent--)
673 void DDF::dump(FILE* f, int indent) const
678 ddf_print_indent(f,indent);
680 switch (m_handle->type) {
682 case ddf_body_t::DDF_EMPTY:
685 fprintf(f," %s",m_handle->name);
688 case ddf_body_t::DDF_STRING:
689 case ddf_body_t::DDF_STRING_UNSAFE:
691 fprintf(f,"char* %s = ",m_handle->name);
693 fprintf(f,"char* = ");
694 if (const char* chptr=m_handle->value.string) {
704 case ddf_body_t::DDF_INT:
706 fprintf(f,"long %s = ",m_handle->name);
708 fprintf(f,"long = ");
709 fprintf(f,"%ld",m_handle->value.integer);
712 case ddf_body_t::DDF_FLOAT:
714 fprintf(f,"double %s = ",m_handle->name);
716 fprintf(f,"double = ");
717 fprintf(f,"%.15f",m_handle->value.floating);
720 case ddf_body_t::DDF_STRUCT:
721 fprintf(f,"struct ");
723 fprintf(f,"%s ",m_handle->name);
725 if (m_handle->value.children.count) {
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;
734 ddf_print_indent(f,indent);
738 case ddf_body_t::DDF_LIST:
741 fprintf(f," %s",m_handle->name);
742 fprintf(f,"[%lu] {",m_handle->value.children.count);
743 if (m_handle->value.children.count) {
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;
752 ddf_print_indent(f,indent);
756 case ddf_body_t::DDF_POINTER:
758 fprintf(f,"void* %s = ",m_handle->name);
760 fprintf(f,"void* = ");
761 if (m_handle->value.pointer)
762 fprintf(f,"%p",m_handle->value.pointer);
768 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
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.
780 void xml_encode(ostream& os, const char* start)
783 while (start && *start) {
784 pos = strcspn(start, "\"<>&");
791 case '"': os << """; break;
792 case '<': os << "<"; break;
793 case '>': os << ">"; break;
794 case '&': os << "&"; break;
795 default: os << *start;
802 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
807 case ddf_body_t::DDF_STRING:
808 case ddf_body_t::DDF_STRING_UNSAFE:
810 if (name_attr && p->name) {
812 xml_encode(os,p->name);
815 if (p->value.string) {
816 if (p->type == ddf_body_t::DDF_STRING) {
818 xml_encode(os,p->value.string);
821 os << " unsafe=\"1\">";
822 xml_encode(os,XMLToolingConfig::getConfig().getURLEncoder()->encode(p->value.string).c_str());
830 case ddf_body_t::DDF_INT:
832 if (name_attr && p->name) {
834 xml_encode(os,p->name);
837 os << '>' << p->value.integer << "</number>";
840 case ddf_body_t::DDF_FLOAT:
842 if (name_attr && p->name) {
844 xml_encode(os,p->name);
847 os << '>' << fixed << p->value.floating << dec << "</number>";
850 case ddf_body_t::DDF_STRUCT:
853 if (name_attr && p->name) {
855 xml_encode(os,p->name);
859 ddf_body_t* child=p->value.children.first;
861 os << "<var name=\"";
862 xml_encode(os,child->name);
864 serialize(child,os,false);
872 case ddf_body_t::DDF_LIST:
874 os << "<array length=\"" << p->value.children.count << '"';
875 if (name_attr && p->name) {
877 xml_encode(os,p->name);
881 ddf_body_t* child=p->value.children.first;
890 case ddf_body_t::DDF_EMPTY:
891 case ddf_body_t::DDF_POINTER:
894 if (name_attr && p->name) {
896 xml_encode(os,p->name);
907 // The stream insertion will work for any ostream-based object.
909 SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
912 os << "<wddxPacket version=\"1.0\" lowercase=\"no\"><header/><data>";
913 serialize(obj.m_handle,os);
914 os << "</data></wddxPacket>";
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.
922 static const char* g_DocType=
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\
947 // This function constructs a DDF object equivalent to the wddx data element rooted
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);
960 DDF deserialize(DOMElement* root, bool lowercase)
963 auto_ptr_char name_val(root->getAttributeNS(NULL, _name));
964 if (name_val.get() && *name_val.get()) {
966 for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
967 obj.name(name_val.get());
970 const XMLCh* tag=root->getTagName();
971 if (XMLString::equals(tag,_var)) {
972 root=XMLHelper::getFirstChildElement(root);
973 tag=(root ? root->getTagName() : &chNull);
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);
988 char* val = toUTF8(child->getNodeValue(), true); // use malloc
990 obj.string(val, false); // don't re-copy the string
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());
1001 obj.integer(val.get());
1004 else if (XMLString::equals(tag,_array)) {
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);
1013 else if (XMLString::equals(tag,_struct)) {
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);
1026 SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
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()));
1034 obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);