/**
 * Destroy a request handle.
 * @ingroup globus_gass_transfer_request
 *
 * This function destroys the caller's reference to a request handle.
 * It must be called for all request handles which are created by calling
 * functions in the "@ref globus_gass_transfer_client" or
 * "@ref globus_gass_transfer_server" sections of this manual.
 * After calling the function, the caller must not attempt to use the
 * request handle for any purpose.
 *
 * @param request
 *        The request to destroy.
 * @retval GLOBUS_SUCCESS
 *        The request handle reference was successfully destroyed.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE
 *        Either an invalid request handle or one which is actively being
 *        used was passed to this function as the @a request parameter.
 */
int
globus_gass_transfer_request_destroy(
    globus_gass_transfer_request_t		request)
{
    globus_gass_transfer_request_struct_t *	req;
    int						rc;

    globus_i_gass_transfer_lock();
    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;
	goto finish;
    }
    if(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FAILED &&
       req->status != GLOBUS_GASS_TRANSFER_REQUEST_DONE &&
       req->status != GLOBUS_GASS_TRANSFER_REQUEST_FINISHING &&
       req->status != GLOBUS_GASS_TRANSFER_REQUEST_FAILING &&
       req->status != GLOBUS_GASS_TRANSFER_REQUEST_REFERRED &&
       req->status != GLOBUS_GASS_TRANSFER_REQUEST_REFERRING &&
       req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING &&
       req->status != GLOBUS_GASS_TRANSFER_REQUEST_DENIED)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;
	goto finish;
    }

    rc =  globus_i_gass_transfer_request_destroy(request);

 finish:
    globus_i_gass_transfer_unlock();
    return rc;
}
/*
 * Function: globus_gass_transfer_fail()
 * 
 * Description: User-triggered error. Signal failure to the
 *              protocol module, and call any oustanding callbacks
 * 
 * Parameters: 
 * 
 * Returns: 
 */
int
globus_gass_transfer_fail(
    globus_gass_transfer_request_t		request,
    globus_gass_transfer_callback_t		callback,
    void *					callback_arg)
{
    globus_gass_transfer_request_struct_t *	req;
    int						rc = GLOBUS_SUCCESS;

    globus_i_gass_transfer_lock();
    req = globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				     request);

    if(req == GLOBUS_NULL)
    {
        rc = GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;

	goto finish;
    }
    if(callback == GLOBUS_NULL)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_NULL_POINTER;

	goto finish;
    }

    rc = globus_i_gass_transfer_fail(request,
				     req,
				     callback,
				     callback_arg);
  finish:
    globus_i_gass_transfer_unlock();
    return rc;
}
/*
 * Function: globus_i_gass_transfer_send_disaptcher()
 * 
 * Description: if the head of the pending fifo should be
 *              sent over, send it.
 * 
 * Parameters: 
 * 
 * Returns: 
 */
void
globus_i_gass_transfer_send_dispatcher(
    globus_gass_transfer_request_t		request)
{
    globus_gass_transfer_pending_t *		head;
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);

    if(req == GLOBUS_NULL)
    {
	return;
    }
    /* If we are not in the PENDING state, we should not look at the queue */
    if(req->status != GLOBUS_GASS_TRANSFER_REQUEST_PENDING)
    {
	return;
    }

    /* If the fifo is empty, there is nothing to do */
    if(globus_fifo_empty(&req->pending_data))
    {
	return;
    }
    head = globus_fifo_peek(&req->pending_data);

    if(head->pending == GLOBUS_TRUE)
    {
	/*
	 * If the first in the fifo has already been sent to
	 * the protocol module, there is nothing to do
	 */
	return;
    }
    else
    {
	head->pending = GLOBUS_TRUE;
        req->status = GLOBUS_GASS_TRANSFER_REQUEST_ACTING;

	globus_i_gass_transfer_unlock();
	req->proto->send_buffer(req->proto,
				request,
				head->bytes,
				head->length,
				head->last_data);
	globus_i_gass_transfer_lock();
    }
}
/**
 * Check the status of a request.
 * @ingroup globus_gass_transfer_request
 *
 * This function queries a request to determine the status of the request.
 * This function should be called after EOF has been reached, or after
 * the initial get, put, or append has returned or had it's callback function
 * called to determine if it is possible to procede, or whether the file
 * transfer was successfully processed.
 *
 * @param request
 *        The request handle to query.
 *
 * @return A #globus_gass_transfer_request_status_t indicating
 *         the current status of the request.
 */
globus_gass_transfer_request_status_t
globus_gass_transfer_request_get_status(
    globus_gass_transfer_request_t		request)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req != GLOBUS_NULL)
    {
	switch(req->status)
	{
	  case GLOBUS_GASS_TRANSFER_REQUEST_STARTING3:
	  case GLOBUS_GASS_TRANSFER_REQUEST_PENDING:
	  case GLOBUS_GASS_TRANSFER_REQUEST_ACTING:
	  case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING:
	  case GLOBUS_GASS_TRANSFER_REQUEST_ACCEPTING:
	    return GLOBUS_GASS_TRANSFER_REQUEST_PENDING;
	  case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING:
	  case GLOBUS_GASS_TRANSFER_REQUEST_FAILING:
	  case GLOBUS_GASS_TRANSFER_REQUEST_FAILED:
	  case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL1:
	  case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL2:
	  case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL3:
	  case GLOBUS_GASS_TRANSFER_REQUEST_USER_FAIL:
	    return GLOBUS_GASS_TRANSFER_REQUEST_FAILED;
	  case GLOBUS_GASS_TRANSFER_REQUEST_FINISHING:
	  case GLOBUS_GASS_TRANSFER_REQUEST_DONE:
	    return GLOBUS_GASS_TRANSFER_REQUEST_DONE;
	  case GLOBUS_GASS_TRANSFER_REQUEST_STARTING:
	  case GLOBUS_GASS_TRANSFER_REQUEST_STARTING2:
	    return GLOBUS_GASS_TRANSFER_REQUEST_STARTING;
	  case GLOBUS_GASS_TRANSFER_REQUEST_REFERRED:
	  case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING:
	  case GLOBUS_GASS_TRANSFER_REQUEST_REFERRING:
	    return GLOBUS_GASS_TRANSFER_REQUEST_REFERRED;
	  case GLOBUS_GASS_TRANSFER_REQUEST_DENIED:
	    return GLOBUS_GASS_TRANSFER_REQUEST_DENIED;
	  case GLOBUS_GASS_TRANSFER_REQUEST_INVALID:
	    return GLOBUS_GASS_TRANSFER_REQUEST_INVALID;
	}
    }
    return GLOBUS_GASS_TRANSFER_REQUEST_INVALID;
}
/**
 * Get the subject string associated with a request.
 * @ingroup globus_gass_transfer_request
 *
 * This function queries a request handle to determine the subject
 * identity of the client who initiated the request. 
 * The string must not be freed by the caller.
 *
 * @param request
 *        A handle to the request to query.
 *
 * @return A string containing the request initiator's subject identity.
 *         If the request handle is invalid or a credential was not used
 *         to initiate the request, this value will be GLOBUS_NULL.
 */
