/**
 * Validate RSL attributes
 *
 * Checks that all of the RSL attributes in the request's RSL match
 * a validation record. If an RSL has an enumerated list of values,
 * then the value of the RSL is compared against that list.
 *
 * @param request
 *        The job request containing the RSL to validate.
 * @param when
 *        Which RSL validation time scope we will use to decide
 *        whether to use the default values or not.
 */
static
int
globus_l_gram_job_manager_check_rsl_attributes(
    globus_gram_jobmanager_request_t *  request,
    globus_rsl_t *                      rsl,
    globus_gram_job_manager_validation_when_t
                                        when)
{
    globus_list_t *                     operands;
    globus_list_t *                     node;
    globus_rsl_t *                      relation;
    char *                              attribute;
    char *                              value_str;
    globus_rvf_record_t *               record;
    globus_rsl_value_t *                value;
    int                                 rc = GLOBUS_SUCCESS;
    static const char *                 operation_types[] =
    {
        "??",
        "=",
        "!=",
        ">",
        ">=",
        "<",
        "<=",
        "??",
        "&",
        "|",
        "+"
    };

    operands = globus_rsl_boolean_get_operand_list(rsl);

    /* Check to make sure that every attribute is recognized by this
     * job manager.
     */
    while(!globus_list_empty(operands))
    {
        relation = globus_list_first(operands);
        operands = globus_list_rest(operands);

        if (!globus_rsl_is_relation(relation))
        {
            int operator = globus_rsl_boolean_get_operator(relation);
            if (operator > 10 || operator < 0)
            {
                operator = 0;
            }

            globus_gram_job_manager_request_log(
                    request,
                    GLOBUS_GRAM_JOB_MANAGER_LOG_ERROR,
                    "event=gram.validate_rsl.end "
                    "level=ERROR "
                    "msg=\"Required RSL relation, got boolean\" "
                    "operator=%s "
                    "status=%d "
                    "\n",
                    operation_types[operator],
                    -GLOBUS_GRAM_PROTOCOL_ERROR_BAD_RSL);
            if (request->gt3_failure_message == NULL)
            {
                request->gt3_failure_message = globus_common_create_string(
                        "Required RSL relation, got boolean %s",
                        operation_types[operator]);
            }
        }
        else if (!globus_rsl_is_relation_eq(relation))
        {
            int operator = globus_rsl_relation_get_operator(relation);

            if (operator > 10 || operator < 0)
            {
                operator = 0;
            }

            globus_gram_job_manager_request_log(
                    request,
                    GLOBUS_GRAM_JOB_MANAGER_LOG_ERROR,
                    "event=gram.validate_rsl.end "
                    "level=ERROR "
                    "msg=\"Unsupported RSL operation\" "
                    "attribute=%s "
                    "operator=%s "
                    "status=%d "
                    "\n",
                    globus_rsl_relation_get_attribute(relation),
                    operation_types[operator],
                    -GLOBUS_GRAM_PROTOCOL_ERROR_BAD_RSL);

            if (request->gt3_failure_message == NULL)
            {
                request->gt3_failure_message = globus_common_create_string(
                        "the job manager does not support the RSL operator "
                        "\"%s\" for the %s attribute",
                        operation_types[operator],
                        globus_rsl_relation_get_attribute(relation));
            }
            return GLOBUS_GRAM_PROTOCOL_ERROR_BAD_RSL;
        }
        attribute = globus_rsl_relation_get_attribute(relation);

        node = globus_list_search_pred(
                request->manager->validation_records,
                globus_l_gram_job_manager_attribute_match,
                attribute);

        if(!node)
        {
            globus_gram_job_manager_request_log(
                    request,
                    GLOBUS_GRAM_JOB_MANAGER_LOG_ERROR,
                    "event=gram.validate_rsl.end "
                    "level=ERROR "
                    "msg=\"Unsupported RSL attribute\" "
                    "attribute=%s "
                    "status=%d "
                    "\n",
                    globus_rsl_relation_get_attribute(relation),
                    -GLOBUS_GRAM_PROTOCOL_ERROR_BAD_RSL);

            if (request->gt3_failure_message == NULL)
            {
                request->gt3_failure_message = globus_common_create_string(
                        "the RSL attribute \"%s\" is not supported by the LRM adapter",
                        globus_rsl_relation_get_attribute(relation));
            }
            return GLOBUS_GRAM_PROTOCOL_ERROR_PARAMETER_NOT_SUPPORTED;
        }

        record = globus_list_first(node);

        /* Check valid_when */
        if((record->valid_when & when) == 0)
        {
            const char * whenstr = "unknown operation";

            switch(when)
            {
              case GLOBUS_GRAM_VALIDATE_JOB_SUBMIT:
                whenstr = "submit";
                rc = GLOBUS_GRAM_PROTOCOL_ERROR_INVALID_SUBMIT_ATTRIBUTE;
                break;
              case GLOBUS_GRAM_VALIDATE_JOB_MANAGER_RESTART:
                whenstr = "restart";
                rc = GLOBUS_GRAM_PROTOCOL_ERROR_INVALID_RESTART_ATTRIBUTE;
                break;
              case GLOBUS_GRAM_VALIDATE_STDIO_UPDATE:
                whenstr = "stdio_update";
                rc = GLOBUS_GRAM_PROTOCOL_ERROR_INVALID_STDIO_UPDATE_ATTRIBUTE;
                break;
            }

            globus_gram_job_manager_request_log(
                    request,
                    GLOBUS_GRAM_JOB_MANAGER_LOG_ERROR,
                    "event=gram.validate_rsl.end "
                    "level=ERROR "
                    "msg=\"Invalid RSL attribute for operation\" "
                    "attribute=%s "
                    "operation=%s "
                    "status=%d "
                    "\n",
                    globus_rsl_relation_get_attribute(relation),
                    whenstr,
                    -rc);
            if (request->gt3_failure_message == NULL)
            {
                request->gt3_failure_message = globus_common_create_string(
                        "Invalid RSL attribute \"%s\" for %s",
                        globus_rsl_relation_get_attribute(relation),
                        whenstr);
            }
            return rc;
        }
        /* Check enumerated values if applicable */
        if(record->enumerated_values)
        {
            value = globus_rsl_relation_get_single_value(relation);

            if(!value)
            {
                return
                    globus_l_gram_job_manager_validation_rsl_error(attribute);
            }
            value_str = globus_rsl_value_literal_get_string(value);
            if(!value_str)
            {
                return globus_l_gram_job_manager_validation_rsl_error(
                        attribute);
            }
            if(strstr(record->enumerated_values, value_str) == GLOBUS_NULL)
            {
                rc = globus_l_gram_job_manager_validation_value_error(
                            request,
                            attribute,
                            value_str,
                            record->enumerated_values);

                globus_gram_job_manager_request_log(
                        request,
                        GLOBUS_GRAM_JOB_MANAGER_LOG_ERROR,
                        "event=gram.validate_rsl.end "
                        "level=ERROR "
                        "msg=\"RSL attribute value not in enumeration\" "
                        "attribute=%s "
                        "value=%s "
                        "enumeration=\"%s\" "
                        "status=%d "
                        "\n",
                        record->attribute,
                        value_str,
                        record->enumerated_values,
                        -rc);

                return rc;
            }
        }
    }

    return GLOBUS_SUCCESS;
}
/**
 * Add default values to RSL and verify required parameters
 *
 * Inserts default values to RSL when an RSL parameter is not defined
 * in it. After this is complete, it checks that all RSL parameters
 * with the "required_when" flag set are present in the RSL tree.
 *
 * @param request
 *        Request which contains the RSL tree to validate.
 * @param when
 *        Which RSL validation time scope we will use to decide
 *        whether to use the default values or not.
 */
