/*
	wsrmapi.c

	WS-ReliableMessaging plugin.

	Implements the WS-RM logic for import/wsrm.h and import/wsrx.h

gSOAP XML Web services tools
Copyright (C) 2000-2010, Robert van Engelen, Genivia Inc., All Rights Reserved.
This part of the software is released under one of the following licenses:
GPL, the gSOAP public license, or Genivia's license for commercial use.
--------------------------------------------------------------------------------
gSOAP public license.

The contents of this file are subject to the gSOAP Public License Version 1.3
(the "License"); you may not use this file except in compliance with the
License. You may obtain a copy of the License at
http://www.cs.fsu.edu/~engelen/soaplicense.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.

The Initial Developer of the Original Code is Robert A. van Engelen.
Copyright (C) 2000-2010, Robert van Engelen, Genivia Inc., All Rights Reserved.
--------------------------------------------------------------------------------
GPL license.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA

Author contact information:
engelen@genivia.com / engelen@acm.org

This program is released under the GPL with the additional exemption that
compiling, linking, and/or using OpenSSL is allowed.

This program is released under the GPL with the additional exemption that
compiling, linking, and/or using OpenSSL is allowed.
--------------------------------------------------------------------------------
A commercial use license is available from Genivia, Inc., contact@genivia.com
--------------------------------------------------------------------------------
*/

/**

@mainpage

- @ref wsrm_0 documents the wsrm plugin for WS-ReliableMessaging support.
  Although wsrm uses the wsa plugin for WS-Addressing, there is no need to read
  the wsa plugin documentation because this part of the documentation is
  self-contained. WS-Addressing usage is mentioned when applicable.
- @ref wsa_0 documents the wsa plugin for WS-Addressing (2003/2004/2005
  standards) support. The wsa plugin is used by the wsrm plugin.

*/

