2 * Copyright 2001-2007 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/XMLHelper.h>
37 using namespace shibsp;
38 using namespace xmltooling;
39 using namespace xercesc;
42 // defensive string functions
44 size_t ddf_strlen(const char* s)
46 return s ? strlen(s) : 0;
49 char* ddf_strdup(const char* s)
51 return (s && *s) ? strdup(s) : NULL;
54 #define MAX_NAME_LEN 255
56 /* Parses '.' notation paths, where each component is at most MAX_NAME_LEN long.
57 path contains the address of a constant string which is the current path.
58 name points to a buffer in which to place the first path component.
59 After execution, the path pointer will be moved past the first dot.
60 The actual path string is never modified. Only name is written to.
61 The name buffer is returned from the function. */
62 char* ddf_token(const char** path, char* name)
64 const char* temp=NULL;
67 if (*path==NULL || **path=='\0')
70 temp=strchr(*path,'.');
78 strncpy(name,*path,temp-*path);
79 name[temp-*path]='\0';
87 // body implementation
89 struct shibsp::ddf_body_t {
90 ddf_body_t() : name(NULL), parent(NULL), next(NULL), prev(NULL), type(DDF_EMPTY) {}
92 char* name; // name of node
93 ddf_body_t* parent; // parent node, if any
94 ddf_body_t* next; // next node, if any
95 ddf_body_t* prev; // previous node, if any
105 } type; // data type of node
118 } value; // value of node
121 // library implementation
123 DDF::DDF(const char* n)
125 m_handle=new(nothrow) ddf_body_t;
129 DDF::DDF(const char* n, const char* val)
131 m_handle=new(nothrow) ddf_body_t;
136 DDF::DDF(const char* n, long val)
138 m_handle=new(nothrow) ddf_body_t;
143 DDF::DDF(const char* n, double val)
145 m_handle=new(nothrow) ddf_body_t;
150 DDF::DDF(const char* n, void* val)
152 m_handle=new(nothrow) ddf_body_t;
159 remove().empty().name(NULL);
165 DDF DDF::copy() const
170 switch (m_handle->type) {
171 case ddf_body_t::DDF_EMPTY:
172 return DDF(m_handle->name);
173 case ddf_body_t::DDF_STRING:
174 return DDF(m_handle->name,m_handle->value.string);
175 case ddf_body_t::DDF_INT:
176 return DDF(m_handle->name,m_handle->value.integer);
177 case ddf_body_t::DDF_FLOAT:
178 return DDF(m_handle->name,m_handle->value.floating);
179 case ddf_body_t::DDF_POINTER:
180 return DDF(m_handle->name,m_handle->value.pointer);
181 case ddf_body_t::DDF_STRUCT:
182 case ddf_body_t::DDF_LIST:
184 DDF copy(m_handle->name), temp;
185 if (m_handle->type==ddf_body_t::DDF_STRUCT)
189 ddf_body_t* child=m_handle->value.children.first;
192 DDF temp2=temp.copy();
194 if (copy.m_handle==NULL)
196 if (m_handle->value.children.current==child)
197 copy.m_handle->value.children.current=copy.m_handle->value.children.last;
206 const char* DDF::name() const
208 return (m_handle) ? m_handle->name : NULL;
211 DDF& DDF::name(const char* name)
213 char trunc_name[MAX_NAME_LEN+1]="";
217 free(m_handle->name);
219 strncpy(trunc_name,name,MAX_NAME_LEN);
220 trunc_name[MAX_NAME_LEN]='\0';
221 m_handle->name=ddf_strdup(trunc_name);
231 bool DDF::isnull() const
233 return m_handle ? false : true;
236 bool DDF::isempty() const
238 return m_handle ? (m_handle->type==ddf_body_t::DDF_EMPTY) : false;
241 bool DDF::isstring() const
243 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING) : false;
246 bool DDF::isint() const
248 return m_handle ? (m_handle->type==ddf_body_t::DDF_INT) : false;
251 bool DDF::isfloat() const
253 return m_handle ? (m_handle->type==ddf_body_t::DDF_FLOAT) : false;
256 bool DDF::isstruct() const
258 return m_handle ? (m_handle->type==ddf_body_t::DDF_STRUCT) : false;
261 bool DDF::islist() const
263 return m_handle ? (m_handle->type==ddf_body_t::DDF_LIST) : false;
266 bool DDF::ispointer() const
268 return m_handle ? (m_handle->type==ddf_body_t::DDF_POINTER) : false;
271 const char* DDF::string() const
273 return isstring() ? m_handle->value.string : NULL;
276 long DDF::integer() const
279 switch(m_handle->type) {
280 case ddf_body_t::DDF_INT:
281 return m_handle->value.integer;
282 case ddf_body_t::DDF_FLOAT:
283 return static_cast<long>(m_handle->value.floating);
284 case ddf_body_t::DDF_STRING:
285 return m_handle->value.string ? atol(m_handle->value.string) : 0;
286 case ddf_body_t::DDF_STRUCT:
287 case ddf_body_t::DDF_LIST:
288 return m_handle->value.children.count;
294 double DDF::floating() const
297 switch(m_handle->type) {
298 case ddf_body_t::DDF_INT:
299 return m_handle->value.integer;
300 case ddf_body_t::DDF_FLOAT:
301 return m_handle->value.floating;
302 case ddf_body_t::DDF_STRING:
303 return m_handle->value.string ? atof(m_handle->value.string) : 0;
304 case ddf_body_t::DDF_STRUCT:
305 case ddf_body_t::DDF_LIST:
306 return m_handle->value.children.count;
312 void* DDF::pointer() const
314 return ispointer() ? m_handle->value.pointer : NULL;
317 size_t DDF::strlen() const
319 return ddf_strlen(string());
322 bool DDF::operator==(const char* s) const
324 if (string()==NULL || s==NULL)
325 return (string()==NULL && s==NULL);
327 return (::strcmp(string(),s)==0);
333 switch (m_handle->type) {
334 case ddf_body_t::DDF_STRING:
335 if (m_handle->value.string)
336 free(m_handle->value.string);
338 case ddf_body_t::DDF_LIST:
339 case ddf_body_t::DDF_STRUCT:
342 while (m_handle->value.children.first)
344 temp.m_handle=m_handle->value.children.first;
349 m_handle->type=ddf_body_t::DDF_EMPTY;
354 DDF& DDF::string(char* val, bool copyit)
356 if (empty().m_handle) {
357 m_handle->value.string = copyit ? ddf_strdup(val) : val;
358 if (!m_handle->value.string && val && *val)
360 m_handle->type=ddf_body_t::DDF_STRING;
365 DDF& DDF::string(long val)
369 sprintf(buf,"%ld",val);
373 DDF& DDF::string(double val)
377 snprintf(buf,39,"%f",val);
381 DDF& DDF::integer(long val)
383 if (empty().m_handle) {
384 m_handle->value.integer=val;
385 m_handle->type=ddf_body_t::DDF_INT;
390 DDF& DDF::integer(const char* val)
392 if (empty().m_handle) {
393 m_handle->value.integer=(val ? atol(val) : 0);
394 m_handle->type=ddf_body_t::DDF_INT;
399 DDF& DDF::floating(double val)
401 if (empty().m_handle) {
402 m_handle->value.floating=val;
403 m_handle->type=ddf_body_t::DDF_FLOAT;
408 DDF& DDF::floating(const char* val)
410 if (empty().m_handle) {
411 m_handle->value.floating=(val ? atof(val) : 0);
412 m_handle->type=ddf_body_t::DDF_FLOAT;
417 DDF& DDF::structure()
419 if (empty().m_handle) {
420 m_handle->type=ddf_body_t::DDF_STRUCT;
421 m_handle->value.children.first=NULL;
422 m_handle->value.children.last=NULL;
423 m_handle->value.children.current=NULL;
424 m_handle->value.children.count=0;
431 if (empty().m_handle) {
432 m_handle->type=ddf_body_t::DDF_LIST;
433 m_handle->value.children.first=NULL;
434 m_handle->value.children.last=NULL;
435 m_handle->value.children.current=NULL;
436 m_handle->value.children.count=0;
441 DDF& DDF::pointer(void* val)
443 if (empty().m_handle) {
444 m_handle->value.pointer=val;
445 m_handle->type=ddf_body_t::DDF_POINTER;
450 DDF& DDF::add(DDF& child)
452 if ((!isstruct() && !islist()) || !child.m_handle)
455 if (m_handle==child.m_handle->parent)
461 getmember(child.name()).destroy();
465 if (!m_handle->value.children.first)
466 m_handle->value.children.first=child.m_handle;
468 m_handle->value.children.last->next=child.m_handle;
469 child.m_handle->prev=m_handle->value.children.last;
471 m_handle->value.children.last=child.m_handle;
472 child.m_handle->parent=m_handle;
473 m_handle->value.children.count++;
477 DDF& DDF::addbefore(DDF& child, DDF& before)
479 if (!islist() || !child.m_handle || !before.m_handle || before.m_handle->parent!=m_handle)
483 if (m_handle->value.children.first==before.m_handle)
484 m_handle->value.children.first=child.m_handle;
485 child.m_handle->prev=before.m_handle->prev;
486 if (child.m_handle->prev)
487 child.m_handle->prev->next=child.m_handle;
488 before.m_handle->prev=child.m_handle;
489 child.m_handle->next=before.m_handle;
490 child.m_handle->parent=m_handle;
491 m_handle->value.children.count++;
495 DDF& DDF::addafter(DDF& child, DDF& after)
497 if (!islist() || !child.m_handle || !after.m_handle || after.m_handle->parent!=m_handle)
501 if (m_handle->value.children.last==after.m_handle)
502 m_handle->value.children.last=child.m_handle;
503 child.m_handle->next=after.m_handle->next;
504 if (child.m_handle->next)
505 child.m_handle->next->prev=child.m_handle;
506 after.m_handle->next=child.m_handle;
507 child.m_handle->prev=after.m_handle;
508 child.m_handle->parent=m_handle;
509 m_handle->value.children.count++;
513 void DDF::swap(DDF& arg)
515 ddf_body_t* temp=arg.m_handle;
516 arg.m_handle=m_handle;
522 if (!m_handle || !m_handle->parent)
526 m_handle->next->prev=m_handle->prev;
529 m_handle->prev->next=m_handle->next;
531 if (m_handle->parent->value.children.first==m_handle)
532 m_handle->parent->value.children.first=m_handle->next;
534 if (m_handle->parent->value.children.last==m_handle)
535 m_handle->parent->value.children.last=m_handle->prev;
537 if (m_handle->parent->value.children.current==m_handle)
538 m_handle->parent->value.children.current=m_handle->prev;
540 m_handle->parent->value.children.count--;
541 m_handle->parent=NULL;
547 DDF DDF::parent() const
551 p.m_handle=(m_handle ? m_handle->parent : NULL);
559 if (islist() || isstruct())
560 f.m_handle=m_handle->value.children.current=m_handle->value.children.first;
568 if ((islist() || isstruct()) && m_handle->value.children.current!=m_handle->value.children.last) {
569 if (!m_handle->value.children.current)
570 n.m_handle=m_handle->value.children.current=m_handle->value.children.first;
572 n.m_handle=m_handle->value.children.current=m_handle->value.children.current->next;
581 if ((islist() || isstruct()) && m_handle->value.children.last) {
582 m_handle->value.children.current=m_handle->value.children.last->prev;
583 l.m_handle=m_handle->value.children.last;
592 if (islist() || isstruct()) {
593 p.m_handle=m_handle->value.children.current;
595 m_handle->value.children.current=m_handle->value.children.current->prev;
600 DDF DDF::operator[](unsigned long index) const
604 if (islist() && index<m_handle->value.children.count) {
605 for (d.m_handle=m_handle->value.children.first; index; index--)
606 d.m_handle=d.m_handle->next;
609 throw range_error("DDF object not a list with >=index+1 elements");
613 DDF DDF::addmember(const char* path)
615 char name[MAX_NAME_LEN+1];
616 const char* path_ptr=path;
618 if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) {
622 DDF new_member=getmember(name);
623 if (!new_member.m_handle) {
625 new_member=add(temp);
628 if (new_member.m_handle) {
629 if (ddf_strlen(path_ptr)>0) {
630 DDF last_member=new_member.addmember(path_ptr);
631 if (!last_member.m_handle)
632 return new_member.destroy();
643 DDF DDF::getmember(const char* path) const
645 char name[MAX_NAME_LEN+1];
646 const char* path_ptr=path;
649 if (isstruct() && ddf_strlen(ddf_token(&path_ptr,name))>0) {
650 current.m_handle=m_handle->value.children.first;
651 while (current.m_handle && strcmp(current.m_handle->name,name)!=0)
652 current.m_handle=current.m_handle->next;
654 if (current.m_handle && ddf_strlen(path_ptr)>0)
655 current=current.getmember(path_ptr);
661 void ddf_print_indent(FILE* f, int indent)
663 for (; indent>0; indent--)
667 void DDF::dump(FILE* f, int indent) const
672 ddf_print_indent(f,indent);
674 switch (m_handle->type) {
676 case ddf_body_t::DDF_EMPTY:
679 fprintf(f," %s",m_handle->name);
682 case ddf_body_t::DDF_STRING:
684 fprintf(f,"char* %s = ",m_handle->name);
686 fprintf(f,"char* = ");
687 if (const char* chptr=m_handle->value.string) {
697 case ddf_body_t::DDF_INT:
699 fprintf(f,"long %s = ",m_handle->name);
701 fprintf(f,"long = ");
702 fprintf(f,"%ld",m_handle->value.integer);
705 case ddf_body_t::DDF_FLOAT:
707 fprintf(f,"double %s = ",m_handle->name);
709 fprintf(f,"double = ");
710 fprintf(f,"%.15f",m_handle->value.floating);
713 case ddf_body_t::DDF_STRUCT:
714 fprintf(f,"struct ");
716 fprintf(f,"%s ",m_handle->name);
718 if (m_handle->value.children.count) {
721 child.m_handle=m_handle->value.children.first;
722 while (child.m_handle) {
723 child.dump(f,indent+2);
724 child.m_handle=child.m_handle->next;
727 ddf_print_indent(f,indent);
731 case ddf_body_t::DDF_LIST:
734 fprintf(f," %s",m_handle->name);
735 fprintf(f,"[%lu] {",m_handle->value.children.count);
736 if (m_handle->value.children.count) {
739 child.m_handle=m_handle->value.children.first;
740 while (child.m_handle) {
741 child.dump(f,indent+2);
742 child.m_handle=child.m_handle->next;
745 ddf_print_indent(f,indent);
749 case ddf_body_t::DDF_POINTER:
751 fprintf(f,"void* %s = ",m_handle->name);
753 fprintf(f,"void* = ");
754 if (m_handle->value.pointer)
755 fprintf(f,"%p",m_handle->value.pointer);
761 fprintf(f,"UNKNOWN -- WARNING: ILLEGAL VALUE");
769 // Serialization is fairly easy. We have to walk the DDF and hand-generate a
770 // wddxPacket XML fragment, with some simple extensions. We escape the four major
771 // special characters, which requires that we output strings one char at a time.
773 void xml_encode(ostream& os, const char* start)
776 while (start && *start) {
777 pos = strcspn(start, "\"<>&");
784 case '"': os << """; break;
785 case '<': os << "<"; break;
786 case '>': os << ">"; break;
787 case '&': os << "&"; break;
788 default: os << *start;
795 void serialize(ddf_body_t* p, ostream& os, bool name_attr=true)
800 case ddf_body_t::DDF_STRING:
802 if (name_attr && p->name) {
804 xml_encode(os,p->name);
807 if (p->value.string) {
809 xml_encode(os,p->value.string);
816 case ddf_body_t::DDF_INT:
818 if (name_attr && p->name) {
820 xml_encode(os,p->name);
823 os << '>' << p->value.integer << "</number>";
826 case ddf_body_t::DDF_FLOAT:
828 if (name_attr && p->name) {
830 xml_encode(os,p->name);
833 os << '>' << fixed << p->value.floating << dec << "</number>";
836 case ddf_body_t::DDF_STRUCT:
839 if (name_attr && p->name) {
841 xml_encode(os,p->name);
845 ddf_body_t* child=p->value.children.first;
847 os << "<var name=\"";
848 xml_encode(os,child->name);
850 serialize(child,os,false);
858 case ddf_body_t::DDF_LIST:
860 os << "<array length=\"" << p->value.children.count << '"';
861 if (name_attr && p->name) {
863 xml_encode(os,p->name);
867 ddf_body_t* child=p->value.children.first;
876 case ddf_body_t::DDF_EMPTY:
877 case ddf_body_t::DDF_POINTER:
880 if (name_attr && p->name) {
882 xml_encode(os,p->name);
893 // The stream insertion will work for any ostream-based object.
895 SHIBSP_API ostream& shibsp::operator<<(ostream& os, const DDF& obj)
898 os << "<wddxPacket version=\"1.0\" lowercase=\"no\"><header/><data>";
899 serialize(obj.m_handle,os);
900 os << "</data></wddxPacket>";
904 // This is a DTD internal subset based on a compatible permutation of the WDDX spec, with the
905 // extension of a name attribute on all the typed elements, which DDF has, but WDDX does not.
908 static const char* g_DocType=
910 <!DOCTYPE wddxPacket [\n\
911 <!ELEMENT wddxPacket (header, data)>\n\
912 <!ATTLIST wddxPacket version CDATA #FIXED \"1.0\" lowercase (yes|no) \"yes\">\n\
913 <!ELEMENT header (comment?)>\n\
914 <!ELEMENT comment (#PCDATA)>\n\
915 <!ELEMENT data (null | number | string | array | struct)>\n\
916 <!ELEMENT null EMPTY>\n\
917 <!ATTLIST null name CDATA #IMPLIED type CDATA #IMPLIED>\n\
918 <!ELEMENT string (#PCDATA | char)*>\n\
919 <!ATTLIST string name CDATA #IMPLIED type CDATA #IMPLIED>\n\
920 <!ELEMENT char EMPTY>\n\
921 <!ATTLIST char code CDATA #REQUIRED>\n\
922 <!ELEMENT number (#PCDATA)>\n\
923 <!ATTLIST number name CDATA #IMPLIED type CDATA #IMPLIED>\n\
924 <!ELEMENT array (null | number | string | array | struct)*>\n\
925 <!ATTLIST array length CDATA #REQUIRED name CDATA #IMPLIED type CDATA #IMPLIED>\n\
926 <!ELEMENT struct (var*)>\n\
927 <!ATTLIST struct name CDATA #IMPLIED type CDATA #IMPLIED>\n\
928 <!ELEMENT var (null | number | string | array | struct)>\n\
929 <!ATTLIST var name CDATA #REQUIRED>\n\
933 // This function constructs a DDF object equivalent to the wddx data element rooted
936 static const XMLCh _no[] = UNICODE_LITERAL_2(n,o);
937 static const XMLCh _name[] = UNICODE_LITERAL_4(n,a,m,e);
938 static const XMLCh _var[] = UNICODE_LITERAL_3(v,a,r);
939 static const XMLCh _string[] = UNICODE_LITERAL_6(s,t,r,i,n,g);
940 static const XMLCh _number[] = UNICODE_LITERAL_6(n,u,m,b,e,r);
941 static const XMLCh _array[] = UNICODE_LITERAL_5(a,r,r,a,y);
942 static const XMLCh _struct[] = UNICODE_LITERAL_6(s,t,r,u,c,t);
943 static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e);
945 DDF deserialize(DOMElement* root, bool lowercase)
948 auto_ptr_char name_val(root->getAttribute(_name));
949 if (name_val.get() && *name_val.get()) {
951 for (char* pch=const_cast<char*>(name_val.get()); *pch=tolower(*pch); pch++);
952 obj.name(name_val.get());
955 const XMLCh* tag=root->getTagName();
956 if (XMLString::equals(tag,_var)) {
957 root=XMLHelper::getFirstChildElement(root);
958 tag=(root ? root->getTagName() : &chNull);
961 if (XMLString::equals(tag,_string)) {
962 DOMNode* child=root->getFirstChild();
963 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
964 char* val = toUTF8(child->getNodeValue(), true); // use malloc
966 obj.string(val, false); // don't re-copy the string
969 else if (XMLString::equals(tag,_number)) {
970 DOMNode* child=root->getFirstChild();
971 if (child && child->getNodeType()==DOMNode::TEXT_NODE) {
972 auto_ptr_char val(child->getNodeValue());
973 if (val.get() && strchr(val.get(),'.'))
974 obj.floating(val.get());
976 obj.integer(val.get());
979 else if (XMLString::equals(tag,_array)) {
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);
988 else if (XMLString::equals(tag,_struct)) {
990 DOMNodeList* children=root->getChildNodes();
991 for (unsigned int i=0; children && i<children->getLength(); i++)
992 if (children->item(i)->getNodeType()==DOMNode::ELEMENT_NODE) {
993 DDF temp=deserialize(static_cast<DOMElement*>(children->item(i)),lowercase);
1001 SHIBSP_API istream& shibsp::operator>>(istream& is, DDF& obj)
1003 // Parse the input stream into a DOM tree and construct the equivalent DDF.
1004 DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(is);
1005 XercesJanitor<DOMDocument> docj(doc);
1006 const XMLCh* lowercase=doc->getDocumentElement()->getAttribute(_lowercase);
1007 DOMElement* first=XMLHelper::getFirstChildElement(XMLHelper::getLastChildElement(doc->getDocumentElement()));
1009 obj=deserialize(first,XMLString::compareString(lowercase,_no)!=0);