text:  are almost at the point where our Powershell commandlet will be useful, but first we need to learn about establishing the session protocol. You see, SIP stands for “Session Initiation Protocol”. The key here is the word “Initiation”. Yup, SIP just manages telling so and so that so and so wants to talk to them. It doesn’t actually do the communication.
  Imagine this scenario. You want to talk to someone, let’s call him Iggy, who lives on the distant planet of Flurgleblarf. You have no idea where exactly Iggy lives though, so you call your friend Sip over. Sip knows how to figure out who is where on Flurgleblarf and responds that “yes. I can find Iggy”. That’s fine, so you can now figure out where Iggy is.
  However, Flurgleblarf is far from a homogenous planet. Well over one hundred mutually unintelligible languages are spoken there and you know only two of them – Flurglussian and Blarfag. If Iggy speaks a different language than these two, you’ll need to find a translator. Luckily, you and everyone on Flurgleblarf knows someone who can speak Sdp. You go to your local Sdp expert and ask him to ask Iggy’s Sdp expert what languages he knows. Even better, you tell your Sdp expert to tell Iggy’s that you can speak Flurglussian and Blarfag. If Iggy can’t speak one of those, he can suggest an alternate language.
  So that is basically what SDP is. It stands for “Session Description Protocol” and allows you to setup text, voice, and media communications. For now, we are only going to talk about text communications, but we will discuss the others in future posts.
  SDP consists of different lines, beginning with a letter and an equal sign that describe the media types supported. For a good short description of the information one can express with SDP, see the Wikipedia entry.
  Lucky for us, UCMA provides support for SDP so we don’t need to worry about its syntax. The interface that we will need to provide SDP support is IOfferAnswer. In order for our commandlet to partake in media negotiation, it must implement this interface.
  public class SendInstantMessageCommand : PSCmdlet, IOfferAnswer
  This interface contains five methods. Two of them, HandleReInviteOfferReceived and HandleThirdPartyControlOfferReceived apply only to reinvites and we will not discuss them today. A ReINVITE occurs when one of the parties wishes to change media. An example of this is two parties switching from an IM session to a video session.
  The other three methods are of interest to us today.
  · GetOffer – This method is called when you are initiating the INVITE and allows you to specify what type of media to use for the dialog.
  · GetAnswer – This method is called when you are receiving the INVITE and the other party did not specify an offer.
  · SetAnswer – This method is called when you are receiving an INVITE and the other party did specify an offer. In this case your job is to accept the offer or specify that certain media types are not acceptable.
  In order to get UCMA to call your IOfferAnswer methods, you must set the OfferAnswerNegotiation property of the SignalingSession before you call BeginParticipate.
  _session.OfferAnswerNegotiation = this;
  As mentioned before, you do not need to implement HandleReInviteOfferReceived or HandleThirdPartyControlOfferReceived because they only apply to reinvites. We will have the same response to both GetAnswer and GetOffer so let’s create a method that returns a ContentDescription we can use for either one. Below is the code for it, which I will proceed to explain. You will need to add a using statement for System.Net.Mime to get this to compile.
  /// <summary>
  /// Retrieves the content description for offers and answers
  /// </summary>
  /// <param name="session">The session</param>
  /// <returns>The content description</returns>
  private ContentDescription GetContentDescription(SignalingSession session)
  {
  IPAddress ipAddress;
  // This method is called back every time an outbound INVITE is sent.
  WriteVerbose("Getting the content description.");
  if (_session.Connection != null)
  {
  ipAddress = _session.Connection.LocalEndpoint.Address;
  }
  else
  ipAddress = IPAddress.Any;
  Sdp<SdpGlobalDescription, SdpMediaDescription> sessionDescription = new Sdp<SdpGlobalDescription, SdpMediaDescription>();
  //Set the origin line of the SDP
  //s, t, and v lines are automatically constructed
  sessionDescription.GlobalDescription.Origin.Version = 0;
  sessionDescription.GlobalDescription.Origin.SessionId = "0";
  sessionDescription.GlobalDescription.Origin.UserName = "-";
  sessionDescription.GlobalDescription.Origin.Connection.Set(ipAddress.ToString());
  //Set the connection line
  sessionDescription.GlobalDescription.Connection.TrySet(ipAddress.ToString());
  SdpMediaDescription mditem = new SdpMediaDescription("message");
  mditem.Port = 5061;
  mditem.TransportProtocol = "sip";
  mditem.Formats = "null";
  SdpAttribute aitem = new SdpAttribute("accept-types", "text/plain");
  mditem.Attributes.Add(aitem);
  //Append the Media description to the Global description
  sessionDescription.MediaDescriptions.Add(mditem);
  ContentType ct = new ContentType("application/sdp");
  WriteVerbose("Created content description");
  return new ContentDescription(ct, sessionDescription.GetBytes());
  }
  In the following sections, let’s break this down into individual pieces that we can understand.
  We need to specify the IP address for the connection piece in SDP. If we have a connection associated with our session, we use its IP address. If we do not have one, we play it safe and use IPAddress.Any.
  if (_session.Connection != null)
  ipAddress = _session.Connection.LocalEndpoint.Address;
  else
  ipAddress = IPAddress.Any;
  This creates the session description helper that we will use to create our SDP response.
  Sdp<SdpGlobalDescription, SdpMediaDescription> sessionDescription = new Sdp<SdpGlobalDescription, SdpMediaDescription>();
  The following four statements construct the o= section of the SDP offer, or in other words the origin. The version is the version of SDP and should always be 0. We do not care about the session Id so we set it to 0. The – for the user name indicates we do not care about it. Finally, we set the connection address to the IP address we previously obtained.
  sessionDescription.GlobalDescription.Origin.Version = 0;
  sessionDescription.GlobalDescription.Origin.SessionId = "0";
  sessionDescription.GlobalDescription.Origin.UserName = "-";
  sessionDescription.GlobalDescription.Origin.Connection.Set(ipAddress.ToString());
  This sets the c= line of the SDP offer. The TrySet method makes sure the IP address is recognized but does not throw an exception if it is not.
  sessionDescription.GlobalDescription.Connection.TrySet(ipAddress.ToString());
  The SDPMediaDescription class creates perhaps the most important piece of the SDP – stating what types of media we accept. There are a number of valid values for it and what is most important is the two peers agree on the names. “Audio” and “video” are common values but here we will use “message”.
  SdpMediaDescription mditem = new SdpMediaDescription("message");
  We will set the port to 5061 because this commandlet will always use Tls. For Tcp, you would want to use port 5060. The transport protocol should almost always be “sip” in the case of messages, though it could be some other protocol. For video and audio, it will most certainly not be sip. The message media type does not have any format information so we can leave that null – though note that we cannot just simply set it to null.
  We will set the Formats property to the string null, but these are tokens found at the end of the “m” line. For Rtp, this may be a list of payload types.
  mditem.Port = 5061;
  mditem.TransportProtocol = "sip";
  mditem.Formats = "null";
  This is a very important line that states that the MIME type we will use is text/plain.
  SdpAttribute aitem = new SdpAttribute("accept-types", "text/plain");
  These last lines basically add the MIME type description to our media description, then add the media description to our SDP (it is possible to have multiple media descriptions) and then return a new ContentDescription instance that accepts the MIME type of the content description (which should always be “application/sdp”) and the bytes of the session description we just created.
  mditem.Attributes.Add(aitem);
  //Append the Media description to the Global description
  sessionDescription.MediaDescriptions.Add(mditem);
  ContentType ct = new ContentType("application/sdp");
  You can now set the method bodies of GetOffer and GetAnswer to the following.
  return GetContentDescription((SignalingSession)sender);
  We are now left only with SetAnswer. In this case the client has sent us the offer and we need to respond to it. For the short term, this code will not be called because our commandlet is not set up to be called itself. It only sends messages and cannot receive them. In the future though, this code will be helpful.
  For SetAnswer we need to parse the response and see if it supports our “message” content type with the SIP protocol. If for some reason we are unable to parse, we must terminate the session. The code for SetAnswer is the following.
  public void SetAnswer(object sender, ContentDescription answer)
  SignalingSession session = sender as SignalingSession;
  // Verify that the answer received is consistent
  WriteVerbose("Setting the media answer.");
  byte[] Answer = answer.GetBody();
  if (Answer != null)
  Sdp<SdpGlobalDescription, SdpMediaDescription> sessionDescription = new Sdp<SdpGlobalDescription, SdpMediaDescription>();
  if (!sessionDescription.TryParse(Answer))
  {
  Cleanup();
  Error("CannotParseAnswer", "Unable to parse the answer.");
  return;
  }
  else
  IList<SdpMediaDescription> ActiveMediaTypes = sessionDescription.MediaDescriptions;
  if ((ActiveMediaTypes.Count == 1) &&
  (ActiveMediaTypes[0].MediaName.Equals("message", StringComparison.Ordinal)) &&
  (ActiveMediaTypes[0].Port > 0) &&
  (ActiveMediaTypes[0].TransportProtocol.Equals("sip", StringComparison.OrdinalIgnoreCase)))
  {
  WriteVerbose("Supported media type");
  }
  else
  Cleanup();
  Error("UnsupportedMediaType", "Unsupported media type");
  Now that I have explained it, this code probably does not look very scary. We first try to parse the SDP and if that fails we terminate the session. We don’t need to worry about the callback here because we don’t care about this session any more. We also do not need to worry about removing it from our dictionary because EndParticipate will fail.
  After we have successfully parsed the SDP, we make sure that we define a port, use the “message” media type, and use “sip” as the transport protocol. By using SIP as the transport protocol, we are saying that we will use the SIP MESSAGE message to communicate.
  Of course, our commandlet still can’t do much that is interesting. In the next post though, we’ll finally send our message..
  In truth, this is now a new blog. I'm not sure what we will be covering in the next several months, short of the already stated UCMA 2.0 APIs. However, there are some sharp differences between this blog and my old one.
  First, it already has a new look and feel. I would love to personalize the site a bit more, such as what I have done on my photo blog, but this system is quite a bit more limiting.
  As with the recent past, I will not discuss photography or real estate on this blog. Photography is avidly discussed in my other blog -. For real estate, my wife has taken that blog over and syndicates it in several places - such as
  The biggest change is that this is no longer a Speech Server blog. I may discuss some aspects of the Speech APIs from time to time, but only in the context of how they contribute to the OCS APIs. The simple fact is I no longer work in this group. I work in the OCS Server Core group and I am therefore most knowledgeable about what is going on in that area.
  My goal is for this blog to be a consistent source of information on Office Communications Server. In particular, I hope to cover many of the parts of the product new in R2 on this blog. I will venture outside the safe confines of Office Communications Server and discuss some quirks I have noticed with Windows services. (Disclaimer: I am not in the Windows org so these are just my observations after some experimentation) Some of you may be aware that in the services control panel, you can right click on a service and on the "Recovery" tab you can manage what occurs when a service fails.
  The following are the options.
  First failure: what should occur the first time the service fails. Valid options are "Take No Action", "Restart the Service", "Run a Program", and "Restart the Computer".
  Second failure: same options the second time a service fails
  Subsequent failures: same options for any subsequent failure
  Reset fail count after: the number of days the service must be running before the failure count is reset
  Restart service after: the amount of time in minutes to wait to restart the service
  This is very nice, but it is very easy to misunderstand what these values actually do. I have seen a number of services (and I tried this myself) set these values to 0 days and 0 minutes. The problem is your service will continually restart if you set the failure count to reset after 0 days, if the service at least started correctly. The result is only the first option ("first failure") will ever be run.
  To fix this, set the failure count to reset after one day. The drawback to this approach is your service may stay stopped after failing several times but this likely means something is toast anyways.
  One thing also to take into account is not all services will work with the reset logic - or in other words just setting the recovery options on any service does not guarantee that it will restart. In order for the service to restart, it must exit abnormally. This generally means the service must exist with a non-zero exit code and the service status must not be stopped (note: this has changed for Vista - it is possible to set the service status to stopped and provide an exit code to trigger the restart logic).