Posted: February 22nd, 2012 | Author: Michael | Filed under: UCMA 2.0, UCMA 3.0 | Tags: Conversation | No Comments »
To make it possible for developers to extend the functionality of Lync without having to deal with the nitty-gritty details of Session Initiation Protocol (a.k.a. SIP), UCMA introduces a lot of abstractions. One of the abstractions that pops up most frequently is the Conversation class. Unfortunately, it’s also one of the most confusing to new UCMA developers. This is partly because its purpose isn’t immediately obvious when you start doing things like sending IMs or answering audio calls. Continue reading “What are Conversation objects for?” »
Posted: February 9th, 2012 | Author: Michael | Filed under: Lync Development, MSPL, OCS Development | Tags: MSPL | 7 Comments »
Some things are much easier to understand when you can see them in action. To add to my post on setting up MSPL scripts, I recorded a 12-minute video that shows the step-by-step process for installing an MSPL script on a Lync Front End Server and checking that it has started up properly. Let me know if this is a helpful format or if you have any questions or suggestions!
Posted: December 8th, 2011 | Author: Michael | Filed under: Lync Development, MSPL, OCS Development | Tags: MSPL, SDP | No Comments »
Microsoft SIP Processing Language, a.k.a. MSPL, can be handy for changing Lync’s routing behavior, to do things like block or reroute calls based on their origin, or send all calls of a certain type to a UCMA application. In certain cases, when writing MSPL scripts, you may want your script to behave differently depending on whether it is dealing with an audio call or an instant messaging session. It’s not immediately obvious how to distinguish between these two types of calls in MSPL, so I wanted to write up a quick description of how you can do this. Continue reading “Distinguishing IM vs. audio calls in MSPL” »
Posted: November 16th, 2011 | Author: Michael | Filed under: Lync Development, OCS Development | Tags: GRUU, routing | 7 Comments »
The term “GRUU” comes up fairly often in Lync development, and I wanted to use this post to give a brief overview of what it means and why you would use one.
GRUUs in their natural habitat?
Contrary to what you may have been led to believe, a GRUU is not a large herd animal or a part of the small intestine. The initials actually stand for “Globally Routable User-Agent URI,” which may or may not clarify things for you at all. The definition of a GRUU is explained in an IETF document which you can find here. In a nutshell, a GRUU is a SIP URI which has a few properties:
Continue reading “What is a GRUU?” »
Posted: October 7th, 2011 | Author: Michael | Filed under: UCMA 2.0, UCMA 3.0 | Tags: signaling, SIP | 5 Comments »
For the most part, UCMA keeps Lync operations at a high level, and allows you to ignore the details of the SIP messages that are going back and forth. At times, however, you may need to look at the actual SIP message that a UCMA application is sending or receiving, or add a specific header to an outgoing message. This post shows how to do both of these things. Continue reading “Manipulating SIP headers with UCMA” »
Posted: September 12th, 2011 | Author: Michael | Filed under: UCMA 2.0, UCMA 3.0 | Tags: transfers | No Comments »
The steps in a transfer have always confused me a bit, and since I’ve found that other people also sometimes get confused when working with Lync transfers in UCMA, I thought I would write up a few notes on how they work.
Before I knew much of anything about telephony, I had a vague notion that transfers worked as in the diagram below. Phone A would be in a call with Phone B, and B’s end of the call would sort of get passed over to Phone C.
If you think of transfers this way, you would expect that when you transfer a call, the original call between A and B stays active and becomes a call between A and C. You would also probably expect that Phone A doesn’t need to do anything in order for the transfer to occur, and that all the work happens between Phone B and Phone C as they switch places. Making sense so far?
In actual fact, what happens in Lync is very different. Here are the steps, in a nutshell:
- Endpoint B sends a REFER message to Endpoint A. This message has the SIP URI of Endpoint C.
- Endpoint A initiates an entirely new call to Endpoint C.
- The original call between A and B is terminated.
This last step, terminating the original call, can happen either immediately after the REFER message, or after the call from A to C connects successfully. In Lync, this is the difference between an unattended transfer (the former case) and an attended transfer (the latter case).
Here is a diagram of the actual process:
One case where this can be confusing is if you are looking at the call state changes. Let’s say you’ve hooked up an event handler to the AudioVideoCall.StateChanged event on the call between A and B, to record every state change. This is what you’ll see when you call the BeginTransfer method on that AudioVideoCall object:
- Established
- Transferring
- Terminating
- Terminated
This often confuses UCMA developers at first, because it appears as though the transfer has failed and the call has terminated. But what’s actually happened here is that the transfer has succeeded, and so the original call (between A and B, in our diagram) can terminate. If neither Endpoint A nor Endpoint C are managed by your UCMA application, you no longer have any control over the new call that results from the transfer, so you can’t track its state changes even if you want to.
There is another special type of transfers, supervised transfers, which I won’t go into here since I’ve covered them in a previous post. I also have a post showing how to perform a transfer in UCMA from back in the 2.0 days (the process hasn’t really changed in UCMA 3.0). Finally, if you want a more comprehensive discussion of transfers and other ways to have fun with audio calls in UCMA, you can always refer to the book.
Posted: June 22nd, 2011 | Author: Michael | Filed under: Lync Development, OCS Development | Tags: Lync, OCS, Office Communications Server | No Comments »
In writing applications to extend OCS or Lync — especially applications that mimic client functionality — I’ve run into a number of situations where the application has a user’s domain and username but needs to figure out the user’s SIP URI in order to perform OCS/Lync operations on behalf of that user. There is not much documentation out there on how to do this, and it’s hard to find. So I wanted to write up a quick explanation of how to look up the SIP URI of a given domain account from Active Directory.
I’ll start with a couple of notes. First, it’s true that some organizations by convention use a SIP URI format that makes it easy to derive the SIP URI from the username with a bit of string manipulation. For instance, if all users have SIP URIs of the form sip:username@fabrikam.com, the application can put together the SIP URI itself by concatenating the username with “@fabrikam.com”. Also, in many cases SIP URIs by convention are the same as email addresses with sip: added to the front. If this is the case, an application that has the email address of the user can simply add sip: to produce the SIP URI.
The important thing to note is that while some organizations use these SIP URI formats by convention, there is nothing to prevent an administrator from giving a user a completely different SIP URI. If you are creating an application that needs to work in environments besides your own, it is potentially dangerous to assume that all SIP URIs will follow a consistent format like the ones described above.
This isn’t a huge deal, because retrieving SIP URIs for users, assuming you can connect to Active Directory to do an LDAP query, is relatively simple. The SIP URI for a user (at least, for users who are UC-enabled and have SIP URIs assigned) is stored in the msRTCSIP-PrimaryUserAddress property. There are plenty of articles out there on the Internet describing how to retrieve information from Active Directory, so I won’t go into all the details here, but you can use code that looks something like this:
public string GetSipUri(string cn, string ou)
{
DirectoryEntry entry = new DirectoryEntry(
"<a href="ldap://dc.fabrikam.com/cn">LDAP://dc.fabrikam.com/cn</a>=" + cn + ", ou=" + ou +
", dc=fabrikam, dc=com");
PropertyCollection properties = entry.Properties;
return properties["msRTCSIP-PrimaryUserAddress"].ToString();
}
You’ll need to reference System.DirectoryServices to do this. Basically what you are doing is retrieving the object representing the user via LDAP, and grabbing the msRTCSIP-PrimaryUserAddress property from that object.
If you want to do the reverse of this (look up the username based on a SIP URI) you can use the DirectorySearcher object, with a search filter like the following:
searcher.Filter = "(&(objectClass=user)(msRTCSIP-PrimaryUserAddress=sip:michael@fabrikam.com))";
Feel free to get in touch if you have any questions!
Posted: February 21st, 2010 | Author: Michael | Filed under: OCS Development, UCMA 2.0 | 1 Comment »
Many of you, since encountering OCS 2007 R2 and its trusty sidekick server-side API, UCMA 2.0, have been wondering, “How do I spy on people and secretly record their audio conferences?”
Luckily for you, to complement its delightfully straightforward automation of SIP messaging, UCMA 2.0 has a rich array of covert operations functionality.
That may be a slight exaggeration. What it does provide is a way for server-side applications to perform a “trusted conference join.” A UCMA 2.0 application running as a trusted service which has authenticated with the OCS server by means of a certificate can join a conference invisibly, meaning it does not show up in the roster of conference participants.
Although I was tempted to title this article “Wiretapping in UCMA 2.0,” the trusted conference join feature has many uses. It allows you to create applications that provide services to OCS conferences without causing distracting and tacky-looking bots to appear in the list of participants. Some examples:
- Recording conversations for auditing, monitoring, or training purposes
- Conference timekeeping for businesses that bill by the minute
- Piping music or audio announcements into a conference
- Silent monitoring of conferences for training purposes
- Scaring people in a conference by suddenly saying something when they didn’t know you were there
So how do you do it?
First of all, your application needs to be using an application endpoint rather than a user endpoint. User endpoints cannot join conferences as a trusted participant.
When you call the BeginJoin method on the ConferenceSession object to join a conference,you can supply a ConferenceJoinInformation object with the URI of the conference you want to join. This ConferenceJoinInformation object also has a property called IsTrustedJoin. When this property is set to true,your application endpoint will join the conference as a trusted participant.
Let’s say you are joining a conference that has already started, and you have the conference URI stored in a local variable. You would do something like this:
// Create a ConferenceJoinInformation object with the conference URI
ConferenceJoinInformation joinInfo = new ConferenceJoinInformation(
new Microsoft.Rtc.Signaling.RealTimeAddress(conferenceUri));
// Make this a trusted join
joinInfo.IsTrustedJoin = true;
// Create a new conversation
conversation = new Conversation(_applicationEndpoint);
// Use the conversation to join the conference
conversation.ConferenceSession.BeginJoin(joinInfo,
result =>
{
conversation.ConferenceSession.EndJoin(result);
},
null);
First, you create a new instance of ConferenceJoinInformation, passing in the conference URI. In order for the constructor to like it, you need to turn the string that contains the URI into a RealTimeAddress object.
Next, you set the IsTrustedJoin property to true.
Finally, you create a new conversation and call the BeginJoin method, passing in the join information.
When the asynchronous operation completes, your application will be a participant in the conference, sending and receiving media like any other, but it will be INVISIBLE.
There is one other point I want to call out here before concluding. If you want to have more than one of these invisible participants from the same application (e.g. one to record and one to make animal noises) you can do this, on two conditions: you will need to create a Conversation object for each participant, and you will need to impersonate a fake URI so that the participants have different URIs.
To do this, you use the Impersonate method on the Conversation object, as below:
// Create a new conversation with impersonation
conversation = new Conversation(_applicationEndpoint);
conversation.Impersonate("RandomSounds@____________.com",
"tel:+15555551212", "Random Sounds");
The URI you use can be completely fabricated; it doesn’t need to be a real contact.
This technique can be especially handy when combined with a back-to-back user agent (B2BUA) to proxy remote users into a conference invisibly. More on this in a future post if there is interest.
I take no responsibility for any imprudent or illegal things you do with trusted conference participants!
Posted: October 8th, 2009 | Author: Michael | Filed under: OCS Development, UCMA 2.0 | No Comments »
Recently I was working on some code to invite new participants to an A/V conference, and learned about some Office Communicator behavior that may throw you off if you are trying to dial out to a URI.
The AudioVideoMcuSession class exposes a BeginDialOut method which tells the MCU to dial out to a particular URI to bring in a new participant. By supplying McuDialOutOptions, you can specify the display name and language to use for the invited participant, which can be useful if you are inviting someone at a PSTN phone number.
If your invitation goes to an endpoint running Office Communicator, though, Communicator will reject the dial-out invitation, take the conference URI, and join the conference itself. It’s like those bosses you hear horror stories about who will brashly reject a new idea you propose, and then suggest it themselves at the next company meeting.
So if you call BeginDialOut with a SIP URI and the endpoint that receives the dial-out is a Communicator client, you will see a ConferenceFailureException in your code with the reason “userDeclined,” but Communicator will still successfully join the conference.
You have two options for dealing with this: you can ignore that particular exception, confirm that the invited participant has joined the conference,and proceed; or you can use the BeginInviteRemoteParticipants method on the Conversation object to bring in the new participants. If all you have is the AudioVideoMcuSession,you can find the associated Conversation at AudioVideoMcuSession.ConferenceSession.Conversation.