Lync 2010 UCMA 3.0 SDK - Call Forwarding - lync-2010

How can I use the Lync API to change current user "call forwarding" option to another SIP or a number?
Thanks

private void OnIncomingAudioVideoCallReceived(object sender, CallReceivedEventArgs<AudioVideoCall> e)
{
// Forward incoming Audio calls OR accept call
try
{
_logger.Info("Incoming call from " + e.RemoteParticipant.Uri);
e.Call.Forward("sip:..");
}
catch (InvalidOperationException ioex)
{
_logger.Error("Failed forwarding incoming call", ioex);
}
}

Related

Server restart results in never ending Bad_ServiceUnsupported errors, onSubscriptionTransferFailed not called

my Milo client (sdk 0.4.1) subscribes on server events by use of UaSubscription and can receive events sucessfully. But once I restart the server, the clients only logs errors in an endless loop in the form of:
[ERROR] 2021-06-11 17:29:11.467 [milo-netty-event-loop-0]
UascClientMessageHandler -
errorMessage=ErrorMessage{error=StatusCode{name=Bad_ServiceUnsupported,
value=0x800B0000, quality=bad}, reason=null}
Unfortunately implementing the onSubscriptionTransferFailed method does not help because it is never called.
client.getSubscriptionManager().addSubscriptionListener(new UaSubscriptionManager.SubscriptionListener() {
#Override
public void onSubscriptionTransferFailed(UaSubscription subscription, StatusCode statusCode) {
try {
LOGGER.info("onSubscriptionTransferFailed");
client.getSubscriptionManager().clearSubscriptions();
client.disconnect().get();
run(client, serverAddress, biConsumer, requestedPublishingInterval);
} catch (InterruptedException | ExecutionException e) {
LOGGER.error("Failed re-subscription: {}", e.getMessage(), e);
}
}
}
Any idea how I can get the client to detect its current problem and resubscribe on server events?
Thank you in advance.
Update:
Found this commit https://github.com/eclipse/milo/commit/e854374845e6c5f46a7b033c2c62cee2ee10622a and was able to fix the problem by just increasing the Milo client sdk version to 0.6.1. Version 0.5.3 should probably also fix it but I did not test it.

Keycloack admin client hangs when attempting to make requests

I'm trying to get some user data out of Keycloack using the admin-client SDK. I've built the client like so:
Keycloak kc = KeycloakBuilder.builder() //
.serverUrl("some_url")
.realm("some-realm")
.username("admin") //
.password("password") //
.clientId("curl")
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).connectionCheckoutTimeout(10, TimeUnit.SECONDS).build()) //
.build();
System.out.println("built");
UsersResource baz = kc.realm(keycloakConfiguration.getRealm()).users();
System.out.println(baz.count());
What seems to happen is that my program hangs indefinitely when attempting to fetch baz - my debugger never hits it. I'm not quite sure what's going on - my credentials are correct. What is the correct way to cause the builder to either 1. fail after a certain time period, or 2.verify that my credentials are correct? It's maddeninly frustrating to debug.
You could create a custom method to check if you client is "online". This method could look like:
public boolean isKeycloakClientValid(Keycloak keycloakClient) {
try {
tryToPingKeycloak(keycloakClient);
} catch (Exception e) {
logger.error("Error while pinging the keycloak server", e);
return false;
}
return true;
}
With the help method:
private void tryToPingKeycloak(KeycloakClient keycloakClient) {
keycloakClient.serverInfo().getInfo();
}
Now you could check your client before using it:
if (isKeycloakClientValid(kc)) {
UsersResource baz = kc.realm(keycloakConfiguration.getRealm()).users();
}

How to know who received a message in a MUC room