char *
globus_gass_transfer_request_get_subject(
    globus_gass_transfer_request_t		request)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return GLOBUS_NULL;
    }
    else
    {
	return req->subject;
    }
}
/**
 * Get an string describing why a request was denied.
 * @ingroup globus_gass_transfer_request
 *
 * This function queries a request which was denied by a server to
 * determine why it was denied. The denial reason will be expressed
 * as a response string. The string must be freed by the caller.
 *
 * @param request
 *        A handle to the request to query.
 *
 * @return A string indicating why the request
 *         was denied. If the request handle is invalid or the
 *         request was not denied, then this function returns GLOBUS_NULL.
 * @see globus_gass_transfer_request_get_denial_reason()
 */
char *
globus_gass_transfer_request_get_denial_message(
    globus_gass_transfer_request_t		request)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return GLOBUS_NULL;
    }
    else
    {
	return globus_libc_strdup(req->denial_message);
    }
}
/**
 * Get an integer code describing why the request was denied.
 * @ingroup globus_gass_transfer_request
 *
 * This function queries a request which was denied by a server to
 * determine why it was denied. The denial reason will be expressed
 * in a protocol-specific response code. Knowledge of the protocol
 * is needed to understand this response.
 *
 * @param request
 *        A handle to the request to query.
 *
 * @return A protocol-specific integer indicating why the request
 *         was denied. If the request handle is invalid or the
 *         request was not denied, then this function returns 0.
 * @see globus_gass_transfer_request_get_denial_message()
 */
int
globus_gass_transfer_request_get_denial_reason(
    globus_gass_transfer_request_t 		request)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return 0;
    }
    else
    {
	return req->denial_reason;
    }
}
/**
 * Determine the type of a request.
 * @ingroup globus_gass_transfer_request
 *
 * This function is used by GASS server implementations to discover what
 * type of operation the client is requesting for an URL.
 *
 * @param request
 *        The request to query.
 *
 * @return The @link #globus_gass_transfer_request_type_t type@endlink
 * of the request.
 */
globus_gass_transfer_request_type_t
globus_gass_transfer_request_get_type(
    globus_gass_transfer_request_t		request)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return GLOBUS_GASS_TRANSFER_REQUEST_TYPE_INVALID;
    }
    else
    {
	return req->type;
    }
}
/**
 * Get the length of a file to be transferred using GASS.
 * @ingroup globus_gass_transfer_request
 *
 * This function queries the request handle to determine the amount of
 * data that will be transferred to copy the URL. The length may be
 * @a GLOBUS_GASS_TRANSFER_LENGTH_UNKNOWN if the sender can not determine the length
 * before making or authorizing the request.
 *
 * @param request
 *        The request to query.
 *
 * @return The length of the file located at the request's URL, or
 *         @a GLOBUS_GASS_TRANSFER_LENGTH_UNKNOWN if that cannot be determined.
 */
globus_size_t
globus_gass_transfer_request_get_length(
    globus_gass_transfer_request_t		request)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return GLOBUS_GASS_TRANSFER_LENGTH_UNKNOWN;
    }
    else
    {
	return req->length;
    }
}
/**
 * Set the length of a transfer associated request handle.
 * @ingroup globus_gass_transfer_request
 *
 * This function modifies the given request handle so that it's length field
 * is set to give length parameter.
 *
 * This function must only be called by protocol modules when constructing
 * a request handle when receiving the response to a get request. This
 * function can only be called once per request handle.
 *
 * @param request
 *        A handle to the request to modify.
 * @param length
 *        The length of the file request.
 *
 * @retval GLOBUS_SUCCESS
 *         The URL was set for the request handle.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE
 *         The request handle was invalid, or the URL had already been set.
 */
void
globus_gass_transfer_request_set_length(
    globus_gass_transfer_request_t		request,
    globus_size_t				length)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return ;
    }
    else
    {
	req->length = length;
    }
}
/**
 * Extract referral information from a request handle.
 * @ingroup globus_gass_transfer_request
 *
 * This function queries the request handle to determine any referral
 * information that it contains. This function should only be called
 * on request handles in the GLOBUS_GASS_TRANSFER_REQUEST_REFERRED
 * state. If no referral information is stored in the request handle,
 * then the referral will be initialized to an empty referral.
 * The referral must be destroyed by calling
 * globus_gass_transfer_referral_destroy() by the caller.
 *
 * @param request
 *        The request handle to query.
 * @param referral
 *        A pointer to an uninitialized referral structure. It will be
 *        populated by calling this function.
 *
 * @retval GLOBUS_SUCCESS
 *         The referral was successfully extracted from the request
 *         handle.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_NULL_POINTER
 *         The referral pointer was GLOBUS_NULL;
 */
int
globus_gass_transfer_request_get_referral(
    globus_gass_transfer_request_t		request,
    globus_gass_transfer_referral_t *		referral)
{
    globus_gass_transfer_request_struct_t *	req;

    /* Sanity check of arguments */
    if(referral == GLOBUS_NULL)
    {
	return GLOBUS_GASS_TRANSFER_ERROR_NULL_POINTER;
    }
    /* Check for illegal handle */
    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;
    }
    else if(req->referral_count == 0)
    {
	referral->url = GLOBUS_NULL;
	referral->count = 0;

	return GLOBUS_SUCCESS;
    }
    else
    {
	globus_size_t				i;

	referral->url =
	    globus_malloc(sizeof(char *) * req->referral_count);

	for(i = 0; i < req->referral_count; i++)
	{
	    referral->url[i] = globus_libc_strdup(req->referral_url[i]);
	}
	referral->count = req->referral_count;

	return GLOBUS_SUCCESS;
    }
}
/**
 * Get the user pointer associated with a request
 * @ingroup globus_gass_transfer_request
 *
 * This function extracts the user pointer from a request handle. The
 * user-pointer may be used by the application which is generating or
 * servicing the request to store a pointer to any application-specific
 * piece of data.
 *
 * @param request
 *        The request to query.
 *
 * @return The user pointer's value.
 */
void *
globus_gass_transfer_request_get_user_pointer(
    globus_gass_transfer_request_t		request)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL||
       req->type == GLOBUS_GASS_TRANSFER_REQUEST_TYPE_INVALID)
    {
	return GLOBUS_NULL;
    }
    else
    {
	return req->user_pointer;
    }
}
/**
 * Set the user pointer associated with a request handle.
 * @ingroup globus_gass_transfer_request
 *
 * This function sets the user pointer from a request handle. The
 * user-pointer may be used by the application which is generating or
 * servicing the request to store a pointer to any application-specific
 * piece of data.
 *
 * @param request
 *        The request to modify.
 * @param user_pointer
 *        The new value of the user pointer for the request.
 *
 * @retval GLOBUS_SUCCES
 *         The user pointer's value was set.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE
 *         An invalid request handle was passed to this function
 */
