/*
	wsrmapi.c

	WS-ReliableMessaging plugin.

	Implements the WS-RM 1.0 and 1.1 logic for import/wsrm.h import/wsrm5.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 -b -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. The -b option ensures that one-way response message operations are
   added for duplex communications that require a client-side callback service.
-# 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 an import "wsrm.h" to support
WS-ReliableMessaging 1.1 or import "wsrm5.h" to support WS-ReliableMessaging
1.0 (2005). The imports are automatically generated by wsdl2h for a set of
WSDLs that use WS-ReliableMessaging as per WS-Policy. 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-output-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 (or a last message with the older WS-RM 1.0) 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) or in duplex mode with one-way messages.
- Optionally, 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.
- Optionally, 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. 
- Optionally, 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.

The destination service normally serves as ReplyTo, FaultTo, and AcksTo
service, which is the default scenario with request-response message exchanges.
Replies, faults, and acknowledgements can also be asynchroniously transmitted in
duplex mode. In that case, the WS-RM source client acts as a callback service
to accept messages on a port.

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, using a 'soap' context struct (use one 'soap' context per thread):

@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 *source = NULL;       // WS-RM source (NULL means current)
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 (optional)

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

// Step 1: create a sequence
if (soap_wsrm_create_offer(soap, destination, source, id, expires, NoDiscard, opt_msg_id, &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: optionally resend messages before terminating
if (soap_wsrm_nack(seq)) // any non-acks for messages sent?
  soap_wsrm_resend(soap, seq, 0, 0); // resend all non-acked messages

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

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

For duplex communication, the responses are sent to the WS-RM source's port,
to which we need to listen via a callback service. There are two ways to do so,
by polling or by running a separate threat that accepts responses. In the code
below we use a polling approach to process responses sent asynchronously to the
WS-RM source:

@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 *source = "...";      // WS-RM source address destination sends to
int source_port = ...;           // port of WS-RM source address
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 (optional)
int retry;

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

struct soap *callback = soap_new(); // callback for polling

// Step 1: create a WS-RM source port binding for the callback to poll messages
if (!soap_valid_socket(soap_bind(callback, NULL, source_port, 100)))
  ... // error

// Step 2: create a sequence
if (soap_wsrm_create_offer(soap, destination, source, id, expires, NoDiscard, opt_msg_id, &seq))
{ if (soap->error != 202) // Error != HTTP Accepted
  { soap_wsrm_seq_free(soap, seq);
    ... // error creating sequence
  }
}
// poll 10 times for 1 second until the sequence created response received
for (retry = 10; retry && !soap_wsrm_seq_created(soap, seq); retry--)
  if (callback_poll(callback, 1)) // 1 second poll
    ... // error
if (!retry)
  ... // error

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

// Step 4: optionally close first before terminating
if (soap_wsrm_close(soap, seq, NULL))
{ if (soap->error != 202) // Error != HTTP Accepted
  { soap_wsrm_seq_free(soap, seq);
    ... // error closing sequence
  }
}

if (callback_poll(callback, -500000)) // 500 ms poll
  ... // error

// Step 5: optionally resend messages before terminating
if (soap_wsrm_nack(seq)) // any nacks?
{ soap_wsrm_resend(soap, seq, 0, 0); // resend all non-acked messages
  if (callback_poll(callback, -100000)) // 100 ms poll
    ... // error
}

// Step 6: terminate
if (soap_wsrm_terminate(soap, seq, NULL))
{ if (soap->error != 202) // Error != HTTP Accepted
  { soap_wsrm_seq_free(soap, seq);
    ... // error creating sequence
  }
}

// only needed with WS-RM 1.1 to accept terminate response
if (callback_poll(callback, -500000)) // 500 ms poll
  ... // error

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

The polling operation can be implemented as follows:

@code
int callback_poll(struct soap *soap, int timeout)
{ int poll = 5; // 5 poll cycles times max, ensures make progress
  soap->accept_timeout = timeout; // polling timout
  soap->recv_timeout = timeout;
  soap->send_timeout = soap->recv_timeout = 1; // 1 sec
  while (poll-- && soap_valid_socket(soap_accept(soap)))
  { soap_serve(soap);
    soap_destroy(soap);
    soap_end(soap);
  }
  if (soap->error == SOAP_STOP || soap->error == SOAP_EOF) // timed out
    return soap->error = SOAP_OK;
  return soap->error;
}
@endcode

In addition, callback service operations must be defined to handle faults and,
when applicable, one-way message responses sent by the destination.

Fault messages are accepted by the callback and processed by the service
operation that can be customized to your needs:

@code
int SOAP_ENV__Fault(struct soap *soap, 
  _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   *Code,                  // SOAP 1.2
  struct SOAP_ENV__Reason *Reason,                // SOAP 1.2
  char                    *Node,                  // SOAP 1.2
  char                    *Role,                  // SOAP 1.2
  struct SOAP_ENV__Detail *Detail                 // SOAP 1.2
)
{ soap_send_empty_response(soap, 202);
  if (!detail)
    detail = Detail;
  if (detail && detail->__type == SOAP_TYPE__wsrm__Identifier)
  { // the sequence id is in the Fault Detail __type and fault members
    char *id = (char*)detail->fault;
    // we opt to treat all faults fatal, so let's terminate the sequence
    soap_wsrm_sequence_handle seq = soap_wsrm_seq_lookup_id(soap, id);
    if (seq)
    { soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
      soap_wsrm_seq_release(soap, seq);
      return soap->error;
    }
  }
  return SOAP_OK;
}
@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
discarded 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.

If the source ReplyTo and AcksTo addresses differ in your project, then use the
following:

@code
if (soap_wsrm_create_offer_acksto(soap, destination, replyto, acksto, id, expires, DiscardEntireSequence, opt_msg_id, &seq)))
  ... // error
@endcode

@subsection wsrm_4_2 Creating a Sequence without 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 from source to destination then this
is not needed. A sequence is created without an offer as follows:

@code
if (soap_wsrm_create(soap, destination, source, expires, DiscardEntireSequence, opt_msg_id, &seq))
  ... // error
@endcode

@subsection wsrm_4_3 Exchanging Messages in a Sequence

Each message exchange with the WS-RM destination should be preceded with a
@ref soap_wsrm_request or @ref 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

To generate WS-Addressing message IDs, use @ref soap_wsa_rand_uuid, for example:

@code
const char *exampleRequestAction = "urn:example/examplePort/example";

if (soap_wsrm_request(soap, seq, soap_wsa_rand_uuid(soap), exampleRequestAction))
  ... // error: out of memory
@endcode

The @ref 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
@ref soap_wsrm_create_offer. Because the address may change due to a redirect,
we encourage the use of @ref 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

Before sending a message, a WS-RM request should be issued using @ref
soap_wsrm_request_acks. 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 in which case the acknowledgements are sent to.

@code
if (soap_wsrm_request_acks(soap, seq, opt_msg_id, exampleRequestAction))
  ... // error
if (soap_call_ns__example(soap, soap_wsrm_to(seq), exampleRequestAction, &response))
  if (soap->error != 202) // Error != HTTP Accepted
    ... // error
@endcode

When duplex messaging is used via a callback, a polling operation will be
needed afterwards:

@code
if (callback_poll(callback, -500000)) // 500 ms poll
  ... // error
@endcode

The @ref soap_wsrm_close returns acknowledgements automatically, so requesting
intermediate acknowledgements is not required to issue a final
@ref soap_wsrm_resend to resend all non-acknowledged messages, e.g. after
@ref 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
@ref soap_wsrm_create or @ref 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 = source; // endpoint of WS-RM source
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)
    ... // error
}
@endcode

An optional source address information header can be added with
@ref 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 and message ordering is relevant. Resending
will lead to an unordered message delivery.

Also, 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
@ref soap_wsrm_check_retry function can be used as follows to only retry the
message exchange (or send) when needed:

@code
while (!soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction) && 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 @ref 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.

Note that this mechanism does not replace acknowledgements for delivery.
Delivery acknowledgements are verified with @ref soap_wsrm_nack.

@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 for a request-response scenario:

@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 *source = NULL;       // WS-RM source (current)
ULONG64 expires = 60000;         // 1 minute sequence lifetime

soap_wsrm_sequence_handle seq;

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

while (!soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction) && 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
} 

soap_wsrm_seq_free(soap, seq);

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

A duplex mode client that accepts responses via a callback:

@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 *source = "...";      // WS-RM source address destination sends to
int source_port = ...;           // port of WS-RM source address
ULONG64 expires = 60000;         // 1 minute sequence lifetime
int retry;

soap_wsrm_sequence_handle seq;

struct soap *callback = soap_new(); // callback for polling

// WS-RM source port binding for the callback to poll messages
if (!soap_valid_socket(soap_bind(callback, NULL, source_port, 100)))
  ... // error

if (soap_wsrm_create_offer(soap, destination, source, NULL, expires, DiscardEntireSequence, NULL, &seq))
{ if (soap->error != 202)
  { soap_wsrm_seq_free(soap, seq);
    ... // error creating sequence
  }
} 

// poll 10 times for 1 second until the sequence created response received
for (retry = 10; retry && !soap_wsrm_seq_created(soap, seq); retry--)
  if (callback_poll(callback, 1)) // 1 second poll
    ... // error
if (!retry)
  ... // error

while (!soap_wsrm_request_acks(soap, seq, NULL, exampleRequestAction) && 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;
  }
  if (soap_wsrm_check_retry(soap, seq))
    break; // do not continue
  sleep(1); // wait a second to give network a chance to recover
}

if (callback_poll(callback, -500000)) // 500 ms poll
  ... // error

if (soap_wsrm_close(soap, seq, NULL))
{ if (soap->error != 202)
  { soap_wsrm_seq_free(soap, seq);
    ... // error closing sequence
  }
}

// Resend messages marked as non-acked (as an option)
for (retry = 2; retry && soap_wsrm_nack(seq); retry--)
{ soap_wsrm_resend(soap, seq, 0, 0); // 0 0 means full range of msg nums
  if (callback_poll(callback, -500000)) // 500 ms poll
    ... // error
}

if (soap_wsrm_terminate(soap, seq, NULL))
{ if (soap->error != 202)
  { soap_wsrm_seq_free(soap, seq);
    ... // error closing sequence
  }
} 

if (callback_poll(callback, -500000)) // 500 ms poll
  ... // error

soap_wsrm_seq_free(soap, seq);

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

@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

For duplex communications, it is advisable to send acknowledgements to the
peer, which are normally piggy-backed on messages to the peer. If no messages
are sent to the peer, explicit acknowledgements can be sent:

@code
if (soap_wsrm_acknowledgement(soap, seq, NULL))
  ... // error
@endcode

To sent acknowledgements to all peers with open sequences, use:

@code
int timeout = -10000; // 10 ms
if (soap_wsrm_pulse(soap, timeout))
  ... // error
@endcode

where timeout is in seconds (pos value) or microseconds (neg value). Depending
on the message exchange scenarios, it may be advisable to have the server send
periodic acknowledgement updates as follows:

@code
soap->accept_timeout = 1; // 1s intervals
for (;;)
{ // server loop, accept next message
  if (!soap_valid_socket(accept()))
  {
    // error or timeout?
    if (soap->errnum)
    {
      soap_stream_fault(std::cerr);
      exit(1); // may want to exit, but trying to continue is also possible
    }
    else
    { // timeout occurs after 1s
      // send acks to peers (optional), take 10 ms per message 
      soap_wsrm_pulse(soap, -10000); // 10 ms
      sleep(1); // optional, but needed for UDP: since accept() returns immediately
    }
  }
  else
  { ... // serve
  }
@endcode

Each service operation that supports WS-ReliableMessaging and WS-Addressing
should use the @ref soap_wsrm_check, @ref soap_wsrm_sender_fault,
@ref soap_wsrm_receiver_fault, and @ref 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 @ref soap_wsrm_sender_fault or @ref
soap_wsrm_receiver_fault before @ref 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
@ref soap_wsrm_check allow the sequence to continue.

@subsection wsrm_5_2 Handling Duplex Callback Service Operations

To set up a callback to accept responses in a duplex scenario, we set up the
server in the same way as the destination server. Service opertions should not
use @ref 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

The one-way response service operations are automatically generated with wsdl2h
option -b.

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)
{
  // check WS-RM/WSA headers
  if (soap_wsrm_check(soap))
    return soap->error;
  ... // process
  return SOAP_OK;
}
@endcode

@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
@ref 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 *source = NULL;

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

if (soap_wsrm_create_offer(soap, destination, source, 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 *source = from;

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

if (soap_wsrm_create_offer(soap, destination, source, 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 C++ Server Objects

Run soapcpp2 options -a -i (or -j) on the .h file with the service operation
definitions.

When you prefer to use soapcpp2 options -i or -j, please run soacpp2 again as
follows:

@verbatim
soapcpp2 -A -pwsrx import/wsrx.h
@endverbatim

This generates wsrxClient.cpp and wsrxServer.cpp needed with the
WS-ReliableMessaging plugin and code.

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
for (;;)
{
  if (!soap_valid_socket(service.accept()))
  {
    service.soap_stream_fault(std::cerr);
    exit(1);
  }
  if (soap_begin_serve(service) == SOAP_OK)
  {
    if (service.dispatch() == SOAP_NO_METHOD)
    {
      if (soap_serve_request(service) != SOAP_OK)
      {
        soap_send_fault(service);
        service.soap_stream_fault(std::cerr);
      }
    }
    else if (service->error)
      service.soap_stream_fault(std::cerr);
  }
  service.destroy();
}
@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;
char soap_wsrm_idname[40] = "";
int 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_chk_acks(struct soap *soap);
static int soap_wsrm_add_acks(struct soap *soap, struct soap_wsrm_sequence *seq, int all);
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_data(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_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 *replyto, 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 WS-RM destination
server. Optionally the 'replyto' address can be given of the WS-RM source to
reply 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] replyto endpoint address of the WS-RM source to reply/ack to (optional)
@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 *replyto, 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
WS-RM 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 WS-RM destination server.
Optionally the 'replyto' address can be given of the WS-RM source to reply 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] replyto endpoint address of the WS-RM source to reply/ack to (optional)
@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 or NULL when response is asynchronously
send and the sequence is initialized upon receipt of the create response.
@return SOAP_OK or error code
*/
int
soap_wsrm_create_offer(struct soap *soap, const char *to, const char *replyto, const char *id, LONG64 expires, enum wsrm__IncompleteSequenceBehaviorType behavior, const char *wsa_id, soap_wsrm_sequence_handle *seq)
{ return soap_wsrm_create_offer_acksto(soap, to, replyto, NULL, id, expires, behavior, wsa_id, seq);
}
/**
@fn int soap_wsrm_create_offer_acksto(struct soap *soap, const char *to, const char *replyto, 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
WS-RM 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 WS-RM destination server.
Optionally the 'replyto' address can be given of the WS-RM source to reply to
and an 'acksto' address can be given for acknowledgement messages to be sent to
(normally to the reply 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] replyto endpoint address of the WS-RM source (optional)
@param[in] acksto endpoint address for WS-RM acknowledgements (optional)
(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 or NULL when response is asynchronously
send and the sequence is initialized upon receipt of the create response.
@return SOAP_OK or error code
*/
int
soap_wsrm_create_offer_acksto(struct soap *soap, const char *to, const char *replyto, 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 (soap_wsa_request(soap, wsa_id, to, action))
    return soap->error;
  if (!replyto)
    replyto = soap_wsa_anonymousURI;
  if (!acksto)
    acksto = replyto;
  soap_wsa_add_ReplyTo(soap, replyto);
  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 */
#ifdef SOAP_WSRM_2007
    req.Offer->Endpoint.Address = (char*)replyto; /* required: use acksto endpoint */
    if (expires)
      req.Offer->Expires = req.Expires; /* expire value */
#endif
    if (behavior != NoDiscard)
      req.Offer->IncompleteSequenceBehavior = &behavior;
  }
  else
    req.Offer = NULL;
  data->state = SOAP_WSRM_OFF; /* disable caching */
  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)->handle = 1;
  (*seq)->expires = time(NULL) + expires/1000;
  (*seq)->behavior = behavior;
  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);
  s = (char*)malloc(strlen(wsa_id) + 1);
  if (!s)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error = SOAP_EOM;
  }
  /* trick to keep track of response via RelatesTo */
  (*seq)->id = strcpy(s, wsa_id);
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  if (soap_call___wsrm__CreateSequence(soap, to, action, &req, &res))
  { if (soap->error == 202)
      soap->error = SOAP_OK;
    return soap->error;
  }
  /* process response */
  s = (char*)malloc(strlen(res.Identifier) + 1);
  if (!s)
    return soap->error = SOAP_EOM;
  MUTEX_LOCK(soap_wsrm_session_lock);
  if ((*seq)->id)
    free((void*)(*seq)->id);
  (*seq)->id = strcpy(s, res.Identifier);
  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);
  }
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  (*seq)->state = SOAP_WSRM_CREATED;
  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 (!soap_wsrm_seq_valid(soap, seq))
    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? */
    }
    else if (seq->behavior == DiscardFollowingFirstGap && soap->header->wsrm__Sequence->MessageNumber != seq->lastnum + 1)
      return soap_wsrm_error(soap, seq, wsrm__SequenceTerminated); /* close or term */
    else
    { 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 = (struct wsrm__SequenceType*)soap_malloc(soap, sizeof(struct wsrm__SequenceType))))
    return soap->error;
  soap_default_wsrm__SequenceType(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, seq, 0))
    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 = (struct wsrm__AckRequestedType*)soap_malloc(soap, sizeof(struct wsrm__AckRequestedType))))
    return soap->error;
  soap_default_wsrm__AckRequestedType(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_valid(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)
{
#ifdef SOAP_WSRM_2007
  struct wsrm__CloseSequenceType req;
  struct wsrm__CloseSequenceResponseType res;
#endif
  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;
  if (seq->state == SOAP_WSRM_TERMINATED)
    return soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
  /* 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"));
      /* No error? */
    }
    else if (seq->behavior == DiscardFollowingFirstGap && soap->header->wsrm__Sequence->MessageNumber != seq->lastnum + 1)
      return soap_wsrm_error(soap, seq, wsrm__SequenceTerminated); /* close or terminate */
    else
    { 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 */
  /* this appears necessary, but not mentioned in the WS-RM specs */
#ifdef SOAP_WSRM_2007
  if (soap_wsrm_add_acks(soap, seq, 1))
    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;
  soap_default_wsrm__CloseSequenceType(soap, &req);
  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 */
  seq->state = SOAP_WSRM_CLOSED;
  if (soap_call___wsrm__CloseSequence(soap, seq->to, soap->header->wsa5__Action, &req, &res))
  { if (soap->error == 202)
      soap->error = SOAP_OK;
    else
      return soap->error;
  }
  else if (!res.Identifier || strcmp(res.Identifier, seq->id))
    return soap_wsrm_error(soap, seq, wsrm__UnknownSequence);
  return SOAP_OK;
#endif
#ifdef SOAP_WSRM_2005
  if (soap_wsrm_request(soap, seq, wsa_id, SOAP_NAMESPACE_OF_wsrm"/LastMessage"))
    return soap->error;
  soap->header->wsrm__Sequence->LastMessage = (struct _wsrm__UsesSequenceSSL*)soap_malloc(soap, sizeof(struct _wsrm__UsesSequenceSSL));
  soap_default__wsrm__UsesSequenceSSL(soap, soap->header->wsrm__Sequence->LastMessage);
  seq->state = SOAP_WSRM_CLOSED;
  if (soap_send___wsrm__LastMessage(soap, seq->to, soap->header->wsa5__Action))
    return soap->error;
  return soap_recv_empty_response(soap);
#endif
}

/**
@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;
  soap_default_wsrm__TerminateSequenceType(soap, &req);
  req.Identifier = soap_strdup(soap, seq->id);
  req.LastMsgNumber = &seq->num;
  data->state = SOAP_WSRM_OFF; /* disable caching */
  seq->state = SOAP_WSRM_TERMINATED;
  if (soap_call___wsrm__TerminateSequence(soap, seq->to, soap->header->wsa5__Action, &req, &res))
  { if (soap->error == 202)
      soap->error = SOAP_OK;
    else
      return soap->error;
  }
  else if (!res.Identifier || strcmp(res.Identifier, seq->id))
    return soap_wsrm_error(soap, seq, wsrm__UnknownSequence);
  if (seq->behavior == DiscardEntireSequence
   && (soap_wsrm_nack(seq) || (seq->lastnum && (!seq->ranges || seq->ranges->lower != 1 || seq->ranges->upper != seq->lastnum))))
    return soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
  if (seq->fault != (enum wsrm__FaultCodes)(-1))
    return soap_wsrm_error(soap, seq, seq->fault);
  return SOAP_OK;
}

/**
@fn int soap_wsrm_pulse(struct soap *soap, int timeout)
@brief Sends acknowledgements for all open sessions to all peers. Must set send and recv timeouts to prevent blocking.
@param soap context
@return SOAP_OK or error code
*/
int
soap_wsrm_pulse(struct soap *soap, int timeout)
{ soap_wsrm_sequence_handle seq;
  int ct = soap->connect_timeout;
  int st = soap->send_timeout;
  int rt = soap->recv_timeout;
  int a = 1;
  DBGFUN("soap_wsrm_pulse");
  soap->connect_timeout = soap->send_timeout = soap->recv_timeout = timeout;
  MUTEX_LOCK(soap_wsrm_session_lock);
  for (seq = soap_wsrm_session; seq; seq = seq->next)
  { if (seq->state == SOAP_WSRM_CREATED && seq->lastnum)
      seq->ackreq = 1;
  }
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  while (a)
  { a = 0;
    MUTEX_LOCK(soap_wsrm_session_lock);
    for (seq = soap_wsrm_session; seq; seq = seq->next)
    { if (seq->ackreq && seq->lastnum)
      { seq->refs++;
        MUTEX_UNLOCK(soap_wsrm_session_lock);
        soap_wsrm_acknowledgement(soap, seq, NULL);
        soap_wsrm_seq_release(soap, seq);
	a = 1;
        break;
      }
    }
    if (!a)
      MUTEX_UNLOCK(soap_wsrm_session_lock);
  }
  soap->connect_timeout = ct;
  soap->send_timeout = st;
  soap->recv_timeout = rt;
  return SOAP_OK;
}