/**

@page wsrm_0 The wsrm plugin for client applications and stand-alone services

@section wsrm_1 WS-ReliableMessaging Setup

The material in this section relates to the WS-ReliableMessaging and
WS-Addressing (2005) specifications.

To use the wsrm plugin:
-# Run wsdl2h -t typemap.dat on a WSDL of a service that requires
   WS-ReliableMessaging and WS-Addressing headers. The typemap.dat file
   included in the gSOAP package is used to recognize and translate header
   blocks.
-# Run soapcpp2 -a on the header file produced by wsdl2h. To enable
   reliable-messaging and addressing-based service operation selection, you
   MUST use soapcpp2 option -a. This allows the service to dispatch methods
   based on the WS-Addressing action information header value (when the wsa
   plugin is registered).
-# (Re-)compile and link stdsoap2.c/pp or libgsoap, (dom.c/pp when needed),
   plugin/wsrmapi.c, plugin/wsaapi.c, custom/duration.c, and the soapcpp2-
   generated source files.
-# Use the wsrm plugin API functions described below. The wsrm plugin uses the
   wsa plugin to implement the WS-Addressing 2005 operations. Both must be
   registered. The wsrm plugin API is self-contained. There is no need to use
   the wsa plugin API, unless WS-Addressing-specific headers must be added to
   messages.

An example wsrm client/server application can be found in samples/wsrm.

A gSOAP service definitions header file with a "wsrm import" to support
WS-ReliableMessaging is automatically generated by wsdl2h for a set of WSDLs
that use WS-ReliableMessaging. The wsdl2h-generated header file should be
further processed by soapcpp2 to generate the binding code. The wsrmapi.h and
wsrmapi.c implement the WS-ReliableMessaging API described in this document.

A wsdl2h-generated service definitions header file might include the following
imports, where the wsrm.h is mandatory to support WS-ReliableMessaging:

@code
#import "soap12.h"
#import "wsrm.h"
@endcode

The wsrm.h header file is imported from import/wsrm.h by soapcpp2. The wsrm.h
import can be manually added to enable WS-ReliableMessaging when needed.
The gSOAP service definitions header file is processed with soapcpp2 to
generate the client-side and/or server-side binding code.

Note that the wsrm.h and the WS-ReliableMessaging-dependent wsrx.h and wsa5.h
header files are located in the import directory of the gSOAP package. These
files define the WS-ReliableMessaging and WS-Addressing information header
elements and types. The wsrx.h header file defines the WS-ReliableMessaging
CreateSequence, CloseSequence, and TerminateSequence operations, as well as an
one-way SequenceAcknowledgement operation to accept acknowledgements. The
soap12.h header file enables SOAP 1.2 messaging.

For developers working on protocols: the WS-ReliableMessaging header blocks
in wsrm.h were generated from the WS-ReliableMessaging schema with the wsdl2h
tool and WS/WS-typemap.dat as follows:

@code
    > wsdl2h -cgyex -o wsrm.h -t WS/WS-typemap.dat WS/WS-ReliableMessaging.xsd
@endcode

This step is not needed to use the wsrm plugin.

@section wsrm_2 WS-ReliableMessaging Information Header Bindings

To associate WS-ReliableMessaging and WS-Addressing information headers with
service operations, the SOAP Header struct SOAP_ENV__Header must have been
declared and contain the necessary header blocks to be transported with SOAP
messages. The SOAP_ENV__Header for WS-ReliableMessaging and WS-Addressing is
predefined in wsrm.h and imported into the gSOAP service definitions header
file (this is automatically generated by wsdl2h). For each service operation in
the gSOAP service definitions header file that uses WS-ReliableMessaging and/or
WS-Addressing method-header-part directives are used.

For example, the following gSOAP service definitions header file illustrates a
typical import and service operation definition of operation 'example' in
service namespace 'ns':

@code
#import "wsrm.h"

//gsoap ns service method-header-part: example wsa5__MessageID
//gsoap ns service method-header-part: example wsa5__RelatesTo
//gsoap ns service method-header-part: example wsa5__From
//gsoap ns service method-header-part: example wsa5__ReplyTo
//gsoap ns service method-header-part: example wsa5__FaultTo
//gsoap ns service method-header-part: example wsa5__To
//gsoap ns service method-header-part: example wsa5__Action

//gsoap ns service method-header-part: example wsrm__Sequence
//gsoap ns service method-header-part: example wsrm__AckRequested
//gsoap ns service method-header-part: example wsrm__SequenceAcknowledgement

//gsoap ns service method-action:          example urn:example/examplePort/example
//gsoap ns service method-response-action: example urn:example/examplePort/exampleResponse

int ns__example(char *in, struct ns__exampleResponse { char *out; } *);
@endcode

The wsa5 information headers are defined in wsa5.h and imported by wsrm.h (both
are located in the 'import' directory). Here, all of the WS-Addressing and
WS-ReliableMessaging information headers are bound to the ns__example
operation request and response messages.

The method action directive is important for WS-Addressing, because
WS-Addressing Action information headers must be included that are unique for
each operation. The soapcpp2 option -a ensures that WS-Addressing Action header
blocks (and HTTP Action headers) are processed at the receiving side, which
means that the service dispatcher uses the Action together with the operation
name to invoke the service operation at the destination. This also means that
Action headers must be properly set by the client.

Note: the SOAP_ENV__Header struct can be declared in multiple files. The
soapcpp2 tool gathers all members of the structs into the "final"
SOAP_ENV__Header struct used by the gSOAP engine and your application. This is
convenient when service-specific header blocks are combined with
WS-ReliableMessaging and WS-Addressing header blocks or when WS-Security header
blocks are added by the WSSE plugin.

@section wsrm_3 WS-ReliableMessaging Overview

In this section a brief overview of WS-ReliableMessaging is given. For more
details please refer to the WS-ReliableMessaging protocol or tutorials on the
subject. The following introduces the basic concepts of WS-ReliableMessaging
from a practical point of view.

WS-ReliableMessaging is useful to improve the reliability of one-way
asynchronous messaging, for unreliable data gram messaging (SOAP-over-UDP), or
to improve reliable delivery of responses relayed to other destinations, such
as response messages that are relayed to destinations indicated by the
WS-Addressing ReplyTo header. The protocol is also useful when multiple sources
are sending messages that arrive in different order or must be flagged as an
incomplete message collection when messages are missing as defined by the
notion of a collection of related messages.

WS-ReliableMessaging is not essential to improve the reliability of
request-response message exchanges between two parties over HTTP, since a
successful delivery of a request message can be inferred from the fact that a
response was received for the request sent.

WS-ReliableMessaging "protects" message sequences, i.e. a collection of related
messages. A WS-ReliableMessaging message sequence is created after which the
sequence of messages is sent. The sequence is closed and terminated by the
client after the last message. Either the message sequence is complete or not,
and the resulting action to discard the incomplete message sequence or not
depends on the chosen behavior.  Duplicate messages (e.g. resulting from
resends) are always discarded.

To create a new sequence, a client (WS-RM source) requests from the
server (WS-RM destination) a unique (new) message sequence identification. The
server responds with the identification to be used as well as other details,
such as the expiration time of the sequence and the behavior implemented when a
sequence was received incomplete:

- NoDiscard means that the sequence of messages is not discarded by the
  destination server when one or more messages are missing; 
- DiscardFollowingFirstGap means that the initial messages of the sequence are
  retained by the destination up to the first gap (a missing message) in the
  sequence;
- DiscardEntireSequence means that the entire sequence of messages will be
  discarded when a single message is missing.

When the client terminates the sequence, it first sends a sequence close
request and then a sequence termination request to the destination server. The
sequence close informs the server how many messages should have been received.
The client can still resend messages after the close, but no new messages are
supposed to be send. After the optional resends, the client requests
termination of the sequence. The termination will be successful depending on
the behavior when messages went missing, as was listed above.

The ensure reliable delivery, the WS-ReliableMessaging protocol allows the
client to resend messages. Message resends are desirable when messages are lost
in transit. Since the client has limited information on delivery success
(message delivery acknowledgments can get lost as well), the client may resend
more messages than necessary. This could lead to message duplication. However,
messages that were already received by the server are discarded.

The client may request message delivery acknowledgements from the server. The
server sends message receipt acknowledgements for all the messages it has
received in the sequence back to the client, usually by piggy-backing them with
the response of a subsequent message exchange. When the client is informed
about successful delivery it reduces the number of resends the client will
attempt.

Messages in a sequence are uniquely identified by their enumeration number in
the sequence. Messages may be transmitted out of order. A missing message
number indicates a gap in the message sequence. Message receipt
acknowledgements consist of ranges of message numbers. Acknowledgements are
normally sent to the source to help identify which messages should be resend.

With the WS-Addressing protocol, message responses and fault messages can be
relayed to other destinations. The ReplyTo and FaultTo WS-Addressing header
blocks are used for this purpose. The WS-ReliableMessaging protocol allows
message acknowledgements to be relayed. The WS-ReliableMessaging AcksTo header
block is used for this purpose.

In all, there are four types of communicating peers that are visible to the
source (the client):

- The destination service. The WS-ReliableMessaging sequence is essentially
  controlled by this service. When a message is sent by the source to the
  destination service (over HTTP, TCP, or UDP), the WS-Addressing To
  information header may contain the endpoint address. The destination service
  normally returns message responses back to the client (HTTP
  request-response).
- One of more ReplyTo destination services that accept response messages from
  the destination service. Rather than sending responses back to the client,
  the destination service relays them to another service. The WS-Addressing
  ReplyTo information header is used by the client to indicate the response
  relay target.
- One or more FaultTo destination services that accept SOAP fault messages from
  the destination service. Rather than sending SOAP Faults back to the client,
  the destination service relays them to another service. The WS-Addressing
  FaultTo information header is used by the client to indicate the fault
  relay target. 
- One AcksTo destination service that accept WS-ReliableMessaging
  acknowledgements. Rather than sending acknowledgements piggy-backed with
  response messages back to the client, the destination service relays them to
  another service. The WS-ReliableMessaging AcksTo information header is used
  by the client when the sequence is created to indicate the acknowledgements
  relay target. The AcksTo cannot be changed after sequence creation to
  termination.

Any or all of the above destination services except the first can be the
source (client) itself, so message responses, faults, and acknowledgements can
be relayed back to the client. To this end, it is important that the client
sets the WS-Addressing From information header for each message to match the
ReplyTo, FaultTo, and AcksTo address.

The practical aspects of message sequence creation, the message exchanges, the
message relays, and sequence close/termination are presented for the client
side first and then for each of the four types of destination servers.

@section wsrm_4 Client-side Usage

@subsection wsrm_4_1 Creating, Closing, and Terminating Message Sequences

A sequence is created, closed, terminated, and cleaned-up on the client side as
follows:

@code
struct soap *soap = soap_new(); // Note: can use C++ proxy instead of 'soap'
soap_register_plugin(soap, soap_wsa);
soap_register_plugin(soap, soap_wsrm);

const char *destination = "..."; // WS-RM destination server address
const char *acksto = "...";      // WS-RM AcksTo server, or NULL if none
ULONG64 expires = 10000;         // 10000 ms to expire (10 seconds)
const char *id = NULL;           // id = NULL: generate a temp sequence ID
const char *opt_msg_id = NULL;   // WS-Addressing message ID, or NULL

soap_wsrm_sequence_handle seq;   // a local handle to the sequence state

// Step 1: create a sequence
if (soap_wsrm_create_offer(soap, destination, acksto, id, expires, NoDiscard, opt_msg_id, &seq))
{ if (seq)
    soap_wsrm_seq_free(soap, seq);
  ... // error creating sequence
} 

// Step 2: exchange messages with WS-RM destination, request acks, receive acks, issue resends (see later) 
...

// Step 3: optionally close first before terminating
if (soap_wsrm_close(soap, seq, NULL))
{ soap_wsrm_seq_free(soap, seq);
  ... // error closing sequence
} 

// Step 4: terminate
if (soap_wsrm_terminate(soap, seq, NULL))
{ soap_wsrm_seq_free(soap, seq);
  ... // error terminating sequence
} 

// Step 5: cleanup
soap_wsrm_seq_free(soap, seq);
@endcode

As usual, the context can release all its temporary and deserialized data with
the following pair of calls:

@code
soap_destroy(soap);
soap_end(soap);
@endcode

This cleanup of memory resources may be performed at any time in the sequence
of message exchange or afterwards when desired. The sequence state is
maintained independent of these cleanup operations.

The sequence termination may fail when the delivery of a sequence of messages
is incomplete or when the lifetime of the sequence expired
(see @ref SOAP_WSRM_MAX_SEC_TO_EXPIRE). The WS-RM destination determines the
failure based on the final sequence state and the sequence behavior. The
behavior is set to NoDiscard by default, which means that the sequence is not
discared when transmission gaps appeared in the messages and the sequence is
incomplete. The desired behavior can be specified with a sequence creation
offer as explained in the next section.

@subsection wsrm_4_2 Creating a Sequence with an Offer

To enable a destination server to produce a reliable message response sequence,
you need to create a sequence with an offer. Otherwise, response messages are
not tracked and delivery not verified. Two-way messaging should use the offer
mechanism, but if all messages are one-way then this is not needed. A sequence
is created with an offer of sequence lifetime and behavior parameters as
follows:

@code
ULONG64 expires = 60000;       // 1 minute = 60,000 milliseconds
const char *id = NULL;         // id = NULL: generate a temp sequence ID
const char *opt_msg_id = NULL; // WS-Addressing message ID

if (soap_wsrm_create_offer(soap, destination, acksto, id, expires, DiscardEntireSequence, opt_msg_id, &seq))
{ if (seq)
    soap_wsrm_seq_free(soap, seq);
  ... // error creating sequence
}
@endcode

This offers sequence parameters to the WS-RM destination, which may accept the
offer and apply the requested lifetime (expiration time in ms) and sequence
behavior.

The sequence behavior is either

- NoDiscard (default) means that the sequence of messages is not discarded by
  the destination server when one or more messages are missing; 
- DiscardFollowingFirstGap means that the initial messages of the sequence are
  retained by the destination up to the first gap (a missing message) in the
  sequence;
- DiscardEntireSequence means that the entire sequence of messages will be
  discarded when a single message is missing.

@subsection wsrm_4_3 Exchanging Messages in a Sequence

Each message exchange with the WS-RM destination should be preceded with a
soap_wsrm_request() or soap_wsrm_request_acks() call to set the required WS-RM
information headers for the message send operation or request-response
exchange.

For example, consider the 'example' operation defined previously and suppose we
invoke the 'example' operation in a sequence (after creation and before
closing):

@code
const char *exampleRequestAction = "urn:example/examplePort/example";
const char *exampleRequestMessageID = NULL; // optional WS-Addressing ID

struct ns__exampleResponse response;

if (soap_wsrm_request(soap, seq, exampleRequestMessageID, exampleRequestAction))
  ... // error: out of memory

if (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response))
  soap_print_fault(soap, stderr); // an error occurred
else
  ... // process the response
@endcode

The soap_wsrm_request() takes the sequence handle and optional WS-Addressing
message ID and mandatory WS-Addressing action string (this string must match
the method-action defined in the gSOAP service definition header file). It
produces a WS-RM header block with the message number incremented by one for
the invocation. Messages are enumerated from one and included in the WS-RM
header to allow the destination to determine which messages were received in
the sequence (for acknowledgements) and to ignore duplicate messages.

The remote invocation soap_call_ns__example() uses soap_wsrm_to(seq) for the
WS-RM destination address, which was set by soap_wsrm_create() or
soap_wsrm_create_offer(). Because the address may change due to a redirect, we
encourage the use of soap_wsrm_to() for the WS-RM destination address.

A C++ proxy object (generated by soapcpp2 option -i) that invokes a service
operation should reset the destination address explicitly by setting the
'soap_endpoint' member string before each operation invocation.

@subsection wsrm_4_4 Exchanging Messages with Acknowledgements in a Sequence

Message acknowledgements are requested as follows:

@code
if (soap_wsrm_request_acks(soap, seq, exampleRequestMessageID, exampleRequestAction))
  ... // error: out of memory
@endcode

This informs the WS-RM destination to return message delivery acknowledgements
back to the sender (piggy-backed in the header of the response message), unless
the AcksTo is set to target an acknowledgement service endpoint.

The soap_wsrm_close() returns acknowledgements automatically, so requesting
intermediate acknowledgements is not required to issue a final
soap_wsrm_resend() to resend all non-acknowledged messages, e.g. after
soap_wsrm_close(). See also below.

@subsection wsrm_4_5 Resending Non-Acknowledged Messages

All non-acknowledged messages in a sequence that were previously sent can be
resend (from the internal sender-side cache of sent messages in a sequence) as
follows:

@code
soap_wsrm_resend(soap, seq, 0, 0);
@endcode

To resend a range of non-acknowledged messages, say between 3 and 7, use:

@code
soap_wsrm_resend(soap, seq, 3, 7);
@endcode

Or all messages after message number 3:

@code
soap_wsrm_resend(soap, seq, 3, 0);
@endcode

Resends should be used with care, since in the worst case when no
acknowledgements have been received all messages up to the last will be resend
(and ignored by the WS-RM destination when a message is received more than
once).

Note that when an AcksTo destination service address was set with
soap_wsrm_create() or soap_wsrm_create_offer(), then the acknowledgements will
not be returned to the sender (client). In this case message resends are
performed for all messages sent, since these have not been acknowledged.

It is permitted to issue resends between creation and termination of a
sequence, including after a sequence close (as long as no new messages are sent
after close). The sequence close provides acknowledgement information to limit
the number of messages that need to be resend.

To find out if none, some, or all messages have been acknowledged, use:

@code
ULONG64 nack = soap_wsrm_nack(seq);
if (nack == 0)
  ... // all sent messages have been acknowledged
else if (nack == soap_wsrm_num(seq))
  ... // none of the sent messages have been acknowledged
else
  ... // some sent messages have been acknowledged
@endcode

@subsection wsrm_4_6 Relaying Response and Fault Messages with WS-Addressing

WS-ReliableMessaging is important when messages are relayed, and especially
when relayed over UDP. 

The ReplyTo and FaultTo destination service endpoints can be specified for each
message as follows:

@code
const char *replyto = "..."; // endpoint of response processing service
const char *faultto = "..."; // endpoint of fault processing service

if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction)
 || soap_wsa_add_ReplyTo(soap, replyto)
 || soap_wsa_add_FaultTo(soap, faultto))
  ... // error: out of memory
if (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response))
{ if (soap->error == 202)
    ... // request was accepted by destination (HTTP 202 Accept)
  else
    ... // other error, see below on how to detect acks
}
@endcode

To sent acknowledgements back to the client, set AcksTo to NULL:

@code
ULONG64 expires = 60000;         // 1 minute = 60,000 milliseconds
const char *id = NULL;           // id = NULL: generate a temp sequence ID
const char *opt_msg_id = NULL;   // WS-Addressing message ID
const char *destination = "..."; // WS-RM destination server address
const char *acksto = NULL;       // sent acks to client

soap_wsrm_sequence_handle seq;   // a local handle to the sequence state

if (soap_wsrm_create_offer(soap, destination, acksto, id, expires, NoDiscard, opt_msg_id, &seq))
{ if (seq)
    soap_wsrm_seq_free(soap, seq);
  ... // error creating sequence
} 

if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction))
  ... // error: out of memory
if (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response))
{ if (soap->error == 202)
    ... // request was accepted by destination (HTTP 202 Accept)
  else if (soap->error == SOAP_NO_TAG) // empty <Body>
    ... // request was accepted by destination, acks are returned in empty Body
  else
    ... // other error
}
@endcode

Otherwise, setting the AcksTo will relay acknowledgements to the address
specified.

An optional source address information header can be added with
soap_wsa_add_From():

@code
const char *from = "..."; // endpoint of the client (could be any URI)

if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction)
 || soap_wsa_add_From(soap, from))
  ... // error: out of memory
@endcode

Adding a From address is optional and usually not required with
WS-ReliableMessaging.

@subsection wsrm_4_7 Using Retry Loops to Improve Robustness of Message Sends

A potential problem with reliable message delivery with resends can fail when
the initial send was not successful, for example due to a connection failure.
The wsrm plugin records all sent messages when the send operation was not
interrupted. A problem occurs when the message cache contains incomplete
messages and these messages cannot be resend. This section presents an
additional mechanism to ensure messages are cached properly for automatic
retransmission.

Besides network failues, a request-response message exchange can also appear to
fail due to a non SOAP_OK returned, such as a benign HTTP 202 Accept To
distinguish fatal send errors from errors returned by the peer, the
soap_wsrm_retry() function can be used as follows to only retry the message
exchange (or send) when needed:

@code
if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction))
  ... // error: out of memory
while (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response))
{ if (soap->error == 202)
  { // request was accepted by destination (HTTP 202 Accept)
    break;
  }
  else if (soap->error == SOAP_NO_TAG) // empty <Body>
  { // request was accepted by destination, acks are returned
    break;
  }
  soap_print_fault(soap, stderr);
  if (soap_wsrm_check_retry(soap, seq))
    break; // do not continue
  sleep(1); // wait a second to give network a chance to recover
}

if (soap->error == SOAP_OK)
  ... // response can be processed
@endcode

Note that the soap_wsrm_request() is only invoked once in the above to
increment the message enumeration.

The loop retries transmissions a maximum of @ref SOAP_WSRM_MAX_RETRIES
iterations before giving up.

@subsection wsrm_4_8 Example Client

The following code shows an example WS-RM client fragment that combines the
concepts introduced in the previous sections:

@code
struct soap *soap = soap_new(); // Note: can use C++ proxy instead of 'soap'
soap_register_plugin(soap, soap_wsa);
soap_register_plugin(soap, soap_wsrm);

struct ns__exampleResponse response;
const char *exampleRequestAction = "urn:example/examplePort/example";

const char *destination = "..."; // WS-RM destination server address
const char *acksto = "...";      // WS-RM AcksTo server, or NULL if none
const char *replyto = "...";     // WS-A ReplyTo server to relay response
const char *faultto = "...";     // WS-A FaultTo server to relay fauls
ULONG64 expires = 60000;         // 1 minute sequence lifetime

soap_wsrm_sequence_handle seq;

if (soap_wsrm_create_offer(soap, destination, acksto, NULL, expires, DiscardEntireSequence, NULL, &seq))
{ if (seq)
    soap_wsrm_seq_free(soap, seq);
  ... // error creating sequence
} 

if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction)
 || soap_wsa_add_ReplyTo(soap, replyto)
 || soap_wsa_add_FaultTo(soap, faultto))
  ... // error: out of memory

while (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response))
{ if (soap->error == 202)
  { // request was accepted by destination (HTTP 202 Accept)
    break;
  }
  else if (soap->error == SOAP_NO_TAG) // empty <Body>
  { // request was accepted by destination, acks are returned
    break;
  }
  soap_print_fault(soap, stderr);
  if (soap_wsrm_check_retry(soap, seq))
    break; // do not continue
  sleep(1); // wait a second to give network a chance to recover
}

if (soap->error == SOAP_OK)
  ... // response can be processed

if (soap_wsrm_close(soap, seq, NULL))
{ soap_wsrm_seq_free(soap, seq);
  ... // error closing sequence
} 

soap_wsrm_resend(soap, seq, 0, 0); // resend non-acked messages

if (soap_wsrm_terminate(soap, seq, NULL))
{ soap_wsrm_seq_free(soap, seq);
  ... // error terminating sequence
} 

soap_wsrm_seq_free(soap, seq);

soap_destroy(soap);
soap_end(soap);
soap_free(soap);
@endcode

Responses can be discarded by the WS-RM destination service as requested with
the soap_wsa_add_NoReply() call, which adds a ReplyTo 'noreply' WS-Addressing
header block URI.

@section wsrm_5 Server-side Usage

To set up a WS-ReliableMessaging compliant server, register the wsa and wsrm
plugins with the soap context (or with the C++ proxy object generated by
soapcpp2 option -i):

@code
struct soap *soap = soap_new(); // Note: can use C++ proxy instead of 'soap'
soap_register_plugin(soap, soap_wsa);
soap_register_plugin(soap, soap_wsrm);
@endcode

The following subsections detail the differences between the types of WS-RM
destination services.

@subsection wsrm_5_1 Setting up a WS-RM Destination Service

Each service operation that supports WS-ReliableMessaging and WS-Addressing
should use the soap_wsrm_check(), soap_wsrm_sender_fault(),
soap_wsrm_receiver_fault(), and soap_wsrm_reply() functions as follows:

@code
int ns__example(struct soap *soap, char *in, struct ns__exampleResponse *response)
{ const char *ResponseAction = "urn:example/examplePort/exampleResponse";

  // fatal service operation-specific errors (before soap_wsrm_check)
  if (!database) // suppose we need a database, if there is none terminate
    return soap_wsrm_receiver_fault(soap, "No database!", NULL);

  // check for WS-RM/WSA and set WS-RM/WSA return headers
  if (soap_wsrm_check(soap))
    return soap->error;

  // check for non-fatal service operation-specific errors
  if (!in || !*in) // sender did not put anything in the 'in' string: fault
    return soap_wsrm_sender_fault(soap, "No string content!", NULL);

  response->out = ...

  // return normally, relaying the response to the ReplyTo service when needed
  return soap_wsrm_reply(soap, NULL, ResponseAction);
}
@endcode

An error produced by soap_wsrm_sender_fault() or soap_wsrm_receiever_fault()
before soap_wsrm_check() is considered fatal, it will terminate the sequence
and the sender (client) will not be able to continue the sequence
transmissions. While the faults preduced after soap_wsrm_check() allow the
sequence to continue.

@subsection wsrm_5_2 Setting up a ReplyTo Service Endpoint

A response-processing destination service may or may not support WS-RM. This
allows response messages to be relayed to simple services.

To set up a response-processing destination (accepting ReplyTo-relayed response
messages), set up the server in the same way as the destination server. Service
opertions should not use soap_wsrm_check() and soap_wsrm_reply(). Because
response messages are sent (as if these were request messages), the service
must define the appropriate one-way operations and gSOAP service definitions
bindings.

For example, the one-way response message of the ns__example operation is
defined as follows in the gSOAP service definitions header file:

@code
//gsoap ns service method-header-part: exampleResponse wsa5__MessageID
//gsoap ns service method-header-part: exampleResponse wsa5__RelatesTo
//gsoap ns service method-header-part: exampleResponse wsa5__From
//gsoap ns service method-header-part: exampleResponse wsa5__ReplyTo
//gsoap ns service method-header-part: exampleResponse wsa5__FaultTo
//gsoap ns service method-header-part: exampleResponse wsa5__To
//gsoap ns service method-header-part: exampleResponse wsa5__Action

//gsoap ns service method-header-part: exampleResponse wsrm__SequenceAcknowledgement

//gsoap ns service method-action: exampleResponse urn:example/examplePort/exampleResponse

int ns__exampleResponse(char *out, void);
@endcode

Note that when these definitions are combined with the previous definition for
ns__example, there is no need to define the ns__ExampleResponse struct any
longer as this is implied by the ns__exampleResponse function content.

The server operation implementation is for example:

@code
int ns__exampleResponse(struct soap *soap, char *out)
{
  ... // process
  return SOAP_OK;
}
@endcode

Note that message acknowledgements can be piggy-backed with these response
messages from the destination service when AcksTo is not set (at the
destintation service). These acknowledgements are processed automatically to
update the local sequence states when the wsrm plugin is registered.

@subsection wsrm_5_3 Setting up a FaultTo Service Endpoint

The SOAP Fault is accepted as a message by a service when defined as follows in the gSOAP service definition header file:

@code
//gsoap SOAP_ENV service method-action: Fault http://schemas.xmlsoap.org/ws/2004/08/addressing/fault
int SOAP_ENV__Fault
(       _QName			 faultcode,		// SOAP 1.1
        char			*faultstring,		// SOAP 1.1
        char			*faultactor,		// SOAP 1.1
        struct SOAP_ENV__Detail	*detail,		// SOAP 1.1
        struct SOAP_ENV__Code	*SOAP_ENV__Code,	// SOAP 1.2
        struct SOAP_ENV__Reason	*SOAP_ENV__Reason,	// SOAP 1.2
        char			*SOAP_ENV__Node,	// SOAP 1.2
        char			*SOAP_ENV__Role,	// SOAP 1.2
        struct SOAP_ENV__Detail	*SOAP_ENV__Detail,	// SOAP 1.2
	void
);
@endcode

Given the above service definitions, the soapcpp2 tool generates a
SOAP_ENV__Fault service dispatcher that will invoke the SOAP_ENV__Fault()
function that should be implemented at the server side:

@code
int SOAP_ENV__Fault(
  struct soap *soap,
  char *faultcode,
  char *faultstring,
  char *faultactor,
  struct SOAP_ENV__Detail *detail,
  struct SOAP_ENV__Code *SOAP_ENV__Code,
  struct SOAP_ENV__Reason *SOAP_ENV__Reason,
  char *SOAP_ENV__Node,
  char *SOAP_ENV__Role,
  struct SOAP_ENV__Detail *SOAP_ENV__Detail
  )
{ 
  // SOAP 1.1
  ... = faultcode;
  ... = faultstring;
  ... = faultactor;
  ... = detail;
  // SOAP 1.2
  ... = SOAP_ENV__Code;
  ... = SOAP_ENV__Reason;
  ... = SOAP_ENV__Node;
  ... = SOAP_ENV__Role;
  ... = SOAP_ENV__Detail;
  return soap_send_empty_response(soap, SOAP_OK); // HTTP 202 Accepted
}
@endcode

Because the FaultTo service only uses WS-Addressing, there is no need to
register or use the wsrm plugin.

@subsection wsrm_5_4 Setting up an AcksTo Service Endpoint

To set up an AcksTo acknowledgement-processing destination, the service
definition header file should at least import wsrm.h:

@code
#import "wsrm.h"
@endcode

This is sufficient, since the wsrm plugin implements an acknowledgement
processing capability with the built-in __wsrm__SequenceAcknowledgement()
server operation, that updates the local sequence state when acknowledgements
are received.

How to use the sequence state of the AcksTo server and use the state updates is
up to the application developer.

@section wsrm_6 WS-ReliableMessaging over HTTPS with Basic Authentication

The HTTPS client and server are set up as shown in the gSOAP documentation and
examples. There are no additional API calls needed to support WS-RM with HTTPS.

Note that the WS-RM destination service may also relay messages to other HTTPS
services, thus the WS-RM destination acts as a receiver (server) and sender
(client). Therefore, the WS-RM destination server's SSL context should be set
to authenticate the other servers:

@code
if (soap_ssl_server_context(soap,
  SOAP_SSL_DEFAULT,
  "server.pem",	// keyfile (server)
  "password",	// password to read the key file (server)
  "cacert.pem",	// cacert file to store trusted certificates (client)
  NULL,		// optional capath
  NULL, 	// DH file name or DH param key len bits, NULL: RSA
  NULL,		// file with random data to seed randomness
  argv[1]	// unique server identification for SSL session cache
))
{ soap_print_fault(soap, stderr);
  ... // handle error
}
@endcode

Here, the cacert.pem file contains certificates to authenticate the ReplyTo,
FaultTo, and AcksTo services when HTTPS is used.

The client side sets up the SSL context with the soap_ssl_client_context() as
instructed in the documentation and by the examples. Multi-threaded HTTPS
clients and servers must register mutex locks with OpenSSL

To use Basic Authentication at the client side, set the userid and passwd
values:

@code
soap->userid = "..."; // Basic Auth user id
soap->passwd = "..."; // Basic Auth password
if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction)
  ... // error: out of memory
if (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response))
{ if (soap->error == 401)
    ... // authentication failed for the userid/passwd pair
  else
    ... // other error
}
@endcode

At the server side add the authentication check to the service operation before
soap_wsrm_check() to terminate the sequence when an authentication failure
occurs. For example:

@code
int ns__example(struct soap *soap, char *in, struct ns__exampleResponse *response)
{ if (!soap->userid || !soap->passwd || strcmp(soap->userid, "...") || strcmp(soap->passwd, "..."))
  { soap->authrealm = "..."; // optional to set HTTP WWW-Authenticate: Basic realm="..."
    return 401; // HTTP 401 Unauthorized
  }
  if (soap_wsrm_check(soap))
    return soap->error;
@endcode

Here, we check only one userid-passwd pair though normally we could search for
valid credentials in an authentication store.

Note: never use Basic Authentication over HTTP because the password is sent in
the clear. You must use HTTPS to encrypt the HTTP authentication information
and message content. HTTP Digest Auth is preferred for this reason, because
Digest Auth verifies the digest of a userid-passwd rather than require the
password to be exchanged or stored in cleartext.

@section wsrm_7 WS-ReliableMessaging over UDP with Timeouts

The use of UDP is automatic at the client side using the "soap.udp://"
protocol. Therefore, endpoints should use a "soap.udp://" URL to connect. (when
using an already opened socket, the SOAP_IO_UDP flag must be used, see the
documentation.)

Note that UDP datagram messages should not exceed 8K, which is usually a size
that UDP datagrams can support. To reduce the message size, we recommend
compression (-DWITH_GZIP compile flag to enable ZLIB and use
libgsoapssl.a/libgsoapssl++.a for OpenSSL and ZLIB compression combined).

The code of an UDP-enabled server is identical to an HTTP/TCP server except
that the soap_accept() call is disabled and unnecessary.

When message responses are not returned to the client, the client may block
indefinitely when it expects a response. Therefore we recommend the use of send
and receive timeouts:

@code
struct soap *soap = soap_new();

const char *destination = "soap.udp://...";
const char *acksto = NULL;

soap->send_timeout = soap->recv_timeout = 1; // 1 second to timeout

if (soap_wsrm_create_offer(soap, destination, acksto, NULL, expires, NoDiscard, NULL, &seq))
 ... // an error occured

if (soap_wsrm_request(soap, seq, NULL, exampleRequestAction))
  ... // an error occured
if (soap_call_ns__example(soap, ...))
{ if (soap->error == SOAP_EOF && soap->errnum == 0)
    ... // a timeout occured
  else
    ... // an error occured
}
@endcode

Note that the WS-Addressing ReplyTo and the use of NoReply do not return
response message from the server. However, acknowledgements will be returned
when acknowledgements were requested (unless acknowledgements are relayed with
AcksTo).

@code
struct soap *soap = soap_new();

soap->send_timeout = soap->recv_timeout = 1; // 1 second to timeout

const char *destination = "soap.udp://...";
const char *from = "..."; // some identifying URI
const char *acksto = from;

soap->send_timeout = soap->recv_timeout = 1; // 1 second to timeout

if (soap_wsrm_create_offer(soap, destination, acksto, NULL, expires, NoDiscard, NULL, &seq))
 ... // an error occured

if (soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction)
 || soap_wsa_add_NoReply(soap)
  ... // an error occured
if (soap_call_ns__example(soap, ...))
{ if (soap->error == SOAP_EOF && soap->errnum == 0)
    ... // a timeout occured
  else if (soap->error == SOAP_NO_TAG)
    ... // ack was received and recorded
  else
    ... // an error occured
}
@endcode

In this case an acknowledgement will be returned and the timeout reflects a
possible network packet loss. 

@section wsrm_8 WS-ReliableMessaging and WS-Security

WS-Security can be combined with WS-ReliableMessaging using the
<a href="../../wsse/html/index.html">WSSE plugin</a>.

Both plugins must be registered at the client and server side. These APIs are
independent.

@section wsrm_9 The wsrm Plugin and Generated C++ Server Objects

Plugins are developed in C to support C and C++. There is no disadvantage to
the use of C for this purpose. The only exception is the server-side use of the
wsrm plugin with a server object generated by soapcpp2 option -i. With this
option the following methods must be implemented as wrappers for the wsrm
plugin service operations:

@code
int wsrmService::CreateSequence(
  struct wsrm__CreateSequenceType *wsrm__CreateSequence,
  struct wsrm__CreateSequenceResponseType *wsrm__CreateSequenceResponse)
{ return __wsrm__CreateSequence(this, wsrm__CreateSequence, wsrm__CreateSequenceResponse);
}
int wsrmService::CloseSequence(
  struct wsrm__CloseSequenceType *wsrm__CloseSequence,
  struct wsrm__CloseSequenceResponseType *wsrm__CloseSequenceResponse)
{ return __wsrm__CloseSequence(this, wsrm__CloseSequence, wsrm__CloseSequenceResponse);
}
int wsrmService::TerminateSequence(
  struct wsrm__TerminateSequenceType *wsrm__TerminateSequence,
  struct wsrm__TerminateSequenceResponseType *wsrm__TerminateSequenceResponse)
{ return __wsrm__TerminateSequence(this, wsrm__TerminateSequence, wsrm__TerminateSequenceResponse);
}
int wsrmService::SequenceAcknowledgement()
{ return __wsrm__SequenceAcknowledgement(this);
}
@endcode

In addition, we need to dispatch the wsrm service operations when received.
Suppose we have a myService server class generated by soapcpp2 -i, which is
used to process requests with the serve() member function (also generated):

@code
myService service;
if (soap_invalid_socket(service.bind(NULL, port, 100)))
  ... // error
if (soap_invalid_socket(service.accept()))
  ... // error
if (service.serve() == SOAP_NO_METHOD) // no matching operation
{ wsrmService wsrm(service);           // wsrm server
  soap_copy_stream(&wsrm, &service);   // copy open connection
  wsrm.dispatch();                     // process WS-RM request
}
@endcode

*/

