I am using LyncClient library to create a widget and when a call comes in externally the remote participant sometimes comes up as 'sip:emailaddress#domain' if the contact is in the users outlook contacts.
Wondering if there is a way or library that allows me to open up the contact card for that email address and then get phone numbers if there are any.
Been pulling at my hair for a while now and can't figure it out. Any tips or experiences (good and bad) would be great! Let me know if you guys need more information.
I made a program that gets the phone address out of a SIP URL.
a SIP Url is basically in this format(Without quotes): "sip:username#domain"
try
{
LyncClient lyncClient = LyncClient.GetClient();
Contact contact;
List<object> endPoints = new List<object>();
Dictionary<string, string> phoneNumbers = new Dictionary<string, string>();
contact = lyncClient.ContactManager.GetContactByUri("sip:myusername#domain.com"); //PASS THE SIP ADDRESS HERE
var telephoneNumber = (List<object>)contact.GetContactInformation(ContactInformationType.ContactEndpoints);
//var contactName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
//var availability = contact.GetContactInformation(ContactInformationType.Activity).ToString();
//foreach (object endPoint in telephoneNumber)
//{
//Console.WriteLine(((ContactEndpoint)endPoint).DisplayName + " " + ((ContactEndpoint)endPoint).Type.ToString());
//}
endPoints = telephoneNumber.Where<object>(N => ((ContactEndpoint)N).Type == ContactEndpointType.HomePhone || ((ContactEndpoint)N).Type == ContactEndpointType.MobilePhone || ((ContactEndpoint)N).Type == ContactEndpointType.OtherPhone || ((ContactEndpoint)N).Type == ContactEndpointType.WorkPhone).ToList<object>();
foreach (var endPoint in endPoints)
{
//Console.WriteLine(((ContactEndpoint)test).DisplayName.ToString());
string numberType = Regex.Replace(((ContactEndpoint)endPoint).Type.ToString(), #"Phone", "");
//string number = Regex.Replace(((ContactEndpoint)endPoint).DisplayName.ToString(), #"[^0-9]", "");
string number = "";
//Numbers only with dashes
if (Regex.IsMatch(((ContactEndpoint)endPoint).DisplayName.ToString(), #"^\d{3}-\d{3}-\d{4}$"))
{
number = ((ContactEndpoint)endPoint).DisplayName.ToString();
try
{
phoneNumbers.Add(numberType, number);
}
catch
{
}
}
//Console.WriteLine(numberType + " " + number);
}
foreach (var entry in phoneNumbers)
{
//entry.Key is the PhoneType
//entry.Value is the Phone Number
}
}
catch (Exception ex)
{
MessageBox.Show("An error occurred: " + ex.Message);
}
I don't think that this is the email address.
SIP URI's has the same format as an email address: sip:username#sipdomain, so maybe Lync is just sending the peer sip address.
In this case you just have to grab the sub-string between "sip:" and "#" to get the caller id.
Another problem is that there are multiple ways for SIP to send the caller id. Maybe you should look for Asserted/Preferred identity (and Lync just extracts it from the SIP "Contact" header).
Related
I'm trying using gRPC dynamically typed values but with the little information about their usefulness, It's almost impossible to do this... So I will show the image/code that I have problems and the questions that are eating my brain
gRPC Method I'm doing:
public override Task<HelloReply2> TestObject(Status request, ServerCallContext context) {
//The part I may have problems
var status = new Status();
//here I want a User that corresponds to my request.Data
//example -> request.Data = User1 (gives me null if User1 don`t exist in db)
// request.Data = 14 (gives me null if 14 don`t exist in db)
// request.Data = true (gives me null if true don`t exist in db)
var a1 = _context.Users_5.FirstOrDefault(x => x.Username.Equals(request.Data));
var b1 = _context.Users_5.FirstOrDefault(x => x.Email.Equals(request.Data));
var c1 = _context.Users_5.FirstOrDefault(x => x.Age.Equals(request.Data));
var d1 = _context.Users_5.FirstOrDefault(x => x.Test.Equals(request.Data));
//is a bool
//here i want too Create dynamic values
status.Data = Value.ForStruct(new Struct {
Fields =
{
["Integer"] = Value.ForNumber(c1!.Age),
["StringName"] = Value.ForString(a1!.Username),
["StringEmail"] = Value.ForString(b1!.Email),
["Boolean"] = Value.ForBool(d1!.Test)
}
});
//Below is just a simple string who gives different string (depending on the
//data Status (also how to read the message from the status.Data ?)
HelloReply2 hello = new();
if(a1 != null)
{
hello.Message = "There is a User with the Username " + request.Data + ". His Email is " + a1.Email;
} else if (b1 != null) {
hello.Message = "There is a User with the Email " + request.Data + ". His Username is " + b1.Username;
}
else if (c1 != null)
{
hello.Message = "There is at least one User with that Age of " + request.Data + ". His Username is " + c1.Username;
}
else if (d1 != null)
{
if(d1.Test == true)
{
hello.Message = "There is at least one User who dislikes chocolate: " + request.Data + ". His Username is " + d1.Username;
} else
{
hello.Message = hello.Message = "There is at least one User who likes chocolate: " + request.Data + ". His Username is " + d1.Username;
}
}
else
{
hello.Message = "We didn't find something with the value that the User put in. Value:" + request.Data;
}
return Task.FromResult(hello);
}
Questions: How to Get the one Value from my gRPC? How to convert a "Object" in c# (one string, one integer or one List) into a ONE value of google.protobuf.Value (so it not give me errors like this Controller from a Web Api below)? Is something wrong with my gRPC Service Method (is something wrong reading the dynamic values? Can I do that calls for getting a User for a DB? How to read dynamic values?)
// I try using Google.Protobuf.WellKnownTypes.Value obj but
//not workings because gives me a lot of values to put
[HttpGet("TypeObject/{obj}")]
public async Task<ActionResult<HelloReply2>> TypeObject([FromRoute] Object obj){
Status objRequest = new Status { Data = (Google.Protobuf.WellKnownTypes.Value)
obj };
//cannot do this (gives me error of casting but the ideia is doing something
//similar to this)
var hello = await _greetClient.TestObjectAsync(objRequest);
return Ok(hello);
}
Any help on how to resolve this error of using Value gRPC or if is something wrong with the code is always welcome.
Edit:
One day after this question I don't have any solutions / progress. I was think of doing Any or OneOf for testing but it also gives me errors (who don't make sense at all). This code from Microsoft (C# Format part is not recognize) doesn't work in my project with the protos reload (the problem is not in the protos)
Link: https://learn.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/protobuf-any-oneof
How I can use Any / OneOf without give me error in the Formating? What is the difference between Value and this two? Can the three (Value, Any, OneOf) be dynamic/Object values (if yes how to convert the types)?
Edit 2:
Still have problems, I'm trying using gRPC Any , and maybe have some progress (not all).
So with Any I have my method in the server gRPC and it is like this
public override Task<HelloReply2> TestObject3(AnyMessage request, ServerCallContext context){
HelloReply2 anyMessageResponse;
var y = request.TypeUrl;
switch (request.TypeUrl)
{
case "type.googleapis.com/any.HelloRequest":
var string_1 = request.Unpack<HelloRequest>();
anyMessageResponse = new HelloReply2{
Message = "You type String: " + $"{string_1.Name}"
};
break;
case "type.googleapis.com/any.TestInteger1":
var integer_1 = request.Unpack<TestInteger1>();
anyMessageResponse = new HelloReply2{
Message = "You type Integer: " + $"{integer_1.Message}"
};
break;
case "type.googleapis.com/any.TestBool1":
var bool_1 = request.Unpack<TestInteger1>();
anyMessageResponse = new HelloReply2{
Message = "You type Bool: " + $"{bool_1.Message}"
};
break;
default:
throw new InvalidOperationException("Unexpected type URL.");}
return Task.FromResult(anyMessageResponse);
}
This ideia comes from here (https://github.com/grpc/grpc-dotnet/issues/917), but the client part their don't have any much info or I don't understand that part
This is what I did in the WebApi (who is my client and the code is similar to the above one)
using AnyMessage = Google.Protobuf.WellKnownTypes.Any;
[HttpGet("TypeObject3/{obj3}")]
public async Task<ActionResult<HelloReply2>> TypeObject3([FromRoute] string obj3)
{
AnyMessage objRequest = new() { TypeUrl = obj3 };
var hello = await _greetClient.TestObject3Async(objRequest);
var l = hello.Message;
return Ok(hello);
}
First I had the variable Any declared in the method instead of string but as you can only put string and stringBytes so I preferred to put it like this (with the string as an obj3 variable) but my goal is to see if the variable is of type TestBool1 or TestInteger1 as I have declared in the protos and not be a string that I will be able to see, and the biggest problem was if I had more variables inside the messages how to proceed? So my secondary question is how to use Any on the client side via the Web-Api? I forgot to say but I'm using .Net 6 Core and for testing I'm using Swagger, where at this moment my error hits the Exception dictated by the Server method.
Questions: Why TypeUrl is a string and not object? How to fix my problem? How to test the object type (or string) for more values if the messages was with 1 more types?
Also I will show my test proto too show how I'm doing this
import "google/protobuf/struct.proto";
import "google/protobuf/any.proto";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayNormalHello (Empty_2) returns (HelloReply);
rpc SayHello (HelloRequest) returns (HelloReply2);
rpc TestInt (TestInteger1) returns (HelloReply2);
rpc TestBoolean (TestBool1) returns (HelloReply2);
rpc TestObject (Status) returns (HelloReply2); //Not working
rpc TestObject2 (Status2) returns (HelloReply2); //Not working
rpc TestObject3 (google.protobuf.Any) returns (HelloReply2); //Also
//Not working
}
message Empty_2{
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
// The response message containing the greetings.
message HelloReply2 {
string message = 1;
}
message TestInteger1 {
int32 message = 1;
}
message TestBool1 {
bool message = 1;
}
message Status {
google.protobuf.Value data = 1;
}
message Status2 {
google.protobuf.Any data = 1;
}
Any help is welcome.
In the project I developed, I needed an infrastructure where I could perform dynamic operations such as REST service using gRPC. As I understand it, what you want is something similar to this.
I have developed a solution for this. It might work for you too.
You can gain some flexibility by making a definition with a single String field for Requests and Responses that you want to be Dynamic, and then using this field to hold JSON data as strings.
For example, instead of defining different responses for different types, you can solve it by making one definition like this.
message HelloReply {
// Stringified JSON Data
string data = 1;
}
i'm building a routing agent for exchange 2010 using c# (framework 3.5)
i have a 3rd party app that recieves emails , and authenticate users via their email address.
the problem starts when i sent an email to a distribution group,
the "To" field is set to the D-group email address, and it causes my trouble with the 3rd party app.
how can i convert the TO field of an email massage sent to : xxxGroup#xxx.com
into: user1inGroup#xxx.com;user2inGroup#xxx.com,.....
this is part of my code, i tried deleting the "to" field, but nothing seems to work.
void ownRoutingAgent_OnResolvedMessage(ResolvedMessageEventSource source, QueuedMessageEventArgs messageEventArgs)
{
bool forwardToSeg = false;
if (true) EventViewerLogger.WriteInfo("FromAddress: " + messageEventArgs.MailItem.FromAddress.ToString());
if (true) EventViewerLogger.WriteInfo("SecureSenders: " + m_SecureSenderAddress);
distGroupList = generateDistGroupList();
////////////////////////////////////////////
//Check if recepient is a distrebution group
Random rnd = new Random();
int numOfUser = rnd.Next(0, senderAddresses.Length);
messageEventArgs.MailItem.FromAddress = new RoutingAddress(senderAddresses[numOfUser]);
// run over all recipients list
//foreach (EnvelopeRecipient recp in messageEventArgs.MailItem.Recipients)
//{
foreach (MyClass disGrp in distGroupList)
{
// Checks if Recipients contain an e-mail group.
// if yes, does not route to seg.
if (messageEventArgs.MailItem.Message.To[0].NativeAddress.ToString().ToUpper() == disGrp.emailAdress.ToUpper())
{
messageEventArgs.MailItem.Message.To[0].NativeAddress.Remove(0);
messageEventArgs.MailItem.Message.To.Remove(new EmailRecipient(messageEventArgs.MailItem.Message.To[0].DisplayName.ToString(),messageEventArgs.MailItem.Message.To[0].NativeAddress.ToString()));
foreach (EnvelopeRecipient yywx in messageEventArgs.MailItem.Recipients)
{
//remove group address from mail-recipients
// messageEventArgs.MailItem.Message.To.Add*******
// = messageEventArgs.MailItem.Recipients
//add all group members to the "TO" field
//messageEventArgs.MailItem.Recipients;
}
}
}
use the AddressBook class to do that or expand the recipients of a message in a Transport Agent . if you want to expand a list that would requires an AD call which can be very costly in terms of performance in a Transport Agent.
I am using the Exchange Web Services Managed API 2.2 to monitor users inboxes and need to determine if an e-mail is a new item, a reply or a forwarded message.
I have seen various articles on SO such as how to notice if a mail is a forwarded mail? and Is there a way to determine if a email is a reply/response using ews c#? which both help in their specific cases but I still cannot work out how to distinguish between a reply and a forwarded item.
In the first article an extra 5 bytes is added each time (forward or reply) so I don't know what the last action was.
The second article suggests using the InReplyTo however when I examine the property for forwarded e-mails it contains the original senders e-mail address (not null).
I have seen other articles such as this or this that suggest using extended properties to examine the values in PR_ICON_INDEX, PR_LAST_VERB_EXECUTED and PR_LAST_VERB_EXECUTION_TIME.
My code looks as follows but never returns a value for lastVerbExecuted
var lastVerbExecutedProperty = new ExtendedPropertyDefinition(4225, MapiPropertyType.Integer);
var response = service.BindToItems(newMails, new PropertySet(BasePropertySet.IdOnly, lastVerbExecutedProperty));
var items = response.Select(itemResponse => itemResponse.Item);
foreach (var item in items)
{
object lastVerb;
if (item.TryGetProperty(lastVerbExecutedProperty, out lastVerb))
{
// do something
}
}
PR_ICON_INDEX, PR_LAST_VERB_EXECUTED and PR_LAST_VERB_EXECUTION_TIME would only work to tell you if the recipient has acted on a message in their Inbox. Eg if the user had replied or forwarded a message in their inbox then these properties get set on the message in their Inbox. On the message that was responded to or forwarded these properties would not be set. I would suggest you use the In-Reply-To Transport header which should be set on any message that is replied to or forwarded, this should contain the internet messageid of the message that was replied to or forwarded eg.
FindItemsResults<Item> fiRs = service.FindItems(WellKnownFolderName.Inbox, new ItemView(10));
PropertySet fiRsPropSet = new PropertySet(BasePropertySet.FirstClassProperties);
ExtendedPropertyDefinition PR_TRANSPORT_MESSAGE_HEADERS = new ExtendedPropertyDefinition(0x007D, MapiPropertyType.String);
fiRsPropSet.Add(PR_TRANSPORT_MESSAGE_HEADERS);
service.LoadPropertiesForItems(fiRs.Items, fiRsPropSet);
foreach (Item itItem in fiRs)
{
Object TransportHeaderValue = null;
if(itItem.TryGetProperty(PR_TRANSPORT_MESSAGE_HEADERS,out TransportHeaderValue)) {
string[] stringSeparators = new string[] { "\r\n" };
String[] taArray = TransportHeaderValue.ToString().Split(stringSeparators, StringSplitOptions.None);
for (Int32 txCount = 0; txCount < taArray.Length; txCount++)
{
if (taArray[txCount].Length > 12)
{
if (taArray[txCount].Substring(0, 12).ToLower() == "in-reply-to:")
{
String OriginalId = taArray[txCount].Substring(13);
Console.WriteLine(OriginalId);
}
}
}
}
}
Apart from the Subject prefix that was discussed in the other link I don't know of any other proprieties that will differentiate between a reply or forward.
Cheers
Glen
The best way to rely is on the ResponeCode of Extended properties
Refer below scripts
private static int IsForwardOrReplyMail(ExchangeService service, EmailMessage messageToCheck)
{
try
{
// Create extended property definitions for PidTagLastVerbExecuted and PidTagLastVerbExecutionTime.
ExtendedPropertyDefinition PidTagLastVerbExecuted = new ExtendedPropertyDefinition(0x1081, MapiPropertyType.Integer);
ExtendedPropertyDefinition PidTagLastVerbExecutionTime = new ExtendedPropertyDefinition(0x1082, MapiPropertyType.SystemTime);
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.Subject, PidTagLastVerbExecutionTime, PidTagLastVerbExecuted);
messageToCheck = EmailMessage.Bind(service, messageToCheck.Id, propSet);
// Determine the last verb executed on the message and display output.
object responseType;
messageToCheck.TryGetProperty(PidTagLastVerbExecuted, out responseType);
if (responseType != null && ((Int32)responseType) == 104)
{
//FORWARD
return 104;
}
else if (responseType != null && ((Int32)responseType) == 102)
{
//REPLY
return 102;
}
}
catch (Exception)
{
return 0;
//throw new NotImplementedException();
}
}
To determine if it was a reply to a email, you can use the EmailMessage objects InReplyTo property, e.g:
EmailMessage mail = ((EmailMessage)Item.Bind(service, new ItemId(UniqueId)));
if (mail.InReplyTo == null)
return;
else
..your code
I use Managed Wifi API library and ok to connect a "HasProfile" wifi AP like this:
WlanClient client = new WlanClient();
foreach (var item in client.Interfaces)
{
ViewModel.CurrentWlan = item;
Wlan.WlanAvailableNetwork[] networks = item.GetAvailableNetworkList(0);
foreach (Wlan.WlanAvailableNetwork network in networks)
{
var name = Helpers.GetStringForSSID(network.dot11Ssid);
ConnectionModel model = new ConnectionModel
{
DisplayName = string.Format("{0} (signal {1})", name, network.wlanSignalQuality),
IsConnected = network.flags.HasFlag(Wlan.WlanAvailableNetworkFlags.Connected),
SSID = network.dot11Ssid.SSID,
SsidString = Convert.ToBase64String(network.dot11Ssid.SSID),
ProfileName = network.profileName,
Name = name
};
if (network.flags.HasFlag(Wlan.WlanAvailableNetworkFlags.HasProfile))
{
model.XmlProfile = item.GetProfileXml(model.ProfileName);
}
if (network.flags == Wlan.WlanAvailableNetworkFlags.HasProfile)
{
model.IsRemembered = true;
}
ViewModel.Connections.Add(model);
}
}
OK now, all the availible APs are in the ViewModel.Connections.
Then I can connect one of the AP that Has Profiles:
private void OnConnect_Handler(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
ConnectionModel model = button.DataContext as ConnectionModel;
ViewModel.CurrentWlan.SetProfile(Wlan.WlanProfileFlags.AllUser, model.XmlProfile, true);
ViewModel.CurrentWlan.Connect(Wlan.WlanConnectionMode.Profile, Wlan.Dot11BssType.Any, model.ProfileName);
}
and it's worked!
then I found a problem that is, such code only can connect those APs Has Profile, if I want to connect to a AP without profile(I think that means 'AP never connected'), I should use the following code:
string profileName = model.ProfileName;
string mac = "1008B1CD976F";
string key = "IsThisPasswordField?";
string profile = string.Format("<?xml version=\"1.0\"?><WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\"><name>{0}</name><SSIDConfig><SSID><hex>{1}</hex><name>{0}</name></SSID></SSIDConfig><connectionType>ESS</connectionType><MSM><security><authEncryption><authentication>open</authentication><encryption>WEP</encryption><useOneX>false</useOneX></authEncryption><sharedKey><keyType>networkKey</keyType><protected>false</protected><keyMaterial>{2}</keyMaterial></sharedKey><keyIndex>0</keyIndex></security></MSM></WLANProfile>",
profileName, mac, key);
ViewModel.CurrentWlan.SetProfile(Wlan.WlanProfileFlags.AllUser, profile, true);
ViewModel.CurrentWlan.Connect(Wlan.WlanConnectionMode.Profile, Wlan.Dot11BssType.Any, profileName);
But I tried serveral times always failed because:
(1) I don't know what the XML content is, why can't just input password then Connect(ssid, password)? is it because WIFI programming doesn't go like this?
(2) If must set a profile, how to input the right things such as:
string mac = "1008B1CD976F";
string key = "IsThisPasswordField?";
how to know this 'mac', and how to encrypt this key(if it's the AP's password)?
EDIT: here's the Managed Wifi API site. But there's no documentation.
First: The hex-string, you want to insert into the profile is not the mac of the accesspoint, but the hex-string of the ssid.
The profile xml is missleading here. I had the same problem, until I converted the hex-string back of a known profile. It was the ssid ;-)
You can just convert the ssid to it's hex representation by so:
string ssid = "YourSSID";
byte[] ssidBytes = Text.Encoding.Default.GetBytes(ssid);
string ssidHex = BitConverter.ToString(ssidBytes);
ssidHex = ssidHex.Replace("-", "");
Second: Because of a wrong hex-string of your ssid, your connection approach with the clear text password did not work either.
So just use the hex-representation of the ssid and you can connect with the password in clear text, as you tried before.
I have a C# managed Application that runs on a Lync 2013 Server and uses MSPL. I route every call from MSPL to the application and handle it there. Lync to Lync calls work fine and their to Header is in the form sip:user#domain.com. But when a call from outside the network (non-lync like mobile phone etc.) to the workphone of a Lyncuser is started, the Uri is like sip:+12341234#domain.com;user=phone (sip:[workphone]#domain). Passing this string to the Presence Retrieval function does not work.
var sips = new string[] { phone }; // The "To" number
presenceService.BeginPresenceQuery(sips, categories, null, null, null);
This always returns an empty result. How can I first retrieve the user associated with the phone number to get its presence?
I solved it this way:
public static UserObject FindContactBySip(string sip)
{
return UserList.FirstOrDefault(u => u.HasSip(sip));
}
private static void InitFindUsersInAD()
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
var user = new UserPrincipal(ctx);
user.Name = "*";
var searcher = new PrincipalSearcher(user);
var result = searcher.FindAll();
var sipList = new List<string>();
UserList = new List<UserObject>();
foreach (var res in result)
{
var underlying = (DirectoryEntry)res.GetUnderlyingObject();
string email = string.Empty, phone = string.Empty, policies = string.Empty;
foreach (var keyval in underlying.Properties.Values)
{
var kv = keyval as System.DirectoryServices.PropertyValueCollection;
if (kv != null && kv.Value is string)
{
if (kv.PropertyName.Equals("msRTCSIP-PrimaryUserAddress"))
{
email = (kv.Value ?? string.Empty).ToString();
}
else if (kv.PropertyName.Equals("msRTCSIP-Line"))
{
phone = (kv.Value ?? string.Empty).ToString();
}
else if (kv.PropertyName.Equals("msRTCSIP-UserPolicies"))
{
policies = (kv.Value ?? string.Empty).ToString();
}
}
}
if (!string.IsNullOrEmpty(phone) && !string.IsNullOrEmpty(email))
{
var userobj = new UserObject(email, phone, policies);
UserList.Add(userobj);
}
}
}
First I initialize the UserList (List // Custom class) from the AD. Then I call FindContactBySip and check if the provided SIP equals the Email or Phone of the User.
I have found two other ways to solve your problem.
In MSPL you can:
toContactCardInfo = QueryCategory(toUserUri, 0, "contactCard", 0);
Which gives you:
<contactCard xmlns=""http://schemas.microsoft.com/2006/09/sip/contactcard"" >
<identity >
<name >
<displayName >
Lync User</displayName>
</name>
<email >
lync.user#xxx.com</email>
</identity>
</contactCard>
You can turn the email address into a sip address. This only works if your lync setup uses email address for sip addresses.
The other method is to use 'P-Asserted-Identity' sip header to determine who the phone call is being routed to/from. The only problem is that it doesn't show up in the inital invites (as that would be for the From side anyway), but in the 180 ringing response from the Lync Client.
P-Asserted-Identity: <sip:lync.user#xxx.com>, <tel:+123456789;ext=12345>
So if you wait for the 180 ringing response then I would recommand that you use P-Asserted-Identity method and you don't even need to escape out of MSPL for it!