Switched remaining files to Apache license.
[shibboleth/cpp-sp.git] / xmlproviders / TargetedID.cpp
1 /*
2  *  Copyright 2001-2005 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 /* TargetedID.cpp - eduPersonTargetedID custom attribute handling
18
19    Scott Cantor
20    4/30/05
21
22    $History:$
23 */
24
25 #include "internal.h"
26 #include <xercesc/util/Base64.hpp>
27
28 using namespace shibboleth;
29 using namespace saml;
30 using namespace std;
31
32 namespace {
33     class TargetedID : public SAMLAttribute
34     {
35     public:
36         TargetedID(
37             const XMLCh* name=NULL,
38             const XMLCh* ns=NULL,
39             const saml::QName* type=NULL,
40             long lifetime=0,
41             const Iterator<const XMLCh*>& values=EMPTY(const XMLCh*),
42             const Iterator<const XMLCh*>& nameQualifiers=EMPTY(const XMLCh*),
43             const Iterator<const XMLCh*>& spNameQualifiers=EMPTY(const XMLCh*)
44             );
45         TargetedID(DOMElement* e);
46         TargetedID(istream& in);
47         ~TargetedID();
48     
49         saml::SAMLObject* clone() const;
50         
51         saml::Iterator<const XMLCh*> getValues() const;
52         saml::Iterator<std::string> getSingleByteValues() const;
53
54         void setValues(const saml::Iterator<const XMLCh*>& values=EMPTY(const XMLCh*)) {
55             throw SAMLException("unsupported operation");
56         }
57         void addValue(const XMLCh* value) {
58             throw SAMLException("unsupported operation");
59         }
60         void removeValue(unsigned long index);
61         
62         static const XMLCh NameID[];
63         static const XMLCh SPNameQualifier[];
64         static const XMLCh FORMAT_PERSISTENT[];
65     protected:
66         void valueToDOM(unsigned int index, DOMElement* e) const;
67         void valueFromDOM(DOMElement* e);
68         void ownStrings();
69     
70     private:
71         vector<const XMLCh*> m_nameQualifiers;
72         vector<const XMLCh*> m_spNameQualifiers;
73         mutable vector<const XMLCh*> m_encodedValues;
74     };
75
76     struct TargetedIDBuilder : public virtual IAttributeFactory
77     {
78         TargetedIDBuilder(const DOMElement* e) {}
79         SAMLAttribute* build(DOMElement* e) const {
80             return new TargetedID(e);
81         }
82     };
83 }
84
85 IPlugIn* TargetedIDFactory(const DOMElement* e)
86 {
87     return new TargetedIDBuilder(e);
88 }
89
90 TargetedID::TargetedID(
91     const XMLCh* name,
92     const XMLCh* ns,
93     const saml::QName* type,
94     long lifetime,
95     const Iterator<const XMLCh*>& values,
96     const Iterator<const XMLCh*>& nameQualifiers,
97     const Iterator<const XMLCh*>& spNameQualifiers
98     ) : SAMLAttribute(name,ns,NULL,lifetime,values)
99 {
100     RTTI(TargetedID);
101     if (values.size()!=nameQualifiers.size() || values.size()!=spNameQualifiers.size())
102         throw MalformedException("TargetedID() requires the number of qualifiers to equal the number of values");
103
104     while (nameQualifiers.hasNext())
105         m_nameQualifiers.push_back(saml::XML::assign(nameQualifiers.next()));
106     while (spNameQualifiers.hasNext())
107         m_spNameQualifiers.push_back(saml::XML::assign(spNameQualifiers.next()));
108 }
109
110 TargetedID::TargetedID(DOMElement* e) : SAMLAttribute(e,false)
111 {
112     RTTI(TargetedID);
113     fromDOM(e);
114 }
115
116 TargetedID::TargetedID(istream& in) : SAMLAttribute(in,false)
117 {
118     RTTI(TargetedID);
119     fromDOM(m_document->getDocumentElement());
120 }
121
122 TargetedID::~TargetedID()
123 {
124     if (m_bOwnStrings) {
125         for (vector<const XMLCh*>::iterator i=m_nameQualifiers.begin(); i!=m_nameQualifiers.end(); i++) {
126             XMLCh* p = const_cast<XMLCh*>(*i);
127             XMLString::release(&p);
128         }
129         for (vector<const XMLCh*>::iterator j=m_spNameQualifiers.begin(); j!=m_spNameQualifiers.end(); j++) {
130             XMLCh* p = const_cast<XMLCh*>(*j);
131             XMLString::release(&p);
132         }
133     }
134
135     // We always own any encoded values we've built.
136     for (vector<const XMLCh*>::iterator i=m_encodedValues.begin(); i!=m_encodedValues.end(); i++) {
137         XMLCh* p = const_cast<XMLCh*>(*i);
138         XMLString::release(&p);
139     }
140 }
141
142 void TargetedID::ownStrings()
143 {
144     if (!m_bOwnStrings) {
145         for (vector<const XMLCh*>::iterator i=m_nameQualifiers.begin(); i!=m_nameQualifiers.end(); i++)
146             (*i)=saml::XML::assign(*i);
147         for (vector<const XMLCh*>::iterator j=m_spNameQualifiers.begin(); j!=m_spNameQualifiers.end(); j++)
148             (*j)=saml::XML::assign(*j);
149         SAMLAttribute::ownStrings();
150     }
151 }
152
153 Iterator<const XMLCh*> TargetedID::getValues() const
154 {
155     if (m_encodedValues.empty()) {
156         getSingleByteValues();
157         for (vector<string>::const_iterator i=m_sbValues.begin(); i!=m_sbValues.end(); i++)
158             m_encodedValues.push_back(XMLString::transcode(i->c_str()));
159     }
160     return m_encodedValues;
161 }
162
163 Iterator<string> TargetedID::getSingleByteValues() const
164 {
165     if (m_sbValues.empty()) {
166         for (unsigned long i=0; i<m_values.size(); i++) {
167             auto_ptr_char a(m_nameQualifiers[i]);
168             auto_ptr_char b(m_spNameQualifiers[i]);
169             auto_ptr_char c(m_values[i]);
170             if (a.get() && *(a.get()) && b.get() && *(b.get()) && c.get() && *(c.get())) {
171                 string cat(a.get()); cat+="!"; cat+=b.get(); cat+="!"; cat+=c.get();
172                 m_sbValues.push_back(cat);
173             }
174             else
175                 m_sbValues.push_back("");
176         }
177     }
178     return m_sbValues;
179 }
180
181 void TargetedID::removeValue(unsigned long index)
182 {
183     if (m_bOwnStrings) {
184         XMLCh* p=const_cast<XMLCh*>(m_nameQualifiers[index]);
185         XMLString::release(&p);
186         p=const_cast<XMLCh*>(m_spNameQualifiers[index]);
187         XMLString::release(&p);
188     }
189     m_nameQualifiers.erase(m_nameQualifiers.begin()+index);
190     m_spNameQualifiers.erase(m_spNameQualifiers.begin()+index);
191
192     if (!m_encodedValues.empty()) {
193         XMLCh* p=const_cast<XMLCh*>(m_encodedValues[index]);
194         XMLString::release(&p);
195         m_encodedValues.erase(m_encodedValues.begin()+index);
196     }
197     
198     SAMLAttribute::removeValue(index);
199 }
200
201 void TargetedID::valueFromDOM(DOMElement* e)
202 {
203     // Look for a SAML2 NameID.
204     e=saml::XML::getFirstChildElement(e,::XML::SAML2ASSERT_NS,NameID);
205     if (e && !XMLString::compareString(FORMAT_PERSISTENT,e->getAttributeNS(NULL,L(Format)))) {
206         m_nameQualifiers.push_back(e->getAttributeNS(NULL,L(NameQualifier)));
207         m_spNameQualifiers.push_back(e->getAttributeNS(NULL,SPNameQualifier));
208         if (e->hasChildNodes() && e->getFirstChild()->getNodeType()==DOMNode::TEXT_NODE)
209             m_values.push_back(e->getFirstChild()->getNodeValue());
210         else
211             m_values.push_back(&chNull);
212         return;
213     }
214
215     // Insert a null value placeholder.
216     m_nameQualifiers.push_back(&chNull);    
217     m_spNameQualifiers.push_back(&chNull);    
218     m_values.push_back(&chNull);
219 }
220
221 void TargetedID::valueToDOM(unsigned int index, DOMElement* e) const
222 {
223     const XMLCh* nq=m_nameQualifiers[index];
224     const XMLCh* spnq=m_spNameQualifiers[index];
225     const XMLCh* val=m_values[index];
226     if (!saml::XML::isEmpty(nq) && !saml::XML::isEmpty(spnq) && !saml::XML::isEmpty(val)) {
227         // Build a SAML2 NameID.
228         DOMElement* nameid=e->getOwnerDocument()->createElementNS(::XML::SAML2ASSERT_NS,NameID);
229         nameid->setAttributeNS(NULL,L(Format),FORMAT_PERSISTENT);    
230         nameid->setAttributeNS(NULL,L(NameQualifier),nq);
231         nameid->setAttributeNS(NULL,SPNameQualifier,spnq);
232         nameid->appendChild(e->getOwnerDocument()->createTextNode(val));
233         e->appendChild(nameid);
234     }
235 }
236
237 SAMLObject* TargetedID::clone() const
238 {
239     return new TargetedID(m_name,m_namespace,m_type,m_lifetime,m_values,m_nameQualifiers,m_spNameQualifiers);
240 }
241
242 const XMLCh TargetedID::NameID[] =
243 { chLatin_N, chLatin_a, chLatin_m, chLatin_e, chLatin_I, chLatin_D, chNull };
244
245 const XMLCh TargetedID::SPNameQualifier[] =
246 { chLatin_S, chLatin_P, chLatin_N, chLatin_a, chLatin_m, chLatin_e,
247   chLatin_Q, chLatin_u, chLatin_a, chLatin_l, chLatin_i, chLatin_f, chLatin_i, chLatin_e, chLatin_r, chNull
248 };
249
250 const XMLCh TargetedID::FORMAT_PERSISTENT[] =
251 {
252     chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_o, chLatin_a, chLatin_s, chLatin_i, chLatin_s, chColon,
253     chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_s, chColon, chLatin_t, chLatin_c, chColon,
254     chLatin_S, chLatin_A, chLatin_M, chLatin_L, chColon, chDigit_2, chPeriod, chDigit_0, chColon,
255     chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_i, chLatin_d, chDash,
256     chLatin_f, chLatin_o, chLatin_r, chLatin_m, chLatin_a, chLatin_t, chColon,
257     chLatin_p, chLatin_e, chLatin_r, chLatin_s, chLatin_i, chLatin_s, chLatin_t, chLatin_e, chLatin_n, chLatin_t, chNull
258 };