#include "wsrmapi.h"

#ifdef __cplusplus
extern "C" {
#endif

/** Plugin identification for plugin registry */
const char soap_wsrm_id[] = SOAP_WSRM_ID;

/** Sequence session database */
static struct soap_wsrm_sequence *soap_wsrm_session = NULL;
const char *soap_wsrm_idname = NULL;
unsigned long soap_wsrm_idnum = 0;

/** Sequence session database lock */
static MUTEX_TYPE soap_wsrm_session_lock = MUTEX_INITIALIZER;

/******************************************************************************\
 *
 *	Static protos
 *
\******************************************************************************/

static int soap_wsrm_init(struct soap *soap, struct soap_wsrm_data *data, void *arg);
static int soap_wsrm_copy(struct soap *soap, struct soap_plugin *p, struct soap_plugin *q);
static void soap_wsrm_delete(struct soap *soap, struct soap_plugin *p);

static int soap_wsrm_send(struct soap *soap, const char *buf, size_t len);
static int soap_wsrm_preparefinalrecv(struct soap *soap);
static int soap_wsrm_disconnect(struct soap *soap);

static int soap_wsrm_process_ack(struct soap *soap, struct _wsrm__SequenceAcknowledgement *ack);

static int soap_wsrm_add_acks(struct soap *soap);
static int soap_wsrm_set_ack(struct soap *soap, struct soap_wsrm_sequence *seq, struct _wsrm__SequenceAcknowledgement *ack);
#if 0
static int soap_wsrm_post(struct soap *soap, const char *endpoint, const char *host, int port, const char *path, const char *action, size_t count);
#endif

static int soap_wsrm_resend_seq(struct soap *soap, struct soap_wsrm_sequence *seq, ULONG64 lower, ULONG64 upper);

static const char *soap_wsrm_seq_newid(struct soap *soap, struct soap_wsrm_data *data);
static struct soap_wsrm_sequence *soap_wsrm_seq_lookup(const struct soap_wsrm_data *data, const char *id);
static struct soap_wsrm_sequence *soap_wsrm_seq_lookup_ack(const struct soap_wsrm_data *data, const char *id);
static struct soap_wsrm_sequence *soap_wsrm_seq_insert(struct soap *soap, struct soap_wsrm_data *data);
static int soap_wsrm_msg_append(struct soap *soap, struct soap_wsrm_data *data, const char *buf, size_t len);
static int soap_wsrm_seq_terminated(struct soap *soap, struct soap_wsrm_sequence *seq);

static int soap_wsrm_num_lookup(struct soap *soap, const struct soap_wsrm_sequence *seq, ULONG64 num);
static int soap_wsrm_num_insert(struct soap *soap, struct soap_wsrm_sequence *seq, ULONG64 num);
static int soap_wsrm_num_size(const struct soap_wsrm_sequence *seq);
static void soap_wsrm_num_free(struct soap *soap, struct soap_wsrm_sequence *seq);
static struct soap_wsrm_message *soap_wsrm_msg_new(struct soap *soap, struct soap_wsrm_sequence *seq, ULONG64 num);
static void soap_wsrm_msg_free(struct soap *soap, struct soap_wsrm_message *p);

/******************************************************************************\
 *
 *	Client-side WS-RM Operations
 *
\******************************************************************************/

/**
@fn int soap_wsrm_create(struct soap *soap, const char *to, const char *acksto, LONG64 expires, const char *wsa_id, soap_wsrm_sequence_handle *seq)
@brief Creates a new sequence. Sequences are usually created by the sender
(client) and confirmed by the receiver (server). The 'to' server address must
be used for all messages of the sequence to be sent to the server. Optionally
the 'acksto' address can be given for acknowledgement messages to be sent to. A
sequence ID is generated by the server upon success.
@param soap context
@param[in] to endpoint address of the WS-RM destination server (required)
@param[in] acksto endpoint address of the WS-RM acknowledgement server
(optional, use NULL when acks are piggy-backed on response messages to the
source)
@param[in] expires max sequence duration (its lifetime) in ms (use 0 for inifinite)
@param[in] wsa_id WS-Addressing message ID (optional, use NULL when omitted)
@param[out] seq sequence handle is set
@return SOAP_OK or error code
*/
int
soap_wsrm_create(struct soap *soap, const char *to, const char *acksto, LONG64 expires, const char *wsa_id, soap_wsrm_sequence_handle *seq)
{ return soap_wsrm_create_offer(soap, to, acksto, "", expires, NoDiscard, wsa_id, seq);
}

/**
@fn int soap_wsrm_create_offer(struct soap *soap, const char *to, const char *acksto, const char *id, LONG64 expires, enum wsrm__IncompleteSequenceBehaviorType behavior, const char *wsa_id, soap_wsrm_sequence_handle *seq)
@brief Creates a new sequence by offering suggested WS-RM parameters to the
destination. Sequences are usually created by the sender (client) and
confirmed by the receiver (server). The 'to' server address must be used for
all messages of the sequence to be sent to the server. Optionally the 'acksto'
address can be given for acknowledgement messages to be sent to.
server upon success.
@param soap context
@param[in] to endpoint address of the WS-RM destination server (required)
@param[in] acksto endpoint address of the WS-RM acknowledgement server
(optional, use NULL when acks are piggy-backed on response messages to the
source)
@param[in] id offered WS-RM sequence identifier (optional, generate when NULL)
@param[in] expires max sequence duration (its lifetime) in ms (use 0 for inifinite)
@param[in] behavior offered DiscardEntireSequence, DiscardFollowingFirstGap, or
NoDiscard, which specifies the WS-RM destination's action when a sequence is
closed/terminated when it is incomplete, and notifies the source when failed.
@param[in] wsa_id WS-Addressing message ID (optional, use NULL when omitted)
@param[out] seq sequence handle is set
@return SOAP_OK or error code
*/
int
soap_wsrm_create_offer(struct soap *soap, const char *to, const char *acksto, const char *id, LONG64 expires, enum wsrm__IncompleteSequenceBehaviorType behavior, const char *wsa_id, soap_wsrm_sequence_handle *seq)
{ char *s;
  struct wsrm__CreateSequenceType req;
  struct wsrm__CreateSequenceResponseType res;
  struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  const char *action = SOAP_NAMESPACE_OF_wsrm"/CreateSequence";
  DBGFUN3("soap_wsrm_create_offer", "id=%s", id?id:"(null)", "to=%s", to?to:"(null)", "acksto=%s", acksto?acksto:"(null)");
  *seq = NULL;
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  soap_default_wsrm__CreateSequenceType(soap, &req);
  if (!to)
    to = soap_wsa_anonymousURI;
  if (!acksto)
    acksto = soap_wsa_anonymousURI;
  if (soap_wsa_request(soap, wsa_id, to, action))
    return soap->error;
  req.AcksTo.Address = (char*)acksto;
  if (expires)
  { req.Expires = (xsd__duration*)soap_malloc(soap, sizeof(xsd__duration));
    *req.Expires = expires;
  }
  if (!id || *id)
  { req.Offer = (struct wsrm__OfferType*)soap_malloc(soap, sizeof(struct wsrm__OfferType));
    if (!req.Offer)
      return soap->error;
    soap_default_wsrm__OfferType(soap, req.Offer);
    if (!id)
    { MUTEX_LOCK(soap_wsrm_session_lock);
      id = soap_wsrm_seq_newid(soap, data);
      MUTEX_UNLOCK(soap_wsrm_session_lock);
      if (!id)
        return soap->error;
    }
    req.Offer->Identifier = soap_strdup(soap, id); /* required */
    req.Offer->Endpoint = req.AcksTo; /* required: use acksto endpoint */
    req.Offer->Expires = req.Expires; /* expire value */
    req.Offer->IncompleteSequenceBehavior = &behavior;
  }
  else
    req.Offer = NULL;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  if (soap_call___wsrm__CreateSequence(soap, to, action, &req, &res))
    return soap->error;
  if (!res.Identifier)
    return soap_wsrm_error(soap, NULL, wsrm__CreateSequenceRefused);
  MUTEX_LOCK(soap_wsrm_session_lock);
  *seq = soap_wsrm_seq_insert(soap, data);
  if (!*seq)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  /* seq has a handle, soap_wsrm_cleanup() should not remove it */
  (*seq)->handle = 1;
  s = (char*)malloc(strlen(res.Identifier) + 1);
  if (!s)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error = SOAP_EOM;
  }
  (*seq)->id = strcpy(s, res.Identifier);
  if (id && *id)
  { s = (char*)malloc(strlen(id) + 1);
    if (!s)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error = SOAP_EOM;
    }
    (*seq)->acksid = strcpy(s, id);
  }
  s = (char*)malloc(strlen(to) + 1);
  if (!s)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error = SOAP_EOM;
  }
  (*seq)->to = strcpy(s, to);
  if (res.Expires && (!expires || *res.Expires <= expires))
    (*seq)->expires = time(NULL) + *res.Expires/1000;
  if (res.IncompleteSequenceBehavior)
    (*seq)->behavior = *res.IncompleteSequenceBehavior;
  else
    (*seq)->behavior = behavior;
  if (res.Accept && res.Accept->AcksTo.Address)
  { /* accepted: update the sequence for the responses */
    s = (char*)malloc(strlen(res.Accept->AcksTo.Address) + 1);
    if (!s)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error = SOAP_EOM;
    }
    (*seq)->acksto = strcpy(s, res.Accept->AcksTo.Address);
  }
  (*seq)->state = SOAP_WSRM_CREATED;
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  return SOAP_OK;
}