int
globus_gass_transfer_request_set_user_pointer(
    globus_gass_transfer_request_t		request,
    void *					user_pointer)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;
    }
    else
    {
	req->user_pointer = user_pointer;
	return GLOBUS_SUCCESS;
    }
}
/**
 * Set the type of a request.
 * @ingroup globus_gass_transfer_request
 *
 * This function modifies a request handle by setting the type of
 * operation that it is being used for. This function may only be
 * called once per handle, and only from a GASS protocol module
 * implementation.
 *
 * @param request
 *        The request handle to modify.
 * @param type
 *        The type of operation that this request handle will be used for.
 *
 * @retval GLOBUS_SUCCESS
 *         The request handle's type has been set.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE
 *         The request handle was invalid or it's type was already set.
 *         The request handle was not modified.
 *
 * @note Only GASS Protocol modules may call this function.
 */
int
globus_gass_transfer_request_set_type(
    globus_gass_transfer_request_t		request,
    globus_gass_transfer_request_type_t		type)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL||
       req->type != GLOBUS_GASS_TRANSFER_REQUEST_TYPE_INVALID)
    {
	return GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;
    }
    else
    {
	req->type = type;
	return GLOBUS_SUCCESS;
    }
}
/**
 * Set the URL to which a request handle refers.
 * @ingroup globus_gass_transfer_request
 *
 * This function modifies the given request handle so that it's URL field
 * is set to string pointed to by @a url.
 *
 * No copy is made of the string, so the caller must not free it. It must
 * be allocated by calling one of the memory allocators in globus_libc, as
 * it will be freed when the request handle is destroyed.
 *
 * This function must only be called by protocol modules when constructing
 * a request handle when accepting a new request. This function can only
 * be called once per request handle.
 *
 * @param request
 *        A handle to the request to modify.
 * @param url
 *        A string containing the URL that this request will be associated
 *        with.
 *
 * @retval GLOBUS_SUCCESS
 *         The URL was set for the request handle.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE
 *         The request handle was invalid, or the URL had already been set.
 */
int
globus_gass_transfer_request_set_url(
    globus_gass_transfer_request_t		request,
    char *					url)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL ||
       req->url != GLOBUS_NULL)
    {
	return GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;
    }
    else
    {
	req->url = url;
	return GLOBUS_SUCCESS;
    }
}
/**
 * @ingroup globus_gass_transfer_request
 */
int
globus_gass_transfer_request_set_subject(
    globus_gass_transfer_request_t		request,
    char *					subject)
{
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;
    }
    else if(req->subject != GLOBUS_NULL)
    {
	return GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;
    }
    else
    {
	req->subject = subject;
	return GLOBUS_SUCCESS;
    }
}
static
void
globus_l_gass_transfer_operation_complete(
    globus_gass_transfer_request_t		request,
    globus_byte_t *				bytes,
    globus_size_t				nbytes,
    globus_bool_t				failed,
    globus_bool_t				last_data,
    globus_gass_transfer_dispatch_func_t	dispatcher)
{
    globus_gass_transfer_request_struct_t *	req;
    globus_gass_transfer_pending_t *		head;
    globus_gass_transfer_callback_t		fail_callback=GLOBUS_NULL;
    void *					callback_arg;

    globus_i_gass_transfer_lock();
    req = globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				     request);

    if(req == GLOBUS_NULL)
    {
	goto finish;
    }

    switch(req->status)
    {
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING:
	if(! last_data)
	{
	    /*
	     * normal operation, go back to pending state, callback
	     * to user
	     */
	    req->status = GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING;

	    while(req->status == GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING ||
		  (
		      (req->status == GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING ||
		       req->status == GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING)
		   && !globus_fifo_empty(&req->pending_data)))
	    {
		head = globus_fifo_dequeue(&req->pending_data);

		/* Call back to user */
		globus_i_gass_transfer_unlock();
		head->callback(head->callback_arg,
			       request,
			       head->bytes,
			       nbytes,
			       last_data);
		globus_i_gass_transfer_lock();
		nbytes = 0;
		last_data = GLOBUS_TRUE;

		globus_free(head);

		if(req->status == GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING)
		{
		    req->status = GLOBUS_GASS_TRANSFER_REQUEST_PENDING;
		}
	    }
	    if(req->status == GLOBUS_GASS_TRANSFER_REQUEST_PENDING)
	    {
		/* dispatch next, if available */
		dispatcher(request);
		break;
	    }
	    else if(req->status == GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING)
	    {
		req->status = GLOBUS_GASS_TRANSFER_REQUEST_FAILED;
		fail_callback = req->fail_callback;
		callback_arg = req->fail_callback_arg;

		/* free up references to request and proto */
		req->proto->destroy(req->proto,
				    request);
		/* free up the GASS's reference to this request */
		globus_i_gass_transfer_request_destroy(request);
		
		globus_i_gass_transfer_unlock();
		if(fail_callback != GLOBUS_NULL)
		{
		    fail_callback(callback_arg,
				  request);
		}
		return;
	    }
	}
	else
	{
	    /* failed or done */
	    if(failed)
	    {
		req->status = GLOBUS_GASS_TRANSFER_REQUEST_FAILING;
		last_data = GLOBUS_TRUE;
	    }
	    else
	    {
		req->status = GLOBUS_GASS_TRANSFER_REQUEST_FINISHING;
	    }

	    while(!globus_fifo_empty(&req->pending_data))
	    {
		head = globus_fifo_dequeue(&req->pending_data);

		/* Call back to user */
		globus_i_gass_transfer_unlock();
		head->callback(head->callback_arg,
			       request,
			       head->bytes,
			       nbytes,
			       last_data);

		globus_i_gass_transfer_lock();

		nbytes = 0;
		globus_free(head);
	    }
	    fail_callback = req->fail_callback;
	    callback_arg = req->fail_callback_arg;

	    /* free up references to request and proto */
	    req->proto->destroy(req->proto,
				request);
	    /* free up the proto's and GASS's reference to this request */
	    globus_i_gass_transfer_request_destroy(request);

	    if(globus_i_gass_transfer_deactivating)
	    {
		globus_i_gass_transfer_request_destroy(request);
	    }

	    globus_i_gass_transfer_unlock();
	    if(fail_callback != GLOBUS_NULL)
	    {
		fail_callback(callback_arg,
			      request);
	    }
	    return;
	}

	break;
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING:
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_FAILING;
	last_data = GLOBUS_TRUE;

	while(!globus_fifo_empty(&req->pending_data))
	{
	    head = globus_fifo_dequeue(&req->pending_data);

	    /* Call back to user */
	    globus_i_gass_transfer_unlock();
	    head->callback(head->callback_arg,
			   request,
			   head->bytes,
			   nbytes,
			   last_data);

	    globus_free(head);

	    nbytes = 0;
	    globus_i_gass_transfer_lock();
	}
	fail_callback = req->fail_callback;
	callback_arg = req->fail_callback_arg;
	/* free up references to request and proto */
	req->proto->destroy(req->proto,
			    request);
	/* free up the proto's and GASS's reference to this request */
	globus_i_gass_transfer_request_destroy(request);

	globus_i_gass_transfer_unlock();
	fail_callback(callback_arg,
		      request);
	return;

      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING:
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_REFERRING;
	last_data = GLOBUS_TRUE;

	while(!globus_fifo_empty(&req->pending_data))
	{
	    head = globus_fifo_dequeue(&req->pending_data);

	    /* Call back to user */
	    globus_i_gass_transfer_unlock();
	    head->callback(head->callback_arg,
			   request,
			   head->bytes,
			   nbytes,
			   last_data);

	    globus_free(head);

	    nbytes = 0;
	    globus_i_gass_transfer_lock();
	}
	/* free up references to request and proto */
	req->proto->destroy(req->proto,
			    request);
	/* free up the proto's and GASS's reference to this request */
	globus_i_gass_transfer_request_destroy(request);

	globus_i_gass_transfer_unlock();

	return;

      case GLOBUS_GASS_TRANSFER_REQUEST_PENDING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FAILING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FAILED:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL1:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL2:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL3:
      case GLOBUS_GASS_TRANSFER_REQUEST_USER_FAIL:
      case GLOBUS_GASS_TRANSFER_REQUEST_REFERRED:
      case GLOBUS_GASS_TRANSFER_REQUEST_DENIED:
      case GLOBUS_GASS_TRANSFER_REQUEST_DONE:
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING:
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING2:
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING3:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACCEPTING:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FINISHING:
      case GLOBUS_GASS_TRANSFER_REQUEST_REFERRING:
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_PENDING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FAILING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FAILED);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL1);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL2);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL3);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_USER_FAIL);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_REFERRED);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_DENIED);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_DONE);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_STARTING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_STARTING2);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_STARTING3);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACCEPTING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FINISHING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_REFERRING);
	goto finish;
      case GLOBUS_GASS_TRANSFER_REQUEST_INVALID:
	goto finish;
    }

  finish:
    globus_i_gass_transfer_unlock();
    return;
}
/**
 * Request referred.
 * @ingroup globus_gass_transfer_protocol
 *
 * This function notifies the GASS Transfer Library that new request
 * generated by a client calling one of the functions in the
 * "@ref globus_gass_transfer_client" section of the manual has been
 * referred to another URL by the server, and so processing has stopped.
 *
 * @param request
 *        The request handle used for this request. This was created by
 *        the user calling one of the functions in the "@ref
 *        globus_gass_transfer_client" section of this manual.
 * @param url
 *        An array of url strings containing alternate locations for this
 *        file. The GASS transfer library is responsible for freeing this
 *        array. It must be allocated using one of the
 *        memory allocators defined in the Globus Common Library.
 * @param num_urls
 *        The length of the @a url array.
 *
 * @see globus_gass_transfer_proto_request_ready(),
 *      globus_gass_transfer_proto_request_denied(),
 *      globus_gass_transfer_proto_request_referred()
 */