static
int
globus_l_gram_job_manager_insert_default_rsl(
    globus_gram_jobmanager_request_t *  request,
    globus_rsl_t *                      rsl,
    globus_gram_job_manager_validation_when_t
                                        when)
{
    globus_rvf_record_t *               record;
    globus_list_t **                    attributes;
    globus_rsl_t *                      new_relation;
    char *                              new_relation_str;
    globus_list_t *                     validation_records;
    int                                 rc = GLOBUS_SUCCESS;

    attributes = globus_rsl_boolean_get_operand_list_ref(rsl);

    validation_records = request->manager->validation_records;

    while(!globus_list_empty(validation_records))
    {
        record = globus_list_first(validation_records);
        validation_records = globus_list_rest(validation_records);

        if(record->default_value && (record->default_when&when))
        {
            if(!globus_l_gram_job_manager_attribute_exists(
                        *attributes,
                        record->attribute))
            {
                new_relation_str = globus_common_create_string(
                        "%s = %s",
                        record->attribute,
                        record->default_value);

                globus_gram_job_manager_request_log(
                        request,
                        GLOBUS_GRAM_JOB_MANAGER_LOG_TRACE,
                        "event=gram.validate_rsl.info "
                        "level=TRACE "
                        "msg=\"Inserting default RSL for attribute\" "
                        "attribute=%s "
                        "default=\"%s\" "
                        "\n",
                        record->attribute,
                        record->default_value);

                new_relation = globus_rsl_parse(new_relation_str);

                globus_list_insert(attributes, new_relation);

                free(new_relation_str);
            }
        }
        if(record->required_when & when)
        {
            if(!globus_l_gram_job_manager_attribute_exists(
                        *attributes,
                        record->attribute))
            {
                rc = globus_l_gram_job_manager_missing_value_error(
                            record->attribute);

                globus_gram_job_manager_request_log(
                        request,
                        GLOBUS_GRAM_JOB_MANAGER_LOG_ERROR,
                        "event=gram.validate_rsl.end "
                        "level=ERROR "
                        "msg=\"RSL missing required attribute\" "
                        "attribute=%s "
                        "\n",
                        record->attribute);

                return rc;
            }
        }
    }
    return rc;
}
int
globus_gram_job_manager_staging_remove(
    globus_gram_jobmanager_request_t *  request,
    globus_gram_job_manager_staging_type_t
                                        type,
    char *                              from,
    char *                              to)
{
    globus_gram_job_manager_staging_info_t 
                                        query;
    globus_gram_job_manager_staging_info_t *
                                        item;
    globus_list_t **                    list;
    globus_list_t *                     node;
    const char *                        typestr = "";

    switch(type)
    {
      case GLOBUS_GRAM_JOB_MANAGER_STAGE_IN:
          typestr = "file_stage_in";
          break;
      case GLOBUS_GRAM_JOB_MANAGER_STAGE_IN_SHARED:
          typestr = "file_stage_in_shared";
          break;
      case GLOBUS_GRAM_JOB_MANAGER_STAGE_OUT:
          typestr = "file_stage_out";
          break;
      case GLOBUS_GRAM_JOB_MANAGER_STAGE_STREAMS:
          typestr = "file_stream_out";
          break;
    }

    globus_gram_job_manager_request_log(
            request,
            GLOBUS_GRAM_JOB_MANAGER_LOG_DEBUG,
            "event=gram.staging_remove.start "
            "level=DEBUG "
            "gramid=%s "
            "src=\"%s\" "
            "dst=\"%s\" "
            "type=%s "
            "\n",
            request->job_contact_path,
            from,
            to,
            typestr);

    query.evaled_from = from;
    query.evaled_to = to;
    query.type = type;

    switch(type)
    {
      case GLOBUS_GRAM_JOB_MANAGER_STAGE_IN:
        list = &request->stage_in_todo;
        break;
      case GLOBUS_GRAM_JOB_MANAGER_STAGE_IN_SHARED:
        list = &request->stage_in_shared_todo;
        break;
      case GLOBUS_GRAM_JOB_MANAGER_STAGE_OUT:
        list = &request->stage_out_todo;
        break;
      case GLOBUS_GRAM_JOB_MANAGER_STAGE_STREAMS:
        list = &request->stage_stream_todo;
        break;
    }

    node = globus_list_search_pred(
            *list,
            globus_l_gram_job_manager_staging_match,
            &query);

    if(node)
    {
        item = globus_list_remove(list, node);

        globus_gram_job_manager_request_log(
            request,
            GLOBUS_GRAM_JOB_MANAGER_LOG_TRACE,
            "event=gram.staging_remove.end "
            "level=TRACE "
            "gramid=%s "
            "msg=\"%s\" "
            "src=\"%s\" "
            "dst=\"%s\" "
            "type=%s "
            "status=%d "
            "\n",
            request->job_contact_path,
            "File staged",
            from,
            to,
            typestr,
            0);

        if (item->type == GLOBUS_GRAM_JOB_MANAGER_STAGE_IN)
        {
            if (strncmp(item->evaled_from, "http://", 7) == 0)
            {
                request->job_stats.file_stage_in_http_count++;
            }
            else if (strncmp(item->evaled_from, "https://", 8) == 0)
            {
                request->job_stats.file_stage_in_https_count++;
            }
            else if (strncmp(item->evaled_from, "ftp://", 6) == 0)
            {
                request->job_stats.file_stage_in_ftp_count++;
            }
            else if (strncmp(item->evaled_from, "gsiftp://", 6) == 0)
            {
                request->job_stats.file_stage_in_gsiftp_count++;
            }
        }
        else if (item->type == GLOBUS_GRAM_JOB_MANAGER_STAGE_IN_SHARED)
        {
            if (strncmp(item->evaled_from, "http://", 7) == 0)
            {
                request->job_stats.file_stage_in_shared_http_count++;
            }
            else if (strncmp(item->evaled_from, "https://", 8) == 0)
            {
                request->job_stats.file_stage_in_shared_https_count++;
            }
            else if (strncmp(item->evaled_from, "ftp://", 6) == 0)
            {
                request->job_stats.file_stage_in_shared_ftp_count++;
            }
            else if (strncmp(item->evaled_from, "gsiftp://", 6) == 0)
            {
                request->job_stats.file_stage_in_shared_gsiftp_count++;
            }
        }
        else if (item->type == GLOBUS_GRAM_JOB_MANAGER_STAGE_OUT ||
                 item->type == GLOBUS_GRAM_JOB_MANAGER_STAGE_STREAMS)
        {
            if (strncmp(item->evaled_to, "http://", 7) == 0)
            {
                request->job_stats.file_stage_out_http_count++;
            }
            else if (strncmp(item->evaled_to, "https://", 8) == 0)
            {
                request->job_stats.file_stage_out_https_count++;
            }
            else if (strncmp(item->evaled_to, "ftp://", 6) == 0)
            {
                request->job_stats.file_stage_out_ftp_count++;
            }
            else if (strncmp(item->evaled_to, "gsiftp://", 6) == 0)
            {
                request->job_stats.file_stage_out_gsiftp_count++;
            }
        }
        globus_rsl_value_free_recursive(item->from);
        globus_rsl_value_free_recursive(item->to);
        free(item->evaled_from);
        free(item->evaled_to);
        free(item);
    }
    else
    {
        globus_gram_job_manager_request_log(
            request,
            GLOBUS_GRAM_JOB_MANAGER_LOG_WARN,
            "event=gram.staging_remove.end "
            "level=WARN "
            "gramid=%s "
            "msg=\"%s\" "
            "src=\"%s\" "
            "dst=\"%s\" "
            "type=%s "
            "status=%d "
            "msg=\"%s\" "
            "\n",
            request->job_contact_path,
            "File staged",
            from,
            to,
            typestr,
            0,
            "Unexpected staging completion");
    }
    return GLOBUS_SUCCESS;
}