/**
@fn int soap_wsrm_request_num(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id, const char *wsa_action, ULONG64 num)
@brief Adds a WS-RM sequence message number to the next message transmitted.
No acks are requested.
@param soap context
@param seq sequence handle set by soap_wsrm_create() or soap_wsrm_create_offer()
@param[in] wsa_id WS-Addressing message ID (optional, use NULL when omitted)
@param[in] wsa_action mandatory WS-Addressing action of the next message sent
@param[in] num WS-RM sequence message number
@return SOAP_OK or error code
*/
int
soap_wsrm_request_num(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id, const char *wsa_action, ULONG64 num)
{ struct soap_wsrm_data *data;
  DBGFUN1("soap_wsrm_request_num", "num="SOAP_ULONG_FORMAT, num);
  if (seq->state == SOAP_WSRM_CLOSED)
    return soap_wsrm_error(soap, seq, wsrm__SequenceClosed);
  if (seq->state != SOAP_WSRM_CREATED)
    return soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
  if (num == 0)
    return soap_wsrm_error(soap, seq, wsrm__MessageNumberRollover);
  data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  /* process response message sequence ID */
  /* TODO: move this to preparefinalrecv() for client-side only */
  if (seq->acksid && soap->header && soap->header->wsrm__Sequence && !strcmp(soap->header->wsrm__Sequence->Identifier, seq->acksid))
  { if (soap_wsrm_num_lookup(soap, seq, soap->header->wsrm__Sequence->MessageNumber))
    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Response message already received\n"));
      /* TODO: error? */
    }
    if (seq->behavior == DiscardFollowingFirstGap && soap->header->wsrm__Sequence->MessageNumber != seq->lastnum + 1)
      return soap_wsrm_error(soap, seq, wsrm__SequenceClosed); /* close or term */
    seq->lastnum = soap->header->wsrm__Sequence->MessageNumber;
    MUTEX_LOCK(soap_wsrm_session_lock);
    if (soap_wsrm_num_insert(soap, seq, seq->lastnum))
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error;
    }
    MUTEX_UNLOCK(soap_wsrm_session_lock);
  }
  if (seq->num < num)
    seq->num = num;
  MUTEX_LOCK(soap_wsrm_session_lock);
  data->msg = soap_wsrm_msg_new(soap, seq, num);
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  if (!data->msg)
    return soap->error;
  soap_header(soap);
  if (!soap->header)
    return soap->error;
  if (!soap->header->wsrm__Sequence && !(soap->header->wsrm__Sequence = (_wsrm__Sequence*)soap_malloc(soap, sizeof(_wsrm__Sequence))))
    return soap->error;
  soap_default__wsrm__Sequence(soap, soap->header->wsrm__Sequence);
  soap->header->wsrm__Sequence->Identifier = soap_strdup(soap, seq->id);
  soap->header->wsrm__Sequence->MessageNumber = num;
  /* add acks for ackRequested response from server */
  if (soap_wsrm_add_acks(soap))
    return soap->error;
  seq->retry = SOAP_WSRM_MAX_RETRIES;
  data->state = SOAP_WSRM_ON;
  return soap_wsa_request(soap, wsa_id, seq->to, wsa_action);
}

/**
@fn int soap_wsrm_request(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id, const char *wsa_action)
@brief Adds a WS-RM sequence message number to the next message transmitted to
the WS-RM destination and increments the message counter by one. No acks are
requested.
@param soap context
@param seq sequence handle set by soap_wsrm_create() or soap_wsrm_create_offer()
@param[in] wsa_id WS-Addressing message ID (optional, use NULL when omitted)
@param[in] wsa_action mandatory WS-Addressing action of the next message sent
@return SOAP_OK or error code
*/
int
soap_wsrm_request(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id, const char *wsa_action)
{ DBGFUN("soap_wsrm_request");
  return soap_wsrm_request_num(soap, seq, wsa_id, wsa_action, seq->num + 1);
}

/**
@fn int soap_wsrm_request_acks(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id, const char *wsa_action)
@brief Adds a WS-RM sequence message number to the next message transmitted to
the WS-RM destination and increments the message counter by one. Message acks
for the current sequence are requested.
@param soap context
@param seq sequence handle set by soap_wsrm_create() or soap_wsrm_create_offer()
@param[in] wsa_id WS-Addressing message ID (optional, use NULL when omitted)
@param[in] wsa_action mandatory WS-Addressing action of the next message sent
@return SOAP_OK or error code
*/
int
soap_wsrm_request_acks(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id, const char *wsa_action)
{ DBGFUN("soap_wsrm_request_ack");
  if (soap_wsrm_request_num(soap, seq, wsa_id, wsa_action, seq->num + 1))
    return soap->error;
  soap->header->__sizeAckRequested = 1;
  if (!soap->header->wsrm__AckRequested && !(soap->header->wsrm__AckRequested = (_wsrm__AckRequested*)soap_malloc(soap, sizeof(_wsrm__AckRequested))))
    return soap->error;
  soap_default__wsrm__AckRequested(soap, soap->header->wsrm__AckRequested);
  soap->header->wsrm__AckRequested->Identifier = soap->header->wsrm__Sequence->Identifier;
  return SOAP_OK;
}

/**
@fn int soap_wsrm_check_retry(struct soap *soap, soap_wsrm_sequence_handle seq)
@brief Client-side check to verify if the remote call can be retried when a
failure occured. Increases the robustness of messages sends, by ensuring that
the message was at least transmitted (but not necessarily received). Also
implements HTTP 307 Temporary Redirect. Retries are limited to
SOAP_WSRM_MAX_RETRIES iterations.
@param soap context
@param seq sequence handle set by soap_wsrm_create or soap_wsrm_create_offer
@return SOAP_OK when retry is safe, error code otherwise
*/
int
soap_wsrm_check_retry(struct soap *soap, soap_wsrm_sequence_handle seq)
{ struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  DBGFUN("soap_wsrm_check_retry");
  if (soap_wsrm_seq_terminated(soap, seq) || seq->retry-- == 0)
    return soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
  /* should actually not call this function when OK */
  if (soap->error == SOAP_OK)
    return SOAP_OK;
  /* HTTP temporary redirect? */
  if (soap->error == 307)
  { char *s;
    s = (char*)malloc(strlen(soap->endpoint) + 1);
    if (s)
      seq->to = strcpy(s, soap->endpoint);
  }
  /* if the sequence id was changed (eg. response), cannot resend and we fail */
  else if (!soap->header
        || !soap->header->wsrm__Sequence
	|| !soap->header->wsrm__Sequence->Identifier
	|| !seq->id
	|| strcmp(soap->header->wsrm__Sequence->Identifier, seq->id))
    return soap->error;
  /* otherwise, send was not successful and we should try again */
  data->state = SOAP_WSRM_ON;
  /* SOAP_OK to retry the call loop */
  return soap->error = SOAP_OK;
}

/**
@fn int soap_wsrm_resend(struct soap *soap, soap_wsrm_sequence_handle seq, ULONG64 lower, ULONG64 upper)
@brief Client-side call to resend all non-acknowledged messages that were
automatically cached for this sequence. Messages stored in the sequence for
retransmission (those that were previously sent but not acknowledged) are
resent to the soap_wsrm_to() address, which was set by soap_wsrm_create() or
soap_wsrm_create_offer(). It is recommended to resend messages after the last
message in the sequence was transmitted before closing the sequence. To reduce
unnecessary resend attempts, it is recommended to use soap_wsrm_request_ack()
with the last message to request acks for messages already delivered.
@param soap context
@param seq sequence handle set by soap_wsrm_create or soap_wsrm_create_offer
@param[in] lower resend message range lower bound (0 for lowest)
@param[in] upper resend message range upper bound (or 0 for infinite)
@return SOAP_OK or error code (can be ignored when resends are retried later)
*/
int
soap_wsrm_resend(struct soap *soap, soap_wsrm_sequence_handle seq, ULONG64 lower, ULONG64 upper)
{ struct SOAP_ENV__Header *h = soap->header;
  struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  DBGFUN("soap_wsrm_resend");
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  MUTEX_LOCK(soap_wsrm_session_lock);
  if (seq)
  { if (soap_wsrm_resend_seq(soap, seq, lower, upper))
    { soap->header = h;
      MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error;
    }
  }
  else
  { for (seq = soap_wsrm_session; seq; seq = seq->next)
    { if (soap_wsrm_resend_seq(soap, seq, lower, upper))
      { soap->header = h;
        MUTEX_UNLOCK(soap_wsrm_session_lock);
        return soap->error;
      }
    }
  }
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  soap->header = h;
  return SOAP_OK;
}

/**
@fn int soap_wsrm_close(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id)
@brief Closes the sequence, but does not yet terminate it. No new messages
should be send, but messages can be resend with soap_wsrm_resend() if desired.
@param soap context
@param seq sequence handle set by soap_wsrm_create or soap_wsrm_create_offer
@param[in] wsa_id WS-Addressing message ID (optional)
@return SOAP_OK or error code
*/
int
soap_wsrm_close(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id)
{ struct wsrm__CloseSequenceType req;
  struct wsrm__CloseSequenceResponseType res;
  struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  DBGFUN1("soap_wsrm_close", "wsa_id=%s", wsa_id?wsa_id:"(null)");
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  /* process previous response message sequence ID */
  /* TODO: move this to preparefinalrecv() */
  if (seq->acksid && soap->header && soap->header->wsrm__Sequence && !strcmp(soap->header->wsrm__Sequence->Identifier, seq->acksid))
  { if (soap_wsrm_num_lookup(soap, seq, soap->header->wsrm__Sequence->MessageNumber))
    { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Response message already received\n"));
      /* TODO: error? */
    }
    if (seq->behavior == DiscardFollowingFirstGap && soap->header->wsrm__Sequence->MessageNumber != seq->lastnum + 1)
      return soap_wsrm_error(soap, seq, wsrm__SequenceClosed); /* close or term */
    seq->lastnum = soap->header->wsrm__Sequence->MessageNumber;
    MUTEX_LOCK(soap_wsrm_session_lock);
    if (soap_wsrm_num_insert(soap, seq, seq->lastnum))
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error;
    }
    MUTEX_UNLOCK(soap_wsrm_session_lock);
  }
  /* add acks for ackRequested response from server */
  /* TODO: this appears necessary, but not mentioned in the WS-RM specs */
  if (soap->header)
  { if (soap_wsrm_add_acks(soap))
      return soap->error;
    soap->header->wsrm__Sequence = NULL;
  }
  if (soap_wsa_request(soap, wsa_id, seq->to, SOAP_NAMESPACE_OF_wsrm"/CloseSequence"))
    return soap->error;
  req.Identifier = soap_strdup(soap, seq->id);
  if (seq->num)
    req.LastMsgNumber = &seq->num;
  else
    req.LastMsgNumber = NULL;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  if (soap_call___wsrm__CloseSequence(soap, seq->to, soap->header->wsa5__Action, &req, &res))
    return soap->error;
  seq->state = SOAP_WSRM_CLOSED;
  if (!res.Identifier || strcmp(res.Identifier, seq->id))
    return soap_wsrm_error(soap, seq, wsrm__UnknownSequence);
  return SOAP_OK;
}

/**
@fn int soap_wsrm_terminate(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id)
@brief Terminates the sequence. No new messages should be send and no resends
should be tried. Usually done after soap_wsrm_close() or any time to terminate
the sequence prematurely.
@param soap context
@param seq sequence handle set by soap_wsrm_create or soap_wsrm_create_offer
@param[in] wsa_id WS-Addressing message ID (optional)
@return SOAP_OK or error code
*/
int
soap_wsrm_terminate(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id)
{ struct wsrm__TerminateSequenceType req;
  struct wsrm__TerminateSequenceResponseType res;
  struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  DBGFUN1("soap_wsrm_terminate", "wsa_id=%s", wsa_id?wsa_id:"(null)");
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  soap->header = NULL;
  if (soap_wsa_request(soap, wsa_id, seq->to, SOAP_NAMESPACE_OF_wsrm"/TerminateSequence"))
    return soap->error;
  req.Identifier = soap_strdup(soap, seq->id);
  req.LastMsgNumber = &seq->num;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  if (soap_call___wsrm__TerminateSequence(soap, seq->to, soap->header->wsa5__Action, &req, &res))
    return soap->error;
  seq->state = SOAP_WSRM_TERMINATED;
  if (!res.Identifier || strcmp(res.Identifier, seq->id))
    return soap_wsrm_error(soap, seq, wsrm__UnknownSequence);
  return SOAP_OK;
}

/**
@fn void soap_wsrm_seq_free(struct soap *soap, soap_wsrm_sequence_handle seq)
@brief Must be called to free the sequence allocated by soap_wsrm_create() or
by soap_wsrm_create_offer(). Sequence handles are not automatically reclaimed
by the engine.
@param soap context
@param seq sequence handle set by soap_wsrm_create() or soap_wsrm_create_offer()
*/
void
soap_wsrm_seq_free(struct soap *soap, soap_wsrm_sequence_handle seq)
{ struct soap_wsrm_data *data;
  struct soap_wsrm_sequence **q;
  time_t now;
  DBGFUN("soap_wsrm_seq_free");
  data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return;
  if (!seq)
    now = time(NULL);
  MUTEX_LOCK(soap_wsrm_session_lock);
  q = &soap_wsrm_session;
  while (*q)
  { if ((!seq && !(*q)->handle && (*q)->expires < now) || *q == seq)
    { struct soap_wsrm_message *p, *r;
      struct soap_wsrm_sequence *t = *q;
      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Deleting sequence %s\n", t->id?t->id:"(null)"));
      if (t->id)
        free((void*)t->id);
      if (t->acksid)
        free((void*)t->acksid);
      if (t->to)
        free((void*)t->to);
      if (t->acksto)
        free((void*)t->acksto);
      soap_wsrm_num_free(soap, t);
      for (p = t->messages; p; p = r)
      { r = p->next;
        soap_wsrm_msg_free(soap, p);
        free((void*)p);
      }
      *q = t->next;
      free((void*)t);
      if (seq)
        break;
    }
    else
      q = &(*q)->next;
  }
  MUTEX_UNLOCK(soap_wsrm_session_lock);
}

/**
@fn const char *soap_wsrm_to(soap_wsrm_sequence_handle seq)
@brief Returns the endpoint address of the destination service that serves the
sequence. Initially set with soap_wsrm_create or soap_wsrm_create_offer. HTTP
307 Temporary Redirect can change the endpoint during the lifetime of a message
sequence. Thus, this function returns the most recent endpoint binding that can
be used to send message to the server endpoint.
@param seq sequence handle set by soap_wsrm_create() or soap_wsrm_create_offer()
@return the sequence endpoint address
*/
const char *
soap_wsrm_to(soap_wsrm_sequence_handle seq)
{ return seq->to;
}