/**
@fn int soap_wsrm_acknowledgement(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id)
@brief Sends acknowledgements
@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_acknowledgement(struct soap *soap, soap_wsrm_sequence_handle seq, const char *wsa_id)
{ struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  const char *to;
  DBGFUN1("soap_wsrm_acknowledgement", "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"/SequenceAcknowledgement"))
    return soap->error;
  /* force adding acks */
  if (soap_wsrm_add_acks(soap, seq, 1))
    return soap->error;
  data->state = SOAP_WSRM_OFF; /* disable caching */
  seq->ackreq = 0;
  to = seq->acksto;
  if (!to || !strcmp(to, soap_wsa_anonymousURI))
    to = seq->to;
  if (soap_send___wsrm__SequenceAcknowledgement(soap, to, soap->header->wsa5__Action))
    return soap->error;
  return soap_recv_empty_response(soap);
}

/**
@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(). Sequences created by soap_wsrm_create_offer return
handles that 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 = time(NULL);
  DBGFUN("soap_wsrm_seq_free");
  data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  if (!data)
    return;
  MUTEX_LOCK(soap_wsrm_session_lock);
  q = &soap_wsrm_session;
  while (*q)
  { if ((!(*q)->handle && !(*q)->refs && (*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_ACK)
      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;
  const char *replyTo;
  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_valid(soap, seq))
  { soap_wsrm_error(soap, seq, wsrm__SequenceTerminated);
    MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  replyTo = soap_wsa_ReplyTo(soap);
  if (replyTo && strcmp(replyTo, soap_wsa_anonymousURI))
  { char *s;
    s = (char*)malloc(strlen(replyTo) + 1);
    if (!s)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error = SOAP_EOM;
    }
    if (seq->to)
      free((void*)seq->to);
    seq->to = strcpy(s, replyTo);
  }
  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, 202); /* HTTP 202 */
  }
  if (seq->state == SOAP_WSRM_CLOSED && num > seq->lastnum)
  {
#ifdef SOAP_WSRM_2005
    soap_wsrm_error(soap, seq, wsrm__LastMessageNumberExceeded);
#else
    soap_wsrm_error(soap, seq, wsrm__SequenceClosed);
#endif
    MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  if (seq->behavior == DiscardFollowingFirstGap && num != seq->lastnum + 1)
  { soap_wsrm_error(soap, seq, wsrm__SequenceTerminated); /* close or term */
    MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  if (num > seq->lastnum)
    seq->lastnum = num;
  if (soap_wsrm_num_insert(soap, seq, num))
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap->error;
  }
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  return soap_wsrm_chk_acks(soap);
}