void
globus_gass_transfer_proto_request_referred(
    globus_gass_transfer_request_t		request,
    char **					url,
    globus_size_t				num_urls)
{
    globus_gass_transfer_request_struct_t *	req;
    globus_gass_transfer_callback_t		callback;
    void *					callback_arg;
    globus_size_t				i;
    globus_gass_transfer_pending_t *		head;

    globus_i_gass_transfer_lock();
    req = globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				     request);

    if(req == GLOBUS_NULL)
    {
	goto finish;
    }
    switch(req->status)
    {
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING:
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_REFERRED;
	req->referral_url = url;
	req->referral_count = num_urls;

	callback = req->callback;
	callback_arg = req->callback_arg;

	globus_i_gass_transfer_unlock();
	callback(callback_arg,
		 request);
	globus_i_gass_transfer_lock();

	/* free up GASS's reference to the request */
	globus_i_gass_transfer_request_destroy(request);

	break;

      case GLOBUS_GASS_TRANSFER_REQUEST_USER_FAIL:
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_FAILED;
	req->referral_url = url;
	req->referral_count = num_urls;

	callback = req->callback;
	callback_arg = req->callback_arg;

	globus_i_gass_transfer_unlock();
	callback(callback_arg,
		 request);
	globus_i_gass_transfer_lock();

	/* free up GASS's reference to the request */
	globus_i_gass_transfer_request_destroy(request);

	break;
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING:
	/* request is in progress, when operation completes,
	 * the callback queue will be drained
	 */
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING;
	req->referral_url = url;
	req->referral_count = num_urls;

	break;


      case GLOBUS_GASS_TRANSFER_REQUEST_PENDING:
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_REFERRING;
	
	while(!globus_fifo_empty(&req->pending_data))
	{
	    head = globus_fifo_dequeue(&req->pending_data);

	    /* Call back to user */
	    globus_i_gass_transfer_unlock();
	    head->callback(head->callback_arg,
			   request,
			   head->bytes,
			   0,
			   GLOBUS_TRUE);
	    globus_i_gass_transfer_lock();

	    globus_free(head);

	    req->status = GLOBUS_GASS_TRANSFER_REQUEST_REFERRED;
	}
	/* free up references to request and proto */
	req->proto->destroy(req->proto,
			    request);
	/* free up the GASS's reference to this request */
	globus_i_gass_transfer_request_destroy(request);
	
	break;

      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING:
	/* user callback in progress */
        req->status = GLOBUS_GASS_TRANSFER_REQUEST_REFERRING;
	req->referral_url = url;
	req->referral_count = num_urls;

	/* callbacks are going to occur after the current
	 * one completes (in the operation_complete function
	 * above)
	 */
	break;

      case GLOBUS_GASS_TRANSFER_REQUEST_FAILED:
      case GLOBUS_GASS_TRANSFER_REQUEST_REFERRED:
      case GLOBUS_GASS_TRANSFER_REQUEST_DENIED:
      case GLOBUS_GASS_TRANSFER_REQUEST_DONE:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL1:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL2:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL3:
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING2:
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING3:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACCEPTING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FAILING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FINISHING:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING:
        /* free urls, no state change */
        goto free_urls;

      case GLOBUS_GASS_TRANSFER_REQUEST_REFERRING:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING:
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_REFERRING);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING);
        goto free_urls;
	
      case GLOBUS_GASS_TRANSFER_REQUEST_INVALID:
	goto finish;
    }
  finish:
    globus_i_gass_transfer_unlock();
    return;

  free_urls:
    for(i = 0; i < num_urls; i++)
    {
	globus_free(url[i]);
    }
    globus_free(url);

    return;
}
/**
 * Request denied.
 * @ingroup globus_gass_transfer_protocol
 *
 * This function notifies the GASS Transfer Library that new request
 * generated by a client calling one of the functions in the
 * "@ref globus_gass_transfer_client" section of the manual has been
 * denied by the server, and so cannot be processed by
 * the protocol module.
 *
 * @param request
 *        The request handle used for this request. This was created by
 *        the user calling one of the functions in the "@ref
 *        globus_gass_transfer_client" section of this manual.
 * @param reason
 *        A protocol-specific reason code.
 * @param message
 *        A string containing a message describing why the request
 *        was denied. The GASS Transfer library is responsible for
 *        freeing this message. It must be allocated using one of the
 *        memory allocators defined in the Globus Common Library.
 *
 * @see globus_gass_transfer_proto_request_ready(),
 *      globus_gass_transfer_proto_request_referred()
 */