/**
@fn const char *soap_wsrm_acksto(soap_wsrm_sequence_handle seq)
@brief Returns the endpoint address of the AcksTo acknowledgement service that
serves the sequence, when set with soap_wsrm_create() or
soap_wsrm_create_offer() or NULL otherwise. HTTP 307 Temporary Redirect can
change the endpoint during the lifetime of a message sequence. Thus, this
function returns the most recent endpoint binding.
@param seq sequence handle set by soap_wsrm_create() or soap_wsrm_create_offer()
@return the sequence endpoint address
*/
const char *
soap_wsrm_acksto(soap_wsrm_sequence_handle seq)
{ return seq->acksto;
}

/**
@fn ULONG64 soap_wsrm_num(soap_wsrm_sequence_handle seq)
@brief Returns the current message number of the sequence
@param seq sequence handle set by soap_wsrm_create() or soap_wsrm_create_offer()
@return the sequence endpoint address
*/
ULONG64
soap_wsrm_num(soap_wsrm_sequence_handle seq)
{ return seq->num;
}

/**
@fn ULONG64 soap_wsrm_nack(soap_wsrm_sequence_handle seq)
@brief Returns the number of non-acknowledged messages sent
@param seq sequence handle set by soap_wsrm_create() or soap_wsrm_create_offer()
@return the sequence endpoint address
*/
ULONG64
soap_wsrm_nack(soap_wsrm_sequence_handle seq)
{ struct soap_wsrm_message *p;
  ULONG64 nack = 0;
  MUTEX_LOCK(soap_wsrm_session_lock);
  for (p = seq->messages; p; p = p->next)
    if (p->state == SOAP_WSRM_NACK)
      nack++;
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  return nack;
}

/******************************************************************************\
 *
 *	Server
 *
\******************************************************************************/

/**
@fn int soap_wsrm_check(struct soap *soap)
@brief Server-side check for the presence of WS-Addressing and WS-RM header
blocks in the SOAP header. Also prepares the return WS-RM header. This function
should be called in the each service operation that supports WS-RM. Do not use
this function in a ReplyTo response-accepting destination service operation.
@param soap context
@return SOAP_OK or error code (and server operation must return this value)
*/
int
soap_wsrm_check(struct soap *soap)
{ struct soap_wsrm_sequence *seq;
  struct soap_wsrm_data *data;
  ULONG64 num;
  DBGFUN("soap_wsrm_check");
  if (soap_wsa_check(soap))
    return soap->error;
  data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  if (!soap->header || !soap->header->wsrm__Sequence)
    return soap_wsrm_error(soap, NULL, wsrm__WSRMRequired);
  MUTEX_LOCK(soap_wsrm_session_lock);
  seq = soap_wsrm_seq_lookup_ack(data, soap->header->wsrm__Sequence->Identifier);
  if (!seq)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap_wsrm_error(soap, NULL, wsrm__UnknownSequence);
  }
  if (soap_wsrm_seq_terminated(soap, seq))
  { soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
    MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  num = soap->header->wsrm__Sequence->MessageNumber;
  if (soap_wsrm_num_lookup(soap, seq, num))
  { struct soap_wsrm_message *p;
    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Message "SOAP_ULONG_FORMAT" already received: duplicate message not processed, non-acked response returned\n", num));
    for (p = seq->messages; p; p = p->next)
    { if (p->num == num)
      { if (p->state == SOAP_WSRM_NACK && p->list) /* not acked */
        { struct soap_wsrm_content *q;
          DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resending message "SOAP_ULONG_FORMAT"\n", num));
	  if (soap_begin_send(soap))
          { MUTEX_UNLOCK(soap_wsrm_session_lock);
	    return soap_closesock(soap);
	  }
          DBGLOG(SENT, SOAP_MESSAGE(fdebug, "\n==== BEGIN RESEND ====\n"));
          for (q = p->list; q; q = q->next)
          { DBGMSG(SENT, q->buf, q->len);
            if (data->fsend(soap, q->buf, q->len))
            { MUTEX_UNLOCK(soap_wsrm_session_lock);
              return soap->error;
	    }
          }
          MUTEX_UNLOCK(soap_wsrm_session_lock);
          if (soap_end_send(soap))
            return soap_closesock(soap);
          DBGLOG(SENT, SOAP_MESSAGE(fdebug, "\n==== END RESEND ====\n"));
	  return soap->error = SOAP_STOP;
        }
	break;
      }
    }
    MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap_send_empty_response(soap, SOAP_OK); /* HTTP 202 */
  }
  if (seq->behavior == DiscardFollowingFirstGap && num != seq->lastnum + 1)
  { soap_wsrm_error(soap, seq, wsrm__SequenceClosed); /* close or term */
    MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  seq->lastnum = num;
  if (soap_wsrm_num_insert(soap, seq, seq->lastnum))
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  return soap_wsrm_add_acks(soap);
}

/**
@fn int soap_wsrm_reply_num(struct soap *soap)
@brief Adds a WS-RM sequence message number to the next message transmitted.
No acks are requested. No WS-Addressing relay.
@param soap context
@return SOAP_OK or error code (and server operation must return this value)
*/
int
soap_wsrm_reply_num(struct soap *soap)
{ struct soap_wsrm_sequence *seq;
  struct soap_wsrm_data *data;
  DBGFUN("soap_wsrm_reply_num");
  data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  if (!soap->header || !soap->header->wsrm__Sequence)
    return soap_wsrm_error(soap, NULL, wsrm__WSRMRequired);
  MUTEX_LOCK(soap_wsrm_session_lock);
  seq = soap_wsrm_seq_lookup_ack(data, soap->header->wsrm__Sequence->Identifier);
  if (!seq)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap_wsrm_error(soap, NULL, wsrm__UnknownSequence);
  }
  if (seq->id)
  { seq->num++;
    if (seq->num == 0)
    { soap_wsrm_error(soap, seq, wsrm__MessageNumberRollover);
      MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error;
    }
    if ((data->msg = soap_wsrm_msg_new(soap, seq, seq->num)) == NULL)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error;
    }
    data->state = SOAP_WSRM_ON;
    data->seq = seq;
    seq->handle = 1; /* do not dealloc until message sent or error */
    soap_default__wsrm__Sequence(soap, soap->header->wsrm__Sequence);
    soap->header->wsrm__Sequence->Identifier = soap_strdup(soap, seq->id);
    soap->header->wsrm__Sequence->MessageNumber = seq->num;
  }
  else
    soap->header->wsrm__Sequence = NULL;
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  soap->header->__sizeAckRequested = 0;
  return SOAP_OK;
}

/**
@fn int soap_wsrm_reply(struct soap *soap, const char *wsa_id, const char *wsa_action)
@brief Server-side server operation reply to be performed when the service
operation returns. Server operations that support WS-Addressing and WS-RM must
call this function to return normally (and allow the response to be relayed).
@param soap context
@param[in] wsa_id WS-Addressing message ID (optional)
@param[in] wsa_action mandatory WS-Addressing action of the response
@return SOAP_OK or error code (and server operation must return this value)
*/
int
soap_wsrm_reply(struct soap *soap, const char *wsa_id, const char *wsa_action)
{ DBGFUN("soap_wsrm_reply");
  if (soap_wsrm_reply_num(soap))
    return soap->error;
  return soap_wsa_reply(soap, wsa_id, wsa_action);
}

/**
@fn int soap_wsrm_reply_request_acks(struct soap *soap, const char *wsa_id, const char *wsa_action)
@brief Server-side server operation reply to be performed when the service
operation returns. Message acks for the current sequence are requested, but
only when client made a create sequence offer. Server operations that support
WS-Addressing and WS-RM must call this function or soap_wsrm_reply() to return
normally (and allow the response to be relayed).
@param soap context
@param[in] wsa_id WS-Addressing message ID (optional)
@param[in] wsa_action mandatory WS-Addressing action of the response
@return SOAP_OK or error code (and server operation must return this value)
*/
int
soap_wsrm_reply_request_acks(struct soap *soap, const char *wsa_id, const char *wsa_action)
{ DBGFUN("soap_wsrm_reply_request_acks");
  if (soap_wsrm_reply_num(soap))
    return soap->error;
  if (soap->header->wsrm__Sequence)
  { if (!soap->header->wsrm__AckRequested && !(soap->header->wsrm__AckRequested = (_wsrm__AckRequested*)soap_malloc(soap, sizeof(_wsrm__AckRequested))))
      return soap->error;
    soap->header->__sizeAckRequested = 1;
    soap_default__wsrm__AckRequested(soap, soap->header->wsrm__AckRequested);
    soap->header->wsrm__AckRequested->Identifier = soap->header->wsrm__Sequence->Identifier;
  }
  return soap_wsa_reply(soap, wsa_id, wsa_action);
}

/**
@fn void soap_wsrm_cleanup(struct soap *soap)
@brief Cleans up all expired sequences and releases resources. To be used at
the server side to periodically clean up WS-RM sequences. Server-side cleanup
is automatic, as long as WS-RM is in use. Does not release client-side
sequences allocated by soap_wsrm_create() or soap_wsrm_create_offer().
@param soap context
*/
void
soap_wsrm_cleanup(struct soap *soap)
{ DBGFUN("soap_wsrm_cleanup");
  soap_wsrm_seq_free(soap, NULL);
}

#ifdef __cplusplus
}
#endif

/******************************************************************************\
 *
 *	Server-side Predefined WS-RM Operations
 *
\******************************************************************************/

/**
@fn int __wsrm__CreateSequence(struct soap *soap, struct wsrm__CreateSequenceType *req, struct wsrm__CreateSequenceResponseType *res)
@brief WS-RM CreateSequence service operation. Automatically invoked by the
server to process a create sequence request (with or without offer). Creates a
new local sequence state to keep track of messages.
@param soap context
@param[in] req wsrm__CreateSequence request message
@param[out] res wsrm__CreateSequenceResponse response message
@return SOAP_OK or error code
*/
int
__wsrm__CreateSequence(struct soap *soap, struct wsrm__CreateSequenceType *req, struct wsrm__CreateSequenceResponseType *res)
{ struct soap_wsrm_sequence *seq;
  struct soap_wsrm_data *data;
  DBGFUN("__wsrm__CreateSequence");
  if (soap_wsa_check(soap))
    return soap->error;
  data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  if (!req)
    return soap_wsrm_error(soap, NULL, wsrm__WSRMRequired);
  soap_wsrm_cleanup(soap);
  MUTEX_LOCK(soap_wsrm_session_lock);
  seq = soap_wsrm_seq_insert(soap, data);
  if (!seq)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  seq->num = 0;
  if (req->AcksTo.Address)
  { char *s;
    s = (char*)malloc(strlen(req->AcksTo.Address) + 1);
    if (!s)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error = SOAP_EOM;
    }
    seq->acksto = strcpy(s, req->AcksTo.Address);
  }
  else
    seq->acksto = NULL;
  if (req->Expires && *req->Expires/1000 < SOAP_WSRM_MAX_SEC_TO_EXPIRE)
    seq->expires = time(NULL) + *req->Expires/1000;
  seq->state = SOAP_WSRM_CREATED;
  if (req->Offer)
  { res->Accept = (struct wsrm__AcceptType*)soap_malloc(soap, sizeof(struct wsrm__AcceptType));
    if (!res->Accept)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error = SOAP_EOM;
    }
    soap_default_wsrm__AcceptType(soap, res->Accept);
    res->Accept->AcksTo.Address = soap->header->wsa5__To; /* TODO: acksto for responses req->AcksTo; */
    res->Expires = req->Expires;
    if (req->Offer->Identifier)
    { /* check if offered identifier is acceptable */
      /* if (!soap_wsrm_seq_lookup(data, req->Offer->Identifier)) */
      { char *s;
        if ((s = (char*)malloc(strlen(req->Offer->Identifier) + 1)))
          seq->id = strcpy(s, req->Offer->Identifier);
      }
    }
    if (req->Offer->Expires && *req->Offer->Expires/1000 < SOAP_WSRM_MAX_SEC_TO_EXPIRE)
      seq->expires = time(NULL) + *req->Offer->Expires/1000;
    /* copy same behavior for response messages */
    if (req->Offer->IncompleteSequenceBehavior)
    { res->IncompleteSequenceBehavior = req->Offer->IncompleteSequenceBehavior;
      seq->behavior = *res->IncompleteSequenceBehavior;
    }
  }
  if (!(seq->acksid = soap_wsrm_seq_newid(soap, data)))
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  res->Identifier = soap_strdup(soap, seq->acksid);
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  soap->header->wsrm__Sequence = NULL;
  soap->header->wsrm__AckRequested = NULL;
  return soap_wsa_reply(soap, NULL, SOAP_NAMESPACE_OF_wsrm"/CreateSequenceResponse");
}

/**
@fn int __wsrm__CloseSequence(struct soap *soap, struct wsrm__CloseSequenceType *req, struct wsrm__CloseSequenceResponseType *res)
@brief WS-RM CloseSequence service operation. Automatically invoked by the
server to process the close sequence request. Updates the server's local
sequence state to closed.
@param soap context
@param[in] req wsrm__CloseSequence request message
@param[out] res wsrm__CloseSequenceResponse response message
@return SOAP_OK or error code
*/
int
__wsrm__CloseSequence(struct soap *soap, struct wsrm__CloseSequenceType *req, struct wsrm__CloseSequenceResponseType *res)
{ struct soap_wsrm_sequence *seq;
  struct soap_wsrm_data *data;
  DBGFUN("__wsrm__CloseSequence");
  if (soap_wsa_check(soap))
    return soap->error;
  data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  if (!req)
    return soap_wsrm_error(soap, NULL, wsrm__WSRMRequired);
  MUTEX_LOCK(soap_wsrm_session_lock);
  seq = soap_wsrm_seq_lookup_ack(data, req->Identifier);
  if (!seq)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap_wsrm_error(soap, NULL, wsrm__UnknownSequence);
  }
  if (seq->state == SOAP_WSRM_TERMINATED)
  { soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
    MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  if (req->LastMsgNumber)
    seq->lastnum = *req->LastMsgNumber;
  seq->state = SOAP_WSRM_CLOSED;
  res->Identifier = req->Identifier;
  soap->header->wsrm__Sequence = NULL;
  if (!soap->header->wsrm__SequenceAcknowledgement && !(soap->header->wsrm__SequenceAcknowledgement = (struct _wsrm__SequenceAcknowledgement*)soap_malloc(soap, sizeof(struct _wsrm__SequenceAcknowledgement))))
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  soap->header->__sizeSequenceAcknowledgement = 1;
  if (soap_wsrm_set_ack(soap, seq, soap->header->wsrm__SequenceAcknowledgement))
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  return soap_wsa_reply(soap, NULL, SOAP_NAMESPACE_OF_wsrm"/CloseSequenceResponse");
}