For my thesis, I am using Smack to log a XMPP network that uses the MUC module.
Another software is currently sending IoT sensor data into different MUC rooms.
I'd like to know for every message sent into a MUC room, which users were in that room at the time of the message. Is this possible? I could use a messageListener to every muc room, however the listener only receives a message as an argument. Therefore I could not know who is logged into the room inside the listener method.
you can get all muc message in StanzaListener in xmpp. Please follow few steps to done this
Step 1. Declare as a global variables
ChatManagerListener chatListener;
Chat chat;
StanzaListener packetListener;
Step 2. Use this code in oncreate or in fragment
Note: Make sure you have connected with chat server.
packetListener = new StanzaListener() {
#Override
public void processPacket(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
if (packet instanceof Message) {
final Message message = (Message) packet;
}
}
};
XMPP.getInstance().getConnection(acitiviy)).addAsyncStanzaListener(stanzaListener, null);
ServiceDiscoveryManager sdm = ServiceDiscoveryManager
.getInstanceFor(XMPP.getInstance().getConnection(acitiviy)));
sdm.addFeature("jabber.org/protocol/si");
sdm.addFeature("http://jabber.org/protocol/si");
sdm.addFeature("http://jabber.org/protocol/disco#info");
sdm.addFeature("jabber:iq:privacy");
Step 3. Methods for one to one chat purposer
void sendMessage(String message) {
if (chat != null) {
try {
chat.sendMessage(message);
Message msg = new Message();
msg.setTo(JidCreate.bareFrom(jid));
msg.setFrom(XMPP.getInstance().getConnection(acitiviy)
.getUser());
ChatStateExtension ext = new ChatStateExtension(
ChatState.paused);
msg.addExtension(ext);
lastComposing = System.currentTimeMillis();
chat.sendMessage(msg);
} catch (SmackException.NotConnectedException e) {
} catch (Exception e) {
}
}
}
Step 4. On destroy
XMPP.getInstance().getConnection(acitiviy)).removeAsyncStanzaListener(stanzaListener);
Hope this will help you and if you want more information take a look from here. Thankyou
Nothing prervents you from calling Multi UserCaht.getParticipants() from within the listener. But be warned: If your goal is to determine the other receivers of receivers, then this approach is fragile. I also suggest to think about using PubSub instead of MUC for your IoT use case.

Looking for a SIP client that can use an h.264 RTSP stream as a video source

We are looking to integrate an IP camera (h.264 RTSP stream) with an asterisk PBX system for use in a school for distributed education (so a remote teacher can "dial in and teach").
Ideally we would like to be able to create a SIP client as an autoanswer pbx extension.
We are considering running a *nix box that can use a network video stream as a source for video, mix a separate audio source and present a SIP endpoint.
I understand that SIP express router may be able to:
"call an external C script, which could parse and change the SDP info within the SIP headers of clients it porxy's with, and change address of where it expects to recieve media from."
but I'm thinking it may be easier to look for a way to present an h.264 rtsp stream as /dev/videoX and use a standard SIP client.
If anyone has any pointers or any ideas for research I'd be really appreciative :-)
Thanks for reading!
W
p.s. there are IP cameras out there that claim to have SIP clients, but all I have seen only offer SIP for establishing a bi-directional audio session.
I don't know whether you found the correct answer to this question or not after all this time, but maybe I can give you some advice with VoIP and IP camera management and it would help anybody who has the same problem.
Your goal (if I understand your problem correctly) is basically creating a conference call solution which answers all the incoming calls and attach the IP camera video to all of these calls. I currently work at a company called Ozeki and - I don't know if there's an open-source solution for your problem or not - I'm going to show you an example code with SIP account registration, IP camera connection and answer all the incoming calls with the camera video.
So here is the source code:
public partial class Form1 : Form
{
private IIPCamera _camera;
private DrawingImageProvider _imageProvider;
private MediaConnector _connector;
private VideoViewerWF _videoViewerWf;
private ISoftPhone _softphone;
private IPhoneLine _phoneLine;
private IPhoneCall _call;
private PhoneCallAudioSender _audioSender;
private PhoneCallVideoSender _videoSender;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000);
_softphone.IncomingCall += softphone_IncomingCall;
_connector = new MediaConnector();
_videoSender = new PhoneCallVideoSender();
_audioSender = new PhoneCallAudioSender();
_imageProvider = new DrawingImageProvider();
_videoViewerWf = new VideoViewerWF();
SetVideoViewer();
}
private void SetVideoViewer()
{
CameraBox.Controls.Add(_videoViewerWf);
_videoViewerWf.Size = new Size(260, 180);
_videoViewerWf.BackColor = Color.Black;
_videoViewerWf.TabStop = false;
_videoViewerWf.FlipMode = FlipMode.None;
_videoViewerWf.Location = new Point(35, 30);
_videoViewerWf.Name = "_videoViewerWf";
}
private void Register(bool registrationRequired, string displayName, string userName, string authenticationId, string registerPassword, string domainHost)
{
try
{
var account = new SIPAccount(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost);
_phoneLine = _softphone.CreatePhoneLine(account);
_phoneLine.RegistrationStateChanged += phoneLine_RegistrationStateChanged;
_softphone.RegisterPhoneLine(_phoneLine);
}
catch (Exception ex)
{
label_Phoneline.Text = ex.Message;
}
}
private void phoneLine_RegistrationStateChanged(object sender, RegistrationStateChangedArgs e)
{
InvokeGuiThread(() => label_Phoneline.Text = e.State.ToString());
}
private void softphone_IncomingCall(object sender, VoIPEventArgs<iphonecall> e)
{
if (_camera != null)
{
_call = e.Item;
_call.CallStateChanged += call_CallStateChanged;
ConnectToCall();
_call.Answer();
}
}
private void ConnectToCall()
{
_videoSender.AttachToCall(_call);
_audioSender.AttachToCall(_call);
_connector.Connect(_camera.VideoChannel, _videoSender);
_connector.Connect(_camera.AudioChannel, _audioSender);
}
private void call_CallStateChanged(object sender, CallStateChangedArgs e)
{
InvokeGuiThread(() => label_Call.Text = e.State.ToString());
if (e.State == CallState.Completed)
if (_call != null)
{
_call.CallStateChanged -= call_CallStateChanged;
_connector.Disconnect(_camera.VideoChannel, _videoSender);
_connector.Disconnect(_camera.AudioChannel, _audioSender);
}
}
private void button_Connect_Click(object sender, EventArgs e)
{
_camera = IPCameraFactory.GetCamera("cameraIPAddress:8080", "admin", "admin");
_connector.Connect(_camera.VideoChannel, _imageProvider);
_videoViewerWf.SetImageProvider(_imageProvider);
_videoViewerWf.Start();
_camera.Start();
}
private void button_SIPRegister_Click(object sender, EventArgs e)
{
Register(true, "100", "100", "100", "100", "PBXAddress");
}
private void InvokeGuiThread(Action action)
{
BeginInvoke(action);
}
}
And the (example) GUI:
If you click on Connect button then the IPCameraFactory.GetCamera() will be called which is for connecting to the specified camera using the arguments (RTSP/RTP/HTTP messages in the background).
The Register button calls the Register() method to register your SIP account to the PBX. You can check the registration status at the Status label.
If there is an incoming call then the answer will occur automatically and the camera video and audio channel will attach to the call. So the caller will see and hear you.
Note:
Most of the IP camera microphones have really bad quality so you may connect your own microphone to the call. It's really simple too.
Create a Microphone object:
Microphone microphone = Microphone.GetDefaultDevice();
if (microphone != null)
microphone.Start();
And if there's an incoming call then connect this microphone to the call:
_connector.Connect(microphone, _audioSender);
And that's it. I hope I could help you!
live555 has some open-source implementations like openRTSP and playSIP which could help with your requirements.

UCMA 3.0 API Conferencing Error : Cannot join a different conference after receiving a conference invitation or conference escalation request

We have a UCMA 3.0 based application/bot that matches end users with experts. It migrates incoming one-one chat requests from end users into a multi user conference and then invites experts into the resulting multi user conference. The application itself continues to be a participant in the conference. At any given time, there may be several such conferences being brokered by our application but only one per end user. However, a single expert may be participating in more than one conference at the same time.
In our application logs we occasionally see the following exception.
Error in Conference Migration conf call # 63809878 ,Address :sip:xxxxxx#xxx.com;gruu;opaque=app:conf:focus:id:TQRREACE System.InvalidOperationException: Cannot join a different conference after receiving a conference invitation or conference escalation request.
at Microsoft.Rtc.Collaboration.ConferenceSession.VerifyAndGetConferenceAddress(String conferenceUri, String parameterName)
at Microsoft.Rtc.Collaboration.ConferenceSession.BeginJoinCommon(String conferenceUri, ConferenceJoinOptions options, AsyncCallback userCallback, Object state)
at Microsoft.Rtc.Collaboration.ConferenceSession.BeginJoin(String conferenceUri, ConferenceJoinOptions options, AsyncCallback userCallback, Object state)
at a(String A_0, String A_1, String A_2, Boolean A_3, Boolean A_4)
Below is the code snippet used to make conference. Previously this site was an OCS 2007 R2 Installation and was migrated to Lync 2010 Server.
Site is running in mixed mode. It occurs only on production server and we are not able to generate this exception on dev server, we
have tested it after generating more than 15 conferences simultaniously but no luck.
private void CreateAdHohConf(string user1Uri, string user2uri, string subject)
{
Exception exception = null;
// Create conference scheduling details for the conference.
ConferenceScheduleInformation scheduleInfo = new ConferenceScheduleInformation();
// Restrict the conference to invited users only.
scheduleInfo.AccessLevel = ConferenceAccessLevel.Everyone;
// Set a subject for the conference.
scheduleInfo.Subject = subject;
scheduleInfo.Description = subject;
scheduleInfo.ConferenceId = ConferenceServices.GenerateConferenceId();
scheduleInfo.ExpiryTime = System.DateTime.Now.AddHours(8);
scheduleInfo.IsPasscodeOptional = true;
scheduleInfo.PhoneAccessEnabled = false;
// Don't automatically assign a leader.
scheduleInfo.AutomaticLeaderAssignment = AutomaticLeaderAssignment.Everyone;
// Add the caller and recipient as participants.
scheduleInfo.Participants.Add(new ConferenceParticipantInformation("sip:" + user1Uri, ConferencingRole.Leader));
scheduleInfo.Participants.Add(new ConferenceParticipantInformation("sip:" + user2uri, ConferencingRole.Leader));
scheduleInfo.Mcus.Add(new ConferenceMcuInformation(McuType.ApplicationSharing));
scheduleInfo.Mcus.Add(new ConferenceMcuInformation(McuType.InstantMessaging));
scheduleInfo.Mcus.Add(new ConferenceMcuInformation(McuType.AudioVideo));
scheduleInfo.Mcus.Add(new ConferenceMcuInformation(McuType.Meeting));
//Scheduling conference
ConferenceServices objLocalConfSvc = lyncAgent.LocalEndpoint.ConferenceServices;
Conference confSession = null;
objLocalConfSvc.BeginScheduleConference(scheduleInfo,
result =>
{
try
{
confSession = objLocalConfSvc.EndScheduleConference(result);
}
catch (RealTimeException rtex)
{
exception = rtex;
}
catch (Exception ex)
{
exception = ex;
}
finally
{
_waitForConferenceScheduling.Set();
}
}, objLocalConfSvc);
_waitForConferenceScheduling.WaitOne();
//Begin Join conference
ConferenceSession objLocalConfSession=this.call.Conversation.ConferenceSession;
try
{
ConferenceJoinOptions joinOptions = new ConferenceJoinOptions() { CanManageLobby = false, JoinMode = JoinMode.Default };
objLocalConfSession.BeginJoin(new RealTimeAddress(confSession.ConferenceUri).Uri, joinOptions,
result => {
try
{
objLocalConfSession.EndJoin(result);
}
catch (Exception ex)
{
exception = ex;
}
finally
{
//Again, for sync. reasons.
_waitForConferenceJoin.Set();
}
}
, this.call.Conversation.ConferenceSession);
// Wait until join completes.new RealTimeAddress(this._conference.ConferenceUri).Uri,
_waitForConferenceJoin.WaitOne();
}
catch (InvalidOperationException ioex)
{
exception = ioex;
}
catch (Exception ex)
{
exception = ex;
}
//Begin Escalation
Conversation objLocalConv= this.call.Conversation;
try
{
objLocalConv.BeginEscalateToConference(
result =>
{
try
{
objLocalConv.EndEscalateToConference(result);
}
catch (Exception ex)
{
exception = ex;
}
finally
{
//Sync It
_waitForEscalation.Set();
}
}
, objLocalConv);
// Wait until escalation completes.
_waitForEscalation.WaitOne();
}
catch (InvalidOperationException ioex)
{
exception = ioex;
}
catch (Exception ex)
{
exception = ex;
}
finally
{
if (exception != null)
{
lyncAgent.Logger.Error( "Error in Conference Migration conf call # " + GetHashCode() + " , Address :" + confSession.ConferenceUri , exception);
}
}
}
Please suggest what could be the possible problem on priority basis.
Thanks in advance.
Does this method reside in an object where it is possible that it will be called by multiple sources at the same time?
If so, using what appears to be a class level variable like _waitForConferenceScheduling could be problematic. Thread A could end up accidentally letting Thread B proceed before Thread B's async action is actually completed. So Thread B could call .BeginEscalate before .EndJoin was called.
When I write UCMA code, I generally use nested callbacks to prevent this type of thing from happening.
Other than that, I'd recommend you run OCSLogger on your application server and the Lync Front End server to gather SIPStack, S3 and Collaboration logs. Looking at the actual SIP messages in detail will provide some clues.
You'd be looking for an INVITE to the conference and the response back to that INVITE.
We managed to detect the reason. It happens if any one in the participant list have already added any contact for meeting in conversation with our endpoint.