void
globus_gass_transfer_proto_request_denied(
    globus_gass_transfer_request_t		request,
    int						reason,
    char *					message)
{
    globus_gass_transfer_request_struct_t *	req;
    globus_gass_transfer_callback_t		callback;
    void *					callback_arg;

    globus_i_gass_transfer_lock();
    req = globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				     request);

    if(req == GLOBUS_NULL)
    {
	goto finish;
    }
    switch(req->status)
    {
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING:
      case GLOBUS_GASS_TRANSFER_REQUEST_USER_FAIL:
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_DENIED;

	req->denial_reason = reason;
	req->denial_message = message;

	callback = req->callback;
	callback_arg = req->callback_arg;

	globus_i_gass_transfer_unlock();
	callback(callback_arg,
		 request);
	globus_i_gass_transfer_lock();

	/* free up proto's and GASS's reference to the request */
	globus_i_gass_transfer_request_destroy(request);

	break;
      case GLOBUS_GASS_TRANSFER_REQUEST_REFERRED:
      case GLOBUS_GASS_TRANSFER_REQUEST_REFERRING:
      case GLOBUS_GASS_TRANSFER_REQUEST_DENIED:
      case GLOBUS_GASS_TRANSFER_REQUEST_DONE:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING:
      case GLOBUS_GASS_TRANSFER_REQUEST_PENDING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FAILED:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL1:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL2:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL3:
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING2:
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING3:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACCEPTING:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FAILING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FINISHING:
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_REFERRED);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_REFERRING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_DENIED);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_DONE);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_PENDING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FAILED);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL1);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL2);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL3);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_STARTING2);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_STARTING3);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACCEPTING);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FAILING);
        globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FINISHING);
	goto finish;
      case GLOBUS_GASS_TRANSFER_REQUEST_INVALID:
	goto finish;
    }

  finish:
    globus_i_gass_transfer_unlock();
    return;
}
/**
 * New listener request.
 * @ingroup globus_gass_transfer_protocol
 *
 * This function notifies the GASS Transfer Library that new request
 * generated by a server calling the globus_gass_transfer_register_accept()
 * function has begun processing in a protocol module,
 * and that protocol module is now ready to send or receive data to handle
 * this request. 
 *
 * @param listener
 *        The listener handle used to accept this request.
 * @param request
 *        The request handle used for this request. This was created by
 *        the user calling one of the functions in the "@ref
 *        globus_gass_transfer_client" section of this manual.
 * @param proto
 *        The protocol-module specific request structure. This structure
 *        contains a set of function pointers to allow GASS to continue
 *        to process this request.
 */
void
globus_gass_transfer_proto_new_listener_request(
    globus_gass_transfer_listener_t		listener,
    globus_gass_transfer_request_t		request,
    globus_gass_transfer_request_proto_t *	proto)
{
    globus_gass_transfer_listener_struct_t *	l;
    globus_gass_transfer_request_struct_t *	req;

    globus_i_gass_transfer_lock();
    l = globus_handle_table_lookup(&globus_i_gass_transfer_listener_handles,
				   listener);

    if(l == GLOBUS_NULL)
    {
	globus_i_gass_transfer_unlock();
	return;
    }

    req = globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				     request);
    if(req == GLOBUS_NULL)
    {
	globus_i_gass_transfer_unlock();
	return;
    }

    req->proto = proto;

    switch(l->status)
    {
      case GLOBUS_GASS_TRANSFER_LISTENER_ACCEPTING:
	l->status = GLOBUS_GASS_TRANSFER_LISTENER_STARTING;

	if(proto == GLOBUS_NULL)
	{
	    req->status = GLOBUS_GASS_TRANSFER_REQUEST_FAILED;

            /* Destroy GASS's reference to this request proto */
            globus_i_gass_transfer_request_destroy(request);
	}
	else
	{
	    req->status = GLOBUS_GASS_TRANSFER_REQUEST_STARTING2;

	    globus_assert(req->type !=
			  GLOBUS_GASS_TRANSFER_REQUEST_TYPE_INVALID);
	}

	/* Callback to user, regarding this request */
	globus_i_gass_transfer_unlock();
	req->callback(req->callback_arg,
		      request);
	return;
      case GLOBUS_GASS_TRANSFER_LISTENER_CLOSING2:
	l->status = GLOBUS_GASS_TRANSFER_LISTENER_CLOSED;
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_FAILED;

	/* Callback to user, regarding this request */
	globus_i_gass_transfer_unlock();

	req->callback(req->callback_arg,
		      request);
	globus_i_gass_transfer_lock();
	/*
	 * Fail and destroy this request, since the user
	 * has called the close function on this listener
	 */
	if(req->proto)
	{
	    req->proto->fail(req->proto,
			     request);
	    req->proto->destroy(req->proto,
				request);
	}
	
	/* Destroy GASS's reference to this request proto */
	globus_i_gass_transfer_request_destroy(request);

	/* Callback to user, regarding this listener */
	globus_i_gass_transfer_unlock();
	l->close_callback(l->close_callback_arg,
			  listener);

	l->proto->destroy(l->proto,
			  listener);

	globus_i_gass_transfer_lock();

	/* Destroy GASS's reference to this listener */
	globus_i_gass_transfer_listener_destroy(listener);
	globus_i_gass_transfer_listener_destroy(listener);

	globus_i_gass_transfer_unlock();

	return;
      case GLOBUS_GASS_TRANSFER_LISTENER_STARTING:
      case GLOBUS_GASS_TRANSFER_LISTENER_INVALID:
      case GLOBUS_GASS_TRANSFER_LISTENER_READY:
      case GLOBUS_GASS_TRANSFER_LISTENER_LISTENING:
      case GLOBUS_GASS_TRANSFER_LISTENER_CLOSING1:
      case GLOBUS_GASS_TRANSFER_LISTENER_CLOSED:
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_STARTING);
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_INVALID);
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_READY);
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_LISTENING);
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_CLOSING1);
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_CLOSED);
    }

    globus_i_gass_transfer_unlock();

    return;
}
/**
 * Request ready.
 * @ingroup globus_gass_transfer_protocol
 *
 * This function notifies the GASS Transfer Library that new request
 * generated by a client has begun processing in a protocol module,
 * and that protocol module is now ready to send or receive data to handle
 * this request. 
 *
 * @param request
 *        The request handle used for this request. This was created by
 *        the user calling one of the functions in the "@ref
 *        globus_gass_transfer_client" section of this manual.
 * @param proto
 *        The protocol-module specific request structure. This structure
 *        contains a set of function pointers to allow GASS to continue
 *        to process this request.
 *
 * @see globus_gass_transfer_proto_request_referred(),
 *      globus_gass_transfer_proto_request_denied()
 */