/**
@fn int soap_wsrm_reply_num(struct soap *soap, int flag)
@brief Prepares reply from service. When flag is set, 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, int flag)
{ 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)
  { if (flag)
    { 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;
      if (data->seq)
        data->seq->refs--;
      data->seq = seq;
      seq->refs++; /* do not dealloc until message sent or error */
    }
    soap_default_wsrm__SequenceType(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;
  if (!soap->header->SOAP_WSA(ReplyTo) || !soap->header->SOAP_WSA(ReplyTo)->Address || !strcmp(soap->header->SOAP_WSA(ReplyTo)->Address, soap_wsa_anonymousURI))
    soap_wsa_add_ReplyTo(soap, seq->to);
  if (!soap->header->SOAP_WSA(FaultTo) || !soap->header->SOAP_WSA(FaultTo)->Address || !strcmp(soap->header->SOAP_WSA(FaultTo)->Address, soap_wsa_anonymousURI))
    soap_wsa_add_FaultTo(soap, seq->to);
  /* add acks for ackRequested response from server */
  return soap_wsrm_add_acks(soap, seq, 0);
}

/**
@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, 1))
    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, 1))
    return soap->error;
  if (soap->header->wsrm__Sequence)
  { if (!soap->header->wsrm__AckRequested && !(soap->header->wsrm__AckRequested = (struct wsrm__AckRequestedType*)soap_malloc(soap, sizeof(struct wsrm__AckRequestedType))))
      return soap->error;
    soap->header->__sizeAckRequested = 1;
    soap_default_wsrm__AckRequestedType(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;
  char *action;
  const char *replyTo;
  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;
  replyTo = soap_wsa_ReplyTo(soap);
  if (replyTo && strcmp(replyTo, soap_wsa_anonymousURI))
  { char *s;
    s = (char*)malloc(strlen(replyTo) + 1);
    if (!s)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error = SOAP_EOM;
    }
    seq->to = strcpy(s, replyTo);
  }
  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(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;
  action = (char*)soap_malloc(soap, strlen(soap->action) + 9);
  strcpy(action, soap->action);
  strcat(action, "Response");
  return soap_wsa_reply(soap, NULL, action);
}

/**
@fn int __wsrm__CreateSequenceResponse(struct soap *soap, struct wsrm__CreateSequenceResponseType *res)
@brief WS-RM CreateSequenceResponse one-way service operation. Automatically invoked by the
server to process a create sequence response (with or without offer). Creates a
new local sequence state to keep track of messages.
@param soap context
@param[in] res wsrm__CreateSequenceResponse response message
@return SOAP_OK or error code
*/
int
__wsrm__CreateSequenceResponse(struct soap *soap, struct wsrm__CreateSequenceResponseType *res)
{ struct soap_wsrm_sequence *seq;
  struct soap_wsrm_data *data;
  char *s;
  DBGFUN("__wsrm__CreateSequenceResponse");
  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 (!res->Identifier)
    return soap_wsrm_error(soap, NULL, wsrm__CreateSequenceRefused);
  MUTEX_LOCK(soap_wsrm_session_lock);
  seq = soap_wsrm_seq_lookup_data(data, res->Identifier);
  if (!seq && soap_wsa_RelatesTo(soap))
  { seq = soap_wsrm_seq_lookup_data(data, soap_wsa_RelatesTo(soap));
    if (!seq)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap_wsrm_error(soap, NULL, wsrm__UnknownSequence);
    }
    s = (char*)malloc(strlen(res->Identifier) + 1);
    if (!s)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error = SOAP_EOM;
    }
    if (seq->id)
      free((void*)seq->id);
    seq->id = strcpy(s, res->Identifier);
  }
  if (res->Expires && *res->Expires/1000 <= SOAP_WSRM_MAX_SEC_TO_EXPIRE)
    seq->expires = time(NULL) + *res->Expires/1000;
  if (res->IncompleteSequenceBehavior)
    seq->behavior = *res->IncompleteSequenceBehavior;
  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_send_empty_response(soap, 202);
}

