a6b61e3e382374a89442a66a6cfb4b18004f588f
[shibboleth/cpp-opensaml.git] / saml / saml2 / metadata / impl / FolderMetadataProvider.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  * FolderMetadataProvider.cpp
23  * 
24  * MetadataProvider that loads all files in a directory.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "saml2/metadata/Metadata.h"
30 #include "saml2/metadata/MetadataProvider.h"
31
32 #include <memory>
33 #include <xercesc/util/XMLUniDefs.hpp>
34 #include <xmltooling/logging.h>
35 #include <xmltooling/XMLToolingConfig.h>
36 #include <xmltooling/util/PathResolver.h>
37 #include <xmltooling/util/XMLHelper.h>
38
39 #ifndef WIN32
40 # if defined(HAVE_SYS_TYPES_H) && defined(HAVE_DIRENT_H)
41 #  include <dirent.h>
42 #  include <sys/types.h>
43 #  include <sys/stat.h>
44 # else
45 #  error Unsupported directory library headers.
46 # endif
47 #endif
48
49 using namespace opensaml::saml2md;
50 using namespace opensaml;
51 using namespace xmlsignature;
52 using namespace xmltooling::logging;
53 using namespace xmltooling;
54 using namespace std;
55
56 namespace opensaml {
57     namespace saml2md {
58
59         static const XMLCh Chaining[] =             UNICODE_LITERAL_8(C,h,a,i,n,i,n,g);
60         static const XMLCh _MetadataProvider[] =    UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r);
61         static const XMLCh discoveryFeed[] =        UNICODE_LITERAL_13(d,i,s,c,o,v,e,r,y,F,e,e,d);
62         static const XMLCh dropDOM[] =              UNICODE_LITERAL_7(d,r,o,p,D,O,M);
63         static const XMLCh legacyOrgNames[] =       UNICODE_LITERAL_14(l,e,g,a,c,y,O,r,g,N,a,m,e,s);
64         static const XMLCh path[] =                 UNICODE_LITERAL_4(p,a,t,h);
65         static const XMLCh precedence[] =           UNICODE_LITERAL_10(p,r,e,c,e,d,e,n,c,e);
66         static const XMLCh reloadChanges[] =        UNICODE_LITERAL_13(r,e,l,o,a,d,C,h,a,n,g,e,s);
67         static const XMLCh validate[] =             UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);
68         static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);
69         static const XMLCh _XML[] =                 UNICODE_LITERAL_3(X,M,L);
70     
71         MetadataProvider* SAML_DLLLOCAL FolderMetadataProviderFactory(const DOMElement* const & e)
72         {
73             // The goal here is to construct a configuration for a chain of file-based providers
74             // based on the content of the directory we're given.
75
76             auto_ptr_char p(e->getAttributeNS(nullptr, path));
77             if (!p.get() || !*p.get()) {
78                 throw MetadataException("Folder MetadataProvider missing path setting.");
79             }
80
81             string fullname, loc(p.get());
82             XMLToolingConfig::getConfig().getPathResolver()->resolve(loc, PathResolver::XMLTOOLING_CFG_FILE);
83
84             // First we build a new root element of the right type, and copy in the precedence setting.
85             DOMElement* root = e->getOwnerDocument()->createElementNS(nullptr, _MetadataProvider);
86             root->setAttributeNS(nullptr, _type, Chaining);
87             if (e->hasAttributeNS(nullptr, precedence))
88                 root->setAttributeNS(nullptr, precedence, e->getAttributeNS(nullptr, precedence));
89
90             Category& log = Category::getInstance(SAML_LOGCAT".Metadata.Folder");
91             log.info("loading metadata files from folder (%s)", loc.c_str());
92
93 #ifdef WIN32
94             WIN32_FIND_DATA f;
95             fullname = loc + "/*";
96             HANDLE h = FindFirstFile(fullname.c_str(), &f);
97             if (h == INVALID_HANDLE_VALUE) {
98                 if (GetLastError() != ERROR_FILE_NOT_FOUND)
99                     throw MetadataException("Folder MetadataProvider unable to open directory ($1)", params(1, loc.c_str()));
100                 log.warn("no files found in folder (%s)", loc.c_str());
101                 return SAMLConfig::getConfig().MetadataProviderManager.newPlugin(CHAINING_METADATA_PROVIDER, root);
102             }
103             do {
104                 if (f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
105                     if (strcmp(f.cFileName, ".") && strcmp(f.cFileName, ".."))
106                         log.warn("nested folders not supported, skipping (%s)", f.cFileName);
107                     continue;
108                 }
109                 fullname = loc + '/' + f.cFileName;
110                 log.info("will create metadata source from (%s)", fullname.c_str());
111                 auto_ptr_XMLCh entry(fullname.c_str());
112 #else
113             DIR* d = opendir(loc.c_str());
114             if (!d) {
115                 throw MetadataException("Folder MetadataProvider unable to open directory ($1)", params(1, loc.c_str()));
116             }
117             char dir_buf[sizeof(struct dirent) + PATH_MAX];
118             struct dirent* ent = (struct dirent*)dir_buf;
119             struct dirent* entptr = nullptr;
120             while(readdir_r(d, ent, &entptr) == 0 && entptr) {
121                 if (!strcmp(entptr->d_name, ".") || !strcmp(entptr->d_name, ".."))
122                     continue;
123                 fullname = loc + '/' + entptr->d_name;
124                 struct stat stat_buf;
125                 if (stat(fullname.c_str(), &stat_buf) != 0) {
126                     log.warn("unable to access (%s)", entptr->d_name);
127                     continue;
128                 }
129                 else if (S_ISDIR(stat_buf.st_mode)) {
130                     log.warn("nested folders not supported, skipping (%s)", entptr->d_name);
131                     continue;
132                 }
133                 log.info("will create metadata source from (%s)", fullname.c_str());
134                 auto_ptr_XMLCh entry(fullname.c_str());
135 #endif
136                 DOMElement* child = e->getOwnerDocument()->createElementNS(nullptr, _MetadataProvider);
137                 child->setAttributeNS(nullptr, _type, _XML);
138                 child->setAttributeNS(nullptr, path, entry.get());
139                 if (e->hasAttributeNS(nullptr, validate))
140                     child->setAttributeNS(nullptr, validate, e->getAttributeNS(nullptr, validate));
141                 if (e->hasAttributeNS(nullptr, reloadChanges))
142                     child->setAttributeNS(nullptr, reloadChanges, e->getAttributeNS(nullptr, reloadChanges));
143                 if (e->hasAttributeNS(nullptr, discoveryFeed))
144                     child->setAttributeNS(nullptr, discoveryFeed, e->getAttributeNS(nullptr, discoveryFeed));
145                 if (e->hasAttributeNS(nullptr, legacyOrgNames))
146                     child->setAttributeNS(nullptr, legacyOrgNames, e->getAttributeNS(nullptr, legacyOrgNames));
147                 if (e->hasAttributeNS(nullptr, dropDOM))
148                     child->setAttributeNS(nullptr, dropDOM, e->getAttributeNS(nullptr, dropDOM));
149
150                 DOMElement* filter = XMLHelper::getFirstChildElement(e);
151                 while (filter) {
152                     child->appendChild(filter->cloneNode(true));
153                     filter = XMLHelper::getNextSiblingElement(filter);
154                 }
155                 root->appendChild(child);
156
157 #ifdef WIN32
158             } while (FindNextFile(h, &f));
159             FindClose(h);
160 #else
161             }
162             closedir(d);
163 #endif
164             return SAMLConfig::getConfig().MetadataProviderManager.newPlugin(CHAINING_METADATA_PROVIDER, root);
165         }
166
167     };
168 };