Removed unnecessary class from string literals.
[shibboleth/cpp-opensaml.git] / saml / saml2 / metadata / impl / FilesystemMetadataProvider.cpp
1 /*
2  *  Copyright 2001-2006 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 /**
18  * FilesystemMetadataProvider.cpp
19  * 
20  * Supplies metadata from a local file, detecting and reloading changes.
21  */
22
23 #include "internal.h"
24 #include "saml2/metadata/Metadata.h"
25 #include "saml2/metadata/AbstractMetadataProvider.h"
26
27 #include <ctime>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <log4cpp/Category.hh>
31 #include <xercesc/framework/LocalFileInputSource.hpp>
32 #include <xercesc/framework/Wrapper4InputSource.hpp>
33 #include <xercesc/util/XMLUniDefs.hpp>
34 #include <xmltooling/util/NDC.h>
35 #include <xmltooling/util/Threads.h>
36 #include <xmltooling/util/XMLConstants.h>
37
38 using namespace opensaml::saml2md;
39 using namespace xmltooling;
40 using namespace log4cpp;
41 using namespace std;
42
43 namespace opensaml {
44     namespace saml2md {
45         
46         
47         class SAML_DLLLOCAL FilesystemMetadataProvider : public AbstractMetadataProvider
48         {
49         public:
50             FilesystemMetadataProvider(const DOMElement* e);
51             ~FilesystemMetadataProvider();
52
53             Lockable* lock();
54             void unlock() {
55                 if (m_lock)
56                     m_lock->unlock();
57             }
58
59             void init();
60
61             const XMLObject* getMetadata() const {
62                 return m_object;
63             }
64
65         private:
66             XMLObject* load() const;
67             void index();
68         
69             const DOMElement* m_root; // survives only until init() method is done
70             std::string m_source;
71             time_t m_filestamp;
72             bool m_validate;
73             RWLock* m_lock;
74             XMLObject* m_object;
75         }; 
76
77         MetadataProvider* SAML_DLLLOCAL FilesystemMetadataProviderFactory(const DOMElement* const & e)
78         {
79             return new FilesystemMetadataProvider(e);
80         }
81
82     };
83 };
84
85 static const XMLCh uri[] =      UNICODE_LITERAL_3(u,r,i);
86 static const XMLCh url[] =      UNICODE_LITERAL_3(u,r,l);
87 static const XMLCh path[] =     UNICODE_LITERAL_4(p,a,t,h);
88 static const XMLCh pathname[] = UNICODE_LITERAL_8(p,a,t,h,n,a,m,e);
89 static const XMLCh file[] =     UNICODE_LITERAL_4(f,i,l,e);
90 static const XMLCh filename[] = UNICODE_LITERAL_8(f,i,l,e,n,a,m,e);
91 static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);
92
93 FilesystemMetadataProvider::FilesystemMetadataProvider(const DOMElement* e)
94     : AbstractMetadataProvider(e), m_root(e), m_filestamp(0), m_validate(false), m_lock(NULL), m_object(NULL)
95 {
96 #ifdef _DEBUG
97     NDC ndc("FilesystemMetadataProvider");
98 #endif
99     Category& log=Category::getInstance(SAML_LOGCAT".Metadata");
100
101     // Establish source of data...
102     const XMLCh* source=e->getAttributeNS(NULL,uri);
103     if (!source || !*source) {
104         source=e->getAttributeNS(NULL,url);
105         if (!source || !*source) {
106             source=e->getAttributeNS(NULL,path);
107             if (!source || !*source) {
108                 source=e->getAttributeNS(NULL,pathname);
109                 if (!source || !*source) {
110                     source=e->getAttributeNS(NULL,file);
111                     if (!source || !*source) {
112                         source=e->getAttributeNS(NULL,filename);
113                     }
114                 }
115             }
116         }
117     }
118     
119     if (source && *source) {
120         const XMLCh* valflag=e->getAttributeNS(NULL,validate);
121         m_validate=(XMLString::equals(valflag,xmlconstants::XML_TRUE) || XMLString::equals(valflag,xmlconstants::XML_ONE));
122         
123         auto_ptr_char temp(source);
124         m_source=temp.get();
125         log.debug("using external metadata file (%s)", temp.get());
126
127 #ifdef WIN32
128         struct _stat stat_buf;
129         if (_stat(m_source.c_str(), &stat_buf) == 0)
130 #else
131         struct stat stat_buf;
132         if (stat(m_source.c_str(), &stat_buf) == 0)
133 #endif
134             m_filestamp=stat_buf.st_mtime;
135         m_lock=RWLock::create();
136     }
137     else
138         log.debug("no file path/name supplied, will look for metadata inline");
139 }
140
141 FilesystemMetadataProvider::~FilesystemMetadataProvider()
142 {
143     delete m_lock;
144     delete m_object;
145 }
146
147 void FilesystemMetadataProvider::init()
148 {
149     m_object=load();
150     index();
151 }
152
153 XMLObject* FilesystemMetadataProvider::load() const
154 {
155 #ifdef _DEBUG
156     NDC ndc("load");
157 #endif
158     Category& log=Category::getInstance(SAML_LOGCAT".Metadata");
159     
160     try {
161         XMLObject* xmlObject=NULL;
162         
163         if (!m_source.empty()) {
164             // Data comes from a file we have to parse.
165             log.debug("loading metadata from file...");
166             auto_ptr_XMLCh widenit(m_source.c_str());
167             LocalFileInputSource src(widenit.get());
168             Wrapper4InputSource dsrc(&src,false);
169             DOMDocument* doc=NULL;
170             if (m_validate)
171                 doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);
172             else
173                 doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
174             XercesJanitor<DOMDocument> docjanitor(doc);
175             log.infoStream() << "loaded and parsed XML file (" << m_source << ")" << CategoryStream::ENDLINE;
176             
177             // Unmarshall objects, binding the document.
178             xmlObject = XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true);
179             docjanitor.release();
180         }
181         else {
182             // Data comes from the DOM we were handed.
183             log.debug("loading inline metadata...");
184             DOMElement* child = XMLHelper::getFirstChildElement(m_root);
185             if (!child)
186                 throw XMLToolingException("No metadata was found inline.");
187             xmlObject = XMLObjectBuilder::buildOneFromElement(child);
188         }
189         
190         auto_ptr<XMLObject> xmlObjectPtr(xmlObject);
191         
192         doFilters(*xmlObject);
193         
194         xmlObjectPtr->releaseThisAndChildrenDOM();
195         xmlObjectPtr->setDocument(NULL);
196         return xmlObjectPtr.release();
197     }
198     catch (XMLException& e) {
199         auto_ptr_char msg(e.getMessage());
200         log.errorStream() << "Xerces parser error while loading metadata from ("
201             << (m_source.empty() ? "inline" : m_source) << "): " << msg.get() << CategoryStream::ENDLINE;
202         throw XMLParserException(msg.get());
203     }
204     catch (XMLToolingException& e) {
205         log.errorStream() << "error while loading metadata from ("
206             << (m_source.empty() ? "inline" : m_source) << "): " << e.what() << CategoryStream::ENDLINE;
207         throw;
208     }
209 }
210
211 Lockable* FilesystemMetadataProvider::lock()
212 {
213     if (!m_lock)
214         return this;
215         
216     m_lock->rdlock();
217
218     // Check if we need to refresh.
219 #ifdef WIN32
220     struct _stat stat_buf;
221     if (_stat(m_source.c_str(), &stat_buf) == 0)
222 #else
223     struct stat stat_buf;
224     if (stat(m_source.c_str(), &stat_buf) == 0)
225 #endif
226     {
227         if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime) {
228             // Elevate lock and recheck.
229             m_lock->unlock();
230             m_lock->wrlock();
231             if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime) {
232                 SharedLock lockwrap(m_lock,false);  // pops write lock
233                 try {
234                     // Update the timestamp regardless. No point in repeatedly trying.
235                     m_filestamp=stat_buf.st_mtime;
236                     XMLObject* newstuff = load();
237                     delete m_object;
238                     m_object = newstuff;
239                     index();
240                     emitChangeEvent();
241                 }
242                 catch(XMLToolingException& e) {
243                     Category::getInstance(SAML_LOGCAT".Metadata").error("failed to reload metadata from file, sticking with what we have: %s", e.what());
244                 }
245             }
246             else {
247                 m_lock->unlock();
248             }
249             m_lock->rdlock();
250         }
251     }
252     return this;
253 }
254
255 void FilesystemMetadataProvider::index()
256 {
257     clearDescriptorIndex();
258     EntitiesDescriptor* group=dynamic_cast<EntitiesDescriptor*>(m_object);
259     if (group) {
260         AbstractMetadataProvider::index(group, SAMLTIME_MAX);
261         return;
262     }
263     EntityDescriptor* site=dynamic_cast<EntityDescriptor*>(m_object);
264     AbstractMetadataProvider::index(site, SAMLTIME_MAX);
265 }