/**
@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;
  char *action;
  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;
  }
  soap_wsa_add_ReplyTo(soap, seq->to);
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  action = (char*)soap_malloc(soap, strlen(soap->action) + 9);
  strcpy(action, soap->action);
  strcat(action, "Response");
  return soap_wsa_reply(soap, NULL, action);
}

/**
@fn int __wsrm__CloseSequenceResponse(struct soap *soap, struct wsrm__CloseSequenceResponseType *res)
@brief WS-RM CloseSequenceResponse one-way service operation. Automatically invoked by the
server to process the close sequence response. Updates the server's local
sequence state to closed.
@param soap context
@param[in] res wsrm__CloseSequenceResponse response message
@return SOAP_OK or error code
*/
int
__wsrm__CloseSequenceResponse(struct soap *soap, struct wsrm__CloseSequenceResponseType *res)
{ struct soap_wsrm_data *data;
  DBGFUN("__wsrm__CloseSequenceResponse");
  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 (!res->Identifier || !soap_wsrm_seq_lookup_data(data, res->Identifier))
    return soap_wsrm_error(soap, NULL, wsrm__UnknownSequence);
  return soap_send_empty_response(soap, 202);
}

/**
@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 terminate 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;
  char *action;
  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);
  soap_wsrm_cleanup(soap);
  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)
  { MUTEX_UNLOCK(soap_wsrm_session_lock);
    return soap_send_empty_response(soap, 202);
  }
  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->lastnum && (!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;
      }
    }
  }
#ifdef SOAP_WSRM_2005
  res->Identifier = soap_strdup(soap, seq->id);
  if (seq->num)
    res->LastMsgNumber = &seq->num;
  else
    res->LastMsgNumber = NULL;
  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);
  soap_wsa_add_ReplyTo(soap, seq->to);
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  action = soap_strdup(soap, soap->action);
  return soap_wsa_reply(soap, NULL, action);
#else
  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);
  soap_wsa_add_ReplyTo(soap, seq->to);
  MUTEX_UNLOCK(soap_wsrm_session_lock);
  action = (char*)soap_malloc(soap, strlen(soap->action) + 9);
  strcpy(action, soap->action);
  strcat(action, "Response");
  return soap_wsa_reply(soap, NULL, action);
#endif
}

/**
@fn int __wsrm__TerminateSequenceResponse(struct soap *soap, struct wsrm__TerminateSequenceResponseType *res)
@brief WS-RM TerminateSequenceResponse one-way service operation. Automatically invoked by the
server to process the terminate sequence response. Updates the server's local
sequence state to terminated.
@param soap context
@param[in] res wsrm__TerminateSequenceResponse response message
@return SOAP_OK or error code
*/
int
__wsrm__TerminateSequenceResponse(struct soap *soap, struct wsrm__TerminateSequenceResponseType *res)
{ struct soap_wsrm_data *data;
  DBGFUN("__wsrm__TerminateSequenceResponse");
  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 (!res->Identifier || !soap_wsrm_seq_lookup_data(data, res->Identifier))
    return soap_wsrm_error(soap, NULL, wsrm__UnknownSequence);
  return soap_send_empty_response(soap, 202);
}

