Work with mail on MS Exchange server through EWS. Part 1



    Hello readers Habrahabr!

    As part of a series of these posts, I want to talk about a technology such as EWS and how to use it to work with mail stored on MS Exchange 2007 - 2010 servers. I will try to show how simple and convenient it is to use EWS.

    We will start from the very beginning, by familiarizing ourselves with this technology and creating the first project, and ending with more complex mail manipulations.

    This post is an introductory post and most likely will not be interesting to those who are already familiar with EWS.

    Introduction


    Exchange Web Services ( EWS ) is a special protocol developed by MS and designed to manage mail and other components that make up MS Exchange. The protocol is based on XML. Description of supported operations and examples can be found here .

    EWS is a relatively young technology introduced in Exchange 2007, which replaced WebDAV . MS puts a lot of effort into developing and improving EWS. Now it is a powerful tool, with a simple and understandable API, as well as excellent documentation, which allows you to solve the problems of managing MS Exchange objects.

    The EWS architecture is shown in the following image:



    As you can see, EWS is hosted on servers with Client Access Server(CAS) role. This role is responsible for processing requests from the user, i.e. any clients, such as Outlook, MFCMapi, browser + OWA, etc., all of them are connected to CAS. The request from the client application falls into IIS, where the application implementing the EWS functionality is located. Those. EWS "lives" in IIS and runs in a separate process or processes w3wp.exe (processes in which IIS application pools are executed). In addition to EWS, other applications, such as OWA, ECP, and PowerShell, can spin there.

    EWS acts as a layer between the client request and Exchange internals. When an EWS request arrives from a client application, it is proxied to internal Exchange calls, after which it arrives at the Mailbox Server role , where the operations themselves are performed.

    Note: A little about the internal structure of the Mailbox role can be found in my previous article .

    Physically, EWS is located inside a set of .NET assemblies. And starting with Exchange 2010, not only EWS calls, but also OWA get there. Those. MS apparently realized that the idea of ​​several branches of the same functionality code was not successful and decided to leave only one branch, which should simplify and speed up support and development.

    We got a general idea, we can move on to programming.

    Development


    We will need:
    1. MS Exchange Server 2007 - 2010 for testing
    2. Visual Studio 2008 - 2010
    First, create an empty C # console project . Next, you need to add reference'y with a description of the classes / methods / types, etc. that we need to work with EWS. The easiest way to do this, if you have Exchange, is to add a Web Reference . In VS 2010, you need to select a project and select Add Service Rederence , then Advanced , Add Web Reference and in the URL field specify the address of your test server in the format https: // [server name] /EWS/Services.wsdl , i.e. it should look like this:



    After clicking next, the download of the reference will begin, and if everything goes well, you will be asked to indicate the name of this reference, and at the end it will be added to the project.

    That's it, now we can try to connect to the server. And the first thing you need to do is get the ExchangeServiceBinding object , this is perhaps the most important object in EWS, it is through it that all valid methods will be called and it will identify the specific connection on the client side. To create this object, you can use the following method:

    public static ExchangeServiceBinding GetBinding(
        String server, 
        String domain, 
        String user, 
        String password)
    {
        var esb = new ExchangeServiceBinding();
        // Указываем используемые креды
        esb.Credentials = new NetworkCredential(user, password, domain);
        // Задаем входную точку для EWS
        esb.Url = "https://" + server + "/EWS/Exchange.asmx";
        esb.RequestServerVersionValue = new RequestServerVersion();
        // Указываем тип Exchange сервера 
        // Для Exchange 2007 SP1 и 2010 подходит Exchange2007_SP1
        // Для Exchange 2007 без SP нужно указать Exchange2007
        esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007_SP1;
        return esb;
    }
    

    Note: If the server uses a self-signed certificate, then it will not work because such a certificate will not pass validation. As a workaround, you can add additional apply code for any certificates:

    ServicePointManager.ServerCertificateValidationCallback = (obj, certificate, chain, errors) => true;
    

    Having ExchangeServiceBinding, you can try to do something useful, for example, send a message. MSDN tells you to use the CreateItemType object for this . To do this, we write the following method:

    public static void SendMessage(
        ExchangeServiceBinding esb, 
        String to, 
        String subject, 
        String body)
    {
        // Создаем CreateItem request
        // Он одинаковый для создания отправки сообщений 
        // Указываем, что мы хотим его именно отправить, а не сохранить
        var createItemRequest = 
            new CreateItemType
        {
            Items = new NonEmptyArrayOfAllItemsType(),
            MessageDispositionSpecified = true,
            MessageDisposition = MessageDispositionType.SendOnly
        };
        // Создаем item типа Message
        // Указываем recipients, в нашем примере только одного
        var message = new MessageType();
        message.ToRecipients = new EmailAddressType[1];
        message.ToRecipients[0] = new EmailAddressType();
        message.ToRecipients[0].EmailAddress = to;
        // Тема письма
        message.Subject = subject;
        // Содержимое письма и формат
        message.Body = new BodyType();
        message.Body.BodyType1 = BodyTypeType.Text;
        message.Body.Value = body;
        // Связываем наш request с созданным Item'ом
        createItemRequest.Items.Items = new ItemType[1];
        createItemRequest.Items.Items[0] = message;
        // Выполняем операцию
        CreateItemResponseType createItemResponse = esb.CreateItem(createItemRequest);
        // Получаем ответ
        ArrayOfResponseMessagesType responseMessages = createItemResponse.ResponseMessages;
        // Бросаем исключение, если ошибка
        var responseMessage = responseMessages.Items;
        foreach (var rmt in responseMessage.Where(rmt => rmt.ResponseClass == ResponseClassType.Error))
        {
            throw new Exception(rmt.MessageText);
        }
    }
    


    Note: The above code shows the easiest way to send a message, and we do not see the “power” of the classes used, but they provide extensive options for customizing the operations performed (setting all kinds of properties, flags, etc.). More about this and not only I will try to tell in the following articles.

    Calling this code can be done like this:

    static void Main()
    {
        ServicePointManager.ServerCertificateValidationCallback = (obj, certificate, chain, errors) => true;
        try
        {
            const String  
                    server = "myserver",
                    domain = "mydomain",
                    user = "myuser",
                    password = "mypassword",
                    mailTo = "myuser2@mydomain.local",
                    subject = "read me",
                    body = "ehlo, developers!";
            var esb = GetBinding(server, domain, user, password);
            SendMessage(esb, mailTo, subject, body);
            Console.WriteLine("Done!");
        }
        catch (Exception e)
        {
            if(e.Message != null)
                Console.WriteLine(e.Message);
        } 
    }
    


    If it seemed to you that the code was running frighteningly long (I have about 5 seconds), do not worry, this only happens when you first call it. Almost all the time, initialization is performed only once. Also, if this was the first EWS call for the server, then the server needs to initialize itself and create a new instance w3wp to process the request, which may take some time too.

    Conclusion


    We briefly became acquainted with the EWS technology and wrote a simple example of sending a letter to the addressee.

    As you can see, the code is very concise and clear. The MS documentation is pretty good and almost always with examples. If you programmed in C ++ MAPI, then you will appreciate how much this method is easier.

    If you have the task of writing an email client or any other tasks related to remotely working on Exchange objects, I hope that you will use EWS. because IMHO this method is the simplest and most understandable of all with which I had to work.

    Thanks for attention.

    To be continued...


    Also popular now: