https://issues.shibboleth.net/jira/browse/SSPCPP-366
[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 using namespace shibsp;
33 using namespace xmltooling;
34 using namespace std;
35
36 namespace shibsp {
37     SHIBSP_DLLLOCAL Attribute* ExtensibleAttributeFactory(DDF& in) {
38         return new ExtensibleAttribute(in);
39     }
40 };
41
42 ExtensibleAttribute::ExtensibleAttribute(const vector<string>& ids, const char* formatter) : Attribute(ids)
43 {
44     m_obj = Attribute::marshall();
45     m_obj.name("Extensible");
46     m_obj.addmember("_formatter").string(formatter);
47 }
48
49 ExtensibleAttribute::ExtensibleAttribute(DDF& in) : Attribute(in), m_obj(in.copy())
50 {
51 }
52
53 ExtensibleAttribute::~ExtensibleAttribute()
54 {
55     m_obj.destroy();
56 }
57
58 DDF ExtensibleAttribute::getValues()
59 {
60     return m_obj.first();
61 }
62
63 size_t ExtensibleAttribute::valueCount() const
64 {
65     return m_obj.first().integer();
66 }
67
68 void ExtensibleAttribute::clearSerializedValues()
69 {
70     m_serialized.clear();
71 }
72
73 const char* ExtensibleAttribute::getString(size_t index) const
74 {
75     return m_obj.first()[static_cast<unsigned long>(index)].string();
76 }
77
78 const char* ExtensibleAttribute::getScope(size_t index) const
79 {
80     return nullptr;
81 }
82
83 void ExtensibleAttribute::removeValue(size_t index)
84 {
85     Attribute::removeValue(index);
86     DDF vals = m_obj.first();
87     if (index < static_cast<size_t>(vals.integer()))
88         vals[static_cast<unsigned long>(index)].remove().destroy();
89 }
90
91 const vector<string>& ExtensibleAttribute::getSerializedValues() const
92 {
93     if (m_serialized.empty()) {
94         const char* formatter = m_obj["_formatter"].string();
95         if (formatter) {
96             string msg = formatter;
97             DDF val = m_obj.first().first();
98             while (!val.isnull()) {
99
100                 static const char* legal="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_-.[]";
101
102                 m_serialized.push_back(string());
103                 string& processed = m_serialized.back();
104
105                 string::size_type i=0,start=0;
106                 while (start!=string::npos && start<msg.length() && (i=msg.find("$",start))!=string::npos) {
107                     if (i>start)
108                         processed += msg.substr(start,i-start); // append everything in between
109                     start=i+1;                                  // move start to the beginning of the token name
110                     i=msg.find_first_not_of(legal,start);       // find token delimiter
111                     if (i==start) {                             // append a non legal character
112                        processed+=msg[start++];
113                        continue;
114                     }
115                     
116                     string tag = msg.substr(start,(i==string::npos) ? i : i-start);
117                     if (tag == "_string" && val.string()) {
118                         processed += val.string();
119                         start=i;
120                     }
121                     else {
122                         DDF child = val.getmember(tag.c_str());
123                         if (child.string())
124                             processed += child.string();
125                         else if (child.isstruct() && child["_string"].string())
126                             processed += child["_string"].string();
127                         start=i;
128                     }
129                 }
130                 if (start!=string::npos && start<msg.length())
131                     processed += msg.substr(start,i);    // append rest of string
132
133                 val = m_obj.first().next();
134             }
135         }
136     }
137     return Attribute::getSerializedValues();
138 }
139
140 DDF ExtensibleAttribute::marshall() const
141 {
142     DDF ddf = Attribute::marshall();
143     ddf.name("Extensible");
144     ddf.addmember("_formatter").string(m_obj["_formatter"].string());
145     DDF val = m_obj.first().first();
146     while (!val.isnull()) {
147         DDF dup = val.copy();
148         ddf.first().add(dup);
149         val = m_obj.first().next();
150     }
151     return ddf;
152 }