00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00035 #include "OW_config.h"
00036 #include "OW_HTTPXMLCIMListener.hpp"
00037 #include "OW_CIMListenerCallback.hpp"
00038 #include "OW_HTTPServer.hpp"
00039 #include "OW_XMLListener.hpp"
00040 #include "OW_HTTPException.hpp"
00041 #include "OW_Format.hpp"
00042 #include "OW_MutexLock.hpp"
00043 #include "OW_ListenerAuthenticator.hpp"
00044 #include "OW_CIMInstance.hpp"
00045 #include "OW_CIMValue.hpp"
00046 #include "OW_CIMClass.hpp"
00047 #include "OW_CIMProperty.hpp"
00048 #include "OW_RandomNumber.hpp"
00049 #include "OW_ServiceEnvironmentIFC.hpp"
00050 #include "OW_ConfigOpts.hpp"
00051 #include "OW_ServerSocket.hpp"
00052 #include "OW_SelectEngine.hpp"
00053 #include "OW_SelectableIFC.hpp"
00054 #include "OW_IOException.hpp"
00055 #include "OW_Thread.hpp"
00056 #include "OW_Assertion.hpp"
00057 #include "OW_ClientCIMOMHandle.hpp"
00058 #include "OW_CIMProtocolIFC.hpp"
00059 #include "OW_NullLogger.hpp"
00060 #include "OW_FileSystem.hpp"
00061 #include "OW_ConfigFile.hpp"
00062
00063 #include <algorithm>
00064
00065 namespace OW_NAMESPACE
00066 {
00067
00068 using namespace WBEMFlags;
00069
00070
00071
00072
00073 namespace
00074 {
00075
00076 const String COMPONENT_NAME("ow.listener.cimxml");
00077
00078 #ifdef OW_WIN32
00079 class EventSelectable : public SelectableIFC
00080 {
00081 public:
00082 EventSelectable()
00083 : SelectableIFC()
00084 , m_event(NULL)
00085 {
00086 if(!(m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL)))
00087 {
00088 OW_THROW(IOException, "Unable to create WIN32 event handle");
00089 }
00090 }
00091 ~EventSelectable()
00092 {
00093 if(m_event)
00094 {
00095 ::CloseHandle(m_event);
00096 }
00097 }
00098 Select_t getSelectObj() const
00099 {
00100 Select_t st;
00101 st.event = m_event;
00102 return st;
00103 }
00104
00105 void setEvent()
00106 {
00107 if(m_event)
00108 {
00109 if(!::SetEvent(m_event))
00110 {
00111 OW_THROW(IOException, "Signaling termination event failed");
00112 }
00113 }
00114 }
00115
00116 void resetEvent()
00117 {
00118 if(m_event)
00119 {
00120 ::ResetEvent(m_event);
00121 }
00122 }
00123
00124 private:
00125 HANDLE m_event;
00126 };
00127 typedef IntrusiveReference<EventSelectable> EventSelectableRef;
00128
00129 #endif
00130
00131 typedef std::pair<SelectableIFCRef, SelectableCallbackIFCRef> SelectablePair_t;
00132 class HTTPXMLCIMListenerServiceEnvironment : public ServiceEnvironmentIFC
00133 {
00134 public:
00135 HTTPXMLCIMListenerServiceEnvironment(
00136 IntrusiveReference<ListenerAuthenticator> authenticator,
00137 RequestHandlerIFCRef listener,
00138 const LoggerRef& logger,
00139 Reference<Array<SelectablePair_t> > selectables,
00140 const String& certFileName,
00141 const String& keyFileName = String())
00142 : m_pLAuthenticator(authenticator)
00143 , m_XMLListener(listener)
00144 , m_logger(logger ? logger : LoggerRef(new NullLogger))
00145 , m_selectables(selectables)
00146 {
00147 if(certFileName.empty())
00148 {
00149 setConfigItem(ConfigOpts::HTTP_SERVER_HTTP_PORT_opt, String(0), E_OVERWRITE_PREVIOUS);
00150 setConfigItem(ConfigOpts::HTTP_SERVER_HTTPS_PORT_opt, String(-1), E_OVERWRITE_PREVIOUS);
00151 }
00152 else
00153 {
00154 setConfigItem(ConfigOpts::HTTP_SERVER_HTTP_PORT_opt, String(-1), E_OVERWRITE_PREVIOUS);
00155 setConfigItem(ConfigOpts::HTTP_SERVER_HTTPS_PORT_opt, String(0), E_OVERWRITE_PREVIOUS);
00156 setConfigItem(ConfigOpts::HTTP_SERVER_SSL_CERT_opt, certFileName, E_OVERWRITE_PREVIOUS);
00157 setConfigItem(ConfigOpts::HTTP_SERVER_SSL_KEY_opt,
00158 keyFileName.empty()?certFileName:keyFileName, E_OVERWRITE_PREVIOUS);
00159 }
00160
00161 setConfigItem(ConfigOpts::HTTP_SERVER_MAX_CONNECTIONS_opt, String(10), E_OVERWRITE_PREVIOUS);
00162 setConfigItem(ConfigOpts::HTTP_SERVER_SINGLE_THREAD_opt, "false", E_OVERWRITE_PREVIOUS);
00163 setConfigItem(ConfigOpts::HTTP_SERVER_ENABLE_DEFLATE_opt, "true", E_OVERWRITE_PREVIOUS);
00164 setConfigItem(ConfigOpts::HTTP_SERVER_USE_DIGEST_opt, "false", E_OVERWRITE_PREVIOUS);
00165 setConfigItem(ConfigOpts::HTTP_SERVER_USE_UDS_opt, "false", E_OVERWRITE_PREVIOUS);
00166 }
00167 virtual ~HTTPXMLCIMListenerServiceEnvironment() {}
00168 virtual bool authenticate(String &userName,
00169 const String &info, String &details, OperationContext& context) const
00170 {
00171 return m_pLAuthenticator->authenticate(userName, info, details, context);
00172 }
00173 virtual void addSelectable(const SelectableIFCRef& obj,
00174 const SelectableCallbackIFCRef& cb)
00175 {
00176 m_selectables->push_back(std::make_pair(obj, cb));
00177 }
00178
00179 struct selectableFinder
00180 {
00181 selectableFinder(const SelectableIFCRef& obj) : m_obj(obj) {}
00182 template <typename T>
00183 bool operator()(const T& x)
00184 {
00185 return x.first == m_obj;
00186 }
00187 const SelectableIFCRef& m_obj;
00188 };
00189
00190 virtual void removeSelectable(const SelectableIFCRef& obj)
00191 {
00192 m_selectables->erase(std::remove_if (m_selectables->begin(), m_selectables->end(),
00193 selectableFinder(obj)), m_selectables->end());
00194 }
00195 virtual String getConfigItem(const String &name, const String& defRetVal) const
00196 {
00197 return ConfigFile::getConfigItem(m_configItems, name, defRetVal);
00198 }
00199 virtual StringArray getMultiConfigItem(const String &itemName,
00200 const StringArray& defRetVal, const char* tokenizeSeparator) const
00201 {
00202 return ConfigFile::getMultiConfigItem(m_configItems, itemName, defRetVal, tokenizeSeparator);
00203 }
00204 virtual void setConfigItem(const String& item, const String& value, EOverwritePreviousFlag overwritePrevious)
00205 {
00206 ConfigFile::setConfigItem(m_configItems, item, value,
00207 overwritePrevious == E_OVERWRITE_PREVIOUS ? ConfigFile::E_OVERWRITE_PREVIOUS : ConfigFile::E_PRESERVE_PREVIOUS);
00208 }
00209
00210 virtual RequestHandlerIFCRef getRequestHandler(const String&) const
00211 {
00212 RequestHandlerIFCRef ref(m_XMLListener.getLibRef(),
00213 m_XMLListener->clone());
00214 ref->setEnvironment(ServiceEnvironmentIFCRef(const_cast<HTTPXMLCIMListenerServiceEnvironment*>(this)));
00215 return ref;
00216 }
00217 virtual LoggerRef getLogger() const
00218 {
00219 return getLogger(COMPONENT_NAME);
00220 }
00221 virtual LoggerRef getLogger(const String& componentName) const
00222 {
00223 LoggerRef rv(m_logger->clone());
00224 rv->setDefaultComponent(componentName);
00225 return rv;
00226 }
00227 private:
00228 ConfigFile::ConfigMap m_configItems;
00229 IntrusiveReference<ListenerAuthenticator> m_pLAuthenticator;
00230 RequestHandlerIFCRef m_XMLListener;
00231 LoggerRef m_logger;
00232 Reference<Array<SelectablePair_t> > m_selectables;
00233 };
00234 class SelectEngineThread : public Thread
00235 {
00236 public:
00237 SelectEngineThread(const Reference<Array<SelectablePair_t> >& selectables)
00238 : Thread()
00239 , m_selectables(selectables)
00240 #ifdef OW_WIN32
00241 , m_stopObject(new EventSelectable)
00242 {
00243 }
00244 #else
00245 , m_stopObject(UnnamedPipe::createUnnamedPipe())
00246 {
00247 m_stopObject->setBlocking(UnnamedPipe::E_NONBLOCKING);
00248 }
00249 #endif
00250
00254 virtual Int32 run()
00255 {
00256 SelectEngine engine;
00257 SelectableCallbackIFCRef cb(new SelectEngineStopper(engine));
00258 m_selectables->push_back(std::make_pair(m_stopObject, cb));
00259 for (size_t i = 0; i < m_selectables->size(); ++i)
00260 {
00261 engine.addSelectableObject((*m_selectables)[i].first,
00262 (*m_selectables)[i].second);
00263 }
00264 engine.go();
00265 return 0;
00266 }
00267 virtual void doCooperativeCancel()
00268 {
00269 #ifdef OW_WIN32
00270
00271
00272 m_stopObject->setEvent();
00273 #else
00274
00275
00276 if (m_stopObject->writeInt(0) == -1)
00277 {
00278 OW_THROW_ERRNO_MSG(IOException, "Writing to the termination pipe failed");
00279 }
00280 #endif
00281 }
00282
00283 private:
00284 Reference<Array<SelectablePair_t> > m_selectables;
00285 #ifdef OW_WIN32
00286 EventSelectableRef m_stopObject;
00287 #else
00288 UnnamedPipeRef m_stopObject;
00289 #endif
00290 };
00291
00292
00293
00294 }
00295
00296 class HTTPXMLCIMListenerCallback : public CIMListenerCallback
00297 {
00298 private:
00299 IntrusiveReference<ListenerAuthenticator> m_pLAuthenticator;
00300 Bool m_useHTTPS;
00301 public:
00306 HTTPXMLCIMListenerCallback(IntrusiveReference<ListenerAuthenticator> authenticator,
00307 Bool useHTTPS = false,
00308 const LoggerRef& logger = LoggerRef(0))
00309 : m_pLAuthenticator(authenticator)
00310 , m_useHTTPS(useHTTPS)
00311 {
00312 }
00313 ~HTTPXMLCIMListenerCallback()
00314 {
00315 try
00316 {
00317 MutexLock lock(m_mutex);
00318 for (callbackMap_t::iterator i = m_callbacks.begin();
00319 i != m_callbacks.end(); ++i)
00320 {
00321 registrationInfo reg = i->second;
00322
00323 try
00324 {
00325 deleteRegistrationObjects(reg);
00326 }
00327 catch (Exception&)
00328 {
00329
00330
00331 }
00332 catch (...)
00333 {
00334
00335 }
00336 }
00337 m_pLAuthenticator = 0;
00338 }
00339 catch (...)
00340 {
00341
00342 }
00343 }
00363 String registerForIndication(const String& url,
00364 const String& ns, const String& filter,
00365 const String& querylanguage,
00366 const String& sourceNamespace,
00367 const CIMListenerCallbackRef& cb,
00368 const ClientAuthCBIFCRef& authCb,
00369 UInt16 httpPort,
00370 UInt16 httpsPort)
00371 {
00372 registrationInfo reg;
00373
00374 URL curl(url);
00375 reg.cimomUrl = curl;
00376 ClientCIMOMHandleRef hdl = ClientCIMOMHandle::createFromURL(url, authCb);
00377 String ipAddress = hdl->getWBEMProtocolHandler()->getLocalAddress().getAddress();
00378 CIMClass delivery(CIMNULL);
00379 String urlPrefix;
00380
00381 UInt16 listenerPort = httpsPort;
00382 if(m_useHTTPS)
00383 {
00384 urlPrefix = "https://";
00385 try
00386 {
00387 delivery = hdl->getClass(ns, "CIM_IndicationHandlerCIMXML");
00388 }
00389 catch (CIMException& e)
00390 {
00391 if (e.getErrNo() == CIMException::NOT_FOUND)
00392 {
00393
00394 delivery = hdl->getClass(ns, "CIM_IndicationHandlerXMLHTTPS");
00395 }
00396 else
00397 throw;
00398 }
00399 }
00400 else
00401 {
00402 try
00403 {
00404 delivery = hdl->getClass(ns, "CIM_IndicationHandlerCIMXML");
00405 }
00406 catch (CIMException& e)
00407 {
00408 if (e.getErrNo() == CIMException::NOT_FOUND)
00409 {
00410
00411 delivery = hdl->getClass(ns, "CIM_IndicationHandlerXMLHTTP");
00412 }
00413 else
00414 throw;
00415 }
00416 urlPrefix = "http://";
00417 listenerPort = httpPort;
00418 }
00419 CIMInstance ci = delivery.newInstance();
00420 MutexLock lock(m_mutex);
00421 String httpPath;
00422 RandomNumber rn(0, 0x7FFFFFFF);
00423 do
00424 {
00425 String randomHashValue(rn.getNextNumber());
00426 httpPath = "/cimListener" + randomHashValue;
00427 } while (m_callbacks.find(httpPath) != m_callbacks.end());
00428 reg.httpCredentials = m_pLAuthenticator->getNewCredentials();
00429 ci.setProperty("Destination", CIMValue(urlPrefix + reg.httpCredentials + "@" +
00430 ipAddress + ":" + String(UInt32(listenerPort)) + httpPath));
00431 ci.setProperty("SystemCreationClassName", CIMValue("CIM_System"));
00432 ci.setProperty("SystemName", CIMValue(ipAddress));
00433 ci.setProperty("CreationClassName", CIMValue(delivery.getName()));
00434 ci.setProperty("Name", CIMValue(httpPath));
00435 ci.setProperty("Owner", CIMValue("HTTPXMLCIMListener on " + ipAddress));
00436 try
00437 {
00438 reg.handler = hdl->createInstance(ns, ci);
00439 }
00440 catch (CIMException& e)
00441 {
00442
00443 if (e.getErrNo() != CIMException::ALREADY_EXISTS)
00444 {
00445 throw;
00446 }
00447 else
00448 {
00449 reg.handler = CIMObjectPath(ns, ci);
00450 }
00451 }
00452
00453 CIMClass cimFilter = hdl->getClass(ns, "CIM_IndicationFilter", E_LOCAL_ONLY);
00454 ci = cimFilter.newInstance();
00455
00456 ci.setProperty("Query", CIMValue(filter));
00457
00458 ci.setProperty("QueryLanguage", CIMValue(querylanguage));
00459 ci.setProperty("SystemCreationClassName", CIMValue("CIM_System"));
00460 ci.setProperty("SystemName", CIMValue(ipAddress));
00461 ci.setProperty("CreationClassName", CIMValue(cimFilter.getName()));
00462 ci.setProperty("Name", CIMValue(httpPath));
00463 if (!sourceNamespace.empty())
00464 {
00465 ci.setProperty("SourceNamespace", CIMValue(sourceNamespace));
00466 }
00467
00468 reg.filter = hdl->createInstance(ns, ci);
00469
00470
00471
00472 CIMClass cimClientFilterDelivery = hdl->getClass(ns,
00473 "CIM_IndicationSubscription", E_LOCAL_ONLY);
00474 ci = cimClientFilterDelivery.newInstance();
00475
00476 ci.setProperty("filter", CIMValue(reg.filter));
00477 ci.setProperty("handler", CIMValue(reg.handler));
00478
00479
00480 reg.subscription = hdl->createInstance(ns, ci);
00481
00482 reg.callback = cb;
00483 reg.ns = ns;
00484 reg.authCb = authCb;
00485 m_callbacks[httpPath] = reg;
00486 return httpPath;
00487 }
00488
00495 void deregisterForIndication( const String& handle )
00496 {
00497 MutexLock lock(m_mutex);
00498 callbackMap_t::iterator i = m_callbacks.find(handle);
00499 if (i != m_callbacks.end())
00500 {
00501 registrationInfo reg = i->second;
00502 m_callbacks.erase(i);
00503 lock.release();
00504 m_pLAuthenticator->removeCredentials(reg.httpCredentials);
00505 deleteRegistrationObjects(reg);
00506 }
00507 }
00508
00509 protected:
00510 virtual void doIndicationOccurred( CIMInstance& ci,
00511 const String& listenerPath )
00512 {
00513 CIMListenerCallbackRef cb;
00514 {
00515 MutexLock lock(m_mutex);
00516 callbackMap_t::iterator i = m_callbacks.find(listenerPath);
00517 if (i == m_callbacks.end())
00518 {
00519 OW_THROWCIMMSG(CIMException::ACCESS_DENIED,
00520 Format("No listener for path: %1", listenerPath).c_str());
00521 }
00522 cb = i->second.callback;
00523 }
00524 cb->indicationOccurred( ci, listenerPath );
00525 }
00526 private:
00527
00528 #ifdef OW_WIN32
00529 #pragma warning (push)
00530 #pragma warning (disable: 4251)
00531 #endif
00532
00533 struct registrationInfo
00534 {
00535 registrationInfo()
00536 : handler(CIMNULL)
00537 , filter(CIMNULL)
00538 , subscription(CIMNULL)
00539 {}
00540 URL cimomUrl;
00541 String ns;
00542 CIMObjectPath handler;
00543 CIMObjectPath filter;
00544 CIMObjectPath subscription;
00545 CIMListenerCallbackRef callback;
00546 String httpCredentials;
00547 ClientAuthCBIFCRef authCb;
00548 };
00549 typedef Map< String, registrationInfo > callbackMap_t;
00550 callbackMap_t m_callbacks;
00551 void deleteRegistrationObjects( const registrationInfo& reg )
00552 {
00553 ClientCIMOMHandleRef hdl = ClientCIMOMHandle::createFromURL(reg.cimomUrl.toString(), reg.authCb);
00554 hdl->deleteInstance(reg.ns, reg.subscription);
00555 hdl->deleteInstance(reg.ns, reg.filter);
00556 hdl->deleteInstance(reg.ns, reg.handler);
00557 }
00558 Mutex m_mutex;
00559 };
00560
00561
00562
00564 HTTPXMLCIMListener::HTTPXMLCIMListener(const LoggerRef& logger,
00565 const String& certFileName, const String& keyFileName)
00566 : m_pLAuthenticator(new ListenerAuthenticator)
00567 , m_httpServer(new HTTPServer)
00568 , m_httpListenPort(0)
00569 , m_httpsListenPort(0)
00570 , m_certFileName(certFileName)
00571 , m_keyFileName(keyFileName)
00572 , m_callback(new HTTPXMLCIMListenerCallback(m_pLAuthenticator, !certFileName.empty()))
00573 , m_XMLListener(SharedLibraryRef(0), new XMLListener(m_callback))
00574 {
00575 if(!certFileName.empty())
00576 {
00577 if(!FileSystem::canRead(certFileName))
00578 {
00579 OW_THROW_ERRNO_MSG(IOException,
00580 Format("Unable to open certificate file %1",
00581 certFileName).c_str());
00582 }
00583 }
00584 if(!keyFileName.empty())
00585 {
00586 if(!FileSystem::canRead(keyFileName))
00587 {
00588 OW_THROW(IOException,
00589 Format("Unable to open key file %1",
00590 keyFileName).c_str());
00591 }
00592 }
00593
00594 Reference<Array<SelectablePair_t> >
00595 selectables(new Array<SelectablePair_t>);
00596 ServiceEnvironmentIFCRef env(new HTTPXMLCIMListenerServiceEnvironment(
00597 m_pLAuthenticator, m_XMLListener, logger, selectables, certFileName, keyFileName));
00598 m_httpServer->init(env);
00599 m_httpServer->start();
00600
00601
00602
00603 m_httpListenPort = m_httpServer->getLocalHTTPAddress().getPort();
00604 m_httpsListenPort = m_httpServer->getLocalHTTPSAddress().getPort();
00605
00606 m_httpThread = new SelectEngineThread(selectables);
00607 m_httpThread->start();
00608 }
00610 HTTPXMLCIMListener::~HTTPXMLCIMListener()
00611 {
00612 try
00613 {
00614 shutdownHttpServer();
00615 m_pLAuthenticator = 0;
00616 }
00617 catch (...)
00618 {
00619
00620 }
00621 }
00622 void
00623 HTTPXMLCIMListener::shutdownHttpServer()
00624 {
00625 if (m_httpThread)
00626 {
00627 m_httpThread->cooperativeCancel();
00628
00629 m_httpThread->join();
00630 m_httpThread = 0;
00631 }
00632 if (m_httpServer)
00633 {
00634
00635 m_httpServer->shutdown();
00636 m_httpServer = 0;
00637 }
00638 }
00640 String
00641 HTTPXMLCIMListener::registerForIndication(
00642 const String& url,
00643 const String& ns,
00644 const String& filter,
00645 const String& querylanguage,
00646 const String& sourceNamespace,
00647 const CIMListenerCallbackRef& cb,
00648 const ClientAuthCBIFCRef& authCb)
00649 {
00650 return m_callback->registerForIndication(url,ns,filter,querylanguage,
00651 sourceNamespace,cb,authCb,
00652 m_httpListenPort,m_httpsListenPort);
00653 }
00655 void
00656 HTTPXMLCIMListener::deregisterForIndication( const String& handle )
00657 {
00658 m_callback->deregisterForIndication(handle);
00659 }
00660
00661 }
00662