-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathUCMASampleHelper.cs
868 lines (755 loc) · 39.3 KB
/
UCMASampleHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
/********************************************************
* *
* Copyright (C) Microsoft. All rights reserved. *
* *
********************************************************/
using System;
using System.Configuration;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Signaling;
namespace Microsoft.Rtc.Collaboration.Sample.Common
{
class UCMASampleHelper
{
private static ManualResetEvent _sampleFinished = new ManualResetEvent(false);
// The name of this application, to be used as the outgoing user agent string.
// The user agent string is put in outgoing message headers to indicate the Application used.
private static string _applicationName = "OC/16.0.4405.1000 (Skype for Business)";
const string _sipPrefix = "sip:";
// These strings will be used as keys into the App.Config file to get information to avoid prompting. For most of these,
// Suffixes 1-N will be used on each subsequent call. eg. UserName1 will be used for the first user, UserName2 for the second, etc.
private static String _serverFQDNPrompt = "ServerFQDN";
private static String _userNamePrompt = "UserName";
private static String _userDomainPrompt = "UserDomain";
private static String _userURIPrompt = "UserURI";
// Construct the network credential that the UserEndpoint will use to authenticate to the Microsoft Lync Server.
private string _userName; // User name and password pair of a user enabled for Microsoft Lync Server.
private string _userPassword;
private string _userDomain; // Domain that this user is logging into. Note: This is the AD domain, not the portion of the SIP URI following the at sign.
private System.Net.NetworkCredential _credential;
// The URI and connection server of the user used.
private string _userURI; // This should be the URI of the user given above.
// The Server FQDN used.
private static string _serverFqdn;// The Microsoft Lync Server to log in to.
// The Server port used.
private static int _serverPort = 5061;// The Microsoft Lync Server port to log in to.
// Transport type used to communicate with your Microsoft Lync Server instance.
private Microsoft.Rtc.Signaling.SipTransportType _transportType = Microsoft.Rtc.Signaling.SipTransportType.Tls;
private static CollaborationPlatform _collabPlatform;
private static bool _isPlatformStarted;
private static CollaborationPlatform _serverCollabPlatform;
private AutoResetEvent _platformStartupCompleted = new AutoResetEvent(false);
private AutoResetEvent _endpointInitCompletedEvent = new AutoResetEvent(false);
private AutoResetEvent _platformShutdownCompletedEvent = new AutoResetEvent(false);
private UserEndpoint _userEndpoint;
private ApplicationEndpoint _applicationEndpoint;
// The FQDN of the machine that the application contact is targeted at.
private string _applicationHostFQDN;
// The port that the application contact is configured to receive data on.
private int _applicationPort = -1;
// The GRUU assigned to the application contact.
private string _applicationGruu;
// Application id of the auto provisioned platform.
private string _applicationId;
// The URI of the contact being used.
private string _applicationContactURI;
private string _certificateFriendlyName;
private bool _useSuppliedCredentials;
private static int _appContactCount;
private static int _userCount = 1;
// Method to read user settings from app.config file or from the console prompts
// This method returns a UserEndpointSettings object. If you do not want to monitor LocalOwnerPresence, you may
// want to call the CreateEstablishedUserEndpoint method directly. Otherwise, you may call ReadUserSettings
// followed by CreateUserEndpoint, followed by EstablishUserEndpoint methods.
public UserEndpointSettings ReadUserSettings(string userFriendlyName)
{
UserEndpointSettings userEndpointSettings = null;
string prompt = string.Empty;
if (string.IsNullOrEmpty(userFriendlyName))
{
userFriendlyName = "Default User";
}
try
{
Console.WriteLine(string.Empty);
Console.WriteLine("Creating User Endpoint for {0}...", userFriendlyName);
Console.WriteLine();
if (ConfigurationManager.AppSettings[_serverFQDNPrompt + _userCount] != null)
{
_serverFqdn = ConfigurationManager.AppSettings[_serverFQDNPrompt + _userCount];
Console.WriteLine("Using {0} as Microsoft Lync Server", _serverFqdn);
}
else
{
// Prompt user for server FQDN. If server FQDN was entered before, then let the user use the saved value.
string localServer;
StringBuilder promptBuilder = new StringBuilder();
if (!string.IsNullOrEmpty(_serverFqdn))
{
promptBuilder.Append("Current Microsoft Lync Server = ");
promptBuilder.Append(_serverFqdn);
promptBuilder.AppendLine(". Please hit ENTER to retain this setting - OR - ");
}
promptBuilder.Append("Please enter the FQDN of the Microsoft Lync Server that the ");
promptBuilder.Append(userFriendlyName);
promptBuilder.Append(" endpoint is homed on => ");
localServer = PromptUser(promptBuilder.ToString(), null);
if (!String.IsNullOrEmpty(localServer))
{
_serverFqdn = localServer;
}
}
// Prompt user for user name
//prompt = String.Concat("Please enter the User Name for ",
// userFriendlyName,
// " (or hit the ENTER key to use current credentials)\r\n" +
// "Please enter the User Name => ");
//_userName = PromptUser(prompt, _userNamePrompt + _userCount);
_userName = ConfigurationManager.AppSettings["UserName"+ _userCount];
// If user name is empty, use current credentials
if (string.IsNullOrEmpty(_userName))
{
Console.WriteLine("Username was empty - using current credentials...");
_useSuppliedCredentials = true;
}
else
{
// Prompt for password
// prompt = String.Concat("Enter the User Password for ", userFriendlyName, " => ");
//_userPassword = PromptUser(prompt, null);
_userPassword = ConfigurationManager.AppSettings["UserPass"+ _userCount];
//prompt = String.Concat("Please enter the User Domain for ", userFriendlyName, " => ");
//_userDomain = PromptUser(prompt, _userDomainPrompt + _userCount);
_userDomain = ConfigurationManager.AppSettings["UserDomain"+ _userCount];
}
// Prompt user for user URI
prompt = String.Concat("Please enter the User URI for ", userFriendlyName, " in the User@Host format => ");
_userURI = PromptUser(prompt, _userURIPrompt + _userCount);
if (!(_userURI.ToLower().StartsWith("sip:") || _userURI.ToLower().StartsWith("tel:")))
_userURI = "sip:" + _userURI;
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["serverPort" + _userCount]))
_serverPort = Int32.Parse(ConfigurationManager.AppSettings["serverPort" + _userCount]);
// Increment the last user number
_userCount++;
// Initalize and register the endpoint, using the credentials of the user the application will be acting as.
// NOTE: the _userURI should always be of the form "sip:user@host"
userEndpointSettings = new UserEndpointSettings(_userURI, _serverFqdn, _serverPort);
if (!_useSuppliedCredentials)
{
_credential = new System.Net.NetworkCredential(_userName, _userPassword, _userDomain);
userEndpointSettings.Credential = _credential;
}
else
{
userEndpointSettings.Credential = System.Net.CredentialCache.DefaultNetworkCredentials;
}
}
catch (InvalidOperationException iOpEx)
{
// Invalid Operation Exception should only be thrown on poorly-entered input.
Console.WriteLine("Invalid Operation Exception: " + iOpEx.ToString());
}
return userEndpointSettings;
}
// Method to create an endpoint given a UserEndpointSettings object.
// This method returns a UserEndpoint object so that you can wire up Endpoint-specific event handlers.
// If you do not want to get endpoint specific event information at the time the endpoint is established, you may
// want to call the CreateEstablishedUserEndpoint method directly. Otherwise, you may call ReadUserSettings
// followed by CreateUserEndpoint, followed by EstablishUserEndpoint methods.
public UserEndpoint CreateUserEndpoint(UserEndpointSettings userEndpointSettings)
{
// Reuse platform instance so that all endpoints share the same platform.
if (_collabPlatform == null)
{
// Initalize and startup the platform.
ClientPlatformSettings clientPlatformSettings = new ClientPlatformSettings(_applicationName, _transportType);
_collabPlatform = new CollaborationPlatform(clientPlatformSettings);
}
_userEndpoint = new UserEndpoint(_collabPlatform, userEndpointSettings);
return _userEndpoint;
}
// Method to establish an already created UserEndpoint.
// This method returns an established UserEndpoint object. If you do not want to monitor LocalOwnerPresence, you may
// want to call the CreateEstablishedUserEndpoint method directly. Otherwise, you may call ReadUserSettings
// followed by CreateUserEndpoint, followed by EstablishUserEndpoint methods.
public bool EstablishUserEndpoint(UserEndpoint userEndpoint)
{
// Startup the platform, if not already
if (_isPlatformStarted == false)
{
userEndpoint.Platform.BeginStartup(EndPlatformStartup, userEndpoint.Platform);
// Sync; wait for the platform startup to complete.
_platformStartupCompleted.WaitOne();
Console.WriteLine("Platform started...");
_isPlatformStarted = true;
}
// Establish the user endpoint
userEndpoint.BeginEstablish(EndEndpointEstablish, userEndpoint);
// Sync; wait for the registration to complete.
_endpointInitCompletedEvent.WaitOne();
Console.WriteLine("Endpoint established...");
return true;
}
// Method to create an established UserEndpoint.
// This method returns an established UserEndpoint object. If you do not want to monitor LocalOwnerPresence, you may
// want to call this CreateEstablishedUserEndpoint method directly. Otherwise, you may call ReadUserSettings
// followed by CreateUserEndpoint, followed by EstablishUserEndpoint methods.
public UserEndpoint CreateEstablishedUserEndpoint(string endpointFriendlyName)
{
UserEndpointSettings userEndpointSettings;
UserEndpoint userEndpoint = null;
try
{
// Read user settings
userEndpointSettings = ReadUserSettings(endpointFriendlyName);
// Create User Endpoint
userEndpoint = CreateUserEndpoint(userEndpointSettings);
// Establish the user endpoint
EstablishUserEndpoint(userEndpoint);
}
catch (InvalidOperationException iOpEx)
{
// Invalid Operation Exception should only be thrown on poorly-entered input.
Console.WriteLine("Invalid Operation Exception: " + iOpEx.ToString());
}
return userEndpoint;
}
public UserEndpoint CreateUserEndpointWithServerPlatform(string endpointFriendlyName)
{
string prompt = string.Empty;
if (string.IsNullOrEmpty(endpointFriendlyName))
{
endpointFriendlyName = "Default User";
}
try
{
Console.WriteLine(string.Empty);
Console.WriteLine("Creating User Endpoint for {0}...", endpointFriendlyName);
Console.WriteLine();
if (ConfigurationManager.AppSettings[_serverFQDNPrompt + _userCount] != null)
{
if (ReadGenericApplicationContactConfiguration())
{
Console.WriteLine("Using {0} as Microsoft Lync Server", _serverFqdn);
}
else
{
Console.WriteLine("Error. Could not read AppSettings");
}
}
else
{
Console.WriteLine("Please fill in the App.config file.");
}
// Prompt user for user name
prompt = String.Concat(
"Please enter the User Name for ",
endpointFriendlyName,
" (or hit the ENTER key to use current credentials)\r\n" +
"Please enter the User Name => ");
_userName = PromptUser(prompt, _userNamePrompt + _userCount);
// If user name is empty, use current credentials
if (string.IsNullOrEmpty(_userName))
{
Console.WriteLine("Username was empty - using current credentials...");
_useSuppliedCredentials = true;
}
else
{
// Prompt for password
prompt = String.Concat("Enter the User Password for ", endpointFriendlyName, " => ");
_userPassword = PromptUser(prompt, null);
prompt = String.Concat("Please enter the User Domain for ", endpointFriendlyName, " => ");
_userDomain = PromptUser(prompt, _userDomainPrompt + _userCount);
}
// Prompt user for user URI
prompt = String.Concat("Please enter the User URI for ", endpointFriendlyName, " in the User@Host format => ");
_userURI = PromptUser(prompt, _userURIPrompt + _userCount);
if (!(_userURI.ToLower().StartsWith("sip:") || _userURI.ToLower().StartsWith("tel:")))
_userURI = "sip:" + _userURI;
// Reuse platform instance so that all endpoints share the same platform.
if (_serverCollabPlatform == null)
{
CreateAndStartServerPlatform();
}
// Increment the last user number
_userCount++;
// Initalize and register the endpoint, using the credentials of the user the application will be acting as.
// NOTE: the _userURI should always be of the form "sip:user@host"
UserEndpointSettings userEndpointSettings = new UserEndpointSettings(_userURI, _serverFqdn);
if (!_useSuppliedCredentials)
{
_credential = new System.Net.NetworkCredential(_userName, _userPassword, _userDomain);
userEndpointSettings.Credential = _credential;
}
else
{
userEndpointSettings.Credential = System.Net.CredentialCache.DefaultNetworkCredentials;
}
_userEndpoint = new UserEndpoint(_serverCollabPlatform, userEndpointSettings);
_endpointInitCompletedEvent.Reset();
_userEndpoint.BeginEstablish(EndEndpointEstablish, _userEndpoint);
// Sync; wait for the registration to complete.
_endpointInitCompletedEvent.WaitOne();
Console.WriteLine("{0} endpoint established...", endpointFriendlyName);
}
catch (InvalidOperationException iOpEx)
{
// Invalid Operation Exception should only be thrown on poorly-entered input.
Console.WriteLine("Invalid Operation Exception: " + iOpEx.ToString());
}
return _userEndpoint;
}
private bool ReadGenericApplicationContactConfiguration()
{
try
{
_serverFqdn = ConfigurationManager.AppSettings["ServerFQDN"];
_certificateFriendlyName = ConfigurationManager.AppSettings["CertificateFriendlyName"];
_applicationHostFQDN = ConfigurationManager.AppSettings["ApplicationHostFQDN"];
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["ApplicationPort"]))
{
_applicationPort = int.Parse(ConfigurationManager.AppSettings["ApplicationPort"], CultureInfo.InvariantCulture);
}
_applicationGruu = ConfigurationManager.AppSettings["ApplicationGRUU"];
}
catch (ConfigurationErrorsException)
{
return false;
}
if (string.IsNullOrEmpty(_serverFqdn) || string.IsNullOrEmpty(_certificateFriendlyName) ||
(_applicationPort <= 0) || string.IsNullOrEmpty(_applicationGruu))
return false;
return true;
}
private bool ReadApplicationContactConfiguration()
{
_applicationPort = -1;
ReadGenericApplicationContactConfiguration();
try
{
_appContactCount++;
string contactCount = _appContactCount.ToString(CultureInfo.InvariantCulture);
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["ApplicationHostFQDN" + contactCount]))
{
_applicationHostFQDN = ConfigurationManager.AppSettings["ApplicationHostFQDN" + contactCount];
}
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["ApplicationPort" + contactCount]))
{
_applicationPort = int.Parse(ConfigurationManager.AppSettings["ApplicationPort" + contactCount], CultureInfo.InvariantCulture);
}
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["ApplicationGRUU" + contactCount]))
{
_applicationGruu = ConfigurationManager.AppSettings["ApplicationGRUU" + contactCount];
}
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["ApplicationContactURI" + contactCount]))
{
_applicationContactURI = ConfigurationManager.AppSettings["ApplicationContactURI" + contactCount];
if (!_applicationContactURI.Trim().StartsWith(_sipPrefix, StringComparison.OrdinalIgnoreCase))
{
_applicationContactURI = _sipPrefix + _applicationContactURI.Trim();
}
}
}
catch (ConfigurationErrorsException)
{
return false;
}
if (string.IsNullOrEmpty(_serverFqdn) || string.IsNullOrEmpty(_certificateFriendlyName) || string.IsNullOrEmpty(_applicationHostFQDN) ||
(_applicationPort <= 0) || string.IsNullOrEmpty(_applicationGruu) || string.IsNullOrEmpty(_applicationContactURI))
return false;
return true;
}
#region Auto-Provisioning Methods
/// <summary>
/// Returns an application endpoint that is provisioned on a platform
/// as specified by the ApplicationID which could be provided by config
/// file or be prompted to the user; if the platform discovers multiple
/// application endpoints the first one discovered and established will be
/// returned. If no application endpoints are discovered this method
/// returns null.
/// </summary>
/// <returns></returns>
public ApplicationEndpoint CreateAutoProvisionedApplicationEndpoint()
{
_applicationId = null;
try
{
// Attempt to retrieve the application ID of the provisioned
// application from the config file.
_applicationId = System.Configuration.ConfigurationManager.AppSettings["ApplicationID"];
if (string.IsNullOrEmpty(_applicationId))
{
// The application ID wasn't retrieved from the config file
// so prompt for the application ID for the application that
// has been provisioned.
string prompt = "Please enter the unique ID of the application that is provisioned in "
+ "the topology => ";
_applicationId = UCMASampleHelper.PromptUser(prompt, null);
}
if (!string.IsNullOrEmpty(_applicationId))
{
UCMASampleHelper.WriteLine("Creating CollaborationPlatform for the provisioned application"
+ " with ID \'" + _applicationId + "\' using ProvisionedApplicationPlatformSettings.");
ProvisionedApplicationPlatformSettings settings
= new ProvisionedApplicationPlatformSettings("UCMASampleApp", _applicationId);
// Reuse platform instance so that all endpoints share the
// same platform.
if (_collabPlatform == null)
{
// Initalize and startup the platform.
_collabPlatform = new CollaborationPlatform(settings);
// Wire up a handler for the
// ApplicationEndpointOwnerDiscovered event.
_collabPlatform.RegisterForApplicationEndpointSettings(
this.Platform_ApplicationEndpointOwnerDiscovered);
// Initalize and startup the platform.
_collabPlatform.BeginStartup(EndPlatformStartup, _collabPlatform);
if (_endpointInitCompletedEvent.WaitOne())
{
UCMASampleHelper.WriteLine("Found an application EP the Owner Uri is: "
+ _applicationEndpoint.OwnerUri);
}
else
{
Console.WriteLine("Application endpoint was not established within the given time,"
+ " ending Sample.");
UCMASampleHelper.FinishSample();
}
}
else
{
Console.WriteLine("Collaboration platform already exists, ending Sample.");
UCMASampleHelper.FinishSample();
}
}
else
{
Console.WriteLine("No application ID was specified by the user. Unable to create an"
+" ApplicationEndpoint to use in the sample.");
UCMASampleHelper.FinishSample();
}
return _applicationEndpoint;
}
catch (InvalidOperationException iOpEx)
{
// Invalid Operation Exception may be thrown if the data provided
// to the BeginStartUp is called when the platform has already been
// started or terminated.
// TODO (Left to the reader): Error handling code.
UCMASampleHelper.WriteException(iOpEx);
return _applicationEndpoint;
}
}
// Registered event handler for the ApplicationEndpointOwnerDiscovered
// event on the
// CollaborationPlatform for the provisioned application.
private void Platform_ApplicationEndpointOwnerDiscovered(object sender,
ApplicationEndpointSettingsDiscoveredEventArgs e)
{
UCMASampleHelper.WriteLine("ApplicationEndpointOwnerDiscovered event was raised during startup of"
+ " the CollaborationPlatform.");
UCMASampleHelper.WriteLine("The ApplicationEndpointOwnerConfiguration that corresponds to the"
+ " provisioned application with ID " + _applicationId + " are: ");
UCMASampleHelper.WriteLine("Owner display name is: "
+ e.ApplicationEndpointSettings.OwnerDisplayName);
UCMASampleHelper.WriteLine("Owner URI is: " + e.ApplicationEndpointSettings.OwnerUri);
UCMASampleHelper.WriteLine("Now retrieving the ApplicationEndpointSettings from the "
+ "ApplicationEndpointSettingsDiscoveredEventArgs.");
ApplicationEndpointSettings settings = e.ApplicationEndpointSettings;
settings.SupportedMimePartContentTypes = new System.Net.Mime.ContentType[]{
new System.Net.Mime.ContentType("text/plain") };
UCMASampleHelper.WriteLine("Initializing the ApplicationEndpoint that corresponds to the provisioned "
+ "application with ID "+_applicationId);
// Initalize the endpoint using the settings retrieved above.
_applicationEndpoint = new ApplicationEndpoint(_collabPlatform, settings);
// Wire up the StateChanged event if necessary
// Wire up the ApplicationEndpointOwnerPropertiesChanged event if necessary
try
{
// Establish the endpoint.
_applicationEndpoint.BeginEstablish(EndEndpointEstablish, _applicationEndpoint);
}
catch (InvalidOperationException iOpEx)
{
// Invalid Operation Exception may be thrown if the data provided
// to the BeginXXX methods was invalid/malformed.
// TODO (Left to the reader): Error handling code.
UCMASampleHelper.WriteLine("Invalid Operation Exception: " + iOpEx.ToString());
}
}
#endregion
public ApplicationEndpoint CreateApplicationEndpoint(string contactFriendlyName)
{
string prompt = string.Empty;
if (string.IsNullOrEmpty(contactFriendlyName))
{
contactFriendlyName = "Default Contact";
}
Console.WriteLine();
Console.WriteLine("Creating Application Endpoint {0}...", contactFriendlyName);
Console.WriteLine();
// If application settings are provided via the app.config file, then use them
// Else prompt user for details
if (!ReadApplicationContactConfiguration())
{
// Prompt user for server FQDN. If server FQDN was entered before, then let the user use the saved value.
if (string.IsNullOrEmpty(_serverFqdn))
{
prompt = "Please enter the FQDN of the Microsoft Lync Server for this topology => ";
_serverFqdn = PromptUser(prompt, null);
}
if (string.IsNullOrEmpty(_applicationHostFQDN))
{
prompt = "Please enter the FQDN of the Machine that the application service is configured to => ";
_applicationHostFQDN = PromptUser(prompt, null);
}
if (0 >= _applicationPort)
{
// Prompt user for contact port
prompt = "Please enter the port that the application service is configured to => ";
_applicationPort = int.Parse(PromptUser(prompt, null), CultureInfo.InvariantCulture);
}
if (string.IsNullOrEmpty(_applicationGruu))
{
// Prompt user for Contact GRUU
prompt = "Please enter the GRUU assigned to the application service => ";
_applicationGruu = PromptUser(prompt, null);
}
if (string.IsNullOrEmpty(_applicationContactURI))
{
// Prompt user for contact URI
prompt = "Please enter the Contact URI in the User@Host format => ";
_applicationContactURI = PromptUser(prompt, null);
if (!_applicationContactURI.Trim().StartsWith(_sipPrefix, StringComparison.OrdinalIgnoreCase))
{
_applicationContactURI = _sipPrefix + _applicationContactURI.Trim();
}
}
if (string.IsNullOrEmpty(_certificateFriendlyName))
{
// Prompt user for contact URI
prompt = "Please enter the friendly name of the certificate to be used => ";
_certificateFriendlyName = PromptUser(prompt, null);
}
}
// Reuse platform instance so that all endpoints share the same platform.
if (_serverCollabPlatform == null)
{
CreateAndStartServerPlatform();
}
// Initalize and register the endpoint.
// NOTE: the _applicationContactURI should always be of the form "sip:user@host"
ApplicationEndpointSettings appEndpointSettings = new ApplicationEndpointSettings(_applicationContactURI, _serverFqdn, _serverPort);
_applicationEndpoint = new ApplicationEndpoint(_serverCollabPlatform, appEndpointSettings);
_endpointInitCompletedEvent.Reset();
Console.WriteLine("Establishing the endpoint...");
_applicationEndpoint.BeginEstablish(EndEndpointEstablish, _applicationEndpoint);
// Sync; wait for the registration to complete.
_endpointInitCompletedEvent.WaitOne();
Console.WriteLine("Application Endpoint established...");
return _applicationEndpoint;
}
private void CreateAndStartServerPlatform()
{
var certToUse = GetLocalCertificate(_certificateFriendlyName);
ServerPlatformSettings serverPlatformSettings = new ServerPlatformSettings(_applicationName, _applicationHostFQDN, _applicationPort, _applicationGruu, certToUse);
_serverCollabPlatform = new CollaborationPlatform(serverPlatformSettings);
// Startup the platform.
_serverCollabPlatform.BeginStartup(EndPlatformStartup, _serverCollabPlatform);
// Sync; wait for the startup to complete.
_platformStartupCompleted.WaitOne();
Console.WriteLine("Platform started...");
}
/// <summary>
/// If the 'key' is not found in app config, prompts the user using prompt text.
/// </summary>
/// <param name="promptText">If key is not found in app.Config, the user will be prompted for input using this parameter.</param>
/// <param name="key">Searches for this key in app.Config and returns if found. Pass null to always prompt.</param>
/// <returns>String value either from App.Config or user input.</returns>
public static string PromptUser(string promptText, string key)
{
String value;
if (String.IsNullOrEmpty(key) || ConfigurationManager.AppSettings[key] == null)
{
Console.WriteLine(string.Empty);
Console.Write(promptText);
value = Console.ReadLine();
}
else
{
value = ConfigurationManager.AppSettings[key];
Console.WriteLine("Using keypair {0} - {1} from AppSettings...", key, value);
}
return value;
}
/// <summary>
/// Displays <paramref name="textToDisplay"/> and pauses the console to for easier viewing of logs.
/// </summary>
/// <param name="textToDisplay">Text to display with whitespace around it.</param>
public static void PauseBeforeContinuing(string textToDisplay)
{
Console.WriteLine("\n\n********************");
Console.WriteLine(textToDisplay);
Console.WriteLine("********************\n\n");
Console.ReadLine();
}
private void EndPlatformStartup(IAsyncResult ar)
{
CollaborationPlatform collabPlatform = ar.AsyncState as CollaborationPlatform;
try
{
// The platform should now be started.
collabPlatform.EndStartup(ar);
// It should be noted that all the re-thrown exceptions will crash the application. This is intentional.
// Ideal exception handling would report the error and shut down nicely. In production code, consider using
// an IAsyncResult implementation to report the error instead of throwing or put the implementation
// in this try block.
}
catch (OperationFailureException opFailEx)
{
// OperationFailureException will be thrown when the platform cannot establish, here, usually due to invalid data.
Console.WriteLine(opFailEx.Message);
throw;
}
catch (ConnectionFailureException connFailEx)
{
// ConnectionFailureException will be thrown when the platform cannot connect.
// ClientPlatforms will not throw this exception on startup.
Console.WriteLine(connFailEx.Message);
throw;
}
catch (RealTimeException realTimeEx)
{
// RealTimeException may be thrown as a result of any UCMA operation.
Console.WriteLine(realTimeEx.Message);
throw;
}
finally
{
// Again, just for sync. reasons.
_platformStartupCompleted.Set();
}
}
private void EndEndpointEstablish(IAsyncResult ar)
{
LocalEndpoint currentEndpoint = ar.AsyncState as LocalEndpoint;
try
{
currentEndpoint.EndEstablish(ar);
}
catch (RegisterException regEx)
{
// AuthenticationException will be thrown when the credentials are invalid.
Console.WriteLine(regEx.Message);
throw;
}
catch (AuthenticationException authEx)
{
// AuthenticationException will be thrown when the credentials are invalid.
Console.WriteLine(authEx.Message);
throw;
}
catch (ConnectionFailureException connFailEx)
{
// ConnectionFailureException will be thrown when the endpoint cannot connect to the server, or the credentials are invalid.
Console.WriteLine(connFailEx.Message);
throw;
}
catch (InvalidOperationException iOpEx)
{
// InvalidOperationException will be thrown when the endpoint is not in a valid state to connect. To connect, the platform must be started and the Endpoint Idle.
Console.WriteLine(iOpEx.Message);
throw;
}
finally
{
// Again, just for sync. reasons.
_endpointInitCompletedEvent.Set();
}
}
internal void ShutdownPlatform()
{
if (_collabPlatform != null)
{
_collabPlatform.BeginShutdown(EndPlatformShutdown, _collabPlatform);
}
if (_serverCollabPlatform != null)
{
_serverCollabPlatform.BeginShutdown(EndPlatformShutdown, _serverCollabPlatform);
}
//Again, just for synchronous reasons.
_platformShutdownCompletedEvent.WaitOne();
}
private void EndPlatformShutdown(IAsyncResult ar)
{
CollaborationPlatform collabPlatform = ar.AsyncState as CollaborationPlatform;
try
{
//Shutdown actions will not throw.
collabPlatform.EndShutdown(ar);
Console.WriteLine("The platform is now shut down.");
}
finally
{
_platformShutdownCompletedEvent.Set();
}
}
/// <summary>
/// Read the local store for the certificate to use when creating the platform. This is necessary to establish a connection to the Server.
/// </summary>
/// <param name="friendlyName">The friendly name of the certificate to use.</param>
/// <returns>The certificate instance.</returns>
public static X509Certificate2 GetLocalCertificate(string friendlyName)
{
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = store.Certificates;
store.Close();
foreach (X509Certificate2 certificate in certificates)
{
if (certificate.FriendlyName.Equals(friendlyName, StringComparison.OrdinalIgnoreCase))
{
return certificate;
}
}
return null;
}
public static void WriteLine(string line)
{
Console.WriteLine(line);
}
public static void WriteErrorLine(string line)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(line);
Console.ResetColor();
}
public static void WriteException(Exception ex)
{
WriteErrorLine(ex.ToString());
}
/// <summary>
/// Prompts the user to press a key, unblocking any waiting calls to the
/// <code>WaitForSampleFinish</code> method
/// </summary>
public static void FinishSample()
{
Console.WriteLine("Please hit any key to end the sample.");
Console.ReadKey();
_sampleFinished.Set();
}
/// <summary>
///
/// </summary>
public static void WaitForSampleFinish()
{
_sampleFinished.WaitOne();
}
}
}