/**
@fn int __wsrm__SequenceAcknowledgement(struct soap *soap)
@brief WS-RM SequenceAcknowledgement operation.
@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, 202);
}

/**
@fn int __wsrm__AckRequested(struct soap *soap)
@brief WS-RM AckRequested operation.
@param soap context
@return SOAP_OK or error code
*/
int
__wsrm__AckRequested(struct soap *soap)
{ struct soap_wsrm_sequence *seq = NULL;
  DBGFUN("__wsrm__AckRequested");
  soap_wsrm_cleanup(soap);
  /* soap_wsrm_preparefinalrecv was called upon invocation, to process acks */
  soap_send_empty_response(soap, 202);
  if (soap->header && soap->header->wsrm__AckRequested)
  { struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
    if (!data)
      return soap->error = SOAP_PLUGIN_ERROR;
    MUTEX_LOCK(soap_wsrm_session_lock);
    seq = soap_wsrm_seq_lookup_ack(data, soap->header->wsrm__AckRequested->Identifier);
    if (seq)
    { seq->refs++;
      MUTEX_UNLOCK(soap_wsrm_session_lock);
      soap_wsrm_acknowledgement(soap, seq, soap_wsa_rand_uuid(soap));
      soap_wsrm_seq_release(soap, seq);
    }
    else
      MUTEX_UNLOCK(soap_wsrm_session_lock);
  }
  return SOAP_OK;
}

/**
@fn int __wsrm__LastMessage(struct soap *soap)
@brief WS-RM LastMessage operation (WS-RM 1.0).
@param soap context
@return SOAP_OK or error code
*/
int
__wsrm__LastMessage(struct soap *soap)
{ struct soap_wsrm_sequence *seq = NULL;
  DBGFUN("__wsrm__LastMessage");
  soap_wsrm_cleanup(soap);
  /* soap_wsrm_preparefinalrecv was called upon invocation, to process acks */
  soap_send_empty_response(soap, 202);
  if (soap->header && soap->header->wsrm__Sequence)
  { struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
    if (!data)
      return soap->error = SOAP_PLUGIN_ERROR;
    MUTEX_LOCK(soap_wsrm_session_lock);
    seq = soap_wsrm_seq_lookup_ack(data, soap->header->wsrm__Sequence->Identifier);
    if (seq)
    { seq->refs++;
      MUTEX_UNLOCK(soap_wsrm_session_lock);
      if (!soap_wsrm_num_lookup(soap, seq, soap->header->wsrm__Sequence->MessageNumber))
      { seq->lastnum = soap->header->wsrm__Sequence->MessageNumber;
        if (soap_wsrm_num_insert(soap, seq, seq->lastnum))
        { soap_wsrm_seq_release(soap, seq);
          return soap->error;
        }
      }
      if (seq->state == SOAP_WSRM_CREATED)
        soap_wsrm_close(soap, seq, soap_wsa_rand_uuid(soap));
      else
        soap_wsrm_acknowledgement(soap, seq, soap_wsa_rand_uuid(soap));
      seq->state = SOAP_WSRM_CLOSED;
      soap_wsrm_seq_release(soap, seq);
    }
    else
      MUTEX_UNLOCK(soap_wsrm_session_lock);
  }
  return 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)
{ DBGFUN2("soap_wsrm_fault_subcode", "code=%s", faultsubcode?faultsubcode:"(null)", "string=%s", faultstring?faultstring:"(null)");
  soap_wsrm_reply_num(soap, 0);
  if (soap->header && soap->header->wsrm__Sequence && soap->header->wsrm__Sequence->Identifier)
  { char *id = soap->header->wsrm__Sequence->Identifier;
    int err = soap_wsa_fault_subcode(soap, flag, faultsubcode, faultstring, faultdetail);
    soap_faultdetail(soap);
    if (soap->version == 2)
    { soap->fault->SOAP_ENV__Detail->__type = SOAP_TYPE__wsrm__Identifier;
      soap->fault->SOAP_ENV__Detail->fault = (void*)id;
    }
    else
    { soap->fault->detail->__type = SOAP_TYPE__wsrm__Identifier;
      soap->fault->detail->fault = (void*)id;
    }
    return err;
  }
  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_check_faultsubcode(soap);
    if (info)
      *info = soap_check_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)
      soap_wsrm_seq_release(soap, data->seq);
    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;