void
globus_gass_transfer_proto_request_ready(
    globus_gass_transfer_request_t		request,
    globus_gass_transfer_request_proto_t *	proto)
{
    globus_gass_transfer_request_struct_t *	req;
    globus_gass_transfer_callback_t		callback;
    void *					callback_arg;

    globus_i_gass_transfer_lock();
    req = globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				     request);

    if(req == GLOBUS_NULL)
    {
	goto finish;
    }
    switch(req->status)
    {
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING3:
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_PENDING;
	req->proto = proto;

	if(req->type == GLOBUS_GASS_TRANSFER_REQUEST_TYPE_PUT ||
	   req->type == GLOBUS_GASS_TRANSFER_REQUEST_TYPE_APPEND)
	{
	    globus_i_gass_transfer_recv_dispatcher(request);
	}
	else
	{
	    globus_i_gass_transfer_send_dispatcher(request);
	}
	globus_i_gass_transfer_unlock();
	return;
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING:
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_PENDING;
	req->proto = proto;

	callback = req->callback;
	callback_arg = req->callback_arg;

	globus_i_gass_transfer_unlock();
	callback(callback_arg,
		 request);
	return;
      case GLOBUS_GASS_TRANSFER_REQUEST_USER_FAIL:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL3:
	req->status = GLOBUS_GASS_TRANSFER_REQUEST_FAILED;
	req->proto = proto;

	callback = req->callback;
	callback_arg = req->callback_arg;

	globus_i_gass_transfer_unlock();
	callback(callback_arg,
		 request);
	globus_i_gass_transfer_lock();
	/* free up references to request and proto */
	req->proto->destroy(req->proto,
			    request);
	/* free up GASS's reference to this request */
	globus_i_gass_transfer_request_destroy(request);
	break;
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING:
      case GLOBUS_GASS_TRANSFER_REQUEST_PENDING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FAILED:
      case GLOBUS_GASS_TRANSFER_REQUEST_REFERRING:
      case GLOBUS_GASS_TRANSFER_REQUEST_REFERRED:
      case GLOBUS_GASS_TRANSFER_REQUEST_DENIED:
      case GLOBUS_GASS_TRANSFER_REQUEST_DONE:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL1:
      case GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL2:
      case GLOBUS_GASS_TRANSFER_REQUEST_STARTING2:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACCEPTING:
      case GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FAILING:
      case GLOBUS_GASS_TRANSFER_REQUEST_FINISHING:
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_REFERRING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_FAILING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_PENDING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FAILED);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_REFERRING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_REFERRED);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_DENIED);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_DONE);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL1);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_SERVER_FAIL2);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_STARTING2);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACCEPTING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_ACTING_TO_PENDING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FAILING);
	globus_assert(req->status != GLOBUS_GASS_TRANSFER_REQUEST_FINISHING);
	/* FALLSTHROUGH */
      case GLOBUS_GASS_TRANSFER_REQUEST_INVALID:
	goto finish;
    }

  finish:
    globus_i_gass_transfer_unlock();
    return;
}
/**
 * Server listener ready.
 * @ingroup globus_gass_transfer_protocol
 *
 * This function notifies the GASS Transfer Library that the protocol module
 * has decided that a new request can be accepted on this particular listener.
 * It must only be called after the GASS Transfer Library has called the
 * @link globus_gass_transfer_listener_proto_t::listen listen function@endlink
 * in a #globus_gass_transfer_listener_proto_t protocol module-specific
 * listener structure.
 *
 * @param listener
 *        The listener handle which is now ready for accepting a new
 *        connection.
 */