/**
@fn int __wsrm__TerminateSequence(struct soap *soap, struct wsrm__TerminateSequenceType *req, struct wsrm__TerminateSequenceResponseType *res)
@brief WS-RM TerminateSequence service operation. Automatically invoked by the
server to process the close sequence request. Updates the server's local
sequence state to terminated.
@param soap context
@param[in] req wsrm__TerminateSequence request message
@param[out] res wsrm__TerminateSequenceResponse response message
@return SOAP_OK or error code
*/
int
__wsrm__TerminateSequence(struct soap *soap, struct wsrm__TerminateSequenceType *req, struct wsrm__TerminateSequenceResponseType *res)
{ struct soap_wsrm_sequence *seq;
  struct soap_wsrm_data *data;
  DBGFUN("__wsrm__TerminateSequence");
  if (soap_wsa_check(soap))
    return soap->error;
  data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  if (!req)
    return soap_wsrm_error(soap, NULL, wsrm__WSRMRequired);
  MUTEX_LOCK(soap_wsrm_session_lock);
  seq = soap_wsrm_seq_lookup_ack(data, req->Identifier);
  if (!seq)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap_wsrm_error(soap, NULL, wsrm__UnknownSequence);
  }
  if (seq->state == SOAP_WSRM_TERMINATED)
  { soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
    MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  seq->state = SOAP_WSRM_TERMINATED;
  /* all requests arrived? */
  if (req->LastMsgNumber)
  { seq->lastnum = *req->LastMsgNumber;
    /* DiscardEntireSequence: fail when message sequence is incomplete */
    if (seq->behavior == DiscardEntireSequence && (!seq->ranges || seq->ranges->lower != 1 || seq->ranges->upper != seq->lastnum))
    { soap_wsrm_num_free(soap, seq);
      soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
      MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error;
    }
  }
  /* all responses delivered? (when offer is used: responses acked) */
  if (seq->id && seq->behavior == DiscardEntireSequence)
  { struct soap_wsrm_message *p;
    for (p = seq->messages; p; p = p->next)
    { if (p->state == SOAP_WSRM_NACK)
      { soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
        MUTEX_UNLOCK(soap_wsrm_session_lock);
        return soap->error;
      }
    }
  }
  res->Identifier = req->Identifier;
  soap->header->wsrm__Sequence = NULL;
  if (!soap->header->wsrm__SequenceAcknowledgement && !(soap->header->wsrm__SequenceAcknowledgement = (struct _wsrm__SequenceAcknowledgement*)soap_malloc(soap, sizeof(struct _wsrm__SequenceAcknowledgement))))
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  soap->header->__sizeSequenceAcknowledgement = 1;
  if (soap_wsrm_set_ack(soap, seq, soap->header->wsrm__SequenceAcknowledgement))
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  soap_wsrm_num_free(soap, seq);
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  return soap_wsa_reply(soap, NULL, SOAP_NAMESPACE_OF_wsrm"/TerminateSequenceResponse");
}

/**
@fn int __wsrm__SequenceAcknowledgement(struct soap *soap)
@brief WS-RM SequenceAcknowledgement service operation of the AcksTo
destination service for relayed acknowledgements. Invoked by the server. The
AcksTo server maintains the messages that are acknowledged by updating its
local sequence state information by registering message acknowledgement
receipts.
@param soap context
@return SOAP_OK or error code
*/
int
__wsrm__SequenceAcknowledgement(struct soap *soap)
{ DBGFUN("__wsrm__SequenceAcknowledgement");
  soap_wsrm_cleanup(soap);
  /* soap_wsrm_preparefinalrecv was called upon invocation, to process acks */
  return soap_send_empty_response(soap, SOAP_OK);
}

/******************************************************************************\
 *
 *	Server-side SOAP Fault
 *
\******************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

/**
@fn int soap_wsrm_fault_subcode(struct soap *soap, int flag, const char *faultsubcode, const char *faultstring, const char *faultdetail)
@brief Sets sender/receiver SOAP Fault (sub)code for server faults (can be user
defined faults). When called before soap_wsrm_check() in the server operation,
terminates the current sequence. Otherwise, the sequence is not terminated. In
either case the fault is returned to sender (client) or to the FaultTo server
when the WS-Addressing FaultTo header was set by the sender.
@param soap context
@param[in] flag 0=receiver, 1=sender
@param[in] faultsubcode sub code string
@param[in] faultstring fault string
@param[in] faultdetail detail string
@return SOAP_FAULT
*/
int
soap_wsrm_fault_subcode(struct soap *soap, int flag, const char *faultsubcode, const char *faultstring, const char *faultdetail)
{ if (soap->header && soap->header->wsrm__Sequence)
  { struct soap_wsrm_data *data;
    data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
    if (data)
    { struct soap_wsrm_sequence *seq = soap_wsrm_seq_lookup_ack(data, soap->header->wsrm__Sequence->Identifier);
      seq->state = SOAP_WSRM_TERMINATED;
    }
  }
  return soap_wsa_fault_subcode(soap, flag, faultsubcode, faultstring, faultdetail);
}

/**
@fn int soap_wsrm_sender_fault_subcode(struct soap *soap, const char *faultsubcode, const char *faultstring, const char *faultdetail)
@brief Sets sender SOAP Fault (sub)code for server faults (can be user defined
faults). When called before soap_wsrm_check() in the server operation,
terminates the current sequence. Otherwise, the sequence is not terminated. In
either case the fault is returned to sender (client) or to the FaultTo server
when the WS-Addressing FaultTo header was set by the sender.
@param soap context
@param[in] faultsubcode sub code string
@param[in] faultstring fault string
@param[in] faultdetail detail string
@return SOAP_FAULT
*/
int
soap_wsrm_sender_fault_subcode(struct soap *soap, const char *faultsubcode, const char *faultstring, const char *faultdetail)
{ return soap_wsrm_fault_subcode(soap, 1, faultsubcode, faultstring, faultdetail);
}

/**
@fn int soap_wsrm_receiver_fault_subcode(struct soap *soap, const char *faultsubcode, const char *faultstring, const char *faultdetail)
@brief Sets receiver SOAP Fault (sub)code for server faults (can be user
defined faults). When called before soap_wsrm_check() in the server operation,
terminates the current sequence. Otherwise, the sequence is not terminated. In
either case the fault is returned to sender (client) or to the FaultTo server
when the WS-Addressing FaultTo header was set by the sender.
@param soap context
@param[in] faultsubcode sub code string
@param[in] faultstring fault string
@param[in] faultdetail detail string
@return SOAP_FAULT
*/
int
soap_wsrm_receiver_fault_subcode(struct soap *soap, const char *faultsubcode, const char *faultstring, const char *faultdetail)
{ return soap_wsrm_fault_subcode(soap, 0, faultsubcode, faultstring, faultdetail);
}

/**
@fn int soap_wsrm_sender_fault(struct soap *soap, const char *faultstring, const char *faultdetail)
@brief Sets sender SOAP Fault for server faults (can be user defined faults).
When called before soap_wsrm_check() in the server operation, terminates the
current sequence. Otherwise, the sequence is not terminated. In either case the
fault is returned to sender (client) or to the FaultTo server when the
WS-Addressing FaultTo header was set by the sender.
@param soap context
@param[in] faultstring fault string
@param[in] faultdetail detail string
@return SOAP_FAULT
*/
int
soap_wsrm_sender_fault(struct soap *soap, const char *faultstring, const char *faultdetail)
{ return soap_wsrm_fault_subcode(soap, 1, NULL, faultstring, faultdetail);
}

/**
@fn int soap_wsrm_receiver_fault(struct soap *soap, const char *faultstring, const char *faultdetail)
@brief Sets receiver SOAP Fault for server faults (can be user defined faults).
When called before soap_wsrm_check() in the server operation, terminates the
current sequence. Otherwise, the sequence is not terminated. In either case the
fault is returned to sender (client) or to the FaultTo server when the
WS-Addressing FaultTo header was set by the sender.
@param soap context
@param[in] faultstring fault string
@param[in] faultdetail detail string
@return SOAP_FAULT
*/
int
soap_wsrm_receiver_fault(struct soap *soap, const char *faultstring, const char *faultdetail)
{ return soap_wsrm_fault_subcode(soap, 0, NULL, faultstring, faultdetail);
}

/******************************************************************************\
 *
 *	WS-RM Faults
 *
\******************************************************************************/

/**
@fn int soap_wsrm_check_fault(struct soap *soap, enum wsrm__FaultCodes *fault, const char **info)
@brief Checks the presence of a WS-RM fault at the client side (or in the
FaultTo destination service) when a response is received.
@param soap context
@param[out] fault code
@param[out] info string pointer related to the fault (or NULL)
@return SOAP_OK (no fault or not a WS-RM fault) or error when not WS-RM fault
*/
int
soap_wsrm_check_fault(struct soap *soap, enum wsrm__FaultCodes *fault, const char **info)
{ if (soap->error && soap->fault && soap->fault->SOAP_ENV__Code)
  { const char *code = *soap_faultsubcode(soap);
    if (info)
      *info = *soap_faultdetail(soap);
    if (code)
      return soap_s2wsrm__FaultCodes(soap, code, fault);
  }
  return SOAP_OK;
}

/**
@fn int soap_wsrm_error(struct soap *soap, struct soap_wsrm_sequence *seq, enum wsrm__FaultCodes fault)
@brief Sets SOAP Fault (sub)code for server WS-RM fault response. Terminates
the sequence.
@param soap context
@param[in] seq pointer to sequence to terminate or NULL
@param[in] fault is one of enum wsrm__FaultCodes enumeration values
@return SOAP_FAULT
*/
int
soap_wsrm_error(struct soap *soap, struct soap_wsrm_sequence *seq, enum wsrm__FaultCodes fault)
{ struct soap_wsrm_data *data;
  const char *code = soap_wsrm__FaultCodes2s(soap, fault);
  const char *reason = NULL;
  DBGFUN1("soap_wsrm_error", "code=%s", code?code:"(null)");
  data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (data)
  { data->state = SOAP_WSRM_OFF; /* disable caching*/
    if (data->seq)
      data->seq->handle = 0;
    data->seq = NULL;
  }
  if (seq)
  { seq->fault = fault;
    seq->state = SOAP_WSRM_TERMINATED;
  }
  switch (fault)
  { case wsrm__SequenceTerminated:
      reason = "The Sequence has been terminated due to an unrecoverable error.";
      break;
    case wsrm__UnknownSequence:
      reason = "The value of the wsrm:Identifier is not a known Sequence identifier.";
      break;
    case wsrm__InvalidAcknowledgement:
      reason = "The SequenceAcknowledgement violates the cumulative Acknowledgement invariant.";
      break;
    case wsrm__MessageNumberRollover:
      reason = "The maximum value for wsrm:MessageNumber has been exceeded.";
      break;
    case wsrm__CreateSequenceRefused:
      reason = "The Create Sequence request has been refused by the RM Destination.";
      break;
    case wsrm__SequenceClosed:
      reason = "The Sequence is closed and cannot accept new messages.";
      break;
    case wsrm__WSRMRequired:
      reason = "The RM Destination requires the use of WSRM.";
      break;
    default:
      break;
  }
  if (soap->version == 1)
  { soap_header(soap);
    if (soap->header)
    { soap_default_SOAP_ENV__Header(soap, soap->header);
      soap->header->wsrm__SequenceFault = (struct wsrm__SequenceFaultType*)soap_malloc(soap, sizeof(struct wsrm__SequenceFaultType));
      if (soap->header->wsrm__SequenceFault)
      { soap_default_wsrm__SequenceFaultType(soap, soap->header->wsrm__SequenceFault);
        soap->header->wsrm__SequenceFault->FaultCode = fault;
        soap->header->wsrm__SequenceFault->Detail = (struct SOAP_ENV__Detail*)soap_malloc(soap, sizeof(struct SOAP_ENV__Detail));
	if (soap->header->wsrm__SequenceFault->Detail)
	{ soap_default_SOAP_ENV__Detail(soap, soap->header->wsrm__SequenceFault->Detail);
          soap->header->wsrm__SequenceFault->Detail->__any = (char*)reason;
        }
      }
    }
  }
  else
    soap->header = NULL;
  return soap_wsa_sender_fault_subcode(soap, code, reason, NULL);
}

/******************************************************************************\
 *
 *	WS-RM State Dump
 *
\******************************************************************************/

/**
@fn void soap_wsrm_dump(struct soap *soap, FILE *fd)
@brief Dumps the current sequences and details of the wsrm plugin for
diagnotics purposes.
@param soap context
@param[in] fd file descriptor to send text to
*/
void
soap_wsrm_dump(struct soap *soap, FILE *fd)
{ struct soap_wsrm_sequence *seq;
  struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return;
  fprintf(fd, "\n**** %s STATE DUMP ****\n", SOAP_WSRM_ID);
  MUTEX_LOCK(soap_wsrm_session_lock);
  for (seq = soap_wsrm_session; seq; seq = seq->next)
  { struct soap_wsrm_range *r;
    struct soap_wsrm_message *p;
    switch (seq->state)
    { case SOAP_WSRM_UNKNOWN:
        fputs("UNKNOWN ", fd);
	break;
      case SOAP_WSRM_CREATED:
        fputs("CREATED ", fd);
	break;
      case SOAP_WSRM_CLOSED:
        fputs("CLOSED ", fd);
	break;
      case SOAP_WSRM_TERMINATED:
        fputs("TERMINATED ", fd);
	break;
      default:
        break;
    }
    fprintf(fd, "SEQUENCE %s\n", seq->acksid?seq->acksid:"");
    fprintf(fd, "  ID       = %s\n", seq->id?seq->id:"(null)");
    fprintf(fd, "  TO       = %s\n", seq->to?seq->to:"(null)");
    fprintf(fd, "  ACKSTO   = %s\n", seq->acksto?seq->acksto:"(null)");
    fprintf(fd, "  EXPIRES  = %s\n", soap_dateTime2s(soap, seq->expires));
    fprintf(fd, "  BEHAVIOR = %s\n", soap_wsrm__IncompleteSequenceBehaviorType2s(soap, seq->behavior));
    if (seq->id)
    { const char *sep = "";
      fprintf(fd, "  SENT MESSAGES:\n");
      fprintf(fd, "    NUM    = "SOAP_ULONG_FORMAT"\n", seq->num);
      fprintf(fd, "    ACKED  =");
      for (p = seq->messages; p; p = p->next)
      { if (p->state == SOAP_WSRM_ACK)
        { fprintf(fd, "%s "SOAP_ULONG_FORMAT, sep, p->num); 
          sep = ",";
        }
      }
      fprintf(fd, "\n");
    }
    if (seq->acksid)
    { const char *sep = "";
      fprintf(fd, "  RECEIVED MESSAGES:\n");
      fprintf(fd, "    LAST   = "SOAP_ULONG_FORMAT"\n", seq->lastnum);
      fprintf(fd, "    RANGE  =");
      for (r = seq->ranges; r; r = r->next)
      { fprintf(fd, "%s "SOAP_ULONG_FORMAT":"SOAP_ULONG_FORMAT, sep, r->lower, r->upper); 
        sep = ",";
      }
      fprintf(fd, "\n");
    }
    fprintf(fd, "END OF SEQUENCE\n");
  }
  MUTEX_UNLOCK(soap_wsrm_session_lock);
}

/******************************************************************************\
 *
 *	Plugin registry functions
 *
\******************************************************************************/

/**
@fn int soap_wsrm(struct soap *soap, struct soap_plugin *p, void *arg)
@brief Plugin registry function, used with soap_register_plugin.
@param soap context
@param[in,out] p plugin created in registry
@param[in] arg optional argument passed from soap_register_plugin_arg
@return SOAP_OK
*/
int
soap_wsrm(struct soap *soap, struct soap_plugin *p, void *arg)
{ p->id = soap_wsrm_id;
  /* create local plugin data */
  p->data = (void*)SOAP_MALLOC(soap, sizeof(struct soap_wsrm_data));
  /* register the copy constructor */
  p->fcopy = soap_wsrm_copy;
  /* register the destructor */
  p->fdelete = soap_wsrm_delete;
  /* if OK then initialize */
  if (p->data)
  { if (soap_wsrm_init(soap, (struct soap_wsrm_data*)p->data, arg))
    { SOAP_FREE(soap, p->data); /* error: could not init */
      return SOAP_EOM; /* return error */
    }
  }
  return SOAP_OK;
}

