Boolean _parseHostElement( const String& objectName, char*& p, String& host) { // See if there is a host name (true if it begins with "//"): // Host is of the form <hostname>:<port> and begins with "//" // and ends with "/": if (p[0] != '/' || p[1] != '/') { return false; } p += 2; char* slash = strchr(p, '/'); if (!slash) { MessageLoaderParms mlParms( "Common.CIMObjectPath.MISSING_SLASH_AFTER_HOST", "$0, reason:\"missing slash after hostname\"", objectName); throw MalformedObjectNameException(mlParms); } String hostname = String(p, (Uint32)(slash - p)); if (!CIMObjectPathRep::isValidHostname(hostname)) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_HOSTNAME", "$0, reason:\"invalid hostname\"", objectName); throw MalformedObjectNameException(mlParms); } host = hostname; // Do not step past the '/'; it will be consumed by the namespace parser p = slash; return true; }
void CIMObjectPath::setHost(const String& host) { if ((host != String::EMPTY) && (host != System::getHostName()) && !CIMObjectPathRep::isValidHostname(host)) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_HOSTNAME", "$0, reason:\"invalid hostname\"", host); throw MalformedObjectNameException(mlParms); } _rep = _copyOnWriteCIMObjectPathRep(_rep); _rep->_host = host; }
Boolean _parseNamespaceElement( const String& objectName, char*& p, CIMNamespaceName& nameSpace) { // If we don't find a valid namespace name followed by a ':', we // assume we're not looking at a namespace name. char* colon = strchr(p, ':'); if (!colon) { return false; } // A ':' as part of a keybinding value should not be interpreted as // a namespace delimiter. Since keybinding pairs follow the first '.' // in the object path string, the ':' delimiter only counts if it // appears before the '.'. char* dot = strchr(p, '.'); if (dot && (dot < colon)) { return false; } //---------------------------------------------------------------------- // Validate the namespace path. Namespaces must match the following // regular expression: "[A-Za-z_]+(/[A-Za-z_]+)*" //---------------------------------------------------------------------- String namespaceName = String(p, (Uint32)(colon - p)); if (!CIMNamespaceName::legal(namespaceName)) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_NAMESPACE", "$0, reason:\"invalid namespace name\"", objectName); throw MalformedObjectNameException(mlParms); } nameSpace = namespaceName; p = colon+1; return true; }
CIMExportIndicationRequestMessage* CIMExportRequestDecoder::decodeExportIndicationRequest( Uint32 queueId, XmlParser& parser, const String& messageId, const String& requestUri) { CIMInstance instanceName; String destStr = requestUri.subString( requestUri.find("/CIMListener") + 12, PEG_NOT_FOUND); for (const char* name; XmlReader::getEParamValueTag(parser, name);) { if (System::strcasecmp(name, "NewIndication") == 0) { XmlReader::getInstanceElement(parser, instanceName); } else { MessageLoaderParms mlParms( "ExportServer.CIMExportRequestDecoder." "UNRECOGNIZED_EXPPARAMVALUE_NAME", "Unrecognized EXPPARAMVALUE Name $0", name); throw PEGASUS_CIM_EXCEPTION(CIM_ERR_NOT_SUPPORTED, mlParms); } XmlReader::expectEndTag(parser, "EXPPARAMVALUE"); } CIMExportIndicationRequestMessage* request = new CIMExportIndicationRequestMessage( messageId, destStr, instanceName, QueueIdStack(queueId, _returnQueueId)); return request; }
void CIMObjectPath::set( const String& host, const CIMNamespaceName& nameSpace, const CIMName& className, const Array<CIMKeyBinding>& keyBindings) { if ((host != String::EMPTY) && !CIMObjectPathRep::isValidHostname(host)) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_HOSTNAME", "$0, reason:\"invalid hostname\"", host); throw MalformedObjectNameException(mlParms); } _rep = _copyOnWriteCIMObjectPathRep(_rep); _rep->_host.assign(host); _rep->_nameSpace = nameSpace; _rep->_className = className; _rep->_keyBindings = keyBindings; _Sort(_rep->_keyBindings); }
void CIMObjectPath::set(const String& objectName) { // the clear automatically ensures // we have our own copy of the representation clear(); //-------------------------------------------------------------------------- // We will extract components from an object name. Here is an sample // object name: // // //atp:9999/root/cimv25:TennisPlayer.first="Patrick",last="Rafter" //-------------------------------------------------------------------------- // Convert to a C String first: CString pCString = objectName.getCString(); char* p = const_cast<char*>((const char*) pCString); Boolean gotHost; Boolean gotNamespace; gotHost = _parseHostElement(objectName, p, _rep->_host); gotNamespace = _parseNamespaceElement(objectName, p, _rep->_nameSpace); if (gotHost && !gotNamespace) { MessageLoaderParms mlParms( "Common.CIMObjectPath.MISSING_NAMESPACE", "$0, reason:\"host specified, missing namespace\"", objectName); throw MalformedObjectNameException(mlParms); } // Extract the class name: char* dot = strchr(p, '.'); if (!dot) { if (!CIMName::legal(p)) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_CLASSNAME", "$0, reason:\"class name $1 not a legal CIM name\"", objectName, p); throw MalformedObjectNameException(mlParms); } // ATTN: remove this later: a reference should only be able to hold // an instance name. _rep->_className = CIMName (p); return; } String className = String(p, (Uint32)(dot - p)); if (!CIMName::legal(className)) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_CLASSNAME", "$0, reason:\"class name $1 not a legal CIM name\"", objectName, className); throw MalformedObjectNameException(mlParms); } _rep->_className = className; // Advance past dot: p = dot + 1; _parseKeyBindingPairs(objectName, p, _rep->_keyBindings); }
/** ATTN-RK: The DMTF specification for the string form of an object path makes it impossible for a parser to distinguish between a key values of String type and Reference type. Given the ambiguity, this implementation takes a guess at the type of a quoted key value. If the value can be parsed into a CIMObjectPath with at least one key binding, the type is set to REFERENCE. Otherwise, the type is set to STRING. Note: This algorithm appears to be in line with what the Sun WBEM Services implementation does. To be totally correct, it would be necessary to retrieve the class definition and look up the types of the key properties to determine how to interpret the key values. This is clearly too inefficient for internal transformations between CIMObjectPaths and String values. */ void _parseKeyBindingPairs( const String& objectName, char*& p, Array<CIMKeyBinding>& keyBindings) { // Get the key-value pairs: while (*p) { // Get key part: char* equalsign = strchr(p, '='); if (!equalsign) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_KEYVALUEPAIR", "$0, reason:\"invalid key-value pair, missing equal sign\"", objectName); throw MalformedObjectNameException(mlParms); } *equalsign = 0; if (!CIMName::legal(p)) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_KEYNAME", "$0, reason:\"invalid key-value pair, invalid key name:$1\"", objectName, p); throw MalformedObjectNameException(mlParms); } CIMName keyName (p); // Get the value part: String valueString; p = equalsign + 1; CIMKeyBinding::Type type; if (*p == '"') { // Could be CIMKeyBinding::STRING or CIMKeyBinding::REFERENCE p++; Buffer keyValueUTF8(128); while (*p && *p != '"') { if (*p == '\\') { p++; if ((*p != '\\') && (*p != '"')) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_KEYVALUE", "$0, reason:\"invalid key-value pair, " "malformed value\"", objectName); throw MalformedObjectNameException(mlParms); } } keyValueUTF8.append(*p++); } if (*p++ != '"') { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_KEYVALUEPAIR_MISSINGQUOTE", "$0, reason:\"invalid key-value pair, " "missing quote in key value\"", objectName); throw MalformedObjectNameException(mlParms); } // Convert the UTF-8 value to a UTF-16 String valueString.assign( (const char*)keyValueUTF8.getData(), keyValueUTF8.size()); /* Guess at the type of this quoted key value. If the value can be parsed into a CIMObjectPath with at least one key binding, the type is assumed to be a REFERENCE. Otherwise, the type is set to STRING. (See method header for details.) */ type = CIMKeyBinding::STRING; /* Performance shortcut will check for equal sign instead of doing the full CIMObjectPath creation and exception handling */ if (strchr(keyValueUTF8.getData(), '=')) { // found an equal sign, high probability for a reference try { CIMObjectPath testForPath(valueString); if (testForPath.getKeyBindings().size() > 0) { // We've found a reference value! type = CIMKeyBinding::REFERENCE; } } catch (const Exception &) { // Not a reference value; leave type as STRING } } } else if (toupper(*p) == 'T' || toupper(*p) == 'F') { type = CIMKeyBinding::BOOLEAN; char* r = p; Uint32 n = 0; while (*r && *r != ',') { *r = toupper(*r); r++; n++; } if (!(((strncmp(p, "TRUE", n) == 0) && n == 4) || ((strncmp(p, "FALSE", n) == 0) && n == 5))) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_BOOLVALUE", "$0, reason:\"invalid key-value pair, " "value should be TRUE or FALSE\"", objectName); throw MalformedObjectNameException(mlParms); } valueString.assign(p, n); p = p + n; } else { type = CIMKeyBinding::NUMERIC; char* r = p; Uint32 n = 0; while (*r && *r != ',') { r++; n++; } Boolean isComma = false; if (*r) { *r = '\0'; isComma = true; } if (*p == '-') { Sint64 x; if (!StringConversion::stringToSignedInteger(p, x)) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_NEGATIVNUMBER_VALUE", "$0, reason:\"invalid key-value pair, " "invalid negative number value $1\"", objectName, p); throw MalformedObjectNameException(mlParms); } } else { Uint64 x; if (!StringConversion::stringToUnsignedInteger(p, x)) { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_NEGATIVNUMBER_VALUE", "$0, reason:\"invalid key-value pair, " "invalid number value $1\"", objectName, p); throw MalformedObjectNameException(mlParms); } } valueString.assign(p, n); if (isComma) { *r = ','; } p = p + n; } keyBindings.append(CIMKeyBinding(keyName.getString (), valueString, type)); if (*p) { if (*p++ != ',') { MessageLoaderParms mlParms( "Common.CIMObjectPath.INVALID_KEYVALUEPAIR_MISSCOMMA", "$0, reason:\"invalid key-value pair, " "next key-value pair has to start with comma\"", objectName); throw MalformedObjectNameException(mlParms); } } } _Sort(keyBindings); }
Message* CIMClientRep::_doRequest( AutoPtr<CIMRequestMessage>& request, const Uint32 expectedResponseMessageType ) { if (!_connected) { request.reset(); throw NotConnectedException(); } String messageId = XmlWriter::getNextMessageId(); const_cast<String &>(request->messageId) = messageId; _authenticator.setRequestMessage(0); // ATTN-RK-P2-20020416: We should probably clear out the queue first. PEGASUS_ASSERT(getCount() == 0); // Shouldn't be any messages in our queue // // Set HTTP method in request to POST // //Bug 478/418 - Change this to do post call, not mpost request->setHttpMethod (HTTP_METHOD__POST); // l10n // Set the Accept-Languages and Content-Languages into // the request message request->operationContext.set(AcceptLanguageListContainer(requestAcceptLanguages)); request->operationContext.set(ContentLanguageListContainer(requestContentLanguages)); //gathering statistical information about client operation ClientPerfDataStore* perfDataStore = ClientPerfDataStore::Instance(); perfDataStore->reset(); perfDataStore->setOperationType(request->getType()); perfDataStore->setMessageID(request->messageId); // Sending a new request, so clear out the response Content-Languages responseContentLanguages = ContentLanguages::EMPTY; _requestEncoder->enqueue(request.get()); request.release(); Uint64 startMilliseconds = TimeValue::getCurrentTime().toMilliseconds(); Uint64 nowMilliseconds = startMilliseconds; Uint64 stopMilliseconds = nowMilliseconds + _timeoutMilliseconds; while (nowMilliseconds < stopMilliseconds) { // // Wait until the timeout expires or an event occurs: // _monitor->run(Uint32(stopMilliseconds - nowMilliseconds)); // // Check to see if incoming queue has a message // Message* response = dequeue(); if (response) { // Shouldn't be any more messages in our queue PEGASUS_ASSERT(getCount() == 0); // // Reconnect to reset the connection // if Server response contained a Connection: Close Header // if (response->getCloseConnect() == true) { _reconnect(); response->setCloseConnect(false); } // // Future: If M-POST is used and HTTP response is 501 Not // Implemented or 510 Not Extended, retry with POST method // if (response->getType() == CLIENT_EXCEPTION_MESSAGE) { Exception* clientException = ((ClientExceptionMessage*)response)->clientException; delete response; AutoPtr<Exception> d(clientException); // Make the ContentLanguage of the exception available through // the CIMClient API (its also available in the exception). responseContentLanguages = clientException->getContentLanguages(); // // Determine and throw the specific class of client exception // CIMClientMalformedHTTPException* malformedHTTPException = dynamic_cast<CIMClientMalformedHTTPException*>( clientException); if (malformedHTTPException) { throw *malformedHTTPException; } CIMClientHTTPErrorException* httpErrorException = dynamic_cast<CIMClientHTTPErrorException*>( clientException); if (httpErrorException) { throw *httpErrorException; } CIMClientXmlException* xmlException = dynamic_cast<CIMClientXmlException*>(clientException); if (xmlException) { throw *xmlException; } CIMClientResponseException* responseException = dynamic_cast<CIMClientResponseException*>(clientException); if (responseException) { throw *responseException; } CIMException* cimException = dynamic_cast<CIMException*>(clientException); if (cimException) { throw *cimException; } throw *clientException; } else if (response->getType() == expectedResponseMessageType) { CIMResponseMessage* cimResponse = (CIMResponseMessage*)response; if (cimResponse->messageId != messageId) { // l10n // CIMClientResponseException responseException( // String("Mismatched response message ID: Got \"") + // cimResponse->messageId + "\", expected \"" + // messageId + "\"."); MessageLoaderParms mlParms( "Client.CIMClient.MISMATCHED_RESPONSE", "Mismatched response message ID: Got \"$0\", " "expected \"$1\".", cimResponse->messageId, messageId); String mlString(MessageLoader::getMessage(mlParms)); CIMClientResponseException responseException(mlString); delete response; throw responseException; } // l10n // Get the Content-Languages from the response's operationContext // and make available through the CIMClient API responseContentLanguages = ((ContentLanguageListContainer)cimResponse->operationContext.get (ContentLanguageListContainer::NAME)).getLanguages(); if (cimResponse->cimException.getCode() != CIM_ERR_SUCCESS) { CIMException cimException( cimResponse->cimException.getCode(), cimResponse->cimException.getMessage()); cimException.setContentLanguages(responseContentLanguages); delete response; throw cimException; } /* if excicution gets here everytihng is working correctly and a proper response was generated and recived */ //check that client side statistics are valid before handing them to the // client application via a call back Boolean re_check = perfDataStore->checkMessageIDandType(cimResponse->messageId, cimResponse->getType()); if (re_check && !perfDataStore->getStatError() && perfDataStore->isClassRegistered()) { //if callback method throws an exception it will be seen by the client //no try/catch block is used here intentionaly - becasue exceptions //come from the client application so client app. should handle them ClientOpPerformanceData item = perfDataStore->createPerfDataStruct(); perfDataStore->handler_prt->handleClientOpPerformanceData(item); }//end of if statmet that call the callback method return response; } else if (dynamic_cast<CIMRequestMessage*>(response) != 0) { // Respond to an authentication challenge _requestEncoder->enqueue(response); nowMilliseconds = TimeValue::getCurrentTime().toMilliseconds(); stopMilliseconds = nowMilliseconds + _timeoutMilliseconds; continue; } else { // l10n // CIMClientResponseException responseException( // "Mismatched response message type."); MessageLoaderParms mlParms( "Client.CIMOperationResponseDecoder.MISMATCHED_RESPONSE_TYPE", "Mismatched response message type."); String mlString(MessageLoader::getMessage(mlParms)); CIMClientResponseException responseException(mlString); delete response; throw responseException; } } nowMilliseconds = TimeValue::getCurrentTime().toMilliseconds(); pegasus_yield(); } // // Reconnect to reset the connection (disregard late response) // try { _reconnect(); } catch (...) { } // // Throw timed out exception: // throw ConnectionTimeoutException(); }
Message* CIMExportClient::_doRequest( CIMRequestMessage* pRequest, MessageType expectedResponseMessageType) { PEG_METHOD_ENTER (TRC_EXPORT_CLIENT, "CIMExportClient::_doRequest()"); AutoPtr<CIMRequestMessage> request(pRequest); if (!_connected && !_doReconnect) { PEG_METHOD_EXIT(); throw NotConnectedException(); } if (_doReconnect) { try { _connect(); _doReconnect = false; } catch (const Exception& e) { PEG_TRACE((TRC_EXPORT_CLIENT, Tracer::LEVEL1, "Failed to connect to indication listener: %s", (const char*)e.getMessage().getCString())); PEG_METHOD_EXIT(); throw; } catch (...) { PEG_TRACE_CSTRING(TRC_EXPORT_CLIENT, Tracer::LEVEL1, "Failed to connect to indication listener."); PEG_METHOD_EXIT(); throw; } } String messageId = XmlWriter::getNextMessageId(); const_cast<String &>(request->messageId) = messageId; _authenticator.setRequestMessage(0); // ATTN-RK-P2-20020416: We should probably clear out the queue first. PEGASUS_ASSERT(getCount() == 0); // Shouldn't be any messages in our queue // // Set HTTP method in request to POST // request->setHttpMethod(HTTP_METHOD__POST); _requestEncoder->enqueue(request.release()); Uint64 startMilliseconds = TimeValue::getCurrentTime().toMilliseconds(); Uint64 nowMilliseconds = startMilliseconds; Uint64 stopMilliseconds = nowMilliseconds + _timeoutMilliseconds; while (nowMilliseconds < stopMilliseconds) { // // Wait until the timeout expires or an event occurs: // _monitor->run(Uint32(stopMilliseconds - nowMilliseconds)); // // Check to see if incoming queue has a message // AutoPtr<Message> response(dequeue()); if (response.get() != 0) { // Shouldn't be any more messages in our queue PEGASUS_ASSERT(getCount() == 0); // // Close the connection if response contained a "Connection: Close" // header (e.g. at authentication challenge) // if (response->getCloseConnect() == true) { _disconnect(); _doReconnect = true; response->setCloseConnect(false); } // // Future: If M-POST is used and HTTP response is 501 Not // Implemented or 510 Not Extended, retry with POST method // if (response->getType() == CLIENT_EXCEPTION_MESSAGE) { Exception* clientException = ((ClientExceptionMessage*)response.get())->clientException; PEG_TRACE_CSTRING(TRC_EXPORT_CLIENT, Tracer::LEVEL2, "Client Exception Message received."); AutoPtr<Exception> d(clientException); // // Determine and throw the specific class of client exception // CIMClientMalformedHTTPException* malformedHTTPException = dynamic_cast<CIMClientMalformedHTTPException*>( clientException); if (malformedHTTPException) { PEG_METHOD_EXIT(); throw *malformedHTTPException; } CIMClientHTTPErrorException* httpErrorException = dynamic_cast<CIMClientHTTPErrorException*>( clientException); if (httpErrorException) { PEG_METHOD_EXIT(); throw *httpErrorException; } CIMClientXmlException* xmlException = dynamic_cast<CIMClientXmlException*>(clientException); if (xmlException) { PEG_METHOD_EXIT(); throw *xmlException; } CIMClientResponseException* responseException = dynamic_cast<CIMClientResponseException*>(clientException); if (responseException) { PEG_METHOD_EXIT(); throw *responseException; } PEG_METHOD_EXIT(); throw *clientException; } else if (response->getType() == expectedResponseMessageType) { PEG_TRACE_CSTRING(TRC_EXPORT_CLIENT, Tracer::LEVEL4, "Received expected indication response message."); CIMResponseMessage* cimResponse = (CIMResponseMessage*)response.get(); if (cimResponse->messageId != messageId) { MessageLoaderParms mlParms( "ExportClient.CIMExportClient.MISMATCHED_RESPONSE_ID", "Mismatched response message ID: Got \"$0\", " "expected \"$1\".", cimResponse->messageId, messageId); String mlString(MessageLoader::getMessage(mlParms)); CIMClientResponseException responseException(mlString); PEG_METHOD_EXIT(); throw responseException; } if (cimResponse->cimException.getCode() != CIM_ERR_SUCCESS) { PEG_TRACE_CSTRING(TRC_EXPORT_CLIENT, Tracer::LEVEL1, "Received indication failure message."); CIMException cimException( cimResponse->cimException.getCode(), cimResponse->cimException.getMessage()); PEG_METHOD_EXIT(); throw cimException; } PEG_METHOD_EXIT(); return response.release(); } else if (dynamic_cast<CIMRequestMessage*>(response.get()) != 0) { // // Respond to an authentication challenge. // Reconnect if the connection was closed. // if (_doReconnect) { _connect(); } _requestEncoder->enqueue(response.release()); nowMilliseconds = TimeValue::getCurrentTime().toMilliseconds(); stopMilliseconds = nowMilliseconds + _timeoutMilliseconds; continue; } else { MessageLoaderParms mlParms( "ExportClient.CIMExportClient.MISMATCHED_RESPONSE", "Mismatched response message type."); String mlString(MessageLoader::getMessage(mlParms)); CIMClientResponseException responseException(mlString); PEG_TRACE((TRC_EXPORT_CLIENT, Tracer::LEVEL1, (const char*)mlString.getCString())); PEG_METHOD_EXIT(); throw responseException; } } nowMilliseconds = TimeValue::getCurrentTime().toMilliseconds(); } // // Reconnect to reset the connection (disregard late response) // PEG_TRACE_CSTRING(TRC_EXPORT_CLIENT, Tracer::LEVEL2, "Connection to the listener timed out."); _disconnect(); _authenticator.resetChallengeStatus(); _doReconnect = true; // // Throw timed out exception: // PEG_METHOD_EXIT(); throw ConnectionTimeoutException(); }
void CIMExportRequestDecoder::handleMethodRequest( Uint32 queueId, HttpMethod httpMethod, char* content, const String& requestUri, const char* cimProtocolVersionInHeader, const char* cimExportMethodInHeader, const String& userName, const String& ipAddress, const AcceptLanguageList& httpAcceptLanguages, const ContentLanguageList& httpContentLanguages, Boolean closeConnect) { // Set the Accept-Language into the thread for this service. // This will allow all code in this thread to get // the languages for the messages returned to the client. Thread::setLanguages(httpAcceptLanguages); // // If CIM Listener is shutting down, return error response // if (_serverTerminating) { sendHttpError( queueId, HTTP_STATUS_SERVICEUNAVAILABLE, String::EMPTY, "CIM Listener is shutting down.", closeConnect); return; } // Create a parser: XmlParser parser(content); XmlEntry entry; String messageId; const char* cimExportMethodName = ""; AutoPtr<CIMExportIndicationRequestMessage> request; try { // // Process <?xml ... > // // These values are currently unused const char* xmlVersion = 0; const char* xmlEncoding = 0; XmlReader::getXmlDeclaration(parser, xmlVersion, xmlEncoding); // Expect <CIM ...> const char* cimVersion = 0; const char* dtdVersion = 0; XmlReader::getCimStartTag(parser, cimVersion, dtdVersion); if (!XmlReader::isSupportedCIMVersion(cimVersion)) { sendHttpError( queueId, HTTP_STATUS_NOTIMPLEMENTED, "unsupported-cim-version", String::EMPTY, closeConnect); return; } if (!XmlReader::isSupportedDTDVersion(dtdVersion)) { sendHttpError( queueId, HTTP_STATUS_NOTIMPLEMENTED, "unsupported-dtd-version", String::EMPTY, closeConnect); return; } // Expect <MESSAGE ...> String protocolVersion; if (!XmlReader::getMessageStartTag( parser, messageId, protocolVersion)) { MessageLoaderParms mlParms( "ExportServer.CIMExportRequestDecoder.EXPECTED_MESSAGE_ELEMENT", "expected MESSAGE element"); throw XmlValidationError(parser.getLine(), mlParms); } // Validate that the protocol version in the header matches the XML if (!String::equalNoCase(protocolVersion, cimProtocolVersionInHeader)) { sendHttpError( queueId, HTTP_STATUS_BADREQUEST, "header-mismatch", String::EMPTY, closeConnect); return; } if (!XmlReader::isSupportedProtocolVersion(protocolVersion)) { // See Specification for CIM Operations over HTTP section 4.3 sendHttpError( queueId, HTTP_STATUS_NOTIMPLEMENTED, "unsupported-protocol-version", String::EMPTY, closeConnect); return; } if (XmlReader::testStartTag(parser, entry, "MULTIEXPREQ")) { // We wouldn't have gotten here if CIMExportBatch header was // specified, so this must be indicative of a header mismatch sendHttpError( queueId, HTTP_STATUS_BADREQUEST, "header-mismatch", String::EMPTY, closeConnect); return; // Future: When MULTIEXPREQ is supported, must ensure // CIMExportMethod header is absent, and CIMExportBatch header // is present. } // Expect <SIMPLEEXPREQ ...> XmlReader::expectStartTag(parser, entry, "SIMPLEEXPREQ"); // Expect <EXPMETHODCALL ...> if (!XmlReader::getEMethodCallStartTag(parser, cimExportMethodName)) { MessageLoaderParms mlParms( "ExportServer.CIMExportRequestDecoder." "EXPECTED_EXPMETHODCALL_ELEMENT", "expected EXPMETHODCALL element"); throw XmlValidationError(parser.getLine(), mlParms); } // The Specification for CIM Operations over HTTP reads: // 3.3.9. CIMExportMethod // // This header MUST be present in any CIM Export Request // message that contains a Simple Export Request. // // It MUST NOT be present in any CIM Export Response message, // nor in any CIM Export Request message that is not a // Simple Export Request. It MUST NOT be present in any CIM // Operation Request or Response message. // // The name of the CIM export method within a Simple Export // Request is defined to be the value of the NAME attribute // of the <EXPMETHODCALL> element. // // If a CIM Listener receives a CIM Export Request for which // either: // // - The CIMExportMethod header is present but has an invalid // value, or; // - The CIMExportMethod header is not present but the Export // Request Message is a Simple Export Request, or; // - The CIMExportMethod header is present but the Export // Request Message is not a Simple Export Request, or; // - The CIMExportMethod header is present, the Export Request // Message is a Simple Export Request, but the CIMIdentifier // value (when unencoded) does not match the unique method // name within the Simple Export Request, // // then it MUST fail the request and return a status of // "400 Bad Request" (and MUST include a CIMError header in the // response with a value of header-mismatch), subject to the // considerations specified in Errors. if (System::strcasecmp( cimExportMethodName, cimExportMethodInHeader) != 0) { // ATTN-RK-P3-20020404: How to decode cimExportMethodInHeader? sendHttpError( queueId, HTTP_STATUS_BADREQUEST, "header-mismatch", String::EMPTY, closeConnect); return; } // This try block only catches CIMExceptions, because they must be // responded to with a proper EMETHODRESPONSE. Other exceptions are // caught in the outer try block. try { // Delegate to appropriate method to handle: if (System::strcasecmp( cimExportMethodName, "ExportIndication") == 0) { request.reset(decodeExportIndicationRequest( queueId, parser, messageId, requestUri)); } else { throw PEGASUS_CIM_EXCEPTION_L(CIM_ERR_NOT_SUPPORTED, MessageLoaderParms( "ExportServer.CIMExportRequestDecoder." "UNRECOGNIZED_EXPORT_METHOD", "Unrecognized export method: $0", cimExportMethodName)); } } catch (CIMException& e) { sendEMethodError( queueId, httpMethod, messageId, cimExportMethodName, e, closeConnect); return; } // Expect </EXPMETHODCALL> XmlReader::expectEndTag(parser, "EXPMETHODCALL"); // Expect </SIMPLEEXPREQ> XmlReader::expectEndTag(parser, "SIMPLEEXPREQ"); // Expect </MESSAGE> XmlReader::expectEndTag(parser, "MESSAGE"); // Expect </CIM> XmlReader::expectEndTag(parser, "CIM"); } catch (XmlValidationError& e) { PEG_TRACE((TRC_XML,Tracer::LEVEL1, "CIMExportRequestDecoder::handleMethodRequest - " "XmlValidationError exception has occurred. Message: %s", (const char*) e.getMessage().getCString())); sendHttpError( queueId, HTTP_STATUS_BADREQUEST, "request-not-valid", e.getMessage(), closeConnect); return; } catch (XmlSemanticError& e) { PEG_TRACE((TRC_XML,Tracer::LEVEL1, "CIMExportRequestDecoder::handleMethodRequest - " "XmlSemanticError exception has occurred. Message: %s", (const char*) e.getMessage().getCString())); // ATTN-RK-P2-20020404: Is this the correct response for these errors? sendHttpError( queueId, HTTP_STATUS_BADREQUEST, "request-not-valid", e.getMessage(), closeConnect); return; } catch (XmlException& e) { PEG_TRACE((TRC_XML,Tracer::LEVEL1, "CIMExportRequestDecoder::handleMethodRequest - " "XmlException has occurred. Message: %s", (const char*) e.getMessage().getCString())); sendHttpError( queueId, HTTP_STATUS_BADREQUEST, "request-not-well-formed", e.getMessage(), closeConnect); return; } catch (Exception& e) { // Don't know why I got this exception. Seems like a bad thing. // Any exceptions we're expecting should be caught separately and // dealt with appropriately. This is a last resort. sendHttpError( queueId, HTTP_STATUS_INTERNALSERVERERROR, String::EMPTY, e.getMessage(), closeConnect); return; } catch (...) { // Don't know why I got whatever this is. Seems like a bad thing. // Any exceptions we're expecting should be caught separately and // dealt with appropriately. This is a last resort. sendHttpError( queueId, HTTP_STATUS_INTERNALSERVERERROR, String::EMPTY, String::EMPTY, closeConnect); return; } // l10n TODO - might want to move A-L and C-L to Message // to make this more maintainable // Add the language headers to the request. // Note: Since the text of an export error response will be ignored // by the export client, ignore Accept-Language in the export request. // This will cause any export error response message to be sent in the // default language. request->operationContext.insert(IdentityContainer(userName)); request->operationContext.set( ContentLanguageListContainer(httpContentLanguages)); request->operationContext.set( AcceptLanguageListContainer(AcceptLanguageList())); request->ipAddress = ipAddress; request->setCloseConnect(closeConnect); _outputQueue->enqueue(request.release()); }