Moved URLEncoder down to tooling lib, added exception->querystring method.
[shibboleth/cpp-xmltooling.git] / xmltooling / XMLToolingConfig.cpp
1 /*
2  *  Copyright 2001-2007 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  * XMLToolingConfig.cpp
19  * 
20  * Library configuration 
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "XMLToolingConfig.h"
26 #include "encryption/Encrypter.h"
27 #include "impl/UnknownElement.h"
28 #include "security/TrustEngine.h"
29 #include "security/OpenSSLCryptoX509CRL.h"
30 #include "security/CredentialResolver.h"
31 #include "soap/SOAP.h"
32 #include "soap/SOAPTransport.h"
33 #include "util/NDC.h"
34 #include "util/ReplayCache.h"
35 #include "util/StorageService.h"
36 #include "util/TemplateEngine.h"
37 #include "util/URLEncoder.h"
38 #include "util/XMLConstants.h"
39 #include "validation/ValidatorSuite.h"
40
41 #ifdef HAVE_DLFCN_H
42 # include <dlfcn.h>
43 #endif
44
45 #include <stdexcept>
46 #include <curl/curl.h>
47 #include <log4cpp/Category.hh>
48 #include <log4cpp/PropertyConfigurator.hh>
49 #include <log4cpp/OstreamAppender.hh>
50 #include <xercesc/util/PlatformUtils.hpp>
51 #ifndef XMLTOOLING_NO_XMLSEC
52     #include <xsec/framework/XSECProvider.hpp>
53     #include <openssl/err.h>
54 #endif
55
56 using namespace soap11;
57 using namespace xmlencryption;
58 using namespace xmlsignature;
59 using namespace xmltooling;
60 using namespace log4cpp;
61 using namespace std;
62
63 DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLParserException,xmltooling);
64 DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLObjectException,xmltooling);
65 DECL_XMLTOOLING_EXCEPTION_FACTORY(MarshallingException,xmltooling);
66 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnmarshallingException,xmltooling);
67 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownElementException,xmltooling);
68 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownAttributeException,xmltooling);
69 DECL_XMLTOOLING_EXCEPTION_FACTORY(UnknownExtensionException,xmltooling);
70 DECL_XMLTOOLING_EXCEPTION_FACTORY(ValidationException,xmltooling);
71 DECL_XMLTOOLING_EXCEPTION_FACTORY(IOException,xmltooling);
72
73 #ifndef XMLTOOLING_NO_XMLSEC
74     DECL_XMLTOOLING_EXCEPTION_FACTORY(XMLSecurityException,xmltooling);
75     DECL_XMLTOOLING_EXCEPTION_FACTORY(SignatureException,xmlsignature);
76     DECL_XMLTOOLING_EXCEPTION_FACTORY(EncryptionException,xmlencryption);
77 #endif
78
79 namespace xmltooling {
80     static XMLToolingInternalConfig g_config;
81     static vector<Mutex*> g_openssl_locks;
82
83     extern "C" void openssl_locking_callback(int mode,int n,const char *file,int line)
84     {
85         if (mode & CRYPTO_LOCK)
86             g_openssl_locks[n]->lock();
87         else
88             g_openssl_locks[n]->unlock();
89     }
90     
91     #ifndef WIN32
92     extern "C" unsigned long openssl_thread_id(void)
93     {
94         return (unsigned long)(pthread_self());
95     }
96     #endif
97 }
98
99 XMLToolingConfig& XMLToolingConfig::getConfig()
100 {
101     return g_config;
102 }
103
104 XMLToolingInternalConfig& XMLToolingInternalConfig::getInternalConfig()
105 {
106     return g_config;
107 }
108
109 bool XMLToolingInternalConfig::log_config(const char* config)
110 {
111     try {
112         if (!config || !*config)
113             config=getenv("XMLTOOLING_LOG_CONFIG");
114         if (!config || !*config)
115             config="WARN";
116         
117         bool level=false;
118         Category& root = Category::getRoot();
119         if (!strcmp(config,"DEBUG")) {
120             root.setPriority(Priority::DEBUG);
121             level=true;
122         }
123         else if (!strcmp(config,"INFO")) {
124             root.setPriority(Priority::INFO);
125             level=true;
126         }
127         else if (!strcmp(config,"NOTICE")) {
128             root.setPriority(Priority::NOTICE);
129             level=true;
130         }
131         else if (!strcmp(config,"WARN")) {
132             root.setPriority(Priority::WARN);
133             level=true;
134         }
135         else if (!strcmp(config,"ERROR")) {
136             root.setPriority(Priority::ERROR);
137             level=true;
138         }
139         else if (!strcmp(config,"CRIT")) {
140             root.setPriority(Priority::CRIT);
141             level=true;
142         }
143         else if (!strcmp(config,"ALERT")) {
144             root.setPriority(Priority::ALERT);
145             level=true;
146         }
147         else if (!strcmp(config,"EMERG")) {
148             root.setPriority(Priority::EMERG);
149             level=true;
150         }
151         else if (!strcmp(config,"FATAL")) {
152             root.setPriority(Priority::FATAL);
153             level=true;
154         }
155         if (level)
156             root.setAppender(new OstreamAppender("default",&cerr));
157         else
158             PropertyConfigurator::configure(config);
159     }
160     catch (const ConfigureFailure& e) {
161         Category::getInstance(XMLTOOLING_LOGCAT".Logging").crit("failed to initialize log4cpp: %s", e.what());
162         return false;
163     }
164     
165     return true;
166 }
167
168 void XMLToolingConfig::setReplayCache(ReplayCache* replayCache)
169 {
170     delete m_replayCache;
171     m_replayCache = replayCache;
172 }
173
174 void XMLToolingConfig::setTemplateEngine(TemplateEngine* templateEngine)
175 {
176     delete m_templateEngine;
177     m_templateEngine = templateEngine;
178 }
179
180 void XMLToolingConfig::setURLEncoder(URLEncoder* urlEncoder)
181 {
182     delete m_urlEncoder;
183     m_urlEncoder = urlEncoder;
184 }
185
186 bool XMLToolingInternalConfig::init()
187 {
188 #ifdef _DEBUG
189     xmltooling::NDC ndc("init");
190 #endif
191     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLToolingConfig");
192     try {
193         log.debug("library initialization started");
194
195         if (curl_global_init(CURL_GLOBAL_ALL)) {
196             log.fatal("failed to initialize libcurl, OpenSSL, or Winsock");
197             return false;
198         }
199         log.debug("libcurl %s initialization complete", LIBCURL_VERSION);
200
201         xercesc::XMLPlatformUtils::Initialize();
202         log.debug("Xerces initialization complete");
203
204 #ifndef XMLTOOLING_NO_XMLSEC
205         XSECPlatformUtils::Initialise();
206         m_xsecProvider=new XSECProvider();
207         log.debug("XMLSec initialization complete");
208 #endif
209
210         m_parserPool=new ParserPool();
211         m_validatingPool=new ParserPool(true,true);
212         m_lock=xercesc::XMLPlatformUtils::makeMutex();
213         
214         // Load catalogs from path.
215         if (!catalog_path.empty()) {
216             char* catpath=strdup(catalog_path.c_str());
217             char* sep=NULL;
218             char* start=catpath;
219             while (start && *start) {
220                 sep=strchr(start,PATH_SEPARATOR_CHAR);
221                 if (sep)
222                     *sep=0;
223                 auto_ptr_XMLCh temp(start);
224                 m_validatingPool->loadCatalog(temp.get());
225                 start = sep ? sep + 1 : NULL;
226             }
227             free(catpath);
228         }
229
230         // default registrations
231         XMLObjectBuilder::registerDefaultBuilder(new UnknownElementBuilder());
232
233         registerKeyInfoClasses();
234         registerEncryptionClasses();
235         registerSOAPClasses();
236
237         m_urlEncoder = new URLEncoder();
238         
239         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(XMLParserException,xmltooling);
240         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(XMLObjectException,xmltooling);
241         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(MarshallingException,xmltooling);
242         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(UnmarshallingException,xmltooling);
243         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(UnknownElementException,xmltooling);
244         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(UnknownAttributeException,xmltooling);
245         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(ValidationException,xmltooling);
246         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(IOException,xmltooling);
247         
248 #ifndef XMLTOOLING_NO_XMLSEC
249         XMLObjectBuilder::registerBuilder(QName(xmlconstants::XMLSIG_NS,Signature::LOCAL_NAME),new SignatureBuilder());
250         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(XMLSecurityException,xmltooling);
251         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(SignatureException,xmlsignature);
252         REGISTER_XMLTOOLING_EXCEPTION_FACTORY(EncryptionException,xmlencryption);
253         registerKeyResolvers();
254         registerCredentialResolvers();
255         registerTrustEngines();
256 #endif
257         registerSOAPTransports();
258         initSOAPTransports();
259         registerStorageServices();
260
261         // Register xml:id as an ID attribute.        
262         static const XMLCh xmlid[] = UNICODE_LITERAL_2(i,d);
263         AttributeExtensibleXMLObject::registerIDAttribute(QName(xmlconstants::XML_NS, xmlid)); 
264     }
265     catch (const xercesc::XMLException&) {
266         log.fatal("caught exception while initializing Xerces");
267         curl_global_cleanup();
268         return false;
269     }
270
271     // Set up OpenSSL locking.
272     for (int i=0; i<CRYPTO_num_locks(); i++)
273         g_openssl_locks.push_back(Mutex::create());
274     CRYPTO_set_locking_callback(openssl_locking_callback);
275 #ifndef WIN32
276     CRYPTO_set_id_callback(openssl_thread_id);
277 #endif
278
279     log.info("library initialization complete");
280     return true;
281 }
282
283 void XMLToolingInternalConfig::term()
284 {
285     CRYPTO_set_locking_callback(NULL);
286     for_each(g_openssl_locks.begin(), g_openssl_locks.end(), xmltooling::cleanup<Mutex>());
287     g_openssl_locks.clear();
288
289     SchemaValidators.destroyValidators();
290     XMLObjectBuilder::destroyBuilders();
291     XMLToolingException::deregisterFactories();
292     AttributeExtensibleXMLObject::deregisterIDAttributes();
293
294     StorageServiceManager.deregisterFactories();
295     termSOAPTransports();
296     SOAPTransportManager.deregisterFactories();
297 #ifndef XMLTOOLING_NO_XMLSEC
298     TrustEngineManager.deregisterFactories();
299     CredentialResolverManager.deregisterFactories();
300     KeyResolverManager.deregisterFactories();
301 #endif
302
303     delete m_replayCache;
304     m_replayCache = NULL;
305     
306     delete m_templateEngine;
307     m_templateEngine = NULL;
308
309     delete m_urlEncoder;
310     m_urlEncoder = NULL;
311
312     for (vector<void*>::reverse_iterator i=m_libhandles.rbegin(); i!=m_libhandles.rend(); i++) {
313 #if defined(WIN32)
314         FARPROC fn=GetProcAddress(static_cast<HMODULE>(*i),"xmltooling_extension_term");
315         if (fn)
316             fn();
317         FreeLibrary(static_cast<HMODULE>(*i));
318 #elif defined(HAVE_DLFCN_H)
319         void (*fn)()=(void (*)())dlsym(*i,"xmltooling_extension_term");
320         if (fn)
321             fn();
322         dlclose(*i);
323 #else
324 # error "Don't know about dynamic loading on this platform!"
325 #endif
326     }
327     m_libhandles.clear();
328     
329     delete m_parserPool;
330     m_parserPool=NULL;
331     delete m_validatingPool;
332     m_validatingPool=NULL;
333
334 #ifndef XMLTOOLING_NO_XMLSEC
335     delete m_xsecProvider;
336     m_xsecProvider=NULL;
337     XSECPlatformUtils::Terminate();
338 #endif
339
340     xercesc::XMLPlatformUtils::closeMutex(m_lock);
341     m_lock=NULL;
342     xercesc::XMLPlatformUtils::Terminate();
343
344     curl_global_cleanup();
345     
346  #ifdef _DEBUG
347     xmltooling::NDC ndc("term");
348 #endif
349    Category::getInstance(XMLTOOLING_LOGCAT".XMLToolingConfig").info("library shutdown complete");
350 }
351
352 Lockable* XMLToolingInternalConfig::lock()
353 {
354     xercesc::XMLPlatformUtils::lockMutex(m_lock);
355     return this;
356 }
357
358 void XMLToolingInternalConfig::unlock()
359 {
360     xercesc::XMLPlatformUtils::unlockMutex(m_lock);
361 }
362
363 bool XMLToolingInternalConfig::load_library(const char* path, void* context)
364 {
365 #ifdef _DEBUG
366     xmltooling::NDC ndc("LoadLibrary");
367 #endif
368     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLToolingConfig");
369     log.info("loading extension: %s", path);
370
371     Locker locker(this);
372
373 #if defined(WIN32)
374     HMODULE handle=NULL;
375     char* fixed=const_cast<char*>(path);
376     if (strchr(fixed,'/')) {
377         fixed=strdup(path);
378         char* p=fixed;
379         while (p=strchr(p,'/'))
380             *p='\\';
381     }
382
383     UINT em=SetErrorMode(SEM_FAILCRITICALERRORS);
384     try {
385         handle=LoadLibraryEx(fixed,NULL,LOAD_WITH_ALTERED_SEARCH_PATH);
386         if (!handle)
387              handle=LoadLibraryEx(fixed,NULL,0);
388         if (!handle)
389             throw runtime_error(string("unable to load extension library: ") + fixed);
390         FARPROC fn=GetProcAddress(handle,"xmltooling_extension_init");
391         if (!fn)
392             throw runtime_error(string("unable to locate xmltooling_extension_init entry point: ") + fixed);
393         if (reinterpret_cast<int(*)(void*)>(fn)(context)!=0)
394             throw runtime_error(string("detected error in xmltooling_extension_init: ") + fixed);
395         if (fixed!=path)
396             free(fixed);
397         SetErrorMode(em);
398     }
399     catch(runtime_error& e) {
400         log.error(e.what());
401         if (handle)
402             FreeLibrary(handle);
403         SetErrorMode(em);
404         if (fixed!=path)
405             free(fixed);
406         return false;
407     }
408
409 #elif defined(HAVE_DLFCN_H)
410     void* handle=dlopen(path,RTLD_LAZY);
411     if (!handle)
412         throw runtime_error(string("unable to load extension library '") + path + "': " + dlerror());
413     int (*fn)(void*)=(int (*)(void*))(dlsym(handle,"xmltooling_extension_init"));
414     if (!fn) {
415         dlclose(handle);
416         throw runtime_error(
417             string("unable to locate xmltooling_extension_init entry point in '") + path + "': " +
418                 (dlerror() ? dlerror() : "unknown error")
419             );
420     }
421     try {
422         if (fn(context)!=0)
423             throw runtime_error(string("detected error in xmltooling_extension_init in ") + path);
424     }
425     catch(runtime_error& e) {
426         log.error(e.what());
427         if (handle)
428             dlclose(handle);
429         return false;
430     }
431 #else
432 # error "Don't know about dynamic loading on this platform!"
433 #endif
434     m_libhandles.push_back(handle);
435     log.info("loaded extension: %s", path);
436     return true;
437 }
438
439 #ifndef XMLTOOLING_NO_XMLSEC
440 void xmltooling::log_openssl()
441 {
442     const char* file;
443     const char* data;
444     int flags,line;
445
446     unsigned long code=ERR_get_error_line_data(&file,&line,&data,&flags);
447     while (code) {
448         Category& log=Category::getInstance("OpenSSL");
449         log.errorStream() << "error code: " << code << " in " << file << ", line " << line << CategoryStream::ENDLINE;
450         if (data && (flags & ERR_TXT_STRING))
451             log.errorStream() << "error data: " << data << CategoryStream::ENDLINE;
452         code=ERR_get_error_line_data(&file,&line,&data,&flags);
453     }
454 }
455
456 XSECCryptoX509CRL* XMLToolingInternalConfig::X509CRL() const
457 {
458     return new OpenSSLCryptoX509CRL();
459 }
460 #endif