/**
@fn int soap_wsrm_init(struct soap *soap, struct soap_wsrm_data *data, void *arg)
@brief Initializes plugin data.
@param soap context
@param[in,out] data plugin data
@param[in] arg optional argument passed from soap_register_plugin_arg
@return SOAP_OK
*/
static int
soap_wsrm_init(struct soap *soap, struct soap_wsrm_data *data, void *arg)
{ if (arg || !soap_wsrm_idname)
  { char *s, t[40];
    if (arg)
      s = (char*)arg;
    else
    { time_t r = time(NULL);
      sprintf(t, "urn:uuid:%08x-%04x-%04x-%04x-%04x", (int)r, soap_random & 0xFFFF, soap_random & 0xFFFF, soap_random & 0xFFFF, soap_random & 0xFFFF);
      s = t;
    }
    MUTEX_LOCK(soap_wsrm_session_lock);
    if (soap_wsrm_idname)
      free((void*)soap_wsrm_idname);
    soap_wsrm_idname = strcpy((char*)malloc(strlen(s) + 1), s);
    MUTEX_UNLOCK(soap_wsrm_session_lock);
  }
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  data->seq = NULL;
  data->msg = NULL;
  data->fsend = soap->fsend;
  soap->fsend = soap_wsrm_send;
  data->fpreparefinalrecv = soap->fpreparefinalrecv;
  soap->fpreparefinalrecv = soap_wsrm_preparefinalrecv;
  data->fdisconnect = soap->fdisconnect;
  soap->fdisconnect = soap_wsrm_disconnect;
  return SOAP_OK;
}

/**
@fn int soap_wsrm_copy(struct soap *soap, struct soap_plugin *p, struct soap_plugin *q)
@brief Copy plugin data.
@param soap context
@param[out] p plugin
@param[in] q plugin
@return SOAP_OK
*/
static int
soap_wsrm_copy(struct soap *soap, struct soap_plugin *p, struct soap_plugin *q)
{ /* create local plugin data */
  p->data = (void*)SOAP_MALLOC(soap, sizeof(struct soap_wsrm_data));
  memcpy(p->data, q->data, sizeof(struct soap_wsrm_data));
  ((struct soap_wsrm_data*)p->data)->state = SOAP_WSRM_OFF;
  ((struct soap_wsrm_data*)p->data)->seq = NULL;
  ((struct soap_wsrm_data*)p->data)->msg = NULL;
  return SOAP_OK;
}

/**
@fn void soap_wsrm_delete(struct soap *soap, struct soap_plugin *p)
@brief Deletes plugin data.
@param soap context
@param[in,out] p plugin
@return SOAP_OK
*/
static void
soap_wsrm_delete(struct soap *soap, struct soap_plugin *p)
{ if (p->data)
    SOAP_FREE(soap, p->data);
}

/******************************************************************************\
 *
 *	Callbacks registered by plugin
 *
\******************************************************************************/

/**
@fn int soap_wsrm_send(struct soap *soap, const char *buf, size_t len)
@brief Internal callback function to override fsend(). When the wsrm plugin is
enabled, saves the message to the current sequence that is created at the
client side. Allows unacknowledged messages to be resend with
soap_wsrm_resend().
@param soap context
@param[in] buf message data
@param[in] len message data length
@return SOAP_OK or error code
*/
static int
soap_wsrm_send(struct soap *soap, const char *buf, size_t len)
{ struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (data->state == SOAP_WSRM_ON)
    if (soap_wsrm_msg_append(soap, data, buf, len))
      return soap->error;
  return data->fsend(soap, buf, len); /* pass data on to next send callback */
}

/**
@fn int soap_wsrm_preparefinalrecv(struct soap *soap)
@brief Internal callback function to override fpreparefinalrecv(). Takes acks
returned by response to update the states of the sequences with
acknowledgements. Acknowledged messages do not have to be resend and are
purged from the sequence states. Also checks if at client-side response message
was already received and otherwise records it in the state.
@param soap context
@return SOAP_OK or error code
*/
static int
soap_wsrm_preparefinalrecv(struct soap *soap)
{ struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  DBGFUN("soap_wsrm_preparefinalrecv");
  if (data->fpreparefinalrecv && data->fpreparefinalrecv(soap))
    return soap->error;
#if 0
  /* record service response message num for acks (client side) */
  if (soap->header && soap->header->wsrm__Sequence)
  { struct soap_wsrm_sequence *seq;
    seq = soap_wsrm_seq_lookup_ack(data, soap->header->wsrm__Sequence->Identifier);
    if (seq)
    { if (soap_wsrm_num_lookup(soap, seq, soap->header->wsrm__Sequence->MessageNumber))
      { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Response message already received\n"));
        return soap->error = SOAP_STOP;
      }
      if (seq->behavior == DiscardFollowingFirstGap && soap->header->wsrm__Sequence->MessageNumber != seq->lastnum + 1)
        return soap_wsrm_error(soap, seq, wsrm__SequenceClosed); /* close or term */
      seq->lastnum = soap->header->wsrm__Sequence->MessageNumber;
      if (soap_wsrm_num_insert(soap, seq, seq->lastnum))
        return soap->error;
    }
  }
#endif
  if (soap->header && soap->header->wsrm__SequenceAcknowledgement)
  { int i;
    for (i = 0; i < soap->header->__sizeSequenceAcknowledgement; i++)
    { if (soap_wsrm_process_ack(soap, &soap->header->wsrm__SequenceAcknowledgement[i]))
        return soap->error;
    }
  }
  return SOAP_OK;
}

/**
@fn int soap_wsrm_disconnect(struct soap *soap)
@brief Internal callback function to override fdisconnect(). Turns caching off,
sets the cached message to NACK, and resets the server-side handle, so sequence
can be deallocated later.
@param soap context
@return SOAP_OK or error code
*/
static int
soap_wsrm_disconnect(struct soap *soap)
{ struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  DBGFUN("soap_wsrm_disconnect");
  if (data->fdisconnect && data->fdisconnect(soap))
    return soap->error;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  if (data->msg)
    data->msg->state = SOAP_WSRM_NACK;
  data->msg = NULL;
  if (data->seq)
    data->seq->handle = 0;
  data->seq = NULL;
  return SOAP_OK;
}

/******************************************************************************\
 *
 *	Process Acknowledgements
 *
\******************************************************************************/

/**
@fn int soap_wsrm_process_ack(struct soap *soap, struct _wsrm__SequenceAcknowledgement *ack)
@brief Internal function to purge acknowledged messages as given by
wsrm:SequenceAcknowledgement header.
@param soap context
@param[in] ack from the WS-RM SequenceAcknowledgement header.
@return SOAP_OK or error code
*/
static int
soap_wsrm_process_ack(struct soap *soap, struct _wsrm__SequenceAcknowledgement *ack)
{ struct soap_wsrm_sequence *seq;
  struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  DBGFUN1("soap_wsrm_process_ack", "ack id=%s", ack->Identifier?ack->Identifier:"none");
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  MUTEX_LOCK(soap_wsrm_session_lock);
  seq = soap_wsrm_seq_lookup(data, ack->Identifier);
  if (!seq)
  { char *s;
    /* if we fail? return soap_wsrm_error(soap, NULL, wsrm__UnknownSequence); */
    if (!(seq = soap_wsrm_seq_insert(soap, data)))
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error;
    }
    seq->state = SOAP_WSRM_UNKNOWN;
    s = (char*)malloc(strlen(ack->Identifier) + 1);
    if (!s)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error = SOAP_EOM;
    }
    seq->id = strcpy(s, ack->Identifier);
  }
  /* if Final no new message allowed (resends OK) */
  if (ack->Final)
  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Final\n"));
    /* sequence is closed, so soap_wsrm_request() fails anyway */
  }
  /* process Nack */
  if (ack->__sizeNack)
  { int i;
    for (i = 0; i < ack->__sizeNack; i++)
    { struct soap_wsrm_message *p;
      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Nack="SOAP_LONG_FORMAT"\n", ack->Nack[i]));
      for (p = seq->messages; p; p = p->next)
      { if (p->num == ack->Nack[i])
          p->state = SOAP_WSRM_NACK;
      }
    }
  }
  else if (!ack->None) /* else free acked messages, unless None */
  { int i;
    for (i = 0; i < ack->__sizeAcknowledgementRange; i++)
    { struct soap_wsrm_message *p;
      if (seq->state == SOAP_WSRM_UNKNOWN)
      { ULONG64 j;
        for (j = ack->AcknowledgementRange[i].Lower; j <= ack->AcknowledgementRange[i].Upper; j++)
        { for (p = seq->messages; p; p = p->next)
	    if (p->num == j)
	      break;
	  if (!p && !(p = soap_wsrm_msg_new(soap, seq, j)))
          { MUTEX_UNLOCK(soap_wsrm_session_lock);
            return soap->error;
	  }
          p->state = SOAP_WSRM_ACK; /* just added msg */
        }
      }
      else if (ack->AcknowledgementRange[i].Upper > seq->num)
      { soap_wsrm_error(soap, seq, wsrm__InvalidAcknowledgement);
        MUTEX_UNLOCK(soap_wsrm_session_lock);
	return soap->error;
      }
      else
      { for (p = seq->messages; p; p = p->next)
        { if (ack->AcknowledgementRange[i].Lower <= p->num && p->num <= ack->AcknowledgementRange[i].Upper)
          { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Ack="SOAP_LONG_FORMAT"\n", p->num));
            p->state = SOAP_WSRM_ACK;
            if (p->list)
              soap_wsrm_msg_free(soap, p);
          }
        }
      }
    }
  }
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  return SOAP_OK;
}

/******************************************************************************\
 *
 *	Add and/or Send Acknowledgements
 *
\******************************************************************************/

/**
@fn int soap_wsrm_add_acks(struct soap *soap)
@brief Internal function to add SequenceAcknowledgement headers for
AckRequested. Sends acknowledgements to the AcksTo destination service or back
to the source. These sends can take SOAP_WSRM_TIMEOUT seconds time, with at
most 10 retries performed.
@param soap context
@return SOAP_OK or error code
*/
static int
soap_wsrm_add_acks(struct soap *soap)
{ const char *from = NULL, *to = NULL, *replyto = NULL;
  int numack = 0;
  struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  DBGFUN("soap_wsrm_add_acks");
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  if (!soap->header)
    return soap_wsrm_error(soap, NULL, wsrm__WSRMRequired);
  if (soap->header->wsa5__From && soap->header->wsa5__From->Address && strcmp(soap->header->wsa5__From->Address, soap_wsa_anonymousURI))
    from = soap->header->wsa5__From->Address;
  if (soap->header->wsa5__To && strcmp(soap->header->wsa5__To, soap_wsa_anonymousURI))
    to = soap->header->wsa5__To;
  if (soap->header->wsa5__ReplyTo && soap->header->wsa5__ReplyTo->Address && strcmp(soap->header->wsa5__ReplyTo->Address, soap_wsa_anonymousURI))
    replyto = soap->header->wsa5__ReplyTo->Address;
  soap->header->wsrm__SequenceAcknowledgement = NULL;
  soap->header->wsrm__SequenceFault = NULL;
  /* acks requested? */
  if (soap->header->__sizeAckRequested)
  { int i;
    struct _wsrm__SequenceAcknowledgement *ack;
    ack = (struct _wsrm__SequenceAcknowledgement*)soap_malloc(soap, soap->header->__sizeAckRequested * sizeof(struct _wsrm__SequenceAcknowledgement));
    if (!ack)
      return SOAP_EOM;
    /* for each ack requested, return ack response in header: */
    numack = 0;
    for (i = 0; i < soap->header->__sizeAckRequested; i++)
    { struct soap_wsrm_sequence *seq;
      MUTEX_LOCK(soap_wsrm_session_lock);
      seq = soap_wsrm_seq_lookup_ack(data, soap->header->wsrm__AckRequested->Identifier);
      if (!seq)
      { MUTEX_UNLOCK(soap_wsrm_session_lock);
        return soap_wsrm_error(soap, NULL, wsrm__UnknownSequence);
      }
      /* acks with non-anonym acksto destination */
      /* TODO: this might not be the best place to do since it takes time */
      if (seq->acksto
       && strcmp(seq->acksto, soap_wsa_anonymousURI)
       && (!from    || strcmp(from,    seq->acksto))
       && (!to      || strcmp(to,      seq->acksto))
       && (!seq->to || strcmp(seq->to, seq->acksto))
       && (!replyto || strcmp(replyto, seq->acksto)))
      { int retry;
        const char *acksto;
        struct soap *acksto_soap = soap_copy(soap);
        struct _wsrm__SequenceAcknowledgement ack;
        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Sending ack for seq %s to %s\n", seq->id, seq->acksto));
	acksto_soap->socket = SOAP_INVALID_SOCKET;
	acksto_soap->connect_timeout = SOAP_WSRM_TIMEOUT;
	acksto_soap->recv_timeout = SOAP_WSRM_TIMEOUT;
	acksto_soap->send_timeout = SOAP_WSRM_TIMEOUT;
	acksto_soap->header = NULL;
	soap_header(acksto_soap);
	soap_default_SOAP_ENV__Header(acksto_soap, acksto_soap->header);
        acksto_soap->header->__sizeSequenceAcknowledgement = 1;
	acksto = soap_strdup(acksto_soap, seq->acksto);
	if (soap_wsrm_set_ack(soap, seq, &ack))
	{ soap_end(acksto_soap);
	  soap_free(acksto_soap);
          MUTEX_UNLOCK(soap_wsrm_session_lock);
	  return soap->error;
	}
        MUTEX_UNLOCK(soap_wsrm_session_lock);
        acksto_soap->header->wsrm__SequenceAcknowledgement = &ack;
        soap_wsa_request(acksto_soap, NULL, acksto, SOAP_NAMESPACE_OF_wsrm"/SequenceAcknowledgement");
        if (soap->header->wsa5__FaultTo && soap->header->wsa5__FaultTo->Address)
	  soap_wsa_add_FaultTo(acksto_soap, soap->header->wsa5__FaultTo->Address);
	else
	  soap_wsa_add_FaultTo(acksto_soap, soap_wsa_noneURI);
        data->state = SOAP_WSRM_OFF; /* disable caching*/
	/* send, retry when HTTP 307 at most 10 times */
	retry = 10;
        while (soap_send___wsrm__SequenceAcknowledgement(acksto_soap, acksto, NULL))
	{ if (soap->error != 307 || retry-- == 0)
	    break;
	  acksto = soap_strdup(acksto_soap, soap->endpoint);
	}
	if (!soap->error)
	  soap_recv_empty_response(acksto_soap);
	soap_end(acksto_soap);
	soap_free(acksto_soap);
      }
      else
      { if (soap_wsrm_set_ack(soap, seq, &ack[numack]))
        { MUTEX_UNLOCK(soap_wsrm_session_lock);
          return soap->error;
	}
        MUTEX_UNLOCK(soap_wsrm_session_lock);
        numack++;
      }
    }
    soap->header->__sizeSequenceAcknowledgement = numack;
    soap->header->wsrm__SequenceAcknowledgement = ack;
    soap->header->wsa5__Action = SOAP_NAMESPACE_OF_wsrm"/SequenceAcknowledgement";
  }
  soap->header->__sizeAckRequested = 0;
#if 0
  if (numack && from && replyto && strcmp(from, replyto)) /* requires From */
  /* if (numack && replyto && (!from || strcmp(from, replyto))) */
  { int (*fpost)(struct soap*, const char*, const char*, int, const char*, const char*, size_t);
    _wsrm__Sequence *s;
    /* override HTTP POST by HTTP OK */
    fpost = soap->fpost;
    soap->fpost = soap_wsrm_post;
    s = soap->header->wsrm__Sequence;
    soap->header->wsrm__Sequence = NULL;
    soap_send___wsrm__SequenceAcknowledgement(soap, "", NULL);
    soap->header->wsrm__Sequence = s;
    soap->fpost = fpost;
    /* remove acks from response message to be relayed to ReplyTo */
    soap->header->__sizeSequenceAcknowledgement = 0;
    soap->header->wsrm__SequenceAcknowledgement = NULL;
    /* prevent wsa from sending ACCEPTED */
    soap->keep_alive = 0;
    return soap_closesock(soap);
  }
#endif
  return SOAP_OK;
}