void
globus_gass_transfer_proto_listener_ready(
    globus_gass_transfer_listener_t		listener)
{
    globus_gass_transfer_listener_struct_t *	l;
    globus_gass_transfer_listen_callback_t	callback;
    void *					callback_arg;
    globus_gass_transfer_listen_callback_t	close_callback = GLOBUS_NULL;
    void *					close_callback_arg;

    globus_i_gass_transfer_lock();
    l = globus_handle_table_lookup(&globus_i_gass_transfer_listener_handles,
				   listener);

    if(l == GLOBUS_NULL)
    {
	goto error_exit;
    }
    switch(l->status)
    {
      case GLOBUS_GASS_TRANSFER_LISTENER_INVALID:
	goto error_exit;
      case GLOBUS_GASS_TRANSFER_LISTENER_LISTENING:
	l->status = GLOBUS_GASS_TRANSFER_LISTENER_READY;
	callback = l->listen_callback;
	callback_arg = l->listen_callback_arg;
	globus_i_gass_transfer_unlock();

	callback(callback_arg,
		 listener);

	return;
      case GLOBUS_GASS_TRANSFER_LISTENER_CLOSING1:
	l->status = GLOBUS_GASS_TRANSFER_LISTENER_CLOSED;
	callback = l->listen_callback;
	callback_arg = l->listen_callback_arg;

	close_callback = l->close_callback;
	close_callback_arg = l->close_callback_arg;
	
	/* Destroy our reference to the proto */
	l->proto->destroy(l->proto,
			  listener);
	/*
	 * Destroy GASS's reference
	 * to this listener
	 */
	globus_i_gass_transfer_listener_destroy(listener);

	globus_i_gass_transfer_unlock();

	callback(callback_arg,
		 listener);
	if(close_callback)
	{
	    close_callback(close_callback_arg,
			   listener);
	}
	globus_i_gass_transfer_lock();
	globus_i_gass_transfer_listener_destroy(listener);
	globus_i_gass_transfer_unlock();
	return;

      case GLOBUS_GASS_TRANSFER_LISTENER_READY:
      case GLOBUS_GASS_TRANSFER_LISTENER_STARTING:
      case GLOBUS_GASS_TRANSFER_LISTENER_ACCEPTING:
      case GLOBUS_GASS_TRANSFER_LISTENER_CLOSING2:
      case GLOBUS_GASS_TRANSFER_LISTENER_CLOSED:
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_READY);
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_STARTING);
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_ACCEPTING);
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_CLOSING2);
	globus_assert(l->status != GLOBUS_GASS_TRANSFER_LISTENER_CLOSED);
	break;
    }
    
    globus_i_gass_transfer_unlock();
    return;
  error_exit:
    globus_i_gass_transfer_unlock();
}
static
void
globus_l_gass_transfer_drain_callbacks(
    void *					arg)
{
    globus_gass_transfer_request_t		request;
    globus_gass_transfer_request_struct_t *	req;
    globus_gass_transfer_callback_t		callback;
    void *					callback_arg;

    request = (globus_gass_transfer_request_t) arg;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return;
    }

    if(globus_i_gass_transfer_deactivating)
    {
	callback = globus_i_gass_transfer_deactivate_callback;
	callback_arg = GLOBUS_NULL;
    }
    else
    {
	callback = req->fail_callback;
	callback_arg = req->fail_callback_arg;
    }

    /* drain queue of pending data requests */
    while(!globus_fifo_empty(&req->pending_data))
    {
	globus_gass_transfer_pending_t *	pending;
	
	pending = globus_fifo_dequeue(&req->pending_data);
	
	if(!globus_i_gass_transfer_deactivating)
	{
	    globus_i_gass_transfer_unlock();
	    pending->callback(pending->callback_arg,
			      request,
			      pending->bytes,
			      0,
			      GLOBUS_TRUE);
	    globus_i_gass_transfer_lock();
	}
	globus_free(pending);
    }

    /* free up references to request and proto */
    req->proto->destroy(req->proto,
			request);
    /* free up GASS's reference to this request */
    globus_i_gass_transfer_request_destroy(request);

    if(callback)
    {
	callback(callback_arg,
		 request);
    }
}
/**
 * Receive a byte array associated with a request handle.
 * @ingroup globus_gass_transfer_data
 *
 * This function receives a block of data from a server or client as
 * part of the processing for a request. Multiple data blocks may be
 * registered with the GASS transfer library at once.
 *
 * When processing a server request, this function may only be used in
 * conjunction with "put" or "append" requests. The user must call
 * globus_gass_transfer_authorize() before calling this function.
 *
 * When processing a client request, this function may only be used in
 * conjunction with "get" requests. This function may not
 * be called before either the callback function has been invoked, or the
 * blocking globus_gass_tranfser_put() or globus_gass_transfer_append()
 * function has returned.
 *
 * @param request
 *        The request handle with which this block of bytes is associated.
 * @param bytes
 *        A user-supplied buffer containing the data associated with the
 *        request.
 * @param max_length
 *        The length of the @a bytes array.
 * @param wait_for_length
 *        The minimum amount of data to wait for before invoking the @a
 *        callback function. A partial byte array of at least this amount
 *        will be returned in the callback, unless end-of-file is reached
 *        before this amount.
 * @param callback
 *        Function to call once the @a bytes array has been received.
 * @param user_arg
 *        Argument to be passed to the @a callback function.
 *
 * @retval GLOBUS_SUCCESS
 *         The @a bytes array was successfully registered with the GASS
 *         transfer library. The @a callback function will be invoked once
 *         it has been received.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_NULL_POINTER
 *         The @a bytes or @a callback parameter was NULL.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_INVALID_USER
 *         The @a request was invalid, or it is not one on which bytes
 *         can be sent.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_NOT_INITIALIZED
 *         The callback to a non-blocking file request has not been invoked
 *         yet, a blocking file request has not returned, or the request has
 *         not yet been authorized.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_REQUEST_FAILED
 *         The @a request has failed by either the client, server, or protocol
 *         module implementation.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_DONE
 *         The @a request has already been completed.
 */
int
globus_gass_transfer_receive_bytes(
    globus_gass_transfer_request_t		request,
    globus_byte_t *				bytes,
    globus_size_t				max_length,
    globus_size_t				wait_for_length,
    globus_gass_transfer_bytes_callback_t	callback,
    void *					user_arg)
{
    int						rc;
    globus_gass_transfer_pending_t *		pending;
    globus_gass_transfer_request_struct_t *	req;

    globus_i_gass_transfer_lock();

    /* Sanity check on passed arguments */
    if(bytes == GLOBUS_NULL)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_NULL_POINTER;

	goto error_exit;
    }
    if(callback == GLOBUS_NULL)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_NULL_POINTER;

	goto error_exit;
    }
    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);

    if(req == GLOBUS_NULL)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;

	goto error_exit;
    }
    /*
     * Verify that the request is in a state that allows new data
     * blocks to be received
     */
    rc = globus_l_gass_transfer_state_check(req);
    if(rc != GLOBUS_SUCCESS)
    {
	goto error_exit;
    }

    if(req->client_side == GLOBUS_FALSE &&
	    req->type != GLOBUS_GASS_TRANSFER_REQUEST_TYPE_PUT &&
	    req->type != GLOBUS_GASS_TRANSFER_REQUEST_TYPE_APPEND)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;

	goto error_exit;
    }
    else if(req->client_side != GLOBUS_FALSE &&
	    req->type != GLOBUS_GASS_TRANSFER_REQUEST_TYPE_GET)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;

	goto error_exit;
    }
    /*
     * Create a pending data structure, to be queued up in the request's
     * fifo.
     */
    pending = (globus_gass_transfer_pending_t *)
	globus_malloc(sizeof(globus_gass_transfer_pending_t));

    if(pending == GLOBUS_NULL)
    {
        rc = GLOBUS_GASS_TRANSFER_ERROR_MALLOC_FAILED;
	goto error_exit;
    }
    pending->last_data		= GLOBUS_FALSE;
    pending->length		= max_length;
    pending->wait_for_length	= wait_for_length;
    pending->pending		= GLOBUS_FALSE;
    pending->request		= request;
    pending->bytes		= bytes;
    pending->callback		= callback;
    pending->callback_arg	= user_arg;

    globus_fifo_enqueue(&req->pending_data,
			pending);

    /*
     * Call the recv dispatcher to (maybe) receive some more data from the
     * protocol module.
     */
    globus_i_gass_transfer_recv_dispatcher(request);

    globus_i_gass_transfer_unlock();
    return GLOBUS_SUCCESS;

  error_exit:
    globus_i_gass_transfer_unlock();
    return rc;
}
/**
 * Send a byte array associated with a request handle.
 * @ingroup globus_gass_transfer_data
 *
 * This function sends a block of data to a server or client as
 * part of the processing for a request. Multiple data blocks may be
 * registered with the GASS transfer library at once.
 *
 * When processing a server request, this function may only be used in
 * conjunction with "get" requests. The user must call
 * globus_gass_transfer_authorize() before calling this function.
 *
 * When processing a client request, this function may only be used in
 * conjunction with "put" or "append" requests. This function may not
 * be called before either the callback function has been invoked, or the
 * blocking globus_gass_tranfser_put() or globus_gass_transfer_append()
 * function has returned.
 *
 * @param request
 *        The request handle with which this block of bytes is associated.
 * @param bytes
 *        A user-supplied buffer containing the data associated with the
 *        request.
 * @param send_length
 *        The length of the @a bytes array.
 * @param last_data
 *        A flag to indicate whether this is the final block of data
 *        for the request. If this is true, then the @a callback
 *        function will be delayed until the server acknowledges that
 *        the file has been completely received.
 * @param callback
 *        Function to call once the @a bytes array has been sent.
 * @param user_arg
 *        Argument to be passed to the @a callback function.
 *
 * @retval GLOBUS_SUCCESS
 *         The @a bytes array was successfully registered with the GASS
 *         transfer library. The @a callback function will be invoked once
 *         it has been sent.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_NULL_POINTER
 *         The @a bytes or @a callback parameter was NULL.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_INVALID_USER
 *         The @a request was invalid, or it is not one on which bytes
 *         can be sent.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_NOT_INITIALIZED
 *         The callback to a non-blocking file request has not been invoked
 *         yet, a blocking file request has not returned, or the request has
 *         not yet been authorized.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_REQUEST_FAILED
 *         The @a request has failed by either the client, server, or protocol
 *         module implementation.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_DONE
 *         The @a request has already been completed.
 */
