https://issues.shibboleth.net/jira/browse/SSPCPP-472
[shibboleth/sp.git] / shibsp / attribute / ExtensibleAttribute.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * shibsp/attribute/ExtensibleAttribute.cpp
23  *
24  * An Attribute whose values are arbitrary structures.
25  */
26
27 #include "internal.h"
28 #include "SPConfig.h"
29 #include "attribute/ExtensibleAttribute.h"
30 #include "util/SPConstants.h"
31
32 #include <boost/algorithm/string/trim.hpp>
33
34 using namespace shibsp;
35 using namespace xmltooling;
36 using namespace std;
37
38 namespace shibsp {
39     SHIBSP_DLLLOCAL Attribute* ExtensibleAttributeFactory(DDF& in) {
40         return new ExtensibleAttribute(in);
41     }
42 };
43
44 ExtensibleAttribute::ExtensibleAttribute(const vector<string>& ids, const char* formatter) : Attribute(ids)
45 {
46     m_obj = Attribute::marshall();
47     m_obj.name("Extensible");
48     m_obj.addmember("_formatter").string(formatter);
49 }
50
51 ExtensibleAttribute::ExtensibleAttribute(DDF& in) : Attribute(in), m_obj(in.copy())
52 {
53 }
54
55 ExtensibleAttribute::~ExtensibleAttribute()
56 {
57     m_obj.destroy();
58 }
59
60 DDF ExtensibleAttribute::getValues()
61 {
62     return m_obj.first();
63 }
64
65 size_t ExtensibleAttribute::valueCount() const
66 {
67     return m_obj.first().integer();
68 }
69
70 void ExtensibleAttribute::clearSerializedValues()
71 {
72     m_serialized.clear();
73 }
74
75 const char* ExtensibleAttribute::getString(size_t index) const
76 {
77     return m_obj.first()[static_cast<unsigned long>(index)].string();
78 }
79
80 const char* ExtensibleAttribute::getScope(size_t index) const
81 {
82     return nullptr;
83 }
84
85 void ExtensibleAttribute::removeValue(size_t index)
86 {
87     Attribute::removeValue(index);
88     DDF vals = m_obj.first();
89     if (index < static_cast<size_t>(vals.integer()))
90         vals[static_cast<unsigned long>(index)].remove().destroy();
91 }
92
93 const vector<string>& ExtensibleAttribute::getSerializedValues() const
94 {
95     if (m_serialized.empty()) {
96         const char* formatter = m_obj["_formatter"].string();
97         if (formatter) {
98             string msg = formatter;
99             DDF val = m_obj.first().first();
100             while (!val.isnull()) {
101
102                 static const char* legal="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_-.[]";
103
104                 m_serialized.push_back(string());
105                 string& processed = m_serialized.back();
106
107                 string::size_type i=0,start=0;
108                 while (start != string::npos && start < msg.length() && (i = msg.find("$",start)) != string::npos) {
109                     if (i>start)
110                         processed += msg.substr(start, i-start);    // append everything in between
111                     start = i+1;                                    // move start to the beginning of the token name
112                     i = msg.find_first_not_of(legal, start);        // find token delimiter
113                     if (i == start) {                               // append a non legal character
114                        processed += msg[start++];
115                        continue;
116                     }
117                     
118                     string tag = msg.substr(start, (i==string::npos) ? i : i-start);
119                     if (tag == "_string" && val.string()) {
120                         processed += val.string();
121                         start = i;
122                     }
123                     else {
124                         DDF child = val.getmember(tag.c_str());
125                         if (child.string())
126                             processed += child.string();
127                         else if (child.isstruct() && child["_string"].string())
128                             processed += child["_string"].string();
129                         start = i;
130                     }
131                 }
132                 if (start != string::npos && start < msg.length())
133                     processed += msg.substr(start,i);    // append rest of string
134                 boost::trim(processed);
135
136                 val = m_obj.first().next();
137             }
138         }
139     }
140     return Attribute::getSerializedValues();
141 }
142
143 DDF ExtensibleAttribute::marshall() const
144 {
145     DDF ddf = Attribute::marshall();
146     ddf.name("Extensible");
147     ddf.addmember("_formatter").string(m_obj["_formatter"].string());
148     DDF val = m_obj.first().first();
149     while (!val.isnull()) {
150         DDF dup = val.copy();
151         ddf.first().add(dup);
152         val = m_obj.first().next();
153     }
154     return ddf;
155 }