/**
@fn int soap_wsrm_set_ack(struct soap *soap, struct soap_wsrm_sequence *seq, struct _wsrm__SequenceAcknowledgement *ack)
@brief Internal function called by soap_wsrm_add_acks() to populate the
SequenceAcknowledgement header block.
@param soap context
@param[in] seq pointer to sequence
@param[out] ack pointer to SequenceAcknowledgement to populate
@brief
*/
static int
soap_wsrm_set_ack(struct soap *soap, struct soap_wsrm_sequence *seq, struct _wsrm__SequenceAcknowledgement *ack)
{ DBGFUN("soap_wsrm_set_ack");
  soap_default__wsrm__SequenceAcknowledgement(soap, ack);
  ack->Identifier = soap_strdup(soap, seq->acksid);
  ack->__sizeNack = 0;
  ack->Nack = NULL;
  ack->__sizeAcknowledgementRange = soap_wsrm_num_size(seq);
  if (ack->__sizeAcknowledgementRange == 0)
  { ack->AcknowledgementRange = NULL;
    ack->None = (struct _wsrm__SequenceAcknowledgement_None*)soap_malloc(soap, sizeof(struct _wsrm__SequenceAcknowledgement_None));
    if (!ack->None)
      return SOAP_EOM;
    soap_default__wsrm__SequenceAcknowledgement_None(soap, ack->None);
  }
  else
  { struct soap_wsrm_range *p;
    struct _wsrm__SequenceAcknowledgement_AcknowledgementRange *q;
    ack->AcknowledgementRange = (struct _wsrm__SequenceAcknowledgement_AcknowledgementRange*)soap_malloc(soap, ack->__sizeAcknowledgementRange * sizeof(struct _wsrm__SequenceAcknowledgement_AcknowledgementRange));
    if (!ack->AcknowledgementRange)
      return SOAP_EOM;
    soap_default__wsrm__SequenceAcknowledgement_AcknowledgementRange(soap, ack->AcknowledgementRange);
    for (p = seq->ranges, q = ack->AcknowledgementRange; p; p = p->next, q++)
    { q->Lower = p->lower;
      q->Upper = p->upper;
    }
    ack->None = NULL;
  }
  /* if closed, Final is set */
  if (seq->state == SOAP_WSRM_CREATED)
    ack->Final = NULL;
  else
  { ack->Final = (struct _wsrm__SequenceAcknowledgement_Final*)soap_malloc(soap, sizeof(struct _wsrm__SequenceAcknowledgement_Final));
    if (!ack->Final)
      return SOAP_EOM;
    soap_default__wsrm__SequenceAcknowledgement_Final(soap, ack->Final);
  }
  return SOAP_OK;
}

#if 0
/**
@fn int soap_wsrm_post(struct soap *soap, const char *endpoint, const char *host, int port, const char *path, const char *action, size_t count)
@brief Internal callback overrides the HTTP post operations to send an HTTP OK
response header in place.
@param soap context
@param endpoint (not used)
@param host (not used)
@param port (not used)
@param path (not used)
@param action (HTTP action not used)
@param count message length (if non-chunked)
*/
static int
soap_wsrm_post(struct soap *soap, const char *endpoint, const char *host, int port, const char *path, const char *action, size_t count)
{ return soap->fresponse(soap, SOAP_OK, count);
}
#endif

/******************************************************************************\
 *
 *	WS-RM Resend
 *
\******************************************************************************/

/**
@fn int soap_wsrm_resend_seq(struct soap *soap, struct soap_wsrm_sequence *seq, ULONG64 lower, ULONG64 upper)
@brief Internal function to resend unacknowledged messages of a sequence given a
range of message numbers. Used by soap_wsrm_resend().
@param soap context
@param seq sequence handle set by soap_wsrm_create() or soap_wsrm_create_offer()
@param[in] lower resend message range lower bound (0 for lowest)
@param[in] upper resend message range upper bound (or 0 for infinite)
@return SOAP_OK or error code (can be ignored to continue sequence)
*/
static int
soap_wsrm_resend_seq(struct soap *soap, struct soap_wsrm_sequence *seq, ULONG64 lower, ULONG64 upper)
{ struct soap_wsrm_message *p;
  struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  DBGFUN2("soap_wsrm_resend_seq", "lower="SOAP_LONG_FORMAT, lower, "upper="SOAP_LONG_FORMAT, upper);
  if (!data)
    return soap->error = SOAP_PLUGIN_ERROR;
  data->state = SOAP_WSRM_OFF; /* disable caching*/
  for (p = seq->messages; p; p = p->next)
  { if (p->num >= lower && (p->num <= upper || upper == 0))
    { if (p->state == SOAP_WSRM_NACK && p->list) /* not acked */
      { struct soap_wsrm_content *q;
        DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resending message "SOAP_ULONG_FORMAT"\n", p->num));
	soap->omode |= SOAP_ENC_XML; /* disables HTTP */
        if (soap_connect(soap, seq->to, NULL))
          return soap->error;
        DBGLOG(SENT, SOAP_MESSAGE(fdebug, "\n==== BEGIN RESEND ====\n"));
        for (q = p->list; q; q = q->next)
        { DBGMSG(SENT, q->buf, q->len);
          if (data->fsend(soap, q->buf, q->len))
            return soap->error;
        }
        if (soap_end_send(soap))
          return soap_closesock(soap);
        DBGLOG(SENT, SOAP_MESSAGE(fdebug, "\n==== END RESEND ====\n"));
	soap->omode &= ~SOAP_ENC_XML; /* reenables HTTP */
	if (!soap_begin_recv(soap))
	  soap_ignore_element(soap); /* read content but ignore */
	else if (soap->error != SOAP_NO_DATA && soap->error != 202)
	  return soap_closesock(soap);
	soap_end_recv(soap);
	soap_closesock(soap);
      }
    }
  }
  return SOAP_OK;
}

/******************************************************************************\
 *
 *	Sequences
 *
\******************************************************************************/

/**
@fn const char *soap_wsrm_seq_newid(struct soap *soap, struct soap_wsrm_data *data)
@brief Internal function to generate a new sequence identifier.
@param soap context
@param data plugin
@return sequence identifier string
*/
static const char *
soap_wsrm_seq_newid(struct soap *soap, struct soap_wsrm_data *data)
{ char *s;
  s = (char*)malloc(strlen(soap_wsrm_idname) + 16);
  if (!s)
  { soap->error = SOAP_EOM;
    return NULL;
  }
  sprintf(s, "%s%08lx", soap_wsrm_idname, soap_wsrm_idnum++);
  return s;
}

/**
@fn struct soap_wsrm_sequence *soap_wsrm_seq_lookup(const struct soap_wsrm_data *data, const char *id)
@brief Internal function to look up sequence given its id.
@param data plugin
@param[in] id identifier string
@return sequence or NULL
*/
static struct soap_wsrm_sequence *
soap_wsrm_seq_lookup(const struct soap_wsrm_data *data, const char *id)
{ if (id)
  { struct soap_wsrm_sequence *p;
    for (p = soap_wsrm_session; p; p = p->next)
      if (p->id && !strcmp(p->id, id))
        return p;
  }
  return NULL;
}

/**
@fn struct soap_wsrm_sequence *soap_wsrm_seq_lookup_ack(const struct soap_wsrm_data *data, const char *id)
@brief Internal function to look up sequence given its acksid.
@param data plugin
@param[in] id identifier string
@return sequence or NULL
*/
static struct soap_wsrm_sequence *
soap_wsrm_seq_lookup_ack(const struct soap_wsrm_data *data, const char *id)
{ if (id)
  { struct soap_wsrm_sequence *p;
    for (p = soap_wsrm_session; p; p = p->next)
      if (p->acksid && !strcmp(p->acksid, id))
        return p;
  }
  return NULL;
}

/**
@fn struct soap_wsrm_sequence *soap_wsrm_seq_insert(struct soap *soap, struct soap_wsrm_data *data)
@brief Internal function to create a new local sequence state.
@param soap context
@param data plugin
@return sequence or NULL
*/
static struct soap_wsrm_sequence *
soap_wsrm_seq_insert(struct soap *soap, struct soap_wsrm_data *data)
{ struct soap_wsrm_sequence *seq;
  seq = (struct soap_wsrm_sequence*)malloc(sizeof(struct soap_wsrm_sequence));
  if (!seq)
  { soap->error = SOAP_EOM;
    return NULL;
  }
  seq->handle = 0;
  seq->id = NULL;
  seq->acksid = NULL;
  seq->to = NULL;
  seq->acksto = NULL;
  seq->expires = time(NULL) + SOAP_WSRM_MAX_SEC_TO_EXPIRE;
  seq->behavior = NoDiscard;
  seq->num = 0;
  seq->lastnum = 0;
  seq->fault = (enum wsrm__FaultCodes)(-1);
  seq->state = SOAP_WSRM_NONE;
  seq->retry = 0;
  seq->messages = NULL;
  seq->ranges = NULL;
  seq->next = soap_wsrm_session;
  soap_wsrm_session = seq;
  return seq;
}

/**
@fn int soap_wsrm_msg_append(struct soap *soap, struct soap_wsrm_data *data, const char *buf, size_t len)
@brief Internal function used by soap_wsrm_send() to append message data to the
current message being transmitted for the current sequence.
@param soap context
@param data plugin
@param[in] buf message data
@param[in] len message data length
@return SOAP_OK or error code
*/
static int
soap_wsrm_msg_append(struct soap *soap, struct soap_wsrm_data *data, const char *buf, size_t len)
{ struct soap_wsrm_content *p;
  DBGFUN1("soap_wsrm_msg_append", "len=%lu", (unsigned long)len);
  /* a message must be present */
  if (!data->msg)
    return soap->error = SOAP_PLUGIN_ERROR;
  p = (struct soap_wsrm_content*)malloc(sizeof(struct soap_wsrm_content));
  if (!p)
    return soap->error = SOAP_EOM;
  p->buf = NULL;
  p->next = NULL;
  if (!data->msg->list)
    data->msg->list = p;
  if (data->msg->last)
    data->msg->last->next = p;
  data->msg->last = p;
  p->buf = (char*)malloc(len);
  if (!p->buf)
    return soap->error = SOAP_EOM;
  p->len = len;
  memcpy(p->buf, buf, len);
  return SOAP_OK;
}

/**
@fn int soap_wsrm_seq_terminated(struct soap *soap, struct soap_wsrm_sequence *seq)
@brief Internal function to check if a sequence is terminated or needs to be
terminated when expired.
@param soap context
@param seq pointer to sequence
@return 0 (not terminated) or 1 (terminated)
*/
static int
soap_wsrm_seq_terminated(struct soap *soap, struct soap_wsrm_sequence *seq)
{ DBGFUN("soap_wsrm_seq_terminated");
  if (seq->state == SOAP_WSRM_TERMINATED)
    return 1;
  if (seq->expires && seq->expires < time(NULL))
  { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Sequence %s has expired\n", seq->id?seq->id:"(null)"));
    seq->state = SOAP_WSRM_TERMINATED;
    return 1;
  }
  return 0;
}

/******************************************************************************\
 *
 *	Messages and Message Number Ranges
 *
\******************************************************************************/

/**
@fn int soap_wsrm_num_lookup(struct soap *soap, const struct soap_wsrm_sequence *seq, ULONG64 num)
@brief Internal function to look up a message number in the sequence state.
@param soap context
@param[in] seq pointer to sequence
@param[in] num message num to search
@return 0 (not found) 1 (found)
*/
static int
soap_wsrm_num_lookup(struct soap *soap, const struct soap_wsrm_sequence *seq, ULONG64 num)
{ struct soap_wsrm_range *p;
  DBGFUN1("soap_wsrm_num_lookup", "num="SOAP_ULONG_FORMAT, num);
  if (seq)
    for (p = seq->ranges; p; p = p->next)
      if (p->lower <= num && num <= p->upper)
        return 1;
  return 0;
}

/**
@fn int soap_wsrm_num_insert(struct soap *soap, struct soap_wsrm_sequence *seq, ULONG64 num)
@brief Internal function to insert a message number in the sequence state.
Updates the sequence ranges.
@param soap context
@param seq pointer to sequence
@param[in] num
@return SOAP_OK or error code (out of memory)
*/
static int
soap_wsrm_num_insert(struct soap *soap, struct soap_wsrm_sequence *seq, ULONG64 num)
{ struct soap_wsrm_range *p, **q;
  DBGFUN1("soap_wsrm_num_insert", "num="SOAP_ULONG_FORMAT, num);
  if (!seq)
    return SOAP_OK;
  /* assumes num is not in any range yet */
  /* if match: increment upper bound and normalize by joining consecutive ranges */
  for (p = seq->ranges; p; p = p->next)
  { if (num == p->upper + 1)
    { p->upper = num;
      if (p->next)
      { struct soap_wsrm_range *t = p->next;
        if (p->upper + 1 == t->lower)
        { p->upper = t->upper;
          p->next = t->next;
          free((void*)t);
        }
      }
      return SOAP_OK;
    }
  }
  /* if match: decrement first range's lower bound */
  if (seq->ranges && num == seq->ranges->lower - 1)
  { seq->ranges->lower = num;
    return SOAP_OK;
  }
  /* no match: insert new singleton range */
  p = (struct soap_wsrm_range*)malloc(sizeof(struct soap_wsrm_message));
  if (!p)
    return soap->error = SOAP_EOM;
  p->lower = p->upper = num;
  p->next = NULL;
  for (q = &seq->ranges; *q; q = &(*q)->next)
  { if (num < (*q)->lower)
    { p->next = *q;
      break;
    }
  }  
  *q = p;
  return SOAP_OK;
}

/**
@fn int soap_wsrm_num_size(const struct soap_wsrm_sequence *seq)
@brief Internal function returns the number of message ranges in a sequence
state.
@param seq pointer to sequence
@return number of message ranges
*/
static int
soap_wsrm_num_size(const struct soap_wsrm_sequence *seq)
{ int n = 0;
  if (seq)
  { struct soap_wsrm_range *p;
    for (p = seq->ranges; p; p = p->next)
      n++;
  }
  return n;
}

/**
@fn void soap_wsrm_num_free(struct soap *soap, struct soap_wsrm_sequence *seq)
@brief Internal function to deallocate all message number ranges from a
sequence state.
@param soap context
@param seq pointer to sequence
*/
static void
soap_wsrm_num_free(struct soap *soap, struct soap_wsrm_sequence *seq)
{ DBGFUN("soap_wsrm_num_free");
  if (seq)
  { struct soap_wsrm_range *p, *q;
    for (p = seq->ranges; p; p = q)
    { q = p->next;
      free((void*)p);
    }
  }
  seq->ranges = NULL;
}

/**
@fn static struct soap_wsrm_message *soap_wsrm_msg_new(struct soap *soap, struct soap_wsrm_sequence *seq, ULONG64 num)
@brief Internal function to allocate a new message for a sequence
sequence state.
@param soap context
@param seq sequence pointer
@param num message num
@return pointer to msg
*/
static struct soap_wsrm_message *
soap_wsrm_msg_new(struct soap *soap, struct soap_wsrm_sequence *seq, ULONG64 num)
{ struct soap_wsrm_message *p;
  p = (struct soap_wsrm_message*)malloc(sizeof(struct soap_wsrm_message));
  if (!p)
  { soap->error = SOAP_EOM;
    return NULL;
  }
  p->num = num;
  p->state = SOAP_WSRM_INIT;
  p->list = p->last = NULL;
  p->next = seq->messages;
  seq->messages = p;
  return p;
}

/**
@fn void soap_wsrm_msg_free(struct soap *soap, struct soap_wsrm_message *p)
@brief Internal function to deallocate all cached messages ranges from a
sequence state.
@param soap context
@param p pointer to message (in a sequence state)
*/
static void
soap_wsrm_msg_free(struct soap *soap, struct soap_wsrm_message *p)
{ struct soap_wsrm_content *q, *r;
  DBGFUN1("soap_wsrm_msg_free", "num="SOAP_ULONG_FORMAT, p->num);
  for (q = p->list; q; q = r)
  { r = q->next;
    free((void*)q);
  }
  p->list = p->last = NULL;
}

#ifdef __cplusplus
}
#endif

