An application can subscribe to events supported by the server, including events related to presence. An application also can publish presence information by using constructors and methods on the SipSubscription class. To create a SipSubscription instance, a developer must provide an implementation of the ISipSubscriptionProcessor interface.

Capabilities of the SipSubscription Class

The SipSubscription class provides low level subscription and notification capabilities. A user of this class must pass an ISipSubscriptionProcessor interface to interact with the subscription.

The principal functionalities exposed by the SipSubscription class are:

  • Exposure of notifications received for a subscription

  • Automatic refresh of a subscription before it expires

  • Automatic resubscription if a subscription terminates unexpectedly

  • The Refresh method, which allows users to refresh a subscription any time during the life of the subscription

Details of ISipSubscriptionProcessor Member Methods

The following sections provide information about the methods that make up the ISipSubscriptionProcessor interface.

GetExtensionHeaders

The GetExtensionHeaders method is invoked when SipSubscription sends a Refresh request to the server (from the server's perspective this is a SUBSCRIBE request). Users who intend to include additional extension headers in the SUBSCRIBE request should set the extensionHeaders parameter. This method is most often used to add a specific ACCEPT header, which informs the server about ContentType types that are acceptable for notification messages.

GetMessageBody

The GetMessageBody method is invoked when SipSubscription sends a Refresh request to the server (from the server's perspective this is a SUBSCRIBE request). If the subscription request requires that the message body must be sent with the request, this method should set the contentType and messageBody parameters. contentType should be set to null for SUBSCRIBE requests in which there is no messageBody.

ProcessErrorResponse

The ProcessErrorResponse method is invoked whenever a response other than 200 OK is received on the subscription dialog.

SipSubscription does not throw an error on a subscription attempt using bad parameters. Use ProcessErrorResponse to find detail information associated with the subscription attempt.

SubscriptionStateChanged

The SubscriptionStateChanged method is invoked whenever the underlying subscription dialog state is changed, except in query mode (that is, except when IsQueryOnly is true). If the subscription state transitions to WaitingForRetry due to an error response from the server, ProcessErrorResponse is called in addition to SubscriptionStateChanged. For a summary of the possible state transitions, see CurrentState.

ProcessNotification

The ProcessNotification method is invoked on every notification. The most common implementation of this method entails writing code to parse the notification message body.

Example Implementations of ISipSubscriptionProcessor Methods

The first example shows an implementation of the methods on the ISipSubscriptionProcessor interface with the purpose of receiving notifications related to changes in the contacts and contact groups list.

Copy Code
public class ContactGroupSubscription : ISipSubscriptionProcessor
{
  void GetExtensionHeaders(SipSubscription.RequestType requestType, out IEnumerable<SignalingHeader> extensionHeaders)
  {
	extensionHeaders = null;
  }

  void GetMessageBody(SipSubscription.RequestType requestType, out ContentType contentType, out byte[] messageBody)
  {
	contentType = null;
	messageBody = new Byte[0];
  }

  void ProcessErrorResponse(SipResponseData message)
  {
	Console.WriteLine ("error = " + message.ResponseCode);
  }

  void SubscriptionStateChanged(SubscriptionStateChangedEventArgs e)
  {
	Console.WriteLine("state " +e.PreviousState + " => " + e.NewState);
  }

  void ProcessNotification(SipResponseData message)
  {
	String s = message.GetMessageBodyString();
	Console.WriteLine("notification");
	Console.WriteLine(s);
  }
}

The second example shows how to create a SipSubscription instance that uses the preceding ISipSubscriptionProcessor interface implementation (the ContactGroupSubscription class implements the ISipSubscriptionProcessor interface). When the event named in the SipSubscription constructor (“vnd-microsoft-roaming-contacts”) is raised, users are able to get the current list of contacts and contact groups and also can be notified of any changes in this list.

Copy Code
ContactGroupSubscription mycgSubscription = new ContactGroupSubscription();
SipEndpoint myEndpoint = ...; // Assumed to be created elsewhere
RealTimeAddress cgAddress = new RealTimeAddress("sip:group1@contoso.com");
SipSubscription mySubScription = new SipSubscription(myEndpoint, cgAddress, "vnd-microsoft-roaming-contacts", mycgSubscription);
mySubscription.BeginSubscribe();

The third example shows an alternate implementation of the ISipSubscriptionProcessor interface, with the aim of getting data that pertains to the registered user of a particular local endpoint, or roaming data. In Office Communications Server 2007 roaming data consists of three sets of self data:

  • Category list—a list of the different types of published user data from all endpoints

  • Container membership data—all ACL memberships for the containers

  • Subscriber list—a list of people who have added you as a contact, but you have not yet acknowledged them

Creating a SipSubscription instance is similar to that shown in the previous example except that the "vnd-microsoft-roaming-self" is used in the SipSubscription constructor.

Copy Code
public class RoamingDataSubscription : ISipSubscriptionProcessor
{
  void GetExtensionHeaders(SipSubscription.RequestType requestType, out IEnumerable<SignalingHeader> extensionHeaders)
  {
	extensionHeaders = null;
  }

  void GetMessageBody(SipSubscription.RequestType requestType, out ContentType contentType, out byte[] messageBody)
  {
	contentType = null;
	messageBody = new Byte[0];
	if (requestType == SipSubscription.RequestType.Subscribe ||
		requestType == SipSubscription.RequestType.Refresh)
	{
	contentType = new ContentType("application/vnd-microsoft-roaming-self+xml");
	StringWriter sw = new StringWriter(new StringBuilder(128), CultureInfo.InvariantCulture);
	XmlTextWriter writer = new XmlTextWriter(sw);
	writer.WriteStartElement("roamingList");
	writer.WriteAttributeString("xmlns", "http://schemas.microsoft.com/2006/09/sip/roaming-self");
	this.AddRoamingType(writer, "categories");
	this.AddRoamingType(writer, "containers");
	this.AddRoamingType(writer, "subscribers");
	writer.WriteEndElement();
	sw.Close();
	writer.Close();
	messageBody = System.Text.Encoding.UTF8.GetBytes(sw.ToString());
}
  }

  void ProcessErrorResponse(SipResponseData message)
  {
	Console.WriteLine ("error = " + message.ResponseCode);
  }

  void SubscriptionStateChanged(SubscriptionStateChangedEventArgs e)
  {
	Console.WriteLine("state " + e.PreviousState + " => " +e.NewState);
  }

  void ProcessNotification(SipResponseData message)
  {
	String s = message.GetMessageBodyString();
	Console.WriteLine("notification");
	Console.WriteLine(s);
  }

  private void AddRoamingType(XmlTextWriter writer, string roamingType)
  {
	writer.WriteStartElement("roaming");
	writer.WriteAttributeString("type", roamingType);
	writer.WriteEndElement();
  }
}