bool SourceInitiatedSubscriptionListener::Handler::handleSubscriptionManager()

in extensions/openwsman/processors/SourceInitiatedSubscriptionListener.cpp [318:525]


bool SourceInitiatedSubscriptionListener::Handler::handleSubscriptionManager(struct mg_connection* conn, const std::string& endpoint, WsXmlDocH request) {
  const auto request_guard = gsl::finally([&]() {
      ws_xml_destroy_doc(request);
  });

  auto action = getSoapAction(request);
  auto machine_id = getMachineId(request);
  const struct mg_request_info* req_info = mg_get_request_info(conn);
  std::string remote_ip = req_info->remote_addr;
  if (action != ENUM_ACTION_ENUMERATE) {
    processor_.logger_->log_error("%s called by %s (%s) with unknown Action \"%s\"", endpoint.c_str(), machine_id.c_str(), remote_ip.c_str(), action.c_str());
    return false;  // TODO(bakaid): generate fault if possible
  }

  // Create reponse envelope from request
  WsXmlDocH response = wsman_create_response_envelope(request, nullptr);
  const auto response_guard = gsl::finally([&]() {
    ws_xml_destroy_doc(response);
  });

  // Header
  WsXmlNodeH response_header = ws_xml_get_soap_header(response);
  // Header/MessageID
  utils::Identifier msg_id = utils::IdGenerator::getIdGenerator()->generate();
  ws_xml_add_child_format(response_header, XML_NS_ADDRESSING, WSA_MESSAGE_ID, "uuid:%s", msg_id.to_string().c_str());

  // Body
  WsXmlNodeH response_body = ws_xml_get_soap_body(response);
  // Body/EnumerationResponse
  WsXmlNodeH enumeration_response = ws_xml_add_child(response_body, XML_NS_ENUMERATION, WSENUM_ENUMERATE_RESP, nullptr);
  // Body/EnumerationResponse/EnumerationContext
  ws_xml_add_child(enumeration_response, XML_NS_ENUMERATION, WSENUM_ENUMERATION_CONTEXT, nullptr);
  // Body/EnumerationResponse/Items
  WsXmlNodeH enumeration_items = ws_xml_add_child(enumeration_response, XML_NS_WS_MAN, WSENUM_ITEMS, nullptr);
  // Body/EnumerationResponse/EndOfSequence
  ws_xml_add_child(enumeration_response, XML_NS_WS_MAN, WSENUM_END_OF_SEQUENCE, nullptr);

  // Body/EnumerationResponse/Items/Subscription
  WsXmlNodeH subscription = ws_xml_add_child(enumeration_items, nullptr, "Subscription", nullptr);
  ws_xml_set_ns(subscription, XML_NS_CUSTOM_SUBSCRIPTION, "m");

  // Body/EnumerationResponse/Items/Subscription/Version
  std::lock_guard<std::mutex> lock(processor_.mutex_);
  auto it = processor_.subscribers_.find(machine_id);

  std::string subscription_version;
  if (it != processor_.subscribers_.end() && it->second.subscription_ != nullptr) {
    subscription_version = it->second.subscription_version_;
  } else {
    utils::Identifier id = utils::IdGenerator::getIdGenerator()->generate();
    subscription_version = id.to_string();
  }
  ws_xml_add_child_format(subscription, XML_NS_CUSTOM_SUBSCRIPTION, "Version", "uuid:%s", subscription_version.c_str());

  // Body/EnumerationResponse/Items/Subscription/Envelope
  std::string subscription_identifier;
  std::string subscription_endpoint;
  if (it != processor_.subscribers_.end() && it->second.subscription_ != nullptr) {
    WsXmlNodeH subscription_node = ws_xml_get_doc_root(it->second.subscription_);
    ws_xml_copy_node(subscription_node, subscription);
  } else {
    WsXmlDocH subscription_doc = ws_xml_create_envelope();

    // Header
    WsXmlNodeH header = ws_xml_get_soap_header(subscription_doc);
    WsXmlNodeH node;

    // Header/Action
    node = ws_xml_add_child(header, XML_NS_ADDRESSING, WSA_ACTION, EVT_ACTION_SUBSCRIBE);
    ws_xml_add_node_attr(node, XML_NS_SOAP_1_2, SOAP_MUST_UNDERSTAND, "true");

    // Header/MessageID
    utils::Identifier msg_id = utils::IdGenerator::getIdGenerator()->generate();
    ws_xml_add_child_format(header, XML_NS_ADDRESSING, WSA_MESSAGE_ID, "uuid:%s", msg_id.to_string().c_str());

    // Header/To
    node = ws_xml_add_child(header, XML_NS_ADDRESSING, WSA_TO, WSA_TO_ANONYMOUS);
    ws_xml_add_node_attr(node, XML_NS_SOAP_1_2, SOAP_MUST_UNDERSTAND, "true");

    // Header/ResourceURI
    node = ws_xml_add_child(header, XML_NS_WS_MAN, WSM_RESOURCE_URI, "http://schemas.microsoft.com/wbem/wsman/1/windows/EventLog");
    ws_xml_add_node_attr(node, XML_NS_SOAP_1_2, SOAP_MUST_UNDERSTAND, "true");

    // Header/ReplyTo
    node = ws_xml_add_child(header, XML_NS_ADDRESSING, WSA_REPLY_TO, nullptr);
    node = ws_xml_add_child(node, XML_NS_ADDRESSING, WSA_ADDRESS, WSA_TO_ANONYMOUS);
    ws_xml_add_node_attr(node, XML_NS_SOAP_1_2, SOAP_MUST_UNDERSTAND, "true");

    // Header/OptionSet
    WsXmlNodeH option_set = ws_xml_add_child(header, XML_NS_WS_MAN, WSM_OPTION_SET, nullptr);
    ws_xml_ns_add(option_set, XML_NS_SCHEMA_INSTANCE, XML_NS_SCHEMA_INSTANCE_PREFIX);

    // Header/OptionSet/Option (CDATA)
    node = ws_xml_add_child(option_set, XML_NS_WS_MAN, WSM_OPTION, nullptr);
    ws_xml_add_node_attr(node, nullptr, WSM_NAME, "CDATA");
    ws_xml_add_node_attr(node, XML_NS_SCHEMA_INSTANCE, XML_SCHEMA_NIL, "true");

    // Header/OptionSet/Option (IgnoreChannelError)
    node = ws_xml_add_child(option_set, XML_NS_WS_MAN, WSM_OPTION, nullptr);
    ws_xml_add_node_attr(node, nullptr, WSM_NAME, "IgnoreChannelError");
    ws_xml_add_node_attr(node, XML_NS_SCHEMA_INSTANCE, XML_SCHEMA_NIL, "true");

    // Body
    WsXmlNodeH body = ws_xml_get_soap_body(subscription_doc);
    WsXmlNodeH subscribe_node = ws_xml_add_child(body, XML_NS_EVENTING, WSEVENT_SUBSCRIBE, nullptr);

    // Body/Delivery
    {
      utils::Identifier id = utils::IdGenerator::getIdGenerator()->generate();
      subscription_identifier = id.to_string();
    }
    {
      utils::Identifier id = utils::IdGenerator::getIdGenerator()->generate();
      subscription_endpoint = processor_.subscriptions_base_path_ + "/" + id.to_string();
    }

    WsXmlNodeH delivery_node = ws_xml_add_child(subscribe_node, XML_NS_EVENTING, WSEVENT_DELIVERY, nullptr);
    ws_xml_add_node_attr(delivery_node, nullptr, WSEVENT_DELIVERY_MODE, WSEVENT_DELIVERY_MODE_EVENTS);

    // Body/Delivery/Heartbeats
    ws_xml_add_child(delivery_node, XML_NS_WS_MAN, WSM_HEARTBEATS, millisecondsToXsdDuration(processor_.heartbeat_interval_).c_str());

    // Body/Delivery/ConnectionRetry
    auto connection_retry_node = ws_xml_add_child(delivery_node, XML_NS_WS_MAN, WSM_CONNECTIONRETRY, millisecondsToXsdDuration(processor_.connection_retry_interval_).c_str());
    ws_xml_add_node_attr(connection_retry_node, nullptr, "Total", std::to_string(processor_.connection_retry_count_).c_str());

    // Body/Delivery/NotifyTo and Body/EndTo are the same, so we will use this lambda to recreate the same tree
    auto apply_endpoint_nodes = [&](WsXmlNodeH target_node) {
      // ${target_node}/NotifyTo/Address
      ws_xml_add_child_format(target_node, XML_NS_ADDRESSING, WSA_ADDRESS, "https://%s:%hu%s",
                              processor_.listen_hostname_.c_str(),
                              processor_.listen_port_,
                              subscription_endpoint.c_str());
      // ${target_node}/ReferenceProperties
      node = ws_xml_add_child(target_node, XML_NS_ADDRESSING, WSA_REFERENCE_PROPERTIES, nullptr);
      // ${target_node}/ReferenceProperties/Identifier
      ws_xml_add_child_format(node, XML_NS_EVENTING, WSEVENT_IDENTIFIER, "%s", subscription_identifier.c_str());
      // ${target_node}/Policy
      WsXmlNodeH policy = ws_xml_add_child(target_node, nullptr, "Policy", nullptr);
      ws_xml_set_ns(policy, XML_NS_CUSTOM_POLICY, "c");
      ws_xml_ns_add(policy, XML_NS_CUSTOM_AUTHENTICATION, "auth");
      // ${target_node}/Policy/ExactlyOne
      WsXmlNodeH exactly_one = ws_xml_add_child(policy, XML_NS_CUSTOM_POLICY, "ExactlyOne", nullptr);
      // ${target_node}/Policy/ExactlyOne/All
      WsXmlNodeH all = ws_xml_add_child(exactly_one, XML_NS_CUSTOM_POLICY, "All", nullptr);
      // ${target_node}/Policy/ExactlyOne/All/Authentication
      WsXmlNodeH authentication = ws_xml_add_child(all, XML_NS_CUSTOM_AUTHENTICATION, "Authentication", nullptr);
      ws_xml_add_node_attr(authentication, nullptr, "Profile", WSMAN_SECURITY_PROFILE_HTTPS_MUTUAL);
      // ${target_node}/Policy/ExactlyOne/All/Authentication/ClientCertificate
      WsXmlNodeH client_certificate = ws_xml_add_child(authentication, XML_NS_CUSTOM_AUTHENTICATION, "ClientCertificate", nullptr);
      // ${target_node}/Policy/ExactlyOne/All/Authentication/ClientCertificate/Thumbprint
      WsXmlNodeH thumbprint = ws_xml_add_child_format(client_certificate, XML_NS_CUSTOM_AUTHENTICATION, "Thumbprint", "%s", processor_.ssl_ca_cert_thumbprint_.c_str());
      ws_xml_add_node_attr(thumbprint, nullptr, "Role", "issuer");
    };

    // Body/Delivery/NotifyTo
    WsXmlNodeH notifyto_node = ws_xml_add_child(delivery_node, XML_NS_EVENTING, WSEVENT_NOTIFY_TO, nullptr);
    apply_endpoint_nodes(notifyto_node);

    // Body/EndTo
    WsXmlNodeH endto_node = ws_xml_add_child(subscribe_node, XML_NS_EVENTING, WSEVENT_ENDTO, nullptr);
    apply_endpoint_nodes(endto_node);

    // Body/MaxElements
    ws_xml_add_child(delivery_node, XML_NS_WS_MAN, WSM_MAX_ELEMENTS, std::to_string(processor_.max_elements_).c_str());
    // Body/MaxTime
    ws_xml_add_child(delivery_node, XML_NS_WS_MAN, WSENUM_MAX_TIME, millisecondsToXsdDuration(processor_.max_latency_).c_str());

    // Body/Expires
    ws_xml_add_child(subscribe_node, XML_NS_EVENTING, WSEVENT_EXPIRES, millisecondsToXsdDuration(processor_.subscription_expiration_interval_).c_str());

    // Body/Filter
    ws_xml_add_child(subscribe_node, XML_NS_WS_MAN, WSM_FILTER, processor_.xpath_xml_query_.c_str());
    // ws_xml_add_node_attr(filter_node, nullptr, "Dialect", "http://schemas.microsoft.com/win/2004/08/events/eventquery");

    // Body/Bookmark
    if (it != processor_.subscribers_.end() && it->second.bookmark_ != nullptr) {
      WsXmlNodeH bookmark_node = ws_xml_get_doc_root(it->second.bookmark_);
      ws_xml_copy_node(bookmark_node, subscribe_node);
    } else if (processor_.initial_existing_events_strategy_ == INITIAL_EXISTING_EVENTS_STRATEGY_ALL) {
      ws_xml_add_child(subscribe_node, XML_NS_WS_MAN, WSM_BOOKMARK, "http://schemas.dmtf.org/wbem/wsman/1/wsman/bookmark/earliest");
    }

    // Body/SendBookmarks
    ws_xml_add_child(subscribe_node, XML_NS_WS_MAN, WSM_SENDBOOKMARKS, nullptr);

    // Copy the whole Subscription
    WsXmlNodeH subscription_node = ws_xml_get_doc_root(subscription_doc);
    ws_xml_copy_node(subscription_node, subscription);

    // Save subscription
    if (it == processor_.subscribers_.end()) {
      it = processor_.subscribers_.emplace(machine_id, SubscriberData()).first;
    }
    it->second.setSubscription(subscription_version, subscription_doc, subscription_endpoint, subscription_identifier);
  }

  // Send response
  char* xml_buf = nullptr;
  int xml_buf_size = 0;
  ws_xml_dump_memory_enc(response, &xml_buf, &xml_buf_size, "UTF-8");

  sendResponse(conn, machine_id, req_info->remote_addr, xml_buf, xml_buf_size);

  ws_xml_free_memory(xml_buf);

  return true;
}