#ifdef SOAP_WSRM_2005
    case wsrm__LastMessageNumberExceeded:
      reason = "The value for wsrm:MessageNumber exceeds the value of the MessageNumber accompanying a LastMessage element in this Sequence.";
      break;
#endif
    case wsrm__CreateSequenceRefused:
      reason = "The Create Sequence request has been refused by the RM Destination.";
      break;
#ifdef SOAP_WSRM_2007
    case wsrm__SequenceClosed:
      reason = "The Sequence is closed and cannot accept new messages.";
      break;
#endif
    case wsrm__WSRMRequired:
      reason = "The RM Destination requires the use of WSRM.";
      break;
    default:
      break;
  }
  soap_header(soap);
  if (soap->header)
  { soap_default_SOAP_ENV__Header(soap, soap->header);
    if (soap->version == 1)
    { 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;
        }
      }
    }
    if (seq && !seq->handle) /* server side: need to relay error */
      soap_wsa_add_ReplyTo(soap, seq->to);
  }
  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 PLUGIN 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("OPEN ", 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%c  = %s\n", seq->ackreq?'?':' ', seq->acksto?seq->acksto:"(null)");
    fprintf(fd, "  EXPIRES%c = %s\n", seq->handle?'!':' ', 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)
  { const char *s;
    if (arg)
      s = (char*)arg;
    else
      s = soap_wsa_rand_uuid(soap);
    MUTEX_LOCK(soap_wsrm_session_lock);
    strncpy(soap_wsrm_idname, s, sizeof(soap_wsrm_idname));
    soap_wsrm_idname[37] = '\0';
    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);
  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "soap_wsrm_send state=%d\n", data->state));
  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;
  if (data->seq)
  { soap_wsrm_seq_release(soap, data->seq);
    data->seq = NULL;
  }
  if (data->msg)
  { if (data->msg->list)
    { data->state = SOAP_WSRM_OFF; /* disable caching */
      data->msg = 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(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 (p->state != SOAP_WSRM_ACK && 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;
}

/******************************************************************************\
 *
 *	Check and Add/Send Acknowledgements
 *
\******************************************************************************/

/**
@fn int soap_wsrm_chk_acks(struct soap *soap)
@brief Internal function to check for AckRequested headers to update state.
@param soap context
@return SOAP_OK or error code
*/
static int
soap_wsrm_chk_acks(struct soap *soap)
{ DBGFUN("soap_wsrm_chk_acks");
  if (!soap->header)
    return SOAP_OK;
  if (soap->header->__sizeAckRequested)
  { struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
    int i;
    if (!data)
      return soap->error = SOAP_PLUGIN_ERROR;
    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[i].Identifier);
      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Ack requested for %s\n", soap->header->wsrm__AckRequested[i].Identifier));
      if (seq)
        seq->ackreq = 1;
      MUTEX_UNLOCK(soap_wsrm_session_lock);
    }
  }
  return SOAP_OK;
}

/**
@fn int soap_wsrm_add_acks(struct soap *soap, struct soap_wsrm_sequence *seq, int all)
@brief Internal function to add SequenceAcknowledgement headers for
AckRequested. Sends acknowledgements to the AcksTo destination service or back
to the source related to the seq. These sends can take SOAP_WSRM_TIMEOUT
seconds time, with at most 10 retries performed.
@param soap context
@param[in] seq pointer to sequence
@param[in] all when true, always include all acks
@return SOAP_OK or error code
*/
static int
soap_wsrm_add_acks(struct soap *soap, struct soap_wsrm_sequence *seq, int all)
{ int numack = 0;
  int piggy = 0;
  soap_header(soap);
  if (!soap->header)
    return soap->error;
  soap->header->__sizeSequenceAcknowledgement = 0;
  soap->header->wsrm__SequenceAcknowledgement = NULL;
  soap->header->wsrm__SequenceFault = NULL;
  MUTEX_LOCK(soap_wsrm_session_lock);
  if (seq->lastnum
   && (!seq->acksto
     || !strcmp(seq->acksto, soap_wsa_anonymousURI)
     || (seq->to && !strcmp(seq->to, seq->acksto))))
    piggy = 1;
  /* always add acks when we can piggy back them */
  if (piggy || all)
    seq->ackreq = 1;
  if (seq->acksto && strcmp(seq->acksto, soap_wsa_anonymousURI))
  { struct soap_wsrm_sequence *q;
    for (q = soap_wsrm_session; q; q = q->next)
    { if (q != seq && q->acksto && q->ackreq && !strcmp(q->acksto, seq->acksto))
        numack++;
    }
  }
  if (seq->ackreq)
    numack++;
  if (numack)
  { struct soap_wsrm_sequence *q;
    struct _wsrm__SequenceAcknowledgement *ack;
    ack = (struct _wsrm__SequenceAcknowledgement*)soap_malloc(soap, numack * sizeof(struct _wsrm__SequenceAcknowledgement));
    if (!ack)
    { MUTEX_UNLOCK(soap_wsrm_session_lock);
      return soap->error = SOAP_EOM;
    }
    numack = 0;
    for (q = soap_wsrm_session; q; q = q->next)
    { if (q->acksto && q->ackreq && !strcmp(q->acksto, seq->acksto))
      { if (soap_wsrm_set_ack(soap, seq, &ack[numack]))
        { MUTEX_UNLOCK(soap_wsrm_session_lock);
          return soap->error;
        }
        q->ackreq = 0;
        numack++;
      }
    }
    MUTEX_UNLOCK(soap_wsrm_session_lock);
    /* for each ack requested, return ack */
    if (!piggy)
    { int retry;
      const char *acksto;
      struct soap *acksto_soap = soap_copy(soap);
      struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
      DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Sending ack for seq %s to %s\n", seq->id, seq->acksto));
      if (!data)
        return soap->error = SOAP_PLUGIN_ERROR;
      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 = numack;
      acksto_soap->header->wsrm__SequenceAcknowledgement = ack;
      acksto = soap_strdup(acksto_soap, seq->acksto);
      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
    { soap->header->__sizeSequenceAcknowledgement = numack;
      soap->header->wsrm__SequenceAcknowledgement = ack;
      soap->header->wsa5__Action = (char*)SOAP_NAMESPACE_OF_wsrm"/SequenceAcknowledgement";
    }
  }
  else
    MUTEX_UNLOCK(soap_wsrm_session_lock);
  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;
#ifndef SOAP_WSRM_2005
    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);
#endif
  }
  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_CLOSED)
  { 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);
  }
  else
    ack->Final = NULL;
  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 && (upper == 0 || p->num <= upper))
    { 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) + 9);
  if (!s)
  { soap->error = SOAP_EOM;
    return NULL;
  }
  sprintf(s, "%s%8.8x", soap_wsrm_idname, soap_wsrm_idnum++);
  return s;
}