int
globus_gass_transfer_send_bytes(
    globus_gass_transfer_request_t		request,
    globus_byte_t *				bytes,
    globus_size_t				send_length,
    globus_bool_t				last_data,
    globus_gass_transfer_bytes_callback_t	callback,
    void *					user_arg)
{
    int						rc;
    globus_gass_transfer_pending_t *		pending;
    globus_gass_transfer_request_struct_t *	req;

    globus_i_gass_transfer_lock();

    /* Sanity check on passed arguments */
    if(bytes == GLOBUS_NULL)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_NULL_POINTER;

	goto error_exit;
    }
    if(callback == GLOBUS_NULL)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_NULL_POINTER;

	goto error_exit;
    }
    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);

    if(req == GLOBUS_NULL)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;

	goto error_exit;
    }
    else if(req->client_side == GLOBUS_FALSE &&
	    req->type != GLOBUS_GASS_TRANSFER_REQUEST_TYPE_GET)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;

	goto error_exit;
    }
    else if(req->client_side != GLOBUS_FALSE &&
	    req->type != GLOBUS_GASS_TRANSFER_REQUEST_TYPE_PUT &&
	    req->type != GLOBUS_GASS_TRANSFER_REQUEST_TYPE_APPEND)
    {
	rc = GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;

	goto error_exit;
    }

    /*
     * Verify that the request is in a state that allows new data
     * blocks to be sent
     */
    rc = globus_l_gass_transfer_state_check(req);
    if(rc != GLOBUS_SUCCESS)
    {
	goto error_exit;
    }

    /*
     * Verify that the sending this amount of data won't overflow the
     * original request size. 
     */
    rc = globus_l_gass_transfer_size_check(req,
					   send_length);
    if(rc != GLOBUS_SUCCESS)
    {
	goto error_exit;
    }

    /*
     * Create a pending data structure, to be queued up in the request's
     * fifo.
     */
    pending = (globus_gass_transfer_pending_t *)
	globus_malloc(sizeof(globus_gass_transfer_pending_t));

    if(pending == GLOBUS_NULL)
    {
        rc = GLOBUS_GASS_TRANSFER_ERROR_MALLOC_FAILED;
	goto error_exit;
    }
    pending->last_data		= last_data;
    pending->length		= send_length;
    pending->pending		= GLOBUS_FALSE;
    pending->request		= request;
    pending->bytes		= bytes;
    pending->callback		= callback;
    pending->callback_arg	= user_arg;

    /*
     * Posted length is the total amount of data which has been queued
     * for this request. It is used for detecting overflows.
     */
    req->posted_length += send_length;
    globus_fifo_enqueue(&req->pending_data,
			pending);

    /*
     * Call the send dispatcher to (maybe) send some more data to the
     * protocol module to send over the connection.
     */
    globus_i_gass_transfer_send_dispatcher(request);

    globus_i_gass_transfer_unlock();
    return GLOBUS_SUCCESS;

  error_exit:
    globus_i_gass_transfer_unlock();
    return rc;
}
/**
 * Dereference a request handle.
 *
 * This function decreases the reference count on an GASS Transfer
 * request handle. If the reference count becomes zero, then the
 * #globus_gass_transfer_request_struct_t associated with the handle
 * is destroyed.
 *
 * @note This function must be called with the request handle mutex locked.
 *
 * @param request
 *        The request to destroy.
 *
 * @retval GLOBUS_SUCCESS
 *         The request handle's reference count was decremented. The request
 *         structure is freed if this was the final reference to the handle.
 * @retval GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE
 *         The request handle was not valid.
 * @see globus_gass_transfer_request_destroy()
 */
int
globus_i_gass_transfer_request_destroy(
    globus_gass_transfer_request_t		request)
{
    globus_bool_t				referenced;
    globus_gass_transfer_request_struct_t *	req;

    req =
	globus_handle_table_lookup(&globus_i_gass_transfer_request_handles,
				   request);
    if(req == GLOBUS_NULL)
    {
	return GLOBUS_GASS_TRANSFER_ERROR_INVALID_USE;
    }

    referenced =
	globus_handle_table_decrement_reference(&globus_i_gass_transfer_request_handles,
						request);
    if(!referenced)
    {
	int					i;
	globus_list_t *				tmp;

	tmp = globus_list_search(globus_i_gass_transfer_requests,
				 (void *) (intptr_t) request);
	
#if DEBUG_GASS_TRANSFER
	printf(_GTSL("removing from list\n"));
#endif
        if (tmp)
        {
            globus_list_remove(&globus_i_gass_transfer_requests,
                               tmp);

            globus_cond_signal(&globus_i_gass_transfer_shutdown_cond);
            
            if(req->attr)
            {
                globus_object_free(req->attr);
            }
            globus_fifo_destroy(&req->pending_data);
            if (req->url)
            {
                globus_free(req->url);
            }

            /* free referral */
            for(i = 0; i < req->referral_count; i++)
            {
                globus_free(req->referral_url[i]);
            }
            if(req->referral_url)
            {
                globus_free(req->referral_url);
            }
            req->referral_url = GLOBUS_NULL;
            req->referral_count = 0;

            /* free deny message */
            if(req->denial_message)
            {
                globus_free(req->denial_message);
            }

            /* free subject name */
            if(req->subject)
            {
                globus_free(req->subject);
            }

            globus_free(req);
            request = GLOBUS_NULL_HANDLE;
        }

	return GLOBUS_SUCCESS;
    }
    else
    {
	return GLOBUS_SUCCESS;
    }
}