All files / Scripts/ui/getHeaders GetHeadersEWS.ts

40% Statements 24/60
39.39% Branches 13/33
20% Functions 2/10
39.65% Lines 23/58

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 16110x 10x 10x 10x                               10x       4x 4x 45x 45x 3x     1x     5x 5x   5x 5x 5x   5x   3x 3x 2x     3x 1x               5x                                                                                                                                                                                                                
import { GetHeaders } from "./GetHeaders";
import { Errors } from "../../Errors";
import { mhaStrings } from "../../mhaStrings";
import { ParentFrame } from "../../ParentFrame";
 
/*
 * GetHeadersEWS.js
 *
 * This file has all the methods to get PR_TRANSPORT_MESSAGE_HEADERS
 * from the current message via EWS.
 *
 * Requirement Sets and Permissions
 * makeEwsRequestAsync requires 1.0 and ReadWriteMailbox
 */
interface HeaderProp {
    prop: string;
    responseCode: string;
}
 
export class GetHeadersEWS {
    public static extractHeadersFromXml(xml: string): HeaderProp {
        // This filters nodes for the one that matches the given name.
        function filterNode(xmlDocument: XMLDocument, node: string): string {
            const elements = xmlDocument.getElementsByTagName("*");
            for (let i = 0; i < elements.length; i++) {
                const element = elements[i];
                if (element && element.nodeName === node && element.textContent) {
                    return element.textContent.replace(/\r\n|\r|\n/g, "\n");
                }
            }
            return "";
        }
 
        const ret = {} as HeaderProp;
        try {
            // Strip encoded embedded null characters from our XML. parseXML doesn't like them.
            xml = xml.replace(/&#x0;/g, "");
            const parser = new DOMParser();
            const response = parser.parseFromString(xml, "text/xml");
 
            if (response && !response.querySelector("parsererror")) {
                // We can do this because we know there's only the one property.
                const extendedProperty = filterNode(response, "t:ExtendedProperty");
                if (extendedProperty) {
                    ret.prop = extendedProperty;
                }
 
                if (!ret.prop) {
                    ret.responseCode = filterNode(response, "m:ResponseCode");
                }
            }
        } catch {
            // Exceptions thrown from parseXML are super chatty and we do not want to log them.
            // We throw this exception away and just return nothing.
        }
 
        return ret;
    }
 
    private static stripHeaderFromXml(xml: string): string {
        if (!xml) return "";
        return xml
            .replace(/<t:Value>[\s\S]*<\/t:Value>/g, "<t:Value>redacted</t:Value>")
            .replace(/<t:ItemId.*?\/>/g, "<t:ItemId ID=\"redacted\"/>");
    }
 
    // Function called when the EWS request is complete.
    private static callbackEws(asyncResult: Office.AsyncResult<string>): string {
        try {
            // Process the returned response here.
            let header = null;
            if (asyncResult.value) {
                header = GetHeadersEWS.extractHeadersFromXml(asyncResult.value);
 
                // We might not have a prop and also no error. This is OK if the prop is just missing.
                if (!header.prop) {
                    if (header.responseCode === "NoError") {
                        ParentFrame.showError(null, mhaStrings.mhaHeadersMissing, true);
                        return "";
                    }
                }
            }
 
            if (header && header.prop) {
                return header.prop;
            }
            else {
                throw new Error(mhaStrings.mhaRequestFailed);
            }
        }
        catch (e) {
            if (asyncResult) {
                Errors.log(asyncResult.error, "Async Response\n" + GetHeadersEWS.stripHeaderFromXml(JSON.stringify(asyncResult, null, 2)));
            }
 
            ParentFrame.showError(e, "EWS callback failed");
        }
 
        return "";
    }
 
    private static getSoapEnvelope(request: string): string {
        // Wrap an Exchange Web Services request in a SOAP envelope.
        return "<?xml version='1.0' encoding='utf-8'?>" +
            "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'" +
            "               xmlns:t='http://schemas.microsoft.com/exchange/services/2006/types'>" +
            "  <soap:Header>" +
            "     <t:RequestServerVersion Version='Exchange2013'/>" +
            "  </soap:Header>" +
            "  <soap:Body>" +
            request +
            "  </soap:Body>" +
            "</soap:Envelope>";
    }
 
    private static getHeadersRequest(id: string): string {
        // Return a GetItem EWS operation request for the headers of the specified item.
        return "<GetItem xmlns='http://schemas.microsoft.com/exchange/services/2006/messages'>" +
            "  <ItemShape>" +
            "    <t:BaseShape>IdOnly</t:BaseShape>" +
            "    <t:BodyType>Text</t:BodyType>" +
            "    <t:AdditionalProperties>" +
            // PR_TRANSPORT_MESSAGE_HEADERS
            "      <t:ExtendedFieldURI PropertyTag='0x007D' PropertyType='String' />" +
            "    </t:AdditionalProperties>" +
            "  </ItemShape>" +
            "  <ItemIds><t:ItemId Id='" + id + "'/></ItemIds>" +
            "</GetItem>";
    }
 
    private static async makeEwsRequest(mailbox: Office.Mailbox, envelope:string): Promise<string> {
        return new Promise((resolve) => {
            mailbox.makeEwsRequestAsync(envelope, function (asyncResult: Office.AsyncResult<string>) {
                resolve(GetHeadersEWS.callbackEws(asyncResult));
            });
        });
    }
 
    public static async send(): Promise<string> {
        if (!GetHeaders.validItem()) {
            Errors.logMessage("No item selected (EWS)");
            return "";
        }
 
        try {
            ParentFrame.updateStatus(mhaStrings.mhaRequestSent);
            const mailbox = Office.context.mailbox;
            if (mailbox && mailbox.item) {
                const request = GetHeadersEWS.getHeadersRequest(mailbox.item.itemId);
                const envelope = GetHeadersEWS.getSoapEnvelope(request);
                const headers = await GetHeadersEWS.makeEwsRequest(mailbox, envelope);
                return headers;
            }
        } catch (e2) {
            ParentFrame.showError(e2, mhaStrings.mhaRequestFailed);
        }
 
        return "";
    }
}