Example #1
0
/* ****************************************************************************
*
* tenantCheck - 
*/
static std::string tenantCheck(const std::string& tenant)
{
  char*        name    = (char*) tenant.c_str();

  if (strlen(name) > MAX_TENANT_NAME_LEN)
  {
    LM_W(("Bad Input (a tenant name can be max %d characters long. Length: %d)", MAX_TENANT_NAME_LEN, strlen(name)));
    return "bad length - a tenant name can be max " MAX_TENANT_NAME_LEN_STRING " characters long";
  }

  while (*name != 0)
  {
    if ((!isalnum(*name)) && (*name != '_'))
    {
      LM_W(("Bad Input (bad character in tenant name - only underscore and alphanumeric characters are allowed. Offending character: %c)", *name));
      return "bad character in tenant name - only underscore and alphanumeric characters are allowed";
    }

    ++name;
  }

  return "OK";
}
/* ****************************************************************************
*
* attribute - 
*/
static std::string attribute(std::string path, std::string value, ParseData* reqDataP)
{
  LM_T(LmtParse, ("Got an attribute: '%s'", value.c_str()));

  if (value == "")
  {
    reqDataP->errorString = "Empty attribute name";
    LM_W(("Empty attribute name"));
  }

  reqDataP->qcr.res.attributeList.push_back(value);

  return "OK";
}
/* ****************************************************************************
*
* UpdateContextRequest::fill - 
*/
void UpdateContextRequest::fill
(
  const UpdateContextAttributeRequest* ucarP,
  const std::string&                   entityId,
  const std::string&                   entityType,
  const std::string&                   attributeName,
  const std::string&                   metaID,
  const std::string&                   _updateActionType
)
{
  ContextElement*   ceP = new ContextElement();
  ContextAttribute* caP;

  if (ucarP->compoundValueP != NULL)
  {
    caP = new ContextAttribute(attributeName, ucarP->type, ucarP->compoundValueP);
  }
  else
  {
    caP = new ContextAttribute(attributeName, ucarP->type, ucarP->contextValue);
  }

  caP->metadataVector.fill((MetadataVector*) &ucarP->metadataVector);
  ceP->contextAttributeVector.push_back(caP);
  ceP->entityId.fill(entityId, entityType, "false");

  contextElementVector.push_back(ceP);

  //
  // If there is a metaID, then the metadata named ID must exist.
  // If it doesn't exist already, it must be created
  //
  if (metaID != "")
  {
    Metadata* mP = caP->metadataVector.lookupByName("ID");

    if (mP == NULL)
    {
      mP = new Metadata("ID", "", metaID);
      caP->metadataVector.push_back(mP);
    }
    else if (mP->value != metaID)
    {
      LM_W(("Bad Input (metaID differs in URI and payload"));
    }
  }

  updateActionType.set(_updateActionType);
}
Example #4
0
/* ****************************************************************************
*
* EntityIdVector::check -
*/
std::string EntityIdVector::check
(
    RequestType         requestType,
    Format              format,
    const std::string&  indent,
    const std::string&  predetectedError,
    int                 counter
)
{
    // Only OK to be empty if part of a ContextRegistration
    if ((requestType == DiscoverContextAvailability)           ||
            (requestType == SubscribeContextAvailability)          ||
            (requestType == UpdateContextAvailabilitySubscription) ||
            (requestType == QueryContext)                          ||
            (requestType == SubscribeContext))
    {
        if (vec.size() == 0)
        {
            LM_W(("Bad Input (mandatory entity list missing)"));
            return "No entities";
        }
    }

    for (unsigned int ix = 0; ix < vec.size(); ++ix)
    {
        std::string res;

        if ((res = vec[ix]->check(requestType, format, indent, predetectedError, counter)) != "OK")
        {
            LM_W(("Bad Input (invalid vector of EntityIds)"));
            return res;
        }
    }

    return "OK";
}
/* ****************************************************************************
*
* entityIdList -
*/
static int entityIdList(xml_node<>* node, ParseData* parseDataP)
{
  LM_T(LmtParse, ("got an entityIdList"));

  if (parseDataP->rcr.crP->entityIdVectorPresent == true)
  {
    parseDataP->errorString = "Got an entityIdList when one was present already";
    LM_W(("Bad Input (more than one list of entityId)"));
    return 1;
  }

  parseDataP->rcr.crP->entityIdVectorPresent = true;

  return 0;
}
Example #6
0
/* ****************************************************************************
*
* treat -
*
* This is the function that actually treats a node, bu calling its treat function
* provided by src/lib/xmlRequest - the entry point of XML parsing.
*
* It simple compares the current path with the paths in the incoming vector 'parseVector'
* and if a hit is found calls the 'treat' function of that hit (the instance of the vector).
*
* If no hit is found it means that the path of the current XML node is unknown.
* This will result in either a 'PARSE ERROR' or thatthe node is part of a Compound.
*/
static bool treat(ConnectionInfo* ciP, xml_node<>* node, const std::string& path, XmlNode* parseVector, ParseData* parseDataP)
{
  for (unsigned int ix = 0; parseVector[ix].path != "LAST"; ++ix)
  {
    if (path == parseVector[ix].path)
    {
      int r;

      //
      // Before treating a node, a check is made that the value of the node has no forbidden
      // characters.
      // However, if the the node has attributes, then the values of the attributes are checked instead
      //
      if (node->first_attribute() == NULL)
      {
        if (forbiddenChars(node->value()) == true)
        {
          LM_E(("Found a forbidden value in '%s'", node->value()));
          ciP->httpStatusCode = SccBadRequest;
          ciP->answer = std::string("Illegal value for XML attribute");
          return true;
        }
      }
      else
      {
        for (xml_attribute<> *attr = node->first_attribute(); attr; attr = attr->next_attribute())
        {
          if (forbiddenChars(attr->value()) == true)
          {
            LM_E(("Found a forbidden value in attribute: '%s'", node->value()));
            ciP->httpStatusCode = SccBadRequest;
            ciP->answer = std::string("Illegal value for XML attribute");
            return true;
          }
        }
      }

      if ((r = parseVector[ix].treat(node, parseDataP)) != 0)
      {
        LM_W(("Bad Input (xml parse error %d)", r));
      }

      return true;  // Node has been treated
    }
  }

  return false;  // Node was not found in the parse vector
}
/* ****************************************************************************
*
* duration - 
*/
static std::string duration(const std::string& path, const std::string& value, ParseData* reqDataP)
{
  std::string s;

  LM_T(LmtParse, ("Got a duration: '%s'", value.c_str()));

  reqDataP->scar.res.duration.set(value);

  if ((s = reqDataP->scar.res.duration.check(SubscribeContextAvailability, JSON, "", "", 0)) != "OK")
  {
    LM_W(("Bad Input (error parsing duration '%s': %s)", reqDataP->scar.res.duration.get().c_str(), s.c_str()));
    return s;
  }

  return "OK";
}
/* ****************************************************************************
*
* badVerbGetPostDeleteOnly - 
*/
std::string badVerbGetPostDeleteOnly
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  ciP->httpHeader.push_back("Allow");
  ciP->httpHeaderValue.push_back("GET, POST, DELETE");
  ciP->httpStatusCode = SccBadVerb;

  LM_W(("Bad Input (bad verb for url '%s', method '%s')", ciP->url.c_str(), ciP->method.c_str()));

  return "";
}
/* ****************************************************************************
*
* mongoUpdateContext - 
*/
HttpStatusCode mongoUpdateContext
(
  UpdateContextRequest*                 requestP,
  UpdateContextResponse*                responseP,
  const std::string&                    tenant,
  const std::vector<std::string>&       servicePathV,
  std::map<std::string, std::string>&   uriParams,    // FIXME P7: we need this to implement "restriction-based" filters
  const std::string&                    xauthToken,
  const std::string&                    apiVersion,
  bool                                  checkEntityExistance
)
{
    bool reqSemTaken;

    reqSemTake(__FUNCTION__, "ngsi10 update request", SemWriteOp, &reqSemTaken);

    /* Check that the service path vector has only one element, returning error otherwise */
    if (servicePathV.size() > 1)
    {
        LM_W(("Bad Input (service path length %d is greater than the one in update)", servicePathV.size()));
        responseP->errorCode.fill(SccBadRequest, "service path length greater than one in update");
    }
    else
    {
        /* Process each ContextElement */
        for (unsigned int ix = 0; ix < requestP->contextElementVector.size(); ++ix)
        {
          processContextElement(requestP->contextElementVector.get(ix),
                                responseP,
                                requestP->updateActionType.get(),
                                tenant,
                                servicePathV,
                                uriParams,
                                xauthToken,
                                apiVersion,
                                checkEntityExistance);
        }

        /* Note that although individual processContextElements() invocations return ConnectionError, this
           error gets "encapsulated" in the StatusCode of the corresponding ContextElementResponse and we
           consider the overall mongoUpdateContext() as OK. */
        responseP->errorCode.fill(SccOk);
    }    
    reqSemGive(__FUNCTION__, "ngsi10 update request", reqSemTaken);
    
    return SccOk;
}
Example #10
0
/* ****************************************************************************
*
* Notifier::sendNotifyContextAvailabilityRequest -
*
* FIXME: this method is very similar to sendNotifyContextRequest and probably
* they could be refactored in the future to have a common part using a parent
* class for both types of notifications and using it as first argument
*/
void Notifier::sendNotifyContextAvailabilityRequest(NotifyContextAvailabilityRequest* ncar, const std::string& url, const std::string& tenant, Format format) {

    /* Render NotifyContextAvailabilityRequest */
    std::string payload = ncar->render(NotifyContextAvailability, format, "");

    /* Parse URL */
    std::string  host;
    int          port;
    std::string  uriPath;
    std::string  protocol;
    if (!parseUrl(url, host, port, uriPath, protocol))
    {
      LM_W(("Bad Input (sending NotifyContextAvailabilityRequest: malformed URL: '%s')", url.c_str()));
      return;
    }

    /* Set Content-Type depending on the format */
    std::string content_type = (format == XML ? "application/xml" : "application/json");

    /* Send the message (no wait for response, in a separated thread to avoid blocking response)*/
#ifdef SEND_BLOCKING
    sendHttpSocket(host, port, protocol, "POST", tenant, "", "", uriPath, content_type, payload, true, NOTIFICATION_WAIT_MODE);
#endif

#ifdef SEND_IN_NEW_THREAD
    pthread_t tid;
    SenderThreadParams* params = new SenderThreadParams();

    params->ip           = host;
    params->port         = port;
    params->verb         = "POST";
    params->tenant       = tenant;
    params->resource     = uriPath;   
    params->content_type = content_type;
    params->content      = payload;
    strncpy(params->transactionId, transactionId, sizeof(params->transactionId));

    int ret = pthread_create(&tid, NULL, startSenderThread, params);
    if (ret != 0)
    {
      LM_E(("Runtime Error (error creating thread: %d)", ret));
      return;
    }
    pthread_detach(tid);
#endif
}
Example #11
0
/* ****************************************************************************
*
* xmlParse - 
*/
void xmlParse(xml_node<>* father, xml_node<>* node, std::string indentation, std::string fatherPath, XmlNode* parseVector, ParseData* reqDataP)
{
  if (node == NULL)
    return;

  if (node->name() == NULL)
    return;

  if (node->name()[0] == 0)
    return;

  std::string path = fatherPath + "/" + node->name();


  //
  // Lookup node in the node vector
  //
  bool treated = false;
  for (unsigned int ix = 0; parseVector[ix].path != "LAST"; ++ix)
  {
    if (path == parseVector[ix].path)
    {
      int r;

      if ((r = parseVector[ix].treat(node, reqDataP)) != 0)
      {
        fprintf(stderr, "parse vector treat function error: %d\n", r);
        LM_E(("parse vector treat function error: %d", r));
      }

      treated = true;
      break;
    }
  }

  if (treated == false)
    LM_W(("Warning: node '%s' not treated (%s)", path.c_str(), node->name()));

  xml_node<>* child = node->first_node();

  while (child != NULL)
  {
    xmlParse(node, child, indentation + "  ", path, parseVector, reqDataP);
    child = child->next_sibling();
  }
}
/* ****************************************************************************
*
* entityIdId -
*/
static int entityIdId(xml_node<>* node, ParseData* parseDataP)
{
  LM_T(LmtParse, ("Got an entityId:id: '%s'", node->value()));

  if (parseDataP->ucas.entityIdP != NULL)
  {
    parseDataP->ucas.entityIdP->id = node->value();
  }
  else
  {
    LM_W(("Bad Input (XML parse error)"));
    parseDataP->errorString = "Bad Input (XML parse error)";
    return 1;
  }

  return 0;
}
/* ****************************************************************************
*
* QueryContextResponse::check -
*/
std::string QueryContextResponse::check(ConnectionInfo* ciP, RequestType requestType, const std::string& indent, const std::string& predetectedError, int counter)
{
  std::string           res;

  if (predetectedError != "")
  {
    errorCode.fill(SccBadRequest, predetectedError);
  }
  else if ((res = contextElementResponseVector.check(QueryContext, ciP->outFormat, indent, predetectedError, 0)) != "OK")
  {
    LM_W(("Bad Input (%s)", res.c_str()));
    errorCode.fill(SccBadRequest, res);
  }
  else
    return "OK";

  return render(ciP, QueryContext, indent);
}
/* ****************************************************************************
*
* AlarmManager::badInputReset - 
*
* Returns false if no effective alarm transition occurs, otherwise, true is returned.
*/
bool AlarmManager::badInputReset(const std::string& ip)
{
  semTake();

  if (badInputV.find(ip) == badInputV.end())  // Doesn't exist
  {
    semGive();
    return false;
  }

  badInputV.erase(ip);
  ++badInputResets;
  semGive();

  LM_W(("Releasing alarm BadInput %s", ip.c_str()));

  return true;
}
/* ****************************************************************************
*
* AlarmManager::notificationErrorReset - 
*
* Returns false if no effective alarm transition occurs, otherwise, true is returned.
*/
bool AlarmManager::notificationErrorReset(const std::string& url)
{
  semTake();

  if (notificationV.find(url) == notificationV.end())  // Doesn't exist
  {
    semGive();
    return false;
  }
  
  notificationV.erase(url);
  ++notificationErrorResets;
  semGive();

  LM_W(("Releasing alarm NotificationError %s", url.c_str()));

  return true;
}
Example #16
0
/* ****************************************************************************
*
* xmlRequestGet -
*/
static XmlRequest* xmlRequestGet(RequestType request, std::string method)
{
  for (unsigned int ix = 0; ix < sizeof(xmlRequest) / sizeof(xmlRequest[0]); ++ix)
  {
    if ((request == xmlRequest[ix].type) && ((xmlRequest[ix].method == method) || (xmlRequest[ix].method == "*")))
    {
      if (xmlRequest[ix].parseVector != NULL)
        LM_T(LmtHttpRequest, ("Found xmlRequest of type %d, method '%s' - index %d (%s)",
                              request,
                              method.c_str(),
                              ix,
                              xmlRequest[ix].parseVector[0].path.c_str()));
      return &xmlRequest[ix];
    }
  }

  LM_W(("Bad Input (no request found for RequestType '%s', method '%s')", requestType(request), method.c_str()));
  return NULL;
}
/* ****************************************************************************
*
* sourceEntityId -
*/
static int sourceEntityId(xml_node<>* node, ParseData* parseDataP)
{
  LM_T(LmtParse, ("got a sourceEntityId"));

  LM_T(LmtParse, ("calling entityIdParse"));
  std::string es = entityIdParse(RegisterContext,
                                 node,
                                 &parseDataP->rcr.registrationMetadataP->association.entityAssociation.source);

  LM_T(LmtParse, ("back from  entityIdParse"));

  if (es != "OK")
  {
    parseDataP->errorString = es;
    LM_W(("Bad Input (error parsing entity: %s)", es.c_str()));
  }

  return 0;
}
/* ****************************************************************************
*
* ContextElementResponse::fill - 
*/
void ContextElementResponse::fill(QueryContextResponse* qcrP, const std::string& entityId, const std::string& entityType)
{
  if (qcrP == NULL)
  {
    statusCode.fill(SccContextElementNotFound);
    return;
  }

  if (qcrP->contextElementResponseVector.size() == 0)
  {
    statusCode.fill(&qcrP->errorCode);
    contextElement.entityId.fill(entityId, entityType, "false");

    if ((statusCode.code != SccOk) && (statusCode.details == ""))
    {
      statusCode.details = "Entity id: /" + entityId + "/";
    }

    return;
  }

  //
  // FIXME P7: If more than one context element is found, we simply select the first one.
  //           A better approach would be to change this convop to return a vector of responses.
  //           Adding a warning with 'Bad Input' - with this I mean that the user that sends the 
  //           query needs to avoid using this conv op to make any queries that can give more than
  //           one unique context element :-).
  //           This FIXME is related to github issue #588 and (probably) #650.
  //           Also, optimizing this would be part of issue #768
  //
  if (qcrP->contextElementResponseVector.size() > 1)
  {
    LM_W(("Bad Input (more than one context element found the this query - selecting the first one"));
  }

  contextElement.fill(&qcrP->contextElementResponseVector[0]->contextElement);

  if (qcrP->errorCode.code != SccNone)
  {
    statusCode.fill(&qcrP->errorCode);
  }
}
/* ****************************************************************************
*
* AlarmManager::dbError - 
*
* Returns false if no effective alarm transition occurs, otherwise, true is returned.
*/
bool AlarmManager::dbError(const std::string& details)
{
  if (dbOk == false)
  {
    if (dbErrorLogAlways)
    {
      LM_W(("Repeated Database Error: %s", details.c_str()));
    }

    return false;
  }

  semTake();
  ++dbErrors;
  dbOk = false;
  semGive();

  LM_E(("Raising alarm DatabaseError: %s", details.c_str()));
  return true;
}
Example #20
0
/* ****************************************************************************
*
* xmlParse - 
*
* This function is called once (actually it is not that simple) for each node in
* the tree that the XML parser has built for us.
* 
*
* In the node '<contextValue>', isCompoundValuePath returns TRUE.
* Under "<contextValue>", we have either a simple string or a Compound Value Tree.
* In the case of a "simple string" the value of the current node is NON EMPTY,
* and as the node is treated already in the previous call to 'treat()', no further action is needed.
*
* If a node is NOT TREATED and it is NOT a compound, a parse error is issued
*/
void xmlParse
(
   ConnectionInfo*  ciP,
   xml_node<>*      father,
   xml_node<>*      node,
   std::string      indentation,
   std::string      fatherPath,
   XmlNode*         parseVector,
   ParseData*       parseDataP
)
{
  std::string  value            = wsStrip(node->value());
  std::string  name             = wsStrip(node->name());
  std::string  path             = fatherPath + "/" + name;
  bool         treated          = treat(node, path, parseVector, parseDataP);

  if (isCompoundValuePath(path.c_str()) && (value == "") && (node->first_node() != NULL))
  {
    eatCompound(ciP, NULL, node, "");
    compoundValueEnd(ciP, parseDataP);
    return;
  }
  else  if (treated == false)
  {
    ciP->httpStatusCode = SccBadRequest;
    if (ciP->answer == "")
      ciP->answer = std::string("Unknown XML field: '") + name.c_str() + "'";
    LM_W(("ERROR: '%s', PATH: '%s'   ", ciP->answer.c_str(), fatherPath.c_str()));
    return;
  }

  // Recursive calls for all children of this node
  xml_node<>* child = node->first_node();
  while (child != NULL)
  {
    if ((child != NULL) && (child->name() != NULL) && (onlyWs(child->name()) == false))
      xmlParse(ciP, node, child, indentation + "  ", path, parseVector, parseDataP);

    child = child->next_sibling();
  }
}
/* ****************************************************************************
*
* RegisterProviderRequest::check - 
*/
std::string RegisterProviderRequest::check(RequestType requestType, Format format, std::string indent, std::string predetectedError, int counter)
{
   DiscoverContextAvailabilityResponse  response;
   std::string                          res;

   if (predetectedError != "")
   {
     response.errorCode.fill(SccBadRequest, predetectedError);
   }
   else if (((res = metadataVector.check(requestType, format, indent, "", counter))  != "OK") ||
            ((res = duration.check(requestType, format, indent, "", 0))              != "OK") ||
            ((res = providingApplication.check(requestType, format, indent, "", 0))  != "OK") ||
            ((res = registrationId.check(requestType, format, indent, "", 0))        != "OK"))
   {
     response.errorCode.fill(SccBadRequest, res);
   }
   else
      return "OK";

   LM_W(("RegisterProviderRequest Error"));
   return response.render(DiscoverContextAvailability, format, indent);
}
/* ****************************************************************************
*
* mapGetIndividualContextEntityAttribute - 
*/
HttpStatusCode mapGetIndividualContextEntityAttribute
(
  const std::string&         entityId,
  const std::string&         entityType,
  const std::string&         attributeName,
  ContextAttributeResponse*  response,
  ConnectionInfo*            ciP
)
{
  HttpStatusCode        ms;
  QueryContextRequest   qcRequest;
  QueryContextResponse  qcResponse;
  EntityId              entity(entityId, entityType, "false");

  qcRequest.entityIdVector.push_back(&entity);
  qcRequest.attributeList.push_back(attributeName);

  ms = mongoQueryContext(&qcRequest, &qcResponse, ciP->tenant, ciP->servicePathV, ciP->uriParam);

  if ((ms != SccOk) || (qcResponse.contextElementResponseVector.size() == 0))
  {
    // Here I fill in statusCode for the response
    response->statusCode.fill(SccContextElementNotFound, std::string("Entity id: /") + entityId + "/");
    LM_W(("Bad Input (entityId '%s' not found)", entityId.c_str()));
    return ms;
  }

  ContextElementResponse*        cerP   = qcResponse.contextElementResponseVector[0];
  std::vector<ContextAttribute*> attrV  = cerP->contextElement.contextAttributeVector.vec;

  for (unsigned int ix = 0; ix < attrV.size() ; ++ix)
  {
    ContextAttribute* ca = new ContextAttribute(attrV[ix]);
    response->contextAttributeVector.push_back(ca);
  }

  response->statusCode.fill(SccOk);
  return ms;
}
/* ****************************************************************************
*
* StatusCode::fill - 
*/
void StatusCode::fill(const struct UpdateContextResponse& ucrs)
{
  if ((ucrs.errorCode.code != SccOk) && (ucrs.errorCode.code != SccNone))
  {
    fill(ucrs.errorCode);
  }
  else if (ucrs.contextElementResponseVector.vec.size() == 1)
  {
    fill(ucrs.contextElementResponseVector.vec[0]->statusCode);
  }
  else if (ucrs.contextElementResponseVector.vec.size() > 1)
  {
    LM_W(("Filling StatusCode from UpdateContextResponse with more than one contextElementResponse, picking one of them ..."));
    fill(ucrs.contextElementResponseVector.vec[0]->statusCode);
  }
  else
  {
    // Empty UpdateContextResponse::contextElementResponseVector AND unfilled UpdateContextResponse::errorCode
    LM_E(("Internal Error (can't fill StatusCode from UpdateContextResponse)"));
    fill(SccReceiverInternalError, "can't fill StatusCode from UpdateContextResponse");
  }
}
Example #24
0
/* ****************************************************************************
*
* ScopeVector::check -
*/
std::string ScopeVector::check
(
  RequestType         requestType,
  Format              format,
  const std::string&  indent,
  const std::string&  predetectedError,
  int                 counter
)
{
  for (unsigned int ix = 0; ix < vec.size(); ++ix)
  {
    std::string res;

    if ((res = vec[ix]->check(requestType, format, indent, predetectedError, counter)) != "OK")
    {
      LM_W(("Bad Input (error in scope %d: %s)", ix, res.c_str()));
      return res;
    }
  }

  return "OK";
}
/* ****************************************************************************
*
* EntityTypeAttributesResponse::check -
*/
std::string EntityTypeAttributesResponse::check
(
  ConnectionInfo*     ciP,
  const std::string&  indent,
  const std::string&  predetectedError
)
{
  std::string res;

  if (predetectedError != "")
  {
    statusCode.fill(SccBadRequest, predetectedError);
  }
  else if ((res = entityType.check(ciP, indent, predetectedError)) != "OK")
  {
    LM_W(("Bad Input (%s)", res.c_str()));
    statusCode.fill(SccBadRequest, res);
  }
  else
    return "OK";

  return render(ciP, "");
}
Example #26
0
/* ****************************************************************************
*
* entityIdParse - help routine for the XML node entityId
*
* Created mainly to not copy the XML attribute lookup stuff
*/
std::string entityIdParse(RequestType requestType, xml_node<>* node, EntityId* entityIdP)
{
  for (xml_attribute<> *attr = node->first_attribute(); attr; attr = attr->next_attribute())
  {
    if (attr->name() == std::string("type"))
    {
      entityIdP->type = attr->value();
      LM_T(LmtEntityId, ("Got a type for an entity: '%s'", entityIdP->type.c_str()));
    }
    else if (attr->name() == std::string("isPattern"))
    {
      entityIdP->isPattern = attr->value();
      LM_T(LmtEntityId, ("Got an isPattern for an entity: '%s'", entityIdP->isPattern.c_str()));
    }
    else
    {
      LM_W(("Bad Input (unsupported attribute '%s' for EntityId)", attr->name()));
      return "unsupported attribute for EntityId";
    }
  }

  return "OK";
}
/* ****************************************************************************
*
* mongoUnsubscribeContext - 
*/
HttpStatusCode mongoUnsubscribeContext(UnsubscribeContextRequest* requestP, UnsubscribeContextResponse* responseP, const std::string& tenant)
{
    bool           reqSemTaken;
    BSONObj        sub;
    DBClientBase*  connection = NULL;

    reqSemTake(__FUNCTION__, "ngsi10 unsubscribe request", SemWriteOp, &reqSemTaken);

    LM_T(LmtMongo, ("Unsubscribe Context"));

    /* No matter if success or failure, the subscriptionId in the response is always the one
     * in the request */
    responseP->subscriptionId = requestP->subscriptionId;

    if (responseP->subscriptionId.get() == "")
    {
        responseP->statusCode.fill(SccContextElementNotFound);
        LM_W(("Bad Input (no subscriptionId)"));
        return SccOk;
    }

    LM_T(LmtMongo, ("findOne() in '%s' collection _id '%s'}", getSubscribeContextCollectionName(tenant).c_str(),
                    requestP->subscriptionId.get().c_str()));

    /* Look for document */
    connection = getMongoConnection();
    try
    {
        OID id = OID(requestP->subscriptionId.get());
        sub = connection->findOne(getSubscribeContextCollectionName(tenant).c_str(), BSON("_id" << id));
        releaseMongoConnection(connection);
        LM_I(("Database Operation Successful (findOne _id: %s)", id.toString().c_str()));
    }
    catch (const AssertionException &e)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo assertion exception)", reqSemTaken);

        //
        // This happens when OID format is wrong
        // FIXME: this checking should be done at parsing stage, without progressing to
        // mongoBackend. By the moment we can live this here, but we should remove in the future
        // (old issue #95)
        //
        responseP->statusCode.fill(SccContextElementNotFound);
        LM_W(("Bad Input (invalid OID format)"));
        return SccOk;
    }
    catch (const DBException &e)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo db exception)", reqSemTaken);

        responseP->statusCode.fill(SccReceiverInternalError,
                                   std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() +
                                   " - findOne() _id: " + requestP->subscriptionId.get() +
                                   " - exception: " + e.what());
        LM_E(("Database Error (%s)", responseP->statusCode.details.c_str()));

        return SccOk;
    }
    catch (...)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo generic exception)", reqSemTaken);

        responseP->statusCode.fill(SccReceiverInternalError,
                                   std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() +
                                   " - findOne() _id: " + requestP->subscriptionId.get() +
                                   " - exception: " + "generic");
        LM_E(("Database Error (%s)", responseP->statusCode.details.c_str()));

        return SccOk;
    }

    if (sub.isEmpty())
    {
       reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (no subscriptions found)", reqSemTaken);

       responseP->statusCode.fill(SccContextElementNotFound, std::string("subscriptionId: /") + requestP->subscriptionId.get() + "/");
       return SccOk;
    }

    /* Remove document in MongoDB */
    // FIXME: I will prefer to do the find and remove in a single operation. Is the some similar
    // to findAndModify for this?
    LM_T(LmtMongo, ("remove() in '%s' collection _id '%s'}", getSubscribeContextCollectionName(tenant).c_str(),
                    requestP->subscriptionId.get().c_str()));

    connection = getMongoConnection();    
    try
    {
        connection->remove(getSubscribeContextCollectionName(tenant).c_str(), BSON("_id" << OID(requestP->subscriptionId.get())));
        releaseMongoConnection(connection);
        
        LM_I(("Database Operation Successful (remove _id: %s)", requestP->subscriptionId.get().c_str()));
    }
    catch (const DBException &e)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo db exception)", reqSemTaken);

        responseP->statusCode.fill(SccReceiverInternalError,
                                   std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() +
                                   " - remove() _id: " + requestP->subscriptionId.get().c_str() +
                                   " - exception: " + e.what());
        LM_E(("Database Error (%s)", responseP->statusCode.details.c_str()));
        return SccOk;
    }
    catch (...)
    {
        releaseMongoConnection(connection);
        reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request (mongo generic exception)", reqSemTaken);

        responseP->statusCode.fill(SccReceiverInternalError,
                                   std::string("collection: ") + getSubscribeContextCollectionName(tenant).c_str() +
                                   " - remove() _id: " + requestP->subscriptionId.get().c_str() +
                                   " - exception: " + "generic");

        LM_E(("Database Error (%s)", responseP->statusCode.details.c_str()));
        return SccOk;
    }

    /* Destroy any previous ONTIMEINTERVAL thread */
    getNotifier()->destroyOntimeIntervalThreads(requestP->subscriptionId.get());

    responseP->statusCode.fill(SccOk);
    reqSemGive(__FUNCTION__, "ngsi10 unsubscribe request", reqSemTaken);

    //
    // Removing subscription from cache
    //
    subCache->remove(tenant, "", requestP->subscriptionId.get());

    return SccOk;
}
Example #28
0
/* ****************************************************************************
*
* postUpdateContext -
*
* POST /v1/updateContext
* POST /ngsi10/updateContext
*
* Payload In:  UpdateContextRequest
* Payload Out: UpdateContextResponse
*/
std::string postUpdateContext
(
  ConnectionInfo*            ciP,
  int                        components,
  std::vector<std::string>&  compV,
  ParseData*                 parseDataP
)
{
  UpdateContextResponse*  upcrsP = &parseDataP->upcrs.res;
  UpdateContextRequest*   upcrP  = &parseDataP->upcr.res;
  std::string             answer;

  //
  // 01. Check service-path consistency
  //
  // If more than ONE service-path is input, an error is returned as response.
  // If NO service-path is issued, then the default service-path "/" is used.
  // After these checks, the service-path is checked to be 'correct'.
  //
  if (ciP->servicePathV.size() > 1)
  {
    upcrsP->errorCode.fill(SccBadRequest, "more than one service path in context update request");
    LM_W(("Bad Input (more than one service path for an update request)"));
    answer = upcrsP->render(ciP, UpdateContext, "");
    return answer;
  }
  else if (ciP->servicePathV.size() == 0)
  {
    ciP->servicePathV.push_back(DEFAULT_SERVICE_PATH);
  }

  std::string res = servicePathCheck(ciP->servicePathV[0].c_str());
  if (res != "OK")
  {
    upcrsP->errorCode.fill(SccBadRequest, res);
    answer = upcrsP->render(ciP, UpdateContext, "");
    return answer;
  }


  //
  // 02. Send the request to mongoBackend/mongoUpdateContext
  //
  upcrsP->errorCode.fill(SccOk);
  attributesToNotFound(upcrP);
  ciP->httpStatusCode = mongoUpdateContext(upcrP, upcrsP, ciP->tenant, ciP->servicePathV, ciP->uriParam, ciP->httpHeaders.xauthToken, "postUpdateContext");
  foundAndNotFoundAttributeSeparation(upcrsP, upcrP, ciP);



  //
  // 03. Normal case - no forwards
  //
  // If there is nothing to forward, just return the result
  //
  bool forwarding = forwardsPending(upcrsP);
  if (forwarding == false)
  {
    answer = upcrsP->render(ciP, UpdateContext, "");
    upcrP->release();
    return answer;
  }


  //
  // 04. mongoBackend doesn't give us the values of the attributes.
  //     So, here we have to do a search inside the initial UpdateContextRequest to fill in all the
  //     attribute-values in the output from mongoUpdateContext
  //
  for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx)
  {
    ContextElement* ceP = &upcrsP->contextElementResponseVector[cerIx]->contextElement;

    for (unsigned int aIx = 0; aIx < ceP->contextAttributeVector.size(); ++aIx)
    {
      ContextAttribute* aP = upcrP->attributeLookup(&ceP->entityId, ceP->contextAttributeVector[aIx]->name);

      if (aP == NULL)
      {
        LM_E(("Internal Error (attribute '%s' not found)", ceP->contextAttributeVector[aIx]->name.c_str()));
      }
      else
      {
        ceP->contextAttributeVector[aIx]->value = aP->value;
        ceP->contextAttributeVector[aIx]->type  = aP->type;
      }
    }
  }


  //
  // 05. Forwards necessary - sort parts in outgoing requestV
  //     requestV is a vector of UpdateContextRequests and each Context Provider
  //     will have a slot in the vector. 
  //     When a ContextElementResponse is found in the output from mongoUpdateContext, a
  //     UpdateContextRequest is to be found/created and inside that UpdateContextRequest
  //     a ContextElement for the Entity of the ContextElementResponse.
  //
  //     Non-found parts go directly to 'response'.
  //
  UpdateContextRequestVector  requestV;
  UpdateContextResponse       response;

  response.errorCode.fill(SccOk);
  for (unsigned int cerIx = 0; cerIx < upcrsP->contextElementResponseVector.size(); ++cerIx)
  {
    ContextElementResponse* cerP  = upcrsP->contextElementResponseVector[cerIx];

    if (cerP->contextElement.contextAttributeVector.size() == 0)
    {
      //
      // If we find a contextElement without attributes here, then something is wrong
      //
      LM_E(("Orion Bug (empty contextAttributeVector for ContextElementResponse %d)", cerIx));
    }
    else
    {
      for (unsigned int aIx = 0; aIx < cerP->contextElement.contextAttributeVector.size(); ++aIx)
      {
        ContextAttribute* aP = cerP->contextElement.contextAttributeVector[aIx];
        
        //
        // 0. If the attribute is 'not-found' - just add the attribute to the outgoing response
        //
        if (aP->found == false)
        {
          response.notFoundPush(&cerP->contextElement.entityId, new ContextAttribute(aP), NULL);
          continue;
        }


        //
        // 1. If the attribute is found locally - just add the attribute to the outgoing response
        //
        if (aP->providingApplication.get() == "")
        {
          response.foundPush(&cerP->contextElement.entityId, new ContextAttribute(aP));
          continue;
        }


        //
        // 2. Lookup UpdateContextRequest in requestV according to providingApplication.
        //    If not found, add one.
        UpdateContextRequest*  reqP = requestV.lookup(aP->providingApplication.get());
        if (reqP == NULL)
        {
          reqP = new UpdateContextRequest(aP->providingApplication.get(), &cerP->contextElement.entityId);
          reqP->updateActionType.set("UPDATE");
          requestV.push_back(reqP);
        }

        //
        // 3. Increase the correct format counter
        //
        if (aP->providingApplication.getFormat() == XML)
        {
          reqP->xmls++;
        }
        else
        {
          reqP->jsons++;
        }

        //
        // 3. Lookup ContextElement in UpdateContextRequest according to EntityId.
        //    If not found, add one (to the ContextElementVector of the UpdateContextRequest).
        //
        ContextElement* ceP = reqP->contextElementVector.lookup(&cerP->contextElement.entityId);
        if (ceP == NULL)
        {
          ceP = new ContextElement(&cerP->contextElement.entityId);
          reqP->contextElementVector.push_back(ceP);
        }


        //
        // 4. Add ContextAttribute to the correct ContextElement in the correct UpdateContextRequest
        //
        ceP->contextAttributeVector.push_back(new ContextAttribute(aP));
      }
    }
  }


  //
  // Now we are ready to forward the Updates
  //


  //
  // Calling each of the Context Providers, merging their results into the
  // total response 'response'
  //
  for (unsigned int ix = 0; ix < requestV.size(); ++ix)
  {
    if (requestV[ix]->contextProvider == "")
    {
      LM_E(("Internal Error (empty context provider string)"));
      continue;
    }

    UpdateContextResponse upcrs;
    Format                format = requestV[ix]->format();

    updateForward(ciP, requestV[ix], &upcrs, format);

    //
    // Add the result from the forwarded update to the total response in 'response'
    //
    response.merge(&upcrs);
  }

  answer = response.render(ciP, UpdateContext, "");

  //
  // Cleanup
  //
  upcrP->release();
  requestV.release();
  upcrsP->release();
  upcrsP->fill(&response);
  response.release();

  return answer;
}
Example #29
0
/* ****************************************************************************
*
* updateForward - 
*
* An entity/attribute has been found on some context provider.
* We need to forward the update request to the context provider, indicated in upcrsP->contextProvider
*
* 1. Parse the providing application to extract IP, port and URI-path
* 2. Render the string of the request we want to forward
* 3. Send the request to the providing application (and await the response)
* 4. Parse the response and fill in a binary UpdateContextResponse
* 5. Fill in the response from the redirection into the response of this function
* 6. 'Fix' StatusCode
* 7. Freeing memory
*
*
* FIXME P5: The function 'updateForward' is implemented to pick the format (XML or JSON) based on the
*           count of the Format for all the participating attributes. If we have more attributes 'preferring'
*           XML than JSON, the forward is done in XML, etc. This is all OK.
*           What is not OK is that the Accept HTTP header is set to the same format as the Content-Type HTTP Header.
*           While this is acceptable, it is not great. As the broker understands both XML and JSON, we could send
*           the forward message with an Acceot header of XML/JSON and then at reading the response, instead of 
*           throwing away the HTTP headers, we could read the "Content-Type" and do the parse according the Content-Type.
*/
static void updateForward(ConnectionInfo* ciP, UpdateContextRequest* upcrP, UpdateContextResponse* upcrsP, Format format)
{
  std::string     ip;
  std::string     protocol;
  int             port;
  std::string     prefix;
  std::string     answer;


  //
  // 1. Parse the providing application to extract IP, port and URI-path
  //
  if (parseUrl(upcrP->contextProvider, ip, port, prefix, protocol) == false)
  {
    LM_W(("Bad Input (invalid providing application '%s')", upcrP->contextProvider.c_str()));
    //  Somehow, if we accepted this providing application, it is the brokers fault ...
    //  SccBadRequest should have been returned before, when it was registered!

    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    return;
  }


  //
  // 2. Render the string of the request we want to forward
  //
  Format       outFormat = ciP->outFormat;
  std::string  payload;
  char*        cleanPayload;

  ciP->outFormat  = format;
  payload         = upcrP->render(ciP, UpdateContext, "");
  ciP->outFormat  = outFormat;
  cleanPayload    = (char*) payload.c_str();

  if (format == XML)
  {
    if ((cleanPayload = xmlPayloadClean(payload.c_str(), "<updateContextRequest>")) == NULL)
    {
      LM_E(("Runtime Error (error rendering forward-request)"));
      upcrsP->errorCode.fill(SccContextElementNotFound, "");
      return;
    }
  }


  //
  // 3. Send the request to the Context Provider (and await the reply)
  // FIXME P7: Should Rush be used?
  //
  std::string     out;
  std::string     verb         = "POST";
  std::string     resource     = prefix + "/updateContext";
  std::string     tenant       = ciP->tenant;
  std::string     servicePath  = (ciP->httpHeaders.servicePathReceived == true)? ciP->httpHeaders.servicePath : "";
  std::string     mimeType     = (format == XML)? "application/xml" : "application/json";

  out = httpRequestSend(ip,
                        port,
                        protocol,
                        verb,
                        tenant,
                        servicePath,
                        ciP->httpHeaders.xauthToken,
                        resource,
                        mimeType,
                        cleanPayload,
                        false,
                        true,
                        mimeType);

  if ((out == "error") || (out == ""))
  {
    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    LM_E(("Runtime Error (error forwarding 'Update' to providing application)"));
    return;
  }


  //
  // 4. Parse the response and fill in a binary UpdateContextResponse
  //
  std::string  s;
  std::string  errorMsg;

  if (format == XML)
  {
    cleanPayload = xmlPayloadClean(out.c_str(), "<updateContextResponse>");
  }
  else
  {
    cleanPayload = jsonPayloadClean(out.c_str());
  }

  if ((cleanPayload == NULL) || (cleanPayload[0] == 0))
  {
    //
    // This is really an internal error in the Context Provider
    // It is not in the orion broker though, so 404 is returned
    //
    LM_W(("Other Error (context provider response to UpdateContext is empty)"));
    upcrsP->errorCode.fill(SccContextElementNotFound, "invalid context provider response");
    return;
  }

  //
  // NOTE
  // When coming from a convenience operation, such as GET /v1/contextEntities/EID/attributes/attrName,
  // the verb/method in ciP is GET. However, the parsing function expects a POST, as if it came from a 
  // POST /v1/updateContext. 
  // So, here we change the verb/method for POST.
  //
  ParseData parseData;

  ciP->verb   = POST;
  ciP->method = "POST";

  parseData.upcrs.res.errorCode.fill(SccOk);

  if (format == XML)
  {
    s = xmlTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, "updateContextResponse", NULL, &errorMsg);
  }
  else
  {
    s = jsonTreat(cleanPayload, ciP, &parseData, RtUpdateContextResponse, "updateContextResponse", NULL);
  }

  if (s != "OK")
  {
    LM_W(("Internal Error (error parsing reply from prov app: %s)", errorMsg.c_str()));
    upcrsP->errorCode.fill(SccContextElementNotFound, "");
    parseData.upcr.res.release();
    parseData.upcrs.res.release();
    return;
  }


  //
  // 5. Fill in the response from the redirection into the response of this function
  //
  upcrsP->fill(&parseData.upcrs.res);


  //
  // 6. 'Fix' StatusCode
  //
  if (upcrsP->errorCode.code == SccNone)
  {
    upcrsP->errorCode.fill(SccOk);
  }

  if ((upcrsP->contextElementResponseVector.size() == 1) && (upcrsP->contextElementResponseVector[0]->statusCode.code == SccContextElementNotFound))
  {
    upcrsP->errorCode.fill(SccContextElementNotFound);
  }

  
  //
  // 7. Freeing memory
  //
  parseData.upcr.res.release();
  parseData.upcrs.res.release();
}
Example #30
0
/* ****************************************************************************
*
* connectionTreat - 
*
* This is the MHD_AccessHandlerCallback function for MHD_start_daemon
* This function returns:
* o MHD_YES  if the connection was handled successfully
* o MHD_NO   if the socket must be closed due to a serious error
*
* - This function is called once when the headers are read and the ciP is created.
* - Then it is called for data payload and once all the payload an acknowledgement
*   must be done, setting *upload_data_size to ZERO.
* - The last call is made with *upload_data_size == 0 and now is when the connection
*   is open to send responses.
*
* Call 1: *con_cls == NULL
* Call 2: *con_cls != NULL  AND  *upload_data_size != 0
* Call 3: *con_cls != NULL  AND  *upload_data_size == 0
*/
static int connectionTreat
(
   void*            cls,
   MHD_Connection*  connection,
   const char*      url,
   const char*      method,
   const char*      version,
   const char*      upload_data,
   size_t*          upload_data_size,
   void**           con_cls
)
{
  ConnectionInfo*        ciP         = (ConnectionInfo*) *con_cls;
  size_t                 dataLen     = *upload_data_size;

  // 1. First call - setup ConnectionInfo and get/check HTTP headers
  if (ciP == NULL)
  {
    //
    // IP Address and port of caller
    //
    char            ip[32];
    unsigned short  port = 0;

    const union MHD_ConnectionInfo* mciP = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
    if (mciP != NULL)
    {
      port = (mciP->client_addr->sa_data[0] << 8) + mciP->client_addr->sa_data[1];
      snprintf(ip, sizeof(ip), "%d.%d.%d.%d", mciP->client_addr->sa_data[2]  & 0xFF, mciP->client_addr->sa_data[3]  & 0xFF, mciP->client_addr->sa_data[4] & 0xFF, mciP->client_addr->sa_data[5] & 0xFF);
    }
    else
    {
      port = 0;
      snprintf(ip, sizeof(ip), "IP unknown");
    }



    //
    // ConnectionInfo
    //
    if ((ciP = new ConnectionInfo(url, method, version, connection)) == NULL)
    {
      LM_E(("Runtime Error (error allocating ConnectionInfo)"));
      return MHD_NO;
    }

    *con_cls = (void*) ciP; // Pointer to ConnectionInfo for subsequent calls
    ciP->port = port;
    ciP->ip   = ip;
    
    //
    // Transaction starts here
    //
    LM_TRANSACTION_START("from", ip, port, url);  // Incoming REST request starts



    //
    // URI parameters
    // 
    ciP->uriParam[URI_PARAM_NOTIFY_FORMAT]      = DEFAULT_PARAM_NOTIFY_FORMAT;
    ciP->uriParam[URI_PARAM_PAGINATION_OFFSET]  = DEFAULT_PAGINATION_OFFSET;
    ciP->uriParam[URI_PARAM_PAGINATION_LIMIT]   = DEFAULT_PAGINATION_LIMIT;
    ciP->uriParam[URI_PARAM_PAGINATION_DETAILS] = DEFAULT_PAGINATION_DETAILS;
    
    MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, uriArgumentGet, ciP);
    if (ciP->httpStatusCode != SccOk)
    {
      LM_W(("Bad Input (error in URI parameters)"));
      restReply(ciP, ciP->answer);
      return MHD_YES;
    }
    LM_T(LmtUriParams, ("notifyFormat: '%s'", ciP->uriParam[URI_PARAM_NOTIFY_FORMAT].c_str()));

    MHD_get_connection_values(connection, MHD_HEADER_KIND, httpHeaderGet, &ciP->httpHeaders);
    
    char tenant[128];
    ciP->tenantFromHttpHeader = strToLower(tenant, ciP->httpHeaders.tenant.c_str(), sizeof(tenant));
    LM_T(LmtTenant, ("HTTP tenant: '%s'", ciP->httpHeaders.tenant.c_str()));
    ciP->outFormat            = wantedOutputSupported(ciP->httpHeaders.accept, &ciP->charset);
    if (ciP->outFormat == NOFORMAT)
      ciP->outFormat = XML; // XML is default output format

    ciP->servicePath = ciP->httpHeaders.servicePath;
    if (servicePathSplit(ciP) != 0)
    {
      LM_W(("Bad Input (error in ServicePath http-header)"));
      restReply(ciP, ciP->answer);
    }

    if (contentTypeCheck(ciP) != 0)
    {
      LM_W(("Bad Input (invalid mime-type in Content-Type http-header)"));
      restReply(ciP, ciP->answer);
    }
    else if (outFormatCheck(ciP) != 0)
    {
      LM_W(("Bad Input (invalid mime-type in Accept http-header)"));
      restReply(ciP, ciP->answer);
    }
    else
      ciP->inFormat = formatParse(ciP->httpHeaders.contentType, NULL);

    // Set default mime-type for notifications
    if (ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] == "")
    {
      if (ciP->outFormat == XML)
        ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = "XML";
      else if (ciP->outFormat == JSON)
        ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = "JSON";
      else
        ciP->uriParam[URI_PARAM_NOTIFY_FORMAT] = "XML";

      LM_T(LmtUriParams, ("'default' value for notifyFormat (ciP->outFormat == %d)): '%s'", ciP->outFormat, ciP->uriParam[URI_PARAM_NOTIFY_FORMAT].c_str()));
    }

    return MHD_YES;
  }


  //
  // 2. Data gathering calls
  //
  // 2-1. Data gathering calls, just wait
  // 2-2. Last data gathering call, acknowledge the receipt of data
  //
  if (dataLen != 0)
  {
    if (dataLen == ciP->httpHeaders.contentLength)
    {
      if (ciP->httpHeaders.contentLength <= PAYLOAD_MAX_SIZE)
      {
        if (ciP->httpHeaders.contentLength > STATIC_BUFFER_SIZE)
          ciP->payload = (char*) malloc(ciP->httpHeaders.contentLength + 1);
        else
          ciP->payload = static_buffer;

        ciP->payloadSize = dataLen;
        memcpy(ciP->payload, upload_data, dataLen);
        ciP->payload[dataLen] = 0;
      }
      else
      {
        char details[256];
        snprintf(details, sizeof(details), "payload size: %d, max size supported: %d", ciP->httpHeaders.contentLength, PAYLOAD_MAX_SIZE);

        ciP->answer         = restErrorReplyGet(ciP, ciP->outFormat, "", ciP->url, SccRequestEntityTooLarge, details);
        ciP->httpStatusCode = SccRequestEntityTooLarge;
      }

      // All payload received, acknowledge!
      *upload_data_size = 0;
    }
    else
      LM_T(LmtPartialPayload, ("Got %d of payload of %d bytes", dataLen, ciP->httpHeaders.contentLength));

    return MHD_YES;
  }


  // 3. Finally, serve the request (unless an error has occurred)
  if (((ciP->verb == POST) || (ciP->verb == PUT)) && (ciP->httpHeaders.contentLength == 0) && (strncasecmp(ciP->url.c_str(), "/log/", 5) != 0))
  {
    std::string errorMsg = restErrorReplyGet(ciP, ciP->outFormat, "", url, SccLengthRequired, "Zero/No Content-Length in PUT/POST request");
    ciP->httpStatusCode = SccLengthRequired;
    restReply(ciP, errorMsg);
  }
  else if (ciP->answer != "")
    restReply(ciP, ciP->answer);
  else
    serveFunction(ciP);

  return MHD_YES;
}