Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When searching Global Address List, is there a way to do a partial search and not just a "startsWith"

I have the following code to search the global address book by a certain string:

"CONF"

var esb = new ExchangeServiceBinding();
esb.Url = @"https://myurl.com/EWS/Exchange.asmx";

esb.Credentials = new NetworkCredential(_user,_pwd, _domain);

var rnType = new ResolveNamesType {ReturnFullContactData = true, UnresolvedEntry = "CONF"};

ResolveNamesResponseType response = esb.ResolveNames(rnType);
ArrayOfResponseMessagesType responses = resolveNamesResponse.ResponseMessages;
var responseMessage = responses.Items[0] as ResolveNamesResponseMessageType;

ResolutionType[] resolutions = responseMessage.ResolutionSet.Resolution;

the issue is that it seems to be doing a "starts with" search so I have a name called:

"CONF-123" it will show up but if i have a name "JOE-CONF" then it will not.

How can I do a partial string search on this line

var rnType = new ResolveNamesType {ReturnFullContactData = true, UnresolvedEntry = "CONF-"};

i was hoping there was something like:

var rnType = new ResolveNamesType {ReturnFullContactData = true, UnresolvedEntry = "%CONF-%"};

but that doesn't seem to work.

like image 223
leora Avatar asked Oct 16 '15 16:10

leora


People also ask

How do I find my global address list?

Viewing the GAL in Outlook on the WebLaunch Outlook on the web. Click the People icon in the navigation panel in the bottom-left corner of the window. (See figure below.) Click Directory > Default Global Address List.

How do I sync global address list?

You can manually synchronise your address book with the Global Address Book. In Outlook, click 'File', select 'Account Settings', then select 'Download Address Book...' Uncheck 'Download changes since last Send/Receive', then click 'OK'.


2 Answers

EDIT: Jan 4,2016 - Added sample code for searching AD.

What won't work

Searching the GAL via ResolveNames always uses prefix-string match for Ambiguous Name Resolution (ARN). Although EWS documentation does not say this explicitly, the Exchange ActiveSync documentation does. EWS and Exchange ActiveSync are just protocols; they both rely on ARN underneath, so you are stuck with prefix match, whether you use ActiveSync protocol or EWS.

Here is the relevant quote from Exchange ActiveSync documentation (https://msdn.microsoft.com/en-us/library/ee159743%28v=exchg.80%29.aspx)

The text query string that is provided to the Search command is used in a prefix-string match

.

What will work

The best thing to do depends on your use case, but here are some ideas:

Search Active Directory in your client program (the program that contains the code you showed in your question)

Set up your own service to search the GAL. Your client program would connect both to Exchange and to your service. Or your service could proxy EWS, so that the client program needs to connect only to your service.

How would you service get the GAL data? One way would be to use EWS ResolveNames repeatedly, to get the GAL data, 100 entries at a time and cache this data in your service. First, retrieve all the "a"s, then all the "b"s, etc. Of course, there can be more than 100 "a"s in the GAL, so just getting all the "a"s could take multiple searches - you would construct your next search string, based on the last entry returned from each search. This can be slow and painful. You would probably want to cache this data in a database and refresh it periodically.

You can also get to GAL through MAPI. You can use MAPI directly (https://msdn.microsoft.com/en-us/library/cc765775%28v=office.12%29.aspx) or through a helper library like Redemption (http://www.dimastr.com/redemption/home.htm). Whether you use MAPI directly or through Redemption, you will need to install Outlook (or Exchange) on the computer where your code is running. Because of this restriction, it may be best to not use MAPI in your client program, but to stick it in a service running on some server and have your client program connect to that service.

AD Code Sample

Another answer provided sample code to search Active Directory. I am adding a code sample that may be better suited for generic use by people who may find this question through search. Compared to the other sample, the code below has the following improvements:

  • If the search string contains any special characters (like parenthesis), they are escaped, so that the constructed filter string is valid.

  • Searching by just samaccountname many not be sufficient. If "David Smith" has account name "dsmith", searching for "David" by samaccountname would not find him. My sample shows how to search by more fields and gives some of the fields one may want to search.

  • Starting at a root like "GC:" is more robust than trying to construct an LDAP entry from Domain.GetComputerDomain().

  • All IDisposables must be disposed of (usually by using them in a using construct).

    // Search Active Directory users.
    public static IEnumerable<DirectoryEntry> SearchADUsers(string search) {
        // Escape special characters in the search string.
        string escapedSearch = search.Replace("*", "\\2a").Replace("(", "\\28")
            .Replace(")", "\\29").Replace("/", "\\2f").Replace("\\", "\\5c");
    
        // Find entries where search string appears in ANY of the following fields
        // (you can add or remove fields to suit your needs).
        // The '|' characters near the start of the expression means "any".
        string searchPropertiesExpression = string.Format(
            "(|(sn=*{0}*)(givenName=*{0}*)(cn=*{0}*)(dn=*{0}*)(samaccountname=*{0}*))",
            escapedSearch);
    
        // Only want users
        string filter = "(&(objectCategory=Person)(" + searchPropertiesExpression + "))"; 
    
        using (DirectoryEntry gc = new DirectoryEntry("GC:")) {
            foreach (DirectoryEntry root in gc.Children) {
                try {
                    using (DirectorySearcher s = new DirectorySearcher(root, filter)) {
                        s.ReferralChasing = ReferralChasingOption.All;
                        SearchResultCollection results = s.FindAll();
                        foreach (SearchResult result in results) {
                            using (DirectoryEntry de = result.GetDirectoryEntry()) {
                                yield return de;
                            }
                        }
                    }
                } finally {
                    root.Dispose();
                }
            }
        }
    }
    
like image 102
George Avatar answered Oct 13 '22 08:10

George


Though wildcard search isn't possible in EWS, it is possible in AD search. AD Queries support wildcards. i.e., *CONF* can be searched in AD, which will return all results which contain "CONF". Based on the results, query EWS for an corresponding Exchange Object. You need to find a parameter with which you can find the corresponding EWS entry. I guess email address (username) should be sufficient to find the corresponding exchange object.

AD Search code snippet...

private SearchResultCollection SearchByName(string username, string password, string searchKeyword)
{
    DirectorySearcher ds = new DirectorySearcher(new DirectoryEntry("LDAP://" + Domain.GetComputerDomain().ToString().ToLower(), username, password));
    ds.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=*" + searchKeyword + "*))";
    ds.SearchScope = SearchScope.Subtree;
    ds.ServerTimeLimit = TimeSpan.FromSeconds(90);
    return ds.FindAll();
}

AD Query Example here.

like image 28
Abhijit Annaldas Avatar answered Oct 13 '22 08:10

Abhijit Annaldas