/**
@fn struct soap_wsrm_sequence *soap_wsrm_seq_lookup_id(struct soap *soap, const char *id)
@brief Function to look up sequence given its id.
@param soap context
@param[in] id identifier string
@return sequence handle, must be released with @ref soap_wsrm_seq_release when no longer used
*/
struct soap_wsrm_sequence *
soap_wsrm_seq_lookup_id(struct soap *soap, const char *id)
{ struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  struct soap_wsrm_sequence *seq = NULL;
  if (data)
  { MUTEX_LOCK(soap_wsrm_session_lock);
    seq = soap_wsrm_seq_lookup_data(data, id);
    if (seq)
      seq->refs++;
    MUTEX_UNLOCK(soap_wsrm_session_lock);
  }
  return seq;
}

/**
@fn struct soap_wsrm_sequence *soap_wsrm_seq_lookup_data(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_data(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(struct soap *soap, const char *id)
@brief Function to look up sequence given its id.
@param soap context
@param[in] id identifier string
@return sequence handle, must be released with @ref soap_wsrm_seq_release when no longer used
*/
struct soap_wsrm_sequence *
soap_wsrm_seq_lookup(struct soap *soap, const char *id)
{ struct soap_wsrm_data *data = (struct soap_wsrm_data*)soap_lookup_plugin(soap, soap_wsrm_id);
  struct soap_wsrm_sequence *seq = NULL;
  if (data)
  { MUTEX_LOCK(soap_wsrm_session_lock);
    seq = soap_wsrm_seq_lookup_ack(data, id);
    if (seq)
      seq->refs++;
    MUTEX_UNLOCK(soap_wsrm_session_lock);
  }
  return seq;
}

/**
@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->refs = 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 struct soap_wsrm_sequence *soap_wsrm_seq(struct soap *soap)
@brief Assuming a SOAP header is received, gets the sequence associated with the message.
@param soap context
@return sequence handle, must be released with @ref soap_wsrm_seq_release when no longer used
*/
struct soap_wsrm_sequence *
soap_wsrm_seq(struct soap *soap)
{ struct soap_wsrm_sequence *seq = NULL;
  DBGFUN("soap_wsrm_seq");
  if (soap->header && soap->header->wsrm__Sequence)
    seq = soap_wsrm_seq_lookup(soap, soap->header->wsrm__Sequence->Identifier);
  return seq;
}

/**
@fn void soap_wsrm_seq_release(struct soap *soap, struct soap_wsrm_sequence *seq)
@brief Releases handle
@param soap context
@param seq sequence handle
*/
void
soap_wsrm_seq_release(struct soap *soap, struct soap_wsrm_sequence *seq)
{ DBGFUN1("soap_wsrm_seq_release", "refs=%ld", (unsigned long)seq->refs);
  MUTEX_LOCK(soap_wsrm_session_lock);
  if (seq->refs)
    seq->refs--;
  MUTEX_UNLOCK(soap_wsrm_session_lock);
}

/**
@fn int soap_wsrm_seq_created(struct soap *soap, struct soap_wsrm_sequence *seq)
@brief Function to check if a sequence is created.
@param soap context
@param seq pointer to sequence
@return 0 (not created) or 1 (created)
*/
int
soap_wsrm_seq_created(struct soap *soap, struct soap_wsrm_sequence *seq)
{ DBGFUN("soap_wsrm_seq_created");
  if (!seq || seq->state != SOAP_WSRM_CREATED)
    return 0;
  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 0;
  }
  return 1;
}

/**
@fn int soap_wsrm_seq_valid(struct soap *soap, struct soap_wsrm_sequence *seq)
@brief Function to check if a sequence is not valid, terminated, or needs to be
terminated when expired.
@param soap context
@param seq pointer to sequence
@return 0 (invalid) or 1 (valid)
*/
int
soap_wsrm_seq_valid(struct soap *soap, struct soap_wsrm_sequence *seq)
{ DBGFUN("soap_wsrm_seq_valid");
  if (!seq || (seq->state != SOAP_WSRM_CREATED && seq->state != SOAP_WSRM_CLOSED))
    return 0;
  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 0;
  }
  return 1;
}

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

/**
@fn ULONG64 soap_wsrm_msgs(struct soap *soap, const struct soap_wsrm_sequence *seq)
@brief Returns the number of complete messages received, or 0 when there is a gap.
@param soap context
@param[in] seq pointer to sequence
@return number of messages received or 0
*/
ULONG64
soap_wsrm_msgs(struct soap *soap, const struct soap_wsrm_sequence *seq)
{ 
  DBGFUN("soap_wsrm_msgs");
  if (seq && seq->ranges && seq->ranges->lower == 1 && seq->ranges->upper == seq->lastnum)
    return seq->lastnum;
  return 0;
}

/**
@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, **q;
  p = (struct soap_wsrm_message*)malloc(sizeof(struct soap_wsrm_message));
  if (!p)
  { soap->error = SOAP_EOM;
    return NULL;
  }
  p->next = NULL;
  p->num = num;
  p->state = SOAP_WSRM_NACK;
  p->list = p->last = NULL;
  for (q = &seq->messages; *q; q = &(*q)->next)
    ;
  *q = p;
  return p;
}

/**
@fn void soap_wsrm_msg_free(struct soap *soap, struct soap_wsrm_message *p)
@brief Internal function to deallocate all cached message content 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;
    if (q->buf)
      free((void*)q->buf);
    free((void*)q);
  }
  p->list = p->last = NULL;
}

#ifdef __cplusplus
}
#endif

